github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/metrics/metrics.go (about) 1 package metrics 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/prometheus/client_golang/prometheus" 8 "google.golang.org/grpc/connectivity" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/labels" 11 12 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 13 v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" 14 ) 15 16 const ( 17 NameLabel = "name" 18 InstalledLabel = "installed" 19 NamespaceLabel = "namespace" 20 ChannelLabel = "channel" 21 VersionLabel = "version" 22 PhaseLabel = "phase" 23 ReasonLabel = "reason" 24 PackageLabel = "package" 25 Outcome = "outcome" 26 Succeeded = "succeeded" 27 Failed = "failed" 28 ApprovalLabel = "approval" 29 WarningLabel = "warning" 30 GVKLabel = "gvk" 31 ) 32 33 type MetricsProvider interface { 34 HandleMetrics() error 35 } 36 37 type metricsCSV struct { 38 lister v1alpha1.ClusterServiceVersionLister 39 } 40 41 func NewMetricsCSV(lister v1alpha1.ClusterServiceVersionLister) MetricsProvider { 42 return &metricsCSV{lister} 43 } 44 45 func (m *metricsCSV) HandleMetrics() error { 46 cList, err := m.lister.List(labels.Everything()) 47 if err != nil { 48 return err 49 } 50 csvCount.Set(float64(len(cList))) 51 return nil 52 } 53 54 type metricsInstallPlan struct { 55 lister v1alpha1.InstallPlanLister 56 } 57 58 func NewMetricsInstallPlan(lister v1alpha1.InstallPlanLister) MetricsProvider { 59 return &metricsInstallPlan{lister} 60 } 61 62 func (m *metricsInstallPlan) HandleMetrics() error { 63 cList, err := m.lister.InstallPlans(metav1.NamespaceAll).List(labels.Everything()) 64 if err != nil { 65 return err 66 } 67 installPlanCount.Set(float64(len(cList))) 68 return nil 69 } 70 71 type metricsSubscription struct { 72 lister v1alpha1.SubscriptionLister 73 } 74 75 func NewMetricsSubscription(lister v1alpha1.SubscriptionLister) MetricsProvider { 76 return &metricsSubscription{lister} 77 } 78 79 func (m *metricsSubscription) HandleMetrics() error { 80 cList, err := m.lister.Subscriptions(metav1.NamespaceAll).List(labels.Everything()) 81 if err != nil { 82 return err 83 } 84 subscriptionCount.Set(float64(len(cList))) 85 return nil 86 } 87 88 type metricsCatalogSource struct { 89 lister v1alpha1.CatalogSourceLister 90 } 91 92 func NewMetricsCatalogSource(lister v1alpha1.CatalogSourceLister) MetricsProvider { 93 return &metricsCatalogSource{lister} 94 } 95 96 func (m *metricsCatalogSource) HandleMetrics() error { 97 cList, err := m.lister.CatalogSources(metav1.NamespaceAll).List(labels.Everything()) 98 if err != nil { 99 return err 100 } 101 catalogSourceCount.Set(float64(len(cList))) 102 return nil 103 } 104 105 type MetricsNil struct{} 106 107 func NewMetricsNil() MetricsProvider { 108 return &MetricsNil{} 109 } 110 111 func (*MetricsNil) HandleMetrics() error { 112 return nil 113 } 114 115 // To add new metrics: 116 // 1. Register new metrics in Register() below. 117 // 2. Add appropriate metric updates in HandleMetrics (or elsewhere instead). 118 var ( 119 csvCount = prometheus.NewGauge( 120 prometheus.GaugeOpts{ 121 Name: "csv_count", 122 Help: "Number of CSVs successfully registered", 123 }, 124 ) 125 126 installPlanCount = prometheus.NewGauge( 127 prometheus.GaugeOpts{ 128 Name: "install_plan_count", 129 Help: "Number of install plans", 130 }, 131 ) 132 133 subscriptionCount = prometheus.NewGauge( 134 prometheus.GaugeOpts{ 135 Name: "subscription_count", 136 Help: "Number of subscriptions", 137 }, 138 ) 139 140 catalogSourceCount = prometheus.NewGauge( 141 prometheus.GaugeOpts{ 142 Name: "catalog_source_count", 143 Help: "Number of catalog sources", 144 }, 145 ) 146 147 catalogSourceReady = prometheus.NewGaugeVec( 148 prometheus.GaugeOpts{ 149 Name: "catalogsource_ready", 150 Help: "State of a CatalogSource. 1 indicates that the CatalogSource is in a READY state. 0 indicates CatalogSource is in a Non READY state.", 151 }, 152 []string{NamespaceLabel, NameLabel}, 153 ) 154 155 // exported since it's not handled by HandleMetrics 156 CSVUpgradeCount = prometheus.NewCounter( 157 prometheus.CounterOpts{ 158 Name: "csv_upgrade_count", 159 Help: "Monotonic count of CSV upgrades", 160 }, 161 ) 162 163 SubscriptionSyncCount = prometheus.NewCounterVec( 164 prometheus.CounterOpts{ 165 Name: "subscription_sync_total", 166 Help: "Monotonic count of subscription syncs", 167 }, 168 []string{NameLabel, InstalledLabel, ChannelLabel, PackageLabel, ApprovalLabel}, 169 ) 170 171 csvSucceeded = prometheus.NewGaugeVec( 172 prometheus.GaugeOpts{ 173 Name: "csv_succeeded", 174 Help: "Successful CSV install", 175 }, 176 []string{NamespaceLabel, NameLabel, VersionLabel}, 177 ) 178 179 csvAbnormal = prometheus.NewGaugeVec( 180 prometheus.GaugeOpts{ 181 Name: "csv_abnormal", 182 Help: "CSV is not installed", 183 }, 184 []string{NamespaceLabel, NameLabel, VersionLabel, PhaseLabel, ReasonLabel}, 185 ) 186 187 dependencyResolutionSummary = prometheus.NewSummaryVec( 188 prometheus.SummaryOpts{ 189 Name: "olm_resolution_duration_seconds", 190 Help: "The duration of a dependency resolution attempt", 191 Objectives: map[float64]float64{0.95: 0.05, 0.9: 0.01, 0.99: 0.001}, 192 }, 193 []string{Outcome}, 194 ) 195 196 installPlanWarningCount = prometheus.NewCounter( 197 prometheus.CounterOpts{ 198 Name: "installplan_warnings_total", 199 Help: "monotonic count of resources that generated warnings when applied as part of an InstallPlan (for example, due to deprecation)", 200 }, 201 ) 202 203 subscriptionSyncCounters = newSubscriptionSyncCounter() 204 ) 205 206 // subscriptionSyncCounter keeps a record of the Prometheus counters emitted by 207 // Subscription objects. The key of a record is the Subscription name, while the value 208 // is struct containing label values used in the counter. Read and Write access are 209 // protected by mutex. 210 type subscriptionSyncCounter struct { 211 counters map[string]subscriptionSyncLabelValues 212 countersLock sync.RWMutex 213 } 214 215 func newSubscriptionSyncCounter() subscriptionSyncCounter { 216 return subscriptionSyncCounter{ 217 counters: make(map[string]subscriptionSyncLabelValues), 218 } 219 } 220 221 func (s *subscriptionSyncCounter) setValues(key string, val subscriptionSyncLabelValues) { 222 s.countersLock.Lock() 223 defer s.countersLock.Unlock() 224 s.counters[key] = val 225 } 226 227 func (s *subscriptionSyncCounter) readValues(key string) (subscriptionSyncLabelValues, bool) { 228 s.countersLock.RLock() 229 defer s.countersLock.RUnlock() 230 val, ok := s.counters[key] 231 return val, ok 232 } 233 234 type subscriptionSyncLabelValues struct { 235 installedCSV string 236 pkg string 237 channel string 238 approvalStrategy string 239 } 240 241 func RegisterOLM() { 242 prometheus.MustRegister(csvCount) 243 prometheus.MustRegister(csvSucceeded) 244 prometheus.MustRegister(csvAbnormal) 245 prometheus.MustRegister(CSVUpgradeCount) 246 } 247 248 func RegisterCatalog() { 249 prometheus.MustRegister(installPlanCount) 250 prometheus.MustRegister(subscriptionCount) 251 prometheus.MustRegister(catalogSourceCount) 252 prometheus.MustRegister(catalogSourceReady) 253 prometheus.MustRegister(SubscriptionSyncCount) 254 prometheus.MustRegister(dependencyResolutionSummary) 255 prometheus.MustRegister(installPlanWarningCount) 256 } 257 258 func CounterForSubscription(name, installedCSV, channelName, packageName, planApprovalStrategy string) prometheus.Counter { 259 return SubscriptionSyncCount.WithLabelValues(name, installedCSV, channelName, packageName, planApprovalStrategy) 260 } 261 262 func RegisterCatalogSourceState(name, namespace string, state connectivity.State) { 263 switch state { 264 case connectivity.Ready: 265 catalogSourceReady.WithLabelValues(namespace, name).Set(1) 266 default: 267 catalogSourceReady.WithLabelValues(namespace, name).Set(0) 268 } 269 } 270 271 func DeleteCatalogSourceStateMetric(name, namespace string) { 272 catalogSourceReady.DeleteLabelValues(namespace, name) 273 } 274 275 func DeleteCSVMetric(oldCSV *operatorsv1alpha1.ClusterServiceVersion) { 276 // Delete the old CSV metrics 277 csvAbnormal.DeleteLabelValues(oldCSV.Namespace, oldCSV.Name, oldCSV.Spec.Version.String(), string(oldCSV.Status.Phase), string(oldCSV.Status.Reason)) 278 csvSucceeded.DeleteLabelValues(oldCSV.Namespace, oldCSV.Name, oldCSV.Spec.Version.String()) 279 } 280 281 func EmitCSVMetric(oldCSV *operatorsv1alpha1.ClusterServiceVersion, newCSV *operatorsv1alpha1.ClusterServiceVersion) { 282 if oldCSV == nil || newCSV == nil { 283 return 284 } 285 286 // Don't update the metric for copies 287 if newCSV.Status.Reason == operatorsv1alpha1.CSVReasonCopied { 288 return 289 } 290 291 // Delete the old CSV metrics 292 csvAbnormal.DeleteLabelValues(oldCSV.Namespace, oldCSV.Name, oldCSV.Spec.Version.String(), string(oldCSV.Status.Phase), string(oldCSV.Status.Reason)) 293 294 // Get the phase of the new CSV 295 newCSVPhase := string(newCSV.Status.Phase) 296 csvSucceededGauge := csvSucceeded.WithLabelValues(newCSV.Namespace, newCSV.Name, newCSV.Spec.Version.String()) 297 if newCSVPhase == string(operatorsv1alpha1.CSVPhaseSucceeded) { 298 csvSucceededGauge.Set(1) 299 } else { 300 csvSucceededGauge.Set(0) 301 csvAbnormal.WithLabelValues(newCSV.Namespace, newCSV.Name, newCSV.Spec.Version.String(), string(newCSV.Status.Phase), string(newCSV.Status.Reason)).Set(1) 302 } 303 } 304 305 func EmitSubMetric(sub *operatorsv1alpha1.Subscription) { 306 if sub.Spec == nil { 307 return 308 } 309 310 SubscriptionSyncCount.WithLabelValues(sub.GetName(), sub.Status.InstalledCSV, sub.Spec.Channel, sub.Spec.Package, string(sub.Spec.InstallPlanApproval)).Inc() 311 if _, present := subscriptionSyncCounters.readValues(sub.GetName()); !present { 312 subscriptionSyncCounters.setValues(sub.GetName(), subscriptionSyncLabelValues{ 313 installedCSV: sub.Status.InstalledCSV, 314 pkg: sub.Spec.Package, 315 channel: sub.Spec.Channel, 316 approvalStrategy: string(sub.Spec.InstallPlanApproval), 317 }) 318 } 319 } 320 321 func DeleteSubsMetric(sub *operatorsv1alpha1.Subscription) { 322 if sub.Spec == nil { 323 return 324 } 325 SubscriptionSyncCount.DeleteLabelValues(sub.GetName(), sub.Status.InstalledCSV, sub.Spec.Channel, sub.Spec.Package, string(sub.Spec.InstallPlanApproval)) 326 } 327 328 func UpdateSubsSyncCounterStorage(sub *operatorsv1alpha1.Subscription) { 329 if sub.Spec == nil { 330 return 331 } 332 counterValues, _ := subscriptionSyncCounters.readValues(sub.GetName()) 333 approvalStrategy := string(sub.Spec.InstallPlanApproval) 334 335 if sub.Spec.Channel != counterValues.channel || 336 sub.Spec.Package != counterValues.pkg || 337 sub.Status.InstalledCSV != counterValues.installedCSV || 338 approvalStrategy != counterValues.approvalStrategy { 339 // Delete metric will label values of old Subscription first 340 SubscriptionSyncCount.DeleteLabelValues(sub.GetName(), counterValues.installedCSV, counterValues.channel, counterValues.pkg, counterValues.approvalStrategy) 341 342 counterValues.installedCSV = sub.Status.InstalledCSV 343 counterValues.pkg = sub.Spec.Package 344 counterValues.channel = sub.Spec.Channel 345 counterValues.approvalStrategy = approvalStrategy 346 347 subscriptionSyncCounters.setValues(sub.GetName(), counterValues) 348 } 349 } 350 351 func RegisterDependencyResolutionSuccess(duration time.Duration) { 352 dependencyResolutionSummary.WithLabelValues(Succeeded).Observe(duration.Seconds()) 353 } 354 355 func RegisterDependencyResolutionFailure(duration time.Duration) { 356 dependencyResolutionSummary.WithLabelValues(Failed).Observe(duration.Seconds()) 357 } 358 359 func EmitInstallPlanWarning() { 360 installPlanWarningCount.Inc() 361 }