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  }