From 2bcc7a5caea9e18885064a7fc1904d3cb6a72653 Mon Sep 17 00:00:00 2001 From: kabicin Date: Mon, 23 Oct 2023 21:10:00 -0400 Subject: [PATCH] Validate ServiceMonitor config --- controllers/runtimecomponent_controller.go | 4 + utils/utils.go | 89 ++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/controllers/runtimecomponent_controller.go b/controllers/runtimecomponent_controller.go index d489a7ab..7404a870 100644 --- a/controllers/runtimecomponent_controller.go +++ b/controllers/runtimecomponent_controller.go @@ -485,6 +485,10 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req r.ManageError(err, common.StatusConditionTypeReconciled, instance) } else if ok { if instance.Spec.Monitoring != nil && (instance.Spec.CreateKnativeService == nil || !*instance.Spec.CreateKnativeService) { + // Validate the monitoring endpoints' configuration before creating/updating the ServiceMonitor + if err := appstacksutils.ValidatePrometheusMonitoringEndpoints(instance, r.GetClient(), instance.GetNamespace()); err != nil { + return r.ManageError(err, common.StatusConditionTypeReconciled, instance) + } sm := &prometheusv1.ServiceMonitor{ObjectMeta: defaultMeta} err = r.CreateOrUpdate(sm, instance, func() error { appstacksutils.CustomizeServiceMonitor(sm, instance) diff --git a/utils/utils.go b/utils/utils.go index 9b23003f..9099d46b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -959,6 +959,95 @@ func Validate(ba common.BaseComponent) (bool, error) { return true, nil } +func Contains(list []string, s string) bool { + for _, v := range list { + if v == s { + return true + } + } + return false +} + +func appendNameIfUnique(names []string, name string) []string { + if len(name) > 0 && !Contains(names, name) { + return append(names, name) + } + return names +} + +// Returns true if the ConfigMap is not specified as optional, otherwise return false. +func configMapShouldExist(configMapKeySelector corev1.ConfigMapKeySelector) bool { + return configMapKeySelector.Optional == nil || !*configMapKeySelector.Optional +} + +// Returns true if the Secret is not specified as optional, otherwise return false. +func secretShouldExist(secretKeySelector corev1.SecretKeySelector) bool { + return secretKeySelector.Optional == nil || !*secretKeySelector.Optional +} + +// Returns an error if any user specified non-optional Secret or ConfigMap for Prometheus monitoring does not exist, otherwise return nil. +func ValidatePrometheusMonitoringEndpoints(ba common.BaseComponent, client client.Client, namespace string) error { + var basicAuth *prometheusv1.BasicAuth + var oauth2 *prometheusv1.OAuth2 + var bearerTokenSecret corev1.SecretKeySelector + var authorization *prometheusv1.SafeAuthorization + monitoring := ba.GetMonitoring() + if monitoring != nil { + for _, endpoint := range monitoring.GetEndpoints() { + var secretNames []string + var configMapNames []string + // BasicAuth + basicAuth = endpoint.BasicAuth + if basicAuth != nil { + if secretShouldExist(basicAuth.Username) { + secretNames = appendNameIfUnique(secretNames, basicAuth.Username.Name) + } + if secretShouldExist(basicAuth.Password) { + secretNames = appendNameIfUnique(secretNames, basicAuth.Password.Name) + } + } + // OAuth2 + oauth2 = endpoint.OAuth2 + if oauth2 != nil { + if oauth2.ClientID.Secret != nil && secretShouldExist(*oauth2.ClientID.Secret) { + secretNames = appendNameIfUnique(secretNames, oauth2.ClientID.Secret.Name) + } + if oauth2.ClientID.ConfigMap != nil && configMapShouldExist(*oauth2.ClientID.ConfigMap) { + configMapNames = appendNameIfUnique(configMapNames, oauth2.ClientID.ConfigMap.Name) + } + if secretShouldExist(oauth2.ClientSecret) { + secretNames = appendNameIfUnique(secretNames, oauth2.ClientSecret.Name) + } + } + // BearerTokenSecret + bearerTokenSecret = endpoint.BearerTokenSecret + if secretShouldExist(bearerTokenSecret) { + secretNames = appendNameIfUnique(secretNames, bearerTokenSecret.Name) + } + // Authorization + authorization = endpoint.Authorization + if authorization != nil && authorization.Credentials != nil && secretShouldExist(*authorization.Credentials) { + secretNames = appendNameIfUnique(secretNames, authorization.Credentials.Name) + } + // Error if any Secret is specified but does not exist + for _, secretName := range secretNames { + if err := client.Get(context.TODO(), types.NamespacedName{Name: secretName, Namespace: namespace}, &corev1.Secret{}); err != nil { + errorMessage := fmt.Sprintf("Could not find Secret '%s' in this namespace.", secretName) + return errors.New(errorMessage) + } + } + // Error if any ConfigMap is specified but does not exist + for _, configMapName := range configMapNames { + if err := client.Get(context.TODO(), types.NamespacedName{Name: configMapName, Namespace: namespace}, &corev1.ConfigMap{}); err != nil { + errorMessage := fmt.Sprintf("Could not find ConfigMap '%s' in this namespace.", configMapName) + return errors.New(errorMessage) + } + } + } + } + return nil +} + func createValidationError(msg string) error { return fmt.Errorf("validation failed: " + msg) }