@@ -68,12 +68,17 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T)
6868 "olm.operatorframework.io/bundle-version" : "1.2.0" ,
6969 "olm.operatorframework.io/package-name" : "my-package" ,
7070 },
71+ // Both owner and package-name labels are required:
72+ // - owner: identifies which ClusterExtension owns this revision (for label-based queries)
73+ // - package-name: needed by BoxcutterRevisionStatesGetter to populate RevisionMetadata.Package field
7174 Labels : map [string ]string {
72- "olm.operatorframework.io/owner" : "test-123" ,
75+ "olm.operatorframework.io/owner" : "test-123" ,
76+ "olm.operatorframework.io/package-name" : "my-package" ,
7377 },
7478 },
7579 Spec : ocv1.ClusterExtensionRevisionSpec {
76- Revision : 1 ,
80+ LifecycleState : ocv1 .ClusterExtensionRevisionLifecycleStateActive ,
81+ Revision : 1 ,
7782 Phases : []ocv1.ClusterExtensionRevisionPhase {
7883 {
7984 Name : "deploy" ,
@@ -758,6 +763,99 @@ func TestBoxcutter_Apply(t *testing.T) {
758763 assert .Contains (t , latest .Spec .Previous , ocv1.ClusterExtensionRevisionPrevious {Name : "rev-4" })
759764 },
760765 },
766+ {
767+ name : "annotation-only update (same phases, different annotations)" ,
768+ mockBuilder : & mockBundleRevisionBuilder {
769+ makeRevisionFunc : func (bundleFS fs.FS , ext * ocv1.ClusterExtension , objectLabels , revisionAnnotations map [string ]string ) (* ocv1.ClusterExtensionRevision , error ) {
770+ revisionLabels := map [string ]string {
771+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
772+ }
773+ if pkg , ok := revisionAnnotations [labels .PackageNameKey ]; ok {
774+ revisionLabels [labels .PackageNameKey ] = pkg
775+ }
776+ return & ocv1.ClusterExtensionRevision {
777+ ObjectMeta : metav1.ObjectMeta {
778+ Annotations : revisionAnnotations ,
779+ Labels : revisionLabels ,
780+ },
781+ Spec : ocv1.ClusterExtensionRevisionSpec {
782+ Phases : []ocv1.ClusterExtensionRevisionPhase {
783+ {
784+ Name : string (applier .PhaseDeploy ),
785+ Objects : []ocv1.ClusterExtensionRevisionObject {
786+ {
787+ Object : unstructured.Unstructured {
788+ Object : map [string ]interface {}{
789+ "apiVersion" : "v1" ,
790+ "kind" : "ConfigMap" ,
791+ "metadata" : map [string ]interface {}{
792+ "name" : "test-cm" ,
793+ },
794+ },
795+ },
796+ },
797+ },
798+ },
799+ },
800+ },
801+ }, nil
802+ },
803+ },
804+ existingObjs : []client.Object {
805+ ext ,
806+ & ocv1.ClusterExtensionRevision {
807+ ObjectMeta : metav1.ObjectMeta {
808+ Name : "test-ext-1" ,
809+ Annotations : map [string ]string {
810+ labels .BundleVersionKey : "1.0.0" ,
811+ labels .PackageNameKey : "test-package" ,
812+ },
813+ Labels : map [string ]string {
814+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
815+ labels .PackageNameKey : "test-package" ,
816+ },
817+ },
818+ Spec : ocv1.ClusterExtensionRevisionSpec {
819+ Revision : 1 ,
820+ Phases : []ocv1.ClusterExtensionRevisionPhase {
821+ {
822+ Name : string (applier .PhaseDeploy ),
823+ Objects : []ocv1.ClusterExtensionRevisionObject {
824+ {
825+ Object : unstructured.Unstructured {
826+ Object : map [string ]interface {}{
827+ "apiVersion" : "v1" ,
828+ "kind" : "ConfigMap" ,
829+ "metadata" : map [string ]interface {}{
830+ "name" : "test-cm" ,
831+ },
832+ },
833+ },
834+ },
835+ },
836+ },
837+ },
838+ },
839+ },
840+ },
841+ validate : func (t * testing.T , c client.Client ) {
842+ revList := & ocv1.ClusterExtensionRevisionList {}
843+ err := c .List (context .Background (), revList , client.MatchingLabels {controllers .ClusterExtensionRevisionOwnerLabel : ext .Name })
844+ require .NoError (t , err )
845+ // Should still be only 1 revision (in-place update, not new revision)
846+ require .Len (t , revList .Items , 1 )
847+
848+ rev := revList .Items [0 ]
849+ assert .Equal (t , "test-ext-1" , rev .Name )
850+ assert .Equal (t , int64 (1 ), rev .Spec .Revision )
851+ // Verify annotations were updated
852+ assert .Equal (t , "1.0.1" , rev .Annotations [labels .BundleVersionKey ])
853+ assert .Equal (t , "test-package" , rev .Annotations [labels .PackageNameKey ])
854+ // Verify labels still have package name
855+ assert .Equal (t , ext .Name , rev .Labels [controllers .ClusterExtensionRevisionOwnerLabel ])
856+ assert .Equal (t , "test-package" , rev .Labels [labels .PackageNameKey ])
857+ },
858+ },
761859 }
762860
763861 for _ , tc := range testCases {
@@ -780,7 +878,15 @@ func TestBoxcutter_Apply(t *testing.T) {
780878 testFS := fstest.MapFS {}
781879
782880 // Execute
783- installSucceeded , installStatus , err := boxcutter .Apply (t .Context (), testFS , ext , nil , nil )
881+ revisionAnnotations := map [string ]string {}
882+ if tc .name == "annotation-only update (same phases, different annotations)" {
883+ // For annotation-only update test, pass NEW annotations
884+ revisionAnnotations = map [string ]string {
885+ labels .BundleVersionKey : "1.0.1" ,
886+ labels .PackageNameKey : "test-package" ,
887+ }
888+ }
889+ installSucceeded , installStatus , err := boxcutter .Apply (t .Context (), testFS , ext , nil , revisionAnnotations )
784890
785891 // Assert
786892 if tc .expectedErr != "" {
@@ -808,13 +914,17 @@ func TestBoxcutter_Apply(t *testing.T) {
808914
809915func TestBoxcutterStorageMigrator (t * testing.T ) {
810916 t .Run ("creates revision" , func (t * testing.T ) {
917+ testScheme := runtime .NewScheme ()
918+ require .NoError (t , ocv1 .AddToScheme (testScheme ))
919+
811920 brb := & mockBundleRevisionBuilder {}
812921 mag := & mockActionGetter {}
813922 client := & clientMock {}
814923 sm := & applier.BoxcutterStorageMigrator {
815924 RevisionGenerator : brb ,
816925 ActionClientGetter : mag ,
817926 Client : client ,
927+ Scheme : testScheme ,
818928 }
819929
820930 ext := & ocv1.ClusterExtension {
@@ -836,13 +946,17 @@ func TestBoxcutterStorageMigrator(t *testing.T) {
836946 })
837947
838948 t .Run ("does not create revision when revisions exist" , func (t * testing.T ) {
949+ testScheme := runtime .NewScheme ()
950+ require .NoError (t , ocv1 .AddToScheme (testScheme ))
951+
839952 brb := & mockBundleRevisionBuilder {}
840953 mag := & mockActionGetter {}
841954 client := & clientMock {}
842955 sm := & applier.BoxcutterStorageMigrator {
843956 RevisionGenerator : brb ,
844957 ActionClientGetter : mag ,
845958 Client : client ,
959+ Scheme : testScheme ,
846960 }
847961
848962 ext := & ocv1.ClusterExtension {
@@ -866,6 +980,9 @@ func TestBoxcutterStorageMigrator(t *testing.T) {
866980 })
867981
868982 t .Run ("does not create revision when no helm release" , func (t * testing.T ) {
983+ testScheme := runtime .NewScheme ()
984+ require .NoError (t , ocv1 .AddToScheme (testScheme ))
985+
869986 brb := & mockBundleRevisionBuilder {}
870987 mag := & mockActionGetter {
871988 getClientErr : driver .ErrReleaseNotFound ,
@@ -875,6 +992,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) {
875992 RevisionGenerator : brb ,
876993 ActionClientGetter : mag ,
877994 Client : client ,
995+ Scheme : testScheme ,
878996 }
879997
880998 ext := & ocv1.ClusterExtension {
@@ -905,7 +1023,15 @@ func (m *mockBundleRevisionBuilder) GenerateRevisionFromHelmRelease(
9051023 helmRelease * release.Release , ext * ocv1.ClusterExtension ,
9061024 objectLabels map [string ]string ,
9071025) (* ocv1.ClusterExtensionRevision , error ) {
908- return nil , nil
1026+ return & ocv1.ClusterExtensionRevision {
1027+ ObjectMeta : metav1.ObjectMeta {
1028+ Name : "test-revision" ,
1029+ Labels : map [string ]string {
1030+ controllers .ClusterExtensionRevisionOwnerLabel : ext .Name ,
1031+ },
1032+ },
1033+ Spec : ocv1.ClusterExtensionRevisionSpec {},
1034+ }, nil
9091035}
9101036
9111037type clientMock struct {
@@ -921,3 +1047,24 @@ func (m *clientMock) Create(ctx context.Context, obj client.Object, opts ...clie
9211047 args := m .Called (ctx , obj , opts )
9221048 return args .Error (0 )
9231049}
1050+
1051+ func (m * clientMock ) Status () client.StatusWriter {
1052+ return & statusWriterMock {mock : & m .Mock }
1053+ }
1054+
1055+ type statusWriterMock struct {
1056+ mock * mock.Mock
1057+ }
1058+
1059+ func (s * statusWriterMock ) Update (ctx context.Context , obj client.Object , opts ... client.SubResourceUpdateOption ) error {
1060+ // Status updates are expected during migration - return success by default
1061+ return nil
1062+ }
1063+
1064+ func (s * statusWriterMock ) Patch (ctx context.Context , obj client.Object , patch client.Patch , opts ... client.SubResourcePatchOption ) error {
1065+ return nil
1066+ }
1067+
1068+ func (s * statusWriterMock ) Create (ctx context.Context , obj client.Object , subResource client.Object , opts ... client.SubResourceCreateOption ) error {
1069+ return nil
1070+ }
0 commit comments