diff --git a/charts/dolibarr/templates/_helpers.tpl b/charts/dolibarr/templates/_helpers.tpl index 9bf8481b1..9dffdd1aa 100644 --- a/charts/dolibarr/templates/_helpers.tpl +++ b/charts/dolibarr/templates/_helpers.tpl @@ -262,3 +262,46 @@ true {{- end -}} {{- end -}} {{- end -}} + +{{- define "dolibarr.validate" -}} +{{- $_ := include "dolibarr.databaseMode" . -}} +{{- if .Values.backup.enabled -}} +{{- $_ := include "dolibarr.backupEnabled" . -}} +{{- end -}} +{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) -}} +{{- fail "ingress.hosts must contain at least one rule when ingress.enabled=true" -}} +{{- end -}} +{{- if .Values.gateway.enabled -}} + {{- if not .Values.gateway.parentRefs -}} + {{- fail "gateway.enabled requires gateway.parentRefs to be populated to create a valid HTTPRoute." -}} + {{- end -}} + {{- range .Values.gateway.parentRefs -}} + {{- if not .name -}} + {{- fail "Each gateway.parentRefs entry must define a 'name' field" -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- if .Values.externalSecrets.enabled -}} + {{- if not .Values.admin.existingSecret -}} + {{- fail "externalSecrets.enabled requires admin.existingSecret to be set to prevent credential drift between the chart-managed Secret and the ExternalSecret." -}} + {{- end -}} + {{- if not .Values.externalSecrets.data -}} + {{- fail "externalSecrets.data must not be empty when externalSecrets.enabled=true" -}} + {{- end -}} + {{- $passKey := .Values.admin.existingSecretPasswordKey | default "admin-password" -}} + {{- $hasPass := false -}} + {{- range .Values.externalSecrets.data -}} + {{- if eq .secretKey $passKey }}{{ $hasPass = true }}{{ end -}} + {{- end -}} + {{- if not $hasPass -}} + {{- fail (printf "externalSecrets.data must include a mapping for key '%s' (admin password)" $passKey) -}} + {{- end -}} +{{- end -}} +{{- $podLabels := .Values.podLabels | default dict -}} +{{- if hasKey $podLabels "app.kubernetes.io/name" -}} +{{- fail "podLabels must not override selector label app.kubernetes.io/name" -}} +{{- end -}} +{{- if hasKey $podLabels "app.kubernetes.io/instance" -}} +{{- fail "podLabels must not override selector label app.kubernetes.io/instance" -}} +{{- end -}} +{{- end -}} diff --git a/charts/dolibarr/templates/validate.yaml b/charts/dolibarr/templates/validate.yaml new file mode 100644 index 000000000..dcd8e8a8e --- /dev/null +++ b/charts/dolibarr/templates/validate.yaml @@ -0,0 +1,2 @@ +{{/* SPDX-License-Identifier: Apache-2.0 */}} +{{- include "dolibarr.validate" . -}} diff --git a/charts/dolibarr/tests/validation_test.yaml b/charts/dolibarr/tests/validation_test.yaml new file mode 100644 index 000000000..711ca5fa5 --- /dev/null +++ b/charts/dolibarr/tests/validation_test.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: Apache-2.0 +suite: Validation +templates: + - templates/validate.yaml +tests: + - it: should reject ingress without rules + set: + ingress.enabled: true + ingress.hosts: [] + asserts: + - failedTemplate: + errorMessage: "ingress.hosts must contain at least one rule when ingress.enabled=true" + + - it: should reject gateway without parentRefs + set: + gateway.enabled: true + gateway.parentRefs: [] + asserts: + - failedTemplate: + errorMessage: "gateway.enabled requires gateway.parentRefs to be populated to create a valid HTTPRoute." + + - it: should reject gateway parentRefs entry missing name + set: + gateway.enabled: true + gateway.parentRefs: + - namespace: default + asserts: + - failedTemplate: + errorMessage: "Each gateway.parentRefs entry must define a 'name' field" + + - it: should reject reserved selector label overrides + set: + podLabels: + app.kubernetes.io/name: custom + asserts: + - failedTemplate: + errorMessage: "podLabels must not override selector label app.kubernetes.io/name" + + - it: should reject reserved instance selector label overrides + set: + podLabels: + app.kubernetes.io/instance: custom + asserts: + - failedTemplate: + errorMessage: "podLabels must not override selector label app.kubernetes.io/instance" + + - it: should allow null pod labels + set: + podLabels: null + asserts: + - notFailedTemplate: {} + + - it: should reject missing backup settings + set: + backup.enabled: true + backup.s3.bucket: dolibarr-backups + asserts: + - failedTemplate: + errorMessage: "backup.enabled requires backup.s3.endpoint, backup.s3.bucket, and either backup.s3.existingSecret or both backup.s3.accessKey and backup.s3.secretKey" + + - it: should reject external secrets without an existing admin secret + set: + externalSecrets.enabled: true + externalSecrets.data: + - secretKey: admin-password + remoteRef: + key: dolibarr/admin + asserts: + - failedTemplate: + errorMessage: "externalSecrets.enabled requires admin.existingSecret to be set to prevent credential drift between the chart-managed Secret and the ExternalSecret." + + - it: should reject external secrets without data + set: + admin.existingSecret: dolibarr-admin + externalSecrets.enabled: true + externalSecrets.data: [] + asserts: + - failedTemplate: + errorMessage: "externalSecrets.data must not be empty when externalSecrets.enabled=true" + + - it: should reject external secrets missing admin password mapping + set: + admin.existingSecret: dolibarr-admin + externalSecrets.enabled: true + externalSecrets.data: + - secretKey: some-other-key + remoteRef: + key: dolibarr/other + asserts: + - failedTemplate: + errorMessage: "externalSecrets.data must include a mapping for key 'admin-password' (admin password)" + + - it: should allow ingress gateway and external secrets together + set: + admin.existingSecret: dolibarr-admin + ingress.enabled: true + ingress.hosts: + - host: dolibarr.example.com + paths: + - path: / + pathType: Prefix + gateway.enabled: true + gateway.parentRefs: + - name: shared-gateway + namespace: default + externalSecrets.enabled: true + externalSecrets.secretStoreRef.name: fake-store + externalSecrets.data: + - secretKey: admin-password + remoteRef: + key: dolibarr/admin-password + asserts: + - notFailedTemplate: {}