github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/openshift/clusteroperator_controller.go (about) 1 package openshift 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 8 configv1 "github.com/openshift/api/config/v1" 9 apierrors "k8s.io/apimachinery/pkg/api/errors" 10 "k8s.io/apimachinery/pkg/runtime" 11 utilerrors "k8s.io/apimachinery/pkg/util/errors" 12 ctrl "sigs.k8s.io/controller-runtime" 13 "sigs.k8s.io/controller-runtime/pkg/builder" 14 "sigs.k8s.io/controller-runtime/pkg/handler" 15 "sigs.k8s.io/controller-runtime/pkg/reconcile" 16 "sigs.k8s.io/controller-runtime/pkg/source" 17 18 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 19 olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version" 20 ) 21 22 var ( 23 localSchemeBuilder = runtime.NewSchemeBuilder( 24 configv1.AddToScheme, 25 operatorsv1alpha1.AddToScheme, 26 ) 27 28 // AddToScheme adds all types necessary for the controller to operate. 29 AddToScheme = localSchemeBuilder.AddToScheme 30 ) 31 32 type ClusterOperatorReconciler struct { 33 *ReconcilerConfig 34 35 delayRequeue reconcile.Result 36 mutator Mutator 37 syncTracker *SyncTracker 38 co *ClusterOperator 39 } 40 41 func NewClusterOperatorReconciler(opts ...ReconcilerOption) (*ClusterOperatorReconciler, error) { 42 config := new(ReconcilerConfig) 43 config.apply(opts) 44 if err := config.complete(); err != nil { 45 return nil, err 46 } 47 48 co := NewClusterOperator(config.Name) 49 r := &ClusterOperatorReconciler{ 50 ReconcilerConfig: config, 51 delayRequeue: reconcile.Result{RequeueAfter: config.RequeueDelay}, 52 co: co, 53 syncTracker: NewSyncTracker(config.SyncCh, co.DeepCopy()), 54 } 55 56 var mutations SerialMutations 57 if config.Mutator != nil { 58 mutations = append(mutations, config.Mutator) 59 } 60 mutations = append(mutations, 61 MutateFunc(r.setVersions), 62 MutateFunc(r.setProgressing), 63 MutateFunc(r.setAvailable), 64 MutateFunc(r.setDegraded), 65 MutateFunc(r.setUpgradeable), 66 ) 67 r.mutator = mutations 68 69 return r, nil 70 } 71 72 func (r *ClusterOperatorReconciler) SetupWithManager(mgr ctrl.Manager) error { 73 if err := mgr.Add(r.syncTracker); err != nil { 74 return fmt.Errorf("failed to add %T to manager: %s", r.syncTracker, err) 75 } 76 77 bldr := ctrl.NewControllerManagedBy(mgr). 78 For(&configv1.ClusterOperator{}, builder.WithPredicates(watchName(&r.Name))). 79 WatchesRawSource(source.Channel(r.syncTracker.Events(), &handler.EnqueueRequestForObject{})) 80 81 return r.TweakBuilder(bldr).Complete(r) 82 } 83 84 func (r *ClusterOperatorReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { 85 log := r.Log.WithValues("request", req) 86 noRequeue := reconcile.Result{} 87 if req.NamespacedName.Name != r.co.GetName() { 88 // Throw away requests to reconcile all ClusterOperators but our own 89 // These should already be filtered by controller-runtime by this point 90 return noRequeue, nil 91 } 92 93 // Get the ClusterOperator 94 in := &configv1.ClusterOperator{} 95 if err := r.Client.Get(ctx, req.NamespacedName, in); err != nil { 96 if apierrors.IsNotFound(err) { 97 // The ClusterOperator is missing, let's create it 98 stripObject(r.co.ClusterOperator) 99 err = r.Client.Create(ctx, r.co.ClusterOperator) 100 } 101 102 // Transient error or successful creation, requeue 103 return r.delayRequeue, err 104 } 105 106 r.co.ClusterOperator = in.DeepCopy() 107 108 var errs []error 109 res := reconcile.Result{} 110 if err := r.mutate(ctx, r.co); err != nil { 111 // Transitive error, requeue 112 log.Error(err, "Error mutating ClusterOperator") 113 errs = append(errs, err) 114 res = r.delayRequeue 115 } 116 117 if !reflect.DeepEqual(r.co.Status, in.Status) { 118 // Status change detected, update 119 if err := r.Client.Status().Update(ctx, r.co.ClusterOperator); err != nil { 120 // Transitive error, requeue 121 errs = append(errs, err) 122 res = r.delayRequeue 123 } 124 } 125 126 return res, utilerrors.NewAggregate(errs) 127 } 128 129 func (r *ClusterOperatorReconciler) mutate(ctx context.Context, co *ClusterOperator) error { 130 return r.mutator.Mutate(ctx, co) 131 } 132 133 func (r *ClusterOperatorReconciler) setVersions(_ context.Context, co *ClusterOperator) error { 134 // If we've successfully synced, we know our operator is working properly, so we can update the version 135 if r.syncTracker.SuccessfulSyncs() > 0 && !versionsMatch(co.Status.Versions, r.TargetVersions) { 136 co.Status.Versions = r.TargetVersions 137 } 138 139 return nil 140 } 141 142 func (r *ClusterOperatorReconciler) setProgressing(_ context.Context, co *ClusterOperator) error { 143 desired := &configv1.ClusterOperatorStatusCondition{ 144 Type: configv1.OperatorProgressing, 145 LastTransitionTime: r.Now(), 146 } 147 148 if r.syncTracker.SuccessfulSyncs() > 0 && versionsMatch(co.Status.Versions, r.TargetVersions) { 149 desired.Status = configv1.ConditionFalse 150 desired.Message = fmt.Sprintf("Deployed %s", olmversion.OLMVersion) 151 } else { 152 desired.Status = configv1.ConditionTrue 153 desired.Message = fmt.Sprintf("Waiting to see update %s succeed", olmversion.OLMVersion) 154 } 155 156 current := co.GetCondition(configv1.OperatorProgressing) 157 if conditionsEqual(current, desired) { // Comparison ignores lastUpdated 158 return nil 159 } 160 161 co.SetCondition(desired) 162 163 return nil 164 } 165 166 func (r *ClusterOperatorReconciler) setAvailable(_ context.Context, co *ClusterOperator) error { 167 desired := &configv1.ClusterOperatorStatusCondition{ 168 Type: configv1.OperatorAvailable, 169 LastTransitionTime: r.Now(), 170 } 171 172 if r.syncTracker.SuccessfulSyncs() > 0 && versionsMatch(co.Status.Versions, r.TargetVersions) { 173 desired.Status = configv1.ConditionTrue 174 } else { 175 desired.Status = configv1.ConditionFalse 176 } 177 178 current := co.GetCondition(configv1.OperatorAvailable) 179 if conditionsEqual(current, desired) { // Comparison ignores lastUpdated 180 return nil 181 } 182 183 co.SetCondition(desired) 184 185 return nil 186 } 187 188 func (r *ClusterOperatorReconciler) setDegraded(_ context.Context, co *ClusterOperator) error { 189 desired := &configv1.ClusterOperatorStatusCondition{ 190 Type: configv1.OperatorDegraded, 191 LastTransitionTime: r.Now(), 192 } 193 194 if r.syncTracker.SuccessfulSyncs() > 0 && versionsMatch(co.Status.Versions, r.TargetVersions) { 195 desired.Status = configv1.ConditionFalse 196 } else { 197 desired.Status = configv1.ConditionTrue 198 desired.Message = "Waiting for updates to take effect" 199 } 200 201 current := co.GetCondition(configv1.OperatorDegraded) 202 if conditionsEqual(current, desired) { // Comparison ignores lastUpdated 203 return nil 204 } 205 206 co.SetCondition(desired) 207 208 return nil 209 } 210 211 const ( 212 IncompatibleOperatorsInstalled = "IncompatibleOperatorsInstalled" 213 ErrorCheckingOperatorCompatibility = "ErrorCheckingOperatorCompatibility" 214 ) 215 216 func (r *ClusterOperatorReconciler) setUpgradeable(ctx context.Context, co *ClusterOperator) error { 217 desired := &configv1.ClusterOperatorStatusCondition{ 218 Type: configv1.OperatorUpgradeable, 219 Status: configv1.ConditionTrue, 220 LastTransitionTime: r.Now(), 221 } 222 223 // Set upgradeable=false if (either/or): 224 // 1. OLM currently upgrading (takes priorty in the status message) 225 // 2. Operators currently installed that are incompatible with the next minor version of OpenShift 226 // 3. An error occurs while determining 2 227 var err error 228 if r.syncTracker.SuccessfulSyncs() < 1 || !versionsMatch(co.Status.Versions, r.TargetVersions) { 229 // OLM is still upgrading 230 desired.Status = configv1.ConditionFalse 231 desired.Message = "Waiting for updates to take effect" 232 } else { 233 var incompatible skews 234 incompatible, err = incompatibleOperators(ctx, r.Client) 235 if err != nil { 236 // "Fail closed" when we can't determine compatibility 237 // Note: Unspecified compatibility = universal compatibility; i.e. operators that don't specify a "maxOpenShiftVersion" property are compatible with everything. 238 desired.Status = configv1.ConditionFalse 239 desired.Reason = ErrorCheckingOperatorCompatibility 240 desired.Message = fmt.Sprintf("Encountered errors while checking compatibility with the next minor version of OpenShift: %s", err) 241 } else if len(incompatible) > 0 { 242 // Operators are installed that have incompatible and/or invalid max versions 243 desired.Status = configv1.ConditionFalse 244 desired.Reason = IncompatibleOperatorsInstalled 245 desired.Message = incompatible.String() 246 } 247 } 248 249 // Only return transient errors likely resolved by retrying immediately 250 err = transientErrors(err) 251 252 current := co.GetCondition(configv1.OperatorUpgradeable) 253 if conditionsEqual(current, desired) { // Comparison ignores lastUpdated 254 return err 255 } 256 257 co.SetCondition(desired) 258 259 return err 260 }