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 }