// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package admission import ( "errors" "strings" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "github.com/projectcapsule/capsule/pkg/api/meta" ) // +kubebuilder:object:generate=true // +kubebuilder:validation:XValidation:rule="has(self.client.url) != has(self.client.service)",message="client must configure exactly one of url or service" type DynamicAdmissionConfig struct { // Name the Admission Webhook Name meta.RFC1123Name `json:"name,omitempty"` // Labels added to the Admission Webhook // +optional Labels map[string]string `json:"labels,omitempty"` // Annotations added to the Admission Webhook // +optional Annotations map[string]string `json:"annotations,omitempty"` // Client defines how the Kubernetes API server reaches the admission webhook. // Exactly one of URL or Service must be configured. Client *admissionregistrationv1.WebhookClientConfig `json:"client"` } // ValidateWebhookClientConfig checks the invariant required by the Kubernetes // admissionregistration API before a dynamic webhook object is constructed. func ValidateWebhookClientConfig(client *admissionregistrationv1.WebhookClientConfig) error { if client == nil { return errors.New("webhook client config is required") } hasURL := client.URL != nil hasService := client.Service != nil if hasURL == hasService { return errors.New("webhook client config must configure exactly one of url or service") } return nil } func DynamicWebhookURL(baseURL *string, webhookPath string) *string { cleanPath := normalizePath(webhookPath) if cleanPath == "" { if baseURL == nil || *baseURL == "" { return nil } u := *baseURL return &u } if baseURL == nil || *baseURL == "" { u := cleanPath return &u } base := strings.TrimRight(*baseURL, "/") if base == strings.TrimRight(cleanPath, "/") { u := cleanPath return &u } if strings.HasSuffix(base, cleanPath) { u := base return &u } u := base + cleanPath return &u } func DynamicClientWithPath( in admissionregistrationv1.WebhookClientConfig, webhookPath string, ) admissionregistrationv1.WebhookClientConfig { out := in if out.URL != nil { out.URL = DynamicWebhookURL(out.URL, webhookPath) return out } cleanPath := normalizePath(webhookPath) if cleanPath == "" { return out } if out.Service != nil { svc := *out.Service svc.Path = &cleanPath out.Service = &svc } return out }