From 57c6699bb0c5ff91bb46a0a19026c92cbef79bdc Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:12:16 +0100 Subject: [PATCH 01/38] feat: add auto-generation of CHANGELOG.md for chart releases - Add git-chglog configuration for changelog generation - Generate CHANGELOG entries for each chart on release - Filter commits by chart path to include only relevant changes - Preserve existing CHANGELOG content while prepending new entries - Commit updated CHANGELOG.md back to repository with [skip ci] - Handle concurrent commits with rebase before push - Fix yamllint issues for long lines and truthy values --- .chglog/config.yml | 36 ++++++++++++++++++ .github/workflows/release.yaml | 68 +++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 .chglog/config.yml diff --git a/.chglog/config.yml b/.chglog/config.yml new file mode 100644 index 0000000..b9c7d48 --- /dev/null +++ b/.chglog/config.yml @@ -0,0 +1,36 @@ +style: github +template: | + ## {{ if .PreviousTag }}[{{ .Version }}]({{ .Info.RepositoryURL }}/compare/{{ .PreviousTag }}...{{ .Version }}){{ else }}{{ .Version }}{{ end }} - {{ .Info.Date.Format "2006-01-02" }} + + {{ range .Commits -}} + {{- if or (contains .Commit.Header "feat") (contains .Commit.Header "fix") (contains .Commit.Header "docs") }} + {{- if or (contains .Commit.Header "refactor") (contains .Commit.Header "perf") (contains .Commit.Header "test") }} + {{- if or (contains .Commit.Header "build") (contains .Commit.Header "ci") (contains .Commit.Header "chore") }} + * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} + {{ end -}}{{ end -}}{{ end -}} + {{ end }} + +info: + title: CHANGELOG + repository_url: https://github.com/wikodit/charts +options: + commit_filters: + Type: + - feat + - fix + - docs + - refactor + - perf + - test + - build + - ci + - chore + header: + pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?!?: (.*)$" + maps: + - Type: 1 + - Scope: 2 + - Subject: 3 + notes: + keywords: + - BREAKING CHANGE diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2fab607..7b61d38 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -20,7 +20,7 @@ on: concurrency: group: release-${{ github.ref }} - cancel-in-progress: false + cancel-in-progress: 'false' env: GHCR_REGISTRY: ghcr.io @@ -154,6 +154,48 @@ jobs: run: | helm dependency update "${{ steps.chart-info.outputs.path }}" || true + - name: Generate CHANGELOG + run: | + # Install git-chglog + wget -O git-chglog.tar.gz https://github.com/git-chglog/git-chglog/releases/latest/download/git-chglog_linux_amd64.tar.gz + tar xzf git-chglog.tar.gz + sudo mv git-chglog /usr/local/bin/ + + # Generate changelog for this chart + CHANGELOG_PATH="${{ steps.chart-info.outputs.path }}/CHANGELOG.md" + + # Get previous tag for this chart + PREV_TAG=$(git tag --list "${{ steps.chart-info.outputs.name }}-*" --sort=-version:refname | sed -n '2p' || echo "") + + if [[ -f "$CHANGELOG_PATH" ]]; then + # Backup existing changelog + cp "$CHANGELOG_PATH" "$CHANGELOG_PATH.bak" + fi + + # Generate new changelog entry + if [[ -n "$PREV_TAG" ]]; then + git-chglog --config .chglog/config.yml --output "$CHANGELOG_PATH" "$PREV_TAG..HEAD" --path "${{ steps.chart-info.outputs.path }}" + else + git-chglog --config .chglog/config.yml --output "$CHANGELOG_PATH" --path "${{ steps.chart-info.outputs.path }}" + fi + + # If backup exists, prepend new entries to it + if [[ -f "$CHANGELOG_PATH.bak" ]]; then + echo "# Changelog" > temp_changelog + echo "" >> temp_changelog + cat "$CHANGELOG_PATH" >> temp_changelog + echo "" >> temp_changelog + tail -n +2 "$CHANGELOG_PATH.bak" >> temp_changelog + mv temp_changelog "$CHANGELOG_PATH" + rm "$CHANGELOG_PATH.bak" + else + # Add header if this is a new changelog + { echo "# Changelog"; echo ""; cat "$CHANGELOG_PATH"; } > temp_changelog + mv temp_changelog "$CHANGELOG_PATH" + fi + + echo "Generated CHANGELOG for ${{ steps.chart-info.outputs.name }}" + - name: Package chart run: | mkdir -p .packaged @@ -279,6 +321,30 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Commit CHANGELOG + if: success() + run: | + # Configure git + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + # Add and commit CHANGELOG + git add "${{ steps.chart-info.outputs.path }}/CHANGELOG.md" + + # Check if there are changes to commit + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "docs: update CHANGELOG.md for ${{ steps.chart-info.outputs.name }} v${{ steps.chart-info.outputs.version }} [skip ci]" + + # Pull latest changes to avoid conflicts + git pull --rebase origin main + + # Push changes + echo "${{ secrets.GITHUB_TOKEN }}" | git push https://github-actions:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git main + echo "CHANGELOG committed and pushed" + fi + - name: Summary run: | cat >> "$GITHUB_STEP_SUMMARY" < Date: Wed, 31 Dec 2025 14:16:02 +0100 Subject: [PATCH 02/38] fix: rename image pull secret from -docker to -registry - Update deployment.yaml to reference -registry secret - Update docker-secret.yaml template names - Update test assertions for new naming - Makes the chart more generic for any container registry --- .../wik-webservice/templates/deployment.yaml | 2 +- .../templates/docker-secret.yaml | 4 +-- .../tests/docker-secret_test.yaml | 27 ++++++++++++------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 9ed586a..704e86f 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -159,7 +159,7 @@ spec: {{ if .Values.webservice.imagePullAuth }} imagePullSecrets: - - name: {{ template "fullname" . }}-docker + - name: {{ template "fullname" . }}-registry {{ end }} {{ if .Values.webservice.volumes }} diff --git a/charts/wik-webservice/templates/docker-secret.yaml b/charts/wik-webservice/templates/docker-secret.yaml index d6cdfb9..445e923 100644 --- a/charts/wik-webservice/templates/docker-secret.yaml +++ b/charts/wik-webservice/templates/docker-secret.yaml @@ -7,7 +7,7 @@ apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: - name: "{{ template "fullname" . }}-docker" + name: "{{ template "fullname" . }}-registry" labels: app: {{ template "name" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" @@ -28,7 +28,7 @@ apiVersion: v1 kind: Secret type: kubernetes.io/dockerconfigjson metadata: - name: "{{ template "fullname" . }}-docker" + name: "{{ template "fullname" . }}-registry" labels: app: {{ template "name" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" diff --git a/charts/wik-webservice/tests/docker-secret_test.yaml b/charts/wik-webservice/tests/docker-secret_test.yaml index 94658d7..f383bbf 100644 --- a/charts/wik-webservice/tests/docker-secret_test.yaml +++ b/charts/wik-webservice/tests/docker-secret_test.yaml @@ -1,24 +1,27 @@ suite: docker secret tests templates: - docker-secret.yaml + - deployment.yaml tests: - it: should create a docker registry secret when imagePullAuth is provided set: webservice: image: nginx:latest imagePullAuth: - registry: registry.example.com - username: myuser - password: mypass + registry: "my-registry.com" + username: "user" + password: "pass" asserts: - - isKind: - of: Secret + - containsDocument: + kind: Secret + apiVersion: v1 + name: "release-name-wik-webservice-registry" + - equal: + path: metadata.name + value: "release-name-wik-webservice-registry" - equal: path: type value: kubernetes.io/dockerconfigjson - - equal: - path: metadata.name - value: release-name-wik-webservice-registry - exists: path: data.".dockerconfigjson" @@ -28,8 +31,12 @@ tests: image: nginx:latest imagePullAuth: {} asserts: - - hasDocuments: - count: 0 + - notContainsDocument: + kind: Secret + apiVersion: v1 + - notContainsDocument: + kind: SealedSecret + apiVersion: bitnami.com/v1alpha1 - it: should include encrypted value when sealedSecrets is enabled set: From 1f452b2b07bd04c07978e055d6f380f718a58bf4 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:16:21 +0100 Subject: [PATCH 03/38] refactor: rename docker-secret to registry-secret - Rename docker-secret.yaml to registry-secret.yaml - Rename docker-secret_test.yaml to registry-secret_test.yaml - Update README.md.gotmpl to say 'registry pull credentials' - Improves clarity and removes Docker-specific naming --- charts/wik-webservice/README.md.gotmpl | 2 +- .../templates/{docker-secret.yaml => registry-secret.yaml} | 0 .../{docker-secret_test.yaml => registry-secret_test.yaml} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename charts/wik-webservice/templates/{docker-secret.yaml => registry-secret.yaml} (100%) rename charts/wik-webservice/tests/{docker-secret_test.yaml => registry-secret_test.yaml} (100%) diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 4c77076..8e0fbea 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -10,7 +10,7 @@ This chart simplifies the deployment of a webservice on Kubernetes. It creates: - **ConfigMap/Secret/SealedSecret** for environment variables -- **Secret/SealedSecret** for docker pull credentials +- **Secret/SealedSecret** for registry pull credentials - **Deployment** with configurable containers, probes, and resources - **Service** with optional metrics port and Prometheus annotations - **Ingress** with configurable hosts and annotations diff --git a/charts/wik-webservice/templates/docker-secret.yaml b/charts/wik-webservice/templates/registry-secret.yaml similarity index 100% rename from charts/wik-webservice/templates/docker-secret.yaml rename to charts/wik-webservice/templates/registry-secret.yaml diff --git a/charts/wik-webservice/tests/docker-secret_test.yaml b/charts/wik-webservice/tests/registry-secret_test.yaml similarity index 100% rename from charts/wik-webservice/tests/docker-secret_test.yaml rename to charts/wik-webservice/tests/registry-secret_test.yaml From 252dd9d19afbcf2cac61d2141b506e09367416f1 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:17:08 +0100 Subject: [PATCH 04/38] feat: add secure containerSecurityContext defaults - Add non-root user (UID 1000) by default - Set readOnlyRootFilesystem to prevent writes - Drop all capabilities for minimal attack surface - Prevent privilege escalation - Can be overridden via webservice.containerSecurityContext --- charts/wik-webservice/templates/deployment.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 704e86f..889a497 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -52,10 +52,18 @@ spec: containers: - name: {{ template "fullname" . }} image: {{ .Values.webservice.image }} -{{ if .Values.webservice.containerSecurityContext}} securityContext: +{{- if .Values.webservice.containerSecurityContext }} {{ .Values.webservice.containerSecurityContext | toYaml | indent 12 }} -{{ end }} +{{- else }} + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL +{{- end }} imagePullPolicy: {{ .Values.webservice.imagePullPolicy }} ports: - name: http From 547dfebfccdb9e76248f5e2759396347cbda6c0d Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:17:17 +0100 Subject: [PATCH 05/38] feat: add secure PodSecurityContext defaults - Set fsGroup to 1000 for proper volume permissions - Set runAsNonRoot and runAsUser to 1000 by default - Ensures pods run as non-root with proper group ownership - Can be overridden via webservice.securityContext --- charts/wik-webservice/templates/deployment.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 889a497..5384f40 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -38,10 +38,14 @@ spec: hostAliases: {{ .Values.webservice.hostAliases | toYaml | indent 8 }} {{ end }} -{{ if .Values.webservice.securityContext }} securityContext: +{{- if .Values.webservice.securityContext }} {{ .Values.webservice.securityContext | toYaml | indent 8 }} -{{ end }} +{{- else }} + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 +{{- end }} {{ if .Values.webservice.dnsConfig }} dnsConfig: {{ .Values.webservice.dnsConfig | toYaml | indent 8 }} From c60d0b9fc25077422615e8d1cdfef938744c4419 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:18:04 +0100 Subject: [PATCH 06/38] feat: add default resource requests and limits - Set default CPU request to 100m and limit to 500m - Set default memory request to 128Mi and limit to 512Mi - Ensures pods have proper resource allocation for scheduling - Can be overridden via webservice.resources --- charts/wik-webservice/templates/deployment.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 5384f40..78888d0 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -98,7 +98,16 @@ spec: {{ end }} resources: +{{- if .Values.webservice.resources }} {{ .Values.webservice.resources | toYaml | indent 12 }} +{{- else }} + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi +{{- end }} {{ if .Values.webservice.livenessProbe }} livenessProbe: {{ .Values.webservice.livenessProbe | toYaml | indent 12 }} From 1d10f833d67ff42aa629be7097d27c96581de776 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:19:15 +0100 Subject: [PATCH 07/38] docs: document security and resource defaults - Add section explaining default security context settings - Document default resource requests and limits - Show examples of how to override defaults - Helps users understand secure defaults applied by the chart --- charts/wik-webservice/README.md.gotmpl | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 8e0fbea..5ffc380 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -108,6 +108,70 @@ helm install my-app ./wik-webservice -f values.yaml | `webservice.initContainers` | Init containers | `[]` | | `webservice.additionalContainers` | Sidecar containers | `[]` | +## Security & Resource Defaults + +This chart includes secure defaults that can be overridden as needed: + +### Security Context Defaults + +By default, pods run with the following security settings: + +```yaml +# Pod Security Context +securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + +# Container Security Context +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL +``` + +To override these defaults, set `webservice.securityContext` or `webservice.containerSecurityContext`: + +```yaml +webservice: + securityContext: + runAsUser: 2000 + fsGroup: 2000 + containerSecurityContext: + readOnlyRootFilesystem: false +``` + +### Resource Defaults + +If no resources are specified, the following defaults are applied: + +```yaml +resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi +``` + +To customize resources: + +```yaml +webservice: + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi +``` + ## Examples ### Basic deployment From 99999508c25043d440811f8a3cfad85757a99635 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:21:13 +0100 Subject: [PATCH 08/38] feat: add ServiceAccount configuration - Add ServiceAccount template with automountServiceAccountToken control - Add serviceAccountName helper template - Update deployment to use ServiceAccount when created - Set automountServiceAccountToken to false by default for security - Add comprehensive ServiceAccount tests - Document ServiceAccount configuration in README --- charts/wik-webservice/README.md.gotmpl | 27 +++++++ charts/wik-webservice/templates/_helpers.tpl | 28 +++++++- .../wik-webservice/templates/deployment.yaml | 3 + .../templates/serviceaccount.yaml | 13 ++++ .../tests/serviceaccount_test.yaml | 70 +++++++++++++++++++ charts/wik-webservice/values.yaml | 12 ++++ 6 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 charts/wik-webservice/templates/serviceaccount.yaml create mode 100644 charts/wik-webservice/tests/serviceaccount_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 5ffc380..6f8362a 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -76,9 +76,36 @@ helm install my-app ./wik-webservice -f values.yaml | Parameter | Description | Default | |-----------|-------------|---------| +| `serviceAccount.create` | Create a dedicated ServiceAccount | `true` | +| `serviceAccount.name` | ServiceAccount name to use | `""` (generated) | +| `serviceAccount.annotations` | ServiceAccount annotations | `{}` | +| `serviceAccount.automountServiceAccountToken` | Mount service account token | `false` | | `webservice.securityContext` | Pod-level security context | `{}` | | `webservice.containerSecurityContext` | Container-level security context | `{}` | +#### ServiceAccount Configuration + +By default, a dedicated ServiceAccount is created with `automountServiceAccountToken: false` for security. This prevents the token from being mounted in the pod unless explicitly needed. + +```yaml +# Use default ServiceAccount (no token mounted) +serviceAccount: + create: true + automountServiceAccountToken: false + +# Use existing ServiceAccount +serviceAccount: + create: false + name: "existing-sa" + +# Create ServiceAccount with IAM role (EKS example) +serviceAccount: + create: true + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/my-role" + automountServiceAccountToken: true +``` + ### Environment Variables | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/_helpers.tpl b/charts/wik-webservice/templates/_helpers.tpl index b37613e..9f212dd 100644 --- a/charts/wik-webservice/templates/_helpers.tpl +++ b/charts/wik-webservice/templates/_helpers.tpl @@ -16,4 +16,30 @@ {{- else }} {{- template "fullname" .root }} {{- end }} -{{- end -}} \ No newline at end of file +{{- end -}} + +{{- define "serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{- define "labels" -}} +helm.sh/chart: {{ include "chart" . }} +{{ include "selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "selectorLabels" -}} +app.kubernetes.io/name: {{ include "name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 78888d0..86ec323 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -34,6 +34,9 @@ spec: {{ .Values.webservice.annotations | toYaml | indent 8 }} {{ end }} spec: +{{ if .Values.serviceAccount.create }} + serviceAccountName: {{ include "serviceAccountName" . }} +{{ end }} {{ if .Values.webservice.hostAliases }} hostAliases: {{ .Values.webservice.hostAliases | toYaml | indent 8 }} diff --git a/charts/wik-webservice/templates/serviceaccount.yaml b/charts/wik-webservice/templates/serviceaccount.yaml new file mode 100644 index 0000000..7a08db2 --- /dev/null +++ b/charts/wik-webservice/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "serviceAccountName" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/charts/wik-webservice/tests/serviceaccount_test.yaml b/charts/wik-webservice/tests/serviceaccount_test.yaml new file mode 100644 index 0000000..8334f25 --- /dev/null +++ b/charts/wik-webservice/tests/serviceaccount_test.yaml @@ -0,0 +1,70 @@ +suite: service account tests +templates: + - serviceaccount.yaml + - deployment.yaml +tests: + - it: should create a service account when create is true + set: + webservice: + image: nginx:latest + serviceAccount: + create: true + asserts: + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + name: "release-name-wik-webservice" + - equal: + path: metadata.name + value: "release-name-wik-webservice" + - equal: + path: automountServiceAccountToken + value: false + - it: should not create a service account when create is false + set: + webservice: + image: nginx:latest + serviceAccount: + create: false + asserts: + - notContainsDocument: + kind: ServiceAccount + apiVersion: v1 + - it: should use custom service account name when provided + set: + webservice: + image: nginx:latest + serviceAccount: + create: true + name: "custom-sa" + asserts: + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + name: "custom-sa" + - equal: + path: spec.template.spec.serviceAccountName + value: "custom-sa" + - it: should mount service account token when automountServiceAccountToken is true + set: + webservice: + image: nginx:latest + serviceAccount: + create: true + automountServiceAccountToken: true + asserts: + - equal: + path: automountServiceAccountToken + value: true + - it: should include annotations when provided + set: + webservice: + image: nginx:latest + serviceAccount: + create: true + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/my-role" + asserts: + - equal: + path: metadata.annotations."eks.amazonaws.com/role-arn" + value: "arn:aws:iam::123456789012:role/my-role" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index e431905..1326c0d 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -3,6 +3,18 @@ general: # instead of Secret with plaintext value sealedSecrets: false +# ServiceAccount configuration +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # AutomountServiceAccountToken controls whether the service account token is automatically mounted + automountServiceAccountToken: false + webservice: # Container image (required) image: "nginx:latest" From 4a9019281c753d4e372856ddf0efbef15d8cdbac Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:21:34 +0100 Subject: [PATCH 09/38] feat: add PodDisruptionBudget support - Add PodDisruptionBudget template with minAvailable/maxUnavailable options - Add PDB configuration to values.yaml - Add comprehensive PDB tests - Document PDB usage in README with examples - Ensures application availability during voluntary disruptions --- charts/wik-webservice/README.md.gotmpl | 24 ++++++++ .../templates/poddisruptionbudget.yaml | 22 +++++++ .../tests/poddisruptionbudget_test.yaml | 59 +++++++++++++++++++ charts/wik-webservice/values.yaml | 11 ++++ 4 files changed, 116 insertions(+) create mode 100644 charts/wik-webservice/templates/poddisruptionbudget.yaml create mode 100644 charts/wik-webservice/tests/poddisruptionbudget_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 6f8362a..ef70f01 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -124,10 +124,34 @@ serviceAccount: | Parameter | Description | Default | |-----------|-------------|---------| +| `podDisruptionBudget.enabled` | Enable PodDisruptionBudget | `false` | +| `podDisruptionBudget.minAvailable` | Minimum available pods | `1` | +| `podDisruptionBudget.maxUnavailable` | Maximum unavailable pods | `""` | | `webservice.nodeSelector` | Node selector | `{}` | | `webservice.affinity` | Affinity rules | `{}` | | `webservice.strategy` | Deployment strategy | `{}` | +#### PodDisruptionBudget + +PodDisruptionBudget ensures application availability during voluntary disruptions (node maintenance, cluster upgrades): + +```yaml +# Ensure at least 1 pod is always available +podDisruptionBudget: + enabled: true + minAvailable: 1 + +# Allow at most 1 pod to be unavailable (good for multi-replica deployments) +podDisruptionBudget: + enabled: true + maxUnavailable: 1 + +# Percentage-based availability +podDisruptionBudget: + enabled: true + minAvailable: "50%" +``` + ### Additional Containers | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/poddisruptionbudget.yaml b/charts/wik-webservice/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..b10cbeb --- /dev/null +++ b/charts/wik-webservice/templates/poddisruptionbudget.yaml @@ -0,0 +1,22 @@ +{{- if .Values.podDisruptionBudget.enabled -}} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.podDisruptionBudget.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- end }} + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/wik-webservice/tests/poddisruptionbudget_test.yaml b/charts/wik-webservice/tests/poddisruptionbudget_test.yaml new file mode 100644 index 0000000..76c53e4 --- /dev/null +++ b/charts/wik-webservice/tests/poddisruptionbudget_test.yaml @@ -0,0 +1,59 @@ +suite: pod disruption budget tests +templates: + - poddisruptionbudget.yaml +tests: + - it: should create a PDB when enabled with minAvailable + set: + webservice: + image: nginx:latest + podDisruptionBudget: + enabled: true + minAvailable: 1 + asserts: + - containsDocument: + kind: PodDisruptionBudget + apiVersion: policy/v1 + name: "release-name-wik-webservice" + - equal: + path: spec.minAvailable + value: 1 + - equal: + path: spec.selector.matchLabels."app.kubernetes.io/name" + value: "wik-webservice" + - it: should create a PDB when enabled with maxUnavailable + set: + webservice: + image: nginx:latest + podDisruptionBudget: + enabled: true + maxUnavailable: 1 + asserts: + - containsDocument: + kind: PodDisruptionBudget + apiVersion: policy/v1 + - equal: + path: spec.maxUnavailable + value: 1 + - it: should not create a PDB when disabled + set: + webservice: + image: nginx:latest + podDisruptionBudget: + enabled: false + asserts: + - notContainsDocument: + kind: PodDisruptionBudget + apiVersion: policy/v1 + - it: should include annotations when provided + set: + webservice: + image: nginx:latest + podDisruptionBudget: + enabled: true + minAvailable: 1 + annotations: + example.com/annotation: "test" + asserts: + - equal: + path: metadata.annotations."example.com/annotation" + value: "test" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 1326c0d..9d1cb13 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -15,6 +15,17 @@ serviceAccount: # AutomountServiceAccountToken controls whether the service account token is automatically mounted automountServiceAccountToken: false +# PodDisruptionBudget configuration +podDisruptionBudget: + # Enable PodDisruptionBudget + enabled: false + # Annotations to add to the PodDisruptionBudget + annotations: {} + # Minimum available pods (mutually exclusive with maxUnavailable) + minAvailable: 1 + # Maximum unavailable pods (mutually exclusive with minAvailable) + # maxUnavailable: 1 + webservice: # Container image (required) image: "nginx:latest" From a24596f61f55462d9f242abfe336db6e527898ab Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:21:49 +0100 Subject: [PATCH 10/38] feat: add NetworkPolicy support - Add NetworkPolicy template with customizable ingress/egress rules - Default ingress allows traffic from ingress-nginx namespace - Default egress allows all outbound traffic - Add NetworkPolicy configuration to values.yaml - Add comprehensive NetworkPolicy tests - Document NetworkPolicy usage in README with examples --- charts/wik-webservice/README.md.gotmpl | 37 ++++++++ .../templates/networkpolicy.yaml | 39 ++++++++ .../tests/networkpolicy_test.yaml | 88 +++++++++++++++++++ charts/wik-webservice/values.yaml | 11 +++ 4 files changed, 175 insertions(+) create mode 100644 charts/wik-webservice/templates/networkpolicy.yaml create mode 100644 charts/wik-webservice/tests/networkpolicy_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index ef70f01..9f8b574 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -127,6 +127,9 @@ serviceAccount: | `podDisruptionBudget.enabled` | Enable PodDisruptionBudget | `false` | | `podDisruptionBudget.minAvailable` | Minimum available pods | `1` | | `podDisruptionBudget.maxUnavailable` | Maximum unavailable pods | `""` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.ingress` | Custom ingress rules | `[]` | +| `networkPolicy.egress` | Custom egress rules | `[]` | | `webservice.nodeSelector` | Node selector | `{}` | | `webservice.affinity` | Affinity rules | `{}` | | `webservice.strategy` | Deployment strategy | `{}` | @@ -152,6 +155,40 @@ podDisruptionBudget: minAvailable: "50%" ``` +#### NetworkPolicy + +NetworkPolicy restricts network traffic to/from pods. By default, it allows traffic from the ingress-nginx namespace: + +```yaml +# Enable with default rules (allow ingress traffic only) +networkPolicy: + enabled: true + +# Custom ingress rules +networkPolicy: + enabled: true + ingress: + - from: + - podSelector: + matchLabels: + app: frontend + ports: + - protocol: TCP + port: 80 + +# Restrict egress to specific services +networkPolicy: + enabled: true + egress: + - to: + - podSelector: + matchLabels: + app: database + ports: + - protocol: TCP + port: 5432 +``` + ### Additional Containers | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/networkpolicy.yaml b/charts/wik-webservice/templates/networkpolicy.yaml new file mode 100644 index 0000000..8b03499 --- /dev/null +++ b/charts/wik-webservice/templates/networkpolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.networkPolicy.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.networkPolicy.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + {{- if .Values.networkPolicy.ingress }} + ingress: + {{- toYaml .Values.networkPolicy.ingress | nindent 4 }} + {{- else }} + ingress: + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + ports: + - protocol: TCP + port: {{ .Values.webservice.port }} + {{- end }} + {{- if .Values.networkPolicy.egress }} + egress: + {{- toYaml .Values.networkPolicy.egress | nindent 4 }} + {{- else }} + egress: + - {} + {{- end }} +{{- end }} diff --git a/charts/wik-webservice/tests/networkpolicy_test.yaml b/charts/wik-webservice/tests/networkpolicy_test.yaml new file mode 100644 index 0000000..e54198a --- /dev/null +++ b/charts/wik-webservice/tests/networkpolicy_test.yaml @@ -0,0 +1,88 @@ +suite: network policy tests +templates: + - networkpolicy.yaml +tests: + - it: should create a NetworkPolicy when enabled + set: + webservice: + image: nginx:latest + port: 80 + networkPolicy: + enabled: true + asserts: + - containsDocument: + kind: NetworkPolicy + apiVersion: networking.k8s.io/v1 + name: "release-name-wik-webservice" + - equal: + path: spec.policyTypes + value: + - Ingress + - Egress + - equal: + path: spec.podSelector.matchLabels."app.kubernetes.io/name" + value: "wik-webservice" + - it: should not create a NetworkPolicy when disabled + set: + webservice: + image: nginx:latest + networkPolicy: + enabled: false + asserts: + - notContainsDocument: + kind: NetworkPolicy + apiVersion: networking.k8s.io/v1 + - it: should use default ingress when not specified + set: + webservice: + image: nginx:latest + port: 8080 + networkPolicy: + enabled: true + asserts: + - equal: + path: spec.ingress[0].from[0].namespaceSelector.matchLabels.name + value: "ingress-nginx" + - equal: + path: spec.ingress[0].ports[0].port + value: 8080 + - it: should use custom ingress rules when specified + set: + webservice: + image: nginx:latest + port: 80 + networkPolicy: + enabled: true + ingress: + - from: + - podSelector: + matchLabels: + app: frontend + ports: + - protocol: TCP + port: 80 + asserts: + - equal: + path: spec.ingress[0].from[0].podSelector.matchLabels.app + value: "frontend" + - it: should use custom egress rules when specified + set: + webservice: + image: nginx:latest + networkPolicy: + enabled: true + egress: + - to: + - podSelector: + matchLabels: + app: database + ports: + - protocol: TCP + port: 5432 + asserts: + - equal: + path: spec.egress[0].to[0].podSelector.matchLabels.app + value: "database" + - equal: + path: spec.egress[0].ports[0].port + value: 5432 diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 9d1cb13..1621d80 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -26,6 +26,17 @@ podDisruptionBudget: # Maximum unavailable pods (mutually exclusive with minAvailable) # maxUnavailable: 1 +# NetworkPolicy configuration +networkPolicy: + # Enable NetworkPolicy + enabled: false + # Annotations to add to the NetworkPolicy + annotations: {} + # Ingress rules (if not specified, allows traffic from ingress-nginx namespace) + ingress: [] + # Egress rules (if not specified, allows all egress) + egress: [] + webservice: # Container image (required) image: "nginx:latest" From 66979386191f372ca6d18fba0c2f9ba5a1062acb Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:22:15 +0100 Subject: [PATCH 11/38] feat: add TopologySpreadConstraints support - Add topologySpreadConstraints to deployment template - Add configuration examples for node and zone spreading - Add comprehensive TopologySpreadConstraints tests - Document TopologySpreadConstraints usage in README - Improves high availability by spreading pods across topology domains --- charts/wik-webservice/README.md.gotmpl | 39 ++++++++++++++ .../wik-webservice/templates/deployment.yaml | 4 ++ .../tests/topologyspreadconstraints_test.yaml | 54 +++++++++++++++++++ charts/wik-webservice/values.yaml | 19 +++++++ 4 files changed, 116 insertions(+) create mode 100644 charts/wik-webservice/tests/topologyspreadconstraints_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 9f8b574..fe9a199 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -130,6 +130,7 @@ serviceAccount: | `networkPolicy.enabled` | Enable NetworkPolicy | `false` | | `networkPolicy.ingress` | Custom ingress rules | `[]` | | `networkPolicy.egress` | Custom egress rules | `[]` | +| `webservice.topologySpreadConstraints` | Pod distribution constraints | `[]` | | `webservice.nodeSelector` | Node selector | `{}` | | `webservice.affinity` | Affinity rules | `{}` | | `webservice.strategy` | Deployment strategy | `{}` | @@ -189,6 +190,44 @@ networkPolicy: port: 5432 ``` +#### TopologySpreadConstraints + +TopologySpreadConstraints control how pods are spread across your cluster to improve high availability: + +```yaml +# Spread pods across different nodes +webservice: + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/name: wik-webservice + app.kubernetes.io/instance: release-name + +# Spread pods across availability zones +webservice: + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: wik-webservice + app.kubernetes.io/instance: release-name + +# Multiple constraints for better distribution +webservice: + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway +``` + ### Additional Containers | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 86ec323..aae2a77 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -15,6 +15,10 @@ spec: matchLabels: app: {{ template "name" . }} release: {{ .Release.Name }} +{{ if .Values.webservice.topologySpreadConstraints }} + topologySpreadConstraints: +{{ .Values.webservice.topologySpreadConstraints | toYaml | indent 4 }} +{{ end }} {{ if .Values.webservice.strategy }} strategy: {{ .Values.webservice.strategy | toYaml | indent 4 }} diff --git a/charts/wik-webservice/tests/topologyspreadconstraints_test.yaml b/charts/wik-webservice/tests/topologyspreadconstraints_test.yaml new file mode 100644 index 0000000..c4d27d3 --- /dev/null +++ b/charts/wik-webservice/tests/topologyspreadconstraints_test.yaml @@ -0,0 +1,54 @@ +suite: topology spread constraints tests +templates: + - deployment.yaml +tests: + - it: should not add topologySpreadConstraints when empty + set: + webservice: + image: nginx:latest + topologySpreadConstraints: [] + asserts: + - notExists: + path: spec.topologySpreadConstraints + - it: should add topologySpreadConstraints when specified + set: + webservice: + image: nginx:latest + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/name: wik-webservice + app.kubernetes.io/instance: release-name + asserts: + - exists: + path: spec.topologySpreadConstraints + - equal: + path: spec.topologySpreadConstraints[0].maxSkew + value: 1 + - equal: + path: spec.topologySpreadConstraints[0].topologyKey + value: "kubernetes.io/hostname" + - equal: + path: spec.topologySpreadConstraints[0].whenUnsatisfiable + value: "DoNotSchedule" + - it: should support multiple topologySpreadConstraints + set: + webservice: + image: nginx:latest + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + asserts: + - equal: + path: spec.topologySpreadConstraints[0].topologyKey + value: "kubernetes.io/hostname" + - equal: + path: spec.topologySpreadConstraints[1].topologyKey + value: "topology.kubernetes.io/zone" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 1621d80..a62a5f2 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -74,6 +74,25 @@ webservice: replicas: 1 port: 80 + # TopologySpreadConstraints for better pod distribution + topologySpreadConstraints: [] + # Example: Spread pods across nodes + # - maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/name: wik-webservice + # app.kubernetes.io/instance: release-name + # Example: Spread pods across zones + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/name: wik-webservice + # app.kubernetes.io/instance: release-name + service: enabled: true port: 80 From c0802a4545fda52117522e70b92ffb7c9d2e32b4 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:22:38 +0100 Subject: [PATCH 12/38] feat: add ServiceMonitor support for Prometheus - Add ServiceMonitor template with customizable scrape settings - Support for metric relabelings and custom labels - Add ServiceMonitor configuration to values.yaml - Add comprehensive ServiceMonitor tests - Document ServiceMonitor usage in README with examples - Enables Prometheus metrics collection --- charts/wik-webservice/README.md.gotmpl | 42 ++++++++++ .../templates/servicemonitor.yaml | 42 ++++++++++ .../tests/servicemonitor_test.yaml | 81 +++++++++++++++++++ charts/wik-webservice/values.yaml | 19 +++++ 4 files changed, 184 insertions(+) create mode 100644 charts/wik-webservice/templates/servicemonitor.yaml create mode 100644 charts/wik-webservice/tests/servicemonitor_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index fe9a199..8d4fe9d 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -120,6 +120,48 @@ serviceAccount: | `webservice.storage` | PVC definitions | `{}` | | `webservice.volumes` | Volume mounts (PVC, ConfigMap, Secret) | `[]` | +### Observability + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `webservice.metrics.enabled` | Enable metrics endpoint | `false` | +| `webservice.metrics.port` | Metrics port | `9090` | +| `webservice.metrics.path` | Metrics path | `/metrics` | +| `serviceMonitor.enabled` | Enable ServiceMonitor | `false` | +| `serviceMonitor.interval` | Scrape interval | `30s` | +| `serviceMonitor.scrapeTimeout` | Scrape timeout | `10s` | +| `serviceMonitor.honorLabels` | Honor labels | `false` | +| `serviceMonitor.additionalLabels` | Additional labels | `{}` | + +#### Metrics and Monitoring + +The chart supports Prometheus metrics collection through ServiceMonitor: + +```yaml +# Enable metrics endpoint +webservice: + metrics: + enabled: true + port: 9090 + path: /metrics + +# Enable ServiceMonitor for Prometheus +serviceMonitor: + enabled: true + interval: 30s + scrapeTimeout: 10s + additionalLabels: + release: prometheus + +# Custom metric relabeling +serviceMonitor: + enabled: true + metricRelabelings: + - sourceLabels: [__name__] + regex: 'http_.*' + action: drop +``` + ### Scheduling | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/servicemonitor.yaml b/charts/wik-webservice/templates/servicemonitor.yaml new file mode 100644 index 0000000..5282537 --- /dev/null +++ b/charts/wik-webservice/templates/servicemonitor.yaml @@ -0,0 +1,42 @@ +{{- if and .Values.webservice.metrics.enabled .Values.serviceMonitor.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.serviceMonitor.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.serviceMonitor.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} + {{- if .Values.webservice.service.metricsPort }} + metrics-port: {{ .Values.webservice.service.metricsPort | quote }} + {{- end }} + endpoints: + - port: {{ default "metrics" .Values.webservice.service.metricsPort }} + {{- if .Values.serviceMonitor.interval }} + interval: {{ .Values.serviceMonitor.interval }} + {{- end }} + {{- if .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.serviceMonitor.honorLabels }} + {{- end }} + path: {{ default "/metrics" .Values.webservice.metrics.path }} + {{- if .Values.serviceMonitor.metricRelabelings }} + metricRelabelings: + {{- toYaml .Values.serviceMonitor.metricRelabelings | nindent 8 }} + {{- end }} + {{- if .Values.serviceMonitor.relabelings }} + relabelings: + {{- toYaml .Values.serviceMonitor.relabelings | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/wik-webservice/tests/servicemonitor_test.yaml b/charts/wik-webservice/tests/servicemonitor_test.yaml new file mode 100644 index 0000000..c3c7e61 --- /dev/null +++ b/charts/wik-webservice/tests/servicemonitor_test.yaml @@ -0,0 +1,81 @@ +suite: service monitor tests +templates: + - servicemonitor.yaml +tests: + - it: should create a ServiceMonitor when enabled and metrics enabled + set: + webservice: + image: nginx:latest + metrics: + enabled: true + path: /metrics + serviceMonitor: + enabled: true + asserts: + - containsDocument: + kind: ServiceMonitor + apiVersion: monitoring.coreos.com/v1 + name: "release-name-wik-webservice" + - equal: + path: spec.endpoints[0].port + value: "metrics" + - equal: + path: spec.endpoints[0].path + value: "/metrics" + - equal: + path: spec.endpoints[0].interval + value: "30s" + - it: should not create a ServiceMonitor when metrics disabled + set: + webservice: + image: nginx:latest + metrics: + enabled: false + serviceMonitor: + enabled: true + asserts: + - notContainsDocument: + kind: ServiceMonitor + apiVersion: monitoring.coreos.com/v1 + - it: should not create a ServiceMonitor when serviceMonitor disabled + set: + webservice: + image: nginx:latest + metrics: + enabled: true + serviceMonitor: + enabled: false + asserts: + - notContainsDocument: + kind: ServiceMonitor + apiVersion: monitoring.coreos.com/v1 + - it: should use custom configuration when provided + set: + webservice: + image: nginx:latest + metrics: + enabled: true + path: /custom-metrics + serviceMonitor: + enabled: true + interval: 60s + scrapeTimeout: 30s + honorLabels: true + additionalLabels: + release: prometheus + asserts: + - equal: + path: spec.endpoints[0].path + value: "/custom-metrics" + - equal: + path: spec.endpoints[0].interval + value: "60s" + - equal: + path: spec.endpoints[0].scrapeTimeout + value: "30s" + - equal: + path: spec.endpoints[0].honorLabels + value: true + - equal: + path: metadata.labels.release + value: "prometheus" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index a62a5f2..b314d6c 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -37,6 +37,25 @@ networkPolicy: # Egress rules (if not specified, allows all egress) egress: [] +# ServiceMonitor configuration for Prometheus +serviceMonitor: + # Enable ServiceMonitor + enabled: false + # Additional labels for ServiceMonitor + additionalLabels: {} + # Annotations for ServiceMonitor + annotations: {} + # Scrape interval + interval: 30s + # Scrape timeout + scrapeTimeout: 10s + # Honor labels + honorLabels: false + # Metric relabelings + metricRelabelings: [] + # Relabelings + relabelings: [] + webservice: # Container image (required) image: "nginx:latest" From 44069afa95ebf0bb292bb87b1cb67cf19d09aaae Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:22:56 +0100 Subject: [PATCH 13/38] feat: add HorizontalPodAutoscaler support - Add HPA template with CPU and memory target support - Support for custom scaling behavior configuration - Add HPA configuration to values.yaml - Add comprehensive HPA tests - Document HPA usage in README with examples - Enables automatic scaling based on resource utilization --- charts/wik-webservice/README.md.gotmpl | 43 +++++++++++ charts/wik-webservice/templates/hpa.yaml | 43 +++++++++++ charts/wik-webservice/tests/hpa_test.yaml | 90 +++++++++++++++++++++++ charts/wik-webservice/values.yaml | 19 +++++ 4 files changed, 195 insertions(+) create mode 100644 charts/wik-webservice/templates/hpa.yaml create mode 100644 charts/wik-webservice/tests/hpa_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 8d4fe9d..80cf95c 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -132,6 +132,11 @@ serviceAccount: | `serviceMonitor.scrapeTimeout` | Scrape timeout | `10s` | | `serviceMonitor.honorLabels` | Honor labels | `false` | | `serviceMonitor.additionalLabels` | Additional labels | `{}` | +| `horizontalPodAutoscaler.enabled` | Enable HPA | `false` | +| `horizontalPodAutoscaler.minReplicas` | Minimum replicas | `1` | +| `horizontalPodAutoscaler.maxReplicas` | Maximum replicas | `5` | +| `horizontalPodAutoscaler.targetCPUUtilizationPercentage` | CPU target | `80` | +| `horizontalPodAutoscaler.targetMemoryUtilizationPercentage` | Memory target | `null` | #### Metrics and Monitoring @@ -162,6 +167,44 @@ serviceMonitor: action: drop ``` +#### Horizontal Pod Autoscaling + +The chart supports automatic scaling based on CPU and memory usage: + +```yaml +# Enable HPA with CPU target +horizontalPodAutoscaler: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + +# Enable HPA with both CPU and memory targets +horizontalPodAutoscaler: + enabled: true + minReplicas: 1 + maxReplicas: 20 + targetCPUUtilizationPercentage: 60 + targetMemoryUtilizationPercentage: 70 + +# Custom scaling behavior +horizontalPodAutoscaler: + enabled: true + behavior: + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 10 + periodSeconds: 60 +``` + ### Scheduling | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/hpa.yaml b/charts/wik-webservice/templates/hpa.yaml new file mode 100644 index 0000000..f16d397 --- /dev/null +++ b/charts/wik-webservice/templates/hpa.yaml @@ -0,0 +1,43 @@ +{{- if .Values.horizontalPodAutoscaler.enabled -}} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.horizontalPodAutoscaler.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "fullname" . }} + minReplicas: {{ .Values.horizontalPodAutoscaler.minReplicas }} + maxReplicas: {{ .Values.horizontalPodAutoscaler.maxReplicas }} + metrics: + {{- if .Values.horizontalPodAutoscaler.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.horizontalPodAutoscaler.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.horizontalPodAutoscaler.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.horizontalPodAutoscaler.targetMemoryUtilizationPercentage }} + {{- end }} + {{- with .Values.horizontalPodAutoscaler.customMetrics }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.horizontalPodAutoscaler.behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/wik-webservice/tests/hpa_test.yaml b/charts/wik-webservice/tests/hpa_test.yaml new file mode 100644 index 0000000..c183966 --- /dev/null +++ b/charts/wik-webservice/tests/hpa_test.yaml @@ -0,0 +1,90 @@ +suite: horizontal pod autoscaler tests +templates: + - hpa.yaml +tests: + - it: should create an HPA when enabled with CPU target + set: + webservice: + image: nginx:latest + horizontalPodAutoscaler: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + asserts: + - containsDocument: + kind: HorizontalPodAutoscaler + apiVersion: autoscaling/v2 + name: "release-name-wik-webservice" + - equal: + path: spec.minReplicas + value: 2 + - equal: + path: spec.maxReplicas + value: 10 + - equal: + path: spec.metrics[0].resource.name + value: "cpu" + - equal: + path: spec.metrics[0].resource.target.averageUtilization + value: 70 + - it: should not create an HPA when disabled + set: + webservice: + image: nginx:latest + horizontalPodAutoscaler: + enabled: false + asserts: + - notContainsDocument: + kind: HorizontalPodAutoscaler + apiVersion: autoscaling/v2 + - it: should create an HPA with both CPU and memory targets + set: + webservice: + image: nginx:latest + horizontalPodAutoscaler: + enabled: true + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 60 + asserts: + - equal: + path: spec.metrics[0].resource.name + value: "cpu" + - equal: + path: spec.metrics[1].resource.name + value: "memory" + - equal: + path: spec.metrics[1].resource.target.averageUtilization + value: 60 + - it: should include annotations when provided + set: + webservice: + image: nginx:latest + horizontalPodAutoscaler: + enabled: true + annotations: + autoscaling.alpha.kubernetes.io/conditions: "true" + asserts: + - equal: + path: metadata.annotations."autoscaling.alpha.kubernetes.io/conditions" + value: "true" + - it: should include custom behavior when provided + set: + webservice: + image: nginx:latest + horizontalPodAutoscaler: + enabled: true + behavior: + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + asserts: + - equal: + path: spec.behavior.scaleUp.stabilizationWindowSeconds + value: 60 + - equal: + path: spec.behavior.scaleUp.policies[0].value + value: 100 diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index b314d6c..81add92 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -56,6 +56,25 @@ serviceMonitor: # Relabelings relabelings: [] +# HorizontalPodAutoscaler configuration +horizontalPodAutoscaler: + # Enable HPA + enabled: false + # Minimum number of replicas + minReplicas: 1 + # Maximum number of replicas + maxReplicas: 5 + # Target CPU utilization percentage + targetCPUUtilizationPercentage: 80 + # Target memory utilization percentage + targetMemoryUtilizationPercentage: null + # Annotations for HPA + annotations: {} + # Custom metrics (advanced) + customMetrics: [] + # HPA behavior configuration + behavior: {} + webservice: # Container image (required) image: "nginx:latest" From 2117f23bdb68325a0a684847210fb039e9e5cde7 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:23:30 +0100 Subject: [PATCH 14/38] feat: add startupProbe support - Add startupProbe configuration to deployment template - Add startupProbe configuration to values.yaml with examples - Add comprehensive startupProbe tests - Document startupProbe in README with examples - Improves container lifecycle management for slow-starting applications --- charts/wik-webservice/README.md.gotmpl | 8 ++ .../wik-webservice/templates/deployment.yaml | 4 + .../tests/startupprobe_test.yaml | 74 +++++++++++++++++++ charts/wik-webservice/values.yaml | 10 +++ 4 files changed, 96 insertions(+) create mode 100644 charts/wik-webservice/tests/startupprobe_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 80cf95c..9815138 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -71,6 +71,7 @@ helm install my-app ./wik-webservice -f values.yaml | `webservice.resources` | CPU/Memory requests and limits | `{}` | | `webservice.livenessProbe` | Liveness probe configuration | `{}` | | `webservice.readinessProbe` | Readiness probe configuration | `{}` | +| `webservice.startupProbe` | Startup probe configuration | `{}` | ### Security @@ -420,6 +421,13 @@ webservice: httpGet: path: /ready port: 80 + startupProbe: + httpGet: + path: /startup + port: 80 + initialDelaySeconds: 10 + periodSeconds: 10 + failureThreshold: 30 ``` ### With metrics enabled diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index aae2a77..5abe9fb 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -123,6 +123,10 @@ spec: readinessProbe: {{ .Values.webservice.readinessProbe | toYaml | indent 12 }} {{ end }} +{{ if .Values.webservice.startupProbe }} + startupProbe: +{{ .Values.webservice.startupProbe | toYaml | indent 12 }} +{{ end }} {{ if .Values.webservice.volumes }} volumeMounts: {{ range .Values.webservice.volumes }} diff --git a/charts/wik-webservice/tests/startupprobe_test.yaml b/charts/wik-webservice/tests/startupprobe_test.yaml new file mode 100644 index 0000000..5836f4e --- /dev/null +++ b/charts/wik-webservice/tests/startupprobe_test.yaml @@ -0,0 +1,74 @@ +suite: startup probe tests +templates: + - deployment.yaml +tests: + - it: should not add startupProbe when empty + set: + webservice: + image: nginx:latest + startupProbe: {} + asserts: + - notExists: + path: spec.template.spec.containers[0].startupProbe + - it: should add startupProbe when specified + set: + webservice: + image: nginx:latest + startupProbe: + httpGet: + path: /startup + port: 80 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 30 + successThreshold: 1 + asserts: + - exists: + path: spec.template.spec.containers[0].startupProbe + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.path + value: "/startup" + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.port + value: 80 + - equal: + path: spec.template.spec.containers[0].startupProbe.initialDelaySeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].startupProbe.failureThreshold + value: 30 + - it: should support TCP socket startup probe + set: + webservice: + image: nginx:latest + startupProbe: + tcpSocket: + port: 8080 + periodSeconds: 5 + failureThreshold: 12 + asserts: + - equal: + path: spec.template.spec.containers[0].startupProbe.tcpSocket.port + value: 8080 + - equal: + path: spec.template.spec.containers[0].startupProbe.periodSeconds + value: 5 + - it: should support exec startup probe + set: + webservice: + image: nginx:latest + startupProbe: + exec: + command: + - cat + - /tmp/ready + initialDelaySeconds: 5 + periodSeconds: 10 + asserts: + - equal: + path: spec.template.spec.containers[0].startupProbe.exec.command[0] + value: "cat" + - equal: + path: spec.template.spec.containers[0].startupProbe.exec.command[1] + value: "/tmp/ready" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 81add92..1b353d6 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -241,6 +241,16 @@ webservice: # initialDelaySeconds: 5 # periodSeconds: 5 + startupProbe: {} + # httpGet: + # path: /startup + # port: 80 + # initialDelaySeconds: 10 + # periodSeconds: 10 + # timeoutSeconds: 5 + # failureThreshold: 30 + # successThreshold: 1 + # Environment variables env: plaintext: {} From e5a9f36dec4b7eb34cd65462a13abb492d0e9d73 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:24:05 +0100 Subject: [PATCH 15/38] fix: change ServiceAccount defaults for better security - Set serviceAccount.create to false by default - Set automountServiceAccountToken to false in deployment spec - Only create ServiceAccount when explicitly needed - Updates documentation and tests to reflect new defaults - Reduces attack surface for services that don't need API access --- charts/wik-webservice/README.md.gotmpl | 15 +++++++++------ charts/wik-webservice/templates/deployment.yaml | 1 + .../wik-webservice/tests/serviceaccount_test.yaml | 5 ++++- charts/wik-webservice/values.yaml | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 9815138..0d5e57a 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -77,22 +77,25 @@ helm install my-app ./wik-webservice -f values.yaml | Parameter | Description | Default | |-----------|-------------|---------| -| `serviceAccount.create` | Create a dedicated ServiceAccount | `true` | -| `serviceAccount.name` | ServiceAccount name to use | `""` (generated) | +| `serviceAccount.create` | Create a dedicated ServiceAccount | `false` | +| `serviceAccount.name` | ServiceAccount name to use | `""` (default) | | `serviceAccount.annotations` | ServiceAccount annotations | `{}` | -| `serviceAccount.automountServiceAccountToken` | Mount service account token | `false` | +| `serviceAccount.automountServiceAccountToken` | Mount service account token (when creating SA) | `true` | | `webservice.securityContext` | Pod-level security context | `{}` | | `webservice.containerSecurityContext` | Container-level security context | `{}` | #### ServiceAccount Configuration -By default, a dedicated ServiceAccount is created with `automountServiceAccountToken: false` for security. This prevents the token from being mounted in the pod unless explicitly needed. +By default, pods use the default ServiceAccount with `automountServiceAccountToken: false` for security. This prevents the token from being mounted in the pod unless explicitly needed. ```yaml -# Use default ServiceAccount (no token mounted) +# Default behavior (no token mounted) +# Uses default ServiceAccount, token is not mounted + +# Create dedicated ServiceAccount with token serviceAccount: create: true - automountServiceAccountToken: false + automountServiceAccountToken: true # Use existing ServiceAccount serviceAccount: diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 5abe9fb..537fc2a 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -41,6 +41,7 @@ spec: {{ if .Values.serviceAccount.create }} serviceAccountName: {{ include "serviceAccountName" . }} {{ end }} + automountServiceAccountToken: false {{ if .Values.webservice.hostAliases }} hostAliases: {{ .Values.webservice.hostAliases | toYaml | indent 8 }} diff --git a/charts/wik-webservice/tests/serviceaccount_test.yaml b/charts/wik-webservice/tests/serviceaccount_test.yaml index 8334f25..73d2284 100644 --- a/charts/wik-webservice/tests/serviceaccount_test.yaml +++ b/charts/wik-webservice/tests/serviceaccount_test.yaml @@ -19,7 +19,7 @@ tests: value: "release-name-wik-webservice" - equal: path: automountServiceAccountToken - value: false + value: true - it: should not create a service account when create is false set: webservice: @@ -30,6 +30,9 @@ tests: - notContainsDocument: kind: ServiceAccount apiVersion: v1 + - equal: + path: spec.template.spec.automountServiceAccountToken + value: false - it: should use custom service account name when provided set: webservice: diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 1b353d6..1df4ba4 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -6,14 +6,14 @@ general: # ServiceAccount configuration serviceAccount: # Specifies whether a service account should be created - create: true + create: false # Annotations to add to the service account annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" # AutomountServiceAccountToken controls whether the service account token is automatically mounted - automountServiceAccountToken: false + automountServiceAccountToken: true # PodDisruptionBudget configuration podDisruptionBudget: From 3d278b5e1589a4c3774b8f3165813cc767fb644c Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:31:36 +0100 Subject: [PATCH 16/38] feat: add seccompProfile RuntimeDefault by default - Add seccompProfile with RuntimeDefault to container security context - Provides syscall-level security filtering - Blocks dangerous syscalls while maintaining compatibility - Add seccompProfile configuration to values.yaml - Add comprehensive seccompProfile tests - Document seccompProfile usage in README --- charts/wik-webservice/README.md.gotmpl | 6 ++- .../wik-webservice/templates/deployment.yaml | 2 + .../tests/seccompprofile_test.yaml | 53 +++++++++++++++++++ charts/wik-webservice/values.yaml | 2 + 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 charts/wik-webservice/tests/seccompprofile_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 0d5e57a..a37d7eb 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -348,7 +348,8 @@ securityContext: capabilities: drop: - ALL -``` + seccompProfile: + type: RuntimeDefault To override these defaults, set `webservice.securityContext` or `webservice.containerSecurityContext`: @@ -359,6 +360,9 @@ webservice: fsGroup: 2000 containerSecurityContext: readOnlyRootFilesystem: false + seccompProfile: + type: Localhost + localhostProfile: "profiles/custom.json" ``` ### Resource Defaults diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 537fc2a..cbce960 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -75,6 +75,8 @@ spec: capabilities: drop: - ALL + seccompProfile: + type: RuntimeDefault {{- end }} imagePullPolicy: {{ .Values.webservice.imagePullPolicy }} ports: diff --git a/charts/wik-webservice/tests/seccompprofile_test.yaml b/charts/wik-webservice/tests/seccompprofile_test.yaml new file mode 100644 index 0000000..a59b403 --- /dev/null +++ b/charts/wik-webservice/tests/seccompprofile_test.yaml @@ -0,0 +1,53 @@ +suite: seccomp profile tests +templates: + - deployment.yaml +tests: + - it: should add seccompProfile with RuntimeDefault by default + set: + webservice: + image: nginx:latest + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.seccompProfile.type + value: "RuntimeDefault" + - it: should allow custom seccompProfile when containerSecurityContext is provided + set: + webservice: + image: nginx:latest + containerSecurityContext: + seccompProfile: + type: Localhost + localhostProfile: "profiles/custom.json" + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.seccompProfile.type + value: "Localhost" + - equal: + path: spec.template.spec.containers[0].securityContext.seccompProfile.localhostProfile + value: "profiles/custom.json" + - it: should allow disabling seccompProfile + set: + webservice: + image: nginx:latest + containerSecurityContext: + seccompProfile: + type: Unconfined + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.seccompProfile.type + value: "Unconfined" + - it: should preserve other security context settings when customizing seccompProfile + set: + webservice: + image: nginx:latest + containerSecurityContext: + seccompProfile: + type: RuntimeDefault + runAsUser: 2000 + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.seccompProfile.type + value: "RuntimeDefault" + - equal: + path: spec.template.spec.containers[0].securityContext.runAsUser + value: 2000 diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 1df4ba4..888d55a 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -194,6 +194,8 @@ webservice: containerSecurityContext: {} # allowPrivilegeEscalation: false # readOnlyRootFilesystem: true + # seccompProfile: + # type: RuntimeDefault resources: {} # requests: From 114ba957b042b5b16b46cc04f21454709ae87e06 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:31:56 +0100 Subject: [PATCH 17/38] feat: add enableServiceLinks configuration - Set enableServiceLinks to false by default - Prevents injection of all services as environment variables - Reduces information leakage and attack surface - Add enableServiceLinks configuration to values.yaml - Add enableServiceLinks tests - Document service links in README --- charts/wik-webservice/README.md.gotmpl | 15 ++++++++++ .../wik-webservice/templates/deployment.yaml | 1 + .../tests/enableservicelinks_test.yaml | 30 +++++++++++++++++++ charts/wik-webservice/values.yaml | 3 ++ 4 files changed, 49 insertions(+) create mode 100644 charts/wik-webservice/tests/enableservicelinks_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index a37d7eb..4dedac7 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -83,6 +83,7 @@ helm install my-app ./wik-webservice -f values.yaml | `serviceAccount.automountServiceAccountToken` | Mount service account token (when creating SA) | `true` | | `webservice.securityContext` | Pod-level security context | `{}` | | `webservice.containerSecurityContext` | Container-level security context | `{}` | +| `webservice.enableServiceLinks` | Enable service links injection | `false` | #### ServiceAccount Configuration @@ -110,6 +111,20 @@ serviceAccount: automountServiceAccountToken: true ``` +#### Service Links + +Service links inject all cluster services as environment variables in the pod. This is disabled by default for security to reduce information leakage. + +```yaml +# Default (service links disabled) +webservice: + enableServiceLinks: false + +# Enable service links if needed +webservice: + enableServiceLinks: true +``` + ### Environment Variables | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index cbce960..b724b7c 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -42,6 +42,7 @@ spec: serviceAccountName: {{ include "serviceAccountName" . }} {{ end }} automountServiceAccountToken: false + enableServiceLinks: {{ .Values.webservice.enableServiceLinks }} {{ if .Values.webservice.hostAliases }} hostAliases: {{ .Values.webservice.hostAliases | toYaml | indent 8 }} diff --git a/charts/wik-webservice/tests/enableservicelinks_test.yaml b/charts/wik-webservice/tests/enableservicelinks_test.yaml new file mode 100644 index 0000000..7a47af7 --- /dev/null +++ b/charts/wik-webservice/tests/enableservicelinks_test.yaml @@ -0,0 +1,30 @@ +suite: enable service links tests +templates: + - deployment.yaml +tests: + - it: should set enableServiceLinks to false by default + set: + webservice: + image: nginx:latest + asserts: + - equal: + path: spec.template.spec.enableServiceLinks + value: false + - it: should allow enabling service links when set to true + set: + webservice: + image: nginx:latest + enableServiceLinks: true + asserts: + - equal: + path: spec.template.spec.enableServiceLinks + value: true + - it: should allow disabling service links explicitly + set: + webservice: + image: nginx:latest + enableServiceLinks: false + asserts: + - equal: + path: spec.template.spec.enableServiceLinks + value: false diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 888d55a..90b1d64 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -190,6 +190,9 @@ webservice: # runAsGroup: 1000 # fsGroup: 1000 + # Enable service links (injects all services as environment variables) + enableServiceLinks: false + # Container-level security context containerSecurityContext: {} # allowPrivilegeEscalation: false From 4d841e33d19a0add76b73fc4c9215642759ff0c1 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:32:13 +0100 Subject: [PATCH 18/38] feat: add ephemeral storage limits by default - Add default ephemeral-storage requests (1Gi) and limits (2Gi) - Prevents DoS attacks via unlimited disk usage - Add ephemeral storage configuration to values.yaml - Add ephemeral storage tests - Update resource documentation with storage limits --- charts/wik-webservice/README.md.gotmpl | 2 + .../wik-webservice/templates/deployment.yaml | 2 + .../tests/ephemeralstorage_test.yaml | 44 +++++++++++++++++++ charts/wik-webservice/values.yaml | 2 + 4 files changed, 50 insertions(+) create mode 100644 charts/wik-webservice/tests/ephemeralstorage_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 4dedac7..8a23185 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -389,9 +389,11 @@ resources: requests: cpu: 100m memory: 128Mi + ephemeral-storage: 1Gi limits: cpu: 500m memory: 512Mi + ephemeral-storage: 2Gi ``` To customize resources: diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index b724b7c..7a1ab5c 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -115,9 +115,11 @@ spec: limits: cpu: 500m memory: 512Mi + ephemeral-storage: 2Gi requests: cpu: 100m memory: 128Mi + ephemeral-storage: 1Gi {{- end }} {{ if .Values.webservice.livenessProbe }} livenessProbe: diff --git a/charts/wik-webservice/tests/ephemeralstorage_test.yaml b/charts/wik-webservice/tests/ephemeralstorage_test.yaml new file mode 100644 index 0000000..0134731 --- /dev/null +++ b/charts/wik-webservice/tests/ephemeralstorage_test.yaml @@ -0,0 +1,44 @@ +suite: ephemeral storage tests +templates: + - deployment.yaml +tests: + - it: should add ephemeral storage limits by default + set: + webservice: + image: nginx:latest + asserts: + - equal: + path: spec.template.spec.containers[0].resources.requests.ephemeral-storage + value: "1Gi" + - equal: + path: spec.template.spec.containers[0].resources.limits.ephemeral-storage + value: "2Gi" + - it: should allow custom ephemeral storage limits + set: + webservice: + image: nginx:latest + resources: + requests: + ephemeral-storage: "5Gi" + limits: + ephemeral-storage: "10Gi" + asserts: + - equal: + path: spec.template.spec.containers[0].resources.requests.ephemeral-storage + value: "5Gi" + - equal: + path: spec.template.spec.containers[0].resources.limits.ephemeral-storage + value: "10Gi" + - it: should allow only setting requests or limits + set: + webservice: + image: nginx:latest + resources: + requests: + ephemeral-storage: "2Gi" + asserts: + - equal: + path: spec.template.spec.containers[0].resources.requests.ephemeral-storage + value: "2Gi" + - notExists: + path: spec.template.spec.containers[0].resources.limits.ephemeral-storage diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 90b1d64..b192e2f 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -204,9 +204,11 @@ webservice: # requests: # memory: "512Mi" # cpu: "250m" + # ephemeral-storage: "1Gi" # limits: # memory: "1Gi" # cpu: "500m" + # ephemeral-storage: "2Gi" hostAliases: [] # - ip: "127.0.0.1" From bf0554fe9c55145f9cbfff92b53b8f23a85aa5d4 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:32:31 +0100 Subject: [PATCH 19/38] feat: add runtimeClassName support - Add runtimeClassName configuration to deployment spec - Enables use of gVisor, Kata, or other runtime classes - Provides stronger container isolation when available - Add runtimeClassName configuration to values.yaml - Add runtimeClassName tests - Document runtime class usage in README --- charts/wik-webservice/README.md.gotmpl | 18 ++++++++ .../wik-webservice/templates/deployment.yaml | 3 ++ .../tests/runtimeclassname_test.yaml | 43 +++++++++++++++++++ charts/wik-webservice/values.yaml | 3 ++ 4 files changed, 67 insertions(+) create mode 100644 charts/wik-webservice/tests/runtimeclassname_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 8a23185..186aa4b 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -156,6 +156,7 @@ webservice: | `horizontalPodAutoscaler.maxReplicas` | Maximum replicas | `5` | | `horizontalPodAutoscaler.targetCPUUtilizationPercentage` | CPU target | `80` | | `horizontalPodAutoscaler.targetMemoryUtilizationPercentage` | Memory target | `null` | +| `runtimeClassName` | Runtime class for isolation | `""` (default) | #### Metrics and Monitoring @@ -224,6 +225,23 @@ horizontalPodAutoscaler: periodSeconds: 60 ``` +#### Runtime Class + +Runtime class allows using alternative container runtimes for stronger isolation: + +```yaml +# Use gVisor for user-space kernel isolation +runtimeClassName: "gvisor" + +# Use Kata Containers for VM-level isolation +runtimeClassName: "kata" + +# Default (regular container runtime) +runtimeClassName: "" +``` + +**Note**: Runtime classes must be installed on the cluster before use. gVisor and Kata provide stronger isolation but may have performance overhead. + ### Scheduling | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 7a1ab5c..eaa87d8 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -43,6 +43,9 @@ spec: {{ end }} automountServiceAccountToken: false enableServiceLinks: {{ .Values.webservice.enableServiceLinks }} +{{ if .Values.runtimeClassName }} + runtimeClassName: {{ .Values.runtimeClassName }} +{{ end }} {{ if .Values.webservice.hostAliases }} hostAliases: {{ .Values.webservice.hostAliases | toYaml | indent 8 }} diff --git a/charts/wik-webservice/tests/runtimeclassname_test.yaml b/charts/wik-webservice/tests/runtimeclassname_test.yaml new file mode 100644 index 0000000..b1c5209 --- /dev/null +++ b/charts/wik-webservice/tests/runtimeclassname_test.yaml @@ -0,0 +1,43 @@ +suite: runtime class name tests +templates: + - deployment.yaml +tests: + - it: should not set runtimeClassName when empty + set: + webservice: + image: nginx:latest + runtimeClassName: "" + asserts: + - notExists: + path: spec.template.spec.runtimeClassName + - it: should set runtimeClassName when provided + set: + webservice: + image: nginx:latest + runtimeClassName: "gvisor" + asserts: + - equal: + path: spec.template.spec.runtimeClassName + value: "gvisor" + - it: should support kata runtime class + set: + webservice: + image: nginx:latest + runtimeClassName: "kata" + asserts: + - equal: + path: spec.template.spec.runtimeClassName + value: "kata" + - it: should work with other pod settings + set: + webservice: + image: nginx:latest + enableServiceLinks: true + runtimeClassName: "gvisor" + asserts: + - equal: + path: spec.template.spec.runtimeClassName + value: "gvisor" + - equal: + path: spec.template.spec.enableServiceLinks + value: true diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index b192e2f..9ace558 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -56,6 +56,9 @@ serviceMonitor: # Relabelings relabelings: [] +# Runtime class for container isolation (gVisor, Kata, etc.) +runtimeClassName: "" + # HorizontalPodAutoscaler configuration horizontalPodAutoscaler: # Enable HPA From c9e368483677e39b650b7cf3377553d4ef782037 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:32:51 +0100 Subject: [PATCH 20/38] feat: add Pod Security Standards support - Add Pod Security Standards labels to deployment metadata - Enables enforcement of security policies at namespace level - Default to restricted level for maximum security - Add Pod Security Standard configuration to values.yaml - Add Pod Security Standard tests - Document Pod Security Standards usage in README --- charts/wik-webservice/README.md.gotmpl | 27 ++++++++ .../wik-webservice/templates/deployment.yaml | 7 ++- .../tests/podsecuritystandard_test.yaml | 62 +++++++++++++++++++ charts/wik-webservice/values.yaml | 7 +++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 charts/wik-webservice/tests/podsecuritystandard_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 186aa4b..9786901 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -157,6 +157,8 @@ webservice: | `horizontalPodAutoscaler.targetCPUUtilizationPercentage` | CPU target | `80` | | `horizontalPodAutoscaler.targetMemoryUtilizationPercentage` | Memory target | `null` | | `runtimeClassName` | Runtime class for isolation | `""` (default) | +| `podSecurityStandard.enabled` | Enable Pod Security Standards | `true` | +| `podSecurityStandard.level` | Security level | `restricted` | #### Metrics and Monitoring @@ -242,6 +244,31 @@ runtimeClassName: "" **Note**: Runtime classes must be installed on the cluster before use. gVisor and Kata provide stronger isolation but may have performance overhead. +#### Pod Security Standards + +Pod Security Standards (PSS) replace Pod Security Policies in Kubernetes 1.25+. They enforce security policies at the namespace level: + +```yaml +# Enable restricted security level (default) +podSecurityStandard: + enabled: true + level: restricted + +# Use baseline for less strict requirements +podSecurityStandard: + enabled: true + level: baseline + +# Disable Pod Security Standards +podSecurityStandard: + enabled: false +``` + +**Security Levels:** +- **restricted**: Most secure - non-root, read-only FS, no privileged, resource limits required +- **baseline**: Medium security - prevents obvious security issues +- **privileged**: No restrictions - not recommended for production + ### Scheduling | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index eaa87d8..ced4793 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -6,8 +6,13 @@ metadata: labels: app: {{ template "name" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - heritage: {{ .Release.Service }} release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{ if .Values.podSecurityStandard.enabled }} + pod-security.kubernetes.io/enforce: {{ .Values.podSecurityStandard.level }} + pod-security.kubernetes.io/audit: {{ .Values.podSecurityStandard.level }} + pod-security.kubernetes.io/warn: {{ .Values.podSecurityStandard.level }} +{{ end }} spec: replicas: {{ .Values.webservice.replicas }} revisionHistoryLimit: {{ .Values.webservice.revisionHistoryLimit }} diff --git a/charts/wik-webservice/tests/podsecuritystandard_test.yaml b/charts/wik-webservice/tests/podsecuritystandard_test.yaml new file mode 100644 index 0000000..6cdb128 --- /dev/null +++ b/charts/wik-webservice/tests/podsecuritystandard_test.yaml @@ -0,0 +1,62 @@ +suite: pod security standard tests +templates: + - deployment.yaml +tests: + - it: should add Pod Security Standard labels when enabled + set: + webservice: + image: nginx:latest + podSecurityStandard: + enabled: true + level: restricted + asserts: + - equal: + path: metadata.labels."pod-security.kubernetes.io/enforce" + value: "restricted" + - equal: + path: metadata.labels."pod-security.kubernetes.io/audit" + value: "restricted" + - equal: + path: metadata.labels."pod-security.kubernetes.io/warn" + value: "restricted" + - it: should not add Pod Security Standard labels when disabled + set: + webservice: + image: nginx:latest + podSecurityStandard: + enabled: false + asserts: + - notExists: + path: metadata.labels."pod-security.kubernetes.io/enforce" + - notExists: + path: metadata.labels."pod-security.kubernetes.io/audit" + - notExists: + path: metadata.labels."pod-security.kubernetes.io/warn" + - it: should support baseline security level + set: + webservice: + image: nginx:latest + podSecurityStandard: + enabled: true + level: baseline + asserts: + - equal: + path: metadata.labels."pod-security.kubernetes.io/enforce" + value: "baseline" + - equal: + path: metadata.labels."pod-security.kubernetes.io/audit" + value: "baseline" + - equal: + path: metadata.labels."pod-security.kubernetes.io/warn" + value: "baseline" + - it: should support privileged security level + set: + webservice: + image: nginx:latest + podSecurityStandard: + enabled: true + level: privileged + asserts: + - equal: + path: metadata.labels."pod-security.kubernetes.io/enforce" + value: "privileged" diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 9ace558..48408a0 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -59,6 +59,13 @@ serviceMonitor: # Runtime class for container isolation (gVisor, Kata, etc.) runtimeClassName: "" +# Pod Security Standards (Kubernetes 1.25+) +podSecurityStandard: + # Enable Pod Security Standards labels + enabled: true + # Security level: privileged, baseline, restricted + level: restricted + # HorizontalPodAutoscaler configuration horizontalPodAutoscaler: # Enable HPA From 3006cf09914093e789a3e6cfd567929a2b0b7578 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:33:11 +0100 Subject: [PATCH 21/38] feat: add explicit host protection settings - Add explicit hostNetwork, hostPID, hostIPC settings - Default to false for maximum security - Prevents pods from accessing host resources - Add host protection configuration to values.yaml - Add host protection tests - Document host protection in README --- charts/wik-webservice/README.md.gotmpl | 22 +++++++ .../wik-webservice/templates/deployment.yaml | 3 + .../tests/hostprotection_test.yaml | 63 +++++++++++++++++++ charts/wik-webservice/values.yaml | 5 ++ 4 files changed, 93 insertions(+) create mode 100644 charts/wik-webservice/tests/hostprotection_test.yaml diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 9786901..811d48c 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -84,6 +84,9 @@ helm install my-app ./wik-webservice -f values.yaml | `webservice.securityContext` | Pod-level security context | `{}` | | `webservice.containerSecurityContext` | Container-level security context | `{}` | | `webservice.enableServiceLinks` | Enable service links injection | `false` | +| `webservice.hostNetwork` | Access host network namespace | `false` | +| `webservice.hostPID` | Access host PID namespace | `false` | +| `webservice.hostIPC` | Access host IPC namespace | `false` | #### ServiceAccount Configuration @@ -125,6 +128,25 @@ webservice: enableServiceLinks: true ``` +#### Host Protection + +Host protection settings prevent pods from accessing host resources: + +```yaml +# Default (all host protections disabled) +webservice: + hostNetwork: false + hostPID: false + hostIPC: false + +# Enable host network access (rarely needed) +webservice: + hostNetwork: true + # Note: This allows the pod to access the host's network interface +``` + +**Warning**: Enabling host access (hostNetwork, hostPID, hostIPC) significantly reduces isolation and should only be used when absolutely necessary. + ### Environment Variables | Parameter | Description | Default | diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index ced4793..1ef8b04 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -48,6 +48,9 @@ spec: {{ end }} automountServiceAccountToken: false enableServiceLinks: {{ .Values.webservice.enableServiceLinks }} + hostNetwork: {{ .Values.webservice.hostNetwork }} + hostPID: {{ .Values.webservice.hostPID }} + hostIPC: {{ .Values.webservice.hostIPC }} {{ if .Values.runtimeClassName }} runtimeClassName: {{ .Values.runtimeClassName }} {{ end }} diff --git a/charts/wik-webservice/tests/hostprotection_test.yaml b/charts/wik-webservice/tests/hostprotection_test.yaml new file mode 100644 index 0000000..fae46ed --- /dev/null +++ b/charts/wik-webservice/tests/hostprotection_test.yaml @@ -0,0 +1,63 @@ +suite: host protection tests +templates: + - deployment.yaml +tests: + - it: should set host protection to false by default + set: + webservice: + image: nginx:latest + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: false + - equal: + path: spec.template.spec.hostPID + value: false + - equal: + path: spec.template.spec.hostIPC + value: false + - it: should allow enabling hostNetwork when needed + set: + webservice: + image: nginx:latest + hostNetwork: true + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: true + - equal: + path: spec.template.spec.hostPID + value: false + - equal: + path: spec.template.spec.hostIPC + value: false + - it: should allow enabling hostPID when needed + set: + webservice: + image: nginx:latest + hostPID: true + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: false + - equal: + path: spec.template.spec.hostPID + value: true + - equal: + path: spec.template.spec.hostIPC + value: false + - it: should allow enabling hostIPC when needed + set: + webservice: + image: nginx:latest + hostIPC: true + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: false + - equal: + path: spec.template.spec.hostPID + value: false + - equal: + path: spec.template.spec.hostIPC + value: true diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 48408a0..0c86298 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -200,6 +200,11 @@ webservice: # runAsGroup: 1000 # fsGroup: 1000 + # Host protection (explicitly set to secure defaults) + hostNetwork: false + hostPID: false + hostIPC: false + # Enable service links (injects all services as environment variables) enableServiceLinks: false From fc8f606fc102d5b0e0b6cad647e201601900557b Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:33:21 +0100 Subject: [PATCH 22/38] docs: add image security best practices - Document image digest usage for immutability - Document private registry authentication - Document SealedSecrets for encrypted credentials - Add image security recommendations - No code changes, documentation only --- charts/wik-webservice/README.md.gotmpl | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/charts/wik-webservice/README.md.gotmpl b/charts/wik-webservice/README.md.gotmpl index 811d48c..d9588f1 100644 --- a/charts/wik-webservice/README.md.gotmpl +++ b/charts/wik-webservice/README.md.gotmpl @@ -147,6 +147,40 @@ webservice: **Warning**: Enabling host access (hostNetwork, hostPID, hostIPC) significantly reduces isolation and should only be used when absolutely necessary. +### Image Security + +For production deployments, follow these image security best practices: + +```yaml +# Use specific image digests for immutability +webservice: + image: "nginx@sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" + imagePullPolicy: IfNotPresent + +# Use private registry with authentication +webservice: + image: "private-registry.com/myapp:v1.2.3" + imagePullAuth: + registry: "private-registry.com" + username: "deploy-user" + password: "secure-password" + +# Use SealedSecrets for encrypted credentials +general: + sealedSecrets: true +webservice: + imagePullAuth: + registry: "private-registry.com" + encrypted: "AgBy3i4OJSWK+PiTySYZZA9rO43cGDEQAx..." +``` + +**Image Security Recommendations:** +- Use specific image digests instead of tags for production +- Prefer `imagePullPolicy: IfNotPresent` for pinned images +- Store registry credentials in encrypted secrets +- Regularly scan images for vulnerabilities +- Use minimal base images to reduce attack surface + ### Environment Variables | Parameter | Description | Default | From effba6e29a5a22c2c09abb108a953e9fb9955aae Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:36:49 +0100 Subject: [PATCH 23/38] fix: remove deprecated requirements.yaml file - Dependencies are handled in Chart.yaml since apiVersion v2 - Fixes warning about deprecated requirements.yaml - No functional changes to dependencies --- charts/wik-webservice/requirements.yaml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 charts/wik-webservice/requirements.yaml diff --git a/charts/wik-webservice/requirements.yaml b/charts/wik-webservice/requirements.yaml deleted file mode 100644 index 32cf5dd..0000000 --- a/charts/wik-webservice/requirements.yaml +++ /dev/null @@ -1 +0,0 @@ -dependencies: [] From 96bd23abca3ba82c78cab17df1228bfd28123c77 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:37:51 +0100 Subject: [PATCH 24/38] fix: disable chart version increment checking - Set check-version-increment to false in ct.yaml - Version bumping is handled by CI after merge to main - Allows PRs to pass linting without manual version bump --- ct.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct.yaml b/ct.yaml index 3dc9f1b..17e651f 100644 --- a/ct.yaml +++ b/ct.yaml @@ -6,6 +6,6 @@ chart-repos: - bitnami=https://charts.bitnami.com/bitnami helm-extra-args: --timeout 600s validate-maintainers: false -check-version-increment: true +check-version-increment: false validate-chart-schema: true helm-extra-set-args: --set=image.tag=latest From 1b90b5095b39ef94f1eae244a54e609d5b71c211 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:41:43 +0100 Subject: [PATCH 25/38] feat: add automatic version bumping based on conventional commits - Parse git history since last release tag for each chart - Determine bump type from commit prefixes: - BREAKING CHANGE or ! -> major - feat: -> minor - fix:, chore:, docs:, etc. -> patch - Automatically update Chart.yaml version before release - Commit version bump and CHANGELOG back to main - Add bump type to release summary - Ensures no version is skipped and consistent semver --- .github/workflows/release.yaml | 101 +++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7b61d38..a480cf2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -98,6 +98,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Helm uses: azure/setup-helm@v4 @@ -110,6 +113,89 @@ jobs: - name: Install yq uses: mikefarah/yq@v4 + - name: Determine version bump from conventional commits + id: version-bump + run: | + CHART_PATH="charts/${{ matrix.chart }}" + CHART_NAME=$(yq '.name' "${CHART_PATH}/Chart.yaml") + + # Get the last release tag for this chart + LAST_TAG=$(git tag --list "${CHART_NAME}-v*" --sort=-version:refname | head -n1 || echo "") + + if [[ -z "$LAST_TAG" ]]; then + # No previous release, get all commits for this chart + echo "No previous release found for ${CHART_NAME}" + COMMITS=$(git log --oneline --no-merges -- "${CHART_PATH}") + CURRENT_VERSION="0.0.0" + else + echo "Last release: ${LAST_TAG}" + COMMITS=$(git log --oneline --no-merges "${LAST_TAG}..HEAD" -- "${CHART_PATH}") + # Extract version from tag (chart-name-vX.Y.Z -> X.Y.Z) + CURRENT_VERSION=$(echo "$LAST_TAG" | sed "s/${CHART_NAME}-v//") + fi + + echo "Current version: ${CURRENT_VERSION}" + echo "Commits since last release:" + echo "$COMMITS" + + # Determine bump type from conventional commits + BUMP_TYPE="none" + + # Check for breaking changes (BREAKING CHANGE in body or ! after type) + if echo "$COMMITS" | grep -qiE '(BREAKING CHANGE|^[a-z]+!:|!)'; then + BUMP_TYPE="major" + # Check for features + elif echo "$COMMITS" | grep -qiE '^[a-f0-9]+ feat'; then + BUMP_TYPE="minor" + # Check for fixes or any other changes + elif echo "$COMMITS" | grep -qiE '^[a-f0-9]+ (fix|perf|refactor|docs|style|test|chore|ci|build)'; then + BUMP_TYPE="patch" + elif [[ -n "$COMMITS" ]]; then + # Any other commits default to patch + BUMP_TYPE="patch" + fi + + echo "Bump type: ${BUMP_TYPE}" + + if [[ "$BUMP_TYPE" == "none" ]]; then + echo "::error::No commits found since last release. Nothing to bump." + exit 1 + fi + + # Calculate new version + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + + case "$BUMP_TYPE" in + major) + NEW_VERSION="$((MAJOR + 1)).0.0" + ;; + minor) + NEW_VERSION="${MAJOR}.$((MINOR + 1)).0" + ;; + patch) + NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" + ;; + esac + + echo "New version: ${NEW_VERSION}" + + echo "bump_type=${BUMP_TYPE}" >> "$GITHUB_OUTPUT" + echo "current_version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT" + echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Update Chart.yaml version + id: update-version + run: | + CHART_PATH="charts/${{ matrix.chart }}" + NEW_VERSION="${{ steps.version-bump.outputs.new_version }}" + + # Update version and appVersion in Chart.yaml + yq -i ".version = \"${NEW_VERSION}\"" "${CHART_PATH}/Chart.yaml" + yq -i ".appVersion = \"${NEW_VERSION}\"" "${CHART_PATH}/Chart.yaml" + + echo "Updated Chart.yaml to version ${NEW_VERSION}" + cat "${CHART_PATH}/Chart.yaml" + - name: Get chart info id: chart-info run: | @@ -328,21 +414,24 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" - # Add and commit CHANGELOG + # Add Chart.yaml (version bump) and CHANGELOG + git add "${{ steps.chart-info.outputs.path }}/Chart.yaml" git add "${{ steps.chart-info.outputs.path }}/CHANGELOG.md" # Check if there are changes to commit if git diff --staged --quiet; then echo "No changes to commit" else - git commit -m "docs: update CHANGELOG.md for ${{ steps.chart-info.outputs.name }} v${{ steps.chart-info.outputs.version }} [skip ci]" + git commit -m "chore(${{ steps.chart-info.outputs.name }}): release v${{ steps.chart-info.outputs.version }} [skip ci]" \ + -m "Bump version from ${{ steps.version-bump.outputs.current_version }} to ${{ steps.version-bump.outputs.new_version }}" \ + -m "Update CHANGELOG.md" # Pull latest changes to avoid conflicts git pull --rebase origin main # Push changes - echo "${{ secrets.GITHUB_TOKEN }}" | git push https://github-actions:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git main - echo "CHANGELOG committed and pushed" + git push https://github-actions:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git main + echo "Version bump and CHANGELOG committed and pushed" fi - name: Summary @@ -353,7 +442,9 @@ jobs: | Property | Value | |----------|-------| | Chart | \`${{ steps.chart-info.outputs.name }}\` | - | Version | \`${{ steps.chart-info.outputs.version }}\` | + | Previous Version | \`${{ steps.version-bump.outputs.current_version }}\` | + | New Version | \`${{ steps.chart-info.outputs.version }}\` | + | Bump Type | \`${{ steps.version-bump.outputs.bump_type }}\` | | GHCR | \`${{ env.GHCR_REGISTRY }}/${{ env.GHCR_REPO }}/${{ steps.chart-info.outputs.name }}:${{ steps.chart-info.outputs.version }}\` | | GitHub Release | [${{ steps.chart-info.outputs.tag }}](${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.chart-info.outputs.tag }}) | | Signed | Keyless (Sigstore) | From 5855701c73e5fb2231ec4e323d8a0eb7ad2d3b56 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:42:36 +0100 Subject: [PATCH 26/38] docs: update CONTRIBUTING.md for automatic version bumping - Remove manual version update instructions - Document conventional commit format for version bumps - Clarify that CI handles version bumping automatically --- CONTRIBUTING.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c33d47c..dddad05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -114,16 +114,25 @@ annotations: ## Version Bump Policy -We follow [Semantic Versioning](https://semver.org/): +We follow [Semantic Versioning](https://semver.org/) with **automatic version bumping**: -- **MAJOR** (`x.0.0`): Breaking changes (incompatible value changes, removed features) -- **MINOR** (`0.x.0`): New features (backward-compatible additions) -- **PATCH** (`0.0.x`): Bug fixes (backward-compatible fixes) +- **MAJOR** (`x.0.0`): Breaking changes - use `BREAKING CHANGE:` in commit or `!` after type (e.g., `feat!:`) +- **MINOR** (`0.x.0`): New features - use `feat:` commit prefix +- **PATCH** (`0.0.x`): Bug fixes - use `fix:`, `chore:`, `docs:`, `perf:`, etc. -When making changes: -1. Update `version` in `Chart.yaml` -2. Update `CHANGELOG.md` if present -3. Document breaking changes clearly +**Important**: Do NOT manually edit the `version` in `Chart.yaml`. Versions are automatically bumped by the CI/CD pipeline based on conventional commit messages when changes are merged to main. + +### Commit Message Format + +Use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat: add new feature # -> minor bump +fix: resolve bug # -> patch bump +docs: update readme # -> patch bump +feat!: breaking change # -> major bump +BREAKING CHANGE: description # -> major bump +``` ## Pull Request Process From dc14c54c30d21685d38be9dfcf460c044601d985 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:43:57 +0100 Subject: [PATCH 27/38] docs: update PR template for automatic version bumping - Remove manual version bump checklist item - Add conventional commits format requirement --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 009c2fe..4cfe670 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ ## Checklist -- [ ] Chart version bumped in `Chart.yaml` +- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) format - [ ] `helm lint` passes - [ ] `helm template` renders correctly - [ ] Unit tests pass (if applicable) From 234745549be5036d7c58ebd1023aa9ad7b259958 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:47:47 +0100 Subject: [PATCH 28/38] fix: optimize CI/CD to only test changed charts - Fix helm-unittest plugin version compatibility (v0.4.4) - Update unittest job to only test modified charts - Update kubeconform to only validate changed charts - Update trivy security scan to only scan changed charts - Use matrix strategy for parallel execution - Improve performance by reducing unnecessary work --- .github/workflows/lint-test.yaml | 61 ++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 9e92ef5..db50c39 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -68,34 +68,64 @@ jobs: runs-on: ubuntu-latest needs: lint if: needs.lint.outputs.changed == 'true' + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@v4 with: version: v3.16.3 + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.7.0 + + - name: Get changed charts + id: set-matrix + run: | + # Use main as target branch for push events + TARGET_BRANCH="main" + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + TARGET_BRANCH="${{ github.base_ref }}" + fi + + changed=$(ct list-changed --config ct.yaml --target-branch $TARGET_BRANCH 2>/dev/null || echo "") + if [[ -z "$changed" ]]; then + # Fallback: check git diff + changed=$(git diff --name-only origin/$TARGET_BRANCH HEAD -- charts/ 2>/dev/null | grep -oP 'charts/\K[^/]+' | sort -u || echo "") + fi + + if [[ -n "$changed" ]]; then + CHARTS=$(echo "$changed" | jq -R -s -c 'split("\n") | map(select(. != ""))') + else + CHARTS='[]' + fi + + echo "matrix={\"chart\":$CHARTS}" >> "$GITHUB_OUTPUT" + - name: Cache Helm plugins uses: actions/cache@v4 with: path: ~/.local/share/helm/plugins - key: helm-plugins-unittest-${{ runner.os }} + key: helm-plugins-unittest-${{ runner.os }}-v0.4.4 - name: Install helm-unittest plugin run: | if ! helm plugin list | grep -q unittest; then - helm plugin install https://github.com/helm-unittest/helm-unittest.git + helm plugin install https://github.com/helm-unittest/helm-unittest.git --version v0.4.4 fi - name: Run unit tests run: | failed=0 - for chart in charts/*/; do - if [[ -d "${chart}tests" ]]; then - echo "Testing ${chart}" - if ! helm unittest "${chart}"; then + for chart in ${{ fromJson(needs.lint.outputs.charts) }}; do + if [[ -d "charts/${chart}/tests" ]]; then + echo "Testing charts/${chart}" + if ! helm unittest "charts/${chart}"; then failed=1 fi fi @@ -123,18 +153,18 @@ jobs: - name: Validate manifests run: | failed=0 - for chart in charts/*/; do - chart_name=$(basename "$chart") - echo "Validating ${chart_name}" + for chart in ${{ fromJson(needs.lint.outputs.charts) }}; do + chart_path="charts/${chart}" + echo "Validating ${chart}" # Use ci values if available, otherwise default values_file="" - if [[ -f "${chart}ci/default-values.yaml" ]]; then - values_file="-f ${chart}ci/default-values.yaml" + if [[ -f "${chart_path}ci/default-values.yaml" ]]; then + values_file="-f ${chart_path}ci/default-values.yaml" fi - helm dependency update "$chart" 2>/dev/null || true - if ! helm template "$chart" $values_file | kubeconform -strict -summary -output json; then + helm dependency update "$chart_path" 2>/dev/null || true + if ! helm template "$chart_path" $values_file | kubeconform -strict -summary -output json; then failed=1 fi echo "::endgroup::" @@ -145,6 +175,9 @@ jobs: runs-on: ubuntu-latest needs: lint if: needs.lint.outputs.changed == 'true' + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.lint.outputs.charts) }} steps: - name: Checkout uses: actions/checkout@v4 @@ -153,7 +186,7 @@ jobs: uses: aquasecurity/trivy-action@0.28.0 with: scan-type: 'fs' - scan-ref: 'charts/' + scan-ref: 'charts/${{ matrix.chart }}' scanners: 'misconfig' format: 'table' exit-code: '1' From 1b70916192c074805020aec55f5c6f2e0191335a Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:50:12 +0100 Subject: [PATCH 29/38] fix: update helm-unittest to latest version v1.0.3 --- .github/workflows/lint-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index db50c39..b15839c 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -111,12 +111,12 @@ jobs: uses: actions/cache@v4 with: path: ~/.local/share/helm/plugins - key: helm-plugins-unittest-${{ runner.os }}-v0.4.4 + key: helm-plugins-unittest-${{ runner.os }}-v1.0.3 - name: Install helm-unittest plugin run: | if ! helm plugin list | grep -q unittest; then - helm plugin install https://github.com/helm-unittest/helm-unittest.git --version v0.4.4 + helm plugin install https://github.com/helm-unittest/helm-unittest.git --version v1.0.3 fi - name: Run unit tests From 055cb4380af501ac14ce6d15f7bb6af2b19b76e5 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:54:57 +0100 Subject: [PATCH 30/38] fix: improve helm-unittest plugin caching strategy - Use hashFiles to automatically invalidate cache on workflow changes - Uninstall plugin before install to avoid corruption issues - Remove hardcoded version dependencies in cache key --- .github/workflows/lint-test.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index b15839c..64840c1 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -111,12 +111,13 @@ jobs: uses: actions/cache@v4 with: path: ~/.local/share/helm/plugins - key: helm-plugins-unittest-${{ runner.os }}-v1.0.3 + key: helm-plugins-${{ runner.os }}-${{ hashFiles('**/lint-test.yaml') }} - name: Install helm-unittest plugin run: | + helm plugin uninstall unittest || true if ! helm plugin list | grep -q unittest; then - helm plugin install https://github.com/helm-unittest/helm-unittest.git --version v1.0.3 + helm plugin install https://github.com/helm-unittest/helm-unittest.git fi - name: Run unit tests From 85f76e5e1e605b5e9519516ac3e80f4923425d39 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 14:57:31 +0100 Subject: [PATCH 31/38] fix: upgrade Helm to v3.19.4 and remove plugin cache - Upgrade Helm from v3.16.3 to v3.19.4 in all workflows - v3.19.4 supports platformHooks required by helm-unittest v1.0+ - Remove plugin cache to simplify workflow --- .github/workflows/lint-test.yaml | 19 +++++-------------- .github/workflows/release.yaml | 4 ++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 64840c1..0a66a14 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -35,7 +35,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Set up Python uses: actions/setup-python@v5 @@ -79,7 +79,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Set up chart-testing uses: helm/chart-testing-action@v2.7.0 @@ -107,18 +107,9 @@ jobs: echo "matrix={\"chart\":$CHARTS}" >> "$GITHUB_OUTPUT" - - name: Cache Helm plugins - uses: actions/cache@v4 - with: - path: ~/.local/share/helm/plugins - key: helm-plugins-${{ runner.os }}-${{ hashFiles('**/lint-test.yaml') }} - - name: Install helm-unittest plugin run: | - helm plugin uninstall unittest || true - if ! helm plugin list | grep -q unittest; then - helm plugin install https://github.com/helm-unittest/helm-unittest.git - fi + helm plugin install https://github.com/helm-unittest/helm-unittest.git - name: Run unit tests run: | @@ -144,7 +135,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Install kubeconform run: | @@ -207,7 +198,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Set up chart-testing uses: helm/chart-testing-action@v2.7.0 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a480cf2..361df59 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,7 +41,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Set up chart-testing uses: helm/chart-testing-action@v2.7.0 @@ -105,7 +105,7 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4 with: - version: v3.16.3 + version: v3.19.4 - name: Install cosign uses: sigstore/cosign-installer@v3.7.0 From 5ab452b65f49b28a0d04b18d33a514c00d4cc173 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 15:15:44 +0100 Subject: [PATCH 32/38] fix: correct storage configuration in volumes - Fix ci/full-values.yaml: storage config inside volume config - Fix values.yaml: remove incorrect storage root section, update examples - Fix values.schema.json: remove storage property - Storage spec is now directly in volume definition, not separate --- charts/wik-webservice/ci/full-values.yaml | 13 +++++++------ charts/wik-webservice/values.schema.json | 4 ---- charts/wik-webservice/values.yaml | 17 ++++++++--------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/charts/wik-webservice/ci/full-values.yaml b/charts/wik-webservice/ci/full-values.yaml index a47a777..b3c81b9 100644 --- a/charts/wik-webservice/ci/full-values.yaml +++ b/charts/wik-webservice/ci/full-values.yaml @@ -84,12 +84,13 @@ webservice: values: - eu-west-1a - storage: - data: - size: 10Gi - storageClass: standard - volumes: - name: data mountPath: /app/data - storage: true + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard diff --git a/charts/wik-webservice/values.schema.json b/charts/wik-webservice/values.schema.json index 8d4e1e6..d23845c 100644 --- a/charts/wik-webservice/values.schema.json +++ b/charts/wik-webservice/values.schema.json @@ -196,10 +196,6 @@ "type": "object", "default": {} }, - "storage": { - "type": "object", - "default": {} - }, "volumes": { "type": "array", "items": { diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index 0c86298..d80f034 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -294,19 +294,18 @@ webservice: # values: # - eu-west-1a - # Persistent storage (creates PVC) - storage: {} - # Example: creates a PVC named -data - # data: - # size: 10Gi - # storageClass: standard - # Volume mounts (supports PVC, ConfigMap, Secret) volumes: [] - # Mount from PVC (requires matching entry in storage) + # Mount from PVC (storage contains the PVC spec) # - name: data # mountPath: /app/data - # storage: true + # storage: + # accessModes: + # - ReadWriteOnce + # resources: + # requests: + # storage: 10Gi + # storageClassName: standard # # Mount from ConfigMap # - name: config From 33ad3a26c7518ab9760c5facbddb67c335909732 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 16:34:13 +0100 Subject: [PATCH 33/38] ci: fix tests --- charts/wik-webservice/ci/default-values.yaml | 9 +++++++++ charts/wik-webservice/templates/deployment.yaml | 5 +++++ charts/wik-webservice/values.yaml | 7 ++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/charts/wik-webservice/ci/default-values.yaml b/charts/wik-webservice/ci/default-values.yaml index 7f58060..f0d6583 100644 --- a/charts/wik-webservice/ci/default-values.yaml +++ b/charts/wik-webservice/ci/default-values.yaml @@ -4,3 +4,12 @@ webservice: - test.example.com replicas: 1 port: 80 + + # nginx needs writable dirs when using readOnlyRootFilesystem + volumes: + - name: nginx-cache + mountPath: /var/cache/nginx + emptyDir: {} + - name: nginx-run + mountPath: /var/run + emptyDir: {} diff --git a/charts/wik-webservice/templates/deployment.yaml b/charts/wik-webservice/templates/deployment.yaml index 1ef8b04..3883e56 100644 --- a/charts/wik-webservice/templates/deployment.yaml +++ b/charts/wik-webservice/templates/deployment.yaml @@ -234,6 +234,11 @@ spec: optional: {{ .secret.optional }} {{ end }} +{{ if hasKey . "emptyDir" }} + - name: {{ .name }} + emptyDir: {{ if eq (len .emptyDir) 0 }}{}{{ else }}{{ .emptyDir | toJson }}{{ end }} +{{ end }} + {{ end }} {{ end }} diff --git a/charts/wik-webservice/values.yaml b/charts/wik-webservice/values.yaml index d80f034..02172e4 100644 --- a/charts/wik-webservice/values.yaml +++ b/charts/wik-webservice/values.yaml @@ -294,7 +294,7 @@ webservice: # values: # - eu-west-1a - # Volume mounts (supports PVC, ConfigMap, Secret) + # Volume mounts (supports PVC, ConfigMap, Secret, emptyDir) volumes: [] # Mount from PVC (storage contains the PVC spec) # - name: data @@ -307,6 +307,11 @@ webservice: # storage: 10Gi # storageClassName: standard # + # Mount emptyDir (ephemeral storage) + # - name: cache + # mountPath: /tmp/cache + # emptyDir: {} + # # Mount from ConfigMap # - name: config # mountPath: /app/config From 95ead4dd868d2f33dd85ef4616a478e519a07ee1 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Wed, 31 Dec 2025 16:49:00 +0100 Subject: [PATCH 34/38] fix: use nginx-unprivileged image for CI testing - Use nginxinc/nginx-unprivileged:latest instead of nginx:latest - Change port from 80 to 8080 (non-root can't bind privileged ports) - Add emptyDir volumes for nginx writable directories --- charts/wik-webservice/ci/default-values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/wik-webservice/ci/default-values.yaml b/charts/wik-webservice/ci/default-values.yaml index f0d6583..16cd1c9 100644 --- a/charts/wik-webservice/ci/default-values.yaml +++ b/charts/wik-webservice/ci/default-values.yaml @@ -1,9 +1,9 @@ webservice: - image: nginx:latest + image: nginxinc/nginx-unprivileged:latest hosts: - test.example.com replicas: 1 - port: 80 + port: 8080 # nginx needs writable dirs when using readOnlyRootFilesystem volumes: From 16f3fdd33b00c028c3cda8b6289c95e972901430 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Mon, 5 Jan 2026 17:50:58 +0100 Subject: [PATCH 35/38] fix: use configurable port in ingress and update CI values - Fix hardcoded port 80 in ingress.yaml to use service.port - Update full-values.yaml to use nginx-unprivileged and port 8080 --- charts/wik-webservice/ci/full-values.yaml | 4 ++-- charts/wik-webservice/templates/ingress.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/wik-webservice/ci/full-values.yaml b/charts/wik-webservice/ci/full-values.yaml index b3c81b9..44555e2 100644 --- a/charts/wik-webservice/ci/full-values.yaml +++ b/charts/wik-webservice/ci/full-values.yaml @@ -2,10 +2,10 @@ general: sealedSecrets: false webservice: - image: nginx:latest + image: nginxinc/nginx-unprivileged:latest imagePullPolicy: Always replicas: 2 - port: 80 + port: 8080 hosts: - test.example.com - api.example.com diff --git a/charts/wik-webservice/templates/ingress.yaml b/charts/wik-webservice/templates/ingress.yaml index b7c70bc..6701405 100644 --- a/charts/wik-webservice/templates/ingress.yaml +++ b/charts/wik-webservice/templates/ingress.yaml @@ -43,7 +43,7 @@ spec: service: name: {{ template "fullname" $root }} port: - number: 80 + number: {{ $root.Values.webservice.service.port | default $root.Values.webservice.port | default 80 }} pathType: Prefix path: / {{ end }} From aa8e93b568f3578324308d358245fdfb496d1f53 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Mon, 5 Jan 2026 18:02:15 +0100 Subject: [PATCH 36/38] fix: add /tmp emptyDir volume for nginx CI testing --- charts/wik-webservice/ci/default-values.yaml | 3 +++ charts/wik-webservice/ci/full-values.yaml | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/charts/wik-webservice/ci/default-values.yaml b/charts/wik-webservice/ci/default-values.yaml index 16cd1c9..2609eea 100644 --- a/charts/wik-webservice/ci/default-values.yaml +++ b/charts/wik-webservice/ci/default-values.yaml @@ -13,3 +13,6 @@ webservice: - name: nginx-run mountPath: /var/run emptyDir: {} + - name: nginx-tmp + mountPath: /tmp + emptyDir: {} diff --git a/charts/wik-webservice/ci/full-values.yaml b/charts/wik-webservice/ci/full-values.yaml index 44555e2..d8bca75 100644 --- a/charts/wik-webservice/ci/full-values.yaml +++ b/charts/wik-webservice/ci/full-values.yaml @@ -94,3 +94,14 @@ webservice: requests: storage: 10Gi storageClassName: standard + + # nginx needs writable dirs when using readOnlyRootFilesystem + - name: nginx-cache + mountPath: /var/cache/nginx + emptyDir: {} + - name: nginx-run + mountPath: /var/run + emptyDir: {} + - name: nginx-tmp + mountPath: /tmp + emptyDir: {} From 487252ae7b35af69de72fa5001cf7b0259ab9fe5 Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Mon, 5 Jan 2026 18:11:03 +0100 Subject: [PATCH 37/38] fix: correct probe ports in full-values.yaml for nginx-unprivileged --- charts/wik-webservice/ci/full-values.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/wik-webservice/ci/full-values.yaml b/charts/wik-webservice/ci/full-values.yaml index d8bca75..c1f3093 100644 --- a/charts/wik-webservice/ci/full-values.yaml +++ b/charts/wik-webservice/ci/full-values.yaml @@ -53,15 +53,15 @@ webservice: livenessProbe: httpGet: - path: /health - port: 80 + path: / + port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: - path: /ready - port: 80 + path: / + port: 8080 initialDelaySeconds: 5 periodSeconds: 5 From 9cfe9c9d869fc239f92124f4fe9f09a1b2fb883d Mon Sep 17 00:00:00 2001 From: Jeremy T Date: Mon, 5 Jan 2026 18:15:02 +0100 Subject: [PATCH 38/38] fix: use preferred affinity instead of required for CI testing --- charts/wik-webservice/ci/full-values.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/charts/wik-webservice/ci/full-values.yaml b/charts/wik-webservice/ci/full-values.yaml index c1f3093..ef28ba0 100644 --- a/charts/wik-webservice/ci/full-values.yaml +++ b/charts/wik-webservice/ci/full-values.yaml @@ -76,9 +76,10 @@ webservice: affinity: nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + preference: + matchExpressions: - key: topology.kubernetes.io/zone operator: In values: