test(system): anchor index-addressed negative asserts to what they name - #3608
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe PR strengthens seven Helm tests. Assertions now target named containers or scoped rules and validate complete argument lists or matching entries. ChangesTest assertion strengthening
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/system/ouroboros/tests/cozystack_overrides_test.yaml`:
- Around line 37-50: Add an assertion before the existing containers[0].args
check to verify that containers[0].name is proxy. Keep the current proxy
argument assertion unchanged and ensure the container identity is validated
first.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 13c9ba82-cdb3-4ff6-af69-ef9e43c9c880
📒 Files selected for processing (7)
packages/system/cilium-networkpolicy/tests/networkpolicy_test.yamlpackages/system/flux-shard-operator/tests/workload_test.yamlpackages/system/keycloak/tests/encryption_test.yamlpackages/system/kilo/tests/kilo_test.yamlpackages/system/monitoring/tests/oidc_test.yamlpackages/system/ouroboros/tests/controller_test.yamlpackages/system/ouroboros/tests/cozystack_overrides_test.yaml
| - equal: | ||
| path: spec.template.spec.containers[0].args | ||
| content: --target-host= | ||
| value: | ||
| - proxy | ||
| - --listen-http=:8080 | ||
| - --listen-https=:8443 | ||
| - --listen-health=:8081 | ||
| - --target-service-name=ingress-nginx-controller | ||
| - --target-service-namespace=cozy-ingress-nginx | ||
| - --target-http-port=80 | ||
| - --target-https-port=443 | ||
| - --dial-timeout=5s | ||
| - --ready-timeout=2s | ||
| - --shutdown-grace=30s |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Anchor the indexed container to proxy.
Line 37 checks containers[0].args without checking the container identity. If the template adds or reorders containers, this test can validate the wrong container or fail when the proxy arguments remain correct. Assert containers[0].name: proxy before this assertion.
Proposed fix
+ - equal:
+ path: spec.template.spec.containers[0].name
+ value: proxy
- equal:
path: spec.template.spec.containers[0].args📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - equal: | |
| path: spec.template.spec.containers[0].args | |
| content: --target-host= | |
| value: | |
| - proxy | |
| - --listen-http=:8080 | |
| - --listen-https=:8443 | |
| - --listen-health=:8081 | |
| - --target-service-name=ingress-nginx-controller | |
| - --target-service-namespace=cozy-ingress-nginx | |
| - --target-http-port=80 | |
| - --target-https-port=443 | |
| - --dial-timeout=5s | |
| - --ready-timeout=2s | |
| - --shutdown-grace=30s | |
| - equal: | |
| path: spec.template.spec.containers[0].name | |
| value: proxy | |
| - equal: | |
| path: spec.template.spec.containers[0].args | |
| value: | |
| - proxy | |
| - --listen-http=:8080 | |
| - --listen-https=:8443 | |
| - --listen-health=:8081 | |
| - --target-service-name=ingress-nginx-controller | |
| - --target-service-namespace=cozy-ingress-nginx | |
| - --target-http-port=80 | |
| - --target-https-port=443 | |
| - --dial-timeout=5s | |
| - --ready-timeout=2s | |
| - --shutdown-grace=30s |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/system/ouroboros/tests/cozystack_overrides_test.yaml` around lines
37 - 50, Add an assertion before the existing containers[0].args check to verify
that containers[0].name is proxy. Keep the current proxy argument assertion
unchanged and ensure the container identity is validated first.
61e4717 to
318b81f
Compare
IvanHunters
left a comment
There was a problem hiding this comment.
Overview
Reviewed statically against the actual chart templates and values.yaml defaults for all seven converted assertions, plus the helm-unittest contains/notContains validator source to confirm the any: true semantics the PR relies on.
Verdict: LGTM with non-blocking notes.
The core claim of this PR checks out: several of the "converted" assertions were genuinely dead before this change, not just theoretically weak.
packages/system/monitoring/templates/grafana/grafana.yamlalways rendersGF_AUTH_GENERIC_OAUTH_CLIENT_SECRETandGF_PATHS_CUSTOM_INIwith avalueFrom/valuefield besidename. The pre-PR bare-mapnotContains(noany: true) does an exactreflect.DeepEqualper the validator source, so it could never match those entries and was permanently green regardless of OIDC mode. Theany: trueaddition (a genuine subset match, confirmed againstcontains_validator.go) is a real fix.- The
--pinned-tenants=,--target-host=, and--shard-cpu-limit=prefix-stylenotContains/removed checks were indeed dead: Helm renders each flag as one joined string (--flag=value), so a bare-prefix element can never equal a rendered element. Theequal-on-full-arg-list replacements influx-shard-operator/tests/workload_test.yamlandouroboros/tests/cozystack_overrides_test.yamlwere checked element-by-element against the rendered defaults (templates/workload.yaml+ packagevalues.yaml,proxy-deployment.yaml+ packagevalues.yamlincluding thecozy-ingress-nginxnamespace override) and match exactly, in order. - The four
equal: containers[0].nameanchors (proxy/kilo/grafana/controller) were checked against their respective single-container templates — all correct, and a reasonable defensive measure against a future sidecar shifting the index. - The Cilium
lengthEqual+ selector +any: trueconversion was checked againsttemplates/networkpolicy.yaml: port7473is indeed absent today, and the neighbouring7946entry does carryprotocol: ANYbesideport, which is exactly the shape the PR says would slip past a plainnotContains— confirming the stated regression scenario is realistic, not hypothetical.
Non-blocking notes
packages/system/ouroboros/tests/cozystack_overrides_test.yaml:37-40— the "proxy dials the cozy-ingress-nginx Service..." test converts its deadnotContainsto a full-listequalonspec.template.spec.containers[0].args, but does not add acontainers[0].name: proxyanchor first. Every other converted assertion in this PR gets that anchor — including the second test in this same file — and CodeRabbit flagged this exact gap with a concrete suggested diff that is still unaddressed at head. It isn't a silent-pass risk today (proxy-deployment.yamlrenders exactly one container, and anequalon the full list fails loudly rather than silently if that ever changes), but it's inconsistent with the PR's own stated principle ("every index on a path gets anchored") and worth a one-line fix before merge for consistency and clearer failure messages down the line.- Nice self-imposed scope discipline: explicitly not sweeping the whole tree, and explicitly not switching to name-based JSONPath filters (which would trade "wrong element" for "no element" — the same failure family). Both calls are correct and the reasoning is sound.
Closing
Solid, well-evidenced test-hygiene fix; the "dead assertion" diagnoses all held up against the actual templates. Addressing note #1 (or explicitly deferring it with a reason) would make the PR fully consistent with its own methodology, but it's not a blocker.
myasnikovdaniil
left a comment
There was a problem hiding this comment.
Reproduced both dead asserts. Pre-PR forms in networkpolicy_test.yaml and oidc_test.yaml stay green while the template regresses, the any: true forms go red. Nine converted guards mutation tested, all nine catch it. Anchors are not vacuous either, dropping a container name: makes equal error on unknown path instead of passing.
All six packages have a Makefile test: target so the runner picks them all up, none skipped. Local helm-unittest is v1.0.3 and CI installs unpinned which is v1.1.2 now, contains_validator.go is identical between them so results transfer.
| value: kilo | ||
| - notContains: | ||
| path: spec.template.spec.containers[0].args | ||
| content: "--compatibility=cilium" |
There was a problem hiding this comment.
Test is named for --compatibility but the assert only forbids exact --compatibility=cilium, so it stays green when the default render carries the flag with another value. Set compatibility: flannel in values.yaml and the suite still passes 5/5.
Same family this PR is fixing, and the header comment at line 12 makes the same claim. Whole-args equal like you did in flux-shard-operator would close it, or just rename the test to name the cilium value.
| value: controller | ||
| - notContains: | ||
| path: spec.template.spec.containers[0].args | ||
| content: --gateway-api |
There was a problem hiding this comment.
This one is live only because the chart renders the flag bare. Every other flag in charts/ouroboros/templates/controller-deployment.yaml is --flag=value, so an upstream bump to --gateway-api=true quietly puts it back to the prefix-vs-element deadness you are fixing elsewhere. Not introduced here and the index anchor is correct, just worth a comment or the same equal treatment.
notContains passes whenever its path does not resolve, and also
whenever it resolves to something that simply lacks the forbidden
content. A different container at the same index qualifies, so these
guards go green the moment a sidecar is placed ahead of the workload
they were written for, while the flag or env var they forbid comes
back on the real container.
Assert the identity at that index first, in each affected case: the
container name for the Deployment-shaped ones, and for the Cilium
policy its selector, since a deny rule has no name. The selector
identifies that rule only while there is one rule to identify, so it
is paired with a length assertion, and with a second one on the
rule's toPorts, because that path carries two indexes and anchoring
the outer one alone leaves the inner free.
The anchor goes only where nothing else holds the index. A positive
contains on the same path fails on its own once a sidecar arrives
ahead of the workload, and an equal on the whole argument list fails
the same way, so neither carries a separate anchor. A negative does,
including the per-element form below: a wildcard resolving to the
sidecar's own arguments matches nothing and passes.
Several of the guards could not fail for the reason they name, and
the anchor alone would have left that hidden behind a comment saying
they were covered. contains and notContains compare elements for
equality, so a mapping is compared whole: `{name: GF_...}` never
matched an entry carrying a value beside the name, and `{port: 7473}`
would not match that port re-added with a protocol beside it, which
is how the neighbouring entry in the same list is written. Those get
`any: true`.
Equality against a rendered argument is the same failure worn
differently, and it arrives in three disguises. A prefix that never
equals an element: `--pinned-tenants=` never equalled
`--pinned-tenants=tenant-a=shard0`, and `--target-host=` never
equalled a rendered `--target-host=<value>`. One value out of many
under a name that claims the flag: `--compatibility=cilium` left the
kilo default render free to carry `--compatibility=flannel` off the
same values key, and the two `--proxy-fqdn` negatives forbade only
the FQDN the default cluster-domain produces. And a form that holds
by luck: `--gateway-api` still fails today only because that template
renders it bare, so an upstream switch to `--gateway-api=true` would
retire the guard in silence.
All of those become notMatchRegex over args[*], which matches the
flag name per element and fails on any value and on the bare form
alike. A length assertion would not have covered the prefix cases,
since an upstream bump that adds the flag while consolidating another
argument keeps the count. A wildcard that resolves to nothing still
passes, so the index anchors stay.
The whole argument list is a different instrument, answering what the
default invocation is rather than whether one flag is absent. It
fails on an argument added, removed or swapped instead of only on the
ones someone thought to enumerate, which is worth having where the
template is first-party and a failure is a deliberate change asking
to be read. flux-shard-operator gets it in `renders the default
Deployment shape`, replacing eleven individual contains that listed
exactly the same eleven elements; `--shard-cpu-limit=` was dead for
the prefix reason and is dropped rather than repaired, because that
list now covers it. The vendored ouroboros templates get no such pin:
upstream churn arrives there on its own schedule, and a whole-list
assertion would fail a test named for one flag for a reason that has
nothing to do with that flag.
Anchor every index on a path, not the outermost one. The Cilium
assertion reads spec.ingressDeny[0].toPorts[0].ports, and pinning the
rule list alone would still let the port return on a second toPorts
entry of the same rule.
Each guard was checked by rendering what it forbids onto the element
it names, and each anchor by rendering the shift it exists to catch.
The form is already used in this tree, in opensearch-operator and
dashboard. Assertions of the same shape remain in files this commit
does not touch, including one in a package it does, and stay out of
scope here.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Signed-off-by: Aleksei Sviridkin <3811295@gmail.com>
318b81f to
d73c102
Compare
myasnikovdaniil
left a comment
There was a problem hiding this comment.
Every assertion this rewrites was checked in both directions, and in 11 of 13 violation scenarios the old form was green on the exact violation its own name forbids.
Two are worth naming because they are the sharpest. A test called no --compatibility arg in the default render passed at base while --compatibility=cilium, the literal string it forbade, was rendered on the real kilo container, because a sidecar occupied containers[0]. And drops the stale frr-mode metrics port passed with port 7473 back in the deny rule, because an unrelated deny inserted ahead shifted the real rule to index 1. Both go red on this PR.
The two exceptions are cases where the old form happened to catch the one literal spelling it named, and the new form catches those too, so nothing regressed.
I also checked the design rule rather than trusting the commit message. notMatchRegex over containers[0].args[*] where containers[0] is a sidecar with no args passes silently, so the name anchors are load-bearing. And the converse: the four notContains left unanchored in the same keycloak file and the two unanchored notMatchRegex in ouroboros are each held by a positive contains on the same path, and every one of them reddens under the corresponding sidecar-ahead mutation in both forms. The selectivity is principled.
All six touched packages define a test: target and pass the make -n test probe, so none is in the silently-skipped class, and all six are green before and after, so no new assertion is vacuous on the unmutated chart.
Three things worth a line each.
The cilium length anchor at networkpolicy_test.yaml:98 couples a port-named test to the rule count. Appending an unrelated cluster-scoped deny to templates/networkpolicy.yaml, with 7473 nowhere in sight, reddens exactly drops the stale frr-mode metrics port and nothing else. That is defensible on your own criterion, since the template is first-party and adding a deny rule there is a deliberate change asking to be read. What is missing is a sentence saying so, so the next person to add a deny rule reads why a test about 7473 turned red instead of concluding the anchor is wrong.
Six assertions now rest on notMatchRegex plus wildcard-path semantics that CI does not pin. The plugin is installed unversioned at .github/workflows/pull-requests.yaml:209-210. On 1.0.3 a wildcard path makes notMatchRegex mean no element may match, which is what all six depend on. If a release changed that to some element does not match, all six become vacuous in precisely the way this PR exists to prevent and nothing in the repo would notice. Not yours, but this PR increases the blast radius of an unpinned tool and pinning it is cheap.
The flux-shard whole-list equal correctly catches --shard-cpu-limit=500m, which the deleted notContains never could, but the failure now surfaces in a test called renders the default Deployment shape, whose name carries no signal that the absence of a cpu-limit flag is deliberate. The comment at :20-27 explains the instrument and not that particular absence.
The red E2E is the node-join flake, 43 of 45 suites passed and the diff is helm-unittest YAML that E2E never loads.
What this PR does
Some negative assertions in these suites can't fail for the reason their test names give.
containsandnotContainscompare list elements for equality, so--pinned-tenants=never equals a rendered--pinned-tenants=tenant-a=shard0, and{name: GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET}never matches an env entry carrying avalueFrombeside the name. Others forbid one rendered value under a name that claims the flag itself, sonotContains --compatibility=ciliumleaves the kilo default render free to carry--compatibility=flanneloff the same values key.Flag guards become
notMatchRegexonargs[*], which matches per element, so any value fails and so does the bare form. Mapping guards getany: true. flux-shard-operator gets anequalon the whole default arg list instead, and--shard-cpu-limit=is dropped because that list covers it. The vendored ouroboros templates keep per-flag guards: a whole-list pin there would go red on any upstream flag change, under a test name about one flag.Several of these address
containers[0]with nothing pinning that index, so a sidecar rendered ahead of the workload turns them green while the flag is live on the real container. Those get anequalon the container name first. A Cilium deny rule has no name, so that one pins the selector plus the rule andtoPortscounts. Where a positivecontainsalready sits on the same path it holds the index by itself and gets no extra anchor.Assertions of the same shape remain in files this PR doesn't touch, including one in a package it does.
Tests
Every dead guard was confirmed dead first: make the forbidden content render, watch the old form stay green.
compatibility: flannelin kilo's values,--gateway-api=truein place of the bare flag,--proxy-fqdnbaked beside the composition flags, the OIDC client secret in themode=Nonegrafana render. Each one turns the new assertion red. Anchors checked the same way, with a sidecar rendered ahead of the workload.hack/helm-unit-tests.shis green.Screenshots
None. This changes test files.
Downstream repositories
Every changed file is under a
tests/directory. No templates, no values, no rendered output.Release note