github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/multiclusterapplicationconfiguration_webhook.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package webhooks
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    13  	"github.com/verrazzano/verrazzano/application-operator/metricsexporter"
    14  	k8sadmission "k8s.io/api/admission/v1"
    15  	corev1 "k8s.io/api/core/v1"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    18  )
    19  
    20  // MultiClusterApplicationConfigurationValidator is a struct holding objects used during validation.
    21  type MultiClusterApplicationConfigurationValidator struct {
    22  	client  client.Client
    23  	decoder *admission.Decoder
    24  }
    25  
    26  // InjectClient injects the client.
    27  func (v *MultiClusterApplicationConfigurationValidator) InjectClient(c client.Client) error {
    28  	v.client = c
    29  	return nil
    30  }
    31  
    32  // InjectDecoder injects the decoder.
    33  func (v *MultiClusterApplicationConfigurationValidator) InjectDecoder(d *admission.Decoder) error {
    34  	v.decoder = d
    35  	return nil
    36  }
    37  
    38  // Handle performs validation of created or updated MultiClusterApplicationConfiguration resources.
    39  func (v *MultiClusterApplicationConfigurationValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
    40  	counterMetricObject, errorCounterMetricObject, handleDurationMetricObject, zapLogForMetrics, err := metricsexporter.ExposeControllerMetrics("MultiClusterApplicationConfigurationValidator", metricsexporter.MultiClusterAppconfigPodHandleCounter, metricsexporter.MultiClusterAppconfigPodHandleError, metricsexporter.MultiClusterAppconfigPodHandleDuration)
    41  	if err != nil {
    42  		return admission.Response{}
    43  	}
    44  	handleDurationMetricObject.TimerStart()
    45  	defer handleDurationMetricObject.TimerStop()
    46  
    47  	mcac := &v1alpha1.MultiClusterApplicationConfiguration{}
    48  	err = v.decoder.Decode(req, mcac)
    49  	if err != nil {
    50  		errorCounterMetricObject.Inc(zapLogForMetrics, err)
    51  		return admission.Errored(http.StatusBadRequest, err)
    52  	}
    53  
    54  	if mcac.ObjectMeta.DeletionTimestamp.IsZero() {
    55  		switch req.Operation {
    56  		case k8sadmission.Create, k8sadmission.Update:
    57  			err = validateMultiClusterResource(v.client, mcac)
    58  			if err != nil {
    59  				errorCounterMetricObject.Inc(zapLogForMetrics, err)
    60  				return admission.Denied(err.Error())
    61  			}
    62  			err = v.validateSecrets(mcac)
    63  			if err != nil {
    64  				errorCounterMetricObject.Inc(zapLogForMetrics, err)
    65  				return admission.Denied(err.Error())
    66  			}
    67  		}
    68  	}
    69  	counterMetricObject.Inc(zapLogForMetrics, err)
    70  	return admission.Allowed("")
    71  }
    72  
    73  // Validate that the secrets referenced in the MultiClusterApplicationConfiguration resource exist in the
    74  // same namespace as the MultiClusterApplicationConfiguration resource.
    75  func (v *MultiClusterApplicationConfigurationValidator) validateSecrets(mcac *v1alpha1.MultiClusterApplicationConfiguration) error {
    76  	if len(mcac.Spec.Secrets) == 0 {
    77  		return nil
    78  	}
    79  
    80  	secrets := corev1.SecretList{}
    81  	listOptions := &client.ListOptions{Namespace: mcac.Namespace}
    82  	err := v.client.List(context.TODO(), &secrets, listOptions)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	var secretsNotFound []string
    88  	for _, mcSecret := range mcac.Spec.Secrets {
    89  		found := false
    90  		for _, secret := range secrets.Items {
    91  			if secret.Name == mcSecret {
    92  				found = true
    93  				break
    94  			}
    95  		}
    96  		if !found {
    97  			secretsNotFound = append(secretsNotFound, mcSecret)
    98  		}
    99  	}
   100  
   101  	if len(secretsNotFound) != 0 {
   102  		secretsDelimited := strings.Join(secretsNotFound, ",")
   103  		return fmt.Errorf("secret(s) %s specified in MultiClusterApplicationConfiguration not found in namespace %s", secretsDelimited, mcac.Namespace)
   104  	}
   105  
   106  	return nil
   107  }