mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Add reconcile duration metric for ClusterExternalSecret controller (#2334)
* Add reconcile duration metric for ClusterExternalSecret controller Signed-off-by: shuheiktgw <s-kitagawa@mercari.com> * chore: fmt imports Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> --------- Signed-off-by: shuheiktgw <s-kitagawa@mercari.com> Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> Co-authored-by: Moritz Johner <beller.moritz@googlemail.com>
This commit is contained in:
parent
21ca3c2dc0
commit
d879f37d9e
9 changed files with 392 additions and 95 deletions
|
@ -36,8 +36,10 @@ import (
|
|||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret/cesmetrics"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/pushsecret"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
|
||||
"github.com/external-secrets/external-secrets/pkg/feature"
|
||||
|
@ -125,7 +127,8 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
logger := zap.New(zap.UseFlagOptions(&opts))
|
||||
ctrl.SetLogger(logger)
|
||||
esmetrics.SetUpMetrics(enableExtendedMetricLabels)
|
||||
ctrlmetrics.SetUpLabelNames(enableExtendedMetricLabels)
|
||||
esmetrics.SetUpMetrics()
|
||||
config := ctrl.GetConfigOrDie()
|
||||
config.QPS = clientQPS
|
||||
config.Burst = clientBurst
|
||||
|
@ -192,6 +195,8 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
}
|
||||
if enableClusterExternalSecretReconciler {
|
||||
cesmetrics.SetUpMetrics()
|
||||
|
||||
if err = (&clusterexternalsecret.Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ClusterExternalSecret"),
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
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 cesmetrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
ClusterExternalSecretSubsystem = "clusterexternalsecret"
|
||||
ClusterExternalSecretReconcileDurationKey = "reconcile_duration"
|
||||
)
|
||||
|
||||
var gaugeVecMetrics = map[string]*prometheus.GaugeVec{}
|
||||
|
||||
// SetUpMetrics is called at the root to set-up the metric logic using the
|
||||
// config flags provided.
|
||||
func SetUpMetrics() {
|
||||
clusterExternalSecretReconcileDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: ClusterExternalSecretSubsystem,
|
||||
Name: ClusterExternalSecretReconcileDurationKey,
|
||||
Help: "The duration time to reconcile the Cluster External Secret",
|
||||
}, ctrlmetrics.NonConditionMetricLabelNames)
|
||||
|
||||
metrics.Registry.MustRegister(clusterExternalSecretReconcileDuration)
|
||||
|
||||
gaugeVecMetrics = map[string]*prometheus.GaugeVec{
|
||||
ClusterExternalSecretReconcileDurationKey: clusterExternalSecretReconcileDuration,
|
||||
}
|
||||
}
|
||||
|
||||
func GetGaugeVec(key string) *prometheus.GaugeVec {
|
||||
return gaugeVecMetrics[key]
|
||||
}
|
|
@ -32,6 +32,8 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret/cesmetrics"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
)
|
||||
|
||||
// ClusterExternalSecretReconciler reconciles a ClusterExternalSecret object.
|
||||
|
@ -58,6 +60,12 @@ const (
|
|||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := r.Log.WithValues("ClusterExternalSecret", req.NamespacedName)
|
||||
|
||||
resourceLabels := ctrlmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace})
|
||||
start := time.Now()
|
||||
|
||||
externalSecretReconcileDuration := cesmetrics.GetGaugeVec(cesmetrics.ClusterExternalSecretReconcileDurationKey)
|
||||
defer func() { externalSecretReconcileDuration.With(resourceLabels).Set(float64(time.Since(start))) }()
|
||||
|
||||
var clusterExternalSecret esv1beta1.ClusterExternalSecret
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &clusterExternalSecret)
|
||||
|
|
|
@ -28,7 +28,9 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret/cesmetrics"
|
||||
ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -379,3 +381,8 @@ func sliceContainsString(toFind string, collection []string) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
ctrlmetrics.SetUpLabelNames(false)
|
||||
cesmetrics.SetUpMetrics()
|
||||
}
|
||||
|
|
|
@ -15,13 +15,12 @@ limitations under the License.
|
|||
package esmetrics
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
|
||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,78 +31,37 @@ const (
|
|||
ExternalSecretReconcileDurationKey = "reconcile_duration"
|
||||
)
|
||||
|
||||
var (
|
||||
NonConditionMetricLabelNames = make([]string, 0)
|
||||
var counterVecMetrics = map[string]*prometheus.CounterVec{}
|
||||
|
||||
ConditionMetricLabelNames = make([]string, 0)
|
||||
|
||||
NonConditionMetricLabels = make(map[string]string)
|
||||
|
||||
ConditionMetricLabels = make(map[string]string)
|
||||
)
|
||||
|
||||
var counterVecMetrics map[string]*prometheus.CounterVec = map[string]*prometheus.CounterVec{}
|
||||
|
||||
var gaugeVecMetrics map[string]*prometheus.GaugeVec = map[string]*prometheus.GaugeVec{}
|
||||
var gaugeVecMetrics = map[string]*prometheus.GaugeVec{}
|
||||
|
||||
// Called at the root to set-up the metric logic using the
|
||||
// config flags provided.
|
||||
func SetUpMetrics(addKubeStandardLabels bool) {
|
||||
// Figure out what the labels for the metrics are
|
||||
if addKubeStandardLabels {
|
||||
NonConditionMetricLabelNames = []string{
|
||||
"name", "namespace",
|
||||
"app_kubernetes_io_name", "app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version", "app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of", "app_kubernetes_io_managed_by",
|
||||
}
|
||||
|
||||
ConditionMetricLabelNames = []string{
|
||||
"name", "namespace",
|
||||
"condition", "status",
|
||||
"app_kubernetes_io_name", "app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version", "app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of", "app_kubernetes_io_managed_by",
|
||||
}
|
||||
} else {
|
||||
NonConditionMetricLabelNames = []string{"name", "namespace"}
|
||||
|
||||
ConditionMetricLabelNames = []string{"name", "namespace", "condition", "status"}
|
||||
}
|
||||
|
||||
// Set default values for each label
|
||||
for _, k := range NonConditionMetricLabelNames {
|
||||
NonConditionMetricLabels[k] = ""
|
||||
}
|
||||
|
||||
for _, k := range ConditionMetricLabelNames {
|
||||
ConditionMetricLabels[k] = ""
|
||||
}
|
||||
|
||||
func SetUpMetrics() {
|
||||
// Obtain the prometheus metrics and register
|
||||
syncCallsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: SyncCallsKey,
|
||||
Help: "Total number of the External Secret sync calls",
|
||||
}, NonConditionMetricLabelNames)
|
||||
}, ctrlmetrics.NonConditionMetricLabelNames)
|
||||
|
||||
syncCallsError := prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: SyncCallsErrorKey,
|
||||
Help: "Total number of the External Secret sync errors",
|
||||
}, NonConditionMetricLabelNames)
|
||||
}, ctrlmetrics.NonConditionMetricLabelNames)
|
||||
|
||||
externalSecretCondition := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: ExternalSecretStatusConditionKey,
|
||||
Help: "The status condition of a specific External Secret",
|
||||
}, ConditionMetricLabelNames)
|
||||
}, ctrlmetrics.ConditionMetricLabelNames)
|
||||
|
||||
externalSecretReconcileDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: ExternalSecretReconcileDurationKey,
|
||||
Help: "The duration time to reconcile the External Secret",
|
||||
}, NonConditionMetricLabelNames)
|
||||
}, ctrlmetrics.NonConditionMetricLabelNames)
|
||||
|
||||
metrics.Registry.MustRegister(syncCallsTotal, syncCallsError, externalSecretCondition, externalSecretReconcileDuration)
|
||||
|
||||
|
@ -125,19 +83,19 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
|
|||
for k, v := range es.Labels {
|
||||
esInfo[k] = v
|
||||
}
|
||||
conditionLabels := RefineConditionMetricLabels(esInfo)
|
||||
conditionLabels := ctrlmetrics.RefineConditionMetricLabels(esInfo)
|
||||
externalSecretCondition := GetGaugeVec(ExternalSecretStatusConditionKey)
|
||||
|
||||
switch condition.Type {
|
||||
case esv1beta1.ExternalSecretDeleted:
|
||||
// Remove condition=Ready metrics when the object gets deleted.
|
||||
externalSecretCondition.Delete(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretReady),
|
||||
"status": string(v1.ConditionFalse),
|
||||
}))
|
||||
|
||||
externalSecretCondition.Delete(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretReady),
|
||||
"status": string(v1.ConditionTrue),
|
||||
|
@ -145,13 +103,13 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
|
|||
|
||||
case esv1beta1.ExternalSecretReady:
|
||||
// Remove condition=Deleted metrics when the object gets ready.
|
||||
externalSecretCondition.Delete(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretDeleted),
|
||||
"status": string(v1.ConditionFalse),
|
||||
}))
|
||||
|
||||
externalSecretCondition.Delete(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretDeleted),
|
||||
"status": string(v1.ConditionTrue),
|
||||
|
@ -160,13 +118,13 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
|
|||
// Toggle opposite Status to 0
|
||||
switch condition.Status {
|
||||
case v1.ConditionFalse:
|
||||
externalSecretCondition.With(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretReady),
|
||||
"status": string(v1.ConditionTrue),
|
||||
})).Set(0)
|
||||
case v1.ConditionTrue:
|
||||
externalSecretCondition.With(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(esv1beta1.ExternalSecretReady),
|
||||
"status": string(v1.ConditionFalse),
|
||||
|
@ -181,7 +139,7 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
|
|||
break
|
||||
}
|
||||
|
||||
externalSecretCondition.With(RefineLabels(conditionLabels,
|
||||
externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
|
||||
map[string]string{
|
||||
"condition": string(condition.Type),
|
||||
"status": string(condition.Status),
|
||||
|
@ -195,36 +153,3 @@ func GetCounterVec(key string) *prometheus.CounterVec {
|
|||
func GetGaugeVec(key string) *prometheus.GaugeVec {
|
||||
return gaugeVecMetrics[key]
|
||||
}
|
||||
|
||||
// Refine the given Prometheus Labels with values from a map `newLabels`
|
||||
// Only overwrite a value if the corresponding key is present in the
|
||||
// Prometheus' Labels already to avoid adding label names which are
|
||||
// not defined in a metric's description. Note that non-alphanumeric
|
||||
// characters from keys of `newLabels` are replaced by an underscore
|
||||
// because Prometheus does not accept non-alphanumeric, non-underscore
|
||||
// characters in label names.
|
||||
func RefineLabels(promLabels prometheus.Labels, newLabels map[string]string) prometheus.Labels {
|
||||
nonAlphanumericRegex := regexp.MustCompile(`[^a-zA-Z0-9 ]+`)
|
||||
var refinement = prometheus.Labels{}
|
||||
|
||||
for k, v := range promLabels {
|
||||
refinement[k] = v
|
||||
}
|
||||
|
||||
for k, v := range newLabels {
|
||||
cleanKey := nonAlphanumericRegex.ReplaceAllString(k, "_")
|
||||
if _, ok := refinement[cleanKey]; ok {
|
||||
refinement[cleanKey] = v
|
||||
}
|
||||
}
|
||||
|
||||
return refinement
|
||||
}
|
||||
|
||||
func RefineNonConditionMetricLabels(labels map[string]string) prometheus.Labels {
|
||||
return RefineLabels(NonConditionMetricLabels, labels)
|
||||
}
|
||||
|
||||
func RefineConditionMetricLabels(labels map[string]string) prometheus.Labels {
|
||||
return RefineLabels(ConditionMetricLabels, labels)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import (
|
|||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||
// Metrics.
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
// Loading registered generators.
|
||||
_ "github.com/external-secrets/external-secrets/pkg/generator/register"
|
||||
// Loading registered providers.
|
||||
|
@ -96,7 +97,7 @@ type Reconciler struct {
|
|||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := r.Log.WithValues("ExternalSecret", req.NamespacedName)
|
||||
|
||||
resourceLabels := esmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace})
|
||||
resourceLabels := ctrlmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace})
|
||||
start := time.Now()
|
||||
|
||||
externalSecretReconcileDuration := esmetrics.GetGaugeVec(esmetrics.ExternalSecretReconcileDurationKey)
|
||||
|
@ -132,7 +133,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
}
|
||||
|
||||
// if extended metrics is enabled, refine the time series vector
|
||||
resourceLabels = esmetrics.RefineLabels(resourceLabels, externalSecret.Labels)
|
||||
resourceLabels = ctrlmetrics.RefineLabels(resourceLabels, externalSecret.Labels)
|
||||
|
||||
defer externalSecretReconcileDuration.With(resourceLabels).Set(float64(time.Since(start)))
|
||||
defer syncCallsTotal.With(resourceLabels).Inc()
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
|
||||
ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
|
||||
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
|
||||
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
|
||||
"github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
|
||||
)
|
||||
|
||||
|
@ -2308,7 +2309,8 @@ func init() {
|
|||
},
|
||||
})
|
||||
|
||||
esmetrics.SetUpMetrics(false)
|
||||
ctrlmetrics.SetUpLabelNames(false)
|
||||
esmetrics.SetUpMetrics()
|
||||
testSyncCallsTotal = esmetrics.GetCounterVec(esmetrics.SyncCallsKey)
|
||||
testSyncCallsError = esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey)
|
||||
testExternalSecretCondition = esmetrics.GetGaugeVec(esmetrics.ExternalSecretStatusConditionKey)
|
||||
|
|
101
pkg/controllers/metrics/labels.go
Normal file
101
pkg/controllers/metrics/labels.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
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 (
|
||||
"regexp"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
NonConditionMetricLabelNames = make([]string, 0)
|
||||
|
||||
ConditionMetricLabelNames = make([]string, 0)
|
||||
|
||||
NonConditionMetricLabels = make(map[string]string)
|
||||
|
||||
ConditionMetricLabels = make(map[string]string)
|
||||
)
|
||||
|
||||
var nonAlphanumericRegex *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9 ]+`)
|
||||
}
|
||||
|
||||
// SetUpLabelNames initializes both non-conditional and conditional metric labels and label names.
|
||||
func SetUpLabelNames(addKubeStandardLabels bool) {
|
||||
NonConditionMetricLabelNames = []string{"name", "namespace"}
|
||||
ConditionMetricLabelNames = []string{"name", "namespace", "condition", "status"}
|
||||
|
||||
// Figure out what the labels for the metrics are
|
||||
if addKubeStandardLabels {
|
||||
NonConditionMetricLabelNames = append(
|
||||
NonConditionMetricLabelNames,
|
||||
"app_kubernetes_io_name", "app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version", "app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of", "app_kubernetes_io_managed_by",
|
||||
)
|
||||
|
||||
ConditionMetricLabelNames = append(
|
||||
ConditionMetricLabelNames,
|
||||
"app_kubernetes_io_name", "app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version", "app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of", "app_kubernetes_io_managed_by",
|
||||
)
|
||||
}
|
||||
|
||||
// Set default values for each label
|
||||
for _, k := range NonConditionMetricLabelNames {
|
||||
NonConditionMetricLabels[k] = ""
|
||||
}
|
||||
|
||||
for _, k := range ConditionMetricLabelNames {
|
||||
ConditionMetricLabels[k] = ""
|
||||
}
|
||||
}
|
||||
|
||||
// RefineLabels refines the given Prometheus Labels with values from a map `newLabels`
|
||||
// Only overwrite a value if the corresponding key is present in the
|
||||
// Prometheus' Labels already to avoid adding label names which are
|
||||
// not defined in a metric's description. Note that non-alphanumeric
|
||||
// characters from keys of `newLabels` are replaced by an underscore
|
||||
// because Prometheus does not accept non-alphanumeric, non-underscore
|
||||
// characters in label names.
|
||||
func RefineLabels(promLabels prometheus.Labels, newLabels map[string]string) prometheus.Labels {
|
||||
var refinement = prometheus.Labels{}
|
||||
|
||||
for k, v := range promLabels {
|
||||
refinement[k] = v
|
||||
}
|
||||
|
||||
for k, v := range newLabels {
|
||||
cleanKey := nonAlphanumericRegex.ReplaceAllString(k, "_")
|
||||
if _, ok := refinement[cleanKey]; ok {
|
||||
refinement[cleanKey] = v
|
||||
}
|
||||
}
|
||||
|
||||
return refinement
|
||||
}
|
||||
|
||||
func RefineNonConditionMetricLabels(labels map[string]string) prometheus.Labels {
|
||||
return RefineLabels(NonConditionMetricLabels, labels)
|
||||
}
|
||||
|
||||
func RefineConditionMetricLabels(labels map[string]string) prometheus.Labels {
|
||||
return RefineLabels(ConditionMetricLabels, labels)
|
||||
}
|
199
pkg/controllers/metrics/labels_test.go
Normal file
199
pkg/controllers/metrics/labels_test.go
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func TestSetUpLabelNames(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
addKubeStandardLabels bool
|
||||
expectedNonConditionMetricLabelNames []string
|
||||
expectedConditionMetricLabelNames []string
|
||||
expectedNonConditionMetricLabels map[string]string
|
||||
expectedConditionMetricLabels map[string]string
|
||||
}{
|
||||
{
|
||||
description: "Add standard labels disabled",
|
||||
addKubeStandardLabels: false,
|
||||
expectedNonConditionMetricLabelNames: []string{
|
||||
"name",
|
||||
"namespace",
|
||||
},
|
||||
expectedConditionMetricLabelNames: []string{
|
||||
"name",
|
||||
"namespace",
|
||||
"condition",
|
||||
"status",
|
||||
},
|
||||
expectedNonConditionMetricLabels: map[string]string{
|
||||
"name": "",
|
||||
"namespace": "",
|
||||
},
|
||||
expectedConditionMetricLabels: map[string]string{
|
||||
"name": "",
|
||||
"namespace": "",
|
||||
"condition": "",
|
||||
"status": "",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Add standard labels enabled",
|
||||
addKubeStandardLabels: true,
|
||||
expectedNonConditionMetricLabelNames: []string{
|
||||
"name",
|
||||
"namespace",
|
||||
"app_kubernetes_io_name",
|
||||
"app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version",
|
||||
"app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of",
|
||||
"app_kubernetes_io_managed_by",
|
||||
},
|
||||
expectedConditionMetricLabelNames: []string{
|
||||
"name",
|
||||
"namespace",
|
||||
"condition",
|
||||
"status",
|
||||
"app_kubernetes_io_name",
|
||||
"app_kubernetes_io_instance",
|
||||
"app_kubernetes_io_version",
|
||||
"app_kubernetes_io_component",
|
||||
"app_kubernetes_io_part_of",
|
||||
"app_kubernetes_io_managed_by",
|
||||
},
|
||||
expectedNonConditionMetricLabels: map[string]string{
|
||||
"name": "",
|
||||
"namespace": "",
|
||||
"app_kubernetes_io_name": "",
|
||||
"app_kubernetes_io_instance": "",
|
||||
"app_kubernetes_io_version": "",
|
||||
"app_kubernetes_io_component": "",
|
||||
"app_kubernetes_io_part_of": "",
|
||||
"app_kubernetes_io_managed_by": "",
|
||||
},
|
||||
expectedConditionMetricLabels: map[string]string{
|
||||
"name": "",
|
||||
"namespace": "",
|
||||
"condition": "",
|
||||
"status": "",
|
||||
"app_kubernetes_io_name": "",
|
||||
"app_kubernetes_io_instance": "",
|
||||
"app_kubernetes_io_version": "",
|
||||
"app_kubernetes_io_component": "",
|
||||
"app_kubernetes_io_part_of": "",
|
||||
"app_kubernetes_io_managed_by": "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
SetUpLabelNames(tc.addKubeStandardLabels)
|
||||
|
||||
if diff := cmp.Diff(NonConditionMetricLabelNames, tc.expectedNonConditionMetricLabelNames); diff != "" {
|
||||
t.Errorf("NonConditionMetricLabelNames does not match the expected value. (-got +want)\n%s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(ConditionMetricLabelNames, tc.expectedConditionMetricLabelNames); diff != "" {
|
||||
t.Errorf("ConditionMetricLabelNames does not match the expected value. (-got +want)\n%s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(NonConditionMetricLabels, tc.expectedNonConditionMetricLabels); diff != "" {
|
||||
t.Errorf("NonConditionMetricLabels are not initialized with empty strings. (-got +want)\n%s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(ConditionMetricLabels, tc.expectedConditionMetricLabels); diff != "" {
|
||||
t.Errorf("ConditionMetricLabels are not initialized with empty strings. (-got +want)\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefineLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
promLabels prometheus.Labels
|
||||
newLabels map[string]string
|
||||
expectedRefinement prometheus.Labels
|
||||
}{
|
||||
{
|
||||
description: "No new labels",
|
||||
promLabels: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
newLabels: map[string]string{},
|
||||
expectedRefinement: prometheus.Labels{"label1": "value1", "label2": "value2"},
|
||||
},
|
||||
{
|
||||
description: "Add unregistered labels",
|
||||
promLabels: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
newLabels: map[string]string{
|
||||
"new_label1": "new_value1",
|
||||
"new_label2": "new_value2",
|
||||
},
|
||||
expectedRefinement: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Overwrite existing labels",
|
||||
promLabels: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
newLabels: map[string]string{
|
||||
"label1": "new_value1",
|
||||
"label2": "new_value2",
|
||||
},
|
||||
expectedRefinement: prometheus.Labels{
|
||||
"label1": "new_value1",
|
||||
"label2": "new_value2",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Clean non-alphanumeric characters in new labels",
|
||||
promLabels: prometheus.Labels{
|
||||
"label_1": "value1",
|
||||
},
|
||||
newLabels: map[string]string{
|
||||
"label@1": "new_value",
|
||||
},
|
||||
expectedRefinement: prometheus.Labels{
|
||||
"label_1": "new_value",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
refinement := RefineLabels(tc.promLabels, tc.newLabels)
|
||||
|
||||
if diff := cmp.Diff(refinement, tc.expectedRefinement); diff != "" {
|
||||
t.Errorf("Refinement does not match the expected value. (-got +want)\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue