Skip to content

Commit 99de79f

Browse files
authored
fix(cdi): pin the CDI release kubevirt-cdi is reconciled against and stop update overwriting it (#4107)
## What this PR does `packages/system/kubevirt-cdi` resolved its CDI version at run time from GitHub's `releases/latest` redirect and wrote the result straight over `templates/cdi-cr.yaml`. That file is not vendored. Upstream ships a bare CDI object; ours adds Helm templating for `cloneStrategyOverride`, `uploadProxyURL` and the worker-pod `podResourceRequirements`, the `ExpandDisks` feature gate, a `CriticalAddonsOnly` toleration, and the two RBAC documents that let a tenant clone from the golden-image namespace. I diffed the two at v1.66.1 to write that list, so it is complete as of that release rather than only hedged: upstream ships 283 bytes with one feature gate, two nodeSelectors and an `imagePullPolicy`, and we are a strict superset of it. Every entry arrived by hand, so `wget -O templates/cdi-cr.yaml` was not refreshing a vendored file, it was overwriting a first-party one. Same loss as the `rm -rf templates` that #4097 removes, arriving through the fetch instead of the wipe. `update` now pins the release the object is reconciled against, and fetches it only to confirm the bytes still hash to a digest I measured. Nothing is written under `templates/`. At the next CDI bump whose asset differs, that check fails, which is the point: upstream moving its own defaults is when the local copy needs a human to reconcile it, not an overwrite. The download is kept on a mismatch, since reconciling one starts by reading it. `RELEASE` is `v1.66.1`, which is the release `kubevirt-cdi-operator` vendors, so the pin and the deployed version agree. What the platform deploys is decided by that package, and it can move to another release without this file changing, so this line is pointed at it by hand. `update` then checks the two agree: it compares `RELEASE` with the `cdi-operator` image tag that package vendors and fails naming both versions when they differ, because otherwise its closing line would report a release the platform may no longer run. That is a check and not a derivation, so `RELEASE` stays a pin a human moves. Upstream publishes no checksum for the asset, so the pin comes from a download rather than from a published file: `a497f90de608c1df26f9ee4095289373f17132d74a2042ca03e00c17c964f8a7`. It catches an asset that moved under a tag that should be immutable. It is not authenticity verification, and the comment beside it says so. The digest command is chosen when `update` expands it, `sha256sum` when it is on PATH and `shasum -a 256` otherwise. Which one ran here is worth stating, because the usual claim about macOS is out of date: this machine picked `sha256sum`, and that binary is `/sbin/sha256sum`, a native Darwin one rather than GNU coreutils. It reports `sha256sum (Darwin) 1.0` and prints the same `<digest> <path>` format. The package already had a `test:` target, from `0418577f5`, so what it gains here is the `.PHONY` line that commit did not add. `hack/package.mk` declares only the nine targets it defines itself, so without it a file or directory named `test` or `update` beside the Makefile makes make consider that target up to date and skip the recipe in silence. The new suite pins the `ExpandDisks` gate, the toleration, `podResourceRequirements`, the `uploadProxyURL` override and the clone RBAC, and renders the upload-proxy Ingress and TLSRoute, each under the `gateway-enabled` value that selects it. The clone-strategy override is pinned by `tests/clone_strategy_test.yaml`, which is already on `main`. One consequence of the pin move is a claim rather than a number, so it gets its own line. Upstream carried `WebhookPvcRendering` from v1.63.0 through v1.65.0 and dropped it by v1.66.0; the local object never carried it, so the divergence ran both ways across those three releases and one way since. The comment says to re-derive the delta at each bump instead of trusting it, because that was a fact about a span of releases and not about CDI. `packages/system/kubevirt-cdi/Makefile` carried two identical `test:` targets, one from `0418577f5` and one appended by a later merge, so make printed `overriding commands for target 'test'` on every invocation in the package. The second sat inside the span this recipe rewrites, so it goes with the rewrite rather than being re-authored into it. The comment kept above it is the reason a reader should not add another. Closes #4157. ### Scope An earlier revision also pinned and tested `kubevirt-cdi-operator`. #3920 has since landed the two real bugs in that recipe, the patch strip level and `patches/tolerations.diff`, so that half is dropped here rather than merged twice. Pinning and testing the operator package is #4190. Named improvements left alone, so they read as choices. - The two upload-proxy tests each render their route under the `gateway-enabled` value that selects it and neither asserts the other is absent; the templates already settle that with `ne "true"` and `eq "true"` on the same variable, and `tests/standalone_render_test.yaml` already asserts each route absent under the values that select the other. So the package does cover it; these two cases just do not, and a third copy would not add anything. - The CDI object carries no version string for a rendered-output assertion, so `RELEASE` is checked by `make update` rather than by `make test`. - Neither route case pins the backend service port, 443 in both templates; that is outside what the suite claims. - The template-loss guard still counts `cdi-cr.yaml` as named, because the download line in `update` mentions the stem, so re-adding a wipe here would have it report two files instead of three; the guard's header already says a mention is not proof of a write, and the case needs a wipe this recipe no longer has. - The suite pins the local additions by design, and most of the upstream-derived half is pinned by nothing: `imagePullPolicy` and the two `nodeSelector` blocks have no assertion anywhere under `tests/`. `HonorWaitForFirstConsumer` is the exception and only as a side effect: `featureGates` is pinned whole, so that gate rides along with `ExpandDisks`. The rest are maintained by the same hand reconciliation the pin forces whenever the upstream asset changes, so a carry-over that drops one of them is green. Three asserts would close what is left. - One more, remote enough to name rather than code around: if the digest tool is missing entirely, `$$actual` comes out empty, the comparison against `CR_SHA256` fails, and the error text diagnoses a moved asset when the real cause is a machine with neither `sha256sum` nor `shasum`. Busybox ships the first and macOS ships the second, so nobody has hit it. Closing it means an executable line, which would then owe mutation coverage of its own, and that is disproportionate to a case with no known instance. The mechanism is written here so the next reader does not have to re-derive it from an error message that points the wrong way. - Separately, the per-package `.PHONY` gap this Makefile's own line argues against is not specific to this package: most package Makefiles do not declare their own `test:` or `update:` phony, and #4214 tracks that. - A key that does not exist yet is not held either: every item in the delta is pinned whole, but a NEW key appearing at `spec` or `spec.config` level passes unnoticed, and such a key can change behaviour rather than sit inert. I confirmed it on a copy extracted from this commit, appending `spec.config.filesystemOverhead.global: "0.99"` and getting a green suite. That is the shape to recognise at the next bump: not a changed value, which is caught, but a new sibling key beside the ones we pin. - And `spec.config.podResourceRequirements` is now pinned whole here and by four leaves in `tests/importer-resources_test.yaml`: the whole-map form is the stronger one and the leaves are a subset of it, so changing the default takes two edits and the second failure reads as a surprise until you know why both exist. - If the operator's image reference ever gains a digest (`:v1.66.1@sha256:...`), `${deployed##*:}` takes the digest's hex instead of the tag, and `update` fails with a loud false mismatch rather than passing silently. - Deriving `RELEASE` with `$(shell ...)` from the sibling manifest was suggested and declined on merits: a derived value is not a pin, the point is that a human chooses which CDI release this object is reconciled against, and computing it from whatever the neighbour last vendored hides that choice from the diff. The operator comparison lives in `update` and not in `test:`, even though `kubevirt-operator` ties its own manifest to the tree from `test:`. A mismatch calls for a hand reconciliation, and `update` is the recipe that asks for one; it is also where the success line that would otherwise name the wrong release is printed. I checked it on a copy extracted from this commit. With the operator's image tag moved to v1.67.0, `make update` exits 2 and names both versions. With the tags equal it passes. With the check's `exit 1` removed, the moved tag passes again, so the red comes from the check. Worth knowing when reading the mismatch path: `kubevirt-operator` deletes its download on a hash mismatch and this package keeps it. That is deliberate here, since reconciling a moved asset starts by reading it, but the two packages do read oppositely and neither file says so. ### Base and ordering This stacks on #4097 and should land after it. Both edit `packages/system/kubevirt-cdi/Makefile`, and this change builds directly on that one's edit to the same recipe, so it is based on the head of `fix/cdi-update-keeps-local-templates` rather than on `main`. GitHub computes mergeability against the PR's base branch, which here is that stack branch and not `main`. Both branches are rebased onto current `main`, which has not touched these paths since the previous merge-base. ### Screenshots No UI changes. ### Downstream repositories Walked the trigger map against the diff. Two entries come close and neither fires. The website triggers on developer tooling and package Makefiles, but `content/en/docs/next/development.md` documents `make update` only as "Update Helm chart and versions from the upstream source", which still holds, and its worked example is cilium, which this does not touch. Its `make test` line refers to the e2e sandbox in `packages/core/testing`, not per-package unit tests. `ccp` triggers on `hack/` layout and on what a make target does, and nothing under `hack/` is touched. Its `package-bump` skill already assumes a package's `update` recipe hardcodes the upstream version instead of resolving it at run time, and names cilium, kafka-operator, opensearch-operator and postgres-operator as examples. `kubevirt-cdi` was one of those counterexamples and now is not. Pinning `kubevirt-cdi-operator` the same way is #4190 and out of scope here. The skill handles vendored Helm charts, in-repo image builds and version enums, and a raw manifest download is none of those. Everything else in the map needs an app, a schema, an installer value, a variant or a release asset. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: ### Release note ```release-note fix(cdi): `make update` in kubevirt-cdi no longer overwrites the hand-maintained CDI object. It pins the CDI release that object is reconciled against instead of resolving GitHub's releases/latest at run time, and verifies the fetched asset against a pinned sha256 without writing under templates/. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Reliability** * Updates now use verified, pinned CDI releases to prevent unintended manifest changes. * Failed or checksum-mismatched downloads no longer overwrite existing configuration. * **Bug Fixes** * Preserved local CDI customizations during release updates. * Added validation for CDI resources, access controls, upload-proxy routing, and TLS configuration. * **Tests** * Added automated checks for incomplete template refreshes and generated Kubernetes resources. * Added chart tests covering CDI behavior, clone permissions, and upload-proxy networking. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents b1519ac + 225b639 commit 99de79f

4 files changed

Lines changed: 241 additions & 38 deletions

File tree

‎hack/package-update-templates.bats‎

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@
44
#
55
# `update` refetches a package's vendored upstream manifests, and several
66
# recipes open by deleting templates/ to guarantee a clean slate. That is safe
7-
# only while every file under templates/ comes back from the recipe. templates/
8-
# is an ordinary chart directory, so a package can also keep first-party
9-
# manifests there -- kubevirt-cdi keeps the CDI upload-proxy Ingress and
10-
# TLSRoute -- and the recipe knows nothing about those. The wipe takes them and
11-
# `make update` exits 0, so the deletion lands in the diff with nothing marking
12-
# it. Whether anything downstream notices depends on the package carrying a
13-
# helm-unittest suite that names the file, and where none does the loss is
14-
# silent all the way to a cluster. That is a property of the package, not of
15-
# the tree, so it is worth re-deriving rather than reading: at the time of
16-
# writing kubevirt-cdi has such a suite and a deletion there fails `make test`
17-
# as well, while some packages that clear their own templates/ declare no
18-
# `test` target at all (#3631 tracks packages without one).
7+
# only while every file under templates/ comes back from the recipe.
8+
# templates/ is an ordinary chart directory, so a package can also keep
9+
# first-party manifests there, and the recipe knows nothing about them. The
10+
# wipe takes them and `make update` exits 0, so the deletion lands in the diff
11+
# with nothing marking it. Whether anything downstream notices depends on the
12+
# package carrying a helm-unittest suite that names the file, and where none
13+
# does the loss is silent all the way to a cluster. That is a property of the
14+
# package, not of the tree, so it is worth re-deriving rather than reading: at
15+
# the time of writing kubevirt-cdi has such a suite and a deletion there fails
16+
# `make test` as well, while some packages that clear their own templates/
17+
# declare no `test` target at all (#3631 tracks packages without one).
1918
#
2019
# The invariant: a recipe that clears templates/ must NAME every file tracked
2120
# under it. Both halves are approximations of the shell the recipe would run,
@@ -269,9 +268,8 @@ unnamed_templates() {
269268

270269
# A tab-indented comment inside the recipe is not shell. A recipe that
271270
# deliberately does not clear templates/ tends to carry a line saying why,
272-
# and searching that line would fail that very package. Both
273-
# spellings are pinned because make accepts both and the tree writes both:
274-
# `@#` in kubevirt-operator's own update recipe, bare `#` everywhere else.
271+
# and searching that line would fail that very package. Both spellings
272+
# are pinned because make accepts both and the tree writes both.
275273
for lead in '#' '@#'; do
276274
printf 'update:\n\tmkdir -p templates\n\t%s deliberately no rm -rf templates here: the upload-proxy files are ours\n' \
277275
"$lead" > "$tmp/pkg/Makefile"
@@ -341,10 +339,10 @@ unnamed_templates() {
341339
exit 1
342340
fi
343341

344-
# The other order, and the one this tree actually writes: kubevirt-cdi puts
345-
# `test:` below its `update:` recipe. Above, `r` is still 0 and the gate
346-
# does the work; here the recipe has already started and only the
347-
# terminator can stop it, so without this case that half is unpinned.
342+
# The other order, and one the tree writes: a `test:` below the `update:`
343+
# recipe. Above, `r` is still 0 and the gate does the work; here the recipe
344+
# has already started and only the terminator can stop it, so without this
345+
# case that half is unpinned.
348346
printf 'update:\n\tmkdir -p templates\nclean:\n\trm -rf templates\n' \
349347
> "$tmp/pkg/Makefile"
350348
if [ -n "$(wipe_command "$tmp/pkg/Makefile")" ]; then
@@ -383,8 +381,8 @@ unnamed_templates() {
383381
exit 1
384382
fi
385383

386-
# And the shape kubevirt-cdi moved to switches the check off, so the scan
387-
# is measuring the removal rather than the package.
384+
# And a recipe that only creates the directory switches the check off, so
385+
# the scan is measuring the removal rather than the package.
388386
printf 'update:\n\tmkdir -p templates\n\twget -O templates/fetched.yaml https://example.invalid/f.yaml\n' \
389387
> "$tmp/pkg/Makefile"
390388
if [ -n "$(wipe_command "$tmp/pkg/Makefile")" ]; then
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `update` fetches here to check the pin, and keeps the file on a mismatch.
2+
# The recipe's error text says why.
3+
/cdi-cr.yaml.download
Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,81 @@
11
export NAME=kubevirt-cdi
22
export NAMESPACE=cozy-$(NAME)
33

4+
# The CDI release templates/cdi-cr.yaml is reconciled against. Moving to a
5+
# newer CDI is an edit to this line. What the platform actually deploys is set
6+
# by ../kubevirt-cdi-operator, so this line does not track that one: it has to
7+
# be pointed at it by hand, and `update` fails while the two disagree.
8+
# Deriving this from the sibling manifest instead would end the pin: the point
9+
# is that a human chooses which release this object is reconciled against, and
10+
# a computed value hides that choice from the diff. A check leaves the choice
11+
# here and only refuses to call a stale one current.
12+
RELEASE = v1.66.1
13+
14+
# sha256 of the cdi-cr.yaml release asset as published. Upstream publishes no
15+
# checksum for it, so the pin is taken from a download rather than fetched
16+
# alongside one: it detects an asset that moved under its tag, and it is not
17+
# authenticity verification.
18+
CR_SHA256 = a497f90de608c1df26f9ee4095289373f17132d74a2042ca03e00c17c964f8a7
19+
20+
# sha256sum is not everywhere: current macOS carries a native one in /sbin,
21+
# older releases carry only shasum(1). Both print "<digest> <path>", so field
22+
# 1 is the digest whichever gets picked.
23+
SHA256SUM = $(shell command -v sha256sum >/dev/null 2>&1 && echo sha256sum || echo 'shasum -a 256')
24+
425
include ../../../hack/package.mk
526

6-
# hack/package.mk defines no `test` target and hack/helm-unit-tests.sh only runs
7-
# a package whose `make -n test` resolves, so a tests/ directory without this
8-
# line is collected by nothing and the suite is skipped in silence.
27+
# Phony because hack/package.mk's .PHONY line lists only the targets that file
28+
# defines: with neither name declared, a path named `test` or `update` next to
29+
# this Makefile would make that target up to date and skip the recipe.
30+
.PHONY: test update
31+
32+
# hack/package.mk defines no `test` target and hack/helm-unit-tests.sh only
33+
# runs a package whose `make -n test` resolves, so a tests/ directory with no
34+
# `test:` target is collected by nothing and the suite is skipped in silence.
935
test:
1036
helm unittest .
1137

12-
# Nothing under templates/ here is purely vendored. The upload-proxy Ingress
13-
# and TLSRoute are ours and no fetch recreates them, so clearing the directory
14-
# would lose them outright. It is not needed either: wget -O truncates the one
15-
# file it writes and leaves the rest.
38+
# templates/cdi-cr.yaml is ours, not upstream's: it adds to upstream's bare CDI
39+
# object, and what it adds is pinned under tests/ rather than restated here.
40+
# The delta is a fact about one release and not a standing property of CDI:
41+
# upstream carried WebhookPvcRendering from v1.63.0 through v1.65.0 and dropped
42+
# it by v1.66.0, and this object never carried it at all, so the difference ran
43+
# both ways for three releases and one way after. Re-derive it at each bump
44+
# instead of inheriting it.
1645
#
17-
# cdi-cr.yaml is ours too, despite being named after an upstream asset: it
18-
# carries Helm templating and RBAC that no fetch reproduces. The fetch below
19-
# still overwrites it, so a CDI bump means diffing it against the upstream
20-
# asset and re-applying whatever is local by hand.
46+
# So nothing here writes under templates/: fetching the release the object is
47+
# derived from confirms the bytes still hash to the pin. What that catches is
48+
# an edit to RELEASE without a matching digest, and an asset that moved under a
49+
# tag that should be immutable. Moving RELEASE is therefore the step that
50+
# forces someone to reconcile this object by hand, which is the point. The
51+
# recipe then compares RELEASE with the cdi-operator image tag that
52+
# ../kubevirt-cdi-operator vendors, because that package can move to a new
53+
# release without touching this file, and a success line naming RELEASE would
54+
# then report a release the platform no longer runs. RELEASE is checked by
55+
# `update` rather than by `test`, because helm-unittest reads rendered output
56+
# and this object renders no version string.
2157
update:
22-
mkdir -p templates
23-
export VERSION=$$(basename $$(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest)) && \
24-
wget https://github.com/kubevirt/containerized-data-importer/releases/download/$$VERSION/cdi-cr.yaml -O templates/cdi-cr.yaml
25-
26-
test:
27-
helm unittest .
58+
wget https://github.com/kubevirt/containerized-data-importer/releases/download/$(RELEASE)/cdi-cr.yaml -O cdi-cr.yaml.download || { rm -f cdi-cr.yaml.download; exit 1; }
59+
@# Kept on a mismatch rather than deleted; the error below says why.
60+
@actual=$$($(SHA256SUM) cdi-cr.yaml.download | awk '{print $$1}'); \
61+
if [ "$$actual" != "$(CR_SHA256)" ]; then \
62+
echo "ERROR: cdi-cr.yaml for $(RELEASE) hashed $$actual," >&2; \
63+
echo " expected $(CR_SHA256). The download is kept as" >&2; \
64+
echo " cdi-cr.yaml.download." >&2; \
65+
echo " If RELEASE just moved, diff it against templates/cdi-cr.yaml," >&2; \
66+
echo " carry over what belongs there and update CR_SHA256. If RELEASE" >&2; \
67+
echo " did not change, the asset moved under a tag that should be" >&2; \
68+
echo " immutable: find out why first." >&2; \
69+
exit 1; \
70+
fi; \
71+
rm -f cdi-cr.yaml.download
72+
@deployed=$$(grep -oE 'quay\.io/kubevirt/cdi-operator:[^"[:space:]]+' ../kubevirt-cdi-operator/templates/cdi-operator.yaml | head -n 1); \
73+
deployed=$${deployed##*:}; \
74+
if [ "$$deployed" != "$(RELEASE)" ]; then \
75+
echo "ERROR: RELEASE is $(RELEASE), but ../kubevirt-cdi-operator vendors" >&2; \
76+
echo " CDI $${deployed:-<no cdi-operator image found>}, and that package decides what the" >&2; \
77+
echo " platform deploys. Reconcile templates/cdi-cr.yaml against the" >&2; \
78+
echo " release it vendors, then move RELEASE and CR_SHA256 to it." >&2; \
79+
exit 1; \
80+
fi
81+
@echo "upstream cdi-cr.yaml for $(RELEASE) matches the pin; templates/cdi-cr.yaml is maintained by hand and was left alone."
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
suite: cozy-kubevirt-cdi local templates
2+
release:
3+
name: kubevirt-cdi
4+
namespace: cozy-kubevirt-cdi
5+
# Both route cases run against this block; the TLSRoute case overrides the
6+
# gate alone. Emission is gated on expose-services and gateway-enabled, so
7+
# without a matching expose-services entry neither route renders and both
8+
# fail on "no manifest found" rather than on the assertion they are about.
9+
# root-host is not part of that gate and no assertion reads it: it is set
10+
# because the hostname is built from it, and without it the TLSRoute renders
11+
# "cdi-uploadproxy.%!s(<nil>)". tests/standalone_render_test.yaml covers the
12+
# opposite case, that the chart renders with nothing injected at all.
13+
set:
14+
_cluster:
15+
root-host: example.com
16+
expose-services: cdi-uploadproxy
17+
gateway-enabled: "false"
18+
tests:
19+
# Each list and map is pinned whole rather than by membership or by leaf:
20+
# `contains` answers "is ours still here" and cannot answer "has anything
21+
# been added beside it", and an assert per leaf cannot answer "has a key
22+
# appeared". Both directions matter for a file whose whole maintenance model
23+
# is a hand reconciliation at each bump. Pinning featureGates whole also
24+
# pins HonorWaitForFirstConsumer, which is upstream's rather than ours; that
25+
# is a side effect of the shape, not a claim that the upstream half is
26+
# covered.
27+
- it: keeps the local additions to the CDI object
28+
template: cdi-cr.yaml
29+
documentSelector:
30+
path: kind
31+
value: CDI
32+
matchMany: false
33+
asserts:
34+
- equal:
35+
path: spec.config.featureGates
36+
value:
37+
- HonorWaitForFirstConsumer
38+
- ExpandDisks
39+
- equal:
40+
path: spec.infra.tolerations
41+
value:
42+
- key: CriticalAddonsOnly
43+
operator: Exists
44+
- equal:
45+
path: spec.config.podResourceRequirements
46+
value:
47+
limits:
48+
cpu: 750m
49+
memory: 600M
50+
requests:
51+
cpu: 100m
52+
memory: 60M
53+
54+
# The default is the empty string, so the block renders nothing and an
55+
# assertion on the default value would pass against a template that had
56+
# stopped rendering the block. Setting it is what makes this able to fail.
57+
- it: renders the upload-proxy URL override when one is set
58+
template: cdi-cr.yaml
59+
documentSelector:
60+
path: kind
61+
value: CDI
62+
matchMany: false
63+
set:
64+
uploadProxyURL: https://cdi-uploadproxy.example.com
65+
asserts:
66+
- equal:
67+
path: spec.config.uploadProxyURLOverride
68+
value: https://cdi-uploadproxy.example.com
69+
70+
# What lets a tenant service account clone from the golden-image namespace.
71+
# `any: true` on both: a bare containsDocument holds every document in the
72+
# template to the match, and this template renders three.
73+
- it: keeps the golden-image clone RBAC
74+
template: cdi-cr.yaml
75+
asserts:
76+
- containsDocument:
77+
apiVersion: rbac.authorization.k8s.io/v1
78+
kind: ClusterRole
79+
name: cdi-copy-dv
80+
any: true
81+
- containsDocument:
82+
apiVersion: rbac.authorization.k8s.io/v1
83+
kind: RoleBinding
84+
name: cdi-clone-dv
85+
namespace: cozy-public
86+
any: true
87+
88+
# containsDocument reaches this document's identity and nothing inside it,
89+
# so what the role grants needs a case of its own. Widening verbs to ["*"]
90+
# hands every service account in the cluster whatever the binding reaches.
91+
- it: grants create on datavolumes/source and nothing else
92+
template: cdi-cr.yaml
93+
documentSelector:
94+
path: metadata.name
95+
value: cdi-copy-dv
96+
asserts:
97+
- equal:
98+
path: rules
99+
value:
100+
- apiGroups:
101+
- cdi.kubevirt.io
102+
resources:
103+
- datavolumes/source
104+
verbs:
105+
- create
106+
107+
# roleRef and subjects decide who may clone out of cozy-public, so each is
108+
# pinned whole rather than field by field. An assert on subjects[0] answers
109+
# "was the Group replaced by something narrower" and cannot answer "was a
110+
# second subject appended", and appending is the direction that widens
111+
# access: pinning the list is what makes both reachable.
112+
- it: binds exactly the service-account Group to the copy role
113+
template: cdi-cr.yaml
114+
documentSelector:
115+
path: metadata.name
116+
value: cdi-clone-dv
117+
asserts:
118+
- equal:
119+
path: roleRef
120+
value:
121+
kind: ClusterRole
122+
name: cdi-copy-dv
123+
apiGroup: rbac.authorization.k8s.io
124+
- equal:
125+
path: subjects
126+
value:
127+
- kind: Group
128+
name: system:serviceaccounts
129+
apiGroup: rbac.authorization.k8s.io
130+
131+
# Addressed as a `template:` because helm-unittest fails outright on one it
132+
# cannot find, which makes a deleted file loud rather than a smaller render.
133+
- it: renders the upload-proxy Ingress when the gateway is off
134+
template: cdi-uploadproxy-ingress.yaml
135+
asserts:
136+
- equal:
137+
path: metadata.annotations["nginx.ingress.kubernetes.io/ssl-passthrough"]
138+
value: "true"
139+
140+
- it: renders the upload-proxy TLSRoute when the gateway is on
141+
template: cdi-uploadproxy-tlsroute.yaml
142+
set:
143+
_cluster:
144+
gateway-enabled: "true"
145+
asserts:
146+
- equal:
147+
path: spec.parentRefs[0].sectionName
148+
value: tls-cdi-uploadproxy

0 commit comments

Comments
 (0)