@@ -18,7 +18,9 @@ package controllers
1818
1919import (
2020 "context"
21+ "fmt"
2122
23+ "github.com/operator-framework/deppy/pkg/deppy/solver"
2224 rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
2325 "k8s.io/apimachinery/pkg/api/equality"
2426 apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -35,6 +37,7 @@ import (
3537 operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
3638 "github.com/operator-framework/operator-controller/internal/resolution"
3739 "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/bundles_and_dependencies"
40+ "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
3841)
3942
4043// OperatorReconciler reconciles a Operator object
@@ -105,54 +108,87 @@ func checkForUnexpectedFieldChange(a, b operatorsv1alpha1.Operator) bool {
105108
106109// Helper function to do the actual reconcile
107110func (r * OperatorReconciler ) reconcile (ctx context.Context , op * operatorsv1alpha1.Operator ) (ctrl.Result , error ) {
108- // define condition parameters
109- var status = metav1 .ConditionTrue
110- var reason = operatorsv1alpha1 .ReasonResolutionSucceeded
111- var message = "resolution was successful"
112-
113111 // run resolution
114112 solution , err := r .Resolver .Resolve (ctx )
115113 if err != nil {
116- status = metav1 .ConditionFalse
117- reason = operatorsv1alpha1 .ReasonResolutionFailed
118- message = err .Error ()
119- } else {
120- // extract package bundle path from resolved variable
121- for _ , variable := range solution .SelectedVariables () {
122- switch v := variable .(type ) {
123- case * bundles_and_dependencies.BundleVariable :
124- packageName , err := v .BundleEntity ().PackageName ()
125- if err != nil {
126- return ctrl.Result {}, err
127- }
128- if packageName == op .Spec .PackageName {
129- bundlePath , err := v .BundleEntity ().BundlePath ()
130- if err != nil {
131- return ctrl.Result {}, err
132- }
133- // Create bundleDeployment if not found or Update if needed
134- dep := r .generateExpectedBundleDeployment (* op , bundlePath )
135- if err := r .ensureBundleDeployment (ctx , dep ); err != nil {
136- return ctrl.Result {}, err
137- }
138- break
139- }
140- }
141- }
114+ apimeta .SetStatusCondition (& op .Status .Conditions , metav1.Condition {
115+ Type : operatorsv1alpha1 .TypeReady ,
116+ Status : metav1 .ConditionFalse ,
117+ Reason : operatorsv1alpha1 .ReasonResolutionFailed ,
118+ Message : err .Error (),
119+ ObservedGeneration : op .GetGeneration (),
120+ })
121+ return ctrl.Result {}, err
122+ }
123+
124+ // lookup the bundle entity in the solution that corresponds to the
125+ // Operator's desired package name.
126+ bundleEntity , err := r .getBundleEntityFromSolution (solution , op .Spec .PackageName )
127+ if err != nil {
128+ apimeta .SetStatusCondition (& op .Status .Conditions , metav1.Condition {
129+ Type : operatorsv1alpha1 .TypeReady ,
130+ Status : metav1 .ConditionFalse ,
131+ Reason : operatorsv1alpha1 .ReasonBundleLookupFailed ,
132+ Message : err .Error (),
133+ ObservedGeneration : op .GetGeneration (),
134+ })
135+ return ctrl.Result {}, err
136+ }
137+
138+ // Get the bundle image reference for the bundle
139+ bundleImage , err := bundleEntity .BundlePath ()
140+ if err != nil {
141+ apimeta .SetStatusCondition (& op .Status .Conditions , metav1.Condition {
142+ Type : operatorsv1alpha1 .TypeReady ,
143+ Status : metav1 .ConditionFalse ,
144+ Reason : operatorsv1alpha1 .ReasonBundleLookupFailed ,
145+ Message : err .Error (),
146+ ObservedGeneration : op .GetGeneration (),
147+ })
148+ return ctrl.Result {}, err
149+ }
150+
151+ // Ensure a BundleDeployment exists with its bundle source from the bundle
152+ // image we just looked up in the solution.
153+ dep := r .generateExpectedBundleDeployment (* op , bundleImage )
154+ if err := r .ensureBundleDeployment (ctx , dep ); err != nil {
155+ apimeta .SetStatusCondition (& op .Status .Conditions , metav1.Condition {
156+ Type : operatorsv1alpha1 .TypeReady ,
157+ Status : metav1 .ConditionFalse ,
158+ Reason : operatorsv1alpha1 .ReasonBundleDeploymentFailed ,
159+ Message : err .Error (),
160+ ObservedGeneration : op .GetGeneration (),
161+ })
162+ return ctrl.Result {}, err
142163 }
143164
144165 // update operator status
145166 apimeta .SetStatusCondition (& op .Status .Conditions , metav1.Condition {
146167 Type : operatorsv1alpha1 .TypeReady ,
147- Status : status ,
148- Reason : reason ,
149- Message : message ,
168+ Status : metav1 . ConditionTrue ,
169+ Reason : operatorsv1alpha1 . ReasonResolutionSucceeded ,
170+ Message : "resolution was successful" ,
150171 ObservedGeneration : op .GetGeneration (),
151172 })
152-
153173 return ctrl.Result {}, nil
154174}
155175
176+ func (r * OperatorReconciler ) getBundleEntityFromSolution (solution * solver.Solution , packageName string ) (* entity.BundleEntity , error ) {
177+ for _ , variable := range solution .SelectedVariables () {
178+ switch v := variable .(type ) {
179+ case * bundles_and_dependencies.BundleVariable :
180+ entityPkgName , err := v .BundleEntity ().PackageName ()
181+ if err != nil {
182+ return nil , err
183+ }
184+ if packageName == entityPkgName {
185+ return v .BundleEntity (), nil
186+ }
187+ }
188+ }
189+ return nil , fmt .Errorf ("entity for package %q not found in solution" , packageName )
190+ }
191+
156192func (r * OperatorReconciler ) generateExpectedBundleDeployment (o operatorsv1alpha1.Operator , bundlePath string ) * unstructured.Unstructured {
157193 // We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
158194 // If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
0 commit comments