1
16
17 package controllers_test
18
19 import (
20 "testing"
21 "time"
22
23 cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
24 cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
25 cmgen "github.com/cert-manager/cert-manager/test/unit/gen"
26 "github.com/stretchr/testify/require"
27 certificatesv1 "k8s.io/api/certificates/v1"
28 corev1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/runtime"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 clocktesting "k8s.io/utils/clock/testing"
33 "sigs.k8s.io/controller-runtime/pkg/event"
34
35 "github.com/cert-manager/issuer-lib/api/v1alpha1"
36 "github.com/cert-manager/issuer-lib/controllers"
37 "github.com/cert-manager/issuer-lib/internal/testapi/api"
38 "github.com/cert-manager/issuer-lib/internal/testapi/testutil"
39 )
40
41 func TestCertificateRequestPredicate(t *testing.T) {
42 predicate := controllers.CertificateRequestPredicate{}
43
44 cr1 := cmgen.CertificateRequest("cr1")
45
46 type testcase struct {
47 name string
48 event event.UpdateEvent
49 shouldReconcile bool
50 }
51
52 testcases := []testcase{
53 {
54 name: "nil",
55 shouldReconcile: true,
56 event: event.UpdateEvent{
57 ObjectOld: cr1,
58 ObjectNew: nil,
59 },
60 },
61 {
62 name: "wrong-type",
63 shouldReconcile: true,
64 event: event.UpdateEvent{
65 ObjectOld: cr1,
66 ObjectNew: &corev1.ConfigMap{},
67 },
68 },
69 {
70 name: "label-changed",
71 shouldReconcile: false,
72 event: event.UpdateEvent{
73 ObjectOld: cmgen.CertificateRequestFrom(cr1,
74 func(cr *cmapi.CertificateRequest) {
75 cr.Labels = map[string]string{
76 "test-label1": "value",
77 }
78 },
79 cmgen.AddCertificateRequestAnnotations(map[string]string{
80 "test-annotation1": "value1",
81 }),
82 ),
83 ObjectNew: cmgen.CertificateRequestFrom(cr1,
84 cmgen.AddCertificateRequestAnnotations(map[string]string{
85 "test-annotation1": "value1",
86 }),
87 ),
88 },
89 },
90 {
91 name: "annotation-added",
92 shouldReconcile: true,
93 event: event.UpdateEvent{
94 ObjectOld: cmgen.CertificateRequestFrom(cr1,
95 cmgen.AddCertificateRequestAnnotations(map[string]string{
96 "test-annotation1": "value1",
97 }),
98 ),
99 ObjectNew: cmgen.CertificateRequestFrom(cr1,
100 cmgen.AddCertificateRequestAnnotations(map[string]string{
101 "test-annotation1": "value1",
102 "test-annotation2": "value2",
103 }),
104 ),
105 },
106 },
107 {
108 name: "ready-condition-changed",
109 shouldReconcile: false,
110 event: event.UpdateEvent{
111 ObjectOld: cmgen.CertificateRequestFrom(cr1,
112 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
113 Type: cmapi.CertificateRequestConditionReady,
114 Reason: cmapi.CertificateRequestReasonPending,
115 Status: cmmeta.ConditionFalse,
116 }),
117 ),
118 ObjectNew: cmgen.CertificateRequestFrom(cr1,
119 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
120 Type: cmapi.CertificateRequestConditionReady,
121 Reason: cmapi.CertificateRequestReasonIssued,
122 Status: cmmeta.ConditionTrue,
123 }),
124 ),
125 },
126 },
127 {
128 name: "ready-condition-added",
129 shouldReconcile: true,
130 event: event.UpdateEvent{
131 ObjectOld: cmgen.CertificateRequestFrom(cr1),
132 ObjectNew: cmgen.CertificateRequestFrom(cr1,
133 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
134 Type: cmapi.CertificateRequestConditionReady,
135 Reason: cmapi.CertificateRequestReasonIssued,
136 Status: cmmeta.ConditionTrue,
137 }),
138 ),
139 },
140 },
141 {
142 name: "ready-condition-added-other-removed",
143 shouldReconcile: true,
144 event: event.UpdateEvent{
145 ObjectOld: cmgen.CertificateRequestFrom(cr1,
146 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
147 Type: cmapi.CertificateRequestConditionApproved,
148 Reason: "",
149 Status: cmmeta.ConditionFalse,
150 }),
151 ),
152 ObjectNew: cmgen.CertificateRequestFrom(cr1,
153 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
154 Type: cmapi.CertificateRequestConditionReady,
155 Reason: "",
156 Status: cmmeta.ConditionFalse,
157 }),
158 ),
159 },
160 },
161 {
162 name: "approved-condition-changed",
163 shouldReconcile: true,
164 event: event.UpdateEvent{
165 ObjectOld: cmgen.CertificateRequestFrom(cr1,
166 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
167 Type: cmapi.CertificateRequestConditionApproved,
168 Reason: "",
169 Status: cmmeta.ConditionFalse,
170 }),
171 ),
172 ObjectNew: cmgen.CertificateRequestFrom(cr1,
173 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
174 Type: cmapi.CertificateRequestConditionApproved,
175 Reason: "",
176 Status: cmmeta.ConditionTrue,
177 }),
178 ),
179 },
180 },
181 {
182 name: "approved-condition-changed-only-reason",
183 shouldReconcile: false,
184 event: event.UpdateEvent{
185 ObjectOld: cmgen.CertificateRequestFrom(cr1,
186 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
187 Type: cmapi.CertificateRequestConditionApproved,
188 Reason: "test1",
189 Status: cmmeta.ConditionFalse,
190 }),
191 ),
192 ObjectNew: cmgen.CertificateRequestFrom(cr1,
193 cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
194 Type: cmapi.CertificateRequestConditionApproved,
195 Reason: "test2",
196 Status: cmmeta.ConditionFalse,
197 }),
198 ),
199 },
200 },
201 }
202
203 for _, tc := range testcases {
204 tc := tc
205 t.Run(tc.name, func(t *testing.T) {
206 result := predicate.Update(tc.event)
207 require.Equal(t, tc.shouldReconcile, result)
208 })
209 }
210 }
211
212 func TestCertificateSigningRequestPredicate(t *testing.T) {
213 predicate := controllers.CertificateSigningRequestPredicate{}
214
215 csr1 := cmgen.CertificateSigningRequest("cr1")
216
217 type testcase struct {
218 name string
219 event event.UpdateEvent
220 shouldReconcile bool
221 }
222
223 testcases := []testcase{
224 {
225 name: "nil",
226 shouldReconcile: true,
227 event: event.UpdateEvent{
228 ObjectOld: csr1,
229 ObjectNew: nil,
230 },
231 },
232 {
233 name: "wrong-type",
234 shouldReconcile: true,
235 event: event.UpdateEvent{
236 ObjectOld: csr1,
237 ObjectNew: &corev1.ConfigMap{},
238 },
239 },
240 {
241 name: "label-changed",
242 shouldReconcile: false,
243 event: event.UpdateEvent{
244 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
245 func(cr *certificatesv1.CertificateSigningRequest) {
246 cr.Labels = map[string]string{
247 "test-label1": "value",
248 }
249 },
250 cmgen.AddCertificateSigningRequestAnnotations(map[string]string{
251 "test-annotation1": "value1",
252 }),
253 ),
254 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
255 cmgen.AddCertificateSigningRequestAnnotations(map[string]string{
256 "test-annotation1": "value1",
257 }),
258 ),
259 },
260 },
261 {
262 name: "annotation-added",
263 shouldReconcile: true,
264 event: event.UpdateEvent{
265 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
266 cmgen.AddCertificateSigningRequestAnnotations(map[string]string{
267 "test-annotation1": "value1",
268 }),
269 ),
270 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
271 cmgen.AddCertificateSigningRequestAnnotations(map[string]string{
272 "test-annotation1": "value1",
273 "test-annotation2": "value2",
274 }),
275 ),
276 },
277 },
278 {
279 name: "failed-condition-changed",
280 shouldReconcile: true,
281 event: event.UpdateEvent{
282 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
283 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
284 Type: certificatesv1.CertificateFailed,
285 Reason: cmapi.CertificateRequestReasonPending,
286 Status: corev1.ConditionFalse,
287 }),
288 ),
289 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
290 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
291 Type: certificatesv1.CertificateFailed,
292 Reason: cmapi.CertificateRequestReasonIssued,
293 Status: corev1.ConditionTrue,
294 }),
295 ),
296 },
297 },
298 {
299 name: "failed-condition-added",
300 shouldReconcile: true,
301 event: event.UpdateEvent{
302 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1),
303 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
304 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
305 Type: certificatesv1.CertificateFailed,
306 Reason: cmapi.CertificateRequestReasonIssued,
307 Status: corev1.ConditionTrue,
308 }),
309 ),
310 },
311 },
312 {
313 name: "failed-condition-added-other-removed",
314 shouldReconcile: true,
315 event: event.UpdateEvent{
316 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
317 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
318 Type: certificatesv1.CertificateApproved,
319 Reason: "",
320 Status: corev1.ConditionTrue,
321 }),
322 ),
323 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
324 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
325 Type: certificatesv1.CertificateFailed,
326 Reason: "",
327 Status: corev1.ConditionTrue,
328 }),
329 ),
330 },
331 },
332 {
333 name: "approved-condition-changed",
334 shouldReconcile: true,
335 event: event.UpdateEvent{
336 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
337 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
338 Type: certificatesv1.CertificateApproved,
339 Reason: "",
340 Status: corev1.ConditionFalse,
341 }),
342 ),
343 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
344 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
345 Type: certificatesv1.CertificateApproved,
346 Reason: "",
347 Status: corev1.ConditionTrue,
348 }),
349 ),
350 },
351 },
352 {
353 name: "approved-condition-changed-only-reason",
354 shouldReconcile: false,
355 event: event.UpdateEvent{
356 ObjectOld: cmgen.CertificateSigningRequestFrom(csr1,
357 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
358 Type: certificatesv1.CertificateApproved,
359 Reason: "test1",
360 Status: corev1.ConditionTrue,
361 }),
362 ),
363 ObjectNew: cmgen.CertificateSigningRequestFrom(csr1,
364 cmgen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
365 Type: certificatesv1.CertificateApproved,
366 Reason: "test2",
367 Status: corev1.ConditionTrue,
368 }),
369 ),
370 },
371 },
372 }
373
374 for _, tc := range testcases {
375 tc := tc
376 t.Run(tc.name, func(t *testing.T) {
377 result := predicate.Update(tc.event)
378 require.Equal(t, tc.shouldReconcile, result)
379 })
380 }
381 }
382
383 type testissuer struct {
384 Status *v1alpha1.IssuerStatus
385 metav1.Object
386 }
387
388 var _ v1alpha1.Issuer = &testissuer{}
389
390 func (*testissuer) GetObjectKind() schema.ObjectKind {
391 panic("not implemented")
392 }
393
394 func (*testissuer) DeepCopyObject() runtime.Object {
395 panic("not implemented")
396 }
397
398 func (ti *testissuer) GetStatus() *v1alpha1.IssuerStatus {
399 return ti.Status
400 }
401
402 func (ti *testissuer) GetIssuerTypeIdentifier() string {
403 return "test"
404 }
405
406 func TestLinkedIssuerPredicate(t *testing.T) {
407 predicate := controllers.LinkedIssuerPredicate{}
408
409 issuer1 := testutil.TestIssuer("issuer-1")
410
411 fakeTime := time.Now()
412 fakeClock := clocktesting.NewFakeClock(fakeTime)
413
414 type testcase struct {
415 name string
416 event event.UpdateEvent
417 shouldReconcile bool
418 }
419
420 testcases := []testcase{
421 {
422 name: "nil",
423 shouldReconcile: true,
424 event: event.UpdateEvent{
425 ObjectOld: nil,
426 ObjectNew: issuer1,
427 },
428 },
429 {
430 name: "random-condition-changed",
431 shouldReconcile: false,
432 event: event.UpdateEvent{
433 ObjectOld: testutil.TestIssuerFrom(issuer1,
434 testutil.SetTestIssuerStatusCondition(
435 fakeClock,
436 "random",
437 cmmeta.ConditionFalse,
438 "test1",
439 "test1",
440 ),
441 ),
442 ObjectNew: testutil.TestIssuerFrom(issuer1,
443 testutil.SetTestIssuerStatusCondition(
444 fakeClock,
445 "random",
446 cmmeta.ConditionTrue,
447 "test2",
448 "test2",
449 ),
450 ),
451 },
452 },
453 {
454 name: "ready-status-nil",
455 shouldReconcile: true,
456 event: event.UpdateEvent{
457 ObjectOld: &testissuer{Status: nil},
458 ObjectNew: testutil.TestIssuerFrom(issuer1,
459 testutil.SetTestIssuerStatusCondition(
460 fakeClock,
461 cmapi.IssuerConditionReady,
462 cmmeta.ConditionFalse,
463 "reason",
464 "message",
465 ),
466 ),
467 },
468 },
469 {
470 name: "ready-condition-added",
471 shouldReconcile: true,
472 event: event.UpdateEvent{
473 ObjectOld: testutil.TestIssuerFrom(issuer1),
474 ObjectNew: testutil.TestIssuerFrom(issuer1,
475 testutil.SetTestIssuerStatusCondition(
476 fakeClock,
477 cmapi.IssuerConditionReady,
478 cmmeta.ConditionFalse,
479 "reason",
480 "message",
481 ),
482 ),
483 },
484 },
485 {
486 name: "ready-condition-identical",
487 shouldReconcile: false,
488 event: event.UpdateEvent{
489 ObjectOld: testutil.TestIssuerFrom(issuer1,
490 testutil.SetTestIssuerStatusCondition(
491 fakeClock,
492 cmapi.IssuerConditionReady,
493 cmmeta.ConditionFalse,
494 "reason1",
495 "message1",
496 ),
497 ),
498 ObjectNew: testutil.TestIssuerFrom(issuer1,
499 testutil.SetTestIssuerStatusCondition(
500 fakeClock,
501 cmapi.IssuerConditionReady,
502 cmmeta.ConditionFalse,
503 "reason2",
504 "message2",
505 ),
506 ),
507 },
508 },
509 {
510 name: "ready-condition-identical-new-observed-generation",
511 shouldReconcile: true,
512 event: event.UpdateEvent{
513 ObjectOld: testutil.TestIssuerFrom(issuer1,
514 testutil.SetTestIssuerStatusCondition(
515 fakeClock,
516 cmapi.IssuerConditionReady,
517 cmmeta.ConditionFalse,
518 "reason1",
519 "message1",
520 ),
521 ),
522 ObjectNew: testutil.TestIssuerFrom(issuer1,
523 testutil.SetTestIssuerGeneration(2),
524 testutil.SetTestIssuerStatusCondition(
525 fakeClock,
526 cmapi.IssuerConditionReady,
527 cmmeta.ConditionFalse,
528 "reason2",
529 "message2",
530 ),
531 ),
532 },
533 },
534 {
535 name: "ready-condition-changed",
536 shouldReconcile: true,
537 event: event.UpdateEvent{
538 ObjectOld: testutil.TestIssuerFrom(issuer1,
539 testutil.SetTestIssuerStatusCondition(
540 fakeClock,
541 cmapi.IssuerConditionReady,
542 cmmeta.ConditionFalse,
543 "reason",
544 "message",
545 ),
546 ),
547 ObjectNew: testutil.TestIssuerFrom(issuer1,
548 testutil.SetTestIssuerStatusCondition(
549 fakeClock,
550 cmapi.IssuerConditionReady,
551 cmmeta.ConditionTrue,
552 "reason",
553 "message",
554 ),
555 ),
556 },
557 },
558 }
559
560 for _, tc := range testcases {
561 tc := tc
562 t.Run(tc.name, func(t *testing.T) {
563 result := predicate.Update(tc.event)
564 require.Equal(t, tc.shouldReconcile, result)
565 })
566 }
567 }
568
569 func TestIssuerPredicate(t *testing.T) {
570 predicate := controllers.IssuerPredicate{}
571
572 issuer1 := testutil.TestIssuer("issuer-1")
573
574 fakeTime := time.Now()
575 fakeClock := clocktesting.NewFakeClock(fakeTime)
576
577 type testcase struct {
578 name string
579 event event.UpdateEvent
580 shouldReconcile bool
581 }
582
583 testcases := []testcase{
584 {
585 name: "nil",
586 shouldReconcile: true,
587 event: event.UpdateEvent{
588 ObjectOld: nil,
589 ObjectNew: issuer1,
590 },
591 },
592 {
593 name: "wrong-type",
594 shouldReconcile: true,
595 event: event.UpdateEvent{
596 ObjectOld: issuer1,
597 ObjectNew: &corev1.ConfigMap{},
598 },
599 },
600 {
601 name: "identical-generations",
602 shouldReconcile: false,
603 event: event.UpdateEvent{
604 ObjectOld: testutil.TestIssuerFrom(issuer1,
605 testutil.SetTestIssuerGeneration(80),
606 ),
607 ObjectNew: testutil.TestIssuerFrom(issuer1,
608 testutil.SetTestIssuerGeneration(80),
609 ),
610 },
611 },
612 {
613 name: "changed-generations",
614 shouldReconcile: true,
615 event: event.UpdateEvent{
616 ObjectOld: testutil.TestIssuerFrom(issuer1,
617 testutil.SetTestIssuerGeneration(80),
618 ),
619 ObjectNew: testutil.TestIssuerFrom(issuer1,
620 testutil.SetTestIssuerGeneration(2),
621 ),
622 },
623 },
624 {
625 name: "changed-annotations",
626 shouldReconcile: true,
627 event: event.UpdateEvent{
628 ObjectOld: testutil.TestIssuerFrom(issuer1,
629 testutil.SetTestIssuerGeneration(80),
630 func(si *api.TestIssuer) {
631 si.SetAnnotations(map[string]string{
632 "test-annotation": "test",
633 })
634 },
635 ),
636 ObjectNew: testutil.TestIssuerFrom(issuer1,
637 testutil.SetTestIssuerGeneration(80),
638 ),
639 },
640 },
641 {
642 name: "ready-condition-added",
643 shouldReconcile: true,
644 event: event.UpdateEvent{
645 ObjectOld: testutil.TestIssuerFrom(issuer1),
646 ObjectNew: testutil.TestIssuerFrom(issuer1,
647 testutil.SetTestIssuerStatusCondition(
648 fakeClock,
649 cmapi.IssuerConditionReady,
650 cmmeta.ConditionFalse,
651 "reason",
652 "message",
653 ),
654 ),
655 },
656 },
657 }
658
659 for _, tc := range testcases {
660 tc := tc
661 t.Run(tc.name, func(t *testing.T) {
662 result := predicate.Update(tc.event)
663 require.Equal(t, tc.shouldReconcile, result)
664 })
665 }
666 }
667
View as plain text