Fix CI/CD
Validate and deploy Kubernetes Dashboard / validate (push) Successful in 17s
Validate and deploy Kubernetes Dashboard / deploy (push) Successful in 24s

This commit is contained in:
2026-09-05 18:17:06 +02:00
parent 1daf39f4fc
commit 9ae5e84d97
5 changed files with 105 additions and 4 deletions
+7 -3
View File
@@ -65,9 +65,13 @@ jobs:
--filename auth-proxy.yaml \
--filename kubernetes-dashboard-ingressroute.yaml
for deployment in kubernetes-dashboard-api kubernetes-dashboard-auth kubernetes-dashboard-web kubernetes-dashboard-metrics-scraper kubernetes-dashboard-kong kubernetes-dashboard-auth-proxy; do
kubectl --namespace "${NAMESPACE}" rollout status "deployment/${deployment}" --timeout=10m
done
bash ./wait-for-deployments.sh \
kubernetes-dashboard-api \
kubernetes-dashboard-auth \
kubernetes-dashboard-web \
kubernetes-dashboard-metrics-scraper \
kubernetes-dashboard-kong \
kubernetes-dashboard-auth-proxy
status=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' --max-time 15 https://kubernetes.brunner.ninja/)
case "${status}" in
+7
View File
@@ -18,6 +18,11 @@ automatically every hour; it is never stored in Git, a static Secret, Gitea, or
Traefik configuration. Supplying the Authorization header makes Dashboard skip
its bearer-token login page.
Every Dashboard container retains a CPU request, right-sized to `25m` from an
observed idle use of roughly `1-2m`. Requests are not removed: they still give
the scheduler a placement signal while the higher CPU limits leave room for
interactive bursts.
This preserves the previous cluster-admin behavior, but Kubernetes audit events
identify every Dashboard action as the same service account. Native Authentik
OIDC would provide per-user identity, but requires kube-apiserver configuration
@@ -44,6 +49,8 @@ Then run `./create-ci-kubeconfig.sh` and store its final line as the Gitea
Actions secret `KUBE_CONFIG_BASE64`. The CI identity can update only named,
already-existing Dashboard objects. It cannot read Secrets, edit the
`admin-user` cluster-admin binding, create resources, or delete resources.
Deployment readiness is checked by polling those exact object names; the CI
identity deliberately has no namespace-wide Deployment `list/watch` access.
The deploy workflow runs only for `main`; pull requests and all pushes run the
validation job. The chart's generated CSRF Secret is created by the bootstrap
+33 -1
View File
@@ -20,6 +20,14 @@ auth:
scaling:
replicas: 2
revisionHistoryLimit: 3
containers:
resources:
requests:
cpu: 25m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
api:
image:
@@ -28,6 +36,14 @@ api:
scaling:
replicas: 2
revisionHistoryLimit: 3
containers:
resources:
requests:
cpu: 25m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
web:
image:
@@ -36,6 +52,14 @@ web:
scaling:
replicas: 2
revisionHistoryLimit: 3
containers:
resources:
requests:
cpu: 25m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
metricsScraper:
enabled: true
@@ -44,6 +68,14 @@ metricsScraper:
tag: 1.2.2
scaling:
revisionHistoryLimit: 3
containers:
resources:
requests:
cpu: 25m
memory: 200Mi
limits:
cpu: 250m
memory: 400Mi
# Browser TLS terminates at Traefik. Kong still uses TLS because the private
# auth proxy sends the privileged projected bearer token on this hop.
@@ -55,7 +87,7 @@ kong:
replicaCount: 2
resources:
requests:
cpu: 100m
cpu: 25m
memory: 256Mi
limits:
cpu: 500m
+14
View File
@@ -11,6 +11,7 @@ bash -n \
"${project_dir}/pin-images.sh" \
"${project_dir}/render.sh" \
"${project_dir}/create-ci-kubeconfig.sh" \
"${project_dir}/wait-for-deployments.sh" \
"${project_dir}/test.sh"
awk -f "${project_dir}/filter-secrets.awk" /dev/null >/dev/null
@@ -34,11 +35,24 @@ if grep -Fq 'namespace: default' "${project_dir}/kubernetes-dashboard-ingressrou
echo "Dashboard route must not use a cross-namespace middleware" >&2
exit 1
fi
if grep -Fq 'rollout status' "${project_dir}/.gitea/workflows/deploy.yml"; then
echo "Restricted CI must poll exact Deployment names instead of list/watch" >&2
exit 1
fi
grep -Fq 'expirationSeconds: 3600' "${project_dir}/auth-proxy.yaml"
grep -Fq 'proxy_pass https://kubernetes-dashboard-kong-proxy' "${project_dir}/auth-proxy.yaml"
grep -Fq 'containerPort: 8443' "${workdir}/dashboard.yaml"
grep -Fq 'dashboard-api:1.14.0@sha256:2bd14c0ffee99d15fb1595644ebd1083ac32c5157c6e6fd8615b0f556a1390c2' "${workdir}/dashboard.yaml"
request_count=$(awk '
$1 == "cpu:" && $2 == "25m" { count++ }
END { print count + 0 }
' "${workdir}/dashboard.yaml")
if [[ "${request_count}" -lt 5 ]] || grep -Fq 'cpu: 100m' "${workdir}/dashboard.yaml"; then
echo "Expected every Dashboard chart component to request only 25m CPU" >&2
exit 1
fi
image=ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c
for file in \
"${workdir}/dashboard.yaml" \
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
namespace=${NAMESPACE:-kubernetes-dashboard}
timeout_seconds=${DEPLOYMENT_TIMEOUT_SECONDS:-600}
poll_seconds=${DEPLOYMENT_POLL_SECONDS:-5}
if [[ "$#" -eq 0 ]]; then
echo "Usage: wait-for-deployments.sh DEPLOYMENT..." >&2
exit 2
fi
for deployment in "$@"; do
deadline=$((SECONDS + timeout_seconds))
state="not observed"
complete=false
while ((SECONDS < deadline)); do
state=$(kubectl --namespace "${namespace}" get "deployment/${deployment}" \
--output=jsonpath='{.metadata.generation}{"|"}{.status.observedGeneration}{"|"}{.spec.replicas}{"|"}{.status.updatedReplicas}{"|"}{.status.readyReplicas}{"|"}{.status.availableReplicas}{"\n"}')
IFS='|' read -r generation observed desired updated ready available <<< "${state}"
observed=${observed:-0}
desired=${desired:-0}
updated=${updated:-0}
ready=${ready:-0}
available=${available:-0}
if [[ "${observed}" -ge "${generation}" \
&& "${updated}" -eq "${desired}" \
&& "${ready}" -eq "${desired}" \
&& "${available}" -eq "${desired}" ]]; then
echo "deployment/${deployment} is ready (${ready}/${desired})"
complete=true
break
fi
sleep "${poll_seconds}"
done
if [[ "${complete}" != true ]]; then
echo "Timed out waiting for deployment/${deployment}; generation observed desired updated ready available: ${state}" >&2
exit 1
fi
done