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 }