feat(ci): add disconnected OCP smoke test for Helm and Operator#5008
feat(ci): add disconnected OCP smoke test for Helm and Operator#5008zdrapela wants to merge 44 commits into
Conversation
|
Skipping CI for Draft Pull Request. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5008 +/- ##
==========================================
+ Coverage 55.39% 63.69% +8.30%
==========================================
Files 122 123 +1
Lines 2365 2424 +59
Branches 544 572 +28
==========================================
+ Hits 1310 1544 +234
+ Misses 1049 878 -171
+ Partials 6 2 -4
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
The container image build workflow finished with status: |
|
The container image build workflow finished with status: |
|
/test e2e-ocp-disconnected-helm-nightly |
|
/test e2e-ocp-disconnected-operator-nightly |
|
/test e2e-ocp-disconnected-helm-nightly |
|
The container image build workflow finished with status: |
|
/test e2e-ocp-disconnected-helm-nightly |
|
/test e2e-ocp-disconnected-helm-nightly |
|
/test e2e-ocp-disconnected-operator-nightly |
|
The container image build workflow finished with status: |
|
/test e2e-ocp-disconnected-helm-nightly |
Helm: After applying IDMS/ITMS, the MachineConfigOperator triggers a rolling update of cluster nodes (drain, reboot). Deploying Helm immediately causes pods to be evicted mid-startup — backstage connects to PG before it's ready and never recovers (503 forever). Fix: wait for all MachineConfigPools to reach Updated=True before deploying. Operator: The c/storage library's getRootlessStorageOpts() ignores the 'graphroot' field in storage.conf for rootless users and defaults to $HOME/.local/share/containers/storage (where chown fails in the CI user namespace). The only field that overrides this is 'rootless_storage_path'. Also set XDG_DATA_HOME so the fallback computation also points to our tmpdir. Move 'podman system reset' before writing configs since some podman versions delete $HOME/.config/containers/ during reset. Assisted-by: OpenCode
The nested-podman entrypoint detects /dev/fuse + fuse-overlayfs and creates an overlay storage config at /home/user/.config/containers/ storage.conf with graphroot=/tmp/graphroot. The VFS driver we were overriding to triggers chown on storage init which fails in the pod's user namespace. Root cause: commands.sh sets HOME=/tmp, so podman no longer finds the entrypoint's config at /home/user/.config/containers/. Our handler then wrote a custom VFS config, but VFS calls chown() on the graphroot directory tree during initialization, and chown is forbidden in the user namespace created by nested_podman: true (hostUsers: false). Fix: run prepare-restricted-environment.sh with HOME=/home/user so podman uses the entrypoint's overlay+fuse-overlayfs config naturally. This matches how other teams (e.g., MCO) use the nested-podman image in CI — no custom storage config needed. Removed: _CONTAINERS_USERNS_CONFIGURED, CONTAINERS_STORAGE_CONF, XDG_DATA_HOME, podman system reset, custom storage.conf. Kept: BUILDAH_ISOLATION=chroot (avoids nested userns for builds). Assisted-by: OpenCode
The entrypoint's storage config creation silently fails because /home/user is not writable by uid 1000 at entrypoint time — the passwd entry that grants gid 0 is created after the mkdir step. All previous attempts used the VFS driver, which calls chown() on the graphroot during storage initialization — forbidden in the pod's user namespace (hostUsers: false). The overlay driver with fuse-overlayfs does NOT chown the graphroot. Fix: write the same overlay+fuse-overlayfs config the entrypoint would have created (driver=overlay, graphroot=/tmp/graphroot, mount_program=/usr/bin/fuse-overlayfs) at the runtime HOME (/tmp, set by commands.sh). Both /dev/fuse and fuse-overlayfs are available in the CI pod (confirmed by previous run's debug output). Assisted-by: OpenCode
Both storage drivers fail in the CI pod's user namespace:
- overlay+fuse-overlayfs needs newuidmap which fails (file capabilities
from build time are not effective inside the pod's userns)
- VFS doesn't need newuidmap but chowns the graphroot directory tree
during storage initialization, which is forbidden in the userns
Fix: use VFS with _CONTAINERS_USERNS_CONFIGURED=1 (skip newuidmap)
and pre-create the graphroot directory tree (vfs/dir, vfs-images,
vfs-layers, runroot/vfs, runroot/libpod) so that the store init's
MkdirAllAndChown finds existing dirs owned by uid 1000 and skips the
chown. BUILDAH_ISOLATION=chroot avoids nested userns for builds.
ignore_chown_errors covers layer-level chown operations.
Write the config at the runtime HOME (/tmp, set by commands.sh) at
${HOME}/.config/containers/storage.conf — the standard rootless path.
Assisted-by: OpenCode
… Podman Ubuntu Podman cannot initialize storage under hostUsers:false. Switch prepare-restricted-environment.sh to --use-oc-mirror true so catalog mirroring no longer requires podman build, and stop wiring the plugin catalog index as the OLM --index-image. Co-authored-by: Cursor <cursoragent@cursor.com>
OCP 4.21 auto-detects OLM v1, but prepare-restricted-environment.sh then fails applying a missing clusterCatalog.yaml after oc-mirror. Use --olm-version v0 (CatalogSource/Subscription) and map RELEASE_VERSION next to '*' for filter-versions. Co-authored-by: Cursor <cursoragent@cursor.com>
The full rhdh-start.yaml references ConfigMaps/Secrets from the normal apply_yaml_files() flow. Use a guest-auth-only CR and force NAME_SPACE=showcase-disconnected so env_variables.sh cannot override it.
Avoid hardcoding redhat-operator-index:v4.21 so the job matches the live cluster major.minor (same oc version pattern as container-init.sh).
Temporarily fetch prepare-restricted-environment.sh from rhdh-operator PR redhat-developer#3259 so oc-mirror + OLM v1 can skip the missing clusterCatalog.yaml on main, and drop the --olm-version v0 workaround.
Extract duplicated Helm/Operator post-mirror steps into disconnected.sh so both handlers call the same helpers without changing deploy behavior.
Use the cluster major.minor already exported by container-init.sh instead of re-querying oc version in the disconnected operator handler.
Merge mirror credentials into the cluster pull-secret, attach a real reg-pull-secret to the installer SA, and wait on ClusterExtension with status dumps when the Backstage CRD never appears.
ClusterCatalog was stuck not Serving with x509 unknown authority. Add the mirror registry CA to image.config additionalTrustedCA and wait for MCP before running prepare.
install-dynamic-plugins failed with x509 on the mirrored catalog index because the operator CR only mounted registries.conf. Match Helm: mount mirror CA, policy.json, and registry auth for the init container.
CI mikefarah/yq rejected --arg when patching Backstage extraFiles. Interpolate mirror CA path and auth secret name via the shell instead.
Point prepare-restricted-environment.sh at refs/pull/3259/head so CI picks up the newest revision of the OLM v1 oc-mirror fix automatically.
Operator reconcile failed because registries.conf and policy.json both referenced rhdh-plugin-mirror-conf, creating duplicate volume names. Mount policy.json from a separate ConfigMap instead.
Operator subPath mounts to .config/containers failed (Not a directory). Mount auth.json under /tmp and set REGISTRY_AUTH_FILE for the init container.
…ator smoke Operator smoke was CrashLooping on plugin-catalog-index (not mirrored) and still starting lightspeed sidecars. Clear CATALOG_INDEX_IMAGE and set flavours: [] so guest→homepage smoke needs neither.
Stop patching the smoke CR with quay.io/rhdh-community/rhdh from IMAGE_* defaults. That ref is not in prepare IDMS; CSV registry.redhat.io hub is.
Empty CATALOG_INDEX_IMAGE skipped dynamic-plugins.default.yaml, so the home-page plugin never installed and guest login hit a / 404. Use the index already mirrored by mirror_plugins instead.
plugin-catalog-index:next enables a local homepage dist path that GA rhdh-hub-rhel9 lacks. Match Helm by skopeo-copying the CI hub to the mirror and pointing the smoke CR at it (flavours remain empty).
Remove leftover ConfigMap wiring from a conflicting edit; homepage comes from catalog defaults on the mirrored CI hub image.
Operator getInitContainer overwrites CR init image patches with RELATED_IMAGE_backstage (CSV GA). Point RELATED_IMAGE at the mirrored CI hub and post-patch the Deployment so homepage plugins resolve.
rhdh-operator#3259 is merged on main, so drop the temporary pull/3259 pin.
|
/test e2e-ocp-disconnected-operator-nightly |
|
|
@zdrapela: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |



https://redhat.atlassian.net/browse/RHIDP-13974
Add end-to-end disconnected (air-gapped) CI smoke tests for RHDH deployed via Helm and Operator on OCP.
What it does
Mirrors RHDH container images, Helm chart, operator, and dynamic plugins to an isolated mirror registry on a disconnected OCP cluster, deploys RHDH, and runs a Playwright smoke test (guest login → homepage).
Architecture
Shared library (
lib/disconnected.sh)require_env/setup_auth— validate env vars, configure container-tools authbuild_imageset_config— generate oc-mirror ImageSetConfiguration from chart valuesrun_oc_mirror— mirror images viaoc-mirror --v2patch_idms— add cross-registry entries to the generated IDMSfetch_script— download scripts from rhdh-operatorHelm handler (
jobs/ocp-disconnected-helm.sh)charts.openshift.io, CI fromoci://quay.io/rhdh/chart)mirror-plugins.shfrom rhdh-operatorvalues_disconnected-smoke.yaml) — guest auth onlyOperator handler (
jobs/ocp-disconnected-operator.sh)prepare-restricted-environment.shPost-renderer (
resources/disconnected/helm-post-renderer.sh)Patches the rendered Deployment to add:
registries.conf— redirects plugin pulls to mirror registrypolicy.json— permissive signature policy for mirrored images/etc/containers/certs.d/<registry>/ca.crtIssues encountered and fixed
Helm "array clobber" (
extraVolumesreplaced by override file)Static
helm-overrides.yamldefined 4extraVolumesbut chart 1.11 has 7. Helm replaces (not merges) arrays from-ffiles. Fix: use--post-rendererto append volumes to the rendered manifests.Missing ConfigMaps (
app-config-rhdh,dynamic-plugins-config)values_showcase.yamlreferences ConfigMaps created byapply_yaml_files()which the disconnected handler skips. Fix: use minimalvalues_disconnected-smoke.yamlwith only guest auth.TLS failure (
x509: certificate signed by unknown authority)Init container's skopeo can't verify mirror registry's self-signed CA. Fix: mount CA cert at
/etc/containers/certs.d/<registry>/ca.crt(standard container-tools per-registry path).Auth failure (
authentication required)Init container has no credentials for the mirror registry. Fix: create
${RELEASE_NAME}-dynamic-plugins-registry-authSecret (chart's built-in optional mount).Signature verification failure (
A signature was required, but no signature exists)RHDH image's
policy.jsonrequires Red Hat GPG signatures forregistry.access.redhat.com. Mirrored images lack signatures (server unreachable). Fix: mount permissivepolicy.jsonin init container.REGISTRY_AUTH_FILEconflict with oc-mirrorThe distribution/distribution library (used by oc-mirror) interprets
REGISTRY_AUTH_FILEas storage driver config, causing panics. Fix: unset before oc-mirror, restore after.Chart digest encoding (
repo@sha256+tag)Chart encodes PostgreSQL digest refs as
repository: "repo@sha256"+tag: "<hash>". Fix:PG_SEPARATORnormalizes this for IDMS fields.Companion PR
CATALOG_INDEX_IMAGEas single Gangway override parameter