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

     1  package operatorstatus
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  
     8  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
     9  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv"
    10  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    11  	"github.com/sirupsen/logrus"
    12  	"k8s.io/apimachinery/pkg/labels"
    13  	"k8s.io/apimachinery/pkg/selection"
    14  )
    15  
    16  const (
    17  	// SelectorKey is the key of the label we use to identify the
    18  	// corresponding ClusterServiceVersion object related to the cluster operator.
    19  	// If we want to update a cluster operator named "package-server" then the
    20  	// corresponding ClusterServiceVersion must have the following label
    21  	//
    22  	// "olm.clusteroperator.name": "package-server"
    23  	//
    24  	SelectorKey = "olm.clusteroperator.name"
    25  )
    26  
    27  // NewCSVWatchNotificationHandler returns a new instance of csv.WatchNotification
    28  // This can be used to get notification of every CSV reconciliation request.
    29  func NewCSVWatchNotificationHandler(log *logrus.Logger, csvSet csv.SetGenerator, finder csv.ReplaceFinder, sender Sender) *handler {
    30  	logger := log.WithField("monitor", "clusteroperator")
    31  	releaseVersion := os.Getenv("RELEASE_VERSION")
    32  
    33  	return &handler{
    34  		csvSet:   csvSet,
    35  		finder:   finder,
    36  		sender:   sender,
    37  		reporter: newCSVStatusReporter(releaseVersion),
    38  		logger:   logger,
    39  	}
    40  }
    41  
    42  // csvEventContext contains all necessary information related to a notification.
    43  type csvEventContext struct {
    44  	// Name of the clusteroperator resource associated with this CSV.
    45  	Name string
    46  
    47  	// Current is the CSV for which we have received notification.
    48  	// If there is an upgrade going on, Current is set to the latest version of
    49  	// the CSV that is replacing the older version.
    50  	// For a chain like this, (v1) -> v2 -> v3 -> (v4)
    51  	// Current will be set to the CSV linked to v4.
    52  	WorkingToward *v1alpha1.ClusterServiceVersion
    53  
    54  	// Current is the CSV for which we have received notification.
    55  	Current *v1alpha1.ClusterServiceVersion
    56  
    57  	// CurrentDeleted indicates that the Current CSV has been deleted
    58  	CurrentDeleted bool
    59  }
    60  
    61  func (c *csvEventContext) GetActiveCSV() *v1alpha1.ClusterServiceVersion {
    62  	if c.WorkingToward != nil {
    63  		return c.WorkingToward
    64  	}
    65  
    66  	return c.Current
    67  }
    68  
    69  func (c *csvEventContext) String() string {
    70  	replaces := "<nil>"
    71  	if c.WorkingToward != nil {
    72  		replaces = c.WorkingToward.GetName()
    73  	}
    74  
    75  	return fmt.Sprintf("name=%s csv=%s deleted=%s replaces=%s", c.Name, c.Current.GetName(), strconv.FormatBool(c.CurrentDeleted), replaces)
    76  }
    77  
    78  type handler struct {
    79  	csvSet   csv.SetGenerator
    80  	finder   csv.ReplaceFinder
    81  	sender   Sender
    82  	reporter *csvStatusReporter
    83  	logger   *logrus.Entry
    84  }
    85  
    86  // OnAddOrUpdate is invoked when a CSV has been added or edited. We tap into
    87  // this notification and do the following:
    88  //
    89  // a. Make sure this is the CSV related to the cluster operator resource we are tracking. Otherwise, do nothing.
    90  //
    91  // b. If this is the right CSV then send it to the monitor.
    92  func (h *handler) OnAddOrUpdate(in *v1alpha1.ClusterServiceVersion) {
    93  	h.onNotification(in, false)
    94  }
    95  
    96  // OnDelete is invoked when a CSV has been deleted. We tap into
    97  // this notification and do the following:
    98  //
    99  // a. Make sure this is the CSV related to the cluster operator resource we are tracking. Otherwise, do nothing.
   100  //
   101  // b. If this is the right CSV then send it to the monitor.
   102  func (h *handler) OnDelete(in *v1alpha1.ClusterServiceVersion) {
   103  	h.onNotification(in, true)
   104  }
   105  
   106  func (h *handler) onNotification(current *v1alpha1.ClusterServiceVersion, deleted bool) {
   107  	name, matched := h.isMatchingCSV(current)
   108  	if !matched {
   109  		return
   110  	}
   111  
   112  	workingToward := h.getLatestInReplacementChain(current)
   113  	context := &csvEventContext{
   114  		Name:           name,
   115  		Current:        current,
   116  		CurrentDeleted: deleted,
   117  		WorkingToward:  workingToward,
   118  	}
   119  
   120  	if err := ownerutil.InferGroupVersionKind(current); err != nil {
   121  		h.logger.Errorf("could not set GroupVersionKind - csv=%s", current.GetName())
   122  	}
   123  
   124  	if workingToward != nil {
   125  		if err := ownerutil.InferGroupVersionKind(workingToward); err != nil {
   126  			h.logger.Errorf("could not set GroupVersionKind - csv=%s", workingToward.GetName())
   127  		}
   128  	}
   129  
   130  	h.logger.Debugf("found a matching CSV %s, sending notification", context)
   131  
   132  	notification := h.reporter.NewNotification(context)
   133  	h.sender.Send(notification)
   134  }
   135  
   136  func (h *handler) getLatestInReplacementChain(in *v1alpha1.ClusterServiceVersion) (final *v1alpha1.ClusterServiceVersion) {
   137  	requirement, _ := labels.NewRequirement(SelectorKey, selection.Exists, []string{})
   138  	selector := labels.NewSelector().Add(*requirement)
   139  	related := h.csvSet.WithNamespaceAndLabels(in.GetNamespace(), v1alpha1.CSVPhaseAny, selector)
   140  
   141  	return h.finder.GetFinalCSVInReplacing(in, related)
   142  }
   143  
   144  func (h *handler) isMatchingCSV(in *v1alpha1.ClusterServiceVersion) (name string, matched bool) {
   145  	// If it is a "copy" CSV we ignore it.
   146  	if in.IsCopied() {
   147  		return
   148  	}
   149  
   150  	// Does it have the right label?
   151  	labels := in.GetLabels()
   152  	if labels == nil {
   153  		return
   154  	}
   155  
   156  	name, _ = labels[SelectorKey]
   157  	if name == "" {
   158  		return
   159  	}
   160  
   161  	matched = true
   162  	return
   163  }