mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +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>
136 lines
6.7 KiB
Bash
136 lines
6.7 KiB
Bash
#!/usr/bin/env bats
|
|
# Unit test: the nats trust anchor reaches tenants as a key-free projection, and
|
|
# the two key-BEARING Secrets the chart creates never do.
|
|
#
|
|
# The chart issues nats-<name>-ca (CA certificate AND CA private key, the latter
|
|
# under tls.key) and nats-<name>-tls (server certificate AND server private key).
|
|
# Neither may be handed to a tenant: read access to the CA key would let the
|
|
# holder issue certificates for anything rather than merely verify the server.
|
|
#
|
|
# What a client actually needs is ca.crt alone. That is delivered by the
|
|
# CA-extraction controller as the key-free <release>.tenant-ca projection,
|
|
# selected here by LABEL rather than by name. The label is stamped only on an
|
|
# object the controller itself produced, whereas a name grant conveys whatever
|
|
# happens to occupy the name.
|
|
#
|
|
# These assertions exist because both halves are one edit away from breaking
|
|
# with a fully green chart suite, for two different reasons. The
|
|
# ResourceDefinition is rendered, by the nats-rd chart inlining cozyrds/*, but
|
|
# that chart carries neither a tests directory nor the test target
|
|
# hack/helm-unit-tests.sh looks for.
|
|
# The dashboard Role is rendered by a chart that has both, and no suite under
|
|
# packages/apps/nats/tests/ asserts on it.
|
|
#
|
|
# The grants are asserted as WHITELISTS against rendered output rather than as
|
|
# a blacklist of forbidden names over source text. A blacklist grepping for
|
|
# "{{ .Release.Name }}-ca" is defeated by quoting the scalar, by $.Release.Name,
|
|
# or by a fullname helper -- all of which grant the CA private key while the
|
|
# guard stays green.
|
|
|
|
load test_helper
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
|
|
COZYRDS="$REPO_ROOT/packages/system/nats-rd/cozyrds/nats.yaml"
|
|
CHART="$REPO_ROOT/packages/apps/nats"
|
|
|
|
# The complete set of grants the tenant holds, asserted as one value rather than
|
|
# per-dimension: a name whitelist alone passes an added label selector, and a
|
|
# label whitelist alone passes an added name. Either dimension can reach a
|
|
# key-bearing Secret -- controller.cert-manager.io/fao is stamped on both of
|
|
# them, so a selector on it is a one-line grant of the CA private key.
|
|
EXPECTED_RD_INCLUDE='[{"resourceNames":["nats-{{ .name }}-credentials"]},{"matchLabels":{"internal.cozystack.io/tenant-ca":"true"}}]'
|
|
|
|
# The dashboard template's entire RBAC output: every rule of the Role, and the
|
|
# kinds it renders. Asserted whole rather than filtered down to the rules that mention
|
|
# secrets, because every filter admits the spelling it did not anticipate: a rule
|
|
# with no resourceNames names no Secret, a rule with resources ["*"] names no
|
|
# resource, and a ClusterRole is not a Role. Each of those reaches both
|
|
# key-bearing Secrets. There is no filter that is right for all of them, and one
|
|
# comparison against the whole output needs none.
|
|
EXPECTED_ROLE_RULES='[{"apiGroups":[""],"resources":["services"],"resourceNames":["nats-test"],"verbs":["get","list","watch"]},{"apiGroups":[""],"resources":["secrets"],"resourceNames":["nats-test-credentials"],"verbs":["get","list","watch"]},{"apiGroups":["cozystack.io"],"resources":["workloadmonitors"],"resourceNames":["nats-test"],"verbs":["get","list","watch"]}]'
|
|
EXPECTED_RBAC_KINDS='["Role","RoleBinding"]'
|
|
|
|
# Subsumed by the whitelist below, which cannot match without this entry. Kept
|
|
# because the runner stops at the first failing test, so this one fires first
|
|
# and says what the loss costs rather than printing two JSON blobs to diff.
|
|
@test "nats-rd cozyrds selects the key-free tenant-CA projection by label" {
|
|
if [ ! -f "$COZYRDS" ]; then
|
|
echo "ResourceDefinition not found at $COZYRDS -- did it move?" >&2
|
|
exit 1
|
|
fi
|
|
|
|
count=$(yq eval \
|
|
'[.spec.secrets.include[] | select(.matchLabels."internal.cozystack.io/tenant-ca" == "true")] | length' \
|
|
"$COZYRDS") || exit 1
|
|
|
|
if [ "$count" -lt 1 ]; then
|
|
echo 'nats-rd does not select internal.cozystack.io/tenant-ca: "true"' >&2
|
|
echo "Without it the tenant cannot read ca.crt and TLS verification is impossible." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
@test "nats-rd cozyrds grants nothing beyond the credentials Secret and the trust anchor" {
|
|
if [ ! -f "$COZYRDS" ]; then
|
|
echo "ResourceDefinition not found at $COZYRDS -- did it move?" >&2
|
|
exit 1
|
|
fi
|
|
|
|
include=$(yq eval --output-format=json --indent=0 '.spec.secrets.include' "$COZYRDS") || exit 1
|
|
|
|
if [ "$include" != "$EXPECTED_RD_INCLUDE" ]; then
|
|
echo "Unexpected nats-rd spec.secrets.include: $include" >&2
|
|
echo "Expected exactly: $EXPECTED_RD_INCLUDE" >&2
|
|
echo "nats-<name>-ca holds the CA private key and nats-<name>-tls the server key;" >&2
|
|
echo "neither may be granted to a tenant, by name or by label." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Asserted against RENDERED output, so the spelling used in the template --
|
|
# quoted, helper-derived, or otherwise -- cannot smuggle a name past the check.
|
|
@test "nats dashboard Role renders exactly the grants it is supposed to" {
|
|
if [ ! -d "$CHART" ]; then
|
|
echo "Chart not found at $CHART -- did it move?" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# stderr is deliberately not discarded: when the render breaks, helm's own
|
|
# message is the only thing that says why, and a CI red showing just
|
|
# "helm template failed" sends the reader back here to rerun it by hand.
|
|
rendered=$(helm template nats-test "$CHART" \
|
|
--namespace tenant-test \
|
|
--set '_cluster.cluster-domain=cozy.local' \
|
|
--show-only templates/dashboard-resourcemap.yaml) || {
|
|
echo "helm template failed for $CHART" >&2
|
|
exit 1
|
|
}
|
|
|
|
# eval-all aggregates the documents into one result, so the Role may sit at any
|
|
# position among them.
|
|
kinds=$(printf '%s\n' "$rendered" | yq eval-all --output-format=json --indent=0 \
|
|
'[.kind]' -) || exit 1
|
|
|
|
if [ "$kinds" != "$EXPECTED_RBAC_KINDS" ]; then
|
|
echo "Unexpected RBAC kinds rendered by the nats dashboard template: $kinds" >&2
|
|
echo "Expected exactly: $EXPECTED_RBAC_KINDS" >&2
|
|
echo "A ClusterRole here would grant across namespaces and is not covered by" >&2
|
|
echo "the rule comparison below, which reads the namespaced Role." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Fails closed on an empty list: a Role that stopped granting anything, or a
|
|
# render that silently produced nothing, does not match the expected rules.
|
|
rules=$(printf '%s\n' "$rendered" | yq eval-all --output-format=json --indent=0 \
|
|
'[select(.kind == "Role") | .rules[]]' -) || exit 1
|
|
|
|
if [ "$rules" != "$EXPECTED_ROLE_RULES" ]; then
|
|
echo "Unexpected rules in the nats dashboard Role: $rules" >&2
|
|
echo "Expected exactly: $EXPECTED_ROLE_RULES" >&2
|
|
echo "nats-<release>-ca holds the CA PRIVATE KEY and nats-<release>-tls the" >&2
|
|
echo "server private key. The trust anchor reaches the tenant as the key-free" >&2
|
|
echo "<release>.tenant-ca projection via tenantsecrets, not by a Role grant." >&2
|
|
exit 1
|
|
fi
|
|
}
|