diff --git a/pkg/plugins/optional/helm/v1alpha/scaffolds/init.go b/pkg/plugins/optional/helm/v1alpha/scaffolds/init.go index 513e78bb0de..1d43c91e335 100644 --- a/pkg/plugins/optional/helm/v1alpha/scaffolds/init.go +++ b/pkg/plugins/optional/helm/v1alpha/scaffolds/init.go @@ -32,9 +32,8 @@ import ( "sigs.k8s.io/kubebuilder/v4/pkg/plugin" "sigs.k8s.io/kubebuilder/v4/pkg/plugins" "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1" - "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm" "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates" - chart_templates "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates" + chartTemplates "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates" templatescertmanager "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/cert-manager" "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/manager" templatesmetrics "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/metrics" @@ -70,11 +69,9 @@ func (s *initScaffolder) InjectFS(fs machinery.Filesystem) { func (s *initScaffolder) Scaffold() error { log.Println("Generating Helm Chart to distribute project") - // Extract Images scaffolded with DeployImage to add ENVVAR to the values imagesEnvVars := s.getDeployImagesEnvVars() - // Extract webhooks from generated YAML files (generated by controller-gen) - webhooks, err := extractWebhooksFromGeneratedFiles() + webhooks, err := s.extractWebhooksFromGeneratedFiles() if err != nil { return fmt.Errorf("failed to extract webhooks: %w", err) } @@ -88,12 +85,11 @@ func (s *initScaffolder) Scaffold() error { &templates.HelmChart{}, &templates.HelmValues{ HasWebhooks: len(webhooks) > 0, - Webhooks: webhooks, DeployImages: imagesEnvVars, Force: s.force, }, &templates.HelmIgnore{}, - &chart_templates.HelmHelpers{}, + &chartTemplates.HelmHelpers{}, &manager.ManagerDeployment{ Force: s.force, DeployImages: len(imagesEnvVars) > 0, @@ -105,8 +101,12 @@ func (s *initScaffolder) Scaffold() error { } if len(webhooks) > 0 { - buildScaffold = append(buildScaffold, &templateswebhooks.WebhookTemplate{}) - buildScaffold = append(buildScaffold, &templateswebhooks.WebhookService{}) + buildScaffold = append(buildScaffold, + &templateswebhooks.WebhookTemplate{ + Webhooks: webhooks, + }, + &templateswebhooks.WebhookService{}, + ) } if err := scaffold.Execute(buildScaffold...); err != nil { @@ -146,87 +146,65 @@ func (s *initScaffolder) getDeployImagesEnvVars() map[string]string { return deployImages } -// Extract webhooks from manifests.yaml file -func extractWebhooksFromGeneratedFiles() ([]helm.WebhookYAML, error) { - var webhooks []helm.WebhookYAML +// extractWebhooksFromGeneratedFiles parse the files and get the webhook data +func (s *initScaffolder) extractWebhooksFromGeneratedFiles() ([]templateswebhooks.Webhook, error) { + var webhooks []templateswebhooks.Webhook manifestFile := "config/webhook/manifests.yaml" - if _, err := os.Stat(manifestFile); err == nil { - content, err := os.ReadFile(manifestFile) - if err != nil { - return nil, fmt.Errorf("failed to read manifests.yaml: %w", err) - } - // Process the content to extract webhooks - webhooks = append(webhooks, extractWebhookYAML(content)...) - } else { - // Return empty if no webhooks were found + if _, err := os.Stat(manifestFile); os.IsNotExist(err) { + log.Printf("webhook manifests were not found at %s", manifestFile) return webhooks, nil } - return webhooks, nil -} - -// extractWebhookYAML parses the webhook YAML content and returns a list of WebhookYAML -func extractWebhookYAML(content []byte) []helm.WebhookYAML { - var webhooks []helm.WebhookYAML - - type WebhookConfig struct { - Kind string `yaml:"kind"` - Webhooks []struct { - Name string `yaml:"name"` - ClientConfig struct { - Service struct { - Name string `yaml:"name"` - Namespace string `yaml:"namespace"` - Path string `yaml:"path"` - } `yaml:"service"` - CABundle string `yaml:"caBundle"` - } `yaml:"clientConfig"` - Rules []helm.WebhookRule `yaml:"rules"` - FailurePolicy string `yaml:"failurePolicy"` - SideEffects string `yaml:"sideEffects"` - AdmissionReviewVersions []string `yaml:"admissionReviewVersions"` - } `yaml:"webhooks"` + content, err := os.ReadFile(manifestFile) + if err != nil { + return nil, fmt.Errorf("failed to read %s: %w", manifestFile, err) } - // Split the input into different documents (to handle multiple YAML docs in one file) docs := strings.Split(string(content), "---") - for _, doc := range docs { - var webhookConfig WebhookConfig + var webhookConfig struct { + Kind string `yaml:"kind"` + Webhooks []struct { + Name string `yaml:"name"` + ClientConfig struct { + Service struct { + Name string `yaml:"name"` + Namespace string `yaml:"namespace"` + Path string `yaml:"path"` + } `yaml:"service"` + } `yaml:"clientConfig"` + Rules []templateswebhooks.WebhookRule `yaml:"rules"` + FailurePolicy string `yaml:"failurePolicy"` + SideEffects string `yaml:"sideEffects"` + AdmissionReviewVersions []string `yaml:"admissionReviewVersions"` + } `yaml:"webhooks"` + } + if err := yaml.Unmarshal([]byte(doc), &webhookConfig); err != nil { - log.Errorf("Error unmarshalling webhook YAML: %v", err) + log.Errorf("Error parsing webhook YAML: %v", err) continue } - // Determine the webhook type (mutating or validating) - webhookType := "unknown" + webhookType := "validating" if webhookConfig.Kind == "MutatingWebhookConfiguration" { webhookType = "mutating" - } else if webhookConfig.Kind == "ValidatingWebhookConfiguration" { - webhookType = "validating" } - // Parse each webhook and append it to the result - for _, webhook := range webhookConfig.Webhooks { - for i := range webhook.Rules { - // If apiGroups is empty, set it to [""] to ensure proper YAML output - if len(webhook.Rules[i].APIGroups) == 0 { - webhook.Rules[i].APIGroups = []string{""} - } - } - webhooks = append(webhooks, helm.WebhookYAML{ - Name: webhook.Name, + for _, w := range webhookConfig.Webhooks { + webhooks = append(webhooks, templateswebhooks.Webhook{ + Name: w.Name, Type: webhookType, - Path: webhook.ClientConfig.Service.Path, - Rules: webhook.Rules, - FailurePolicy: webhook.FailurePolicy, - SideEffects: webhook.SideEffects, - AdmissionReviewVersions: webhook.AdmissionReviewVersions, + ServiceName: w.ClientConfig.Service.Name, + Path: w.ClientConfig.Service.Path, + FailurePolicy: w.FailurePolicy, + SideEffects: w.SideEffects, + AdmissionReviewVersions: w.AdmissionReviewVersions, + Rules: w.Rules, }) } } - return webhooks + return webhooks, nil } // Helper function to copy files from config/ to dist/chart/templates/ diff --git a/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/webhook/webhook.go b/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/webhook/webhook.go index f22f71129a4..24c58be7810 100644 --- a/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/webhook/webhook.go +++ b/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/chart-templates/webhook/webhook.go @@ -27,6 +27,8 @@ var _ machinery.Template = &WebhookTemplate{} type WebhookTemplate struct { machinery.TemplateMixin machinery.ProjectNameMixin + + Webhooks []Webhook } // SetTemplateDefaults sets default configuration for the webhook template @@ -42,12 +44,32 @@ func (f *WebhookTemplate) SetTemplateDefaults() error { return nil } -const webhookTemplate = `{{` + "`" + `{{- if .Values.webhook.enable }}` + "`" + `}} +// Webhook helps generate the bollerplate with the Webhook values +type Webhook struct { + ServiceName string + Name string + Path string + Type string + FailurePolicy string + SideEffects string + AdmissionReviewVersions []string + Rules []WebhookRule +} +// WebhookRule to help map the rules +type WebhookRule struct { + Operations []string + APIGroups []string + APIVersions []string + Resources []string +} + +const webhookTemplate = `{{` + "`" + `{{- if .Values.webhook.enable }}` + "`" + `}} +{{- range .Webhooks }} apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration +kind: {{ if eq .Type "mutating" }}MutatingWebhookConfiguration{{ else }}ValidatingWebhookConfiguration{{ end }} metadata: - name: {{ .ProjectName }}-mutating-webhook-configuration + name: {{ .Name }} namespace: {{ "{{ .Release.Namespace }}" }} annotations: {{` + "`" + `{{- if .Values.certmanager.enable }}` + "`" + `}} @@ -56,87 +78,38 @@ metadata: labels: {{ "{{- include \"chart.labels\" . | nindent 4 }}" }} webhooks: - {{` + "`" + `{{- range .Values.webhook.services }}` + "`" + `}} - {{` + "`" + `{{- if eq .type "mutating" }}` + "`" + `}} - - name: {{` + "`" + `{{ .name }}` + "`" + `}} - clientConfig: - service: - name: {{ .ProjectName }}-webhook-service - namespace: {{` + "`" + `{{ $.Release.Namespace }}` + "`" + `}} - path: {{` + "`" + `{{ .path }}` + "`" + `}} - failurePolicy: {{` + "`" + `{{ .failurePolicy }}` + "`" + `}} - sideEffects: {{` + "`" + `{{ .sideEffects }}` + "`" + `}} - admissionReviewVersions: - {{` + "`" + `{{- range .admissionReviewVersions }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - rules: - {{` + "`" + `{{- range .rules }}` + "`" + `}} - - operations: - {{` + "`" + `{{- range .operations }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - apiGroups: - {{` + "`" + `{{- range .apiGroups }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - apiVersions: - {{` + "`" + `{{- range .apiVersions }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - resources: - {{` + "`" + `{{- range .resources }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: {{ .ProjectName }}-validating-webhook-configuration - namespace: {{ "{{ .Release.Namespace }}" }} - annotations: - {{` + "`" + `{{- if .Values.certmanager.enable }}` + "`" + `}} - cert-manager.io/inject-ca-from: "{{` + "`" + `{{ $.Release.Namespace }}` + "`" + `}}/serving-cert" - {{` + "`" + `{{- end }}` + "`" + `}} -webhooks: - {{` + "`" + `{{- range .Values.webhook.services }}` + "`" + `}} - {{` + "`" + `{{- if eq .type "validating" }}` + "`" + `}} - - name: {{` + "`" + `{{ .name }}` + "`" + `}} + - name: {{ .Name }} clientConfig: service: - name: {{ .ProjectName }}-webhook-service - namespace: {{` + "`" + `{{ $.Release.Namespace }}` + "`" + `}} - path: {{` + "`" + `{{ .path }}` + "`" + `}} - failurePolicy: {{` + "`" + `{{ .failurePolicy }}` + "`" + `}} - sideEffects: {{` + "`" + `{{ .sideEffects }}` + "`" + `}} + name: {{ .ServiceName }} + namespace: {{ "{{ .Release.Namespace }}" }} + path: {{ .Path }} + failurePolicy: {{ .FailurePolicy }} + sideEffects: {{ .SideEffects }} admissionReviewVersions: - {{` + "`" + `{{- range .admissionReviewVersions }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} + {{- range .AdmissionReviewVersions }} + - {{ . }} + {{- end }} rules: - {{` + "`" + `{{- range .rules }}` + "`" + `}} + {{- range .Rules }} - operations: - {{` + "`" + `{{- range .operations }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} + {{- range .Operations }} + - {{ . }} + {{- end }} apiGroups: - {{` + "`" + `{{- range .apiGroups }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} + {{- range .APIGroups }} + - {{ . }} + {{- end }} apiVersions: - {{` + "`" + `{{- range .apiVersions }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} + {{- range .APIVersions }} + - {{ . }} + {{- end }} resources: - {{` + "`" + `{{- range .resources }}` + "`" + `}} - - {{` + "`" + `{{ . }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} - {{` + "`" + `{{- end }}` + "`" + `}} + {{- range .Resources }} + - {{ . }} + {{- end }} + {{- end }} --- +{{- end }} {{` + "`" + `{{- end }}` + "`" + `}} ` diff --git a/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/values.go b/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/values.go index e4874e59254..86942894636 100644 --- a/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/values.go +++ b/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates/values.go @@ -19,7 +19,6 @@ import ( "path/filepath" "sigs.k8s.io/kubebuilder/v4/pkg/machinery" - "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm" ) var _ machinery.Template = &HelmValues{} @@ -33,8 +32,6 @@ type HelmValues struct { DeployImages map[string]string // Force if true allows overwriting the scaffolded file Force bool - // Webhooks stores the webhook configurations - Webhooks []helm.WebhookYAML // HasWebhooks is true when webhooks were found in the config HasWebhooks bool } @@ -125,52 +122,13 @@ crd: # ControllerManager argument "--metrics-bind-address=:8443" is removed. metrics: enable: true -{{ if .Webhooks }} +{{ if .HasWebhooks }} # [WEBHOOKS]: Webhooks configuration # The following configuration is automatically generated from the manifests # generated by controller-gen. To update run 'make manifests' and # the edit command with the '--force' flag webhook: enable: true - services: - {{- range .Webhooks }} - - name: {{ .Name }} - type: {{ .Type }} - path: {{ .Path }} - failurePolicy: {{ .FailurePolicy }} - sideEffects: {{ .SideEffects }} - admissionReviewVersions: - {{- range .AdmissionReviewVersions }} - - {{ . }} - {{- end }} - rules: - {{- range .Rules }} - - operations: - {{- range .Operations }} - - {{ . }} - {{- end }} - apiGroups: - {{- if .APIGroups }} - {{- range .APIGroups }} - {{- if eq . "" }} - - "" - {{- else }} - - {{ . }} - {{- end }} - {{- end }} - {{- else }} - - "" - {{- end }} - apiVersions: - {{- range .APIVersions }} - - {{ . }} - {{- end }} - resources: - {{- range .Resources }} - - {{ . }} - {{- end }} - {{- end }} - {{- end }} {{ end }} # [PROMETHEUS]: To enable a ServiceMonitor to export metrics to Prometheus set true prometheus: diff --git a/pkg/plugins/optional/helm/webhookYAML.go b/pkg/plugins/optional/helm/webhookYAML.go deleted file mode 100644 index 3880fa79065..00000000000 --- a/pkg/plugins/optional/helm/webhookYAML.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2024 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package helm - -// WebhookRule represents the rules for operations, API groups, versions, and resources -type WebhookRule struct { - Operations []string `yaml:"operations"` - APIGroups []string `yaml:"apiGroups"` - APIVersions []string `yaml:"apiVersions"` - Resources []string `yaml:"resources"` -} - -// WebhookYAML stores the details of each webhook generated by controller-gen -type WebhookYAML struct { - Name string `yaml:"name"` - Type string `yaml:"type"` - Path string `yaml:"path"` - Rules []WebhookRule `yaml:"rules"` - FailurePolicy string `yaml:"failurePolicy"` - SideEffects string `yaml:"sideEffects"` - AdmissionReviewVersions []string `yaml:"admissionReviewVersions"` -} diff --git a/testdata/project-v4-with-plugins/dist/chart/templates/webhook/webhooks.yaml b/testdata/project-v4-with-plugins/dist/chart/templates/webhook/webhooks.yaml index 429fd8d6cb0..57d80ae5f0b 100644 --- a/testdata/project-v4-with-plugins/dist/chart/templates/webhook/webhooks.yaml +++ b/testdata/project-v4-with-plugins/dist/chart/templates/webhook/webhooks.yaml @@ -1,9 +1,8 @@ {{- if .Values.webhook.enable }} - apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration +kind: ValidatingWebhookConfiguration metadata: - name: project-v4-with-plugins-mutating-webhook-configuration + name: vmemcached-v1alpha1.kb.io namespace: {{ .Release.Namespace }} annotations: {{- if .Values.certmanager.enable }} @@ -12,86 +11,25 @@ metadata: labels: {{- include "chart.labels" . | nindent 4 }} webhooks: - {{- range .Values.webhook.services }} - {{- if eq .type "mutating" }} - - name: {{ .name }} - clientConfig: - service: - name: project-v4-with-plugins-webhook-service - namespace: {{ $.Release.Namespace }} - path: {{ .path }} - failurePolicy: {{ .failurePolicy }} - sideEffects: {{ .sideEffects }} - admissionReviewVersions: - {{- range .admissionReviewVersions }} - - {{ . }} - {{- end }} - rules: - {{- range .rules }} - - operations: - {{- range .operations }} - - {{ . }} - {{- end }} - apiGroups: - {{- range .apiGroups }} - - {{ . }} - {{- end }} - apiVersions: - {{- range .apiVersions }} - - {{ . }} - {{- end }} - resources: - {{- range .resources }} - - {{ . }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: project-v4-with-plugins-validating-webhook-configuration - namespace: {{ .Release.Namespace }} - annotations: - {{- if .Values.certmanager.enable }} - cert-manager.io/inject-ca-from: "{{ $.Release.Namespace }}/serving-cert" - {{- end }} -webhooks: - {{- range .Values.webhook.services }} - {{- if eq .type "validating" }} - - name: {{ .name }} + - name: vmemcached-v1alpha1.kb.io clientConfig: service: - name: project-v4-with-plugins-webhook-service - namespace: {{ $.Release.Namespace }} - path: {{ .path }} - failurePolicy: {{ .failurePolicy }} - sideEffects: {{ .sideEffects }} + name: webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-example-com-testproject-org-v1alpha1-memcached + failurePolicy: Fail + sideEffects: None admissionReviewVersions: - {{- range .admissionReviewVersions }} - - {{ . }} - {{- end }} + - v1 rules: - {{- range .rules }} - operations: - {{- range .operations }} - - {{ . }} - {{- end }} + - CREATE + - UPDATE apiGroups: - {{- range .apiGroups }} - - {{ . }} - {{- end }} + - example.com.testproject.org apiVersions: - {{- range .apiVersions }} - - {{ . }} - {{- end }} + - v1alpha1 resources: - {{- range .resources }} - - {{ . }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} + - memcacheds --- {{- end }} diff --git a/testdata/project-v4-with-plugins/dist/chart/values.yaml b/testdata/project-v4-with-plugins/dist/chart/values.yaml index 5e9b98e16b5..89757cd37f7 100644 --- a/testdata/project-v4-with-plugins/dist/chart/values.yaml +++ b/testdata/project-v4-with-plugins/dist/chart/values.yaml @@ -72,24 +72,6 @@ metrics: # the edit command with the '--force' flag webhook: enable: true - services: - - name: vmemcached-v1alpha1.kb.io - type: validating - path: /validate-example-com-testproject-org-v1alpha1-memcached - failurePolicy: Fail - sideEffects: None - admissionReviewVersions: - - v1 - rules: - - operations: - - CREATE - - UPDATE - apiGroups: - - example.com.testproject.org - apiVersions: - - v1alpha1 - resources: - - memcacheds # [PROMETHEUS]: To enable a ServiceMonitor to export metrics to Prometheus set true prometheus: