135 lines
5.2 KiB
Bash
135 lines
5.2 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
#
|
||
|
|
# Renders the chart across a matrix of configurations and checks each result.
|
||
|
|
#
|
||
|
|
# Two things are being verified, and `helm lint` only covers the first:
|
||
|
|
#
|
||
|
|
# 1. Every configuration that should work renders to YAML that *parses*. A
|
||
|
|
# template bug that only triggers under a non-default value produces valid
|
||
|
|
# YAML in review and an unapplyable manifest in production, so each render is
|
||
|
|
# fed back through a YAML parser rather than trusted because helm exited zero.
|
||
|
|
#
|
||
|
|
# 2. Every configuration that should be refused is refused, and the refusal says
|
||
|
|
# which value to change. A validation guard that silently accepts a bad value
|
||
|
|
# is worse than no guard, because it moves the failure to run time.
|
||
|
|
#
|
||
|
|
# The matrix lives in a script rather than in the Makefile because it is a list of
|
||
|
|
# cases, and a list is easier to read, review and extend as shell than as make.
|
||
|
|
|
||
|
|
set -uo pipefail
|
||
|
|
|
||
|
|
CHART="${CHART:-deploy/helm/go-bananas}"
|
||
|
|
RELEASE="${RELEASE:-go-bananas}"
|
||
|
|
|
||
|
|
pass=0
|
||
|
|
fail=0
|
||
|
|
work="$(mktemp -d)"
|
||
|
|
trap 'rm -rf "$work"' EXIT
|
||
|
|
|
||
|
|
# yaml_ok verifies that a rendered file is parseable YAML.
|
||
|
|
yaml_ok() {
|
||
|
|
python3 -c '
|
||
|
|
import sys
|
||
|
|
try:
|
||
|
|
import yaml
|
||
|
|
except ImportError:
|
||
|
|
sys.exit(3)
|
||
|
|
list(yaml.safe_load_all(open(sys.argv[1])))
|
||
|
|
' "$1" 2>/dev/null
|
||
|
|
}
|
||
|
|
|
||
|
|
# ok renders a configuration that must succeed.
|
||
|
|
ok() {
|
||
|
|
local desc="$1"
|
||
|
|
shift
|
||
|
|
if ! helm template "$RELEASE" "$CHART" "$@" >"$work/out.yaml" 2>"$work/err.txt"; then
|
||
|
|
printf 'FAIL %s (should have rendered)\n' "$desc"
|
||
|
|
sed -n '1,3p' "$work/err.txt" | sed 's/^/ /'
|
||
|
|
fail=$((fail + 1))
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
yaml_ok "$work/out.yaml"
|
||
|
|
case $? in
|
||
|
|
0) printf 'ok %s\n' "$desc"; pass=$((pass + 1)) ;;
|
||
|
|
3) printf 'ok %s (PyYAML absent, skipped the parse check)\n' "$desc"; pass=$((pass + 1)) ;;
|
||
|
|
*) printf 'FAIL %s (rendered output is not valid YAML)\n' "$desc"; fail=$((fail + 1)) ;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
# rejected renders a configuration that must fail, and requires the message to name
|
||
|
|
# the problem rather than being a bare stack trace.
|
||
|
|
rejected() {
|
||
|
|
local desc="$1"
|
||
|
|
shift
|
||
|
|
if helm template "$RELEASE" "$CHART" "$@" >"$work/out.yaml" 2>"$work/err.txt"; then
|
||
|
|
printf 'FAIL %s (should have been refused but rendered)\n' "$desc"
|
||
|
|
fail=$((fail + 1))
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
if grep -qE 'execution error|Error:' "$work/err.txt"; then
|
||
|
|
printf 'ok %s (refused: %s)\n' "$desc" \
|
||
|
|
"$(grep -oE '(execution error.*|Error: .*)' "$work/err.txt" | head -1 | cut -c1-100)"
|
||
|
|
pass=$((pass + 1))
|
||
|
|
else
|
||
|
|
printf 'FAIL %s (refused, but without an actionable message)\n' "$desc"
|
||
|
|
sed -n '1,3p' "$work/err.txt" | sed 's/^/ /'
|
||
|
|
fail=$((fail + 1))
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "== configurations that must render =="
|
||
|
|
ok "default values"
|
||
|
|
ok "oidc with an existing secret" \
|
||
|
|
--set identity.oidc.generateSecret=false \
|
||
|
|
--set identity.oidc.existingSecret=go-bananas-oidc
|
||
|
|
ok "oidc with a generated secret" \
|
||
|
|
--set identity.mode=oidc \
|
||
|
|
--set config.publicBaseUrl=https://themes.example.com
|
||
|
|
ok "production over https" \
|
||
|
|
--set config.environment=production \
|
||
|
|
--set config.publicBaseUrl=https://themes.example.com
|
||
|
|
ok "identity disabled" --set identity.mode=disabled
|
||
|
|
ok "development bypass" --set identity.mode=dev
|
||
|
|
ok "ingress with TLS" \
|
||
|
|
--set ingress.enabled=true \
|
||
|
|
--set ingress.hosts[0].host=themes.example.com \
|
||
|
|
--set ingress.tls[0].secretName=themes-tls \
|
||
|
|
--set ingress.tls[0].hosts[0]=themes.example.com
|
||
|
|
ok "ephemeral storage" --set persistence.type=emptyDir
|
||
|
|
ok "existing claim" --set persistence.existingClaim=bananas-data
|
||
|
|
ok "autoscaling and disruption budget" \
|
||
|
|
--set autoscaling.enabled=true --set podDisruptionBudget.enabled=true
|
||
|
|
ok "network policy" --set networkPolicy.enabled=true
|
||
|
|
ok "service monitor" --set metrics.serviceMonitor.enabled=true
|
||
|
|
ok "image pinned by digest" --set image.digest=sha256:deadbeef
|
||
|
|
ok "image in another registry" --set image.repository=quay.io/urpi/go-bananas
|
||
|
|
ok "base path" --set config.basePath=/themes
|
||
|
|
ok "extra environment" --set 'extraEnv[0].name=FOO' --set 'extraEnv[0].value=bar'
|
||
|
|
ok "snapshot cronjob" --set persistence.snapshot.enabled=true
|
||
|
|
|
||
|
|
echo
|
||
|
|
echo "== configurations that must be refused =="
|
||
|
|
rejected "both an existing and a generated secret" \
|
||
|
|
--set identity.oidc.existingSecret=go-bananas-oidc
|
||
|
|
rejected "neither an existing nor a generated secret" \
|
||
|
|
--set identity.oidc.generateSecret=false
|
||
|
|
rejected "an empty public base URL" \
|
||
|
|
--set config.publicBaseUrl=
|
||
|
|
rejected "a public base URL with a trailing slash" \
|
||
|
|
--set config.publicBaseUrl=https://themes.example.com/
|
||
|
|
rejected "a public base URL that is not a URL" \
|
||
|
|
--set config.publicBaseUrl=themes.example.com
|
||
|
|
rejected "TLS verification disabled in production" \
|
||
|
|
--set identity.oidc.skipTLSVerify=true --set config.environment=production
|
||
|
|
rejected "an unknown identity mode" --set identity.mode=nonsense
|
||
|
|
rejected "an OIDC mode with no issuer" --set identity.mode=oidc --set identity.oidc.issuer=
|
||
|
|
rejected "a shutdown timeout longer than the grace period" \
|
||
|
|
--set config.shutdownTimeoutMs=60000 --set deployment.terminationGracePeriodSeconds=30
|
||
|
|
rejected "an unknown top-level value" --set thisKeyDoesNotExist=1
|
||
|
|
|
||
|
|
echo
|
||
|
|
if [ "$fail" -gt 0 ]; then
|
||
|
|
printf '%d of %d checks failed\n' "$fail" "$((pass + fail))"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
printf 'all %d chart checks passed\n' "$pass"
|