@@ -44,15 +44,19 @@ func newTestReconciler(mockClient *mockclient.MockClient, cache *mockrepository.
4444// readyObjectMeta returns a standard ObjectMeta with finalizer and ownerReference already set,
4545// representing a PR that has already been reconciled at least once.
4646func readyObjectMeta (name , namespace , repoName string ) metav1.ObjectMeta {
47+ controller := true
48+ blockOwnerDeletion := true
4749 return metav1.ObjectMeta {
4850 Name : name ,
4951 Namespace : namespace ,
5052 Finalizers : []string {porchv1alpha2 .PackageRevisionFinalizer },
5153 OwnerReferences : []metav1.OwnerReference {{
52- APIVersion : configapi .GroupVersion .Identifier (),
53- Kind : configapi .TypeRepository .Kind ,
54- Name : repoName ,
55- UID : "repo-uid" ,
54+ APIVersion : configapi .GroupVersion .Identifier (),
55+ Kind : configapi .TypeRepository .Kind ,
56+ Name : repoName ,
57+ UID : "repo-uid" ,
58+ Controller : & controller ,
59+ BlockOwnerDeletion : & blockOwnerDeletion ,
5660 }},
5761 }
5862}
@@ -118,6 +122,10 @@ func TestReconcileFinalizerAddedWhenMissing(t *testing.T) {
118122 assert .Equal (t , "my-repo" , pr .OwnerReferences [0 ].Name )
119123 assert .Equal (t , types .UID ("repo-uid-123" ), pr .OwnerReferences [0 ].UID )
120124 assert .Equal (t , configapi .TypeRepository .Kind , pr .OwnerReferences [0 ].Kind )
125+ require .NotNil (t , pr .OwnerReferences [0 ].Controller )
126+ assert .True (t , * pr .OwnerReferences [0 ].Controller )
127+ require .NotNil (t , pr .OwnerReferences [0 ].BlockOwnerDeletion )
128+ assert .True (t , * pr .OwnerReferences [0 ].BlockOwnerDeletion )
121129 }).Return (nil )
122130
123131 r := newTestReconciler (mockClient , mockrepository .NewMockContentCache (t ))
@@ -681,6 +689,44 @@ func TestReconcileOwnerRefAlreadySet(t *testing.T) {
681689 ctx := t .Context ()
682690 req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "test-pr" , Namespace : "default" }}
683691
692+ controller := true
693+ blockOwnerDeletion := true
694+ pr := & porchv1alpha2.PackageRevision {
695+ ObjectMeta : metav1.ObjectMeta {
696+ Name : "test-pr" ,
697+ Namespace : "default" ,
698+ Finalizers : []string {porchv1alpha2 .PackageRevisionFinalizer },
699+ OwnerReferences : []metav1.OwnerReference {{
700+ APIVersion : configapi .GroupVersion .Identifier (),
701+ Kind : configapi .TypeRepository .Kind ,
702+ Name : "my-repo" ,
703+ UID : "repo-uid-123" ,
704+ Controller : & controller ,
705+ BlockOwnerDeletion : & blockOwnerDeletion ,
706+ }},
707+ },
708+ Spec : porchv1alpha2.PackageRevisionSpec {RepositoryName : "my-repo" },
709+ }
710+
711+ mockClient := mockclient .NewMockClient (t )
712+ mockClient .EXPECT ().Get (mock .Anything , req .NamespacedName , mock .AnythingOfType ("*v1alpha2.PackageRevision" )).
713+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
714+ * obj .(* porchv1alpha2.PackageRevision ) = * pr
715+ }).Return (nil )
716+ // No Patch, no repo Get — both finalizer and ownerRef already present.
717+
718+ r := newTestReconciler (mockClient , mockrepository .NewMockContentCache (t ))
719+ result , err := r .Reconcile (ctx , req )
720+
721+ assert .NoError (t , err )
722+ assert .Equal (t , ctrl.Result {}, result )
723+ }
724+
725+ func TestReconcileOwnerRefIncompleteGetsUpdated (t * testing.T ) {
726+ ctx := t .Context ()
727+ req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "test-pr" , Namespace : "default" }}
728+
729+ // Seed an ownerRef with matching Kind/Name/UID but nil boolean pointers.
684730 pr := & porchv1alpha2.PackageRevision {
685731 ObjectMeta : metav1.ObjectMeta {
686732 Name : "test-pr" ,
@@ -691,17 +737,154 @@ func TestReconcileOwnerRefAlreadySet(t *testing.T) {
691737 Kind : configapi .TypeRepository .Kind ,
692738 Name : "my-repo" ,
693739 UID : "repo-uid-123" ,
740+ // Controller and BlockOwnerDeletion intentionally nil
694741 }},
695742 },
696743 Spec : porchv1alpha2.PackageRevisionSpec {RepositoryName : "my-repo" },
697744 }
698745
746+ repo := & configapi.Repository {
747+ ObjectMeta : metav1.ObjectMeta {Name : "my-repo" , Namespace : "default" , UID : "repo-uid-123" },
748+ }
749+
699750 mockClient := mockclient .NewMockClient (t )
700751 mockClient .EXPECT ().Get (mock .Anything , req .NamespacedName , mock .AnythingOfType ("*v1alpha2.PackageRevision" )).
701752 Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
702753 * obj .(* porchv1alpha2.PackageRevision ) = * pr
703754 }).Return (nil )
704- // No Patch, no repo Get — both finalizer and ownerRef already present.
755+ mockClient .EXPECT ().Get (mock .Anything , types.NamespacedName {Namespace : "default" , Name : "my-repo" }, mock .AnythingOfType ("*v1alpha1.Repository" )).
756+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
757+ * obj .(* configapi.Repository ) = * repo
758+ }).Return (nil )
759+ mockClient .EXPECT ().Patch (mock .Anything , mock .AnythingOfType ("*v1alpha2.PackageRevision" ), mock .Anything ).
760+ Run (func (_ context.Context , obj client.Object , _ client.Patch , _ ... client.PatchOption ) {
761+ patched := obj .(* porchv1alpha2.PackageRevision )
762+ require .Len (t , patched .OwnerReferences , 1 )
763+ ref := patched .OwnerReferences [0 ]
764+ assert .Equal (t , "my-repo" , ref .Name )
765+ assert .Equal (t , types .UID ("repo-uid-123" ), ref .UID )
766+ assert .Equal (t , configapi .TypeRepository .Kind , ref .Kind )
767+ require .NotNil (t , ref .Controller , "Controller should be set after self-healing" )
768+ assert .True (t , * ref .Controller )
769+ require .NotNil (t , ref .BlockOwnerDeletion , "BlockOwnerDeletion should be set after self-healing" )
770+ assert .True (t , * ref .BlockOwnerDeletion )
771+ }).Return (nil )
772+
773+ r := newTestReconciler (mockClient , mockrepository .NewMockContentCache (t ))
774+ result , err := r .Reconcile (ctx , req )
775+
776+ assert .NoError (t , err )
777+ assert .Equal (t , ctrl.Result {}, result )
778+ }
779+
780+ func TestReconcileOwnerRefWrongAPIVersionGetsUpdated (t * testing.T ) {
781+ ctx := t .Context ()
782+ req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "test-pr" , Namespace : "default" }}
783+
784+ controller := true
785+ blockOwnerDeletion := true
786+ // Seed an ownerRef with correct Kind/Name/UID and booleans but wrong APIVersion.
787+ pr := & porchv1alpha2.PackageRevision {
788+ ObjectMeta : metav1.ObjectMeta {
789+ Name : "test-pr" ,
790+ Namespace : "default" ,
791+ Finalizers : []string {porchv1alpha2 .PackageRevisionFinalizer },
792+ OwnerReferences : []metav1.OwnerReference {{
793+ APIVersion : "wrong.api/v1" ,
794+ Kind : configapi .TypeRepository .Kind ,
795+ Name : "my-repo" ,
796+ UID : "repo-uid-123" ,
797+ Controller : & controller ,
798+ BlockOwnerDeletion : & blockOwnerDeletion ,
799+ }},
800+ },
801+ Spec : porchv1alpha2.PackageRevisionSpec {RepositoryName : "my-repo" },
802+ }
803+
804+ repo := & configapi.Repository {
805+ ObjectMeta : metav1.ObjectMeta {Name : "my-repo" , Namespace : "default" , UID : "repo-uid-123" },
806+ }
807+
808+ mockClient := mockclient .NewMockClient (t )
809+ mockClient .EXPECT ().Get (mock .Anything , req .NamespacedName , mock .AnythingOfType ("*v1alpha2.PackageRevision" )).
810+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
811+ * obj .(* porchv1alpha2.PackageRevision ) = * pr
812+ }).Return (nil )
813+ mockClient .EXPECT ().Get (mock .Anything , types.NamespacedName {Namespace : "default" , Name : "my-repo" }, mock .AnythingOfType ("*v1alpha1.Repository" )).
814+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
815+ * obj .(* configapi.Repository ) = * repo
816+ }).Return (nil )
817+ mockClient .EXPECT ().Patch (mock .Anything , mock .AnythingOfType ("*v1alpha2.PackageRevision" ), mock .Anything ).
818+ Run (func (_ context.Context , obj client.Object , _ client.Patch , _ ... client.PatchOption ) {
819+ patched := obj .(* porchv1alpha2.PackageRevision )
820+ require .Len (t , patched .OwnerReferences , 1 )
821+ ref := patched .OwnerReferences [0 ]
822+ assert .Equal (t , configapi .GroupVersion .Identifier (), ref .APIVersion , "APIVersion should be corrected" )
823+ assert .Equal (t , "my-repo" , ref .Name )
824+ assert .Equal (t , types .UID ("repo-uid-123" ), ref .UID )
825+ require .NotNil (t , ref .Controller )
826+ assert .True (t , * ref .Controller )
827+ require .NotNil (t , ref .BlockOwnerDeletion )
828+ assert .True (t , * ref .BlockOwnerDeletion )
829+ }).Return (nil )
830+
831+ r := newTestReconciler (mockClient , mockrepository .NewMockContentCache (t ))
832+ result , err := r .Reconcile (ctx , req )
833+
834+ assert .NoError (t , err )
835+ assert .Equal (t , ctrl.Result {}, result )
836+ }
837+
838+ func TestReconcileOwnerRefEmptyUIDGetsUpdated (t * testing.T ) {
839+ ctx := t .Context ()
840+ req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "test-pr" , Namespace : "default" }}
841+
842+ controller := true
843+ blockOwnerDeletion := true
844+ // Seed an ownerRef with correct Kind/Name/APIVersion and booleans but empty UID.
845+ pr := & porchv1alpha2.PackageRevision {
846+ ObjectMeta : metav1.ObjectMeta {
847+ Name : "test-pr" ,
848+ Namespace : "default" ,
849+ Finalizers : []string {porchv1alpha2 .PackageRevisionFinalizer },
850+ OwnerReferences : []metav1.OwnerReference {{
851+ APIVersion : configapi .GroupVersion .Identifier (),
852+ Kind : configapi .TypeRepository .Kind ,
853+ Name : "my-repo" ,
854+ UID : "" ,
855+ Controller : & controller ,
856+ BlockOwnerDeletion : & blockOwnerDeletion ,
857+ }},
858+ },
859+ Spec : porchv1alpha2.PackageRevisionSpec {RepositoryName : "my-repo" },
860+ }
861+
862+ repo := & configapi.Repository {
863+ ObjectMeta : metav1.ObjectMeta {Name : "my-repo" , Namespace : "default" , UID : "repo-uid-123" },
864+ }
865+
866+ mockClient := mockclient .NewMockClient (t )
867+ mockClient .EXPECT ().Get (mock .Anything , req .NamespacedName , mock .AnythingOfType ("*v1alpha2.PackageRevision" )).
868+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
869+ * obj .(* porchv1alpha2.PackageRevision ) = * pr
870+ }).Return (nil )
871+ mockClient .EXPECT ().Get (mock .Anything , types.NamespacedName {Namespace : "default" , Name : "my-repo" }, mock .AnythingOfType ("*v1alpha1.Repository" )).
872+ Run (func (_ context.Context , _ types.NamespacedName , obj client.Object , _ ... client.GetOption ) {
873+ * obj .(* configapi.Repository ) = * repo
874+ }).Return (nil )
875+ mockClient .EXPECT ().Patch (mock .Anything , mock .AnythingOfType ("*v1alpha2.PackageRevision" ), mock .Anything ).
876+ Run (func (_ context.Context , obj client.Object , _ client.Patch , _ ... client.PatchOption ) {
877+ patched := obj .(* porchv1alpha2.PackageRevision )
878+ require .Len (t , patched .OwnerReferences , 1 )
879+ ref := patched .OwnerReferences [0 ]
880+ assert .Equal (t , configapi .GroupVersion .Identifier (), ref .APIVersion )
881+ assert .Equal (t , "my-repo" , ref .Name )
882+ assert .Equal (t , types .UID ("repo-uid-123" ), ref .UID , "UID should be populated after self-healing" )
883+ require .NotNil (t , ref .Controller )
884+ assert .True (t , * ref .Controller )
885+ require .NotNil (t , ref .BlockOwnerDeletion )
886+ assert .True (t , * ref .BlockOwnerDeletion )
887+ }).Return (nil )
705888
706889 r := newTestReconciler (mockClient , mockrepository .NewMockContentCache (t ))
707890 result , err := r .Reconcile (ctx , req )
@@ -745,15 +928,19 @@ func TestReconcileEmptyLifecycle(t *testing.T) {
745928 ctx := t .Context ()
746929 req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "test-pr" , Namespace : "default" }}
747930
931+ controller := true
932+ blockOwnerDeletion := true
748933 pr := & porchv1alpha2.PackageRevision {
749934 ObjectMeta : metav1.ObjectMeta {
750935 Name : "test-pr" , Namespace : "default" ,
751936 Finalizers : []string {porchv1alpha2 .PackageRevisionFinalizer },
752937 OwnerReferences : []metav1.OwnerReference {{
753- APIVersion : configapi .GroupVersion .Identifier (),
754- Kind : configapi .TypeRepository .Kind ,
755- Name : "my-repo" ,
756- UID : "repo-uid" ,
938+ APIVersion : configapi .GroupVersion .Identifier (),
939+ Kind : configapi .TypeRepository .Kind ,
940+ Name : "my-repo" ,
941+ UID : "repo-uid" ,
942+ Controller : & controller ,
943+ BlockOwnerDeletion : & blockOwnerDeletion ,
757944 }},
758945 },
759946 Spec : porchv1alpha2.PackageRevisionSpec {RepositoryName : "my-repo" },
@@ -1562,11 +1749,15 @@ func TestReconcileRenderErrorSetsStatus(t *testing.T) {
15621749 pr .Spec .Lifecycle = porchv1alpha2 .PackageRevisionLifecycleDraft
15631750 pr .Spec .RepositoryName = "my-repo"
15641751 pr .Finalizers = []string {porchv1alpha2 .PackageRevisionFinalizer }
1752+ controller := true
1753+ blockOwnerDeletion := true
15651754 pr .OwnerReferences = []metav1.OwnerReference {{
1566- APIVersion : configapi .GroupVersion .Identifier (),
1567- Kind : configapi .TypeRepository .Kind ,
1568- Name : "my-repo" ,
1569- UID : "repo-uid" ,
1755+ APIVersion : configapi .GroupVersion .Identifier (),
1756+ Kind : configapi .TypeRepository .Kind ,
1757+ Name : "my-repo" ,
1758+ UID : "repo-uid" ,
1759+ Controller : & controller ,
1760+ BlockOwnerDeletion : & blockOwnerDeletion ,
15701761 }}
15711762
15721763 mockClient := mockclient .NewMockClient (t )
0 commit comments