Add Argo CD Helm chart release
This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
name: Publish and deploy birthday invitation
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
env:
|
||||
IMAGE: registry.brunner.ninja/feedc0de/30-birthday
|
||||
KUBERNETES_API: https://host.containers.internal:6443
|
||||
KUBERNETES_TLS_SERVER_NAME: 192.168.0.2
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Check JavaScript
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
- name: Check server and browser scripts
|
||||
run: |
|
||||
authfile=$(mktemp)
|
||||
trap 'rm -f "$authfile"' EXIT
|
||||
printf '{"auths":{}}' > "$authfile"
|
||||
export REGISTRY_AUTH_FILE="$authfile"
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < server.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < database.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < public/app.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < public/admin.js
|
||||
podman run --rm \
|
||||
--volume "$PWD:/app:ro" \
|
||||
--workdir /app \
|
||||
docker.io/library/node:24-alpine npm test
|
||||
|
||||
publish:
|
||||
name: Build and push
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
needs: test
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure isolated registry access
|
||||
env:
|
||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
authfile="${RUNNER_TEMP}/quay-auth.json"
|
||||
printf '{"auths":{}}' > "$authfile"
|
||||
chmod 0600 "$authfile"
|
||||
printf '%s' "$QUAY_TOKEN" | podman login registry.brunner.ninja \
|
||||
--authfile "$authfile" --username "$QUAY_USERNAME" --password-stdin
|
||||
echo "REGISTRY_AUTH_FILE=$authfile" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Build image
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
podman build \
|
||||
--label "org.opencontainers.image.revision=${COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.source=https://code.brunner.ninja/feedc0de/30-birthday" \
|
||||
--tag "${IMAGE}:${COMMIT_SHA}" \
|
||||
.
|
||||
|
||||
- name: Push commit tag
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: podman push "${IMAGE}:${COMMIT_SHA}"
|
||||
|
||||
- name: Push latest tag
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
podman tag "${IMAGE}:${COMMIT_SHA}" "${IMAGE}:latest"
|
||||
podman push "${IMAGE}:latest"
|
||||
|
||||
- name: Remove registry credentials
|
||||
if: always()
|
||||
run: rm -f "${RUNNER_TEMP}/quay-auth.json"
|
||||
|
||||
deploy:
|
||||
name: Deploy to Kubernetes
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
needs: publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install kubectl
|
||||
env:
|
||||
KUBECTL_VERSION: v1.36.4
|
||||
run: |
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/kubectl" \
|
||||
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
|
||||
chmod 0700 "${RUNNER_TEMP}/kubectl"
|
||||
|
||||
- name: Configure Kubernetes access
|
||||
env:
|
||||
KUBE_CONFIG_BASE64: ${{ secrets.KUBE_CONFIG_BASE64 }}
|
||||
run: |
|
||||
test -n "${KUBE_CONFIG_BASE64}"
|
||||
printf '%s' "${KUBE_CONFIG_BASE64}" | base64 --decode > "${RUNNER_TEMP}/kubeconfig"
|
||||
chmod 0600 "${RUNNER_TEMP}/kubeconfig"
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
"${RUNNER_TEMP}/kubectl" config set-cluster cluster \
|
||||
--server="${KUBERNETES_API}" \
|
||||
--tls-server-name="${KUBERNETES_TLS_SERVER_NAME}"
|
||||
|
||||
- name: Deploy commit image
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
"${RUNNER_TEMP}/kubectl" --namespace default set image \
|
||||
deployment/30-birthday \
|
||||
"30-birthday=${IMAGE}:${COMMIT_SHA}"
|
||||
|
||||
for attempt in {1..60}; do
|
||||
IFS='|' read -r generation observed desired updated available unavailable <<< "$(
|
||||
"${RUNNER_TEMP}/kubectl" --namespace default get deployment/30-birthday \
|
||||
--output=jsonpath='{.metadata.generation}|{.status.observedGeneration}|{.spec.replicas}|{.status.updatedReplicas}|{.status.availableReplicas}|{.status.unavailableReplicas}'
|
||||
)"
|
||||
|
||||
echo "Rollout ${attempt}/60: generation ${observed}/${generation}, replicas ${updated}/${desired} updated, ${available}/${desired} available"
|
||||
if [[ "${observed}" == "${generation}" \
|
||||
&& "${updated}" == "${desired}" \
|
||||
&& "${available}" == "${desired}" \
|
||||
&& ( -z "${unavailable}" || "${unavailable}" == "0" ) ]]; then
|
||||
echo "Deployment successfully rolled out"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
|
||||
"${RUNNER_TEMP}/kubectl" --namespace default describe deployment/30-birthday
|
||||
exit 1
|
||||
@@ -0,0 +1,149 @@
|
||||
name: Test, publish birthday app and chart
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
IMAGE: registry.brunner.ninja/feedc0de/30-birthday
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/helm.tar.gz" \
|
||||
https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz
|
||||
tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm
|
||||
mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm"
|
||||
chmod 0700 "${RUNNER_TEMP}/helm"
|
||||
echo "${RUNNER_TEMP}" >> "$GITHUB_PATH"
|
||||
- name: Validate application and chart
|
||||
run: |
|
||||
set -euo pipefail
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < server.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < database.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < public/app.js
|
||||
podman run --rm --interactive docker.io/library/node:24-alpine node --check - < public/admin.js
|
||||
podman run --rm --volume "$PWD:/app:ro" --workdir /app \
|
||||
docker.io/library/node:24-alpine npm test
|
||||
chart/test.sh
|
||||
|
||||
publish-image:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
needs: validate
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Configure isolated registry access
|
||||
env:
|
||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
authfile="${RUNNER_TEMP}/quay-auth.json"
|
||||
printf '{"auths":{}}' > "$authfile"
|
||||
chmod 0600 "$authfile"
|
||||
printf '%s' "$QUAY_TOKEN" | podman login registry.brunner.ninja \
|
||||
--authfile "$authfile" --username "$QUAY_USERNAME" --password-stdin
|
||||
echo "REGISTRY_AUTH_FILE=$authfile" >> "$GITHUB_ENV"
|
||||
- name: Build and push immutable image
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
podman build \
|
||||
--label "org.opencontainers.image.revision=${COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.source=https://code.brunner.ninja/feedc0de/30-birthday" \
|
||||
--tag "${IMAGE}:${COMMIT_SHA}" .
|
||||
podman push "${IMAGE}:${COMMIT_SHA}"
|
||||
podman tag "${IMAGE}:${COMMIT_SHA}" "${IMAGE}:latest"
|
||||
podman push "${IMAGE}:latest"
|
||||
- if: always()
|
||||
run: rm -f "${RUNNER_TEMP}/quay-auth.json"
|
||||
|
||||
publish-chart:
|
||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||
needs:
|
||||
- validate
|
||||
- publish-image
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/helm.tar.gz" \
|
||||
https://get.helm.sh/helm-v4.2.2-linux-amd64.tar.gz
|
||||
tar -xzf "${RUNNER_TEMP}/helm.tar.gz" -C "${RUNNER_TEMP}" linux-amd64/helm
|
||||
mv "${RUNNER_TEMP}/linux-amd64/helm" "${RUNNER_TEMP}/helm"
|
||||
chmod 0700 "${RUNNER_TEMP}/helm"
|
||||
echo "${RUNNER_TEMP}" >> "$GITHUB_PATH"
|
||||
- id: chart
|
||||
name: Derive chart version
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch --tags origin main
|
||||
test "$(git rev-parse origin/main)" = "$COMMIT_SHA"
|
||||
description=$(git describe --tags --long --match 'v[0-9]*' --dirty)
|
||||
case "$description" in *-dirty) echo 'Refusing dirty chart tree' >&2; exit 1 ;; esac
|
||||
version=${description#v}
|
||||
case "$version" in *-0-g*) version=${version%%-0-g*} ;; esac
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
- name: Package and publish chart
|
||||
env:
|
||||
PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }}
|
||||
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||
CHART_VERSION: ${{ steps.chart.outputs.version }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$PACKAGE_USERNAME" && test -n "$PACKAGE_TOKEN"
|
||||
helm lint chart
|
||||
helm template 30-birthday chart --namespace default \
|
||||
--set persistence.existingClaim=30-birthday-data \
|
||||
--set ingress.enabled=true --set ingress.host=30.brunner.ninja >/dev/null
|
||||
helm package chart --version "$CHART_VERSION" --app-version "$COMMIT_SHA" --destination "$RUNNER_TEMP"
|
||||
curl --fail --silent --show-error \
|
||||
--user "${PACKAGE_USERNAME}:${PACKAGE_TOKEN}" \
|
||||
--request POST --upload-file "${RUNNER_TEMP}/30-birthday-${CHART_VERSION}.tgz" \
|
||||
https://code.brunner.ninja/api/packages/feedc0de/helm/api/charts
|
||||
- name: Promote GitOps chart version
|
||||
env:
|
||||
GITOPS_TOKEN: ${{ secrets.GITOPS_TOKEN }}
|
||||
CHART_VERSION: ${{ steps.chart.outputs.version }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$GITOPS_TOKEN"
|
||||
test "$(git rev-parse origin/main)" = "$COMMIT_SHA"
|
||||
auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n')
|
||||
git -c "http.extraheader=AUTHORIZATION: basic ${auth}" clone --depth=1 \
|
||||
https://code.brunner.ninja/feedc0de/homelab-gitops.git "$RUNNER_TEMP/gitops"
|
||||
unset auth
|
||||
cd "$RUNNER_TEMP/gitops"
|
||||
gitops_auth=$(printf 'feedc0de:%s' "$GITOPS_TOKEN" | base64 | tr -d '\n')
|
||||
unset GITOPS_TOKEN
|
||||
git config user.name feedc0de
|
||||
git config user.email feedc0de@users.noreply.code.brunner.ninja
|
||||
sed -i -E "0,/targetRevision: [^ ]+/s//targetRevision: ${CHART_VERSION}/" bootstrap/30-birthday.yaml
|
||||
if git diff --quiet; then exit 0; fi
|
||||
git add bootstrap/30-birthday.yaml
|
||||
git commit -m "Deploy birthday invitation chart ${CHART_VERSION}"
|
||||
for attempt in 1 2 3; do
|
||||
if git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" push origin HEAD:main; then exit 0; fi
|
||||
git -c "http.extraheader=AUTHORIZATION: basic ${gitops_auth}" fetch origin main
|
||||
git rebase origin/main
|
||||
done
|
||||
exit 1
|
||||
@@ -89,36 +89,34 @@ Die Einladungsgrafik und PDF sind bewusst nicht im Git-Repository oder Container
|
||||
|
||||
## Kubernetes, Traefik und Authentik
|
||||
|
||||
Ein vollständiges Beispiel liegt in `k8s/invitation.yaml`. Es verwendet:
|
||||
Das veröffentlichte Helm Chart befindet sich in `chart/`. Es owns the
|
||||
Deployment, Service, probes, SQLite persistence contract, and the public/admin
|
||||
Ingresses. SQLite must stay at one replica and uses a `Recreate` strategy.
|
||||
|
||||
- genau einen Pod (SQLite ist hier bewusst nicht horizontal skaliert)
|
||||
- einen `128Mi`-`rook-ceph-block`-PVC für `/data/invites.db`
|
||||
- die Domain `30.brunner.ninja`
|
||||
- ausschließlich Traefiks EntryPoint `websecure`
|
||||
- das bestehende `default-authentik@kubernetescrd`-Middleware nur für `/admin*` und `/api/admin/*`
|
||||
- öffentliche Gast- und Asset-Routen ohne Authentik
|
||||
- `Recreate`-Updates mit einer kurzen Unterbrechung, damit nur ein SQLite-Prozess den Block-PVC verwendet
|
||||
For this homelab, Argo CD is the sole workload writer. Its cluster values and
|
||||
existing-claim reference are in the separate
|
||||
[homelab-gitops application](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/30-birthday).
|
||||
The live `30-birthday-data` PVC is referenced as `existingClaim`; the chart
|
||||
does not render or replace it. Invitation assets stay in the existing
|
||||
`30-birthday-invitation` Secret, outside public Git. Authentik protects only
|
||||
admin paths; guest and invitation asset routes remain public.
|
||||
|
||||
Zuerst das Image bauen und in die eigene Registry pushen:
|
||||
For another cluster, create the invitation Secret first (the local helper
|
||||
expects private assets excluded from Git), then install the chart:
|
||||
|
||||
```bash
|
||||
docker build -t registry.brunner.ninja/feedc0de/30-birthday:latest .
|
||||
docker push registry.brunner.ninja/feedc0de/30-birthday:latest
|
||||
./k8s/create-invitation-secret.sh
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts
|
||||
helm repo update brunner.ninja
|
||||
helm upgrade --install 30-birthday brunner.ninja/30-birthday \
|
||||
--namespace default --wait --timeout 5m
|
||||
```
|
||||
|
||||
Die Gitea Action in `.gitea/workflows/container.yml` übernimmt Build, Push und Deployment automatisch. Sie benötigt diese Repository-Secrets:
|
||||
Main-branch CI tests the app, publishes an immutable image, packages a chart
|
||||
with that exact image as `appVersion`, and promotes only the chart version in
|
||||
homelab-gitops. CI uses `QUAY_USERNAME`, `QUAY_TOKEN`, `PACKAGE_USERNAME`,
|
||||
`PACKAGE_TOKEN`, and `GITOPS_TOKEN`; it has no Kubernetes credentials.
|
||||
|
||||
- `QUAY_USERNAME`: `feedc0de+birthday30`
|
||||
- `QUAY_TOKEN`: Token dieses Robot-Accounts
|
||||
- `KUBE_CONFIG_BASE64`: Kubeconfig des eingeschränkten `30-birthday-deployer`-ServiceAccounts
|
||||
|
||||
Workload und CI-Berechtigungen werden einmalig mit einem Cluster-Administrator angelegt:
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
./k8s/create-ci-kubeconfig.sh
|
||||
```
|
||||
|
||||
Die Action baut `registry.brunner.ninja/feedc0de/30-birthday`, versieht jedes Image mit dem unveränderlichen Commit-SHA und aktualisiert auf `main` ausschließlich das bestehende `default/30-birthday`-Deployment. Ihre Kubernetes-Rolle kann keine Secrets oder anderen Deployments lesen oder verändern.
|
||||
|
||||
Der Cluster stellt Authentiks `/outpost.goauthentik.io/*` bereits zentral bereit. Der App-Service ist nur als `ClusterIP` erreichbar; daher kann ein externer Client Authentiks Identitätsheader nicht direkt am App-Port einschleusen.
|
||||
The cluster exposes Authentik's `/outpost.goauthentik.io/*` centrally. The app
|
||||
Service remains `ClusterIP`, so external clients cannot inject Authentik
|
||||
identity headers through the app port.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: 30-birthday
|
||||
description: Helm chart for the 30th birthday RSVP application
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "bfac12a5bb24142c0c2f38b2a1c7bb0f5212df91"
|
||||
@@ -0,0 +1,25 @@
|
||||
# 30-birthday Helm chart
|
||||
|
||||
Portable Helm chart for the 30th birthday invitation and RSVP application. The
|
||||
chart owns its Deployment, Service, probes and public/admin Ingresses. SQLite
|
||||
requires one replica and a persistent claim. Set `persistence.existingClaim`
|
||||
to adopt an existing PVC; the chart then does not render a PVC object.
|
||||
|
||||
Invitation PDF/JPEG assets and optional admin credentials are referenced by
|
||||
existing Secret name only. Never commit their values.
|
||||
|
||||
The homelab keeps its desired values in
|
||||
[homelab-gitops](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/30-birthday).
|
||||
For a new cluster, create the invitation Secret and run:
|
||||
|
||||
```sh
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts
|
||||
helm repo update brunner.ninja
|
||||
helm upgrade --install 30-birthday brunner.ninja/30-birthday \
|
||||
--namespace default --wait --timeout 5m \
|
||||
--set origin=https://example.invalid \
|
||||
--set ingress.enabled=true --set ingress.host=example.invalid
|
||||
```
|
||||
|
||||
Chart tags version the Helm contract. `appVersion` is the exact immutable image
|
||||
tag published by the same source CI run.
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
release=${RELEASE_NAME:-30-birthday}
|
||||
namespace=${NAMESPACE:-default}
|
||||
|
||||
helm repo add brunner.ninja https://brunner.ninja/charts --force-update
|
||||
helm repo update brunner.ninja
|
||||
if [ -n "${VALUES_FILE:-}" ]; then
|
||||
helm upgrade --install "$release" brunner.ninja/30-birthday \
|
||||
--namespace "$namespace" --create-namespace \
|
||||
--values "$VALUES_FILE" --wait --timeout 5m
|
||||
else
|
||||
helm upgrade --install "$release" brunner.ninja/30-birthday \
|
||||
--namespace "$namespace" --create-namespace --wait --timeout 5m
|
||||
fi
|
||||
@@ -0,0 +1,7 @@
|
||||
{{- define "birthday.name" -}}
|
||||
30-birthday
|
||||
{{- end -}}
|
||||
|
||||
{{- define "birthday.dataClaim" -}}
|
||||
{{- default "30-birthday-data" .Values.persistence.existingClaim -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,86 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: 30-birthday
|
||||
labels:
|
||||
app: 30-birthday
|
||||
app.kubernetes.io/name: 30-birthday
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: 30-birthday
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: 30-birthday
|
||||
app.kubernetes.io/name: 30-birthday
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
fsGroup: 65532
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
containers:
|
||||
- name: 30-birthday
|
||||
image: {{ printf "%s:%s" .Values.image.repository .Chart.AppVersion | quote }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: ORIGIN
|
||||
value: {{ .Values.origin | quote }}
|
||||
- name: DATABASE_FILE
|
||||
value: /data/invites.db
|
||||
- name: TRUST_AUTHENTIK
|
||||
value: {{ .Values.trustAuthentik | quote }}
|
||||
- name: INVITATION_PDF
|
||||
value: {{ .Values.invitationPdf | quote }}
|
||||
- name: INVITATION_IMAGE
|
||||
value: {{ .Values.invitationImage | quote }}
|
||||
{{- if .Values.adminKey.secretName }}
|
||||
- name: ADMIN_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.adminKey.secretName | quote }}
|
||||
key: {{ .Values.adminKey.secretKey | quote }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: invitation
|
||||
mountPath: /private
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "birthday.dataClaim" . | quote }}
|
||||
- name: invitation
|
||||
secret:
|
||||
secretName: {{ .Values.secrets.invitation | quote }}
|
||||
@@ -0,0 +1,58 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: 30-birthday-public
|
||||
annotations:
|
||||
{{- toYaml .Values.ingress.annotations | nindent 4 }}
|
||||
spec:
|
||||
ingressClassName: {{ .Values.ingress.ingressClassName | quote }}
|
||||
{{- if .Values.ingress.tlsSecretName }}
|
||||
tls:
|
||||
- hosts: [{{ .Values.ingress.host | quote }}]
|
||||
secretName: {{ .Values.ingress.tlsSecretName | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ .Values.ingress.host | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: 30-birthday
|
||||
port:
|
||||
name: http
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: 30-birthday-admin
|
||||
annotations:
|
||||
{{- toYaml .Values.ingress.adminAnnotations | nindent 4 }}
|
||||
spec:
|
||||
ingressClassName: {{ .Values.ingress.ingressClassName | quote }}
|
||||
{{- if .Values.ingress.tlsSecretName }}
|
||||
tls:
|
||||
- hosts: [{{ .Values.ingress.host | quote }}]
|
||||
secretName: {{ .Values.ingress.tlsSecretName | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ .Values.ingress.host | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /admin
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: 30-birthday
|
||||
port:
|
||||
name: http
|
||||
- path: /api/admin/
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: 30-birthday
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "birthday.dataClaim" . }}
|
||||
labels:
|
||||
app.kubernetes.io/name: 30-birthday
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- with .Values.persistence.storageClassName }}
|
||||
storageClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: 30-birthday
|
||||
labels:
|
||||
app: 30-birthday
|
||||
app.kubernetes.io/name: 30-birthday
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
selector:
|
||||
app: 30-birthday
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: http
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
chart_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
|
||||
helm lint "$chart_dir"
|
||||
helm template 30-birthday "$chart_dir" --namespace default \
|
||||
--set persistence.existingClaim=existing-data \
|
||||
--set ingress.enabled=true --set ingress.host=test.example \
|
||||
>"$workdir/rendered.yaml"
|
||||
if grep -q '^kind: PersistentVolumeClaim$' "$workdir/rendered.yaml"; then
|
||||
echo 'existingClaim must not render a PVC' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'claimName: "existing-data"' "$workdir/rendered.yaml"
|
||||
grep -q 'name: 30-birthday-public' "$workdir/rendered.yaml"
|
||||
grep -q 'name: 30-birthday-admin' "$workdir/rendered.yaml"
|
||||
grep -q 'path: /api/admin/' "$workdir/rendered.yaml"
|
||||
app_version=$(sed -n 's/^appVersion: *"\{0,1\}\([^" ]*\).*/\1/p' "$chart_dir/Chart.yaml")
|
||||
image_repository=$(sed -n 's/^ repository: *//p' "$chart_dir/values.yaml")
|
||||
grep -Fq "${image_repository}:${app_version}" "$workdir/rendered.yaml"
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"replicaCount": {"type": "integer", "const": 1},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"required": ["repository", "pullPolicy"],
|
||||
"properties": {
|
||||
"repository": {"type": "string", "minLength": 1},
|
||||
"pullPolicy": {"type": "string", "enum": ["Always", "IfNotPresent", "Never"]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"origin": {"type": "string", "minLength": 1},
|
||||
"trustAuthentik": {"type": "boolean"},
|
||||
"invitationPdf": {"type": "string"},
|
||||
"invitationImage": {"type": "string"},
|
||||
"secrets": {"type": "object"},
|
||||
"persistence": {
|
||||
"type": "object",
|
||||
"required": ["enabled", "existingClaim"],
|
||||
"properties": {
|
||||
"enabled": {"type": "boolean"},
|
||||
"existingClaim": {"type": "string"},
|
||||
"size": {"type": "string"},
|
||||
"storageClassName": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"adminKey": {"type": "object"},
|
||||
"ingress": {"type": "object"},
|
||||
"resources": {"type": "object"}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: registry.brunner.ninja/feedc0de/30-birthday
|
||||
pullPolicy: Always
|
||||
|
||||
origin: http://localhost:3000
|
||||
trustAuthentik: false
|
||||
invitationPdf: /private/Einladung.pdf
|
||||
invitationImage: /private/einladung.jpg
|
||||
|
||||
secrets:
|
||||
invitation: 30-birthday-invitation
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
existingClaim: ""
|
||||
size: 128Mi
|
||||
storageClassName: ""
|
||||
|
||||
adminKey:
|
||||
secretName: ""
|
||||
secretKey: ADMIN_KEY
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
host: ""
|
||||
ingressClassName: traefik
|
||||
tlsSecretName: ""
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.priority: "10"
|
||||
adminAnnotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-authentik@kubernetescrd
|
||||
traefik.ingress.kubernetes.io/router.priority: "100"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
ephemeral-storage: 32Mi
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 192Mi
|
||||
ephemeral-storage: 128Mi
|
||||
+1
-2
@@ -5,5 +5,4 @@ set -euo pipefail
|
||||
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
"${repo_root}/k8s/create-invitation-secret.sh"
|
||||
kubectl apply --filename="${repo_root}/k8s/ci-deployer.yaml"
|
||||
kubectl apply --filename="${repo_root}/k8s/invitation.yaml"
|
||||
exec "${repo_root}/chart/install.sh"
|
||||
|
||||
Reference in New Issue
Block a user