@@ -1138,13 +1138,7 @@ func (r *OperatorPolicyReconciler) musthaveOpGroup(
11381138 return false , nil , updateStatus (policy , mismatchCond ("OperatorGroup" ), missing , badExisting ), nil
11391139 }
11401140
1141- // check whether the specs match
1142- desiredUnstruct , err := runtime .DefaultUnstructuredConverter .ToUnstructured (desiredOpGroup )
1143- if err != nil {
1144- return false , nil , false , fmt .Errorf ("error converting desired OperatorGroup to an Unstructured: %w" , err )
1145- }
1146-
1147- updateNeeded , skipUpdate , err := r .mergeObjects (ctx , desiredUnstruct , & opGroup , policy .Spec .ComplianceType )
1141+ updateNeeded , skipUpdate , err := r .mergeOpGroups (ctx , desiredOpGroup , & opGroup )
11481142 if err != nil {
11491143 return false , nil , false , fmt .Errorf ("error checking if the OperatorGroup needs an update: %w" , err )
11501144 }
@@ -1420,18 +1414,7 @@ func (r *OperatorPolicyReconciler) musthaveSubscription(
14201414 }
14211415
14221416 // Subscription found; check if specs match
1423- desiredUnstruct , err := runtime .DefaultUnstructuredConverter .ToUnstructured (desiredSub )
1424- if err != nil {
1425- return nil , nil , false , fmt .Errorf ("error converting desired Subscription to an Unstructured: %w" , err )
1426- }
1427-
1428- // Clear `installPlanApproval` from the desired subscription when in inform mode - since that field can not
1429- // be set in the policy, we should not check it on the object in the cluster.
1430- if policy .Spec .RemediationAction .IsInform () {
1431- unstructured .RemoveNestedField (desiredUnstruct , "spec" , "installPlanApproval" )
1432- }
1433-
1434- updateNeeded , skipUpdate , err := r .mergeObjects (ctx , desiredUnstruct , foundSub , policy .Spec .ComplianceType )
1417+ updateNeeded , skipUpdate , err := r .mergeSubscriptions (ctx , desiredSub , foundSub , policy .Spec .RemediationAction )
14351418 if err != nil {
14361419 return nil , nil , false , fmt .Errorf ("error checking if the Subscription needs an update: %w" , err )
14371420 }
@@ -2857,14 +2840,118 @@ func opPolIdentifier(namespace, name string) depclient.ObjectIdentifier {
28572840 }
28582841}
28592842
2843+ func (r * OperatorPolicyReconciler ) mergeOpGroups (
2844+ ctx context.Context ,
2845+ desired * operatorv1.OperatorGroup ,
2846+ existing * unstructured.Unstructured ,
2847+ ) (updateNeeded , updateIsForbidden bool , err error ) {
2848+ forceUpdate := false
2849+
2850+ desiredUnstruct , err := runtime .DefaultUnstructuredConverter .ToUnstructured (desired )
2851+ if err != nil {
2852+ return false , false , fmt .Errorf ("unable to convert desired OperatorGroup to an Unstructured: %w" , err )
2853+ }
2854+
2855+ // when specified, manually handle targetNamespaces as if it's "mustonlyhave"
2856+ if len (desired .Spec .TargetNamespaces ) > 0 {
2857+ desiredTarget , _ , err := unstructured .NestedStringSlice (desiredUnstruct , "spec" , "targetNamespaces" )
2858+ if err != nil {
2859+ return false , false , fmt .Errorf ("unable to get targetNamespaces from desired OperatorGroup: %w" , err )
2860+ }
2861+
2862+ existingTarget , found , err := unstructured .NestedStringSlice (existing .Object , "spec" , "targetNamespaces" )
2863+ if err != nil {
2864+ return false , false , fmt .Errorf ("unable to get targetNamespaces from existing OperatorGroup: %w" , err )
2865+ } else if ! found {
2866+ forceUpdate = true
2867+ unstructured .SetNestedStringSlice (existing .Object ,
2868+ desired .Spec .TargetNamespaces , "spec" , "targetNamespaces" )
2869+ } else if eq , _ := deeplyEquivalent (desiredTarget , existingTarget , true ); ! eq {
2870+ forceUpdate = true
2871+ unstructured .SetNestedStringSlice (existing .Object ,
2872+ desired .Spec .TargetNamespaces , "spec" , "targetNamespaces" )
2873+ }
2874+ }
2875+
2876+ usesExpressions := desired .Spec .Selector != nil && len (desired .Spec .Selector .MatchExpressions ) > 0
2877+ usesLabels := desired .Spec .Selector != nil && len (desired .Spec .Selector .MatchLabels ) > 0
2878+
2879+ // when specified, manually handle selector as if it's "mustonlyhave"
2880+ if usesExpressions || usesLabels {
2881+ desiredSelector , _ , err := unstructured .NestedMap (desiredUnstruct , "spec" , "selector" )
2882+ if err != nil {
2883+ return false , false , fmt .Errorf ("unable to get selector from desired OperatorGroup: %w" , err )
2884+ }
2885+
2886+ existingSelector , found , err := unstructured .NestedMap (existing .Object , "spec" , "selector" )
2887+ if err != nil {
2888+ return false , false , fmt .Errorf ("unable to get selector from existing OperatorGroup: %w" , err )
2889+ } else if ! found {
2890+ forceUpdate = true
2891+ unstructured .SetNestedMap (existing .Object , desiredSelector , "spec" , "selector" )
2892+ } else if eq , _ := deeplyEquivalent (desiredSelector , existingSelector , true ); ! eq {
2893+ forceUpdate = true
2894+ unstructured .SetNestedMap (existing .Object , desiredSelector , "spec" , "selector" )
2895+ }
2896+ }
2897+
2898+ updateNeeded , forbidden , err := r .mergeObjects (ctx , desiredUnstruct , existing )
2899+
2900+ return updateNeeded || forceUpdate , forbidden , err
2901+ }
2902+
2903+ func (r * OperatorPolicyReconciler ) mergeSubscriptions (
2904+ ctx context.Context ,
2905+ desired * operatorv1alpha1.Subscription ,
2906+ existing * unstructured.Unstructured ,
2907+ action policyv1.RemediationAction ,
2908+ ) (updateNeeded , updateIsForbidden bool , err error ) {
2909+ forceUpdate := false
2910+
2911+ desiredUnstruct , err := runtime .DefaultUnstructuredConverter .ToUnstructured (desired )
2912+ if err != nil {
2913+ return false , false , fmt .Errorf ("unable to convert desired Subscription to an Unstructured: %w" , err )
2914+ }
2915+
2916+ // when specified, manually handle config as if it's "mustonlyhave"
2917+ if desired .Spec .Config != nil {
2918+ desiredConfig , _ , err := unstructured .NestedMap (desiredUnstruct , "spec" , "config" )
2919+ if err != nil {
2920+ return false , false , fmt .Errorf ("unable to get config from desired Subscription" )
2921+ }
2922+
2923+ if len (desiredConfig ) > 0 {
2924+ existingConfig , found , err := unstructured .NestedMap (existing .Object , "spec" , "config" )
2925+ if err != nil {
2926+ return false , false , fmt .Errorf ("unable to get config from existing Subscription" )
2927+ } else if ! found {
2928+ forceUpdate = true
2929+ unstructured .SetNestedMap (existing .Object , desiredConfig , "spec" , "config" )
2930+ } else if eq , _ := deeplyEquivalent (desiredConfig , existingConfig , true ); ! eq {
2931+ forceUpdate = true
2932+ unstructured .SetNestedMap (existing .Object , desiredConfig , "spec" , "config" )
2933+ }
2934+ }
2935+ }
2936+
2937+ // Clear `installPlanApproval` from the desired subscription when in inform mode - since that field can not
2938+ // be set in the policy, we should not check it on the object in the cluster.
2939+ if action .IsInform () {
2940+ unstructured .RemoveNestedField (desiredUnstruct , "spec" , "installPlanApproval" )
2941+ }
2942+
2943+ updateNeeded , forbidden , err := r .mergeObjects (ctx , desiredUnstruct , existing )
2944+
2945+ return updateNeeded || forceUpdate , forbidden , err
2946+ }
2947+
28602948// mergeObjects takes fields from the desired object and sets/merges them on the
28612949// existing object. It checks and returns whether an update is really necessary
28622950// with a server-side dry-run.
28632951func (r * OperatorPolicyReconciler ) mergeObjects (
28642952 ctx context.Context ,
2865- desired map [string ]interface {} ,
2953+ desired map [string ]any ,
28662954 existing * unstructured.Unstructured ,
2867- complianceType policyv1.ComplianceType ,
28682955) (updateNeeded , updateIsForbidden bool , err error ) {
28692956 desiredObj := & unstructured.Unstructured {Object : desired }
28702957
@@ -2873,7 +2960,7 @@ func (r *OperatorPolicyReconciler) mergeObjects(
28732960 removeFieldsForComparison (existingObjectCopy )
28742961
28752962 //nolint:dogsled
2876- _ , errMsg , updateNeeded , _ , _ := handleKeys (desiredObj , existing , existingObjectCopy , complianceType , "" )
2963+ _ , errMsg , updateNeeded , _ , _ := handleKeys (desiredObj , existing , existingObjectCopy , policyv1 . MustHave , "" )
28772964 if errMsg != "" {
28782965 return updateNeeded , false , errors .New (errMsg )
28792966 }
0 commit comments