From df94d74c94ea761b4f75ee38ad49fd026f9ad9a0 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 1 Oct 2026 23:09:59 +0200 Subject: [PATCH 01/33] Add fallstar file to sidelab chart --- charts/sidelab/Chart.yaml | 2 +- charts/sidelab/README.md | 5 + charts/sidelab/fallstar.yaml | 349 +++++++++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 charts/sidelab/fallstar.yaml diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index 9409a2a..8955e2c 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -4,7 +4,7 @@ description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.6 +version: 0.1.7 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/README.md b/charts/sidelab/README.md index b95765a..de5541c 100644 --- a/charts/sidelab/README.md +++ b/charts/sidelab/README.md @@ -22,3 +22,8 @@ kubectl delete namespace sidelab ``` > **Note:** Deleting the namespace deletes the generated `-auth` Secret and the data volume. + +## Fallstar + +`fallstar.yaml` describes the choices of a deployment (database, secrets, lab access, TLS) and the values each one implies, for [Fallstar](https://github.com/devpro/fallstar). +This Helm repository added as a Fallstar recipe source shows the chart in its store with these choices. diff --git a/charts/sidelab/fallstar.yaml b/charts/sidelab/fallstar.yaml new file mode 100644 index 0000000..9392296 --- /dev/null +++ b/charts/sidelab/fallstar.yaml @@ -0,0 +1,349 @@ +# Recipe of this chart for Fallstar: the choices a Sidelab deployment makes, and the values each one implies. +# Format: https://github.com/devpro/fallstar/blob/main/docs/sources.md +title: Sidelab +description: Interactive lab platform, one pod per lab session, each published on its own hostname. +category: application +namespace: sidelab +layer: apps +# The namespace is a resource of the infra layer, so the ExternalSecrets of the apps layer find it before the release exists. +createNamespace: false + +options: + - key: domain + title: Domain + description: The dashboard is sidelab., lab sessions lab-.labs.. + type: string + default: ${{ cluster.domain }} + value: domain + - key: tag + title: Image tag + description: The same pkg.yml run for the launcher and the lab image, .. + type: string + value: image.tag + - key: privateImages + title: Images pulled with a ghcr.io token + group: Images + type: boolean + default: true + + - key: database + title: Database + description: SQLite is one file on one volume, MongoDB allows several replicas, the bundled one is for a disposable demo. + group: Database + type: enum + values: [sqlite, mongo, mongo-bundled] + default: sqlite + - key: storageSize + title: SQLite volume size + group: Database + type: string + default: 2Gi + value: persistence.size + when: { equals: { database: sqlite } } + - key: storageClass + title: Storage class + description: Empty for the cluster default. + group: Database + type: string + value: persistence.storageClass + when: { equals: { database: sqlite } } + - key: replicas + title: Replicas + description: More than one needs MongoDB. + group: Database + type: number + default: 1 + bind: { chart: sidelab, path: [values, replicaCount] } + when: { equals: { database: [mongo, mongo-bundled] } } + + - key: labAccess + title: Lab access + description: Ingress gives each session its own hostname, NodePort its own port. + group: Access + type: enum + values: [ingress, nodeport] + default: ingress + value: launcher.labAccess + - key: ingressClass + title: Ingress class + group: Access + type: string + default: traefik + bind: { chart: sidelab, path: [values, ingress, className] } + when: { equals: { labAccess: ingress } } + - key: tls + title: TLS + description: A wildcard certificate served by the ingress controller, a certificate per hostname from cert-manager, or none. + group: Access + type: enum + values: [wildcard, cert-manager, none] + default: wildcard + when: { equals: { labAccess: ingress } } + - key: clusterIssuer + title: cert-manager ClusterIssuer + group: Access + type: string + default: letsencrypt-dns + when: { equals: { labAccess: ingress, tls: cert-manager } } + - key: trustProxy + title: Trust the ingress controller as a proxy + description: Keeps the client IP, which the login throttle counts. + group: Access + type: boolean + default: true + when: { equals: { labAccess: ingress } } + + - key: sessionDefaultDuration + title: Default session duration, in minutes + group: Sessions + type: number + default: 30 + value: launcher.sessionDefaultDuration + - key: sessionMaxDuration + title: Maximum session duration, in minutes + group: Sessions + type: number + default: 60 + value: launcher.sessionMaxDuration + +values: + image: + repository: ghcr.io/devpro/sidelab-launcher + launcher: + labImage: ghcr.io/devpro/sidelab-app:${{ options.tag }} + +patches: + - when: { secretsManaged: true } + chart: sidelab + patch: + values: + auth: + existingSecret: sidelab-auth + - when: { equals: { database: sqlite } } + chart: sidelab + patch: + values: + database: + backend: sqlite + persistence: + enabled: true + - when: { equals: { database: [mongo, mongo-bundled] } } + chart: sidelab + patch: + values: + replicaCount: ${{ options.replicas }} + database: + backend: mongo + mongo: + existingSecret: sidelab-db + persistence: + enabled: false + - when: { equals: { database: mongo-bundled } } + chart: sidelab + notes: + - path: [values, mongodb, auth, existingSecret] + text: The root password comes from the same Secret as DB_URL, so it is never written in the values. + patch: + values: + mongodb: + enabled: true + auth: + existingSecret: sidelab-db + - when: { option: privateImages } + chart: sidelab + patch: + values: + imagePullSecrets: + - name: ghcr-pull + launcher: + labImagePullSecret: ghcr-pull + - when: { equals: { labAccess: ingress } } + chart: sidelab + patch: + values: + ingress: + enabled: true + className: ${{ options.ingressClass }} + - when: { equals: { labAccess: ingress, tls: wildcard } } + chart: sidelab + notes: + - path: [values, ingress, tls] + text: No cert-manager annotation, the ingress controller serves its wildcard certificate to every lab hostname. + patch: + values: + ingress: + tls: + enabled: true + - when: { equals: { labAccess: ingress, tls: cert-manager } } + chart: sidelab + notes: + - path: [values, ingress, annotations] + text: Copied onto every lab session Ingress, so each session orders its own certificate. + patch: + values: + ingress: + annotations: + cert-manager.io/cluster-issuer: ${{ options.clusterIssuer }} + tls: + enabled: true + - when: { equals: { labAccess: ingress }, option: trustProxy } + chart: sidelab + patch: + values: + extraEnv: + - name: TRUST_PROXY + value: "1" + - when: { equals: { labAccess: nodeport } } + chart: sidelab + patch: + values: + service: + type: NodePort + +resources: + - file: infra/namespaces/sidelab.yaml + shared: true + object: + apiVersion: v1 + kind: Namespace + metadata: { name: sidelab } + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true } + notes: + - path: [] + text: Held in the secret store rather than generated by the chart, so a rebuilt cluster keeps the same signing secret and admin password. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-auth, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-auth + creationPolicy: Owner + template: + data: + JWT_SECRET: "{{ .jwtSecret }}" + ADMIN_USERNAME: admin + ADMIN_PASSWORD: "{{ .adminPassword }}" + data: + - secretKey: jwtSecret + remoteRef: + key: sidelab-jwt-secret + - secretKey: adminPassword + remoteRef: + key: sidelab-admin-password + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, equals: { database: mongo } } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-db, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-db + creationPolicy: Owner + data: + - secretKey: DB_URL + remoteRef: + key: sidelab-mongodb-url + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, equals: { database: mongo-bundled } } + notes: + - path: [] + text: One password for the bundled MongoDB and for the connection string the launcher uses, the Service being -mongodb. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-db, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-db + creationPolicy: Owner + template: + data: + DB_URL: "mongodb://root:{{ .password }}@sidelab-mongodb:27017/sidelab?authSource=admin" + mongodb-root-password: "{{ .password }}" + data: + - secretKey: password + remoteRef: + key: sidelab-mongodb-root-password + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, option: privateImages } + notes: + - path: [] + text: The launcher copies it into every tenant namespace, for the lab image. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: ghcr-pull, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: ghcr-pull + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: '{"auths":{"ghcr.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}}' + data: + - secretKey: username + remoteRef: + key: ghcr-username + - secretKey: password + remoteRef: + key: ghcr-token + +secrets: + - { name: sidelab-jwt-secret, description: Signing key of the launcher tokens., generate: hex32, when: { secretsManaged: true }, target: { secret: sidelab-auth, key: JWT_SECRET, namespace: sidelab } } + - { name: sidelab-admin-username, description: Name of the admin account., value: admin, when: { secretsManaged: true, secretStore: false }, target: { secret: sidelab-auth, key: ADMIN_USERNAME, namespace: sidelab } } + - { name: sidelab-admin-password, description: Password of the admin account., when: { secretsManaged: true }, target: { secret: sidelab-auth, key: ADMIN_PASSWORD, namespace: sidelab } } + - { name: sidelab-mongodb-url, description: "MongoDB connection string, mongodb+srv://:@/sidelab.", when: { secretsManaged: true, equals: { database: mongo } }, target: { secret: sidelab-db, key: DB_URL, namespace: sidelab } } + - { name: sidelab-mongodb-root-password, description: Root password of the bundled MongoDB., when: { secretsManaged: true, equals: { database: mongo-bundled } }, target: { secret: sidelab-db, key: mongodb-root-password, namespace: sidelab } } + - { name: sidelab-mongodb-url, description: "mongodb://root:@sidelab-mongodb:27017/sidelab?authSource=admin, with the root password above.", when: { secretsManaged: true, secretStore: false, equals: { database: mongo-bundled } }, target: { secret: sidelab-db, key: DB_URL, namespace: sidelab } } + - { name: ghcr-username, description: GitHub user pulling the private images., when: { secretStore: true, option: privateImages } } + - { name: ghcr-token, description: "GitHub token with read:packages on ghcr.io/devpro.", when: { secretStore: true, option: privateImages } } + +warnings: + - when: { secretsManaged: false, equals: { database: [mongo, mongo-bundled] } } + text: reads its database from Secret sidelab-db in namespace sidelab, which neither a secret store nor SOPS creates on this cluster + +guide: + prerequisites: + - "With ingress: two DNS records to the ingress load balancer IP, DNS only, sidelab. and *.labs.." + - "With a wildcard certificate: the ingress controller serves one for *.labs. by default." + - "With private images and SOPS: a ghcr-pull Secret of type kubernetes.io/dockerconfigjson in namespace sidelab." + verify: + - kubectl get pods -n sidelab + - kubectl logs -n sidelab deploy/sidelab --tail=30 + - kubectl get ns -l app.kubernetes.io/managed-by=sidelab + gotchas: + - title: Every lab orders its own certificate, rate limit hit + cause: TLS from cert-manager, whose annotation the chart copies onto every session Ingress. + fix: TLS from a wildcard certificate served by the ingress controller. + - title: Lab pod ImagePullBackOff + cause: ghcr-pull missing, or a token without read:packages. + fix: kubectl get secret -n sidelab ghcr-pull; the launcher copies it into sidelab- at every session start. + - title: Second replica fails to start + cause: SQLite is one file on a ReadWriteOnce volume. + fix: Choose MongoDB before raising the replicas. + - title: Lab stuck on starting, pod Pending, Insufficient cpu + cause: Nodes already full, and a lab requests 250m CPU and 512Mi. + fix: Wait for the autoscaler, or raise the node pool minimum before a demo. + - title: Tenant namespaces left after a teardown + cause: The launcher creates them, not Flux. + fix: Delete the namespaces labelled app.kubernetes.io/managed-by=sidelab by hand. From 03b4a6f1b31ac669175b3b3f6227860d971bef11 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 5 Oct 2026 22:05:36 +0200 Subject: [PATCH 02/33] Probe DVWA MariaDB with mariadb-admin, read the sales portal connection string from a Secret MariaDB images dropped mysqladmin in 11.0, so the probes of the 12.2 image failed and the pod never got ready. The sales portal took its MongoDB connection string only as a value, which put its password in the values of a GitOps repository. Co-Authored-By: Claude Opus 5.5 --- charts/devpro-salesportal/Chart.yaml | 2 +- charts/devpro-salesportal/templates/deployment.yaml | 8 ++++++++ charts/devpro-salesportal/values.yaml | 5 +++++ charts/dvwa/Chart.yaml | 2 +- charts/dvwa/templates/deployment.yaml | 4 ++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/charts/devpro-salesportal/Chart.yaml b/charts/devpro-salesportal/Chart.yaml index 3e200cb..45e1e61 100644 --- a/charts/devpro-salesportal/Chart.yaml +++ b/charts/devpro-salesportal/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: devpro-salesportal description: Helm chart for Devpro Sales Portal type: application -version: 0.2.8 +version: 0.2.9 appVersion: "1.1.18203306613" dependencies: - name: mongodb diff --git a/charts/devpro-salesportal/templates/deployment.yaml b/charts/devpro-salesportal/templates/deployment.yaml index af34d03..7cc38f9 100644 --- a/charts/devpro-salesportal/templates/deployment.yaml +++ b/charts/devpro-salesportal/templates/deployment.yaml @@ -92,8 +92,16 @@ spec: value: "http://$(HOST_IP):4317" {{- end }} {{- if .db }} + {{- if .db.connectionStringSecretKeyRef }} + - name: ConnectionStrings__MongoDb + valueFrom: + secretKeyRef: + name: {{ .db.connectionStringSecretKeyRef.name }} + key: {{ .db.connectionStringSecretKeyRef.key }} + {{- else }} - name: ConnectionStrings__MongoDb value: {{ .db.connectionString }} + {{- end }} - name: MongoDb__ConnectionStringName value: "MongoDb" - name: MongoDb__DatabaseName diff --git a/charts/devpro-salesportal/values.yaml b/charts/devpro-salesportal/values.yaml index a792109..941cc54 100644 --- a/charts/devpro-salesportal/values.yaml +++ b/charts/devpro-salesportal/values.yaml @@ -51,7 +51,12 @@ data: port: 8080 healthEndpoint: /health db: + # -- MongoDB connection string, ignored when connectionStringSecretKeyRef is set connectionString: "someconnstring" + # -- Secret key holding the connection string, which keeps its password out of the values + connectionStringSecretKeyRef: {} + # name: salesportal-db + # key: connectionString databaseName: "somedb" extraEnv: [] additionalPodLabels: {} diff --git a/charts/dvwa/Chart.yaml b/charts/dvwa/Chart.yaml index b539004..4b14c14 100644 --- a/charts/dvwa/Chart.yaml +++ b/charts/dvwa/Chart.yaml @@ -2,7 +2,7 @@ name: dvwa description: Damn Vulnerable Web Application - for container security workshops type: application -version: 1.0.1 +version: 1.0.2 appVersion: "1.0.0" keywords: - security diff --git a/charts/dvwa/templates/deployment.yaml b/charts/dvwa/templates/deployment.yaml index 4f8098c..0377a9e 100644 --- a/charts/dvwa/templates/deployment.yaml +++ b/charts/dvwa/templates/deployment.yaml @@ -103,7 +103,7 @@ spec: command: - sh - -c - - mysqladmin ping -u root -p${MARIADB_ROOT_PASSWORD} + - mariadb-admin ping -u root -p"${MARIADB_ROOT_PASSWORD}" initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: @@ -111,7 +111,7 @@ spec: command: - sh - -c - - mysqladmin ping -u root -p${MARIADB_ROOT_PASSWORD} + - mariadb-admin ping -u root -p"${MARIADB_ROOT_PASSWORD}" initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 From 2cc4b4120197633cd790b6a9e6c7148fd3aacb92 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 7 Oct 2026 16:45:44 +0200 Subject: [PATCH 03/33] Add the Fallstar chart Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_011C3sfi4prUmDrjVTzgdzdn --- charts/fallstar/.helmignore | 10 ++ charts/fallstar/Chart.yaml | 18 ++++ charts/fallstar/README.md | 26 ++++++ charts/fallstar/ci/ci-values.yaml | 2 + charts/fallstar/ci/mongo-ingress-values.yaml | 14 +++ charts/fallstar/templates/NOTES.txt | 20 ++++ charts/fallstar/templates/_helpers.tpl | 66 +++++++++++++ charts/fallstar/templates/deployment.yaml | 98 ++++++++++++++++++++ charts/fallstar/templates/ingress.yaml | 34 +++++++ charts/fallstar/templates/pvc.yaml | 18 ++++ charts/fallstar/templates/secret.yaml | 28 ++++++ charts/fallstar/templates/service.yaml | 16 ++++ charts/fallstar/values.yaml | 85 +++++++++++++++++ docs/custom-charts/index.md | 1 + 14 files changed, 436 insertions(+) create mode 100644 charts/fallstar/.helmignore create mode 100644 charts/fallstar/Chart.yaml create mode 100644 charts/fallstar/README.md create mode 100644 charts/fallstar/ci/ci-values.yaml create mode 100644 charts/fallstar/ci/mongo-ingress-values.yaml create mode 100644 charts/fallstar/templates/NOTES.txt create mode 100644 charts/fallstar/templates/_helpers.tpl create mode 100644 charts/fallstar/templates/deployment.yaml create mode 100644 charts/fallstar/templates/ingress.yaml create mode 100644 charts/fallstar/templates/pvc.yaml create mode 100644 charts/fallstar/templates/secret.yaml create mode 100644 charts/fallstar/templates/service.yaml create mode 100644 charts/fallstar/values.yaml diff --git a/charts/fallstar/.helmignore b/charts/fallstar/.helmignore new file mode 100644 index 0000000..517eba1 --- /dev/null +++ b/charts/fallstar/.helmignore @@ -0,0 +1,10 @@ +.DS_Store +.git/ +.gitignore +.helmignore +*.orig +*.swp +*.bak +values.mine.yaml +temp*.yaml +kubeconfig diff --git a/charts/fallstar/Chart.yaml b/charts/fallstar/Chart.yaml new file mode 100644 index 0000000..5292948 --- /dev/null +++ b/charts/fallstar/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: fallstar +description: > + GitOps made easy. + Deploys the Fallstar server, a catalog of recipes rendered into Flux, Argo CD or Fleet manifests on git branches. +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - fallstar + - gitops + - flux +home: https://github.com/devpro/helm-charts +sources: + - https://github.com/devpro/helm-charts +maintainers: + - name: devpro + email: bertrand@devpro.fr diff --git a/charts/fallstar/README.md b/charts/fallstar/README.md new file mode 100644 index 0000000..c6c3c59 --- /dev/null +++ b/charts/fallstar/README.md @@ -0,0 +1,26 @@ +# Fallstar Helm Chart + +Helm chart for [Fallstar](https://github.com/devpro/fallstar), GitOps made easy. + +## Quick Start + +```bash +helm repo add devpro https://devpro.github.io/helm-charts +helm repo update +helm upgrade --install fallstar devpro/fallstar \ + --set image.repository=/fallstar \ + --namespace fallstar --create-namespace +``` + +The image is built from the `Dockerfile` of the Fallstar repository. + +> **Tip:** Every option is documented in [values.yaml](values.yaml). + +## Uninstall + +```bash +helm uninstall fallstar -n fallstar +kubectl delete namespace fallstar +``` + +> **Note:** Deleting the namespace deletes the generated `-auth` Secret and the data volume. diff --git a/charts/fallstar/ci/ci-values.yaml b/charts/fallstar/ci/ci-values.yaml new file mode 100644 index 0000000..6f26068 --- /dev/null +++ b/charts/fallstar/ci/ci-values.yaml @@ -0,0 +1,2 @@ +image: + repository: ci-dummy-registry/fallstar diff --git a/charts/fallstar/ci/mongo-ingress-values.yaml b/charts/fallstar/ci/mongo-ingress-values.yaml new file mode 100644 index 0000000..c22ffb1 --- /dev/null +++ b/charts/fallstar/ci/mongo-ingress-values.yaml @@ -0,0 +1,14 @@ +image: + repository: ci-dummy-registry/fallstar + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +ingress: + enabled: true + className: traefik + host: fallstar.example.com + tls: + enabled: true diff --git a/charts/fallstar/templates/NOTES.txt b/charts/fallstar/templates/NOTES.txt new file mode 100644 index 0000000..ffe5258 --- /dev/null +++ b/charts/fallstar/templates/NOTES.txt @@ -0,0 +1,20 @@ +Fallstar has been deployed. + +{{- if .Values.ingress.enabled }} + +URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.host }} +{{- else }} + +Port-forward to open it locally: + + kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "fallstar.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }} + +Then open http://localhost:{{ .Values.service.port }} +{{- end }} + +Sign in as {{ .Values.auth.adminUsername }}. +{{- if and (not .Values.auth.existingSecret) (not .Values.auth.adminPassword) }} +The generated password is read with: + + kubectl get secret -n {{ .Release.Namespace }} {{ include "fallstar.authSecretName" . }} -o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d; echo +{{- end }} diff --git a/charts/fallstar/templates/_helpers.tpl b/charts/fallstar/templates/_helpers.tpl new file mode 100644 index 0000000..bbba985 --- /dev/null +++ b/charts/fallstar/templates/_helpers.tpl @@ -0,0 +1,66 @@ +{{- define "fallstar.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "fallstar.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "fallstar.labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{ include "fallstar.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "fallstar.selectorLabels" -}} +app.kubernetes.io/name: {{ include "fallstar.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "fallstar.authSecretName" -}} +{{- default (printf "%s-auth" (include "fallstar.fullname" .)) .Values.auth.existingSecret }} +{{- end }} + +{{- define "fallstar.mongoSecretName" -}} +{{- default (printf "%s-mongo" (include "fallstar.fullname" .)) .Values.database.mongo.existingSecret }} +{{- end }} + +{{/* +A generated value is read back from the existing Secret on upgrade, so the master secret never changes by itself. +`lookup` returns nothing outside a cluster, so `helm template` generates a fresh one. +*/}} +{{- define "fallstar.generated" -}} +{{- $existing := lookup "v1" "Secret" .root.Release.Namespace (include "fallstar.authSecretName" .root) -}} +{{- if and $existing $existing.data (hasKey $existing.data .key) -}} +{{- index $existing.data .key | b64dec -}} +{{- else -}} +{{- randAlphaNum .length -}} +{{- end -}} +{{- end }} + +{{- define "fallstar.validateValues" -}} +{{- if not .Values.image.repository }} +{{- fail "image.repository is required." }} +{{- end }} +{{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.url) (not .Values.database.mongo.existingSecret) }} +{{- fail "database.backend=mongo needs database.mongo.url or database.mongo.existingSecret." }} +{{- end }} +{{- if and .Values.auth.secret (lt (len .Values.auth.secret) 32) }} +{{- fail "auth.secret needs at least 32 characters." }} +{{- end }} +{{- if and .Values.ingress.enabled (not .Values.ingress.host) }} +{{- fail "ingress.host is required when ingress.enabled=true." }} +{{- end }} +{{- end }} diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml new file mode 100644 index 0000000..d78bfe9 --- /dev/null +++ b/charts/fallstar/templates/deployment.yaml @@ -0,0 +1,98 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + # One Pod only: the SQLite file and the repository mirrors are local to it, so the old Pod must be gone before the new one starts. + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + {{- include "fallstar.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "fallstar.selectorLabels" . | nindent 8 }} + annotations: + # Environment variables are read at start, so an edited Secret needs a rollout. + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + # A Service named fallstar would inject FALLSTAR_PORT=tcp://..., which the server reads as its port. + enableServiceLinks: false + automountServiceAccountToken: false + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: fallstar + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 4500 + protocol: TCP + envFrom: + - secretRef: + name: {{ include "fallstar.authSecretName" . }} + {{- if eq .Values.database.backend "mongo" }} + - secretRef: + name: {{ include "fallstar.mongoSecretName" . }} + {{- end }} + env: + - name: DB_BACKEND + value: {{ .Values.database.backend | quote }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (printf "%s-data" (include "fallstar.fullname" .)) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/fallstar/templates/ingress.yaml b/charts/fallstar/templates/ingress.yaml new file mode 100644 index 0000000..a07eeae --- /dev/null +++ b/charts/fallstar/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.host | quote }} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end }} + rules: + - host: {{ .Values.ingress.host | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "fallstar.fullname" . }} + port: + name: http +{{- end }} diff --git a/charts/fallstar/templates/pvc.yaml b/charts/fallstar/templates/pvc.yaml new file mode 100644 index 0000000..2a9ca9e --- /dev/null +++ b/charts/fallstar/templates/pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "fallstar.fullname" . }}-data + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end }} diff --git a/charts/fallstar/templates/secret.yaml b/charts/fallstar/templates/secret.yaml new file mode 100644 index 0000000..03190dc --- /dev/null +++ b/charts/fallstar/templates/secret.yaml @@ -0,0 +1,28 @@ +{{- include "fallstar.validateValues" . }} +{{- if not .Values.auth.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fallstar.authSecretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +type: Opaque +stringData: + FALLSTAR_SECRET: {{ .Values.auth.secret | default (include "fallstar.generated" (dict "root" . "key" "FALLSTAR_SECRET" "length" 48)) | quote }} + ADMIN_USERNAME: {{ .Values.auth.adminUsername | quote }} + ADMIN_PASSWORD: {{ .Values.auth.adminPassword | default (include "fallstar.generated" (dict "root" . "key" "ADMIN_PASSWORD" "length" 20)) | quote }} +{{- end }} +{{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.existingSecret) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fallstar.mongoSecretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +type: Opaque +stringData: + DB_URL: {{ .Values.database.mongo.url | quote }} +{{- end }} diff --git a/charts/fallstar/templates/service.yaml b/charts/fallstar/templates/service.yaml new file mode 100644 index 0000000..356eaff --- /dev/null +++ b/charts/fallstar/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "fallstar.selectorLabels" . | nindent 4 }} diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml new file mode 100644 index 0000000..a5b6825 --- /dev/null +++ b/charts/fallstar/values.yaml @@ -0,0 +1,85 @@ +image: + # -- Server image repository, required. + repository: + pullPolicy: IfNotPresent + # -- Defaults to the chart appVersion. + tag: "" + +imagePullSecrets: [] + # - name: "" + +nameOverride: "" +fullnameOverride: "" + +auth: + # -- Master secret of at least 32 characters, generated on first install when empty and kept across upgrades. + # Changing it signs everyone out and makes the stored repository tokens unreadable. + secret: "" + # -- Initial admin account, only used while the database is empty. + adminUsername: "admin" + # -- Generated on first install when empty, the install notes show how to read it. + adminPassword: "" + # -- Existing Secret with the keys FALLSTAR_SECRET, ADMIN_USERNAME and ADMIN_PASSWORD, replacing the three values above. + existingSecret: "" + +database: + # -- sqlite or mongo. + backend: "sqlite" + mongo: + # -- Connection string, e.g. mongodb+srv://:@/fallstar + url: "" + # -- Existing Secret with the key DB_URL, taking priority over url. + existingSecret: "" + +persistence: + # -- Volume for /data: the SQLite file and the mirrors of the repositories. + enabled: true + # -- Empty for the cluster default. + storageClass: "" + size: 2Gi + # -- Existing PersistentVolumeClaim, instead of creating one. + existingClaim: "" + +service: + type: ClusterIP + port: 4500 + +ingress: + enabled: false + # -- e.g. "traefik" or "nginx". + className: "" + # -- Required when enabled. + host: "" + annotations: {} + # cert-manager.io/cluster-issuer: "letsencrypt-prod" + tls: + enabled: false + secretName: "fallstar-tls" + +# -- Pod user of the image, which owns /data. +podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + +containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +resources: {} + # requests: + # cpu: 100m + # memory: 256Mi + +extraEnv: [] + # - name: "" + # value: "" + +nodeSelector: {} +tolerations: [] +affinity: {} diff --git a/docs/custom-charts/index.md b/docs/custom-charts/index.md index 8f99bc2..bea2909 100644 --- a/docs/custom-charts/index.md +++ b/docs/custom-charts/index.md @@ -7,6 +7,7 @@ order: 2 ## Applications * [Keeptrack](keeptrack.md) +* Fallstar * Sidelab ## Databases From 73b2c7a16de1686469311221340ff6b45635cac7 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 7 Oct 2026 16:53:14 +0200 Subject: [PATCH 04/33] Allow several replicas of Fallstar on MongoDB without a volume Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_011C3sfi4prUmDrjVTzgdzdn --- charts/fallstar/ci/scaleout-values.yaml | 14 ++++++++++++++ charts/fallstar/templates/NOTES.txt | 5 +++++ charts/fallstar/templates/_helpers.tpl | 13 +++++++++++++ charts/fallstar/templates/deployment.yaml | 9 +++++++-- charts/fallstar/values.yaml | 5 ++++- 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 charts/fallstar/ci/scaleout-values.yaml diff --git a/charts/fallstar/ci/scaleout-values.yaml b/charts/fallstar/ci/scaleout-values.yaml new file mode 100644 index 0000000..3a0d5cb --- /dev/null +++ b/charts/fallstar/ci/scaleout-values.yaml @@ -0,0 +1,14 @@ +# Scale-out: MongoDB, no volume, several replicas. +# Guards the RollingUpdate branch of the Deployment strategy and the replica validation in _helpers.tpl. +image: + repository: ci-dummy-registry/fallstar + +replicaCount: 3 + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +persistence: + enabled: false diff --git a/charts/fallstar/templates/NOTES.txt b/charts/fallstar/templates/NOTES.txt index ffe5258..3fcecea 100644 --- a/charts/fallstar/templates/NOTES.txt +++ b/charts/fallstar/templates/NOTES.txt @@ -18,3 +18,8 @@ The generated password is read with: kubectl get secret -n {{ .Release.Namespace }} {{ include "fallstar.authSecretName" . }} -o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d; echo {{- end }} + +Database: {{ .Values.database.backend }}, replicas: {{ .Values.replicaCount }}. +{{- if include "fallstar.singleWriter" . }} +A single Pod owns the data, so an upgrade restarts it. More replicas need database.backend=mongo and persistence.enabled=false. +{{- end }} diff --git a/charts/fallstar/templates/_helpers.tpl b/charts/fallstar/templates/_helpers.tpl index bbba985..b6d10d9 100644 --- a/charts/fallstar/templates/_helpers.tpl +++ b/charts/fallstar/templates/_helpers.tpl @@ -50,6 +50,16 @@ A generated value is read back from the existing Secret on upgrade, so the maste {{- end -}} {{- end }} +{{/* +Whether the configuration holds state only one Pod may own: a SQLite file, or a ReadWriteOnce volume. +Returns "true" or "" (empty is false in an `if`). +*/}} +{{- define "fallstar.singleWriter" -}} +{{- if or (eq .Values.database.backend "sqlite") .Values.persistence.enabled -}} +true +{{- end -}} +{{- end }} + {{- define "fallstar.validateValues" -}} {{- if not .Values.image.repository }} {{- fail "image.repository is required." }} @@ -60,6 +70,9 @@ A generated value is read back from the existing Secret on upgrade, so the maste {{- if and .Values.auth.secret (lt (len .Values.auth.secret) 32) }} {{- fail "auth.secret needs at least 32 characters." }} {{- end }} +{{- if and (gt (int .Values.replicaCount) 1) (include "fallstar.singleWriter" .) }} +{{- fail "More than one replica requires database.backend=mongo and persistence.enabled=false: SQLite is a local file, and a ReadWriteOnce volume cannot be mounted by two Pods." }} +{{- end }} {{- if and .Values.ingress.enabled (not .Values.ingress.host) }} {{- fail "ingress.host is required when ingress.enabled=true." }} {{- end }} diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml index d78bfe9..11d6076 100644 --- a/charts/fallstar/templates/deployment.yaml +++ b/charts/fallstar/templates/deployment.yaml @@ -6,10 +6,15 @@ metadata: labels: {{- include "fallstar.labels" . | nindent 4 }} spec: - # One Pod only: the SQLite file and the repository mirrors are local to it, so the old Pod must be gone before the new one starts. - replicas: 1 + replicas: {{ .Values.replicaCount }} strategy: + {{- if include "fallstar.singleWriter" . }} + # The SQLite file or the volume belongs to one Pod, so the old Pod must be gone before the new one starts. type: Recreate + {{- else }} + # Nothing is local to a Pod: roll without downtime. + type: RollingUpdate + {{- end }} selector: matchLabels: {{- include "fallstar.selectorLabels" . | nindent 6 }} diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml index a5b6825..03678f8 100644 --- a/charts/fallstar/values.yaml +++ b/charts/fallstar/values.yaml @@ -11,6 +11,9 @@ imagePullSecrets: [] nameOverride: "" fullnameOverride: "" +# -- More than one replica requires database.backend=mongo and persistence.enabled=false. +replicaCount: 1 + auth: # -- Master secret of at least 32 characters, generated on first install when empty and kept across upgrades. # Changing it signs everyone out and makes the stored repository tokens unreadable. @@ -32,7 +35,7 @@ database: existingSecret: "" persistence: - # -- Volume for /data: the SQLite file and the mirrors of the repositories. + # -- Volume for /data: the SQLite file and the mirrors of the repositories, which MongoDB re-creates when missing. enabled: true # -- Empty for the cluster default. storageClass: "" From 0032ded75a826ef218b5f3e20bc00efdab1f708c Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 7 Oct 2026 16:57:19 +0200 Subject: [PATCH 05/33] Add an HPA and a PodDisruptionBudget to the Fallstar chart Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_011C3sfi4prUmDrjVTzgdzdn --- charts/fallstar/ci/autoscaling-values.yaml | 18 +++++++++++++++++ charts/fallstar/ci/scaleout-values.yaml | 3 +++ charts/fallstar/templates/NOTES.txt | 2 +- charts/fallstar/templates/_helpers.tpl | 16 ++++++++++++++- charts/fallstar/templates/deployment.yaml | 2 ++ charts/fallstar/templates/hpa.yaml | 23 ++++++++++++++++++++++ charts/fallstar/templates/pdb.yaml | 14 +++++++++++++ charts/fallstar/values.yaml | 12 +++++++++++ 8 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 charts/fallstar/ci/autoscaling-values.yaml create mode 100644 charts/fallstar/templates/hpa.yaml create mode 100644 charts/fallstar/templates/pdb.yaml diff --git a/charts/fallstar/ci/autoscaling-values.yaml b/charts/fallstar/ci/autoscaling-values.yaml new file mode 100644 index 0000000..f6edfa4 --- /dev/null +++ b/charts/fallstar/ci/autoscaling-values.yaml @@ -0,0 +1,18 @@ +# Autoscaling on MongoDB: no fixed replicas on the Deployment, an HPA instead. +image: + repository: ci-dummy-registry/fallstar + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +persistence: + enabled: false + +resources: + requests: + cpu: 100m + +autoscaling: + enabled: true diff --git a/charts/fallstar/ci/scaleout-values.yaml b/charts/fallstar/ci/scaleout-values.yaml index 3a0d5cb..75e0528 100644 --- a/charts/fallstar/ci/scaleout-values.yaml +++ b/charts/fallstar/ci/scaleout-values.yaml @@ -12,3 +12,6 @@ database: persistence: enabled: false + +podDisruptionBudget: + enabled: true diff --git a/charts/fallstar/templates/NOTES.txt b/charts/fallstar/templates/NOTES.txt index 3fcecea..b561dbf 100644 --- a/charts/fallstar/templates/NOTES.txt +++ b/charts/fallstar/templates/NOTES.txt @@ -19,7 +19,7 @@ The generated password is read with: kubectl get secret -n {{ .Release.Namespace }} {{ include "fallstar.authSecretName" . }} -o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d; echo {{- end }} -Database: {{ .Values.database.backend }}, replicas: {{ .Values.replicaCount }}. +Database: {{ .Values.database.backend }}, replicas: {{ if .Values.autoscaling.enabled }}{{ .Values.autoscaling.minReplicas }}-{{ .Values.autoscaling.maxReplicas }} (autoscaled){{ else }}{{ .Values.replicaCount }}{{ end }}. {{- if include "fallstar.singleWriter" . }} A single Pod owns the data, so an upgrade restarts it. More replicas need database.backend=mongo and persistence.enabled=false. {{- end }} diff --git a/charts/fallstar/templates/_helpers.tpl b/charts/fallstar/templates/_helpers.tpl index b6d10d9..9270301 100644 --- a/charts/fallstar/templates/_helpers.tpl +++ b/charts/fallstar/templates/_helpers.tpl @@ -60,6 +60,17 @@ true {{- end -}} {{- end }} +{{/* +Highest number of Pods this release can run, autoscaling included. +*/}} +{{- define "fallstar.maxReplicas" -}} +{{- if .Values.autoscaling.enabled -}} +{{- max (.Values.replicaCount | int) (.Values.autoscaling.maxReplicas | int) -}} +{{- else -}} +{{- .Values.replicaCount | int -}} +{{- end -}} +{{- end }} + {{- define "fallstar.validateValues" -}} {{- if not .Values.image.repository }} {{- fail "image.repository is required." }} @@ -70,9 +81,12 @@ true {{- if and .Values.auth.secret (lt (len .Values.auth.secret) 32) }} {{- fail "auth.secret needs at least 32 characters." }} {{- end }} -{{- if and (gt (int .Values.replicaCount) 1) (include "fallstar.singleWriter" .) }} +{{- if and (gt (int (include "fallstar.maxReplicas" .)) 1) (include "fallstar.singleWriter" .) }} {{- fail "More than one replica requires database.backend=mongo and persistence.enabled=false: SQLite is a local file, and a ReadWriteOnce volume cannot be mounted by two Pods." }} {{- end }} +{{- if and .Values.autoscaling.enabled (not .Values.resources.requests) }} +{{- fail "autoscaling.enabled needs resources.requests.cpu, the target being a share of it." }} +{{- end }} {{- if and .Values.ingress.enabled (not .Values.ingress.host) }} {{- fail "ingress.host is required when ingress.enabled=true." }} {{- end }} diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml index 11d6076..e221338 100644 --- a/charts/fallstar/templates/deployment.yaml +++ b/charts/fallstar/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: {{- include "fallstar.labels" . | nindent 4 }} spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} + {{- end }} strategy: {{- if include "fallstar.singleWriter" . }} # The SQLite file or the volume belongs to one Pod, so the old Pod must be gone before the new one starts. diff --git a/charts/fallstar/templates/hpa.yaml b/charts/fallstar/templates/hpa.yaml new file mode 100644 index 0000000..d408ece --- /dev/null +++ b/charts/fallstar/templates/hpa.yaml @@ -0,0 +1,23 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "fallstar.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} diff --git a/charts/fallstar/templates/pdb.yaml b/charts/fallstar/templates/pdb.yaml new file mode 100644 index 0000000..b85e545 --- /dev/null +++ b/charts/fallstar/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + selector: + matchLabels: + {{- include "fallstar.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml index 03678f8..8be0b7e 100644 --- a/charts/fallstar/values.yaml +++ b/charts/fallstar/values.yaml @@ -14,6 +14,18 @@ fullnameOverride: "" # -- More than one replica requires database.backend=mongo and persistence.enabled=false. replicaCount: 1 +# Same requirements as replicaCount, and resources.requests.cpu must be set for the CPU target. +autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 4 + targetCPUUtilizationPercentage: 80 + +# Only useful with two or more replicas: with one, it blocks node drains. +podDisruptionBudget: + enabled: false + minAvailable: 1 + auth: # -- Master secret of at least 32 characters, generated on first install when empty and kept across upgrades. # Changing it signs everyone out and makes the stored repository tokens unreadable. From ed58860be1b0a58e6511588fdf46bb8978c81429 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 12 Oct 2026 17:35:45 +0200 Subject: [PATCH 06/33] Mention IstarCI in the contributing guide --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb2359a..6410584 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,3 +140,8 @@ This is a details block. ``` - [Markdown Extensions](https://vitepress.dev/guide/markdown) + +## Run the pipeline locally + +[IstarCI](https://github.com/devpro/istarci) runs the GitHub Actions pipeline in Docker before the push. +`istarci add` and `istarci install-hook` set it up for this repository. From d6b2a4d5d399e0a5a5b158054659cbc0a8294595 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 12 Oct 2026 17:39:22 +0200 Subject: [PATCH 07/33] Use sudo-free composite actions to install Helm, chart-testing and kube-linter Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/ci.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b35c2a..2d7ba1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,7 @@ jobs: - name: Build with VitePress run: npm run docs:build - name: Install Helm - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 - with: - version: latest + uses: devpro/github-workflow-parts/actions/helm/setup@b0874542b7775a5dff2edb6bd3f7aa4861acda23 - name: Install kube-linter env: KUBE_LINTER_VERSION: "0.8.3" @@ -50,12 +48,15 @@ jobs: curl -sSLo kube-linter.tar.gz "https://github.com/stackrox/kube-linter/releases/download/v${KUBE_LINTER_VERSION}/kube-linter-linux.tar.gz" echo "${KUBE_LINTER_SHA256} kube-linter.tar.gz" | sha256sum -c - tar -xzf kube-linter.tar.gz kube-linter - sudo mv kube-linter /usr/local/bin/ + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv kube-linter "${RUNNER_TEMP}/bin/kube-linter" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" rm kube-linter.tar.gz - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f + uses: devpro/github-workflow-parts/actions/chart-testing/setup@b0874542b7775a5dff2edb6bd3f7aa4861acda23 - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts From 4c6c8a431f84832ef2a54bee9059a3cfabfc8dce Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 12 Oct 2026 17:40:05 +0200 Subject: [PATCH 08/33] Set the restart policy of the fallstar pod, as the other charts do Co-Authored-By: Claude Sonnet 5.5 --- charts/fallstar/templates/deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml index e221338..2e8b0bc 100644 --- a/charts/fallstar/templates/deployment.yaml +++ b/charts/fallstar/templates/deployment.yaml @@ -39,6 +39,7 @@ spec: securityContext: {{- toYaml . | nindent 8 }} {{- end }} + restartPolicy: Always containers: - name: fallstar image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" From 6084d4188be7537083402cceb2241da10788797a Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 12 Oct 2026 18:25:20 +0200 Subject: [PATCH 09/33] Pin the shared actions to the pushed version Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d7ba1a..ad7f598 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Build with VitePress run: npm run docs:build - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@b0874542b7775a5dff2edb6bd3f7aa4861acda23 + uses: devpro/github-workflow-parts/actions/helm/setup@defde40cfcaa2dc67c1cc47a27d20605f76d74b0 - name: Install kube-linter env: KUBE_LINTER_VERSION: "0.8.3" @@ -56,7 +56,7 @@ jobs: - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@b0874542b7775a5dff2edb6bd3f7aa4861acda23 + uses: devpro/github-workflow-parts/actions/chart-testing/setup@defde40cfcaa2dc67c1cc47a27d20605f76d74b0 - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts From 4fae531232517dad43d4c4ba790003cd8d835ef5 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sat, 17 Oct 2026 20:03:01 +0200 Subject: [PATCH 10/33] Fix md in editorconfig --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 60dc40f..9856da9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,6 @@ insert_final_newline = true trim_trailing_whitespace = true max_line_length = 180 -[md] +[*.md] trim_trailing_whitespace = false max_line_length = 240 From d9feb9a6fdb39e995cfe2c7e2a0f30020dddbec4 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 19 Oct 2026 13:04:46 +0200 Subject: [PATCH 11/33] Describe the Fallstar chart to Fallstar, and require the image tag The image is private and the chart public, as for Sidelab: fallstar.yaml names ghcr.io/devpro/fallstar and its ghcr-pull Secret, with the choices of a deployment (database, secrets, ingress, certificate issuer). The image is tagged by its pipeline run, never by the application version, so the tag is a value to set rather than a default that does not exist. Co-Authored-By: Claude Opus 5.5 --- charts/fallstar/Chart.yaml | 1 - charts/fallstar/README.md | 7 + charts/fallstar/ci/autoscaling-values.yaml | 1 + charts/fallstar/ci/ci-values.yaml | 1 + charts/fallstar/ci/mongo-ingress-values.yaml | 1 + charts/fallstar/ci/scaleout-values.yaml | 1 + charts/fallstar/fallstar.yaml | 230 +++++++++++++++++++ charts/fallstar/templates/_helpers.tpl | 3 + charts/fallstar/templates/deployment.yaml | 2 +- charts/fallstar/values.yaml | 2 +- 10 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 charts/fallstar/fallstar.yaml diff --git a/charts/fallstar/Chart.yaml b/charts/fallstar/Chart.yaml index 5292948..58c40fa 100644 --- a/charts/fallstar/Chart.yaml +++ b/charts/fallstar/Chart.yaml @@ -5,7 +5,6 @@ description: > Deploys the Fallstar server, a catalog of recipes rendered into Flux, Argo CD or Fleet manifests on git branches. type: application version: 0.1.0 -appVersion: "0.1.0" keywords: - fallstar - gitops diff --git a/charts/fallstar/README.md b/charts/fallstar/README.md index c6c3c59..93825e7 100644 --- a/charts/fallstar/README.md +++ b/charts/fallstar/README.md @@ -9,10 +9,12 @@ helm repo add devpro https://devpro.github.io/helm-charts helm repo update helm upgrade --install fallstar devpro/fallstar \ --set image.repository=/fallstar \ + --set image.tag= \ --namespace fallstar --create-namespace ``` The image is built from the `Dockerfile` of the Fallstar repository. +`ghcr.io/devpro/fallstar` is private, so its pull needs a Secret of type `kubernetes.io/dockerconfigjson` holding a token with `read:packages`, named in `imagePullSecrets`. > **Tip:** Every option is documented in [values.yaml](values.yaml). @@ -24,3 +26,8 @@ kubectl delete namespace fallstar ``` > **Note:** Deleting the namespace deletes the generated `-auth` Secret and the data volume. + +## Fallstar + +`fallstar.yaml` describes the choices of a deployment (database, secrets, image pull, TLS) and the values each one implies, for [Fallstar](https://github.com/devpro/fallstar) itself. +This Helm repository added as a Fallstar recipe source shows the chart in its store with these choices. diff --git a/charts/fallstar/ci/autoscaling-values.yaml b/charts/fallstar/ci/autoscaling-values.yaml index f6edfa4..80ea66a 100644 --- a/charts/fallstar/ci/autoscaling-values.yaml +++ b/charts/fallstar/ci/autoscaling-values.yaml @@ -1,6 +1,7 @@ # Autoscaling on MongoDB: no fixed replicas on the Deployment, an HPA instead. image: repository: ci-dummy-registry/fallstar + tag: ci database: backend: mongo diff --git a/charts/fallstar/ci/ci-values.yaml b/charts/fallstar/ci/ci-values.yaml index 6f26068..79002ec 100644 --- a/charts/fallstar/ci/ci-values.yaml +++ b/charts/fallstar/ci/ci-values.yaml @@ -1,2 +1,3 @@ image: repository: ci-dummy-registry/fallstar + tag: ci diff --git a/charts/fallstar/ci/mongo-ingress-values.yaml b/charts/fallstar/ci/mongo-ingress-values.yaml index c22ffb1..980da9c 100644 --- a/charts/fallstar/ci/mongo-ingress-values.yaml +++ b/charts/fallstar/ci/mongo-ingress-values.yaml @@ -1,5 +1,6 @@ image: repository: ci-dummy-registry/fallstar + tag: ci database: backend: mongo diff --git a/charts/fallstar/ci/scaleout-values.yaml b/charts/fallstar/ci/scaleout-values.yaml index 75e0528..64800d1 100644 --- a/charts/fallstar/ci/scaleout-values.yaml +++ b/charts/fallstar/ci/scaleout-values.yaml @@ -2,6 +2,7 @@ # Guards the RollingUpdate branch of the Deployment strategy and the replica validation in _helpers.tpl. image: repository: ci-dummy-registry/fallstar + tag: ci replicaCount: 3 diff --git a/charts/fallstar/fallstar.yaml b/charts/fallstar/fallstar.yaml new file mode 100644 index 0000000..c64da63 --- /dev/null +++ b/charts/fallstar/fallstar.yaml @@ -0,0 +1,230 @@ +# Recipe of this chart for Fallstar: the choices a Fallstar deployment makes, and the values each one implies. +# Format: https://github.com/devpro/fallstar/blob/main/docs/sources.md +title: Fallstar +description: GitOps made easy, a catalog of recipes rendered into Flux, Argo CD or Fleet manifests on git branches. +category: application +namespace: fallstar +layer: apps +# The namespace is a resource of the infra layer, so the ExternalSecrets of the apps layer find it before the release exists. +createNamespace: false + +options: + - key: host + title: Host + type: string + default: fallstar.${{ cluster.domain }} + value: ingress.host + - key: tag + title: Image tag + description: A pkg.yml run of the Fallstar repository, 1.0.. + type: string + value: image.tag + - key: privateImages + title: Image pulled with a ghcr.io token + group: Image + type: boolean + default: true + + - key: database + title: Database + description: SQLite is one file on one volume, MongoDB allows several replicas. + group: Database + type: enum + values: [sqlite, mongo] + default: sqlite + - key: storageSize + title: Volume size + description: The SQLite file and the mirrors of the repositories. + group: Database + type: string + default: 2Gi + value: persistence.size + when: { equals: { database: sqlite } } + - key: storageClass + title: Storage class + description: Empty for the cluster default. + group: Database + type: string + value: persistence.storageClass + when: { equals: { database: sqlite } } + - key: replicas + title: Replicas + group: Database + type: number + default: 1 + bind: { chart: fallstar, path: [values, replicaCount] } + when: { equals: { database: mongo } } + + - key: ingressClass + title: Ingress class + group: Access + type: string + default: ${{ cluster.uses.ingress }} + value: ingress.className + - key: clusterIssuer + title: Certificate issuer + description: A cert-manager ClusterIssuer ordering a certificate for the host, the ingress default certificate when empty. + group: Access + type: string + bind: { chart: fallstar, path: [values, ingress, annotations, cert-manager.io/cluster-issuer] } + +values: + image: + repository: ghcr.io/devpro/fallstar + ingress: + enabled: true + tls: + enabled: true + +patches: + - when: { secretsManaged: true } + chart: fallstar + patch: + values: + auth: + existingSecret: fallstar-auth + - when: { equals: { database: sqlite } } + chart: fallstar + patch: + values: + database: + backend: sqlite + persistence: + enabled: true + - when: { equals: { database: mongo } } + chart: fallstar + notes: + - path: [values, persistence] + text: The mirrors of the repositories are re-created from their remotes, so no Pod needs a volume. + patch: + values: + replicaCount: ${{ options.replicas }} + database: + backend: mongo + mongo: + existingSecret: fallstar-db + persistence: + enabled: false + - when: { option: privateImages } + chart: fallstar + patch: + values: + imagePullSecrets: + - name: ghcr-pull + - when: { option: clusterIssuer } + chart: fallstar + patch: + values: + ingress: + annotations: + cert-manager.io/cluster-issuer: ${{ options.clusterIssuer }} + +resources: + - file: infra/namespaces/fallstar.yaml + shared: true + object: + apiVersion: v1 + kind: Namespace + metadata: { name: fallstar } + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true } + notes: + - path: [] + text: Held in the secret store rather than generated by the chart, so a rebuilt cluster keeps the stored repository tokens readable and the same admin password. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: fallstar-auth, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: fallstar-auth + creationPolicy: Owner + template: + data: + FALLSTAR_SECRET: "{{ .secret }}" + ADMIN_USERNAME: admin + ADMIN_PASSWORD: "{{ .adminPassword }}" + data: + - secretKey: secret + remoteRef: + key: fallstar-secret + - secretKey: adminPassword + remoteRef: + key: fallstar-admin-password + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true, equals: { database: mongo } } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: fallstar-db, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: fallstar-db + creationPolicy: Owner + data: + - secretKey: DB_URL + remoteRef: + key: fallstar-mongodb-url + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true, option: privateImages } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: ghcr-pull, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: ghcr-pull + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: '{"auths":{"ghcr.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}}' + data: + - secretKey: username + remoteRef: + key: ghcr-username + - secretKey: password + remoteRef: + key: ghcr-token + +secrets: + - { name: fallstar-secret, description: "Master secret, at least 32 characters: it signs the sessions and encrypts the stored repository tokens.", generate: hex32, when: { secretsManaged: true }, target: { secret: fallstar-auth, key: FALLSTAR_SECRET, namespace: fallstar } } + - { name: fallstar-admin-username, description: Name of the admin account., value: admin, when: { secretsManaged: true, secretStore: false }, target: { secret: fallstar-auth, key: ADMIN_USERNAME, namespace: fallstar } } + - { name: fallstar-admin-password, description: Password of the admin account., when: { secretsManaged: true }, target: { secret: fallstar-auth, key: ADMIN_PASSWORD, namespace: fallstar } } + - { name: fallstar-mongodb-url, description: "MongoDB connection string, mongodb+srv://:@/fallstar.", when: { secretsManaged: true, equals: { database: mongo } }, target: { secret: fallstar-db, key: DB_URL, namespace: fallstar } } + - { name: ghcr-username, description: GitHub user pulling the private image., when: { secretStore: true, option: privateImages } } + - { name: ghcr-token, description: "GitHub token with read:packages on ghcr.io/devpro.", when: { secretStore: true, option: privateImages } } + +warnings: + - when: { secretsManaged: false, equals: { database: mongo } } + text: reads its database from Secret fallstar-db in namespace fallstar, which neither a secret store nor SOPS creates on this cluster + +guide: + prerequisites: + - A DNS record of the host to the ingress load balancer IP. + - "With the private image and SOPS: a ghcr-pull Secret of type kubernetes.io/dockerconfigjson in namespace fallstar." + verify: + - kubectl get pods -n fallstar + - kubectl logs -n fallstar deploy/fallstar --tail=30 + gotchas: + - title: Pod ImagePullBackOff + cause: ghcr-pull missing, or a token without read:packages. + fix: kubectl get secret -n fallstar ghcr-pull + - title: Second replica fails to start + cause: SQLite is one file on a ReadWriteOnce volume. + fix: Choose MongoDB before raising the replicas. + - title: Stored repository tokens no longer readable + cause: FALLSTAR_SECRET changed, it encrypts them. + fix: Restore the previous secret, or enter the tokens again. diff --git a/charts/fallstar/templates/_helpers.tpl b/charts/fallstar/templates/_helpers.tpl index 9270301..5595d10 100644 --- a/charts/fallstar/templates/_helpers.tpl +++ b/charts/fallstar/templates/_helpers.tpl @@ -75,6 +75,9 @@ Highest number of Pods this release can run, autoscaling included. {{- if not .Values.image.repository }} {{- fail "image.repository is required." }} {{- end }} +{{- if not .Values.image.tag }} +{{- fail "image.tag is required." }} +{{- end }} {{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.url) (not .Values.database.mongo.existingSecret) }} {{- fail "database.backend=mongo needs database.mongo.url or database.mongo.existingSecret." }} {{- end }} diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml index 2e8b0bc..565e25b 100644 --- a/charts/fallstar/templates/deployment.yaml +++ b/charts/fallstar/templates/deployment.yaml @@ -42,7 +42,7 @@ spec: restartPolicy: Always containers: - name: fallstar - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} {{- with .Values.containerSecurityContext }} securityContext: diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml index 8be0b7e..3b076c7 100644 --- a/charts/fallstar/values.yaml +++ b/charts/fallstar/values.yaml @@ -2,7 +2,7 @@ image: # -- Server image repository, required. repository: pullPolicy: IfNotPresent - # -- Defaults to the chart appVersion. + # -- Server image tag, required: a pkg.yml run of the Fallstar repository, 1.0.. tag: "" imagePullSecrets: [] From 686cdad67f93e1c753eee8eae78d66bfac08a73f Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 19 Oct 2026 19:33:19 +0200 Subject: [PATCH 12/33] Test the charts in CI: schema validation, unit tests and installs on kind Every chart is rendered with its ci values and validated with kubeconform, which gives capi-templates, letsencrypt and rancher-cluster-templates the ci values they need to render. Fallstar gets helm-unittest suites, run with the standalone binary. Changed charts are installed on a kind cluster named after the run, except those that cannot get ready on a bare cluster. The workflow files lose their byte order mark, which some tools refuse. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FvUmuFc3tsqikAZ8gPa83n --- .editorconfig | 4 + .github/workflows/ci.yml | 83 ++++++++++++-- .github/workflows/pkg.yml | 2 +- .istarci.yml | 3 + AGENTS.md | 4 + charts/capi-templates/Chart.yaml | 2 +- charts/capi-templates/ci/ci-values.yaml | 9 ++ charts/fallstar/.helmignore | 1 + charts/fallstar/tests/deployment_test.yaml | 103 ++++++++++++++++++ charts/fallstar/tests/resources_test.yaml | 103 ++++++++++++++++++ charts/fallstar/tests/validation_test.yaml | 77 +++++++++++++ charts/letsencrypt/Chart.yaml | 2 +- charts/letsencrypt/ci/ci-values.yaml | 6 + charts/rancher-cluster-templates/Chart.yaml | 2 +- .../ci/aws-values.yaml | 39 +++++++ scripts/validate_charts.sh | 45 ++++++++ 16 files changed, 473 insertions(+), 12 deletions(-) create mode 100644 .istarci.yml create mode 100644 charts/capi-templates/ci/ci-values.yaml create mode 100644 charts/fallstar/tests/deployment_test.yaml create mode 100644 charts/fallstar/tests/resources_test.yaml create mode 100644 charts/fallstar/tests/validation_test.yaml create mode 100644 charts/letsencrypt/ci/ci-values.yaml create mode 100644 charts/rancher-cluster-templates/ci/aws-values.yaml create mode 100755 scripts/validate_charts.sh diff --git a/.editorconfig b/.editorconfig index 9856da9..b7680de 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,10 @@ insert_final_newline = true trim_trailing_whitespace = true max_line_length = 180 +# Workflow files are read by tools that refuse a byte order mark. +[.github/workflows/*.yml] +charset = utf-8 + [*.md] trim_trailing_whitespace = false max_line_length = 240 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad7f598..4d1e209 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: CI # title: CI (Continuous Integration) pipeline on: @@ -71,6 +71,37 @@ jobs: - name: Lint charts if: steps.list-changed.outputs.changed == 'true' run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch ${{ github.event.repository.default_branch }} + - name: Install kubeconform + env: + KUBECONFORM_VERSION: "0.8.0" + KUBECONFORM_SHA256: "9bc2bffbf71f261128533edaf912153948b7ff238f9a531ae6d34466ec287883" + run: | + curl -sSLo kubeconform.tar.gz "https://github.com/yannh/kubeconform/releases/download/v${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" + echo "${KUBECONFORM_SHA256} kubeconform.tar.gz" | sha256sum -c - + tar -xzf kubeconform.tar.gz kubeconform + mkdir -p "${RUNNER_TEMP}/bin" + mv kubeconform "${RUNNER_TEMP}/bin/kubeconform" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + rm kubeconform.tar.gz + - name: Validate manifests against the Kubernetes schemas + run: ./scripts/validate_charts.sh + # The standalone binary of the helm-unittest plugin, so no plugin is installed into Helm. + - name: Install helm-unittest + env: + HELM_UNITTEST_VERSION: "1.2.1" + HELM_UNITTEST_SHA256: "48e86ecc1467b009630e5954375e5d8494139f73b466df5fe8c04fa45b634939" + run: | + curl -sSLo helm-unittest.tgz "https://github.com/helm-unittest/helm-unittest/releases/download/v${HELM_UNITTEST_VERSION}/helm-unittest-linux-amd64-${HELM_UNITTEST_VERSION}.tgz" + echo "${HELM_UNITTEST_SHA256} helm-unittest.tgz" | sha256sum -c - + tar -xzf helm-unittest.tgz untt-linux-amd64 + mkdir -p "${RUNNER_TEMP}/bin" + mv untt-linux-amd64 "${RUNNER_TEMP}/bin/untt" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + rm helm-unittest.tgz + - name: Run chart unit tests + run: | + charts=$(dirname charts/*/tests | sort -u) + untt $charts # TODO: Enable when https://github.com/stackrox/kube-linter/issues/575 is fixed # - name: Scan yamls # id: kube-lint-scan @@ -78,10 +109,46 @@ jobs: # with: # directory: charts # config: .kube-linter.yaml - # TODO: debug & fix - # - name: Create kind cluster - # uses: helm/kind-action@v1.4.0 - # if: steps.list-changed.outputs.changed == 'true' - # - name: Run chart-testing (install) - # run: ct install $CHARTS_DIRECTORIES_ARG --target-branch ${{ github.event.repository.default_branch }} - # if: steps.list-changed.outputs.changed == 'true' + # kind and kubectl are installed by hand, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. + - name: Install kind and kubectl + if: steps.list-changed.outputs.changed == 'true' + env: + KIND_VERSION: "0.33.0" + KIND_SHA256: "aee6151561422756b764a4ae28e7f44cda5af5a9eead3cc9985112b1de8d8e0d" + KUBECTL_VERSION: "1.36.4" + KUBECTL_SHA256: "8b8f088da2dab964f853b38464033b1be15ede2839eca751482357c45abdd05a" + run: | + mkdir -p "${RUNNER_TEMP}/bin" + curl -sSLo "${RUNNER_TEMP}/bin/kind" "https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-linux-amd64" + echo "${KIND_SHA256} ${RUNNER_TEMP}/bin/kind" | sha256sum -c - + curl -sSLo "${RUNNER_TEMP}/bin/kubectl" "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl" + echo "${KUBECTL_SHA256} ${RUNNER_TEMP}/bin/kubectl" | sha256sum -c - + chmod +x "${RUNNER_TEMP}/bin/kind" "${RUNNER_TEMP}/bin/kubectl" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. + - name: Create kind cluster + if: steps.list-changed.outputs.changed == 'true' + run: kind create cluster --name ct-${{ github.run_id }} --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s + - name: Install charts + if: steps.list-changed.outputs.changed == 'true' + env: + # Charts that cannot reach a ready state on a bare cluster, given here rather than in ct.yaml so that ct lint still covers them. + # Custom resources only, whose CRDs a bare cluster lacks: capi-templates, gitops-bootstrap, letsencrypt, rancher-cluster-templates. + # Cloud providers and storage drivers: azure-storage, outscale. + # Private images, which the cluster cannot pull: fallstar, liveship, sidelab, trackboard. + # Applications failing their probes without the services they are configured for: keeptrack, todoblazor. + # Default images no longer published: terraform-backend-mongodb, wordpress. + # Not tried on kind yet: grafana-stack, nfs-ganesha. + EXCLUDED_CHARTS: >- + capi-templates,gitops-bootstrap,letsencrypt,rancher-cluster-templates, + azure-storage,outscale, + fallstar,liveship,sidelab,trackboard, + keeptrack,todoblazor, + terraform-backend-mongodb,wordpress, + grafana-stack,nfs-ganesha + run: | + ct install ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }} \ + --excluded-charts "${EXCLUDED_CHARTS// /}" --helm-extra-args "--timeout 300s" + - name: Delete kind cluster + if: always() && steps.list-changed.outputs.changed == 'true' + run: kind delete cluster --name ct-${{ github.run_id }} diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index e432f7e..df4556d 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -1,4 +1,4 @@ -# title: Continuous Delivery (Packaging) +# title: Continuous Delivery (Packaging) # requirements: # - Create manually "gh-pages" branch # - Maintain pages files in "gh-pages" branch: index.tpl, placeholder.png diff --git a/.istarci.yml b/.istarci.yml new file mode 100644 index 0000000..4813205 --- /dev/null +++ b/.istarci.yml @@ -0,0 +1,3 @@ +runner: + # ct install starts a kind cluster through the host Docker daemon, as on a hosted runner. + docker: ["build"] diff --git a/AGENTS.md b/AGENTS.md index 8844788..d853484 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,8 @@ helm lint charts/ # lint helm template myapp charts/ -f values.yaml --namespace myns # render the manifests helm upgrade --install myapp charts/ -f values.yaml --namespace myns --create-namespace kube-linter lint charts --config .kube-linter.yaml # the check CI runs +scripts/validate_charts.sh [charts/] # validate the rendered manifests with kubeconform +untt charts/ # run the chart unit tests (helm-unittest binary) npm run docs:dev # serve the documentation site ``` @@ -36,6 +38,8 @@ Where a chart pins an image tag in `values.yaml`, that tag and `appVersion` must - Chart values are documented with `# --` comments above the key, which is the helm-docs annotation form. - A chart that packages a devpro application keeps its values structure close to the application's configuration keys, so that a setting can be traced from `values.yaml` to the environment variable it becomes. - CI runs `kube-linter` over `charts/` with `.kube-linter.yaml`, and `ct lint` over the charts changed against `main`. +- CI renders every chart with each of its `ci/*-values.yaml` files and validates the manifests with kubeconform, so a chart that needs values to render has a `ci/` directory. +- Unit tests of a chart live in its `tests/` directory as helm-unittest suites, listed in its `.helmignore` so they are not packaged. Exclusions belong in `.kube-linter.yaml` with a comment saying why. - Markdown and YAML are linted in CI through `.markdownlint-cli2.yaml` and `.yamllint.yaml`. - Do not run markdownlint: linting is run manually by the maintainer and in CI. diff --git a/charts/capi-templates/Chart.yaml b/charts/capi-templates/Chart.yaml index f6ddb16..bc1b560 100644 --- a/charts/capi-templates/Chart.yaml +++ b/charts/capi-templates/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: capi-templates description: Helm chart for Cluster API (CAPI) Templates type: application -version: 0.1.2 +version: 0.1.3 appVersion: "1.0.0" home: https://github.com/devpro/helm-charts/tree/main/charts/capi-templates maintainers: diff --git a/charts/capi-templates/ci/ci-values.yaml b/charts/capi-templates/ci/ci-values.yaml new file mode 100644 index 0000000..af58d81 --- /dev/null +++ b/charts/capi-templates/ci/ci-values.yaml @@ -0,0 +1,9 @@ +name: ci-cluster +type: gke +googlecloud: + project: ci-project + region: europe-west1 + zone: europe-west1-b + vpc: ci-vpc + subnet: + name: ci-subnet diff --git a/charts/fallstar/.helmignore b/charts/fallstar/.helmignore index 517eba1..2f957f7 100644 --- a/charts/fallstar/.helmignore +++ b/charts/fallstar/.helmignore @@ -8,3 +8,4 @@ values.mine.yaml temp*.yaml kubeconfig +tests/ diff --git a/charts/fallstar/tests/deployment_test.yaml b/charts/fallstar/tests/deployment_test.yaml new file mode 100644 index 0000000..34ef899 --- /dev/null +++ b/charts/fallstar/tests/deployment_test.yaml @@ -0,0 +1,103 @@ +suite: deployment +# secret.yaml is rendered too, the Deployment carrying its checksum. +templates: + - deployment.yaml + - secret.yaml +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +release: + name: fallstar +tests: + - it: runs the pinned image + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].image + value: registry.example.com/fallstar:1.0.1 + - it: keeps service links off, so no FALLSTAR_PORT reaches the server + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.enableServiceLinks + value: false + - it: recreates the Pod on SQLite + asserts: + - template: deployment.yaml + equal: + path: spec.strategy.type + value: Recreate + - template: deployment.yaml + equal: + path: spec.template.spec.volumes[0].persistentVolumeClaim.claimName + value: fallstar-data + - it: rolls on MongoDB without a volume + set: + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + replicaCount: 3 + asserts: + - template: deployment.yaml + equal: + path: spec.strategy.type + value: RollingUpdate + - template: deployment.yaml + equal: + path: spec.replicas + value: 3 + - template: deployment.yaml + isNotNull: + path: spec.template.spec.volumes[0].emptyDir + - it: reads the backend and the Secrets as environment variables + set: + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - template: deployment.yaml + contains: + path: spec.template.spec.containers[0].env + content: + name: DB_BACKEND + value: mongo + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].envFrom + value: + - secretRef: + name: fallstar-auth + - secretRef: + name: fallstar-mongo + - it: reads existing Secrets when named + set: + auth.existingSecret: my-auth + database.backend: mongo + database.mongo.existingSecret: my-db + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].envFrom + value: + - secretRef: + name: my-auth + - secretRef: + name: my-db + - it: leaves the replicas to the autoscaler + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + resources.requests.cpu: 100m + asserts: + - template: deployment.yaml + notExists: + path: spec.replicas + - it: mounts an existing claim when named + set: + persistence.existingClaim: my-data + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.volumes[0].persistentVolumeClaim.claimName + value: my-data diff --git a/charts/fallstar/tests/resources_test.yaml b/charts/fallstar/tests/resources_test.yaml new file mode 100644 index 0000000..1ba8987 --- /dev/null +++ b/charts/fallstar/tests/resources_test.yaml @@ -0,0 +1,103 @@ +# The optional resources, each rendered only when its values ask for it. +suite: optional resources +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +release: + name: fallstar +tests: + - it: creates a claim by default + template: pvc.yaml + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.resources.requests.storage + value: 2Gi + - it: creates no claim for an existing one + template: pvc.yaml + set: + persistence.existingClaim: my-data + asserts: + - hasDocuments: + count: 0 + - it: creates the auth and MongoDB Secrets + template: secret.yaml + set: + auth.secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + auth.adminPassword: s3cret + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - hasDocuments: + count: 2 + - equal: + path: stringData + value: + FALLSTAR_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ADMIN_USERNAME: admin + ADMIN_PASSWORD: s3cret + documentIndex: 0 + - equal: + path: stringData.DB_URL + value: mongodb://db:27017/fallstar + documentIndex: 1 + - it: generates the master secret and the admin password when empty + template: secret.yaml + asserts: + - matchRegex: + path: stringData.FALLSTAR_SECRET + pattern: ^[A-Za-z0-9]{48}$ + - matchRegex: + path: stringData.ADMIN_PASSWORD + pattern: ^[A-Za-z0-9]{20}$ + - it: creates no Secret when existing ones are named + template: secret.yaml + set: + auth.existingSecret: my-auth + asserts: + - hasDocuments: + count: 0 + - it: scales on CPU when autoscaling + template: hpa.yaml + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + resources.requests.cpu: 100m + asserts: + - equal: + path: spec.maxReplicas + value: 4 + - equal: + path: spec.metrics[0].resource.target.averageUtilization + value: 80 + - it: creates the disruption budget when enabled + template: pdb.yaml + set: + podDisruptionBudget.enabled: true + asserts: + - equal: + path: spec.minAvailable + value: 1 + - it: serves the host over TLS + template: ingress.yaml + set: + ingress.enabled: true + ingress.className: traefik + ingress.host: fallstar.example.com + ingress.tls.enabled: true + asserts: + - equal: + path: spec.ingressClassName + value: traefik + - equal: + path: spec.tls[0] + value: + hosts: + - fallstar.example.com + secretName: fallstar-tls + - equal: + path: spec.rules[0].host + value: fallstar.example.com diff --git a/charts/fallstar/tests/validation_test.yaml b/charts/fallstar/tests/validation_test.yaml new file mode 100644 index 0000000..6a3b816 --- /dev/null +++ b/charts/fallstar/tests/validation_test.yaml @@ -0,0 +1,77 @@ +# The rules of fallstar.validateValues, which run from secret.yaml. +suite: values validation +templates: + - secret.yaml +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +tests: + - it: renders with the defaults and an image + asserts: + - notFailedTemplate: {} + - it: requires the image repository + set: + image.repository: "" + asserts: + - failedTemplate: + errorMessage: image.repository is required. + - it: requires the image tag + set: + image.tag: "" + asserts: + - failedTemplate: + errorMessage: image.tag is required. + - it: requires a connection string on MongoDB + set: + database.backend: mongo + asserts: + - failedTemplate: + errorMessage: database.backend=mongo needs database.mongo.url or database.mongo.existingSecret. + - it: accepts an existing Secret instead of a connection string + set: + database.backend: mongo + database.mongo.existingSecret: fallstar-db + asserts: + - notFailedTemplate: {} + - it: rejects a short master secret + set: + auth.secret: too-short + asserts: + - failedTemplate: + errorMessage: auth.secret needs at least 32 characters. + - it: rejects several replicas on SQLite + set: + replicaCount: 2 + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: rejects several replicas on a volume, even on MongoDB + set: + replicaCount: 2 + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: rejects autoscaling on SQLite, its maximum being above one + set: + autoscaling.enabled: true + resources.requests.cpu: 100m + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: requires a CPU request for autoscaling + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + asserts: + - failedTemplate: + errorMessage: autoscaling.enabled needs resources.requests.cpu, the target being a share of it. + - it: requires a host for the ingress + set: + ingress.enabled: true + asserts: + - failedTemplate: + errorMessage: ingress.host is required when ingress.enabled=true. diff --git a/charts/letsencrypt/Chart.yaml b/charts/letsencrypt/Chart.yaml index d1cfa2e..2c8d70c 100644 --- a/charts/letsencrypt/Chart.yaml +++ b/charts/letsencrypt/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: letsencrypt description: Helm chart for managing Let's Encrypt certificate issuers type: application -version: "0.1.4" +version: "0.1.5" appVersion: "1.0.0" maintainers: - name: devpro diff --git a/charts/letsencrypt/ci/ci-values.yaml b/charts/letsencrypt/ci/ci-values.yaml new file mode 100644 index 0000000..9f353af --- /dev/null +++ b/charts/letsencrypt/ci/ci-values.yaml @@ -0,0 +1,6 @@ +registration: + emailAddress: admin@example.com +ingress: + className: nginx +selfsigned: + enabled: true diff --git a/charts/rancher-cluster-templates/Chart.yaml b/charts/rancher-cluster-templates/Chart.yaml index 4c78518..a4b7cc8 100644 --- a/charts/rancher-cluster-templates/Chart.yaml +++ b/charts/rancher-cluster-templates/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: rancher-cluster-templates description: Helm chart for managing Rancher cluster templates type: application -version: "0.1.6" +version: "0.1.7" appVersion: "0.1.0" annotations: catalog.cattle.io/type: cluster-template diff --git a/charts/rancher-cluster-templates/ci/aws-values.yaml b/charts/rancher-cluster-templates/ci/aws-values.yaml new file mode 100644 index 0000000..b0bbcf4 --- /dev/null +++ b/charts/rancher-cluster-templates/ci/aws-values.yaml @@ -0,0 +1,39 @@ +cluster: + name: ci-cluster + +cloudprovider: aws + +cloudCredentialSecretName: cattle-global-data:cc-ci + +nodepools: + - etcd: true + controlplane: true + worker: true + labels: {} + quantity: 1 + paused: false + name: nodepool-1 + deviceName: "/dev/sda1" + encryptEbsVolume: false + endpoint: "" + iamInstanceProfile: "" + insecureTransport: false + instanceType: t3a.medium + region: us-west-2 + createSecurityGroup: true + keypairName: "" + securityGroupReadonly: false + sshKeyContents: "" + subnetId: "" + monitoring: false + privateAddressOnly: false + requestSpotInstance: false + retries: 5 + rootSize: 16 + spotPrice: 0.5 + sshUser: ubuntu + storageType: gp2 + useEbsOptimizedInstance: false + usePrivateAddress: false + userdata: "" + zone: a diff --git a/scripts/validate_charts.sh b/scripts/validate_charts.sh new file mode 100755 index 0000000..11fd79d --- /dev/null +++ b/scripts/validate_charts.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Renders each chart and validates the manifests against the Kubernetes API schemas, which catches a wrong apiVersion or a misspelled field that helm lint lets through. +# A chart is rendered once per values file in its ci directory, as ct does, and with its default values when it has none. +# Usage: scripts/validate_charts.sh [chart directory...], every chart under charts when none is given. + +set -euo pipefail + +kubernetes_version="${KUBERNETES_VERSION:-1.36.5}" +# Custom resources are read from the CRDs catalog, pinned so that a schema change upstream never breaks an unrelated commit. +# A kind it does not hold, such as the Rancher node driver configurations, is counted as skipped rather than failed. +crds_catalog="https://raw.githubusercontent.com/datreeio/CRDs-catalog/501527ab9c40c827144c8f45945b25a023e344ce/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" + +if [[ $# -gt 0 ]]; then + charts=("$@") +else + charts=(charts/*/) +fi + +failed=() +for chart in "${charts[@]}"; do + chart="${chart%/}" + [[ -f "$chart/Chart.yaml" ]] || continue + + if grep -q "^dependencies:" "$chart/Chart.yaml"; then + helm dependency build "$chart" > /dev/null + fi + + values_files=("$chart"/ci/*-values.yaml) + [[ -e "${values_files[0]}" ]] || values_files=("") + + for values_file in "${values_files[@]}"; do + echo "--- $chart ${values_file:+with $(basename "$values_file")}" + if ! helm template ci "$chart" ${values_file:+--values "$values_file"} --namespace ci --kube-version "$kubernetes_version" \ + | kubeconform -strict -summary -ignore-missing-schemas -kubernetes-version "$kubernetes_version" \ + -schema-location default -schema-location "$crds_catalog"; then + failed+=("$chart ${values_file:+($(basename "$values_file"))}") + fi + done +done + +if [[ ${#failed[@]} -gt 0 ]]; then + echo "Invalid charts:" + printf ' %s\n' "${failed[@]}" + exit 1 +fi From 90d3e1622aa8e6c8217cb6678652afd60bc4d378 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 19 Oct 2026 19:54:41 +0200 Subject: [PATCH 13/33] Fix the terraform-backend-mongodb image tag and the bundled MongoDB host The default tag 1.3.0 was never published: the 1.3 release is 1.3.36770971345. The bundled MongoDB Service is named after the release alone when the release name holds "mongodb", as Bitnami names it, so the connection string follows the same rule. The liveness probe checks the port, as the health endpoint pings MongoDB and a database outage restarted the server. The chart is installed in CI with the bundled MongoDB. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FvUmuFc3tsqikAZ8gPa83n --- .github/workflows/ci.yml | 4 ++-- charts/terraform-backend-mongodb/Chart.yaml | 2 +- .../ci/ci-values.yaml | 5 +++++ .../templates/_helpers.tpl | 22 ++++++++++++++++--- .../templates/deployment.yaml | 4 ++-- charts/terraform-backend-mongodb/values.yaml | 2 +- 6 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 charts/terraform-backend-mongodb/ci/ci-values.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d1e209..f9786d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,14 +137,14 @@ jobs: # Cloud providers and storage drivers: azure-storage, outscale. # Private images, which the cluster cannot pull: fallstar, liveship, sidelab, trackboard. # Applications failing their probes without the services they are configured for: keeptrack, todoblazor. - # Default images no longer published: terraform-backend-mongodb, wordpress. + # Default images no longer published: wordpress. # Not tried on kind yet: grafana-stack, nfs-ganesha. EXCLUDED_CHARTS: >- capi-templates,gitops-bootstrap,letsencrypt,rancher-cluster-templates, azure-storage,outscale, fallstar,liveship,sidelab,trackboard, keeptrack,todoblazor, - terraform-backend-mongodb,wordpress, + wordpress, grafana-stack,nfs-ganesha run: | ct install ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }} \ diff --git a/charts/terraform-backend-mongodb/Chart.yaml b/charts/terraform-backend-mongodb/Chart.yaml index e54f42d..e6c7b28 100644 --- a/charts/terraform-backend-mongodb/Chart.yaml +++ b/charts/terraform-backend-mongodb/Chart.yaml @@ -2,7 +2,7 @@ name: terraform-backend-mongodb description: Helm chart for Terraform Backend MongoDB type: application -version: 0.3.0 +version: 0.3.1 appVersion: "1.3.0" home: https://github.com/devpro/terraform-backend-mongodb sources: diff --git a/charts/terraform-backend-mongodb/ci/ci-values.yaml b/charts/terraform-backend-mongodb/ci/ci-values.yaml new file mode 100644 index 0000000..0a41b35 --- /dev/null +++ b/charts/terraform-backend-mongodb/ci/ci-values.yaml @@ -0,0 +1,5 @@ +# The bundled MongoDB, so that the server reaches a database and its probes pass. +mongodb: + enabled: true + auth: + rootPassword: ci-only-password diff --git a/charts/terraform-backend-mongodb/templates/_helpers.tpl b/charts/terraform-backend-mongodb/templates/_helpers.tpl index 2c25f0f..e948dba 100644 --- a/charts/terraform-backend-mongodb/templates/_helpers.tpl +++ b/charts/terraform-backend-mongodb/templates/_helpers.tpl @@ -34,15 +34,31 @@ Call with a dict: (dict "app" . "root" $) where "." is the current webapi values {{/* MongoDB connection string. webapi.db.connectionString wins; otherwise, if the bundled mongodb subchart is enabled, derived from -its default standalone Service name ("-mongodb", per the alias in Chart.yaml and the -Bitnami chart's own naming convention) and mongodb.auth.rootPassword. +its standalone Service name and mongodb.auth.rootPassword. Only called from deployment.yaml when webapi.db.connectionStringSecretKeyRef is not set. */}} {{- define "terraform-backend-mongodb.mongoConnectionString" -}} {{- if .Values.webapi.db.connectionString -}} {{- .Values.webapi.db.connectionString -}} {{- else if .Values.mongodb.enabled -}} -{{- printf "mongodb://root:%s@%s-mongodb:27017/?authSource=admin" .Values.mongodb.auth.rootPassword .Release.Name -}} +{{- printf "mongodb://root:%s@%s:27017/?authSource=admin" .Values.mongodb.auth.rootPassword (include "terraform-backend-mongodb.mongoServiceName" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Service name of the bundled mongodb subchart, following the Bitnami common.names.fullname rule: +a release name that already contains the chart name is used alone, so the release "terraform-backend-mongodb" gets the Service "terraform-backend-mongodb". +*/}} +{{- define "terraform-backend-mongodb.mongoServiceName" -}} +{{- if .Values.mongodb.fullnameOverride -}} +{{- .Values.mongodb.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "mongodb" .Values.mongodb.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} {{- end -}} {{- end -}} diff --git a/charts/terraform-backend-mongodb/templates/deployment.yaml b/charts/terraform-backend-mongodb/templates/deployment.yaml index 33d1bcd..f87388a 100644 --- a/charts/terraform-backend-mongodb/templates/deployment.yaml +++ b/charts/terraform-backend-mongodb/templates/deployment.yaml @@ -55,9 +55,9 @@ spec: - name: http containerPort: {{ .containerPort }} protocol: TCP + # The health endpoint pings MongoDB, so a database outage would restart the server rather than take it out of the Service. livenessProbe: - httpGet: - path: {{ .healthEndpoint }} + tcpSocket: port: http readinessProbe: httpGet: diff --git a/charts/terraform-backend-mongodb/values.yaml b/charts/terraform-backend-mongodb/values.yaml index 7721af6..5bada79 100644 --- a/charts/terraform-backend-mongodb/values.yaml +++ b/charts/terraform-backend-mongodb/values.yaml @@ -4,7 +4,7 @@ webapi: name: tfbackend image: docker.io/devprofr/terraform-backend-mongodb # images are hosted on DockerHub: https://hub.docker.com/r/devprofr/terraform-backend-mongodb/tags - tag: "1.3.0" # pins to Chart.yaml appVersion - avoid floating tags (e.g. latest) in Production + tag: "1.3.36770971345" # application version 1.3.0 (Chart.yaml appVersion), built by its pipeline run - avoid floating tags (e.g. latest) in Production replicaCount: 1 port: 80 containerPort: 8080 From 8352f5e009d0777c064a44a1d14601e04998125c Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 20 Oct 2026 00:39:57 +0200 Subject: [PATCH 14/33] Install keeptrack, todoblazor, grafana-stack and nfs-ganesha on kind in CI keeptrack and todoblazor get the bundled MongoDB under a fixed name, so their probes pass. keeptrack is installed without its web app, which needs a Firebase service account key. grafana-stack runs Loki as a single binary, which a single node can schedule. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FvUmuFc3tsqikAZ8gPa83n --- .github/workflows/ci.yml | 6 +----- charts/grafana-stack/Chart.yaml | 2 +- charts/grafana-stack/ci/ci-values.yaml | 26 ++++++++++++++++++++++++++ charts/keeptrack/Chart.yaml | 2 +- charts/keeptrack/ci/ci-values.yaml | 12 ++++++++++++ charts/todoblazor/Chart.yaml | 2 +- charts/todoblazor/ci/ci-values.yaml | 9 +++++++++ 7 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 charts/grafana-stack/ci/ci-values.yaml create mode 100644 charts/keeptrack/ci/ci-values.yaml create mode 100644 charts/todoblazor/ci/ci-values.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9786d9..7e10552 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -136,16 +136,12 @@ jobs: # Custom resources only, whose CRDs a bare cluster lacks: capi-templates, gitops-bootstrap, letsencrypt, rancher-cluster-templates. # Cloud providers and storage drivers: azure-storage, outscale. # Private images, which the cluster cannot pull: fallstar, liveship, sidelab, trackboard. - # Applications failing their probes without the services they are configured for: keeptrack, todoblazor. # Default images no longer published: wordpress. - # Not tried on kind yet: grafana-stack, nfs-ganesha. EXCLUDED_CHARTS: >- capi-templates,gitops-bootstrap,letsencrypt,rancher-cluster-templates, azure-storage,outscale, fallstar,liveship,sidelab,trackboard, - keeptrack,todoblazor, - wordpress, - grafana-stack,nfs-ganesha + wordpress run: | ct install ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }} \ --excluded-charts "${EXCLUDED_CHARTS// /}" --helm-extra-args "--timeout 300s" diff --git a/charts/grafana-stack/Chart.yaml b/charts/grafana-stack/Chart.yaml index 72cf62d..0ff8bc0 100644 --- a/charts/grafana-stack/Chart.yaml +++ b/charts/grafana-stack/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: grafana-stack description: Helm chart for managing Grafana Stack type: application -version: "0.2.2" +version: "0.2.3" appVersion: "10.3.1" dependencies: - name: grafana diff --git a/charts/grafana-stack/ci/ci-values.yaml b/charts/grafana-stack/ci/ci-values.yaml new file mode 100644 index 0000000..bc04844 --- /dev/null +++ b/charts/grafana-stack/ci/ci-values.yaml @@ -0,0 +1,26 @@ +# Loki as a single binary on the filesystem, since its scalable mode spreads three replicas of each target over distinct nodes and needs object storage. +loki: + loki: + auth_enabled: false + commonConfig: + replication_factor: 1 + storage: + type: filesystem + singleBinary: + replicas: 1 + write: + replicas: 0 + read: + replicas: 0 + backend: + replicas: 0 + # The self-monitoring and its test rely on the Grafana Agent operator, which a bare cluster lacks. + monitoring: + selfMonitoring: + enabled: false + grafanaAgent: + installOperator: false + lokiCanary: + enabled: false + test: + enabled: false diff --git a/charts/keeptrack/Chart.yaml b/charts/keeptrack/Chart.yaml index 83a044d..898ff8b 100644 --- a/charts/keeptrack/Chart.yaml +++ b/charts/keeptrack/Chart.yaml @@ -2,7 +2,7 @@ name: keeptrack description: Helm chart for Keeptrack type: application -version: 0.2.6 +version: 0.2.7 appVersion: "2.4.1" home: https://github.com/devpro/keeptrack sources: diff --git a/charts/keeptrack/ci/ci-values.yaml b/charts/keeptrack/ci/ci-values.yaml new file mode 100644 index 0000000..906a11a --- /dev/null +++ b/charts/keeptrack/ci/ci-values.yaml @@ -0,0 +1,12 @@ +# The bundled MongoDB under a fixed name, so that the connection string does not depend on the release name. +webapi: + db: + connectionString: mongodb://root:ci-only-password@ci-mongodb:27017/?authSource=admin +mongodb: + enabled: true + fullnameOverride: ci-mongodb + auth: + rootPassword: ci-only-password +# The web app parses a Firebase service account private key at startup, and no key belongs in the repository. +blazorapp: + enabled: false diff --git a/charts/todoblazor/Chart.yaml b/charts/todoblazor/Chart.yaml index 286a625..a150697 100644 --- a/charts/todoblazor/Chart.yaml +++ b/charts/todoblazor/Chart.yaml @@ -2,7 +2,7 @@ name: todoblazor description: Helm chart for Todo Blazor web application type: application -version: 0.1.3 +version: 0.1.4 appVersion: "1.0.0" dependencies: - name: mongodb diff --git a/charts/todoblazor/ci/ci-values.yaml b/charts/todoblazor/ci/ci-values.yaml new file mode 100644 index 0000000..0407340 --- /dev/null +++ b/charts/todoblazor/ci/ci-values.yaml @@ -0,0 +1,9 @@ +# The bundled MongoDB under a fixed name, so that the connection string does not depend on the release name. +webapp: + db: + connectionString: mongodb://root:ci-only-password@ci-mongodb:27017/?authSource=admin +mongodb: + enabled: true + fullnameOverride: ci-mongodb + auth: + rootPassword: ci-only-password From e1dcddf25e746f2a62bba463d2c952c0ad2140ef Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 20 Oct 2026 01:02:37 +0200 Subject: [PATCH 15/33] Install Helm with the action of github-workflow-parts rather than azure/setup-helm Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 4 ++-- .github/workflows/pkg.yml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e10552..aefc108 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Build with VitePress run: npm run docs:build - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@defde40cfcaa2dc67c1cc47a27d20605f76d74b0 + uses: devpro/github-workflow-parts/actions/helm/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc - name: Install kube-linter env: KUBE_LINTER_VERSION: "0.8.3" @@ -56,7 +56,7 @@ jobs: - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@defde40cfcaa2dc67c1cc47a27d20605f76d74b0 + uses: devpro/github-workflow-parts/actions/chart-testing/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index df4556d..1bfeb03 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -53,9 +53,7 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 - with: - version: latest + uses: devpro/github-workflow-parts/actions/helm/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: Host charts repository on GitHub Pages From e837cc9b34502abf460fd3e3bbd241aaffbd2b68 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 22:48:56 +0200 Subject: [PATCH 16/33] Check the docs and the charts in jobs of their own, with the Pages workflow and the Kubernetes setup actions of github-workflow-parts The docs build by the convention of the Pages workflow, on Node.js 24, with npm ci and the new build script. kube-linter, kubeconform, helm-unittest, kind and kubectl come from actions checking each download against its SHA256, rather than installers written in the job. The default branch reaches the scripts through the environment rather than pasted into them, and the publication pins its Go and drops a Node.js no step used. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .github/workflows/ci.yml | 106 +++++++++++--------------------------- .github/workflows/pkg.yml | 27 +++++----- .istarci.yml | 2 +- package.json | 1 + 4 files changed, 46 insertions(+), 90 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aefc108..e40d258 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,114 +21,70 @@ env: CHARTS_DIRECTORIES_ARG: "--chart-dirs charts" jobs: - build: + docs: + name: Docs + uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + with: + # VitePress writes the site the GitLab pipeline publishes, which this job only builds. + deploy: false + job-name: Docs + site-path: public + workflow-parts-version: 3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + + charts: + name: Charts runs-on: ubuntu-latest + timeout-minutes: 60 + env: + # Read from the environment rather than pasted into the scripts by GitHub. + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} steps: - name: Checkout repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # note: important to have chart-testing work (see https://github.com/helm/chart-testing/issues/186) fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: npm - - name: Install Node.js dependencies - run: npm install - - name: Build with VitePress - run: npm run docs:build - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc + uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Install kube-linter - env: - KUBE_LINTER_VERSION: "0.8.3" - KUBE_LINTER_SHA256: "1a6d8419b11971372971fdbc22682b684ebfb7cf1c39591662d1b6ca736c41df" - run: | - curl -sSLo kube-linter.tar.gz "https://github.com/stackrox/kube-linter/releases/download/v${KUBE_LINTER_VERSION}/kube-linter-linux.tar.gz" - echo "${KUBE_LINTER_SHA256} kube-linter.tar.gz" | sha256sum -c - - tar -xzf kube-linter.tar.gz kube-linter - # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. - mkdir -p "${RUNNER_TEMP}/bin" - mv kube-linter "${RUNNER_TEMP}/bin/kube-linter" - echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" - rm kube-linter.tar.gz + uses: devpro/github-workflow-parts/actions/kube-linter/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc + uses: devpro/github-workflow-parts/actions/chart-testing/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts id: list-changed run: | - changed=$(ct list-changed ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }}) + changed=$(ct list-changed ${CHARTS_DIRECTORIES_ARG} --target-branch "$DEFAULT_BRANCH") charts=$(echo "$changed" | tr '\n' ' ' | xargs) if [[ -n "$changed" ]]; then - echo "changed=true" >> $GITHUB_OUTPUT - echo "changed_charts=$charts" >> $GITHUB_OUTPUT + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "changed_charts=$charts" >> "$GITHUB_OUTPUT" fi - name: Lint charts if: steps.list-changed.outputs.changed == 'true' - run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch ${{ github.event.repository.default_branch }} + run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch "$DEFAULT_BRANCH" - name: Install kubeconform - env: - KUBECONFORM_VERSION: "0.8.0" - KUBECONFORM_SHA256: "9bc2bffbf71f261128533edaf912153948b7ff238f9a531ae6d34466ec287883" - run: | - curl -sSLo kubeconform.tar.gz "https://github.com/yannh/kubeconform/releases/download/v${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" - echo "${KUBECONFORM_SHA256} kubeconform.tar.gz" | sha256sum -c - - tar -xzf kubeconform.tar.gz kubeconform - mkdir -p "${RUNNER_TEMP}/bin" - mv kubeconform "${RUNNER_TEMP}/bin/kubeconform" - echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" - rm kubeconform.tar.gz + uses: devpro/github-workflow-parts/actions/kubeconform/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Validate manifests against the Kubernetes schemas run: ./scripts/validate_charts.sh # The standalone binary of the helm-unittest plugin, so no plugin is installed into Helm. - name: Install helm-unittest - env: - HELM_UNITTEST_VERSION: "1.2.1" - HELM_UNITTEST_SHA256: "48e86ecc1467b009630e5954375e5d8494139f73b466df5fe8c04fa45b634939" - run: | - curl -sSLo helm-unittest.tgz "https://github.com/helm-unittest/helm-unittest/releases/download/v${HELM_UNITTEST_VERSION}/helm-unittest-linux-amd64-${HELM_UNITTEST_VERSION}.tgz" - echo "${HELM_UNITTEST_SHA256} helm-unittest.tgz" | sha256sum -c - - tar -xzf helm-unittest.tgz untt-linux-amd64 - mkdir -p "${RUNNER_TEMP}/bin" - mv untt-linux-amd64 "${RUNNER_TEMP}/bin/untt" - echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" - rm helm-unittest.tgz + uses: devpro/github-workflow-parts/actions/helm-unittest/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Run chart unit tests run: | charts=$(dirname charts/*/tests | sort -u) untt $charts - # TODO: Enable when https://github.com/stackrox/kube-linter/issues/575 is fixed - # - name: Scan yamls - # id: kube-lint-scan - # uses: stackrox/kube-linter-action@v1 - # with: - # directory: charts - # config: .kube-linter.yaml - # kind and kubectl are installed by hand, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. + # kind and kubectl are installed by an action of the workflow parts, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. - name: Install kind and kubectl if: steps.list-changed.outputs.changed == 'true' - env: - KIND_VERSION: "0.33.0" - KIND_SHA256: "aee6151561422756b764a4ae28e7f44cda5af5a9eead3cc9985112b1de8d8e0d" - KUBECTL_VERSION: "1.36.4" - KUBECTL_SHA256: "8b8f088da2dab964f853b38464033b1be15ede2839eca751482357c45abdd05a" - run: | - mkdir -p "${RUNNER_TEMP}/bin" - curl -sSLo "${RUNNER_TEMP}/bin/kind" "https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-linux-amd64" - echo "${KIND_SHA256} ${RUNNER_TEMP}/bin/kind" | sha256sum -c - - curl -sSLo "${RUNNER_TEMP}/bin/kubectl" "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl" - echo "${KUBECTL_SHA256} ${RUNNER_TEMP}/bin/kubectl" | sha256sum -c - - chmod +x "${RUNNER_TEMP}/bin/kind" "${RUNNER_TEMP}/bin/kubectl" - echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + uses: devpro/github-workflow-parts/actions/kind/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. - name: Create kind cluster if: steps.list-changed.outputs.changed == 'true' - run: kind create cluster --name ct-${{ github.run_id }} --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s + run: kind create cluster --name "ct-${GITHUB_RUN_ID}" --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s - name: Install charts if: steps.list-changed.outputs.changed == 'true' env: @@ -143,8 +99,8 @@ jobs: fallstar,liveship,sidelab,trackboard, wordpress run: | - ct install ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }} \ + ct install ${CHARTS_DIRECTORIES_ARG} --target-branch "$DEFAULT_BRANCH" \ --excluded-charts "${EXCLUDED_CHARTS// /}" --helm-extra-args "--timeout 300s" - name: Delete kind cluster if: always() && steps.list-changed.outputs.changed == 'true' - run: kind delete cluster --name ct-${{ github.run_id }} + run: kind delete cluster --name "ct-${GITHUB_RUN_ID}" diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 1bfeb03..d2408a1 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -29,14 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout git repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: npm # ref. https://vitepress.dev/guide/deploy#github-pages # - name: Install Node.js dependencies # run: npm install @@ -53,7 +48,7 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@c6d06a61a377a41dc192ce58f08a0b5100d9a2bc + uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: Host charts repository on GitHub Pages @@ -63,23 +58,27 @@ jobs: # important: it needs to be done in the same pipeline as another pages workflow won't be triggered by the pust on the gh-pages branch # (tried with a PAT and several options but didn't work) - name: Checkout gh-pages branch - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: gh-pages # - uses: actions/download-artifact@v4 # with: # name: docs # path: docs + # A Go of its own rather than the one of the runner image, which changes with it. - name: Setup Go environment - uses: actions/setup-go@v7 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: "1.27.3" + cache: false - name: Generate index.html run: | go install github.com/halkeye/helm-repo-html@v0.2.1 - /home/runner/go/bin/helm-repo-html + "$(go env GOPATH)/bin/helm-repo-html" - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 + uses: actions/jekyll-build-pages@44a6e6beabd48582f863aeeb6cb2151cc1716697 # v1.0.13 with: source: ./ destination: ./_site @@ -90,7 +89,7 @@ jobs: # sudo mkdir -p ./_site/docs # sudo cp -r docs/* ./_site/docs/ - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 deploy: runs-on: ubuntu-latest needs: build @@ -100,4 +99,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 \ No newline at end of file diff --git a/.istarci.yml b/.istarci.yml index 4813205..d40c3ff 100644 --- a/.istarci.yml +++ b/.istarci.yml @@ -1,3 +1,3 @@ runner: # ct install starts a kind cluster through the host Docker daemon, as on a hosted runner. - docker: ["build"] + docker: ["charts"] diff --git a/package.json b/package.json index 6d3ff85..4ef5b14 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "vitepress-sidebar": "^1.33.0" }, "scripts": { + "build": "vitepress build docs", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs" From 434b356f0ac0542a1f2815c1a8d5a95ac7542eff Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 22:48:56 +0200 Subject: [PATCH 17/33] Save the text files outside the charts as UTF-8 without a byte order mark, which some tools refuse to read Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .editorconfig | 2 +- .gitignore | 2 +- .kube-linter.yaml | 2 +- .markdownlint-cli2.yaml | 2 +- AGENTS.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 2 +- docs/application-guides/developer-platforms/drupal.md | 2 +- docs/custom-charts/index.md | 2 +- docs/custom-charts/keeptrack.md | 2 +- docs/custom-charts/terraform-backend-mongodb.md | 2 +- .../devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml | 2 +- .../fleet/repo-content/above-crds/cert-manager.crds.yaml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.editorconfig b/.editorconfig index b7680de..37ef933 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -root = true +root = true [*] charset = utf-8-bom diff --git a/.gitignore b/.gitignore index 3b53792..5caf2f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# local downloaded chart compressed files +# local downloaded chart compressed files /**/charts/*.tgz # local temporary files diff --git a/.kube-linter.yaml b/.kube-linter.yaml index bd59dc1..fc03d30 100644 --- a/.kube-linter.yaml +++ b/.kube-linter.yaml @@ -1,4 +1,4 @@ -checks: +checks: addAllBuiltIn: true # ref. https://docs.kubelinter.io/#/generated/checks exclude: diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 690956f..5dd1d21 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,4 +1,4 @@ -gitignore: true +gitignore: true ignores: - "**/node_modules/**" - "CLAUDE.md" diff --git a/AGENTS.md b/AGENTS.md index d853484..f039b54 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# helm-charts: agent context +# helm-charts: agent context ## What this is diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6410584..dbe5947 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing +# Contributing ## File organization diff --git a/README.md b/README.md index 7608036..1532a2b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Devpro Helm Charts +# Devpro Helm Charts [![CI](https://github.com/devpro/helm-charts/actions/workflows/ci.yml/badge.svg)](https://github.com/devpro/helm-charts/actions/workflows/ci.yml) [![PKG](https://github.com/devpro/helm-charts/actions/workflows/pkg.yml/badge.svg)](https://github.com/devpro/helm-charts/actions/workflows/pkg.yml) diff --git a/docs/application-guides/developer-platforms/drupal.md b/docs/application-guides/developer-platforms/drupal.md index 8fa8467..93ab9a3 100644 --- a/docs/application-guides/developer-platforms/drupal.md +++ b/docs/application-guides/developer-platforms/drupal.md @@ -1,4 +1,4 @@ -# Drupal +# Drupal ## Stable repository diff --git a/docs/custom-charts/index.md b/docs/custom-charts/index.md index bea2909..7cd2b89 100644 --- a/docs/custom-charts/index.md +++ b/docs/custom-charts/index.md @@ -1,4 +1,4 @@ ---- +--- order: 2 --- diff --git a/docs/custom-charts/keeptrack.md b/docs/custom-charts/keeptrack.md index cfa61e1..5460e31 100644 --- a/docs/custom-charts/keeptrack.md +++ b/docs/custom-charts/keeptrack.md @@ -1,4 +1,4 @@ -# Keeptrack +# Keeptrack Let's see how to deploy [Keeptrack](https://github.com/devpro/keeptrack) on a Kubernetes cluster. diff --git a/docs/custom-charts/terraform-backend-mongodb.md b/docs/custom-charts/terraform-backend-mongodb.md index e760889..78b2e9a 100644 --- a/docs/custom-charts/terraform-backend-mongodb.md +++ b/docs/custom-charts/terraform-backend-mongodb.md @@ -1,4 +1,4 @@ -# Terraform Backend MongoDB +# Terraform Backend MongoDB Let's see how to deploy [Terraform Backend MongoDB](https://github.com/devpro/terraform-backend-mongodb) on a Kubernetes cluster. diff --git a/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml b/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml index 86d7e1f..9265a43 100644 --- a/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml +++ b/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 The cert-manager Authors. +# Copyright 2022 The cert-manager Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml b/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml index 86d7e1f..9265a43 100644 --- a/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml +++ b/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 The cert-manager Authors. +# Copyright 2022 The cert-manager Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 2de298418108a925ec3c75d647c9020e1e07f45f Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 22:49:19 +0200 Subject: [PATCH 18/33] Save the chart files as UTF-8 without a byte order mark, with a patch version of each chart it changes cow-demo 0.2.4, drupal 1.0.2, dvwa 1.0.3, gitops-bootstrap 0.1.1, keeptrack 0.2.8, nextportal 1.0.1, sidelab 0.1.8, terraform-backend-mongodb 0.3.2, tinyfilemanager 1.0.3, todoblazor 0.1.5, trackboard 0.1.1 and webgoat 1.0.1. Two files of todoblazor held only the mark, and are now empty. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- charts/cow-demo/Chart.yaml | 4 ++-- charts/cow-demo/values.yaml | 2 +- charts/drupal/.helmignore | 2 +- charts/drupal/CONTRIBUTING.md | 2 +- charts/drupal/Chart.yaml | 4 ++-- charts/drupal/README.md | 2 +- charts/drupal/templates/NOTES.txt | 2 +- charts/drupal/templates/deployment.yaml | 2 +- charts/drupal/templates/ingress.yaml | 2 +- charts/drupal/templates/pvc.yaml | 2 +- charts/drupal/templates/service.yaml | 2 +- charts/drupal/values.yaml | 2 +- charts/dvwa/.helmignore | 2 +- charts/dvwa/CONTRIBUTING.md | 2 +- charts/dvwa/Chart.yaml | 4 ++-- charts/dvwa/README.md | 2 +- charts/dvwa/templates/deployment.yaml | 2 +- charts/dvwa/templates/ingress.yaml | 2 +- charts/dvwa/values.yaml | 2 +- charts/gitops-bootstrap/Chart.yaml | 4 ++-- charts/gitops-bootstrap/README.md | 2 +- charts/gitops-bootstrap/templates/argocd-application.yaml | 2 +- charts/gitops-bootstrap/values.yaml | 2 +- charts/keeptrack/CONTRIBUTING.md | 2 +- charts/keeptrack/Chart.yaml | 4 ++-- charts/keeptrack/README.md | 2 +- charts/keeptrack/templates/_helpers.tpl | 2 +- charts/keeptrack/templates/deployment.yaml | 2 +- charts/keeptrack/templates/ingress.yaml | 2 +- charts/keeptrack/templates/service.yaml | 2 +- charts/keeptrack/templates/serviceaccount.yaml | 2 +- charts/keeptrack/values.yaml | 2 +- charts/nextportal/CONTRIBUTING.md | 2 +- charts/nextportal/Chart.yaml | 4 ++-- charts/nextportal/README.md | 2 +- charts/nextportal/templates/NOTES.txt | 2 +- charts/nextportal/templates/_helpers.tpl | 2 +- charts/nextportal/templates/deployment.yaml | 2 +- charts/nextportal/templates/hpa.yaml | 2 +- charts/nextportal/templates/ingress.yaml | 2 +- charts/nextportal/templates/networkpolicy.yaml | 2 +- charts/nextportal/templates/service.yaml | 2 +- charts/nextportal/values.yaml | 2 +- charts/sidelab/.helmignore | 2 +- charts/sidelab/Chart.yaml | 4 ++-- charts/sidelab/README.md | 2 +- charts/sidelab/ci/ci-values.yaml | 2 +- charts/sidelab/templates/NOTES.txt | 2 +- charts/sidelab/templates/_helpers.tpl | 2 +- charts/sidelab/templates/configmap.yaml | 2 +- charts/sidelab/templates/deployment.yaml | 2 +- charts/sidelab/templates/networkpolicy.yaml | 2 +- charts/sidelab/templates/rbac.yaml | 2 +- charts/sidelab/templates/secret.yaml | 2 +- charts/sidelab/values.yaml | 2 +- charts/terraform-backend-mongodb/Chart.yaml | 4 ++-- charts/tinyfilemanager/CONTRIBUTING.md | 2 +- charts/tinyfilemanager/Chart.yaml | 4 ++-- charts/tinyfilemanager/README.md | 2 +- charts/tinyfilemanager/templates/NOTES.txt | 2 +- charts/tinyfilemanager/templates/_helpers.tpl | 2 +- charts/tinyfilemanager/templates/deployment.yaml | 2 +- charts/tinyfilemanager/templates/hpa.yaml | 2 +- charts/tinyfilemanager/templates/ingress.yaml | 2 +- charts/tinyfilemanager/templates/networkpolicy.yaml | 2 +- charts/tinyfilemanager/templates/service.yaml | 2 +- charts/tinyfilemanager/values.yaml | 2 +- charts/todoblazor/.helmignore | 2 +- charts/todoblazor/CONTRIBUTING.md | 2 +- charts/todoblazor/Chart.yaml | 4 ++-- charts/todoblazor/README.md | 2 +- charts/todoblazor/templates/NOTES.txt | 1 - charts/todoblazor/templates/_helpers.tpl | 1 - charts/todoblazor/templates/clusterrolebinding.yaml | 2 +- charts/todoblazor/templates/deployment.yaml | 2 +- charts/todoblazor/templates/ingress.yaml | 2 +- charts/todoblazor/templates/service.yaml | 2 +- charts/todoblazor/templates/serviceaccount.yaml | 2 +- charts/todoblazor/values.yaml | 2 +- charts/trackboard/Chart.yaml | 4 ++-- charts/trackboard/README.md | 2 +- charts/trackboard/templates/NOTES.txt | 2 +- charts/trackboard/templates/_helpers.tpl | 2 +- charts/trackboard/templates/deployment.yaml | 2 +- charts/trackboard/templates/ingress.yaml | 2 +- charts/trackboard/templates/networkpolicy.yaml | 2 +- charts/trackboard/templates/pvc.yaml | 2 +- charts/trackboard/templates/secret.yaml | 2 +- charts/trackboard/templates/service.yaml | 2 +- charts/trackboard/values.yaml | 2 +- charts/webgoat/CONTRIBUTING.md | 2 +- charts/webgoat/Chart.yaml | 2 +- charts/webgoat/README.md | 2 +- charts/webgoat/templates/NOTES.txt | 2 +- charts/webgoat/templates/deployment.yaml | 2 +- charts/webgoat/templates/ingress.yaml | 2 +- charts/webgoat/values.yaml | 2 +- 97 files changed, 106 insertions(+), 108 deletions(-) diff --git a/charts/cow-demo/Chart.yaml b/charts/cow-demo/Chart.yaml index 279290a..1106c88 100644 --- a/charts/cow-demo/Chart.yaml +++ b/charts/cow-demo/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: cow-demo description: Helm chart for Cow Demo Application type: application -version: 0.2.3 +version: 0.2.4 appVersion: "2.0.0" maintainers: - name: devpro diff --git a/charts/cow-demo/values.yaml b/charts/cow-demo/values.yaml index 8875120..0ffeb43 100644 --- a/charts/cow-demo/values.yaml +++ b/charts/cow-demo/values.yaml @@ -1,4 +1,4 @@ -name: cow-demo +name: cow-demo image: devprofr/cow-demo # images are hosted on DockerHub: https://hub.docker.com/r/devprofr/cow-demo/tags tag: 1.0.23271988100 diff --git a/charts/drupal/.helmignore b/charts/drupal/.helmignore index 1b59443..4f3ddb6 100644 --- a/charts/drupal/.helmignore +++ b/charts/drupal/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git .gitignore README.md diff --git a/charts/drupal/CONTRIBUTING.md b/charts/drupal/CONTRIBUTING.md index 3d476d7..e91679d 100644 --- a/charts/drupal/CONTRIBUTING.md +++ b/charts/drupal/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/drupal/Chart.yaml b/charts/drupal/Chart.yaml index 4ced60f..962c415 100644 --- a/charts/drupal/Chart.yaml +++ b/charts/drupal/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: drupal description: Drupal Web Application - for container security workshops type: application -version: 1.0.1 +version: 1.0.2 appVersion: "8.5.0" keywords: - security diff --git a/charts/drupal/README.md b/charts/drupal/README.md index ac1eec5..51b34a9 100644 --- a/charts/drupal/README.md +++ b/charts/drupal/README.md @@ -1,4 +1,4 @@ -# Drupal Helm Chart +# Drupal Helm Chart Helm chart for Drupal — designed for container security workshops. diff --git a/charts/drupal/templates/NOTES.txt b/charts/drupal/templates/NOTES.txt index 867f87e..5f6039a 100644 --- a/charts/drupal/templates/NOTES.txt +++ b/charts/drupal/templates/NOTES.txt @@ -1,3 +1,3 @@ -🎯 Drupal deployed! +🎯 Drupal deployed! ⚠️ FOR WORKSHOP USE ONLY — never expose to the internet long-term! diff --git a/charts/drupal/templates/deployment.yaml b/charts/drupal/templates/deployment.yaml index 465825c..29efe8e 100644 --- a/charts/drupal/templates/deployment.yaml +++ b/charts/drupal/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: drupal diff --git a/charts/drupal/templates/ingress.yaml b/charts/drupal/templates/ingress.yaml index 164ba3a..791f01b 100644 --- a/charts/drupal/templates/ingress.yaml +++ b/charts/drupal/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/drupal/templates/pvc.yaml b/charts/drupal/templates/pvc.yaml index bfac030..9b3d960 100644 --- a/charts/drupal/templates/pvc.yaml +++ b/charts/drupal/templates/pvc.yaml @@ -1,4 +1,4 @@ -{{- if .Values.persistence.enabled }} +{{- if .Values.persistence.enabled }} apiVersion: v1 kind: PersistentVolumeClaim metadata: diff --git a/charts/drupal/templates/service.yaml b/charts/drupal/templates/service.yaml index cbc0f20..b5b99d5 100644 --- a/charts/drupal/templates/service.yaml +++ b/charts/drupal/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: drupal diff --git a/charts/drupal/values.yaml b/charts/drupal/values.yaml index a5a2267..a2630ce 100644 --- a/charts/drupal/values.yaml +++ b/charts/drupal/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 image: vulhub/drupal tag: 8.5.0 diff --git a/charts/dvwa/.helmignore b/charts/dvwa/.helmignore index 1b59443..4f3ddb6 100644 --- a/charts/dvwa/.helmignore +++ b/charts/dvwa/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git .gitignore README.md diff --git a/charts/dvwa/CONTRIBUTING.md b/charts/dvwa/CONTRIBUTING.md index 46b5eb1..d7efddc 100644 --- a/charts/dvwa/CONTRIBUTING.md +++ b/charts/dvwa/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/dvwa/Chart.yaml b/charts/dvwa/Chart.yaml index 4b14c14..a404967 100644 --- a/charts/dvwa/Chart.yaml +++ b/charts/dvwa/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: dvwa description: Damn Vulnerable Web Application - for container security workshops type: application -version: 1.0.2 +version: 1.0.3 appVersion: "1.0.0" keywords: - security diff --git a/charts/dvwa/README.md b/charts/dvwa/README.md index 114e2cb..1831505 100644 --- a/charts/dvwa/README.md +++ b/charts/dvwa/README.md @@ -1,4 +1,4 @@ -# DVWA Helm Chart +# DVWA Helm Chart Helm chart for [Damn Vulnerable Web Application](https://github.com/digininja/DVWA) — designed for container security workshops. diff --git a/charts/dvwa/templates/deployment.yaml b/charts/dvwa/templates/deployment.yaml index 0377a9e..5789ce1 100644 --- a/charts/dvwa/templates/deployment.yaml +++ b/charts/dvwa/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "dvwa.fullname" . }} diff --git a/charts/dvwa/templates/ingress.yaml b/charts/dvwa/templates/ingress.yaml index 4e18eeb..5bcfa24 100644 --- a/charts/dvwa/templates/ingress.yaml +++ b/charts/dvwa/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} {{- if not .Values.ingress.domain }} {{- fail "ingress.domain is required when ingress.enabled=true. Set it with --set ingress.domain=dvwa.yourdomain.com" }} {{- end }} diff --git a/charts/dvwa/values.yaml b/charts/dvwa/values.yaml index 6c4ca19..4d6e584 100644 --- a/charts/dvwa/values.yaml +++ b/charts/dvwa/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 image: repository: ghcr.io/digininja/dvwa diff --git a/charts/gitops-bootstrap/Chart.yaml b/charts/gitops-bootstrap/Chart.yaml index 2f20eda..74d5d2b 100644 --- a/charts/gitops-bootstrap/Chart.yaml +++ b/charts/gitops-bootstrap/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: gitops-bootstrap description: Helm chart for managing bootstraping GitOps type: application -version: "0.1.0" +version: 0.1.1 appVersion: "1.0.0" maintainers: - name: devpro diff --git a/charts/gitops-bootstrap/README.md b/charts/gitops-bootstrap/README.md index 32ee484..0497da5 100644 --- a/charts/gitops-bootstrap/README.md +++ b/charts/gitops-bootstrap/README.md @@ -1,4 +1,4 @@ -# GitOps Bootstrap +# GitOps Bootstrap This Helm chart will install the Kubernetes objects to bootstrap GitOps in an infrastructure. Originally created to bypass issues with Kubernetes Terraform provider with manifests at init. diff --git a/charts/gitops-bootstrap/templates/argocd-application.yaml b/charts/gitops-bootstrap/templates/argocd-application.yaml index dcafe50..172ad00 100644 --- a/charts/gitops-bootstrap/templates/argocd-application.yaml +++ b/charts/gitops-bootstrap/templates/argocd-application.yaml @@ -1,4 +1,4 @@ -{{ range .Values.argocd.applications }} +{{ range .Values.argocd.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/charts/gitops-bootstrap/values.yaml b/charts/gitops-bootstrap/values.yaml index 47faee5..78d191b 100644 --- a/charts/gitops-bootstrap/values.yaml +++ b/charts/gitops-bootstrap/values.yaml @@ -1,4 +1,4 @@ -argocd: +argocd: applications: [] # - name: root-apps # namespace: argocd diff --git a/charts/keeptrack/CONTRIBUTING.md b/charts/keeptrack/CONTRIBUTING.md index 5af6d3f..5bf9634 100644 --- a/charts/keeptrack/CONTRIBUTING.md +++ b/charts/keeptrack/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Update chart dependencies diff --git a/charts/keeptrack/Chart.yaml b/charts/keeptrack/Chart.yaml index 898ff8b..eea457f 100644 --- a/charts/keeptrack/Chart.yaml +++ b/charts/keeptrack/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: keeptrack description: Helm chart for Keeptrack type: application -version: 0.2.7 +version: 0.2.8 appVersion: "2.4.1" home: https://github.com/devpro/keeptrack sources: diff --git a/charts/keeptrack/README.md b/charts/keeptrack/README.md index 918a95c..54bf24d 100644 --- a/charts/keeptrack/README.md +++ b/charts/keeptrack/README.md @@ -1,4 +1,4 @@ -# Helm chart for Devpro Keeptrack +# Helm chart for Devpro Keeptrack This is the official Helm chart to install [Keeptrack](https://github.com/devpro/keeptrack) on a Kubernetes cluster. diff --git a/charts/keeptrack/templates/_helpers.tpl b/charts/keeptrack/templates/_helpers.tpl index 97994f7..5f01a41 100644 --- a/charts/keeptrack/templates/_helpers.tpl +++ b/charts/keeptrack/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Recommended Kubernetes labels (https://helm.sh/docs/chart_best_practices/labels/), added on top of the app-specific `app` / `app.kubernetes.io/name` labels every template already sets directly. These are informational only: never add them to a Deployment's spec.selector.matchLabels, which is immutable and would break `helm upgrade` for existing releases. Call with a dict: (dict "app" . "root" $) where "." is the current blazorapp/webapi values entry. diff --git a/charts/keeptrack/templates/deployment.yaml b/charts/keeptrack/templates/deployment.yaml index 2f368fb..687b9be 100644 --- a/charts/keeptrack/templates/deployment.yaml +++ b/charts/keeptrack/templates/deployment.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/keeptrack/templates/ingress.yaml b/charts/keeptrack/templates/ingress.yaml index bf660e6..971291f 100644 --- a/charts/keeptrack/templates/ingress.yaml +++ b/charts/keeptrack/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if and $.Values.ingress.enabled .enabled .host -}} --- diff --git a/charts/keeptrack/templates/service.yaml b/charts/keeptrack/templates/service.yaml index cb93f70..0237a4e 100644 --- a/charts/keeptrack/templates/service.yaml +++ b/charts/keeptrack/templates/service.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/keeptrack/templates/serviceaccount.yaml b/charts/keeptrack/templates/serviceaccount.yaml index 935ffa6..4c0f030 100644 --- a/charts/keeptrack/templates/serviceaccount.yaml +++ b/charts/keeptrack/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.security.serviceAccount.create }} +{{- if .Values.security.serviceAccount.create }} {{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} apiVersion: v1 diff --git a/charts/keeptrack/values.yaml b/charts/keeptrack/values.yaml index 40ee318..8d2d94e 100644 --- a/charts/keeptrack/values.yaml +++ b/charts/keeptrack/values.yaml @@ -1,4 +1,4 @@ -blazorapp: +blazorapp: enabled: true role: frontend name: keeptrack-blazorapp diff --git a/charts/nextportal/CONTRIBUTING.md b/charts/nextportal/CONTRIBUTING.md index e0613e3..7595a3a 100644 --- a/charts/nextportal/CONTRIBUTING.md +++ b/charts/nextportal/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/nextportal/Chart.yaml b/charts/nextportal/Chart.yaml index cd17079..319b29f 100644 --- a/charts/nextportal/Chart.yaml +++ b/charts/nextportal/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: nextportal description: NextPortal web application - intentionally vulnerable by default for security workshops type: application -version: 1.0.0 +version: 1.0.1 appVersion: "0.1.0" keywords: - security diff --git a/charts/nextportal/README.md b/charts/nextportal/README.md index c896379..de2e488 100644 --- a/charts/nextportal/README.md +++ b/charts/nextportal/README.md @@ -1,4 +1,4 @@ -# NextPortal Helm Chart +# NextPortal Helm Chart Helm chart for [NextPortal Web Application](https://github.com/devpro/container-images/tree/main/src/nextportal) — designed for container security workshops. diff --git a/charts/nextportal/templates/NOTES.txt b/charts/nextportal/templates/NOTES.txt index 1a7b9b5..83fd112 100644 --- a/charts/nextportal/templates/NOTES.txt +++ b/charts/nextportal/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. NextPortal web application has been deployed. +1. NextPortal web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/nextportal/templates/_helpers.tpl b/charts/nextportal/templates/_helpers.tpl index 8f1b1ac..5634107 100644 --- a/charts/nextportal/templates/_helpers.tpl +++ b/charts/nextportal/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "nextportal.name" -}} diff --git a/charts/nextportal/templates/deployment.yaml b/charts/nextportal/templates/deployment.yaml index e3c60f1..7b3406a 100644 --- a/charts/nextportal/templates/deployment.yaml +++ b/charts/nextportal/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "nextportal.fullname" . }} diff --git a/charts/nextportal/templates/hpa.yaml b/charts/nextportal/templates/hpa.yaml index f5dbebf..33bc3b2 100644 --- a/charts/nextportal/templates/hpa.yaml +++ b/charts/nextportal/templates/hpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.autoscaling.enabled }} +{{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: diff --git a/charts/nextportal/templates/ingress.yaml b/charts/nextportal/templates/ingress.yaml index 0d8be98..e17bcf2 100644 --- a/charts/nextportal/templates/ingress.yaml +++ b/charts/nextportal/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/nextportal/templates/networkpolicy.yaml b/charts/nextportal/templates/networkpolicy.yaml index 7a6c1de..5bca7d9 100644 --- a/charts/nextportal/templates/networkpolicy.yaml +++ b/charts/nextportal/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/nextportal/templates/service.yaml b/charts/nextportal/templates/service.yaml index 6283c1c..9e42d70 100644 --- a/charts/nextportal/templates/service.yaml +++ b/charts/nextportal/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "nextportal.fullname" . }} diff --git a/charts/nextportal/values.yaml b/charts/nextportal/values.yaml index 3770fdd..9242416 100644 --- a/charts/nextportal/values.yaml +++ b/charts/nextportal/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/nextportal tag: "0.1.0" pullPolicy: IfNotPresent diff --git a/charts/sidelab/.helmignore b/charts/sidelab/.helmignore index d13bc8e..517eba1 100644 --- a/charts/sidelab/.helmignore +++ b/charts/sidelab/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git/ .gitignore .helmignore diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index 8955e2c..d9d5965 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -1,10 +1,10 @@ -apiVersion: v2 +apiVersion: v2 name: sidelab description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.7 +version: 0.1.8 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/README.md b/charts/sidelab/README.md index de5541c..59f6c90 100644 --- a/charts/sidelab/README.md +++ b/charts/sidelab/README.md @@ -1,4 +1,4 @@ -# Sidelab Helm Chart +# Sidelab Helm Chart Helm chart for Sidelab, a self-hosted interactive lab platform. diff --git a/charts/sidelab/ci/ci-values.yaml b/charts/sidelab/ci/ci-values.yaml index 4ffe840..c05c07b 100644 --- a/charts/sidelab/ci/ci-values.yaml +++ b/charts/sidelab/ci/ci-values.yaml @@ -1,2 +1,2 @@ -image: +image: repository: ci-dummy-registry/app diff --git a/charts/sidelab/templates/NOTES.txt b/charts/sidelab/templates/NOTES.txt index 2ad68bd..65c9007 100644 --- a/charts/sidelab/templates/NOTES.txt +++ b/charts/sidelab/templates/NOTES.txt @@ -1,4 +1,4 @@ -🎉 Sidelab has been deployed! +🎉 Sidelab has been deployed! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Launcher URL diff --git a/charts/sidelab/templates/_helpers.tpl b/charts/sidelab/templates/_helpers.tpl index 262d504..0a9b0c0 100644 --- a/charts/sidelab/templates/_helpers.tpl +++ b/charts/sidelab/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "sidelab.name" -}} diff --git a/charts/sidelab/templates/configmap.yaml b/charts/sidelab/templates/configmap.yaml index 091f786..ada2a9b 100644 --- a/charts/sidelab/templates/configmap.yaml +++ b/charts/sidelab/templates/configmap.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: {{ include "sidelab.fullname" . }}-config diff --git a/charts/sidelab/templates/deployment.yaml b/charts/sidelab/templates/deployment.yaml index 0341d72..3c2a625 100644 --- a/charts/sidelab/templates/deployment.yaml +++ b/charts/sidelab/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "sidelab.fullname" . }} diff --git a/charts/sidelab/templates/networkpolicy.yaml b/charts/sidelab/templates/networkpolicy.yaml index 5d1b93f..d9cbaad 100644 --- a/charts/sidelab/templates/networkpolicy.yaml +++ b/charts/sidelab/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkPolicy.enabled }} +{{- if .Values.networkPolicy.enabled }} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/sidelab/templates/rbac.yaml b/charts/sidelab/templates/rbac.yaml index e595dbe..e4bac63 100644 --- a/charts/sidelab/templates/rbac.yaml +++ b/charts/sidelab/templates/rbac.yaml @@ -1,4 +1,4 @@ -{{- if .Values.rbac.create }} +{{- if .Values.rbac.create }} # Cluster-scoped because tenant namespaces are created on demand. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/charts/sidelab/templates/secret.yaml b/charts/sidelab/templates/secret.yaml index 482e2fe..b7173e6 100644 --- a/charts/sidelab/templates/secret.yaml +++ b/charts/sidelab/templates/secret.yaml @@ -1,4 +1,4 @@ -{{- include "sidelab.validateValues" . }} +{{- include "sidelab.validateValues" . }} {{- if not .Values.auth.existingSecret }} apiVersion: v1 kind: Secret diff --git a/charts/sidelab/values.yaml b/charts/sidelab/values.yaml index 3703d99..fc6aad6 100644 --- a/charts/sidelab/values.yaml +++ b/charts/sidelab/values.yaml @@ -1,4 +1,4 @@ -# -- Base domain: the dashboard defaults to sidelab. and lab sessions to *.labs.. +# -- Base domain: the dashboard defaults to sidelab. and lab sessions to *.labs.. domain: "" image: diff --git a/charts/terraform-backend-mongodb/Chart.yaml b/charts/terraform-backend-mongodb/Chart.yaml index e6c7b28..ccea1b2 100644 --- a/charts/terraform-backend-mongodb/Chart.yaml +++ b/charts/terraform-backend-mongodb/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: terraform-backend-mongodb description: Helm chart for Terraform Backend MongoDB type: application -version: 0.3.1 +version: 0.3.2 appVersion: "1.3.0" home: https://github.com/devpro/terraform-backend-mongodb sources: diff --git a/charts/tinyfilemanager/CONTRIBUTING.md b/charts/tinyfilemanager/CONTRIBUTING.md index 0c66f90..b4faea0 100644 --- a/charts/tinyfilemanager/CONTRIBUTING.md +++ b/charts/tinyfilemanager/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/tinyfilemanager/Chart.yaml b/charts/tinyfilemanager/Chart.yaml index ba12974..02b5bb2 100644 --- a/charts/tinyfilemanager/Chart.yaml +++ b/charts/tinyfilemanager/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: tinyfilemanager description: Tiny File Manager - intentionally vulnerable by default for security workshops type: application -version: 1.0.2 +version: 1.0.3 appVersion: "2.4.6" keywords: - security diff --git a/charts/tinyfilemanager/README.md b/charts/tinyfilemanager/README.md index f592dc1..1d3285d 100644 --- a/charts/tinyfilemanager/README.md +++ b/charts/tinyfilemanager/README.md @@ -1,4 +1,4 @@ -# Tiny File Manager Helm Chart +# Tiny File Manager Helm Chart Helm chart for [Tiny File Manager](https://tinyfilemanager.github.io/) — designed for container security workshops. diff --git a/charts/tinyfilemanager/templates/NOTES.txt b/charts/tinyfilemanager/templates/NOTES.txt index 75ea141..875d8cc 100644 --- a/charts/tinyfilemanager/templates/NOTES.txt +++ b/charts/tinyfilemanager/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. Tiny File Manager web application has been deployed. +1. Tiny File Manager web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/tinyfilemanager/templates/_helpers.tpl b/charts/tinyfilemanager/templates/_helpers.tpl index 09da1b5..f2c02f9 100644 --- a/charts/tinyfilemanager/templates/_helpers.tpl +++ b/charts/tinyfilemanager/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "tinyfilemanager.name" -}} diff --git a/charts/tinyfilemanager/templates/deployment.yaml b/charts/tinyfilemanager/templates/deployment.yaml index 24c0e0a..e77a7a3 100644 --- a/charts/tinyfilemanager/templates/deployment.yaml +++ b/charts/tinyfilemanager/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "tinyfilemanager.fullname" . }} diff --git a/charts/tinyfilemanager/templates/hpa.yaml b/charts/tinyfilemanager/templates/hpa.yaml index 0f1106b..0d09530 100644 --- a/charts/tinyfilemanager/templates/hpa.yaml +++ b/charts/tinyfilemanager/templates/hpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.autoscaling.enabled }} +{{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: diff --git a/charts/tinyfilemanager/templates/ingress.yaml b/charts/tinyfilemanager/templates/ingress.yaml index 5894081..f37740f 100644 --- a/charts/tinyfilemanager/templates/ingress.yaml +++ b/charts/tinyfilemanager/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/tinyfilemanager/templates/networkpolicy.yaml b/charts/tinyfilemanager/templates/networkpolicy.yaml index 4650615..2839435 100644 --- a/charts/tinyfilemanager/templates/networkpolicy.yaml +++ b/charts/tinyfilemanager/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/tinyfilemanager/templates/service.yaml b/charts/tinyfilemanager/templates/service.yaml index fd28b34..3c93b5a 100644 --- a/charts/tinyfilemanager/templates/service.yaml +++ b/charts/tinyfilemanager/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "tinyfilemanager.fullname" . }} diff --git a/charts/tinyfilemanager/values.yaml b/charts/tinyfilemanager/values.yaml index 6bef7c2..3a16f16 100644 --- a/charts/tinyfilemanager/values.yaml +++ b/charts/tinyfilemanager/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/tinyfilemanager tag: "2.4.6" pullPolicy: IfNotPresent diff --git a/charts/todoblazor/.helmignore b/charts/todoblazor/.helmignore index 59a8c7d..0e8a0eb 100644 --- a/charts/todoblazor/.helmignore +++ b/charts/todoblazor/.helmignore @@ -1,4 +1,4 @@ -# Patterns to ignore when building packages. +# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store diff --git a/charts/todoblazor/CONTRIBUTING.md b/charts/todoblazor/CONTRIBUTING.md index 13f79e5..7ee9439 100644 --- a/charts/todoblazor/CONTRIBUTING.md +++ b/charts/todoblazor/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Update chart dependencies diff --git a/charts/todoblazor/Chart.yaml b/charts/todoblazor/Chart.yaml index a150697..259bafe 100644 --- a/charts/todoblazor/Chart.yaml +++ b/charts/todoblazor/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: todoblazor description: Helm chart for Todo Blazor web application type: application -version: 0.1.4 +version: 0.1.5 appVersion: "1.0.0" dependencies: - name: mongodb diff --git a/charts/todoblazor/README.md b/charts/todoblazor/README.md index 33866d6..565b4b0 100644 --- a/charts/todoblazor/README.md +++ b/charts/todoblazor/README.md @@ -1,4 +1,4 @@ -# Helm chart for Todo Blazor +# Helm chart for Todo Blazor This Helm chart will deploy [Todo Blazor](https://github.com/devpro/todo-blazor) on a Kubernetes cluster. diff --git a/charts/todoblazor/templates/NOTES.txt b/charts/todoblazor/templates/NOTES.txt index 5f28270..e69de29 100644 --- a/charts/todoblazor/templates/NOTES.txt +++ b/charts/todoblazor/templates/NOTES.txt @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/charts/todoblazor/templates/_helpers.tpl b/charts/todoblazor/templates/_helpers.tpl index 5f28270..e69de29 100644 --- a/charts/todoblazor/templates/_helpers.tpl +++ b/charts/todoblazor/templates/_helpers.tpl @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/charts/todoblazor/templates/clusterrolebinding.yaml b/charts/todoblazor/templates/clusterrolebinding.yaml index 0ed9e73..5c6c7ef 100644 --- a/charts/todoblazor/templates/clusterrolebinding.yaml +++ b/charts/todoblazor/templates/clusterrolebinding.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.security.rbac.giveClusterAdmin .Values.security.serviceAccount.create }} +{{- if and .Values.security.rbac.giveClusterAdmin .Values.security.serviceAccount.create }} {{- $namespace := .Release.Namespace }} {{- $applications := list .Values.webapp -}} {{ range $applications }} diff --git a/charts/todoblazor/templates/deployment.yaml b/charts/todoblazor/templates/deployment.yaml index 46e152b..287a154 100644 --- a/charts/todoblazor/templates/deployment.yaml +++ b/charts/todoblazor/templates/deployment.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/todoblazor/templates/ingress.yaml b/charts/todoblazor/templates/ingress.yaml index 532e36b..84cf988 100644 --- a/charts/todoblazor/templates/ingress.yaml +++ b/charts/todoblazor/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if and $.Values.ingress.enabled .enabled .host -}} --- diff --git a/charts/todoblazor/templates/service.yaml b/charts/todoblazor/templates/service.yaml index 55ae081..d5ac563 100644 --- a/charts/todoblazor/templates/service.yaml +++ b/charts/todoblazor/templates/service.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/todoblazor/templates/serviceaccount.yaml b/charts/todoblazor/templates/serviceaccount.yaml index c70b502..0c705ad 100644 --- a/charts/todoblazor/templates/serviceaccount.yaml +++ b/charts/todoblazor/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.security.serviceAccount.create }} +{{- if .Values.security.serviceAccount.create }} {{- $applications := list .Values.webapp -}} {{ range $applications }} apiVersion: v1 diff --git a/charts/todoblazor/values.yaml b/charts/todoblazor/values.yaml index 8087a61..aa90fd4 100644 --- a/charts/todoblazor/values.yaml +++ b/charts/todoblazor/values.yaml @@ -1,4 +1,4 @@ -webapp: +webapp: enabled: true name: todoblazor image: docker.io/devprofr/todoblazor diff --git a/charts/trackboard/Chart.yaml b/charts/trackboard/Chart.yaml index 154aae9..ed28073 100644 --- a/charts/trackboard/Chart.yaml +++ b/charts/trackboard/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: trackboard description: Trackboard web application type: application -version: 0.1.0 +version: 0.1.1 appVersion: "0.1.0" keywords: - event diff --git a/charts/trackboard/README.md b/charts/trackboard/README.md index 944e5d4..bef48b7 100644 --- a/charts/trackboard/README.md +++ b/charts/trackboard/README.md @@ -1,4 +1,4 @@ -# Trackboard Helm Chart +# Trackboard Helm Chart Helm chart for [Trackboard Web Application](https://github.com/devpro/container-images/tree/main/src/trackboard) — designed for container security workshops. diff --git a/charts/trackboard/templates/NOTES.txt b/charts/trackboard/templates/NOTES.txt index 567b9b9..ed61d0c 100644 --- a/charts/trackboard/templates/NOTES.txt +++ b/charts/trackboard/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. Trackboard web application has been deployed. +1. Trackboard web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/trackboard/templates/_helpers.tpl b/charts/trackboard/templates/_helpers.tpl index 4379db8..620ad51 100644 --- a/charts/trackboard/templates/_helpers.tpl +++ b/charts/trackboard/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "trackboard.name" -}} diff --git a/charts/trackboard/templates/deployment.yaml b/charts/trackboard/templates/deployment.yaml index 18756e7..53a40a7 100644 --- a/charts/trackboard/templates/deployment.yaml +++ b/charts/trackboard/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/templates/ingress.yaml b/charts/trackboard/templates/ingress.yaml index 204a49b..b843a2f 100644 --- a/charts/trackboard/templates/ingress.yaml +++ b/charts/trackboard/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/trackboard/templates/networkpolicy.yaml b/charts/trackboard/templates/networkpolicy.yaml index 8e9ccc1..a08e761 100644 --- a/charts/trackboard/templates/networkpolicy.yaml +++ b/charts/trackboard/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/trackboard/templates/pvc.yaml b/charts/trackboard/templates/pvc.yaml index 22e5938..9522e71 100644 --- a/charts/trackboard/templates/pvc.yaml +++ b/charts/trackboard/templates/pvc.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: PersistentVolumeClaim metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/templates/secret.yaml b/charts/trackboard/templates/secret.yaml index 5b603ca..0d8ce35 100644 --- a/charts/trackboard/templates/secret.yaml +++ b/charts/trackboard/templates/secret.yaml @@ -1,4 +1,4 @@ -{{- if .Values.admin.password -}} +{{- if .Values.admin.password -}} apiVersion: v1 kind: Secret metadata: diff --git a/charts/trackboard/templates/service.yaml b/charts/trackboard/templates/service.yaml index ac38825..be652ff 100644 --- a/charts/trackboard/templates/service.yaml +++ b/charts/trackboard/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/values.yaml b/charts/trackboard/values.yaml index d2eac54..46b695d 100644 --- a/charts/trackboard/values.yaml +++ b/charts/trackboard/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/trackboard tag: "1.0.0" pullPolicy: IfNotPresent diff --git a/charts/webgoat/CONTRIBUTING.md b/charts/webgoat/CONTRIBUTING.md index 95f8338..5e2a133 100644 --- a/charts/webgoat/CONTRIBUTING.md +++ b/charts/webgoat/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/webgoat/Chart.yaml b/charts/webgoat/Chart.yaml index 1a8e9e6..8536dbc 100644 --- a/charts/webgoat/Chart.yaml +++ b/charts/webgoat/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: webgoat description: OWASP WebGoat — intentionally vulnerable Java/Spring app for security workshops type: application -version: 1.0.0 +version: 1.0.1 appVersion: "2023.8" keywords: - security diff --git a/charts/webgoat/README.md b/charts/webgoat/README.md index 67e2bc1..ac185f5 100644 --- a/charts/webgoat/README.md +++ b/charts/webgoat/README.md @@ -1,4 +1,4 @@ -# OWASP WebGoat Helm Chart +# OWASP WebGoat Helm Chart Helm chart for [WebGoat Web Application](https://owasp.org/www-project-webgoat/) — designed for container security workshops. diff --git a/charts/webgoat/templates/NOTES.txt b/charts/webgoat/templates/NOTES.txt index 8c05230..20df4ad 100644 --- a/charts/webgoat/templates/NOTES.txt +++ b/charts/webgoat/templates/NOTES.txt @@ -1,4 +1,4 @@ -🎯 WebGoat + WebWolf deployed! +🎯 WebGoat + WebWolf deployed! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ACCESS diff --git a/charts/webgoat/templates/deployment.yaml b/charts/webgoat/templates/deployment.yaml index d62af02..ce6e4f0 100644 --- a/charts/webgoat/templates/deployment.yaml +++ b/charts/webgoat/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "webgoat.fullname" . }} diff --git a/charts/webgoat/templates/ingress.yaml b/charts/webgoat/templates/ingress.yaml index fa8176b..f859ead 100644 --- a/charts/webgoat/templates/ingress.yaml +++ b/charts/webgoat/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} {{- if not .Values.ingress.domain }} {{- fail "ingress.domain is required when ingress.enabled=true. Set it with --set ingress.domain=workshop.example.com" }} {{- end }} diff --git a/charts/webgoat/values.yaml b/charts/webgoat/values.yaml index 5f638f8..535ba12 100644 --- a/charts/webgoat/values.yaml +++ b/charts/webgoat/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 webgoat: image: From 007c4df4903f7781cca9096a7502f6ea60224937 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 22:58:21 +0200 Subject: [PATCH 19/33] Delete the kind cluster only once the step creating it ran, rather than whenever charts changed A failure before it, of ct lint for instance, left neither kind nor a cluster, and the deletion then failed on a command not found. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e40d258..e5c9227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,7 @@ jobs: uses: devpro/github-workflow-parts/actions/kind/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. - name: Create kind cluster + id: create-cluster if: steps.list-changed.outputs.changed == 'true' run: kind create cluster --name "ct-${GITHUB_RUN_ID}" --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s - name: Install charts @@ -101,6 +102,7 @@ jobs: run: | ct install ${CHARTS_DIRECTORIES_ARG} --target-branch "$DEFAULT_BRANCH" \ --excluded-charts "${EXCLUDED_CHARTS// /}" --helm-extra-args "--timeout 300s" + # Only a cluster this run created, since a failure before it leaves neither kind nor a cluster. - name: Delete kind cluster - if: always() && steps.list-changed.outputs.changed == 'true' + if: always() && steps.create-cluster.outcome != 'skipped' && steps.create-cluster.outcome != '' run: kind delete cluster --name "ct-${GITHUB_RUN_ID}" From 9b53b374f815de4691269a89f9c68455b6a6c064 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 23:20:00 +0200 Subject: [PATCH 20/33] Install the changed charts three at a time on the one kind cluster, rather than one after another Each chart gets a release and a namespace of its own, as ct install gives it, and a log of its own, shown chart by chart once all are done. The step fails naming every chart that did not install. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5c9227..519bfaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,9 +99,38 @@ jobs: azure-storage,outscale, fallstar,liveship,sidelab,trackboard, wordpress + # Installs side by side on the one cluster, each chart in a release and a namespace of its own. + PARALLEL_INSTALLS: "3" + CHANGED_CHARTS: ${{ steps.list-changed.outputs.changed_charts }} run: | - ct install ${CHARTS_DIRECTORIES_ARG} --target-branch "$DEFAULT_BRANCH" \ - --excluded-charts "${EXCLUDED_CHARTS// /}" --helm-extra-args "--timeout 300s" + excluded=",${EXCLUDED_CHARTS// /}," + charts=() + for path in $CHANGED_CHARTS; do + [[ "$excluded" == *",$(basename "$path"),"* ]] || charts+=("$path") + done + if [ "${#charts[@]}" -eq 0 ]; then + echo "No changed chart to install" + exit 0 + fi + # Each install writes a log of its own, shown chart by chart once all are done, rather than interleaved. + logs="$(mktemp -d)" + printf '%s\n' "${charts[@]}" | xargs -P "$PARALLEL_INSTALLS" -I{} bash -c ' + name="$(basename "$1")" + if ct install --charts "$1" --helm-extra-args "--timeout 300s" > "$2/$name.log" 2>&1; then + echo "$name: installed" + else + echo "$name: failed" + echo "$name" >> "$2/failed" + fi' _ {} "$logs" + for path in "${charts[@]}"; do + echo "::group::$(basename "$path")" + cat "$logs/$(basename "$path").log" + echo "::endgroup::" + done + if [ -s "$logs/failed" ]; then + echo "::error::Charts that did not install: $(tr '\n' ' ' < "$logs/failed")" + exit 1 + fi # Only a cluster this run created, since a failure before it leaves neither kind nor a cluster. - name: Delete kind cluster if: always() && steps.create-cluster.outcome != 'skipped' && steps.create-cluster.outcome != '' From 8e1cad47e87620c321281077309e3af1e3b5a325 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Thu, 22 Oct 2026 23:38:53 +0200 Subject: [PATCH 21/33] Install the charts in a job of their own, once they pass their checks, which IstarCI leaves out of a local run The kind node is a privileged container with no fixed image yet for the vulnerabilities it carries, so it runs on GitHub only, on a runner thrown away after the job, rather than on the Docker of the machine running IstarCI. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .github/workflows/ci.yml | 29 ++++++++++++++++++++++++----- .istarci.yml | 6 +++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 519bfaa..c52bd31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,10 @@ jobs: charts: name: Charts runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 + outputs: + changed: ${{ steps.list-changed.outputs.changed }} + changed-charts: ${{ steps.list-changed.outputs.changed_charts }} env: # Read from the environment rather than pasted into the scripts by GitHub. DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} @@ -77,17 +80,33 @@ jobs: run: | charts=$(dirname charts/*/tests | sort -u) untt $charts + + # A job of its own, which IstarCI leaves out of a local run: the kind node runs as a privileged container on the Docker it is given. + charts-install: + name: Charts install + needs: charts + if: needs.charts.outputs.changed == 'true' + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - name: Install Helm + uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + - name: Set up chart-testing + uses: devpro/github-workflow-parts/actions/chart-testing/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + - name: Add dependency chart repositories + run: ./scripts/add_helm_repo.sh # kind and kubectl are installed by an action of the workflow parts, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. - name: Install kind and kubectl - if: steps.list-changed.outputs.changed == 'true' uses: devpro/github-workflow-parts/actions/kind/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. - name: Create kind cluster id: create-cluster - if: steps.list-changed.outputs.changed == 'true' run: kind create cluster --name "ct-${GITHUB_RUN_ID}" --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s - name: Install charts - if: steps.list-changed.outputs.changed == 'true' env: # Charts that cannot reach a ready state on a bare cluster, given here rather than in ct.yaml so that ct lint still covers them. # Custom resources only, whose CRDs a bare cluster lacks: capi-templates, gitops-bootstrap, letsencrypt, rancher-cluster-templates. @@ -101,7 +120,7 @@ jobs: wordpress # Installs side by side on the one cluster, each chart in a release and a namespace of its own. PARALLEL_INSTALLS: "3" - CHANGED_CHARTS: ${{ steps.list-changed.outputs.changed_charts }} + CHANGED_CHARTS: ${{ needs.charts.outputs.changed-charts }} run: | excluded=",${EXCLUDED_CHARTS// /}," charts=() diff --git a/.istarci.yml b/.istarci.yml index d40c3ff..79caf93 100644 --- a/.istarci.yml +++ b/.istarci.yml @@ -1,3 +1,3 @@ -runner: - # ct install starts a kind cluster through the host Docker daemon, as on a hosted runner. - docker: ["charts"] +workflow: + # ct install runs a kind node, a privileged container, on the Docker of the machine, so it runs on GitHub only, on a runner thrown away after the job. + skipJobs: [charts-install] From be3c7cabe1e493750806fb9a0999c590a7326127 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Fri, 23 Oct 2026 16:52:21 +0200 Subject: [PATCH 22/33] Ask editors for UTF-8 without a byte order mark, as the files are now saved Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 37ef933..11c25d4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ root = true [*] -charset = utf-8-bom +charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space From 3726ae078fdd4d31e4d970b74a59d1eae3d1123d Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Fri, 23 Oct 2026 22:24:26 +0200 Subject: [PATCH 23/33] Use the github-workflow-parts of 13ac7cc Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SESuYdZsJfq852odntm3Yi --- .github/workflows/ci.yml | 20 ++++++++++---------- .github/workflows/pkg.yml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c52bd31..e5947c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,13 +23,13 @@ env: jobs: docs: name: Docs - uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e with: # VitePress writes the site the GitLab pipeline publishes, which this job only builds. deploy: false job-name: Docs site-path: public - workflow-parts-version: 3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + workflow-parts-version: 13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e charts: name: Charts @@ -48,13 +48,13 @@ jobs: # note: important to have chart-testing work (see https://github.com/helm/chart-testing/issues/186) fetch-depth: 0 - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Install kube-linter - uses: devpro/github-workflow-parts/actions/kube-linter/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/kube-linter/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/chart-testing/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts @@ -70,12 +70,12 @@ jobs: if: steps.list-changed.outputs.changed == 'true' run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch "$DEFAULT_BRANCH" - name: Install kubeconform - uses: devpro/github-workflow-parts/actions/kubeconform/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/kubeconform/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Validate manifests against the Kubernetes schemas run: ./scripts/validate_charts.sh # The standalone binary of the helm-unittest plugin, so no plugin is installed into Helm. - name: Install helm-unittest - uses: devpro/github-workflow-parts/actions/helm-unittest/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/helm-unittest/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Run chart unit tests run: | charts=$(dirname charts/*/tests | sort -u) @@ -94,14 +94,14 @@ jobs: with: fetch-depth: 0 - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/chart-testing/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh # kind and kubectl are installed by an action of the workflow parts, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. - name: Install kind and kubectl - uses: devpro/github-workflow-parts/actions/kind/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/kind/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. - name: Create kind cluster id: create-cluster diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index d2408a1..3bcf5d3 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -48,7 +48,7 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@3f5c3623b3ed2e1c59924764f492b7bc2346bd61 + uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: Host charts repository on GitHub Pages From 63fc23144f8fb9702717e40fce72226aa334b50a Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 26 Oct 2026 12:40:31 +0100 Subject: [PATCH 24/33] Use workflow parts 591b0c7, with the scanners installed from checksummed releases, clean image scans, native arm64 builds and a timeout on every job Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 20 ++++++++++---------- .github/workflows/pkg.yml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5947c1..7c21da7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,13 +23,13 @@ env: jobs: docs: name: Docs - uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@591b0c7bfba6fbc7b0cab7d896447e0732b819de with: # VitePress writes the site the GitLab pipeline publishes, which this job only builds. deploy: false job-name: Docs site-path: public - workflow-parts-version: 13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + workflow-parts-version: 591b0c7bfba6fbc7b0cab7d896447e0732b819de charts: name: Charts @@ -48,13 +48,13 @@ jobs: # note: important to have chart-testing work (see https://github.com/helm/chart-testing/issues/186) fetch-depth: 0 - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Install kube-linter - uses: devpro/github-workflow-parts/actions/kube-linter/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/kube-linter/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/chart-testing/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts @@ -70,12 +70,12 @@ jobs: if: steps.list-changed.outputs.changed == 'true' run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch "$DEFAULT_BRANCH" - name: Install kubeconform - uses: devpro/github-workflow-parts/actions/kubeconform/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/kubeconform/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Validate manifests against the Kubernetes schemas run: ./scripts/validate_charts.sh # The standalone binary of the helm-unittest plugin, so no plugin is installed into Helm. - name: Install helm-unittest - uses: devpro/github-workflow-parts/actions/helm-unittest/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/helm-unittest/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Run chart unit tests run: | charts=$(dirname charts/*/tests | sort -u) @@ -94,14 +94,14 @@ jobs: with: fetch-depth: 0 - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Set up chart-testing - uses: devpro/github-workflow-parts/actions/chart-testing/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/chart-testing/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh # kind and kubectl are installed by an action of the workflow parts, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. - name: Install kind and kubectl - uses: devpro/github-workflow-parts/actions/kind/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/kind/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. - name: Create kind cluster id: create-cluster diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 3bcf5d3..4bf2bf2 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -48,7 +48,7 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: devpro/github-workflow-parts/actions/helm/setup@13ac7ccf4ba593797f8c80a75f2559ce0fc2d09e + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: Host charts repository on GitHub Pages From 181858d5983efa7629e82801c62cfd92df33ff22 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Mon, 26 Oct 2026 23:42:56 +0100 Subject: [PATCH 25/33] Lint the Markdown and YAML files in CI as the first job, as every repository does Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011Btfe5mZ8jkmrYUV8dN3Mx --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c21da7..7f37053 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ env: CHARTS_DIRECTORIES_ARG: "--chart-dirs charts" jobs: + markup-lint: + name: Markup + uses: devpro/github-workflow-parts/.github/workflows/reusable-markup-lint.yml@591b0c7bfba6fbc7b0cab7d896447e0732b819de + docs: name: Docs uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@591b0c7bfba6fbc7b0cab7d896447e0732b819de From 39cfa9ceb183fb6544844cabf19414b44443676d Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 00:10:31 +0100 Subject: [PATCH 26/33] Fix the Markdown findings of the markup lint, which now runs in CI The VitePress examples keep the HTML and emphasis they demonstrate. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011Btfe5mZ8jkmrYUV8dN3Mx --- charts/grafana-stack/README.md | 2 +- charts/terraform-backend-mongodb/README.md | 3 ++- docs/application-guides/databases/mysql.md | 3 ++- .../application-guides/pipeline-orchestration/concourse.md | 7 ++++++- docs/examples/api-examples.md | 2 ++ docs/examples/markdown-examples.md | 2 ++ samples/charts/hobbyfarm/README.md | 4 ++-- 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/charts/grafana-stack/README.md b/charts/grafana-stack/README.md index 5952c7a..055152a 100644 --- a/charts/grafana-stack/README.md +++ b/charts/grafana-stack/README.md @@ -12,7 +12,7 @@ Stack components: See also: -- [CNCF Live Webinar: Intro to open source observability with Grafana, Prometheus, Loki, and Tempo](https://community.cncf.io/events/details/cncf-cncf-online-programs-presents-cncf-live-webinar-intro-to-open-source-observability-with-grafana-prometheus-loki-and-tempo/) - November 16, 2021 +- [CNCF: Intro to open source observability](https://community.cncf.io/events/details/cncf-cncf-online-programs-presents-cncf-live-webinar-intro-to-open-source-observability-with-grafana-prometheus-loki-and-tempo/) - November 16, 2021 ## How to use diff --git a/charts/terraform-backend-mongodb/README.md b/charts/terraform-backend-mongodb/README.md index 3c9914d..183da1c 100644 --- a/charts/terraform-backend-mongodb/README.md +++ b/charts/terraform-backend-mongodb/README.md @@ -94,7 +94,8 @@ The defaults under `authentication` in [values.yaml](values.yaml) suit most depl The setting that does need a decision is `network.trustAllProxies`, because the other two depend on it. Terraform's `http` backend sends a Basic credential on every single request, so the server verifies the same password over and over and every failed guess costs it a BCrypt verify. -The lockout puts a ceiling on that, and it is scoped to the username and the caller's source address together rather than to the username alone: a lockout on the username alone would let anybody on the internet lock the real operator out of the backend. +The lockout puts a ceiling on that, and it is scoped to the username and the caller's source address together rather than to the username alone. +A lockout on the username alone would let anybody on the internet lock the real operator out of the backend. Behind an ingress, an application that does not trust the proxy sees the proxy's address on every request. Every caller then shares one lockout bucket, which turns the protection into the denial of service it was designed to avoid. diff --git a/docs/application-guides/databases/mysql.md b/docs/application-guides/databases/mysql.md index 76ed303..c14c76c 100644 --- a/docs/application-guides/databases/mysql.md +++ b/docs/application-guides/databases/mysql.md @@ -40,7 +40,8 @@ kubectl delete ns mysql 3. Connect using the mysql cli, then provide your password: `$ mysql -h mysql-xxxxxxx -p` - To connect to your database directly from outside the K8s cluster: MYSQL_HOST=127.0.0.1, MYSQL_PORT=3306. Execute the following command to route the connection: `kubectl port-forward svc/mysql-xxxxxxx 3306` and `mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}`. + To connect to your database directly from outside the K8s cluster: MYSQL_HOST=127.0.0.1, MYSQL_PORT=3306. + Execute the following command to route the connection: `kubectl port-forward svc/mysql-xxxxxxx 3306` and `mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}`. As usual, look at the progress with `kubectl get pods` ("STATUS" column). diff --git a/docs/application-guides/pipeline-orchestration/concourse.md b/docs/application-guides/pipeline-orchestration/concourse.md index 1bf0f4b..5bef52a 100644 --- a/docs/application-guides/pipeline-orchestration/concourse.md +++ b/docs/application-guides/pipeline-orchestration/concourse.md @@ -91,7 +91,12 @@ spec: kubectl apply -f ingress.yaml # activate ingress -helm install my-name stable/concourse --set concourse.web.externalUrl=https://my-domain.com/ --set web.ingress.enabled=true --set web.ingress.hosts[0]=my-domain.com --set web.ingress.tls[0].secretName=domain-web-tls --set web.ingress.tls[0].hosts[0]=my-domain.com --set secrets.localUsers="test:mysecretpassword" --set web.resources.requests.cpu="200m" --set web.resources.requests.memory="256Mi" +helm install my-name stable/concourse \ + --set concourse.web.externalUrl=https://my-domain.com/ \ + --set web.ingress.enabled=true --set web.ingress.hosts[0]=my-domain.com \ + --set web.ingress.tls[0].secretName=domain-web-tls --set web.ingress.tls[0].hosts[0]=my-domain.com \ + --set secrets.localUsers="test:mysecretpassword" \ + --set web.resources.requests.cpu="200m" --set web.resources.requests.memory="256Mi" ``` ## Known issues diff --git a/docs/examples/api-examples.md b/docs/examples/api-examples.md index 691df9c..18ce89c 100644 --- a/docs/examples/api-examples.md +++ b/docs/examples/api-examples.md @@ -2,6 +2,8 @@ outline: deep --- + + # Runtime API Examples This page demonstrates usage of some of the runtime APIs provided by VitePress. diff --git a/docs/examples/markdown-examples.md b/docs/examples/markdown-examples.md index f9258a5..619f69a 100644 --- a/docs/examples/markdown-examples.md +++ b/docs/examples/markdown-examples.md @@ -1,3 +1,5 @@ + + # Markdown Extension Examples This page demonstrates some of the built-in markdown extensions provided by VitePress. diff --git a/samples/charts/hobbyfarm/README.md b/samples/charts/hobbyfarm/README.md index 9c07605..05b2f1a 100644 --- a/samples/charts/hobbyfarm/README.md +++ b/samples/charts/hobbyfarm/README.md @@ -22,8 +22,8 @@ kubectl delete ns hobbyfarm ## How to start once the application is running -- Open HobbyFarm Admin UI, for example "https://admin.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/" -- Open HobbyFarm UI, for example "https://learn.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/" +- Open HobbyFarm Admin UI, for example `https://admin.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/` +- Open HobbyFarm UI, for example `https://learn.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/` ## How to create or update the chart From 944a6f6e52127ed4a75fbf0b43e52e205f49c575 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 00:13:25 +0100 Subject: [PATCH 27/33] Leave the Helm templates out of yamllint, as they are Go templates that render to YAML Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011Btfe5mZ8jkmrYUV8dN3Mx --- .yamllint.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.yamllint.yaml b/.yamllint.yaml index 226c5d6..b416d9d 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -1,5 +1,10 @@ extends: default +# Helm templates are Go templates that render to YAML, not YAML themselves. +ignore: + - "charts/*/templates/**" + - "samples/charts/*/templates/**" + rules: line-length: level: warning From 754ba5d41eb6d2925bbedc612c1db38c86ddf8c6 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 00:16:34 +0100 Subject: [PATCH 28/33] Fix the YAML findings of the markup lint, allowing the one space inside braces the Fallstar recipes use, as the fallstar repository does Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011Btfe5mZ8jkmrYUV8dN3Mx --- .github/workflows/pkg.yml | 2 +- .yamllint.yaml | 3 +++ charts/terraform-backend-mongodb/values.yaml | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 4bf2bf2..c30f913 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -99,4 +99,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 \ No newline at end of file + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/.yamllint.yaml b/.yamllint.yaml index b416d9d..4aed556 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -10,3 +10,6 @@ rules: level: warning max: 180 truthy: disable + # The Fallstar recipes and Fleet patches write their flow mappings with a space inside the braces. + braces: + max-spaces-inside: 1 diff --git a/charts/terraform-backend-mongodb/values.yaml b/charts/terraform-backend-mongodb/values.yaml index 5bada79..cade6a6 100644 --- a/charts/terraform-backend-mongodb/values.yaml +++ b/charts/terraform-backend-mongodb/values.yaml @@ -43,9 +43,9 @@ webapi: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: "kubernetes.io/arch" - operator: In - values: ["amd64"] + - key: "kubernetes.io/arch" + operator: In + values: ["amd64"] dotnet: environment: Production From cb9392590fd975cba4a5b4a46f9ba83cf8c93844 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 14:35:43 +0100 Subject: [PATCH 29/33] Update fallstar chart --- charts/fallstar/README.md | 8 ++++++++ charts/fallstar/templates/deployment.yaml | 4 ++-- charts/fallstar/templates/secret.yaml | 3 +++ charts/fallstar/tests/deployment_test.yaml | 10 ++++++++++ charts/fallstar/tests/resources_test.yaml | 8 ++++++++ charts/fallstar/values.yaml | 4 +++- 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/charts/fallstar/README.md b/charts/fallstar/README.md index 93825e7..474862e 100644 --- a/charts/fallstar/README.md +++ b/charts/fallstar/README.md @@ -18,6 +18,14 @@ The image is built from the `Dockerfile` of the Fallstar repository. > **Tip:** Every option is documented in [values.yaml](values.yaml). +## Lost password + +The reset command of the image writes a new generated password to the database and prints it. + +```bash +kubectl -n fallstar exec deploy/fallstar -- node --import tsx packages/server/src/reset-password.ts +``` + ## Uninstall ```bash diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml index 565e25b..454224b 100644 --- a/charts/fallstar/templates/deployment.yaml +++ b/charts/fallstar/templates/deployment.yaml @@ -67,13 +67,13 @@ spec: {{- end }} livenessProbe: httpGet: - path: / + path: /healthz port: http initialDelaySeconds: 15 periodSeconds: 20 readinessProbe: httpGet: - path: / + path: /healthz port: http initialDelaySeconds: 5 periodSeconds: 10 diff --git a/charts/fallstar/templates/secret.yaml b/charts/fallstar/templates/secret.yaml index 03190dc..8230aae 100644 --- a/charts/fallstar/templates/secret.yaml +++ b/charts/fallstar/templates/secret.yaml @@ -12,6 +12,9 @@ stringData: FALLSTAR_SECRET: {{ .Values.auth.secret | default (include "fallstar.generated" (dict "root" . "key" "FALLSTAR_SECRET" "length" 48)) | quote }} ADMIN_USERNAME: {{ .Values.auth.adminUsername | quote }} ADMIN_PASSWORD: {{ .Values.auth.adminPassword | default (include "fallstar.generated" (dict "root" . "key" "ADMIN_PASSWORD" "length" 20)) | quote }} + {{- with .Values.auth.adminEmail }} + ADMIN_EMAIL: {{ . | quote }} + {{- end }} {{- end }} {{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.existingSecret) }} --- diff --git a/charts/fallstar/tests/deployment_test.yaml b/charts/fallstar/tests/deployment_test.yaml index 34ef899..fbc7bf0 100644 --- a/charts/fallstar/tests/deployment_test.yaml +++ b/charts/fallstar/tests/deployment_test.yaml @@ -21,6 +21,16 @@ tests: equal: path: spec.template.spec.enableServiceLinks value: false + - it: probes the health endpoint, which reads the database + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: /healthz + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /healthz - it: recreates the Pod on SQLite asserts: - template: deployment.yaml diff --git a/charts/fallstar/tests/resources_test.yaml b/charts/fallstar/tests/resources_test.yaml index 1ba8987..9c87f61 100644 --- a/charts/fallstar/tests/resources_test.yaml +++ b/charts/fallstar/tests/resources_test.yaml @@ -42,6 +42,14 @@ tests: path: stringData.DB_URL value: mongodb://db:27017/fallstar documentIndex: 1 + - it: names the admin email only when set + template: secret.yaml + set: + auth.adminEmail: admin@example.com + asserts: + - equal: + path: stringData.ADMIN_EMAIL + value: admin@example.com - it: generates the master secret and the admin password when empty template: secret.yaml asserts: diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml index 3b076c7..d198461 100644 --- a/charts/fallstar/values.yaml +++ b/charts/fallstar/values.yaml @@ -34,7 +34,9 @@ auth: adminUsername: "admin" # -- Generated on first install when empty, the install notes show how to read it. adminPassword: "" - # -- Existing Secret with the keys FALLSTAR_SECRET, ADMIN_USERNAME and ADMIN_PASSWORD, replacing the three values above. + # -- Email of the initial admin, the author of the commits it makes, none when empty. + adminEmail: "" + # -- Existing Secret with the keys FALLSTAR_SECRET, ADMIN_USERNAME, ADMIN_PASSWORD and optionally ADMIN_EMAIL, replacing the four values above. existingSecret: "" database: From 34565874f883b7b918e421cac73fb9778c669ddf Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 17:01:44 +0100 Subject: [PATCH 30/33] Let the sidelab launcher manage NetworkPolicies, and expose the lab egress ranges as launcher.labEgressCidrs, in chart 0.1.9 The launcher now isolates every lab session with its own NetworkPolicies and refuses to start a session without them, so the ClusterRole grants get, list, create and delete on networkpolicies. launcher.labEgressCidrs becomes K8S_LAB_EGRESS_CIDRS, the private ranges a lab may reach beyond the internet. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014NEKk9yr86P4Mra28RddPa --- charts/sidelab/Chart.yaml | 2 +- charts/sidelab/templates/configmap.yaml | 3 +++ charts/sidelab/templates/rbac.yaml | 5 +++++ charts/sidelab/values.yaml | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index d9d5965..0fb67ae 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -4,7 +4,7 @@ description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.8 +version: 0.1.9 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/templates/configmap.yaml b/charts/sidelab/templates/configmap.yaml index ada2a9b..61046b7 100644 --- a/charts/sidelab/templates/configmap.yaml +++ b/charts/sidelab/templates/configmap.yaml @@ -29,6 +29,9 @@ data: {{- if .Values.launcher.labImagePullSecret }} K8S_LAB_IMAGE_PULL_SECRET: {{ .Values.launcher.labImagePullSecret | quote }} {{- end }} + {{- with .Values.launcher.labEgressCidrs }} + K8S_LAB_EGRESS_CIDRS: {{ join "," . | quote }} + {{- end }} # The admin tenant's namespace is the default, as a fallback when namespaces cannot be listed. K8S_KNOWN_NAMESPACES: {{ .Values.launcher.knownNamespaces | default (printf "%s-admin" .Values.launcher.namespacePrefix) | quote }} --- diff --git a/charts/sidelab/templates/rbac.yaml b/charts/sidelab/templates/rbac.yaml index e4bac63..27d53a7 100644 --- a/charts/sidelab/templates/rbac.yaml +++ b/charts/sidelab/templates/rbac.yaml @@ -32,6 +32,11 @@ rules: resources: ["ingresses"] verbs: ["get", "list", "create", "delete"] + # Per-session isolation; a session does not start without its NetworkPolicies. + - apiGroups: ["networking.k8s.io"] + resources: ["networkpolicies"] + verbs: ["get", "list", "create", "delete"] + # Attaches launcher.labImagePullSecret to the default ServiceAccount of tenant namespaces. - apiGroups: [""] resources: ["serviceaccounts"] diff --git a/charts/sidelab/values.yaml b/charts/sidelab/values.yaml index fc6aad6..714e81c 100644 --- a/charts/sidelab/values.yaml +++ b/charts/sidelab/values.yaml @@ -98,6 +98,9 @@ launcher: # -- dockerconfigjson Secret in the release namespace used to pull labImage, copied into every tenant namespace. labImagePullSecret: "" + # -- CIDRs a lab may reach beyond the internet, for a course that needs a private network; every private, shared and link-local range is closed otherwise. + labEgressCidrs: [] + service: # -- ClusterIP, NodePort or LoadBalancer. type: ClusterIP From 6a70d68e603706e46fdb0cbed1282a527592e916 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 27 Oct 2026 19:32:15 +0100 Subject: [PATCH 31/33] Pass the sidelab launcher its runtime: vm pod settings as launcher.vm, in chart 0.1.10 The image, the device plugin's resource names and an optional RuntimeClass, which a cluster exempts from a baseline Pod Security level since the VM pods' prepare step adds NET_ADMIN. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014NEKk9yr86P4Mra28RddPa --- charts/sidelab/Chart.yaml | 2 +- charts/sidelab/templates/configmap.yaml | 6 ++++++ charts/sidelab/values.yaml | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index 0fb67ae..be95d40 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -4,7 +4,7 @@ description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.9 +version: 0.1.10 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/templates/configmap.yaml b/charts/sidelab/templates/configmap.yaml index 61046b7..f2401de 100644 --- a/charts/sidelab/templates/configmap.yaml +++ b/charts/sidelab/templates/configmap.yaml @@ -32,6 +32,12 @@ data: {{- with .Values.launcher.labEgressCidrs }} K8S_LAB_EGRESS_CIDRS: {{ join "," . | quote }} {{- end }} + K8S_VM_IMAGE: {{ .Values.launcher.vm.image | quote }} + K8S_VM_KVM_RESOURCE: {{ .Values.launcher.vm.kvmResource | quote }} + K8S_VM_TUN_RESOURCE: {{ .Values.launcher.vm.tunResource | quote }} + {{- with .Values.launcher.vm.runtimeClass }} + K8S_VM_RUNTIME_CLASS: {{ . | quote }} + {{- end }} # The admin tenant's namespace is the default, as a fallback when namespaces cannot be listed. K8S_KNOWN_NAMESPACES: {{ .Values.launcher.knownNamespaces | default (printf "%s-admin" .Values.launcher.namespacePrefix) | quote }} --- diff --git a/charts/sidelab/values.yaml b/charts/sidelab/values.yaml index 714e81c..b795d47 100644 --- a/charts/sidelab/values.yaml +++ b/charts/sidelab/values.yaml @@ -101,6 +101,17 @@ launcher: # -- CIDRs a lab may reach beyond the internet, for a course that needs a private network; every private, shared and link-local range is closed otherwise. labEgressCidrs: [] + # A course host declared runtime: vm runs as a pod booting a Firecracker guest, on nodes whose device plugin offers /dev/kvm (docs/kubernetes-vm.md in sidelab). + vm: + # -- Image of the VM pods, built from sidelab's packages/vm. + image: "sidelab-vm:latest" + # -- Extended resource that brings /dev/kvm; devices.kubevirt.io/kvm on a cluster running KubeVirt. + kvmResource: "devic.es/kvm" + # -- Extended resource that brings /dev/net/tun; devices.kubevirt.io/tun on a cluster running KubeVirt. + tunResource: "devic.es/tun" + # -- RuntimeClass of the VM pods, which the cluster exempts from a baseline Pod Security level, since their prepare step adds NET_ADMIN. + runtimeClass: "" + service: # -- ClusterIP, NodePort or LoadBalancer. type: ClusterIP From 8125a742651467076dc26dde2232d39a2527309f Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 28 Oct 2026 01:41:45 +0100 Subject: [PATCH 32/33] Pass the sidelab launcher its runtime: terraform settings as launcher.terraform, in chart 0.1.11 moduleSource becomes TERRAFORM_MODULE_SOURCE, and credentialsSecret names a Secret whose keys reach the launcher's container through envFrom, so provider credentials stay out of values. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014NEKk9yr86P4Mra28RddPa --- charts/sidelab/Chart.yaml | 2 +- charts/sidelab/templates/configmap.yaml | 3 +++ charts/sidelab/templates/deployment.yaml | 4 ++++ charts/sidelab/values.yaml | 8 ++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index be95d40..cd897e6 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -4,7 +4,7 @@ description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.10 +version: 0.1.11 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/templates/configmap.yaml b/charts/sidelab/templates/configmap.yaml index f2401de..449cdf6 100644 --- a/charts/sidelab/templates/configmap.yaml +++ b/charts/sidelab/templates/configmap.yaml @@ -38,6 +38,9 @@ data: {{- with .Values.launcher.vm.runtimeClass }} K8S_VM_RUNTIME_CLASS: {{ . | quote }} {{- end }} + {{- with .Values.launcher.terraform.moduleSource }} + TERRAFORM_MODULE_SOURCE: {{ . | quote }} + {{- end }} # The admin tenant's namespace is the default, as a fallback when namespaces cannot be listed. K8S_KNOWN_NAMESPACES: {{ .Values.launcher.knownNamespaces | default (printf "%s-admin" .Values.launcher.namespacePrefix) | quote }} --- diff --git a/charts/sidelab/templates/deployment.yaml b/charts/sidelab/templates/deployment.yaml index 3c2a625..ad8929c 100644 --- a/charts/sidelab/templates/deployment.yaml +++ b/charts/sidelab/templates/deployment.yaml @@ -69,6 +69,10 @@ spec: - secretRef: name: {{ include "sidelab.mongoSecretName" . }} {{- end }} + {{- with .Values.launcher.terraform.credentialsSecret }} + - secretRef: + name: {{ . }} + {{- end }} env: # The Service lab Pods call back. - name: K8S_LAUNCHER_NAMESPACE diff --git a/charts/sidelab/values.yaml b/charts/sidelab/values.yaml index b795d47..0bdbc1b 100644 --- a/charts/sidelab/values.yaml +++ b/charts/sidelab/values.yaml @@ -112,6 +112,14 @@ launcher: # -- RuntimeClass of the VM pods, which the cluster exempts from a baseline Pod Security level, since their prepare step adds NET_ADMIN. runtimeClass: "" + # A course host declared runtime: terraform is a machine an OpenTofu module provisions, which downloads its agent from the lab and dials it at the lab's URL, so that URL must be reachable from where the machine runs (docs/terraform.md in sidelab). + # Its state is kept under /app/data/terraform, so persistence.enabled keeps a launcher restart from losing track of machines that bill. + terraform: + # -- Module of a host whose lab.yaml names none. + moduleSource: "" + # -- Secret in the release namespace holding the provider's credentials as its standard variables (AWS_ACCESS_KEY_ID, ARM_CLIENT_SECRET, KUBE_TOKEN, ...), passed to the launcher and never to a lab. + credentialsSecret: "" + service: # -- ClusterIP, NodePort or LoadBalancer. type: ClusterIP From c565e0d75b5ba87c442f9e2eccffdf91e4e1daee Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 28 Oct 2026 18:32:15 +0100 Subject: [PATCH 33/33] Send the keeptrack telemetry to an OTLP endpoint opentelemetry.endpoint and opentelemetry.protocol become OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_PROTOCOL, nothing being set while the endpoint is empty. Each application exports under its own OTEL_SERVICE_NAME, with the pod, namespace, version and environment in OTEL_RESOURCE_ATTRIBUTES, the pod name read from the downward API. The chart gets its first helm-unittest suite, left out of the package by .helmignore. --- charts/keeptrack/.helmignore | 1 + charts/keeptrack/Chart.yaml | 6 +- charts/keeptrack/README.md | 1 + charts/keeptrack/templates/deployment.yaml | 15 ++++ .../keeptrack/tests/opentelemetry_test.yaml | 68 +++++++++++++++++++ charts/keeptrack/values.yaml | 25 ++++--- 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 charts/keeptrack/tests/opentelemetry_test.yaml diff --git a/charts/keeptrack/.helmignore b/charts/keeptrack/.helmignore index 0e8a0eb..faeb926 100644 --- a/charts/keeptrack/.helmignore +++ b/charts/keeptrack/.helmignore @@ -21,3 +21,4 @@ .idea/ *.tmproj .vscode/ +tests/ diff --git a/charts/keeptrack/Chart.yaml b/charts/keeptrack/Chart.yaml index eea457f..f4a5741 100644 --- a/charts/keeptrack/Chart.yaml +++ b/charts/keeptrack/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: keeptrack description: Helm chart for Keeptrack type: application -version: 0.2.8 -appVersion: "2.4.1" +version: 0.2.7 +appVersion: "2.4.6" home: https://github.com/devpro/keeptrack sources: - https://github.com/devpro/keeptrack @@ -11,7 +11,7 @@ keywords: - keeptrack dependencies: - name: mongodb - version: 18.6.16 # MongoDB 8.2.6 + version: 18.6.16 # MongoDB 8.2.6 repository: https://charts.bitnami.com/bitnami alias: mongodb condition: mongodb.enabled diff --git a/charts/keeptrack/README.md b/charts/keeptrack/README.md index 54bf24d..45e1ee2 100644 --- a/charts/keeptrack/README.md +++ b/charts/keeptrack/README.md @@ -39,6 +39,7 @@ Notable ones: - `webapi.db`: MongoDB connection (mandatory). - `firebase`: authentication configuration (mandatory). - `webapi.referenceData`: TMDB/RAWG/Discogs API keys and the Open Library book provider override, used to enrich movies/TV shows/video games/books/albums with posters, synopsis, cast and episode data (optional). +- `opentelemetry.endpoint`: an OTLP endpoint, such as an OpenTelemetry Collector, receiving the traces, metrics and logs of both applications (optional). - `security.hardened.enabled`: runs containers with a read-only root filesystem, all Linux capabilities dropped, and the default seccomp profile, for platforms that mandate the Kubernetes Pod Security Standards "restricted" profile. Disabled by default; validate in a non-production environment first. diff --git a/charts/keeptrack/templates/deployment.yaml b/charts/keeptrack/templates/deployment.yaml index 687b9be..c70bba3 100644 --- a/charts/keeptrack/templates/deployment.yaml +++ b/charts/keeptrack/templates/deployment.yaml @@ -101,6 +101,21 @@ spec: value: {{ $.Values.dotnet.httpsRedirectionEnabled | quote }} - name: Features__IsScalarEnabled value: {{ $.Values.dotnet.scalarEnabled | quote }} + {{- if $.Values.opentelemetry.endpoint }} + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: {{ $.Values.opentelemetry.endpoint | quote }} + - name: OTEL_EXPORTER_OTLP_PROTOCOL + value: {{ $.Values.opentelemetry.protocol | quote }} + - name: OTEL_SERVICE_NAME + value: {{ .name | quote }} + # declared before OTEL_RESOURCE_ATTRIBUTES, since $(VAR) only expands a variable defined above it + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES + value: "service.namespace=keeptrack,service.version={{ .tag }},service.instance.id=$(K8S_POD_NAME),k8s.pod.name=$(K8S_POD_NAME),k8s.namespace.name={{ $.Release.Namespace }},deployment.environment.name={{ $.Values.dotnet.environment }}" + {{- end }} {{- if eq .role "frontend" }} - name: WebApi__BaseUrl value: "http://{{ $.Values.webapi.name }}" diff --git a/charts/keeptrack/tests/opentelemetry_test.yaml b/charts/keeptrack/tests/opentelemetry_test.yaml new file mode 100644 index 0000000..4deec6b --- /dev/null +++ b/charts/keeptrack/tests/opentelemetry_test.yaml @@ -0,0 +1,68 @@ +suite: opentelemetry +templates: + - deployment.yaml +release: + name: keeptrack + namespace: keeptrack +tests: + - it: exports nothing without an endpoint, the applications then registering no exporter + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_ENDPOINT + any: true + - it: sends both applications to the endpoint, each under its own service name + set: + opentelemetry.endpoint: http://otel-collector.observability:4317 + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://otel-collector.observability:4317 + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_PROTOCOL + value: grpc + - documentIndex: 0 + contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_SERVICE_NAME + value: keeptrack-blazorapp + - documentIndex: 1 + contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_SERVICE_NAME + value: keeptrack-webapi + - it: tells the pod, namespace and version in the resource attributes, read from the downward API + set: + opentelemetry.endpoint: http://otel-collector.observability:4317 + webapi.tag: 2.4.5 + blazorapp.enabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_RESOURCE_ATTRIBUTES + value: service.namespace=keeptrack,service.version=2.4.5,service.instance.id=$(K8S_POD_NAME),k8s.pod.name=$(K8S_POD_NAME),k8s.namespace.name=keeptrack,deployment.environment.name=Production + - it: sends over HTTP when asked, for a collector listening on 4318 only + set: + opentelemetry.endpoint: http://otel-collector.observability:4318 + opentelemetry.protocol: http/protobuf + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_PROTOCOL + value: http/protobuf diff --git a/charts/keeptrack/values.yaml b/charts/keeptrack/values.yaml index 8d2d94e..4c9152c 100644 --- a/charts/keeptrack/values.yaml +++ b/charts/keeptrack/values.yaml @@ -8,7 +8,7 @@ blazorapp: healthEndpoint: /healthz tls: secretName: keeptrack-blazorapp-tls - tag: 2.4.30663981662 # ref. https://hub.docker.com/r/devprofr/keeptrack-blazorapp/tags + tag: 2.4.40771568745 # https://hub.docker.com/r/devprofr/keeptrack-blazorapp/tags replicaCount: 1 resources: {} # limits: @@ -22,14 +22,13 @@ blazorapp: # value: "yyyy" additionalPodLabels: {} host: "" - # Blazor Server scale-out support - see README "Scaling & high availability". - # Persists the app's Data Protection key ring (which encrypts the auth cookie and antiforgery tokens) - # in MongoDB, so every replica can decrypt what any other replica issued. REQUIRED before setting replicaCount > 1; - # recommended even at 1 replica (sessions then survive pod restarts). + # Blazor Server scale-out support (see README "Scaling & high availability"). + # Persists the app's Data Protection key ring (which encrypts the auth cookie and antiforgery tokens) in MongoDB, + # so every replica can decrypt what any other replica issued. + # REQUIRED before setting replicaCount > 1; recommended even at 1 replica (sessions then survive pod restarts). dataProtection: enabled: false - # important when enabled: connectionString or connectionStringSecretKeyRef must be provided - # (typically the same secret as webapi.db) + # important when enabled: connectionString or connectionStringSecretKeyRef must be provided (typically the same secret as webapi.db) # connectionStringSecretKeyRef: # name: "" # key: "" @@ -48,12 +47,12 @@ webapi: secretName: keeptrack-webapi-tls host: "" replicaCount: 1 - tag: 2.4.30663981662 # ref. https://hub.docker.com/r/devprofr/keeptrack-webapi/tags + tag: 2.4.40771568745 # https://hub.docker.com/r/devprofr/keeptrack-webapi/tags resources: {} extraEnv: [] additionalPodLabels: {} # how many items a signed-in account WITHOUT the Firebase "member"/"admin" role may create per free-tier collection (movies, TV shows); - # such accounts have no access to the other collections at all - + # such accounts have no access to the other collections at all # grant `role: member` via the Firebase Admin SDK to unlock everything (see the app's CONTRIBUTING.md) freeTierItemLimit: 20 db: @@ -120,6 +119,14 @@ dotnet: imagePullSecrets: [] # - name: my-registry-secret +# optional: traces, metrics and logs of both applications sent over OTLP, to an OpenTelemetry Collector for instance; +# nothing is exported while endpoint is empty, and an image older than the OpenTelemetry support ignores these variables +opentelemetry: + # OTEL_EXPORTER_OTLP_ENDPOINT, e.g. http://otel-collector.observability:4317 + endpoint: "" + # OTEL_EXPORTER_OTLP_PROTOCOL: grpc (port 4317) or http/protobuf (port 4318) + protocol: grpc + security: serviceAccount: create: false