@@ -19,6 +19,7 @@ import (
19
19
"sigs.k8s.io/cli-utils/pkg/apply/event"
20
20
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
21
21
"sigs.k8s.io/cli-utils/pkg/common"
22
+ "sigs.k8s.io/cli-utils/pkg/inventory"
22
23
"sigs.k8s.io/cli-utils/pkg/object"
23
24
"sigs.k8s.io/cli-utils/pkg/testutil"
24
25
)
@@ -253,7 +254,7 @@ func TestApplyTask_DryRun(t *testing.T) {
253
254
}
254
255
defer func () { applyOptionsFactoryFunc = oldAO }()
255
256
getClusterObj = func (d dynamic.Interface , info * resource.Info ) (* unstructured.Unstructured , error ) {
256
- return tc .objs [0 ], nil
257
+ return addOwningInventory ( tc .objs [0 ], "id" ) , nil
257
258
}
258
259
259
260
applyTask := & ApplyTask {
@@ -389,7 +390,7 @@ func TestApplyTaskWithError(t *testing.T) {
389
390
defer func () { applyOptionsFactoryFunc = oldAO }()
390
391
391
392
getClusterObj = func (d dynamic.Interface , info * resource.Info ) (* unstructured.Unstructured , error ) {
392
- return tc .objs [0 ], nil
393
+ return addOwningInventory ( tc .objs [0 ], "id" ) , nil
393
394
}
394
395
applyTask := & ApplyTask {
395
396
Objects : tc .objs ,
@@ -433,6 +434,187 @@ func TestApplyTaskWithError(t *testing.T) {
433
434
}
434
435
}
435
436
437
+ var deployment = toUnstructured (map [string ]interface {}{
438
+ "apiVersion" : "apps/v1" ,
439
+ "kind" : "Deployment" ,
440
+ "metadata" : map [string ]interface {}{
441
+ "name" : "deploy" ,
442
+ "namespace" : "default" ,
443
+ },
444
+ })
445
+
446
+ var deploymentObjMetadata = []object.ObjMetadata {
447
+ {
448
+ GroupKind : schema.GroupKind {
449
+ Group : "apps" ,
450
+ Kind : "Deployment" ,
451
+ },
452
+ Name : "deploy" ,
453
+ Namespace : "default" ,
454
+ },
455
+ }
456
+
457
+ func TestApplyTaskWithDifferentInventoryAnnotation (t * testing.T ) {
458
+ testCases := map [string ]struct {
459
+ obj * unstructured.Unstructured
460
+ clusterObj * unstructured.Unstructured
461
+ policy inventory.InventoryPolicy
462
+ expectedObjects []object.ObjMetadata
463
+ expectedEvents []event.Event
464
+ }{
465
+ "InventoryPolicyMustMatch with object doesn't exist on cluster - Can Apply" : {
466
+ obj : deployment ,
467
+ clusterObj : nil ,
468
+ policy : inventory .InventoryPolicyMustMatch ,
469
+ expectedObjects : deploymentObjMetadata ,
470
+ expectedEvents : []event.Event {},
471
+ },
472
+ "InventoryPolicyMustMatch with object annotation is empty - Can't Apply" : {
473
+ obj : deployment ,
474
+ clusterObj : removeOwningInventory (deployment ),
475
+ policy : inventory .InventoryPolicyMustMatch ,
476
+ expectedObjects : nil ,
477
+ expectedEvents : []event.Event {
478
+ {
479
+ Type : event .ApplyType ,
480
+ ApplyEvent : event.ApplyEvent {
481
+ Error : inventory .NewNeedAdoptionError (
482
+ fmt .Errorf ("can't adopt an object without the annotation config.k8s.io/owning-inventory" )),
483
+ },
484
+ },
485
+ },
486
+ },
487
+ "InventoryPolicyMustMatch with object annotation doesn't match - Can't Apply" : {
488
+ obj : deployment ,
489
+ clusterObj : addOwningInventory (deployment , "unmatchd" ),
490
+ policy : inventory .InventoryPolicyMustMatch ,
491
+ expectedObjects : nil ,
492
+ expectedEvents : []event.Event {
493
+ {
494
+ Type : event .ApplyType ,
495
+ ApplyEvent : event.ApplyEvent {
496
+ Error : inventory .NewInventoryOverlapError (
497
+ fmt .Errorf ("can't apply the resource since its annotation config.k8s.io/owning-inventory is a different inventory object" )),
498
+ },
499
+ },
500
+ },
501
+ },
502
+ "InventoryPolicyMustMatch with object annotation matches - Can Apply" : {
503
+ obj : deployment ,
504
+ clusterObj : addOwningInventory (deployment , "id" ),
505
+ policy : inventory .InventoryPolicyMustMatch ,
506
+ expectedObjects : deploymentObjMetadata ,
507
+ expectedEvents : nil ,
508
+ },
509
+ "AdoptIfNoInventory with object doesn't exist on cluster - Can Apply" : {
510
+ obj : deployment ,
511
+ clusterObj : nil ,
512
+ policy : inventory .AdoptIfNoInventory ,
513
+ expectedObjects : deploymentObjMetadata ,
514
+ expectedEvents : []event.Event {},
515
+ },
516
+ "AdoptIfNoInventory with object annotation is empty - Can Apply" : {
517
+ obj : deployment ,
518
+ clusterObj : removeOwningInventory (deployment ),
519
+ policy : inventory .AdoptIfNoInventory ,
520
+ expectedObjects : deploymentObjMetadata ,
521
+ expectedEvents : []event.Event {},
522
+ },
523
+ "AdoptIfNoInventory with object annotation doesn't match - Can't Apply" : {
524
+ obj : deployment ,
525
+ clusterObj : addOwningInventory (deployment , "notmatch" ),
526
+ policy : inventory .AdoptIfNoInventory ,
527
+ expectedObjects : nil ,
528
+ expectedEvents : []event.Event {
529
+ {
530
+ Type : event .ApplyType ,
531
+ ApplyEvent : event.ApplyEvent {
532
+ Error : inventory .NewInventoryOverlapError (
533
+ fmt .Errorf ("can't apply the resource since its annotation config.k8s.io/owning-inventory is a different inventory object" )),
534
+ },
535
+ },
536
+ },
537
+ },
538
+ "AdoptIfNoInventory with object annotation matches - Can Apply" : {
539
+ obj : deployment ,
540
+ clusterObj : addOwningInventory (deployment , "id" ),
541
+ policy : inventory .AdoptIfNoInventory ,
542
+ expectedObjects : deploymentObjMetadata ,
543
+ expectedEvents : []event.Event {},
544
+ },
545
+ "AdoptAll with object doesn't exist on cluster - Can Apply" : {
546
+ obj : deployment ,
547
+ clusterObj : nil ,
548
+ policy : inventory .AdoptAll ,
549
+ expectedObjects : deploymentObjMetadata ,
550
+ expectedEvents : []event.Event {},
551
+ },
552
+ }
553
+
554
+ for tn , tc := range testCases {
555
+ drs := common .DryRunNone
556
+ t .Run (tn , func (t * testing.T ) {
557
+ eventChannel := make (chan event.Event )
558
+ taskContext := taskrunner .NewTaskContext (eventChannel )
559
+
560
+ restMapper := testutil .NewFakeRESTMapper (schema.GroupVersionKind {
561
+ Group : "apps" ,
562
+ Version : "v1" ,
563
+ Kind : "Deployment" ,
564
+ })
565
+
566
+ ao := & fakeApplyOptions {}
567
+ oldAO := applyOptionsFactoryFunc
568
+ applyOptionsFactoryFunc = func (chan event.Event , common.ServerSideOptions , common.DryRunStrategy , util.Factory ) (applyOptions , dynamic.Interface , error ) {
569
+ return ao , nil , nil
570
+ }
571
+ defer func () { applyOptionsFactoryFunc = oldAO }()
572
+
573
+ getClusterObj = func (d dynamic.Interface , info * resource.Info ) (* unstructured.Unstructured , error ) {
574
+ return tc .clusterObj , nil
575
+ }
576
+ applyTask := & ApplyTask {
577
+ Objects : []* unstructured.Unstructured {tc .obj },
578
+ InfoHelper : & fakeInfoHelper {},
579
+ Mapper : restMapper ,
580
+ DryRunStrategy : drs ,
581
+ InvInfo : & fakeInventoryInfo {},
582
+ InventoryPolicy : tc .policy ,
583
+ }
584
+
585
+ var events []event.Event
586
+ var wg sync.WaitGroup
587
+ wg .Add (1 )
588
+ go func () {
589
+ defer wg .Done ()
590
+ for msg := range eventChannel {
591
+ events = append (events , msg )
592
+ }
593
+ }()
594
+
595
+ applyTask .Start (taskContext )
596
+ <- taskContext .TaskChannel ()
597
+ close (eventChannel )
598
+ wg .Wait ()
599
+
600
+ assert .Equal (t , len (tc .expectedObjects ), len (ao .passedObjects ))
601
+ for i , obj := range ao .passedObjects {
602
+ actual , err := object .InfoToObjMeta (obj )
603
+ if err != nil {
604
+ continue
605
+ }
606
+ assert .Equal (t , tc .expectedObjects [i ], actual )
607
+ }
608
+
609
+ assert .Equal (t , len (tc .expectedEvents ), len (events ))
610
+ for i , e := range events {
611
+ assert .Equal (t , tc .expectedEvents [i ].Type , e .Type )
612
+ assert .Equal (t , tc .expectedEvents [i ].ApplyEvent .Error .Error (), e .ApplyEvent .Error .Error ())
613
+ }
614
+ })
615
+ }
616
+ }
617
+
436
618
func toUnstructured (obj map [string ]interface {}) * unstructured.Unstructured {
437
619
return & unstructured.Unstructured {
438
620
Object : obj ,
@@ -452,6 +634,9 @@ func toUnstructureds(rss []resourceInfo) []*unstructured.Unstructured {
452
634
"namespace" : rs .namespace ,
453
635
"uid" : string (rs .uid ),
454
636
"generation" : rs .generation ,
637
+ "annotations" : map [string ]interface {}{
638
+ "config.k8s.io/owning-inventory" : "id" ,
639
+ },
455
640
},
456
641
},
457
642
})
@@ -507,3 +692,32 @@ func (fi *fakeInventoryInfo) Namespace() string {
507
692
func (fi * fakeInventoryInfo ) ID () string {
508
693
return "id"
509
694
}
695
+
696
+ func addOwningInventory (obj * unstructured.Unstructured , id string ) * unstructured.Unstructured {
697
+ if obj == nil {
698
+ return nil
699
+ }
700
+ newObj := obj .DeepCopy ()
701
+ annotations := newObj .GetAnnotations ()
702
+ if len (annotations ) == 0 {
703
+ annotations = make (map [string ]string )
704
+ }
705
+
706
+ annotations ["config.k8s.io/owning-inventory" ] = id
707
+ newObj .SetAnnotations (annotations )
708
+ return newObj
709
+ }
710
+
711
+ func removeOwningInventory (obj * unstructured.Unstructured ) * unstructured.Unstructured {
712
+ if obj == nil {
713
+ return nil
714
+ }
715
+ newObj := obj .DeepCopy ()
716
+ annotations := newObj .GetAnnotations ()
717
+ if len (annotations ) == 0 {
718
+ return newObj
719
+ }
720
+ delete (annotations , "config.k8s.io/owning-inventory" )
721
+ newObj .SetAnnotations (annotations )
722
+ return newObj
723
+ }
0 commit comments