mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-23 03:27:50 +09:00
The shell translator rewrites test bodies and loses Bats reporting. The unit lane needs complete TAP results and JUnit reports without changing how live-cluster suites run. Sourced production helpers still need a dash pass, and unit test bodies need nounset without exporting shell options into subprocesses. Assisted-by: LLM Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
178 lines
9.1 KiB
Bash
178 lines
9.1 KiB
Bash
#!/usr/bin/env bats
|
|
# -----------------------------------------------------------------------------
|
|
# Ties the kubernetes-nodes chart's default Talos image to the kubernetes-worker-image
|
|
# catalog's default golden. The two live in independently edited files:
|
|
#
|
|
# packages/apps/kubernetes-nodes/values.yaml talos.{schematicID,version}
|
|
# packages/system/kubernetes-worker-image/values.yaml images[], storageClass
|
|
#
|
|
# A worker pool that opts in with `osImage.builtin: {}` resolves the golden it
|
|
# clones from the POOL chart's talos defaults, then requires the catalog to hold a
|
|
# matching (schematicID, version) — packages/apps/kubernetes-nodes/templates/
|
|
# nodegroup.yaml fails the whole render when it does not. So a one-sided Talos bump
|
|
# silently breaks every fresh install using osImage.builtin, and nothing catches it
|
|
# until a ~95-minute e2e run does.
|
|
#
|
|
# The StorageClass is the same trap from the other direction: CDI cannot
|
|
# CSI-clone across StorageClasses (it would silently fall back to a host-assisted
|
|
# copy over the pod network), so the render also rejects a group whose
|
|
# storageClass differs from its golden's. Bumping one file's default alone brings
|
|
# that guard down on the default path.
|
|
#
|
|
# Both checks are cheap and exact, which is the point — they turn a slow, remote
|
|
# e2e failure into a local one-second unit test.
|
|
#
|
|
# Compatible with both `bats` directly and the in-repo cozytest.sh runner, which
|
|
# runs each @test in a fresh subshell with `set -u` and does not honor bats
|
|
# setup()/teardown().
|
|
|
|
load test_helper
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
|
|
APP_VALUES="$REPO_ROOT/packages/apps/kubernetes-nodes/values.yaml"
|
|
CATALOG_VALUES="$REPO_ROOT/packages/system/kubernetes-worker-image/values.yaml"
|
|
|
|
@test "worker image catalog holds a golden for the pool chart's default Talos (schematicID, version)" {
|
|
sid=$(yq '.talos.schematicID' "$APP_VALUES")
|
|
ver=$(yq '.talos.version' "$APP_VALUES")
|
|
[ -n "$sid" ] && [ "$sid" != "null" ]
|
|
[ -n "$ver" ] && [ "$ver" != "null" ]
|
|
|
|
# Any entry may match — the catalog is a list of flavors, not an ordered pair.
|
|
if ! yq '.images[] | .schematicID + " " + .version' "$CATALOG_VALUES" \
|
|
| grep -qxF "$sid $ver"; then
|
|
echo "packages/apps/kubernetes-nodes/values.yaml defaults to Talos:" >&2
|
|
echo " schematicID: $sid" >&2
|
|
echo " version: $ver" >&2
|
|
echo "but the kubernetes-worker-image catalog has no matching images[] entry:" >&2
|
|
yq '.images[] | " - " + .schematicID + " " + .version' "$CATALOG_VALUES" >&2
|
|
echo "Every worker pool using 'osImage.builtin: {}' would fail to render." >&2
|
|
echo "Add the pair to the catalog, or bump both files together." >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
@test "worker image catalog golden shares the pool chart's default StorageClass" {
|
|
sid=$(yq '.talos.schematicID' "$APP_VALUES")
|
|
ver=$(yq '.talos.version' "$APP_VALUES")
|
|
app_sc=$(yq '.storageClass' "$APP_VALUES")
|
|
|
|
# The golden's effective class is its own override when set, else the catalog
|
|
# default — mirroring how the pool chart resolves its class.
|
|
entry_sc=$(SID="$sid" VER="$ver" yq \
|
|
'.images[] | select(.schematicID == strenv(SID) and .version == strenv(VER)) | .storageClass' \
|
|
"$CATALOG_VALUES")
|
|
if [ -z "$entry_sc" ] || [ "$entry_sc" = "null" ]; then
|
|
entry_sc=$(yq '.storageClass' "$CATALOG_VALUES")
|
|
fi
|
|
|
|
if [ "$app_sc" != "$entry_sc" ]; then
|
|
echo "StorageClass mismatch on the default osImage.builtin path:" >&2
|
|
echo " packages/apps/kubernetes-nodes/values.yaml storageClass: $app_sc" >&2
|
|
echo " kubernetes-worker-image golden effective storageClass: $entry_sc" >&2
|
|
echo "CDI cannot CSI-clone across StorageClasses, so the tenant render" >&2
|
|
echo "rejects this outright. Keep the two defaults in step." >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Second family of default-drift guards on the same chart: the Talos <-> Kubernetes
|
|
# support matrix. The render guard keyed only on `talos.version`, so a pool could
|
|
# reach a different Talos release through `osImage.builtin.version` /
|
|
# `osImage.factory.version` and boot a kubelet outside its support window with no
|
|
# render failure. The table now lives once in templates/_helpers.tpl and both
|
|
# paths consult it.
|
|
#
|
|
# The symmetry is structural, not observable from a render: the matrix ships one
|
|
# row (`v1.13`) whose Kubernetes list is a superset of `values.schema.json`'s
|
|
# `version` enum, so no schema-valid pairing can make either call site fail today.
|
|
# A helm-unittest case would have to put an out-of-enum `version` in, and
|
|
# helm-unittest validates the schema, so it cannot. These two guards cover what a
|
|
# render cannot: that both call sites exist, and that the table still means
|
|
# something for the defaults the chart ships.
|
|
|
|
NODES_TEMPLATE="$REPO_ROOT/packages/apps/kubernetes-nodes/templates/nodegroup.yaml"
|
|
NODES_SCHEMA="$REPO_ROOT/packages/apps/kubernetes-nodes/values.schema.json"
|
|
|
|
@test "the support matrix is consulted for the image-resolved Talos version, not only talos.version" {
|
|
calls=$(grep -c 'include "kubernetes-nodes.assertTalosSupportsKubernetes"' "$NODES_TEMPLATE" || true)
|
|
if [ "$calls" -lt 2 ]; then
|
|
echo "nodegroup.yaml calls the Talos support-matrix guard $calls time(s); it needs one for talos.version and one for the version osImage.builtin/osImage.factory resolves to" >&2
|
|
echo "without the second call a pool keeps a supported talos.version and still boots an unsupported Talos minor through its image override, with no render failure" >&2
|
|
return 1
|
|
fi
|
|
# And the second call must be fed the RESOLVED version. Passing talos.version
|
|
# twice would satisfy the count above while checking nothing new.
|
|
if ! grep -q '"talosVersion" \$reqVersion' "$NODES_TEMPLATE"; then
|
|
echo "the second support-matrix call does not pass \$reqVersion, so it re-checks the value the first call already checked" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
@test "the support matrix covers every Kubernetes version the schema offers, for the default Talos" {
|
|
ver=$(yq '.talos.version' "$APP_VALUES")
|
|
minor=$(printf '%s\n' "$ver" | grep -oE '^v[0-9]+\.[0-9]+')
|
|
[ -n "$minor" ]
|
|
|
|
helpers="$REPO_ROOT/packages/apps/kubernetes-nodes/templates/_helpers.tpl"
|
|
# The row is a Helm dict literal: `"v1.13" (list "v1.31" "v1.32" ...)`.
|
|
row=$(grep -oE "\"${minor}\" \(list [^)]*\)" "$helpers" || true)
|
|
if [ -z "$row" ]; then
|
|
echo "the Talos support matrix in $helpers has no row for ${minor}, the default talos.version ($ver)" >&2
|
|
echo "an unlisted Talos minor passes the guard unchecked, so the guard means nothing on the shipped defaults" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Every version the schema lets an operator pick must be in that row, or the
|
|
# guard rejects a combination the API advertises as valid. Capture the enum
|
|
# first and refuse an empty read: iterating straight over the command
|
|
# substitution cannot tell "no versions" from "that query found nothing", so a
|
|
# renamed or relocated property would run the loop zero times and report
|
|
# success -- the drift this test exists to catch would be the change that
|
|
# disarms it.
|
|
enum=$(yq -r '.properties.version.enum[]' "$NODES_SCHEMA" || true)
|
|
if [ -z "$enum" ]; then
|
|
echo "could not read .properties.version.enum from $NODES_SCHEMA" >&2
|
|
echo "the enum was renamed, moved, or the file relocated; this guard cannot check the matrix without it" >&2
|
|
return 1
|
|
fi
|
|
for k in $enum; do
|
|
case "$row" in
|
|
*"\"$k\""*) ;;
|
|
*)
|
|
echo "values.schema.json offers version $k, but the Talos ${minor} matrix row does not list it:" >&2
|
|
echo " $row" >&2
|
|
echo "either the row is stale or the enum gained a version Talos ${minor} does not support" >&2
|
|
return 1 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Third guard, and the premise the first one rests on. The catalog is keyed off
|
|
# the kubernetes-nodes chart's `talos` defaults, but the parent kubernetes chart
|
|
# carries its own copy of the same block since the Phase 2 split, and its own
|
|
# values.yaml says to keep the two in sync. Nothing enforced that: the pool
|
|
# chart's `make update` copies files/versions.yaml and images/kubectl.tag from
|
|
# the parent and not the talos block, so a one-sided bump leaves the parent's
|
|
# support-matrix guard reasoning about one Talos release while every worker boots
|
|
# another — and the catalog check above would go on passing, because it only ever
|
|
# reads one of the two files.
|
|
|
|
PARENT_VALUES="$REPO_ROOT/packages/apps/kubernetes/values.yaml"
|
|
|
|
@test "the parent kubernetes chart and kubernetes-nodes agree on their Talos defaults" {
|
|
for key in version schematicID imageFactoryURL installerRepository registryMirrors; do
|
|
parent=$(yq ".talos.${key}" "$PARENT_VALUES")
|
|
pool=$(yq ".talos.${key}" "$APP_VALUES")
|
|
if [ "$parent" != "$pool" ]; then
|
|
echo "talos.${key} differs between the two charts:" >&2
|
|
echo " packages/apps/kubernetes/values.yaml: $parent" >&2
|
|
echo " packages/apps/kubernetes-nodes/values.yaml: $pool" >&2
|
|
echo "both are declared 'keep in sync' and a worker pool follows the second while the parent's support-matrix guard reads the first" >&2
|
|
return 1
|
|
fi
|
|
done
|
|
}
|