66
77 "github.com/google/go-cmp/cmp"
88 "github.com/google/go-cmp/cmp/cmpopts"
9+ "sigs.k8s.io/controller-runtime/pkg/client"
910
1011 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
1112
@@ -26,7 +27,7 @@ func (r *reconciler) ensureServiceMeshOperatorSubscription(ctx context.Context)
2627 return false , nil , err
2728 }
2829
29- desired , err := desiredSubscription (name )
30+ desired , err := desiredSubscription (name , r . config . GatewayAPIOperatorChannel , r . config . GatewayAPIOperatorVersion )
3031 if err != nil {
3132 return have , current , err
3233 }
@@ -48,18 +49,19 @@ func (r *reconciler) ensureServiceMeshOperatorSubscription(ctx context.Context)
4849}
4950
5051// desiredSubscription returns the desired subscription.
51- func desiredSubscription (name types.NamespacedName ) (* operatorsv1alpha1.Subscription , error ) {
52+ func desiredSubscription (name types.NamespacedName , gwapiOperatorChannel , gwapiOperatorVersion string ) (* operatorsv1alpha1.Subscription , error ) {
5253 subscription := operatorsv1alpha1.Subscription {
5354 ObjectMeta : metav1.ObjectMeta {
5455 Namespace : name .Namespace ,
5556 Name : name .Name ,
5657 },
5758 Spec : & operatorsv1alpha1.SubscriptionSpec {
58- Channel : "stable" ,
59- InstallPlanApproval : operatorsv1alpha1 .ApprovalAutomatic ,
59+ Channel : gwapiOperatorChannel ,
60+ InstallPlanApproval : operatorsv1alpha1 .ApprovalManual ,
6061 Package : "servicemeshoperator" ,
6162 CatalogSource : "redhat-operators" ,
6263 CatalogSourceNamespace : "openshift-marketplace" ,
64+ StartingCSV : gwapiOperatorVersion ,
6365 },
6466 }
6567 return & subscription , nil
@@ -115,3 +117,92 @@ func subscriptionChanged(current, expected *operatorsv1alpha1.Subscription) (boo
115117
116118 return true , updated
117119}
120+
121+ // ensureServiceMeshOperatorInstallPlan attempts to ensure that the install plan for the appropriate OSSM operator
122+ // version is approved.
123+ func (r * reconciler ) ensureServiceMeshOperatorInstallPlan (ctx context.Context ) (bool , * operatorsv1alpha1.InstallPlan , error ) {
124+ haveInstallPlan , current , err := r .currentInstallPlan (ctx )
125+ if err != nil {
126+ return false , nil , err
127+ }
128+ switch {
129+ case ! haveInstallPlan :
130+ // The OLM operator creates the initial InstallPlan, so if it doesn't exist yet or it's been deleted, do nothing
131+ // and let the OLM operator handle it.
132+ return false , nil , nil
133+ case haveInstallPlan :
134+ desired := desiredInstallPlan (current )
135+ if updated , err := r .updateInstallPlan (ctx , current , desired ); err != nil {
136+ return true , current , err
137+ } else if updated {
138+ return r .currentInstallPlan (ctx )
139+ }
140+ }
141+ return false , current , nil
142+ }
143+
144+ // currentInstallPlan returns the InstallPlan that describes installing the expected version of the GatewayAPI
145+ // implementation, if one exists.
146+ func (r * reconciler ) currentInstallPlan (ctx context.Context ) (bool , * operatorsv1alpha1.InstallPlan , error ) {
147+ _ , subscription , err := r .currentSubscription (ctx , operatorcontroller .ServiceMeshSubscriptionName ())
148+ if err != nil {
149+ return false , nil , err
150+ }
151+ installPlans := & operatorsv1alpha1.InstallPlanList {}
152+ if err := r .client .List (ctx , installPlans , client .InNamespace (operatorcontroller .OpenshiftOperatorNamespace )); err != nil {
153+ return false , nil , err
154+ }
155+ if installPlans == nil || len (installPlans .Items ) == 0 {
156+ return false , nil , nil
157+ }
158+ for _ , installPlan := range installPlans .Items {
159+ if len (installPlan .OwnerReferences ) == 0 || len (installPlan .Spec .ClusterServiceVersionNames ) == 0 {
160+ continue
161+ }
162+ for _ , ownerRef := range installPlan .OwnerReferences {
163+ if ownerRef .UID == subscription .UID {
164+ for _ , csvName := range installPlan .Spec .ClusterServiceVersionNames {
165+ if csvName == r .config .GatewayAPIOperatorVersion {
166+ return true , & installPlan , nil
167+ }
168+ }
169+ }
170+ }
171+ }
172+ // No valid InstallPlan found.
173+ return false , nil , nil
174+ }
175+
176+ // desiredInstallPlan returns a version of the expected InstallPlan that is approved.
177+ func desiredInstallPlan (current * operatorsv1alpha1.InstallPlan ) * operatorsv1alpha1.InstallPlan {
178+ desired := current .DeepCopy ()
179+ desired .Spec .Approved = true
180+ return desired
181+ }
182+
183+ // updateInstallPlan updates an existing InstallPlan if it differs from the desired state.
184+ func (r * reconciler ) updateInstallPlan (ctx context.Context , current , desired * operatorsv1alpha1.InstallPlan ) (bool , error ) {
185+ changed , updated := installPlanChanged (current , desired )
186+ if ! changed {
187+ return false , nil
188+ }
189+ diff := cmp .Diff (current .Spec , updated .Spec , cmpopts .EquateEmpty ())
190+ if err := r .client .Update (ctx , updated ); err != nil {
191+ return false , fmt .Errorf ("failed to update InstallPlan %s/%s: %w" , current .Namespace , current .Name , err )
192+ }
193+ log .Info ("updated InstallPlan" , "namespace" , updated .Namespace , "name" , updated .Name , "diff" , diff )
194+ return true , nil
195+ }
196+
197+ // installPlanChanged returns a Boolean indicating whether the current InstallPlan matches the expected InstallPlan and
198+ // the updated InstallPlan if they do not match.
199+ func installPlanChanged (current , expected * operatorsv1alpha1.InstallPlan ) (bool , * operatorsv1alpha1.InstallPlan ) {
200+ if cmp .Equal (current .Spec , expected .Spec , cmpopts .EquateEmpty ()) {
201+ return false , nil
202+ }
203+
204+ updated := current .DeepCopy ()
205+ updated .Spec = expected .Spec
206+
207+ return true , updated
208+ }
0 commit comments