github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/operatorstatus/csv_reporter.go (about)

     1  package operatorstatus
     2  
     3  import (
     4  	"fmt"
     5  
     6  	configv1 "github.com/openshift/api/config/v1"
     7  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
     8  	"k8s.io/utils/clock"
     9  )
    10  
    11  const (
    12  	// versionName reflects the name of the version CVO expects in Status.
    13  	versionName = "operator"
    14  
    15  	reasonCSVSucceeded = "ClusterServiceVersionSucceeded"
    16  
    17  	reasonCSVNotSucceeded = "ClusterServiceVersionNotSucceeded"
    18  
    19  	reasonCSVDeleted = "ClusterServiceVersionDeleted"
    20  )
    21  
    22  // newCSVStatusReporter returns a new instance of CSVStatusReporter
    23  func newCSVStatusReporter(releaseVersion string) *csvStatusReporter {
    24  	return &csvStatusReporter{
    25  		clock:          &clock.RealClock{},
    26  		releaseVersion: releaseVersion,
    27  	}
    28  }
    29  
    30  // csvStatusReporter provides the logic for initializing ClusterOperator and
    31  // ClusterOperatorStatus types.
    32  type csvStatusReporter struct {
    33  	clock          clock.Clock
    34  	releaseVersion string
    35  }
    36  
    37  // NewNotification prepares a new notification event to be sent to the monitor.
    38  func (r *csvStatusReporter) NewNotification(context *csvEventContext) NotificationFunc {
    39  	return func() (name string, mutator MutatorFunc) {
    40  		name = context.Name
    41  		mutator = func(existing *configv1.ClusterOperatorStatus) (new *configv1.ClusterOperatorStatus) {
    42  			new = r.GetNewStatus(existing, context)
    43  			return
    44  		}
    45  
    46  		return
    47  	}
    48  }
    49  
    50  // GetNewStatus returns the expected new status based on the notification context.
    51  // We cover the following scenarios:
    52  // a. Fresh install of an operator (v1), no previous version is installed.
    53  //  1. Working toward v1
    54  //  2. v1 successfully installed
    55  //  3. v1 deploy failed
    56  //  4. v1 has been removed, post successful install.
    57  //
    58  // b. Newer version of the operator (v2) is being installed (v1 is already installed)
    59  //  1. Working toward v2. (v1 is being replaced, it waits until v2 successfully
    60  //     is successfully installed) Is v1 available while v2 is being installed?
    61  //  2. When v1 is uninstalled, we remove the old version from status.
    62  //  3. When v3 is installed successfully, we add the new version (v2) to status.
    63  func (r *csvStatusReporter) GetNewStatus(existing *configv1.ClusterOperatorStatus, context *csvEventContext) (status *configv1.ClusterOperatorStatus) {
    64  	builder := &Builder{
    65  		clock:  r.clock,
    66  		status: existing,
    67  	}
    68  
    69  	defer func() {
    70  		status = builder.GetStatus()
    71  	}()
    72  
    73  	// We don't monitor whether the CSV backed operator is in degraded status.
    74  	builder.WithDegraded(configv1.ConditionFalse)
    75  
    76  	// CSV status won't block cluster upgrades
    77  	builder.WithUpgradeable(configv1.ConditionTrue, "Safe to upgrade")
    78  
    79  	// A CSV has been deleted.
    80  	if context.CurrentDeleted {
    81  		csv := context.Current
    82  		gvk := csv.GetObjectKind().GroupVersionKind()
    83  
    84  		builder.WithoutVersion(csv.GetName(), csv.Spec.Version.String()).
    85  			WithoutRelatedObject(gvk.Group, clusterServiceVersionResource, csv.GetNamespace(), csv.GetName())
    86  
    87  		if context.WorkingToward == nil {
    88  			builder.WithProgressing(configv1.ConditionFalse, fmt.Sprintf("Uninstalled version %s", csv.Spec.Version)).
    89  				WithAvailable(configv1.ConditionFalse, fmt.Sprintf("Uninstalled version %s", csv.Spec.Version), reasonCSVDeleted)
    90  
    91  			return
    92  		}
    93  	}
    94  
    95  	// It's either a fresh install or an upgrade.
    96  	csv := context.GetActiveCSV()
    97  	name := csv.GetName()
    98  	version := csv.Spec.Version
    99  	phase := csv.Status.Phase
   100  
   101  	gvk := csv.GetObjectKind().GroupVersionKind()
   102  	builder.WithRelatedObject("", "namespaces", "", csv.GetNamespace()).
   103  		WithRelatedObject(gvk.Group, clusterServiceVersionResource, csv.GetNamespace(), csv.GetName())
   104  
   105  	switch phase {
   106  	case v1alpha1.CSVPhaseSucceeded:
   107  		builder.WithAvailable(configv1.ConditionTrue, fmt.Sprintf("ClusterServiceVersion %v/%v observed in phase %v", csv.Namespace, csv.Name, csv.Status.Phase), reasonCSVSucceeded)
   108  	case v1alpha1.CSVPhaseFailed:
   109  		builder.WithAvailable(configv1.ConditionFalse, fmt.Sprintf("ClusterServiceVersion %v/%v observed in phase %v with reason: %v, message: %v", csv.Namespace, csv.Name, csv.Status.Phase, csv.Status.Reason, csv.Status.Message), reasonCSVNotSucceeded)
   110  	}
   111  
   112  	switch phase {
   113  	case v1alpha1.CSVPhaseSucceeded:
   114  		builder.WithProgressing(configv1.ConditionFalse, fmt.Sprintf("Deployed version %s", version))
   115  	case v1alpha1.CSVPhaseFailed:
   116  		builder.WithProgressing(configv1.ConditionFalse, fmt.Sprintf("Failed to deploy %s", version))
   117  	default:
   118  		builder.WithProgressing(configv1.ConditionTrue, fmt.Sprintf("Working toward %s", version))
   119  	}
   120  
   121  	if phase == v1alpha1.CSVPhaseSucceeded {
   122  		builder.WithVersion(versionName, r.releaseVersion)
   123  		builder.WithVersion(name, version.String())
   124  	}
   125  
   126  	return
   127  }