1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-15 17:51:01 +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:
Shuhei Kitagawa 2023-05-23 05:43:23 +09:00 committed by GitHub
parent 21ca3c2dc0
commit d879f37d9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 392 additions and 95 deletions

View file

@ -36,8 +36,10 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1" 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"
"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"
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics" "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/pushsecret"
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore" "github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
"github.com/external-secrets/external-secrets/pkg/feature" "github.com/external-secrets/external-secrets/pkg/feature"
@ -125,7 +127,8 @@ var rootCmd = &cobra.Command{
} }
logger := zap.New(zap.UseFlagOptions(&opts)) logger := zap.New(zap.UseFlagOptions(&opts))
ctrl.SetLogger(logger) ctrl.SetLogger(logger)
esmetrics.SetUpMetrics(enableExtendedMetricLabels) ctrlmetrics.SetUpLabelNames(enableExtendedMetricLabels)
esmetrics.SetUpMetrics()
config := ctrl.GetConfigOrDie() config := ctrl.GetConfigOrDie()
config.QPS = clientQPS config.QPS = clientQPS
config.Burst = clientBurst config.Burst = clientBurst
@ -192,6 +195,8 @@ var rootCmd = &cobra.Command{
} }
} }
if enableClusterExternalSecretReconciler { if enableClusterExternalSecretReconciler {
cesmetrics.SetUpMetrics()
if err = (&clusterexternalsecret.Reconciler{ if err = (&clusterexternalsecret.Reconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ClusterExternalSecret"), Log: ctrl.Log.WithName("controllers").WithName("ClusterExternalSecret"),

View file

@ -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]
}

View file

@ -32,6 +32,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" 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. // ClusterExternalSecretReconciler reconciles a ClusterExternalSecret object.
@ -58,6 +60,12 @@ const (
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("ClusterExternalSecret", req.NamespacedName) 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 var clusterExternalSecret esv1beta1.ClusterExternalSecret
err := r.Get(ctx, req.NamespacedName, &clusterExternalSecret) err := r.Get(ctx, req.NamespacedName, &clusterExternalSecret)

View file

@ -28,7 +28,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" 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" ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
) )
var ( var (
@ -379,3 +381,8 @@ func sliceContainsString(toFind string, collection []string) bool {
return false return false
} }
func init() {
ctrlmetrics.SetUpLabelNames(false)
cesmetrics.SetUpMetrics()
}

View file

@ -15,13 +15,12 @@ limitations under the License.
package esmetrics package esmetrics
import ( import (
"regexp"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/metrics" "sigs.k8s.io/controller-runtime/pkg/metrics"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
) )
const ( const (
@ -32,78 +31,37 @@ const (
ExternalSecretReconcileDurationKey = "reconcile_duration" ExternalSecretReconcileDurationKey = "reconcile_duration"
) )
var ( var counterVecMetrics = map[string]*prometheus.CounterVec{}
NonConditionMetricLabelNames = make([]string, 0)
ConditionMetricLabelNames = make([]string, 0) var gaugeVecMetrics = map[string]*prometheus.GaugeVec{}
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{}
// Called at the root to set-up the metric logic using the // Called at the root to set-up the metric logic using the
// config flags provided. // config flags provided.
func SetUpMetrics(addKubeStandardLabels bool) { func SetUpMetrics() {
// 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] = ""
}
// Obtain the prometheus metrics and register // Obtain the prometheus metrics and register
syncCallsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{ syncCallsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Subsystem: ExternalSecretSubsystem, Subsystem: ExternalSecretSubsystem,
Name: SyncCallsKey, Name: SyncCallsKey,
Help: "Total number of the External Secret sync calls", Help: "Total number of the External Secret sync calls",
}, NonConditionMetricLabelNames) }, ctrlmetrics.NonConditionMetricLabelNames)
syncCallsError := prometheus.NewCounterVec(prometheus.CounterOpts{ syncCallsError := prometheus.NewCounterVec(prometheus.CounterOpts{
Subsystem: ExternalSecretSubsystem, Subsystem: ExternalSecretSubsystem,
Name: SyncCallsErrorKey, Name: SyncCallsErrorKey,
Help: "Total number of the External Secret sync errors", Help: "Total number of the External Secret sync errors",
}, NonConditionMetricLabelNames) }, ctrlmetrics.NonConditionMetricLabelNames)
externalSecretCondition := prometheus.NewGaugeVec(prometheus.GaugeOpts{ externalSecretCondition := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Subsystem: ExternalSecretSubsystem, Subsystem: ExternalSecretSubsystem,
Name: ExternalSecretStatusConditionKey, Name: ExternalSecretStatusConditionKey,
Help: "The status condition of a specific External Secret", Help: "The status condition of a specific External Secret",
}, ConditionMetricLabelNames) }, ctrlmetrics.ConditionMetricLabelNames)
externalSecretReconcileDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{ externalSecretReconcileDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Subsystem: ExternalSecretSubsystem, Subsystem: ExternalSecretSubsystem,
Name: ExternalSecretReconcileDurationKey, Name: ExternalSecretReconcileDurationKey,
Help: "The duration time to reconcile the External Secret", Help: "The duration time to reconcile the External Secret",
}, NonConditionMetricLabelNames) }, ctrlmetrics.NonConditionMetricLabelNames)
metrics.Registry.MustRegister(syncCallsTotal, syncCallsError, externalSecretCondition, externalSecretReconcileDuration) metrics.Registry.MustRegister(syncCallsTotal, syncCallsError, externalSecretCondition, externalSecretReconcileDuration)
@ -125,19 +83,19 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
for k, v := range es.Labels { for k, v := range es.Labels {
esInfo[k] = v esInfo[k] = v
} }
conditionLabels := RefineConditionMetricLabels(esInfo) conditionLabels := ctrlmetrics.RefineConditionMetricLabels(esInfo)
externalSecretCondition := GetGaugeVec(ExternalSecretStatusConditionKey) externalSecretCondition := GetGaugeVec(ExternalSecretStatusConditionKey)
switch condition.Type { switch condition.Type {
case esv1beta1.ExternalSecretDeleted: case esv1beta1.ExternalSecretDeleted:
// Remove condition=Ready metrics when the object gets deleted. // Remove condition=Ready metrics when the object gets deleted.
externalSecretCondition.Delete(RefineLabels(conditionLabels, externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretReady), "condition": string(esv1beta1.ExternalSecretReady),
"status": string(v1.ConditionFalse), "status": string(v1.ConditionFalse),
})) }))
externalSecretCondition.Delete(RefineLabels(conditionLabels, externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretReady), "condition": string(esv1beta1.ExternalSecretReady),
"status": string(v1.ConditionTrue), "status": string(v1.ConditionTrue),
@ -145,13 +103,13 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
case esv1beta1.ExternalSecretReady: case esv1beta1.ExternalSecretReady:
// Remove condition=Deleted metrics when the object gets ready. // Remove condition=Deleted metrics when the object gets ready.
externalSecretCondition.Delete(RefineLabels(conditionLabels, externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretDeleted), "condition": string(esv1beta1.ExternalSecretDeleted),
"status": string(v1.ConditionFalse), "status": string(v1.ConditionFalse),
})) }))
externalSecretCondition.Delete(RefineLabels(conditionLabels, externalSecretCondition.Delete(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretDeleted), "condition": string(esv1beta1.ExternalSecretDeleted),
"status": string(v1.ConditionTrue), "status": string(v1.ConditionTrue),
@ -160,13 +118,13 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
// Toggle opposite Status to 0 // Toggle opposite Status to 0
switch condition.Status { switch condition.Status {
case v1.ConditionFalse: case v1.ConditionFalse:
externalSecretCondition.With(RefineLabels(conditionLabels, externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretReady), "condition": string(esv1beta1.ExternalSecretReady),
"status": string(v1.ConditionTrue), "status": string(v1.ConditionTrue),
})).Set(0) })).Set(0)
case v1.ConditionTrue: case v1.ConditionTrue:
externalSecretCondition.With(RefineLabels(conditionLabels, externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(esv1beta1.ExternalSecretReady), "condition": string(esv1beta1.ExternalSecretReady),
"status": string(v1.ConditionFalse), "status": string(v1.ConditionFalse),
@ -181,7 +139,7 @@ func UpdateExternalSecretCondition(es *esv1beta1.ExternalSecret, condition *esv1
break break
} }
externalSecretCondition.With(RefineLabels(conditionLabels, externalSecretCondition.With(ctrlmetrics.RefineLabels(conditionLabels,
map[string]string{ map[string]string{
"condition": string(condition.Type), "condition": string(condition.Type),
"status": string(condition.Status), "status": string(condition.Status),
@ -195,36 +153,3 @@ func GetCounterVec(key string) *prometheus.CounterVec {
func GetGaugeVec(key string) *prometheus.GaugeVec { func GetGaugeVec(key string) *prometheus.GaugeVec {
return gaugeVecMetrics[key] 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)
}

View file

@ -39,6 +39,7 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
// Metrics. // Metrics.
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics" "github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
// Loading registered generators. // Loading registered generators.
_ "github.com/external-secrets/external-secrets/pkg/generator/register" _ "github.com/external-secrets/external-secrets/pkg/generator/register"
// Loading registered providers. // Loading registered providers.
@ -96,7 +97,7 @@ type Reconciler struct {
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("ExternalSecret", req.NamespacedName) 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() start := time.Now()
externalSecretReconcileDuration := esmetrics.GetGaugeVec(esmetrics.ExternalSecretReconcileDurationKey) 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 // 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 externalSecretReconcileDuration.With(resourceLabels).Set(float64(time.Since(start)))
defer syncCallsTotal.With(resourceLabels).Inc() defer syncCallsTotal.With(resourceLabels).Inc()

View file

@ -37,6 +37,7 @@ import (
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1" genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest" ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics" "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" "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) testSyncCallsTotal = esmetrics.GetCounterVec(esmetrics.SyncCallsKey)
testSyncCallsError = esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey) testSyncCallsError = esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey)
testExternalSecretCondition = esmetrics.GetGaugeVec(esmetrics.ExternalSecretStatusConditionKey) testExternalSecretCondition = esmetrics.GetGaugeVec(esmetrics.ExternalSecretStatusConditionKey)

View 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)
}

View 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)
}
})
}
}