Files
cozystack/hack/helm-release-size.bats
T
Myasnikov Daniil 7ad7d39349 test(hack): keep every chart's stored Helm release within budget
A chart can grow past the 1 MiB Helm release Secret without any test
noticing: it renders, lints and installs, and only the upgrade whose
manifest is large enough fails, on a live cluster, with a release that
stays failed. backupstrategy-controller sat under 1 KB from the limit
before it crossed it.

Estimate the stored size the way Helm encodes it (release JSON with the
chart's templates and files, gzip, base64) and fail any chart under
packages/ above 90 percent of the limit, plus the largest revision of
backupstrategy-controller with every default Strategy rendered. The
estimate counts files a .helmignore names, because the production path
stores them.

A size measured without its manifest is a lower bound that passes for
the wrong reason, so every chart must render offline except an explicit
list of charts that need cluster state, and the worst-case render must
succeed and contain the default strategies before it is measured.

Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-10-19 08:10:26 +00:00

156 lines
5.5 KiB
Bash

#!/usr/bin/env bats
# Helm keeps every release revision in one Secret, and the apiserver refuses a
# Secret over 1 MiB. A chart past that budget installs and then fails the one
# upgrade whose manifest is large enough ("Secret sh.helm.release.v1.<name>.vN
# is invalid: data: Too long"), and that release stays failed. The stored
# release carries the whole chart directory as it reaches helm-controller:
# the packages artifact applies no .helmignore, so tests/, images/ and the
# Makefile count too. hack/helm-release-size.py reproduces the encoding.
#
# The budget is 90 percent of the limit: the estimate runs about 1 percent
# above real Secrets, and the rest is room for a chart to grow by more than
# one change before anyone has to act.
#
# Harness compatibility note: CI runs this file under Bats through
# `make bats-unit-tests`. It also remains compatible with the narrower legacy
# `hack/cozytest.sh` translator, so it avoids `run`, `$status`, `$output`,
# `skip`, and setup()/teardown(). Paths stay repo-root-relative for both
# runners.
#
# Run with: bats hack/helm-release-size.bats
load test_helper
LIMIT=1048576
BUDGET=943718
# Charts that do not render offline with default values (required values,
# cluster lookups). They are measured on their stored files and templates
# alone, a lower bound. Any other chart must render: a render failure there
# would quietly shrink the measurement to the same lower bound. A chart that
# stops rendering offline on purpose goes here; one that starts rendering
# must come off, so the list never hides a renderable chart.
NO_OFFLINE_RENDER="
packages/apps/bucket
packages/apps/clickhouse
packages/apps/foundationdb
packages/apps/harbor
packages/apps/kafka
packages/apps/kubernetes
packages/apps/kubernetes-nodes
packages/apps/mariadb
packages/apps/mongodb
packages/apps/nats
packages/apps/openbao
packages/apps/opensearch
packages/apps/postgres
packages/apps/qdrant
packages/apps/rabbitmq
packages/apps/redis
packages/apps/tenant
packages/apps/valkey
packages/apps/vm-disk
packages/apps/vm-instance
packages/apps/vpc
packages/apps/vpn
packages/core/platform
packages/extra/bootbox
packages/extra/computeplane
packages/extra/etcd
packages/extra/external-dns
packages/extra/gateway
packages/extra/info
packages/extra/ingress
packages/extra/monitoring
packages/extra/seaweedfs
packages/library/cozy-lib
packages/system/kuberture
packages/system/telepresence
packages/tests/cozy-lib-tests
"
@test "the estimator counts chart files the way Helm stores them" {
tmp=$(mktemp -d)
mkdir -p "$tmp/chart/templates" "$tmp/chart/tests"
printf 'apiVersion: v2\nname: probe\nversion: 0.0.0\n' > "$tmp/chart/Chart.yaml"
# Incompressible, so the stored size cannot be smaller than its base64.
head -c 300000 /dev/urandom > "$tmp/chart/tests/blob"
size=$(printf '' | python3 -I hack/helm-release-size.py "$tmp/chart")
if [ "$size" -lt 400000 ]; then
echo "estimate $size for a chart carrying 300000 random bytes; files are not counted" >&2
false
fi
rm -rf "$tmp"
}
@test "every chart's stored release fits the budget with its default render" {
tmp=$(mktemp -d)
: > "$tmp/over"
: > "$tmp/render"
n=0
for c in packages/*/*/; do
c=${c%/}
[ -f "$c/Chart.yaml" ] || continue
n=$((n + 1))
listed=no
case "$NO_OFFLINE_RENDER" in *"
$c
"*) listed=yes ;; esac
if helm template release "$c" -n default > "$tmp/manifest" 2> "$tmp/err"; then
if [ "$listed" = yes ]; then
echo "$c renders offline now; remove it from NO_OFFLINE_RENDER in hack/helm-release-size.bats" >> "$tmp/render"
fi
elif [ "$listed" = yes ]; then
: > "$tmp/manifest"
else
echo "$c failed to render offline; fix the render, or add it to NO_OFFLINE_RENDER in hack/helm-release-size.bats if it needs cluster state: $(grep -m 1 '^Error' "$tmp/err" | cut -c1-300)" >> "$tmp/render"
continue
fi
size=$(python3 -I hack/helm-release-size.py "$c" < "$tmp/manifest")
if [ "$size" -gt "$BUDGET" ]; then
echo "$c: $size bytes (budget $BUDGET, limit $LIMIT)" >> "$tmp/over"
fi
done
if [ "$n" -lt 50 ]; then
echo "only $n charts found under packages/; the sweep checked almost nothing" >&2
false
fi
if [ -s "$tmp/render" ]; then
cat "$tmp/render" >&2
false
fi
if [ -s "$tmp/over" ]; then
echo "charts whose stored Helm release is over budget:" >&2
cat "$tmp/over" >&2
false
fi
rm -rf "$tmp"
}
@test "backupstrategy-controller fits the budget on its largest revision" {
# The default render gates every Strategy CR off. The revision that renders
# all of them, with the Velero location on as in production, is the one
# that grew past the limit once.
c=packages/system/backupstrategy-controller
tmp=$(mktemp -d)
# Rendered to a file rather than piped: in a pipe the estimator's status
# would stand for the render's, and a failed render would be measured as an
# empty manifest and pass.
if ! helm template backupstrategy-controller "$c" -n cozy-backup-controller \
--set backupStorage.bucketNameOverride=cozy-backups-0123 \
--set velero.bslEnabled=true > "$tmp/manifest"; then
echo "$c failed to render its largest revision; nothing was measured" >&2
false
fi
if ! grep -q '^kind: Kafka$' "$tmp/manifest"; then
echo "$c rendered without the default Strategy CRs; this is not its largest revision" >&2
false
fi
size=$(python3 -I hack/helm-release-size.py "$c" < "$tmp/manifest")
rm -rf "$tmp"
if [ "$size" -gt "$BUDGET" ]; then
echo "$c: $size bytes with every default Strategy rendered (budget $BUDGET, limit $LIMIT)" >&2
false
fi
}