Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.

Commit 9f465c3

Browse files
author
Pablo Mercado
authored
Merge pull request #115 from triggermesh/task/add-semantic-compare-jobs
Add semantic compare jobs
2 parents 68a209e + 520ecd7 commit 9f465c3

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed

pkg/reconciler/semantic/semantic.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package semantic
55

66
import (
77
appsv1 "k8s.io/api/apps/v1"
8+
batchv1 "k8s.io/api/batch/v1"
89
corev1 "k8s.io/api/core/v1"
910
"k8s.io/apimachinery/pkg/api/resource"
1011
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -24,6 +25,7 @@ var Semantic = conversion.EqualitiesOrDie(
2425
serviceAccountEqual,
2526
serviceEqual,
2627
secretEqual,
28+
jobEqual,
2729
)
2830

2931
// eq is an instance of Equalities for internal deep derivative comparisons
@@ -171,3 +173,22 @@ func serviceAccountEqual(a, b *corev1.ServiceAccount) bool {
171173

172174
return true
173175
}
176+
177+
func jobEqual(a, b *batchv1.Job) bool {
178+
if a == b {
179+
return true
180+
}
181+
if a == nil || b == nil {
182+
return false
183+
}
184+
185+
if !eq.DeepDerivative(&a.ObjectMeta, &b.ObjectMeta) {
186+
return false
187+
}
188+
189+
if !eq.DeepDerivative(&a.Spec, &b.Spec) {
190+
return false
191+
}
192+
193+
return true
194+
}

pkg/reconciler/semantic/semantic_test.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/stretchr/testify/require"
1212

1313
appsv1 "k8s.io/api/apps/v1"
14+
batchv1 "k8s.io/api/batch/v1"
1415
corev1 "k8s.io/api/core/v1"
1516
"k8s.io/apimachinery/pkg/runtime"
1617
)
@@ -221,6 +222,104 @@ const (
221222
"updatedReplicas": 1
222223
}
223224
}
225+
`
226+
227+
tJob = `
228+
{
229+
"apiVersion": "batch/v1",
230+
"kind": "Job",
231+
"metadata": {
232+
"annotations": {
233+
"batch.kubernetes.io/job-tracking": ""
234+
},
235+
"creationTimestamp": "2023-04-12T08:32:12Z",
236+
"generation": 1,
237+
"labels": {
238+
"app.kubernetes.io/managed-by": "triggermesh-core",
239+
"app.kubernetes.io/part-of": "triggermesh",
240+
"controller-uid": "dad9425a-3410-40ea-832e-31b087f8f808",
241+
"job-name": "test"
242+
},
243+
"name": "test",
244+
"namespace": "default",
245+
"resourceVersion": "650907",
246+
"uid": "dad9425a-3410-40ea-832e-31b087f8f808"
247+
},
248+
"spec": {
249+
"backoffLimit": 3,
250+
"completionMode": "NonIndexed",
251+
"completions": 1,
252+
"parallelism": 1,
253+
"selector": {
254+
"matchLabels": {
255+
"controller-uid": "dad9425a-3410-40ea-832e-31b087f8f808"
256+
}
257+
},
258+
"suspend": false,
259+
"template": {
260+
"metadata": {
261+
"creationTimestamp": null,
262+
"labels": {
263+
"app.kubernetes.io/managed-by": "triggermesh-core",
264+
"app.kubernetes.io/part-of": "triggermesh",
265+
"controller-uid": "dad9425a-3410-40ea-832e-31b087f8f808",
266+
"job-name": "test"
267+
},
268+
"ownerReferences": [
269+
{
270+
"apiVersion": "eventing.triggermesh.io/v1alpha1",
271+
"kind": "TestKind",
272+
"name": "test",
273+
"uid": "186f3c1b-952b-4e45-9837-2ee6bd4436ec"
274+
}
275+
]
276+
},
277+
"spec": {
278+
"containers": [
279+
{
280+
"env": [
281+
{
282+
"name": "ENV_ONE",
283+
"value": "1"
284+
},
285+
{
286+
"name": "ENV_TWO",
287+
"value": "2"
288+
}
289+
],
290+
"image": "test/test:latest",
291+
"imagePullPolicy": "Always",
292+
"name": "test",
293+
"resources": {},
294+
"terminationMessagePath": "/dev/termination-log",
295+
"terminationMessagePolicy": "File"
296+
}
297+
],
298+
"dnsPolicy": "ClusterFirst",
299+
"restartPolicy": "Never",
300+
"schedulerName": "default-scheduler",
301+
"securityContext": {},
302+
"terminationGracePeriodSeconds": 30
303+
}
304+
}
305+
},
306+
"status": {
307+
"conditions": [
308+
{
309+
"lastProbeTime": "2023-04-12T08:34:02Z",
310+
"lastTransitionTime": "2023-04-12T08:34:02Z",
311+
"message": "Job has reached the specified backoff limit",
312+
"reason": "BackoffLimitExceeded",
313+
"status": "True",
314+
"type": "Failed"
315+
}
316+
],
317+
"failed": 1,
318+
"ready": 0,
319+
"startTime": "2023-04-12T08:32:12Z",
320+
"uncountedTerminatedPods": {}
321+
}
322+
}
224323
`
225324
)
226325

@@ -404,3 +503,90 @@ func loadFixture(t *testing.T, contents string, obj runtime.Object) {
404503
t.Fatalf("Error deserializing fixture object: %s", err)
405504
}
406505
}
506+
507+
func TestJobEqual(t *testing.T) {
508+
current := &batchv1.Job{}
509+
loadFixture(t, tJob, current)
510+
511+
require.GreaterOrEqual(t, len(current.Labels), 2,
512+
"Test suite requires a reference object with at least 2 labels to run properly")
513+
require.True(t, len(current.Spec.Template.Spec.Containers) > 0 &&
514+
len(current.Spec.Template.Spec.Containers[0].Env) > 0 &&
515+
current.Spec.Template.Spec.Containers[0].Env[0].Value != "",
516+
"Test suite requires a reference object with a Container that has at least 1 EnvVar to run properly")
517+
518+
assert.True(t, deploymentEqual(nil, nil), "Two nil elements should be equal")
519+
520+
testCases := map[string]struct {
521+
prep func() *batchv1.Job
522+
expect bool
523+
}{
524+
"not equal when one element is nil": {
525+
func() *batchv1.Job {
526+
return nil
527+
},
528+
false,
529+
},
530+
// counter intuitive but expected result for deep derivative comparisons
531+
"equal when all desired attributes are empty": {
532+
func() *batchv1.Job {
533+
return &batchv1.Job{}
534+
},
535+
true,
536+
},
537+
"not equal when some existing attribute differs": {
538+
func() *batchv1.Job {
539+
desired := current.DeepCopy()
540+
for k := range desired.Labels {
541+
desired.Labels[k] += "test"
542+
break // changing one is enough
543+
}
544+
return desired
545+
},
546+
false,
547+
},
548+
"equal when current has more attributes than desired": {
549+
func() *batchv1.Job {
550+
desired := current.DeepCopy()
551+
for k := range desired.Labels {
552+
delete(desired.Labels, k)
553+
break // deleting one is enough
554+
}
555+
return desired
556+
},
557+
true,
558+
},
559+
"not equal when desired has more attributes than current": {
560+
func() *batchv1.Job {
561+
desired := current.DeepCopy()
562+
for k := range desired.Labels {
563+
desired.Labels[k+"test"] = "test"
564+
break // adding one is enough
565+
}
566+
return desired
567+
},
568+
false,
569+
},
570+
"not equal when EnvVar desired value is empty": {
571+
func() *batchv1.Job {
572+
desired := current.DeepCopy()
573+
desired.Spec.Template.Spec.Containers[0].Env[0].Value = ""
574+
return desired
575+
},
576+
false,
577+
},
578+
}
579+
580+
for name, tc := range testCases {
581+
//nolint:scopelint
582+
t.Run(name, func(t *testing.T) {
583+
desired := tc.prep()
584+
switch tc.expect {
585+
case true:
586+
assert.True(t, jobEqual(desired, current))
587+
case false:
588+
assert.False(t, jobEqual(desired, current))
589+
}
590+
})
591+
}
592+
}

0 commit comments

Comments
 (0)