/* Copyright 2020 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package metrics import ( "fmt" "time" flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1" "github.com/prometheus/client_golang/prometheus" ) // Deployment strategies const ( CanaryStrategy = "canary" BlueGreenStrategy = "blue-green" ABTestingStrategy = "ab-testing" ) // Analysis status const ( AnalysisStatusCompleted = "completed" AnalysisStatusSkipped = "skipped" ) // CanaryMetricLabels holds labels for canary metrics type CanaryMetricLabels struct { Name string Namespace string DeploymentStrategy string AnalysisStatus string } // Values returns label values as a slice for Prometheus metrics func (c CanaryMetricLabels) Values() []string { return []string{c.Name, c.Namespace, c.DeploymentStrategy, c.AnalysisStatus} } // Recorder records the canary analysis as Prometheus metrics type Recorder struct { info *prometheus.GaugeVec duration *prometheus.HistogramVec total *prometheus.GaugeVec status *prometheus.GaugeVec weight *prometheus.GaugeVec analysis *prometheus.GaugeVec successes *prometheus.CounterVec failures *prometheus.CounterVec } // NewRecorder creates a new recorder and registers the Prometheus metrics func NewRecorder(controller string, register bool) Recorder { info := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: controller, Name: "info", Help: "Flagger version and mesh provider information", }, []string{"version", "mesh_provider"}) duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ Subsystem: controller, Name: "canary_duration_seconds", Help: "Seconds spent performing canary analysis.", Buckets: prometheus.DefBuckets, }, []string{"name", "namespace"}) total := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: controller, Name: "canary_total", Help: "Total number of canary object", }, []string{"namespace"}) // 0 - running, 1 - successful, 2 - failed status := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: controller, Name: "canary_status", Help: "Last canary analysis result", }, []string{"name", "namespace"}) weight := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: controller, Name: "canary_weight", Help: "The virtual service destination weight current value", }, []string{"workload", "namespace"}) analysis := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Subsystem: controller, Name: "canary_metric_analysis", Help: "Last canary analysis result per metric", }, []string{"name", "namespace", "metric"}) successes := prometheus.NewCounterVec(prometheus.CounterOpts{ Subsystem: controller, Name: "canary_successes_total", Help: "Total number of canary successes", }, []string{"name", "namespace", "deployment_strategy", "analysis_status"}) failures := prometheus.NewCounterVec(prometheus.CounterOpts{ Subsystem: controller, Name: "canary_failures_total", Help: "Total number of canary failures", }, []string{"name", "namespace", "deployment_strategy", "analysis_status"}) if register { prometheus.MustRegister(info) prometheus.MustRegister(duration) prometheus.MustRegister(total) prometheus.MustRegister(status) prometheus.MustRegister(weight) prometheus.MustRegister(analysis) prometheus.MustRegister(successes) prometheus.MustRegister(failures) } return Recorder{ info: info, duration: duration, total: total, status: status, weight: weight, analysis: analysis, successes: successes, failures: failures, } } // SetInfo sets the version and mesh provider labels func (cr *Recorder) SetInfo(version string, meshProvider string) { cr.info.WithLabelValues(version, meshProvider).Set(1) } // SetDuration sets the time spent in seconds performing canary analysis func (cr *Recorder) SetDuration(cd *flaggerv1.Canary, duration time.Duration) { cr.duration.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Observe(duration.Seconds()) } // SetTotal sets the total number of canaries per namespace func (cr *Recorder) SetTotal(namespace string, total int) { cr.total.WithLabelValues(namespace).Set(float64(total)) } func (cr *Recorder) SetAnalysis(cd *flaggerv1.Canary, metricTemplateName string, val float64) { cr.analysis.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace, metricTemplateName).Set(val) } // SetStatus sets the last known canary analysis status func (cr *Recorder) SetStatus(cd *flaggerv1.Canary, phase flaggerv1.CanaryPhase) { var status int switch phase { case flaggerv1.CanaryPhaseProgressing: status = 0 case flaggerv1.CanaryPhaseFailed: status = 2 default: status = 1 } cr.status.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Set(float64(status)) } // SetWeight sets the weight values for primary and canary destinations func (cr *Recorder) SetWeight(cd *flaggerv1.Canary, primary int, canary int) { cr.weight.WithLabelValues(fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name), cd.Namespace).Set(float64(primary)) cr.weight.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Set(float64(canary)) } // IncSuccesses increments the total number of canary successes func (cr *Recorder) IncSuccesses(labels CanaryMetricLabels) { cr.successes.WithLabelValues(labels.Values()...).Inc() } // IncFailures increments the total number of canary failures func (cr *Recorder) IncFailures(labels CanaryMetricLabels) { cr.failures.WithLabelValues(labels.Values()...).Inc() } // GetStatusMetric returns the status metric func (cr *Recorder) GetStatusMetric() *prometheus.GaugeVec { return cr.status } // GetWeightMetric returns the weight metric func (cr *Recorder) GetWeightMetric() *prometheus.GaugeVec { return cr.weight } // GetTotalMetric returns the total metric func (cr *Recorder) GetTotalMetric() *prometheus.GaugeVec { return cr.total } // GetInfoMetric returns the info metric func (cr *Recorder) GetInfoMetric() *prometheus.GaugeVec { return cr.info } // GetDurationMetric returns the duration metric func (cr *Recorder) GetDurationMetric() *prometheus.HistogramVec { return cr.duration } // GetAnalysisMetric returns the analysis metric func (cr *Recorder) GetAnalysisMetric() *prometheus.GaugeVec { return cr.analysis } // GetSuccessesMetric returns the successes metric func (cr *Recorder) GetSuccessesMetric() *prometheus.CounterVec { return cr.successes } // GetFailuresMetric returns the failures metric func (cr *Recorder) GetFailuresMetric() *prometheus.CounterVec { return cr.failures }