Skip to content

test(rhdh): assert mirrored images and StorageClass in rendered manifests - #749

Open
gustavolira wants to merge 3 commits into
redhat-developer:mainfrom
gustavolira:rhidp-16230-constrained-cluster-assertions
Open

gustavolira wants to merge 3 commits into
redhat-developer:mainfrom
gustavolira:rhidp-16230-constrained-cluster-assertions

Conversation

@gustavolira

@gustavolira gustavolira commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Description of the change

A constrained cluster (mirrored registry only, no default StorageClass) depends on two things in the rhdh chart: global.imageRegistry reaching every image, and the dynamic plugins volume honouring the StorageClass settings. Today CI only checks that helm template succeeds, so a template that hardcodes a registry or drops storageClassName would still pass.

This PR adds two checks:

  1. hack/assert-rendered-values.sh is a bash + yq script with no new tool. The test-charts action runs it right after the existing helm template sanity step, before the kind cluster is created, so it adds about 2s. It renders the chart and asserts:
    • With --set global.imageRegistry=mirror.local, every image reference is under mirror.local/. That covers image fields in containers, init containers, the helm test pod and SonataFlow CRs, plus the CATALOG_INDEX_IMAGE and EXTRA_CATALOG_INDEX_IMAGES env values. The script renders the default values, every charts/rhdh/ci/*-values.yaml, and one extra scenario that turns on the optional images no ci file uses (OKP, orchestrator data index and job service, extra catalog indexes).
    • The helm test pod image follows test.image.* (RHDHBUGS-559).
    • The ephemeral volumeClaimTemplate carries dynamicPlugins.volume.ephemeral.storageClassName or global.defaultStorageClass when either is set, and the explicit value wins when both are. When neither is set it has no storageClassName, so the cluster default applies (RHDHBUGS-781).
    • statefulSetPVC honours its storageClassName.
    • dynamicPlugins.volume.type=emptyDir renders no PVC and no ephemeral claim.
  2. charts/rhdh/ci/with-custom-storage-class-values.yaml sets dynamicPlugins.volume.ephemeral.storageClassName: custom-sc, so ct install (and helm test / --upgrade) runs the plugins volume on the custom-sc StorageClass that the action already creates on kind. No rhdh ci file used that StorageClass before.

The chart version goes from 3.5.17 to 3.5.18 because ct lint needs a bump when a file under charts/rhdh/ changes (here, the new ci file). Templates and values are unchanged.

Relation to the coverage work: this complements #698 and ADR-010. Those measure template coverage, while this PR asserts rendered values. ADR-010 chose chart coverage over helm-unittest for coverage; this PR adds no helm-unittest and does not touch test.yaml or nightly.yaml. Armel Soro and Niall Twomey own the coverage work.

Dropped from the ticket: "fail fast instead of hanging when there is no default StorageClass". The chart could only check this with lookup on cluster-scoped StorageClasses. lookup returns nothing under helm template, --dry-run and ArgoCD, and it needs cluster-scope RBAC. A check like that would fail installs that are fine, so this PR leaves it out.

Which issue(s) does this PR fix or relate to

How to test changes / Special notes to the reviewer

helm dependency build charts/rhdh
./hack/assert-rendered-values.sh

Each assertion was checked against a mutated template and went red, then green again after the template was restored:

Mutation Assertion that failed
Hardcode the test pod image to quay.io/curl/curl:8.23.0 mirror (all scenarios) and test pod image
Drop the storageClassName include from the ephemeral template ephemeral.storageClassName and global.defaultStorageClass
Always render storageClassName: "" (the RHDHBUGS-781 regression) "nothing set: no storageClassName"
emptyDir branch never matches type=emptyDir
CATALOG_INDEX_IMAGE ignores global mirror (all scenarios)
rhdh.image.render ignores global mirror (all scenarios)
EXTRA_CATALOG_INDEX_IMAGES ignores global mirror (optional images)
statefulSetPVC ignores storageClassName statefulSetPVC.storageClassName
OKP image hardcoded mirror (optional images)
Init container image hardcoded mirror (all scenarios)
Test pod image under a lookalike host mirrorXlocal/ mirror (all scenarios)
Default dynamicPlugins.volume.type changed to emptyDir "nothing set: no storageClassName", ephemeral.storageClassName, global.defaultStorageClass
global.defaultStorageClass takes precedence over the explicit value "ephemeral.storageClassName overrides global.defaultStorageClass"
Image extraction in the script returns nothing "no image references found" (all scenarios)

The test workflow's discover-charts job only runs when charts/ or the test CI files change, so a PR that touches only hack/assert-rendered-values.sh gets ShellCheck but not this script. Fixing that means changing test.yaml, which #698 is also editing, so I left it for a follow-up.

Checklist

  • For each Chart updated, version bumped in the corresponding Chart.yaml according to Semantic Versioning.
  • For each Chart updated, variables are documented in the values.yaml and added to the corresponding README.md. The pre-commit utility can be used to generate the necessary content. Run pre-commit run --all-files to run the hooks and then push any resulting changes. The pre-commit Workflow will enforce this and warn you if needed.
  • JSON Schema template updated and re-generated the raw schema via the pre-commit hook. (No schema change.)
  • Tests pass using the Chart Testing tool and the ct lint command.
  • If you updated the orchestrator-infra chart, make sure the versions of the Knative CRDs are aligned with the versions of the CRDs installed by the OpenShift Serverless operators declared in the values.yaml file. See Installing Knative Eventing and Knative Serving CRDs for more details. (Not applicable.)

🤖 Generated with Claude Code

…ests

Constrained clusters (mirrored registry only, no default StorageClass)
depend on the chart applying global.imageRegistry to every image and on
the dynamic plugins volume honouring the StorageClass settings. Nothing
asserted either: CI only checked that helm template succeeds.

Add hack/assert-rendered-values.sh, run by the test-charts action next
to the helm template sanity step, before the kind cluster is created:
- with global.imageRegistry set, every image (containers, init
  containers, helm test pod, SonataFlow CR images, CATALOG_INDEX_IMAGE
  and EXTRA_CATALOG_INDEX_IMAGES) is under the mirror, for the default
  values, every ci/*-values.yaml and a scenario enabling the optional
  images (OKP, orchestrator data index/job service, extra indexes);
- the helm test pod image follows test.image.* (RHDHBUGS-559);
- the ephemeral volumeClaimTemplate carries
  ephemeral.storageClassName or global.defaultStorageClass, and no
  storageClassName when neither is set (RHDHBUGS-781);
- statefulSetPVC honours its storageClassName;
- type=emptyDir renders no PVC or ephemeral claim.

Add ci/with-custom-storage-class-values.yaml so ct install exercises
the dynamic plugins volume on the custom-sc StorageClass the action
already creates on kind. Bump the chart version for the new ci file.

Refs: RHIDP-16230

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gustavolira
gustavolira requested a review from a team as a code owner November 9, 2026 15:08
gustavolira and others added 2 commits November 9, 2026 12:45
The mirror check matched the registry prefix as a regex, so a lookalike
host such as mirrorXlocal/ passed; escape the dots. Compare the emptyDir
volume's sorted keys so the check does not depend on the order the
template renders fields in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The "nothing set" StorageClass check used has() on the claim spec,
  which yq answers false for a missing spec too, so it passed when the
  plugins volume was no longer ephemeral. Compare the spec's keys
  instead; a missing spec now fails the check.
- An empty image list made grep exit 1 under pipefail and aborted the
  script before the "no image references" failure could be reported.
- Assert that ephemeral.storageClassName takes precedence over
  global.defaultStorageClass when both are set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 9, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant