github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/appconfig_defaulter.go (about) 1 // Copyright (c) 2020, 2023, 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 "encoding/json" 9 "net/http" 10 11 oamv1 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 12 "github.com/verrazzano/verrazzano/application-operator/metricsexporter" 13 vzlog "github.com/verrazzano/verrazzano/pkg/log" 14 "go.uber.org/zap" 15 istioversionedclient "istio.io/client-go/pkg/clientset/versioned" 16 v1 "k8s.io/api/admission/v1" 17 "k8s.io/client-go/kubernetes" 18 "sigs.k8s.io/controller-runtime/pkg/client" 19 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 20 ) 21 22 // AppConfigDefaulterPath specifies the path of AppConfigDefaulter 23 const AppConfigDefaulterPath = "/appconfig-defaulter" 24 25 // +kubebuilder:webhook:verbs=create;update,path=/appconfig-defaulter,mutating=true,failurePolicy=fail,groups=core.oam.dev,resources=ApplicationConfigurations,versions=v1alpha2,name=appconfig-defaulter.kb.io 26 27 // AppConfigWebhook uses a list of AppConfigDefaulters to supply appconfig default values 28 type AppConfigWebhook struct { 29 decoder *admission.Decoder 30 Client client.Client 31 KubeClient kubernetes.Interface 32 IstioClient istioversionedclient.Interface 33 Defaulters []AppConfigDefaulter 34 } 35 36 // AppConfigDefaulter supplies appconfig default values 37 type AppConfigDefaulter interface { 38 Default(appConfig *oamv1.ApplicationConfiguration, dryRun bool, log *zap.SugaredLogger) error 39 Cleanup(appConfig *oamv1.ApplicationConfiguration, dryRun bool, log *zap.SugaredLogger) error 40 } 41 42 // InjectDecoder injects admission.Decoder 43 func (a *AppConfigWebhook) InjectDecoder(d *admission.Decoder) error { 44 a.decoder = d 45 return nil 46 } 47 48 var appconfigMarshalFunc = json.Marshal 49 50 // Handle handles appconfig mutate Request 51 func (a *AppConfigWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { 52 counterMetricObject, errorCounterMetricObject, handleDurationMetricObject, zapLogForMetrics, err := metricsexporter.ExposeControllerMetrics("AppConfigDefaulter", metricsexporter.AppconfigHandleCounter, metricsexporter.AppconfigHandleError, metricsexporter.AppconfigHandleDuration) 53 if err != nil { 54 return admission.Response{} 55 } 56 handleDurationMetricObject.TimerStart() 57 defer handleDurationMetricObject.TimerStop() 58 59 log := zap.S().With(vzlog.FieldResourceNamespace, req.Namespace, vzlog.FieldResourceName, req.Name, vzlog.FieldWebhook, "appconfig-defaulter") 60 61 dryRun := req.DryRun != nil && *req.DryRun 62 appConfig := &oamv1.ApplicationConfiguration{} 63 //This json can be used to curl -X POST the webhook endpoint 64 log.Debugw("admission.Request", "request", req) 65 log.Infow("Handling appconfig default", 66 "requestOperation", req.Operation, "requestName", req.Name) 67 68 // if the operation is Delete then decode the old object and call the defaulter to cleanup any app conf defaults 69 if req.Operation == v1.Delete { 70 err := a.decoder.DecodeRaw(req.OldObject, appConfig) 71 if err != nil { 72 return admission.Errored(http.StatusBadRequest, err) 73 } 74 for _, defaulter := range a.Defaulters { 75 err = defaulter.Cleanup(appConfig, dryRun, log) 76 if err != nil { 77 errorCounterMetricObject.Inc(zapLogForMetrics, err) 78 return admission.Errored(http.StatusInternalServerError, err) 79 } 80 } 81 if !dryRun { 82 err = a.cleanupAppConfig(appConfig, log) 83 if err != nil { 84 errorCounterMetricObject.Inc(zapLogForMetrics, err) 85 log.Errorf("Failed cleaning up app config %s: %v", req.Name, err) 86 } 87 } 88 return admission.Allowed("cleaned up appconfig default") 89 } 90 91 err = a.decoder.Decode(req, appConfig) 92 if err != nil { 93 return admission.Errored(http.StatusBadRequest, err) 94 } 95 //mutate the fields in appConfig 96 for _, defaulter := range a.Defaulters { 97 err = defaulter.Default(appConfig, dryRun, log) 98 if err != nil { 99 errorCounterMetricObject.Inc(zapLogForMetrics, err) 100 return admission.Errored(http.StatusInternalServerError, err) 101 } 102 } 103 marshaledAppConfig, err := appconfigMarshalFunc(appConfig) 104 if err != nil { 105 errorCounterMetricObject.Inc(zapLogForMetrics, err) 106 return admission.Errored(http.StatusInternalServerError, err) 107 } 108 counterMetricObject.Inc(zapLogForMetrics, err) 109 return admission.PatchResponseFromRaw(req.Object.Raw, marshaledAppConfig) 110 } 111 112 // cleanupAppConfig cleans up the generated certificates and secrets associated with the given app config 113 func (a *AppConfigWebhook) cleanupAppConfig(appConfig *oamv1.ApplicationConfiguration, log *zap.SugaredLogger) (err error) { 114 // Fixup Istio Authorization policies within a project 115 ap := &AuthorizationPolicy{ 116 Client: a.Client, 117 KubeClient: a.KubeClient, 118 IstioClient: a.IstioClient, 119 } 120 return ap.cleanupAuthorizationPoliciesForProjects(appConfig.Namespace, appConfig.Name, log) 121 }