Skip to content

[edpm_prepare] Apply set_containers overrides from job vars - #4131

Open
rebtoor wants to merge 1 commit into
mainfrom
feature/edpm-set-containers-s2i
Open

[edpm_prepare] Apply set_containers overrides from job vars#4131
rebtoor wants to merge 1 commit into
mainfrom
feature/edpm-set-containers-s2i

Conversation

@rebtoor

@rebtoor rebtoor commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add one small task to edpm_prepare that calls cifmw.general.set_containers when jobs provide cifmw_set_containers_images.
  • Leave the legacy update_containers role untouched.
  • Add preserve_unlisted to set_containers so partial override lists keep unspecified images from the current OpenStackVersion CR before apply.

Design

Problem

Consumer jobs (for example the s2i content provider) return a partial
customContainerImages map: only the services that were built, not the full
OpenStack image set. Those overrides must be applied during edpm_prepare,
before OpenStackControlPlane is deployed. Applying them later (for example
via a pre_tests hook) is too late — the control plane and dataplane may
already be running with operator-default images.

Approach

  • Jobs pass cifmw_set_containers_images as a list of {name, full_registry} entries.
  • edpm_prepare runs one generic set_containers task when that list is non-empty.
  • preserve_unlisted reads the current OpenStackVersion CR, keeps entries not
    present in the override list, overlays the new entries, writes the manifest,
    and applies it via the module's existing oc apply -f path.
  • No s2i-specific logic lives in ci-framework; job YAML owns the translation
    from provider output to cifmw_set_containers_images.

Out of scope (follow-up)

  • Migrating the legacy update_containers role to set_containers for all
    EDPM and DLRN flows. The architecture deploy path already uses
    set_containers; this change adds a narrow override hook for EDPM jobs that
    supply explicit image lists.
  • Replacing update_containers wholesale in this PR would duplicate the large
    parameter surface already present in deploy_architecture.yml and expand
    review scope beyond what consumer jobs need today.

Alternatives considered

  • pre_tests hook + oc patch: wrong point in the deploy timeline.
  • Inlining a full set_containers call in edpm_prepare: rejected; job
    vars plus a thin task keeps the framework generic.
  • Migrating update_containers to set_containers in this PR: larger
    change, separate from the partial-override use case.
  • Applying partial manifests without preserve_unlisted: would replace the
    entire customContainerImages map and drop unspecified services.

Test plan

Depends-on: #4136

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Wire set_containers into edpm_prepare and support merge-patch image overrides

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace edpm_prepare container updates with cifmw.general.set_containers for consistent behavior.
• Apply S2I-provided custom container images during edpm_prepare before control plane deployment.
• Add merge-patch mode to avoid overwriting existing OpenStackVersion image mappings.
Diagram

graph TD
  A["edpm_prepare role"] --> B["Build S2I overrides"] --> C["set_containers module"] --> D[("OpenStackVersion CR YAML")] --> E["oc apply / oc patch"] --> F[("OpenShift API")] --> G[("OpenStackVersion CR")]
  G --> H["OpenStackControlPlane deploy"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Server-side apply (SSA) with field manager
  • ➕ Avoids client-side merge semantics; Kubernetes handles field-level ownership
  • ➕ Can reduce accidental overwrites when multiple actors update the CR
  • ➖ Requires careful field manager/force-conflicts strategy
  • ➖ May be harder to reason about in CI jobs with multiple writers
2. Read existing OpenStackVersion and re-apply fully merged CR
  • ➕ Deterministic desired-state manifest written and applied
  • ➕ Can implement fine-grained merge logic (including deletions)
  • ➖ More code/oc calls (get + merge + apply) and more failure modes
  • ➖ Harder to keep merge rules consistent with Kubernetes patch semantics
3. Keep update_containers in edpm_prepare and extend it for S2I overrides
  • ➕ Less churn in edpm_prepare task file
  • ➕ Avoids adding a new apply mode to set_containers
  • ➖ Diverges EDPM prepare from architecture deploy path again
  • ➖ Duplicates logic that set_containers is consolidating

Recommendation: Current approach is the best tradeoff: consolidating on set_containers reduces drift between EDPM prepare and architecture deploy, and merge-patching only spec.customContainerImages directly addresses the partial-override (S2I) use case with minimal additional complexity. SSA or read/merge/apply are viable but add operational and debugging complexity not clearly justified for this targeted overwrite-avoidance need.

Files changed (3) +181 / -8

Enhancement (2) +134 / -8
set_containers.pyAdd merge-patch apply mode for partial image overrides +37/-3

Add merge-patch apply mode for partial image overrides

• Introduces a new patch_merge option and updates module docs to describe patch vs apply behavior. When apply=true and patch_merge=true, the module runs oc patch --type merge to update only spec.customContainerImages on the existing OpenStackVersion CR, avoiding replacement of the full map.

plugins/modules/set_containers.py

kustomize_and_deploy.ymlSwitch EDPM prepare container wiring to set_containers with S2I integration +97/-5

Switch EDPM prepare container wiring to set_containers with S2I integration

• Replaces the prior update_containers role include with a direct cifmw.general.set_containers invocation. Builds set_containers-compatible image overrides from s2i_content_provider_os_custom_container_images and defaults to merge-patching when only partial S2I overrides are being applied, while keeping backward-compatible fallbacks from cifmw_update_containers_* variables.

roles/edpm_prepare/tasks/kustomize_and_deploy.yml

Tests (1) +47 / -0
test_set_containers.pyCover patch_merge behavior and error handling +47/-0

Cover patch_merge behavior and error handling

• Adds unit tests asserting that patch_merge triggers oc patch with merge type and that non-zero return codes raise an Ansible failure with the expected message. Ensures existing apply-path tests continue to validate oc apply usage.

tests/unit/modules/test_set_containers.py

@qodo-code-review

qodo-code-review Bot commented Aug 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Invalid jsonpath JSON parse 🐞 Bug ≡ Correctness
Description
_fetch_existing_custom_container_images requests jsonpath output but then json.loads() it; jsonpath
output is not guaranteed to be valid JSON, so preserve_unlisted can fail with a decode error and
block edpm_prepare overrides.
Code

plugins/modules/set_containers.py[R503-506]

+            "-n",
+            params["namespace"],
+            "-o",
+            "jsonpath={.spec.customContainerImages}",
Evidence
The new code explicitly requests JSONPath output for .spec.customContainerImages and immediately
feeds it to json.loads, creating a format mismatch that can raise ValueError during decoding and
cause a module failure.

plugins/modules/set_containers.py[496-524]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`_fetch_existing_custom_container_images()` runs `oc get ... -o jsonpath={.spec.customContainerImages}` and then attempts `json.loads(out)`. JSONPath output is not reliably JSON, so this can raise a decode failure and break the module when `preserve_unlisted=true`.

### Issue Context
The goal of `preserve_unlisted` is to merge the current cluster map with overrides; that requires a reliable, parseable representation of `spec.customContainerImages`.

### Fix Focus Areas
- plugins/modules/set_containers.py[496-524]

### Suggested fix
- Change the `oc get` to `-o json` and `json.loads(out)` the whole object, then extract `obj.get("spec", {}).get("customContainerImages", {})`.
 - Alternatively, if available in your target oc/kubectl, use `-o jsonpath-as-json=...`.
- Keep the existing type-check (`dict`) and fail with a clear error if extraction yields a non-mapping.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Preserve ignores oc get failures 🐞 Bug ☼ Reliability
Description
When preserve_unlisted is enabled, oc get failures (rc!=0) return an empty dict, so the module may
apply only the override list and unintentionally drop existing customContainerImages entries.
Code

plugins/modules/set_containers.py[R510-511]

+    if rc != 0 or not out.strip():
+        return {}
Evidence
The new helper explicitly returns {} on any non-zero rc, and the new merge logic then overlays
overrides onto that empty mapping, meaning only overrides remain and unlisted existing images are
not preserved.

plugins/modules/set_containers.py[510-513]
plugins/modules/set_containers.py[635-643]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
With `preserve_unlisted=true`, `_fetch_existing_custom_container_images()` returns `{}` on any `oc get` non-zero rc, silently disabling preservation and risking loss of existing image mappings.

### Issue Context
The PR’s design states partial override lists should keep unspecified images from the current OpenStackVersion CR. Returning `{}` on errors violates that guarantee.

### Fix Focus Areas
- plugins/modules/set_containers.py[496-524]
- plugins/modules/set_containers.py[635-643]

### Suggested fix
- If `rc != 0`, call `module.fail_json(msg=..., rc=rc, stdout=out, stderr=err)`.
- If you need to tolerate “not found” (CR absent), detect that specific case (e.g., parse `err` for NotFound / check `out`), and only then return `{}`.
- Consider also failing if `out.strip()` is empty while `rc==0` (unexpected) unless you can prove it represents an empty map.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Check mode hits cluster 🐞 Bug ☼ Reliability
Description
preserve_unlisted triggers an oc get even in Ansible check mode, so set_containers no longer behaves
as a pure local diff in check mode and may fail in environments where cluster access isn’t
available.
Code

plugins/modules/set_containers.py[R635-639]

+    if module.params["preserve_unlisted"]:
+        if not kubeconfig:
+            module.fail_json(msg="kubeconfig is required when preserve_unlisted=true")
+        existing_images = _fetch_existing_custom_container_images(
+            module, module.params, kubeconfig
Evidence
The module is declared to support check mode, but the new preserve_unlisted logic runs before any
check-mode gating and calls _fetch_existing_custom_container_images(), which runs oc get.

plugins/modules/set_containers.py[540-572]
plugins/modules/set_containers.py[634-643]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The module advertises `supports_check_mode=True`, but the new `preserve_unlisted` branch performs a live `oc get` regardless of check mode.

### Issue Context
Check mode is commonly used for dry-run validation and should avoid side-effecting operations and ideally avoid requiring external connectivity.

### Fix Focus Areas
- plugins/modules/set_containers.py[635-643]

### Suggested fix
- Add a guard:
 - If `module.check_mode` and `preserve_unlisted` is true, either:
   - skip the cluster fetch and document that preservation cannot be evaluated in check mode, or
   - read preservation source from an existing on-disk manifest if present.
- If you choose to skip, consider setting a result field/warning to make behavior explicit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Test doesn't validate merge 🐞 Bug ⚙ Maintainability
Description
The new preserve_unlisted unit test only asserts that oc get/apply are called, but never verifies
that the written CR actually retains the existing image plus the override, so regressions in merge
behavior can slip through.
Code

tests/unit/modules/test_set_containers.py[R338-363]

+    def test_present_preserve_unlisted_calls_oc_get_and_apply(self):
+        set_module_args(
+            self._args(
+                apply=True,
+                preserve_unlisted=True,
+                kubeconfig="/home/zuul/.kube/config",
+                images=[
+                    dict(
+                        name="glanceAPIImage",
+                        full_registry="registry.example:5000/ns/glance:tag",
+                    )
+                ],
+            )
+        )
+        with patch(_MOD + ".os.path.exists", return_value=False), patch(
+            _MOD + ".os.makedirs"
+        ), patch("builtins.open", mock_open()), patch(
+            _MOD + "._run_oc",
+            side_effect=[(0, '{"keystoneAPIImage": "keep-me"}', ""), (0, "", "")],
+        ) as mock_oc:
+            with self.assertRaises(AnsibleExitJson) as cm:
+                set_containers.run_module()
+        self.assertEqual(mock_oc.call_count, 2)
+        self.assertEqual(mock_oc.call_args_list[0][0][1][0], "get")
+        self.assertEqual(mock_oc.call_args_list[1][0][1][0], "apply")
+        self.assertTrue(cm.exception.args[0]["changed"])
Evidence
The test verifies only call count and command verbs, and does not inspect the manifest that should
contain the merged mapping.

tests/unit/modules/test_set_containers.py[338-363]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`test_present_preserve_unlisted_calls_oc_get_and_apply` does not assert the merged `customContainerImages` content written to `dest_path`, so it doesn't actually test the preservation behavior.

### Issue Context
The PR’s key behavior is merging existing images with overrides; validating file content would catch breakages in parsing/merge logic.

### Fix Focus Areas
- tests/unit/modules/test_set_containers.py[338-363]

### Suggested fix
- Capture the file write from `mock_open()` and parse the YAML written.
- Assert that `spec.customContainerImages` contains both:
 - the preserved key from the mocked oc get output, and
 - the overridden key from the input `images` list.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 0ccb20d ⚖️ Balanced

Results up to commit 48a3047 ⚖️ Balanced


🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Non-cifmw_ fact variable 📘 Rule violation ⚙ Maintainability
Description
The role task introduces _edpm_s2i_set_containers_images, which does not follow the required
cifmw_<role>_... naming convention. This can violate ansible-lint strict variable naming rules and
increases the chance of collisions/unclear ownership.
Code

roles/edpm_prepare/tasks/kustomize_and_deploy.yml[R35-36]

+  ansible.builtin.set_fact:
+    _edpm_s2i_set_containers_images: >-
Evidence
PR Compliance ID 1 requires role variables to match ^cifmw_[a-z_][a-z0-9_]*$ and include the role
name after cifmw_. The task sets _edpm_s2i_set_containers_images, which does not meet that
pattern.

AGENTS.md: Ansible Role Variable Names Must Use the cifmw_ Prefix Pattern
roles/edpm_prepare/tasks/kustomize_and_deploy.yml[35-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new role variable `_edpm_s2i_set_containers_images` is created via `set_fact`, but it does not start with `cifmw_` and does not include the role name immediately after the prefix as required.

## Issue Context
This repository enforces `cifmw_<role>_...` variable naming to avoid collisions and satisfy ansible-lint strict mode.

## Fix Focus Areas
- roles/edpm_prepare/tasks/kustomize_and_deploy.yml[31-40]
- roles/edpm_prepare/tasks/kustomize_and_deploy.yml[103-107]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Patch fails if CR missing 🐞 Bug ≡ Correctness
Description
patch_merge=true switches from oc apply to oc patch, which cannot create the OpenStackVersion
resource and will fail if it does not already exist. edpm_prepare auto-enables patch_merge for
S2I partial overrides, so a fresh cluster/run without an existing OpenStackVersion CR will hard-fail
during prepare.
Code

plugins/modules/set_containers.py[R641-656]

+        if module.params["patch_merge"]:
+            patch_body = json.dumps(
+                {"spec": {"customContainerImages": cr["spec"]["customContainerImages"]}}
+            )
+            rc, out, err = _run_oc(
+                module,
+                [
+                    "patch",
+                    "openstackversion",
+                    module.params["metadata_name"],
+                    "-n",
+                    module.params["namespace"],
+                    "--type",
+                    "merge",
+                    "-p",
+                    patch_body,
Evidence
The module’s new patch_merge path runs oc patch openstackversion ... instead of oc apply, and
the EDPM prepare role defaults patch_merge on for S2I image overrides. Previously the prepare path
used update_containers, which applies the CR and therefore creates it if missing.

plugins/modules/set_containers.py[640-663]
roles/edpm_prepare/tasks/kustomize_and_deploy.yml[90-101]
roles/update_containers/tasks/main.yml[29-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When `patch_merge=true`, the module runs `oc patch openstackversion <name> ...`. `oc patch` fails if the object does not exist, unlike the previous `oc apply` behavior which would create the CR. This breaks EDPM prepare flows that attempt to apply S2I images before any OpenStackVersion CR has been created.

### Issue Context
`roles/edpm_prepare/tasks/kustomize_and_deploy.yml` enables `patch_merge` by default for S2I overrides, so this path is exercised automatically in those jobs.

### Fix Focus Areas
- plugins/modules/set_containers.py[640-666]
- roles/edpm_prepare/tasks/kustomize_and_deploy.yml[90-101]

### Implementation notes
- In `patch_merge` mode, first detect whether the OpenStackVersion exists (e.g., `oc get openstackversion <name> -n <ns>`).
- If it does not exist, run `oc apply -f <dest_path>` to create it, then run the merge patch (or skip patch if creation already contains the desired map).
- Add/extend unit tests to cover the fallback behavior when the `oc get` indicates NotFound.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. Empty apply clears images 🐞 Bug ≡ Correctness
Description
edpm_prepare now runs set_containers whenever cifmw_set_containers_apply=true, even if no
images/openstack defaults are provided; the module always generates spec.customContainerImages: {}
and oc apply will push that empty map to the cluster. This can unintentionally wipe previously
configured customContainerImages if a caller sets cifmw_set_containers_apply without also
supplying images/include_openstack.
Code

roles/edpm_prepare/tasks/kustomize_and_deploy.yml[R58-61]

+      (cifmw_set_containers_images is defined and
+      cifmw_set_containers_images | length > 0) or
+      (cifmw_set_containers_apply | default(false) | bool))
+  cifmw.general.set_containers:
Evidence
The EDPM prepare task can run based on cifmw_set_containers_apply alone, while the module always
emits spec.customContainerImages from its computed images dict (which can be empty) and then
applies it with oc apply when patch_merge is not enabled.

roles/edpm_prepare/tasks/kustomize_and_deploy.yml[42-60]
plugins/modules/set_containers.py[461-469]
plugins/modules/set_containers.py[640-663]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new `when` condition allows running `cifmw.general.set_containers` solely because `cifmw_set_containers_apply` is true. If the resulting `images` list is empty and `include_openstack` is false, the module generates `spec.customContainerImages: {}` and (with `patch_merge` false) applies that to the cluster, clearing any existing `customContainerImages`.

### Issue Context
This is a behavioral change in EDPM prepare: the task can now run even when no image inputs are present.

### Fix Focus Areas
- roles/edpm_prepare/tasks/kustomize_and_deploy.yml[42-61]
- roles/edpm_prepare/tasks/kustomize_and_deploy.yml[81-104]
- plugins/modules/set_containers.py[461-469]
- plugins/modules/set_containers.py[640-663]

### Implementation notes
Pick one of:
1. Tighten the `when:` so `cifmw_set_containers_apply` alone does not trigger the task unless either `include_openstack=true` or at least one image override is present.
2. Alternatively, inside the module, if `apply=true` and `customContainerImages` is empty, fail fast with a clear message unless an explicit flag allows clearing.
3. Or default to `patch_merge=true` when `apply=true` and the image set is empty (safer no-op), though failing fast is usually clearer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread roles/edpm_prepare/tasks/kustomize_and_deploy.yml Outdated
Comment thread plugins/modules/set_containers.py Outdated
Comment thread roles/edpm_prepare/tasks/kustomize_and_deploy.yml Outdated
@rebtoor rebtoor changed the title [edpm_prepare][set_containers] Wire set_containers into EDPM prepare path [update_containers] Migrate role to set_containers module Aug 21, 2026
@rebtoor
rebtoor force-pushed the feature/edpm-set-containers-s2i branch 2 times, most recently from 9890761 to 699b551 Compare August 21, 2026 08:27
@rebtoor
rebtoor marked this pull request as draft August 21, 2026 08:30
@rebtoor
rebtoor force-pushed the feature/edpm-set-containers-s2i branch from 699b551 to 6edaaf8 Compare August 21, 2026 08:31
@rebtoor rebtoor changed the title [update_containers] Migrate role to set_containers module [edpm_prepare] Apply set_containers overrides from job vars Aug 21, 2026
@rebtoor
rebtoor force-pushed the feature/edpm-set-containers-s2i branch 2 times, most recently from 369b1fd to 0ccb20d Compare August 21, 2026 10:28
@rebtoor
rebtoor marked this pull request as ready for review August 21, 2026 13:45
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@centosinfra-prod-github-app

Copy link
Copy Markdown

Build failed (check pipeline). Post recheck (without leading slash)
to rerun all jobs. Make sure the failure cause has been resolved before
you rerun jobs.

https://gateway-cloud-softwarefactory.apps.ocp.cloud.ci.centos.org/zuul/t/rdoproject.org/buildset/ac0ffc1bade34d559fbef9da4223eb68

✔️ openstack-k8s-operators-content-provider SUCCESS in 3h 32m 12s
✔️ podified-multinode-edpm-deployment-crc SUCCESS in 1h 29m 58s
✔️ cifmw-crc-podified-edpm-baremetal SUCCESS in 1h 53m 18s
✔️ cifmw-crc-podified-edpm-baremetal-minor-update SUCCESS in 2h 21m 25s
✔️ cifmw-pod-zuul-files SUCCESS in 6m 58s
✔️ openstack-k8s-operators-content-provider-bootc SUCCESS in 2h 07m 47s
cifmw-crc-podified-edpm-baremetal-bootc FAILURE in 1h 28m 19s
✔️ adoption-standalone-to-crc-ceph-provider SUCCESS in 3h 15m 55s
✔️ noop SUCCESS in 0s
✔️ cifmw-pod-ansible-test SUCCESS in 10m 05s
✔️ cifmw-pod-pre-commit SUCCESS in 10m 05s
✔️ cifmw-molecule-edpm_prepare SUCCESS in 6m 28s

rebtoor added a commit to openstack-k8s-operators/watcher-operator that referenced this pull request Aug 21, 2026
Move the speculative watcher consumer off the late pre_tests
OpenStackVersion patch and onto cifmw_set_containers_images so images
are injected before the control plane deploys. Add
opendev-watcher-s2i-pipeline for Gerrit openstack/watcher only; the
shared edpm template also covers watcherclient and tempest-plugin.

Depends-On: openstack-k8s-operators/s2i-openstack-containers#91
Depends-On: openstack-k8s-operators/ci-framework#4131
Co-authored-by: Cursor <cursoragent@cursor.com>
rebtoor added a commit to openstack-k8s-operators/watcher-operator that referenced this pull request Aug 21, 2026
Move the speculative watcher consumer off the late pre_tests
OpenStackVersion patch and onto cifmw_set_containers_images so images
are injected before the control plane deploys. Fold the s2i provider
and deploy jobs into opendev-watcher-edpm-pipeline so RDO config does
not need a second template. Limit those jobs to watcher/ source paths
so python-watcherclient and watcher-tempest-plugin keep EDPM-only.

Depends-On: openstack-k8s-operators/s2i-openstack-containers#91
Depends-On: openstack-k8s-operators/ci-framework#4131
Co-authored-by: Cursor <cursoragent@cursor.com>
@rebtoor

rebtoor commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

recheck

@Valkyrie00

Copy link
Copy Markdown
Contributor

/agentic_review

Valkyrie00
Valkyrie00 previously approved these changes Aug 24, 2026

@Valkyrie00 Valkyrie00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

Comment thread plugins/modules/set_containers.py Outdated
Comment on lines +503 to +506
"-n",
params["namespace"],
"-o",
"jsonpath={.spec.customContainerImages}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Invalid jsonpath json parse 🐞 Bug ≡ Correctness

_fetch_existing_custom_container_images requests jsonpath output but then json.loads() it; jsonpath
output is not guaranteed to be valid JSON, so preserve_unlisted can fail with a decode error and
block edpm_prepare overrides.
Agent Prompt
### Issue description
`_fetch_existing_custom_container_images()` runs `oc get ... -o jsonpath={.spec.customContainerImages}` and then attempts `json.loads(out)`. JSONPath output is not reliably JSON, so this can raise a decode failure and break the module when `preserve_unlisted=true`.

### Issue Context
The goal of `preserve_unlisted` is to merge the current cluster map with overrides; that requires a reliable, parseable representation of `spec.customContainerImages`.

### Fix Focus Areas
- plugins/modules/set_containers.py[496-524]

### Suggested fix
- Change the `oc get` to `-o json` and `json.loads(out)` the whole object, then extract `obj.get("spec", {}).get("customContainerImages", {})`.
  - Alternatively, if available in your target oc/kubectl, use `-o jsonpath-as-json=...`.
- Keep the existing type-check (`dict`) and fail with a clear error if extraction yields a non-mapping.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread plugins/modules/set_containers.py Outdated
Comment on lines +510 to +511
if rc != 0 or not out.strip():
return {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Preserve ignores oc get failures 🐞 Bug ☼ Reliability

When preserve_unlisted is enabled, oc get failures (rc!=0) return an empty dict, so the module may
apply only the override list and unintentionally drop existing customContainerImages entries.
Agent Prompt
### Issue description
With `preserve_unlisted=true`, `_fetch_existing_custom_container_images()` returns `{}` on any `oc get` non-zero rc, silently disabling preservation and risking loss of existing image mappings.

### Issue Context
The PR’s design states partial override lists should keep unspecified images from the current OpenStackVersion CR. Returning `{}` on errors violates that guarantee.

### Fix Focus Areas
- plugins/modules/set_containers.py[496-524]
- plugins/modules/set_containers.py[635-643]

### Suggested fix
- If `rc != 0`, call `module.fail_json(msg=..., rc=rc, stdout=out, stderr=err)`.
- If you need to tolerate “not found” (CR absent), detect that specific case (e.g., parse `err` for NotFound / check `out`), and only then return `{}`.
- Consider also failing if `out.strip()` is empty while `rc==0` (unexpected) unless you can prove it represents an empty map.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +635 to +639
if module.params["preserve_unlisted"]:
if not kubeconfig:
module.fail_json(msg="kubeconfig is required when preserve_unlisted=true")
existing_images = _fetch_existing_custom_container_images(
module, module.params, kubeconfig

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Check mode hits cluster 🐞 Bug ☼ Reliability

preserve_unlisted triggers an oc get even in Ansible check mode, so set_containers no longer behaves
as a pure local diff in check mode and may fail in environments where cluster access isn’t
available.
Agent Prompt
### Issue description
The module advertises `supports_check_mode=True`, but the new `preserve_unlisted` branch performs a live `oc get` regardless of check mode.

### Issue Context
Check mode is commonly used for dry-run validation and should avoid side-effecting operations and ideally avoid requiring external connectivity.

### Fix Focus Areas
- plugins/modules/set_containers.py[635-643]

### Suggested fix
- Add a guard:
  - If `module.check_mode` and `preserve_unlisted` is true, either:
    - skip the cluster fetch and document that preservation cannot be evaluated in check mode, or
    - read preservation source from an existing on-disk manifest if present.
- If you choose to skip, consider setting a result field/warning to make behavior explicit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +338 to +363
def test_present_preserve_unlisted_calls_oc_get_and_apply(self):
set_module_args(
self._args(
apply=True,
preserve_unlisted=True,
kubeconfig="/home/zuul/.kube/config",
images=[
dict(
name="glanceAPIImage",
full_registry="registry.example:5000/ns/glance:tag",
)
],
)
)
with patch(_MOD + ".os.path.exists", return_value=False), patch(
_MOD + ".os.makedirs"
), patch("builtins.open", mock_open()), patch(
_MOD + "._run_oc",
side_effect=[(0, '{"keystoneAPIImage": "keep-me"}', ""), (0, "", "")],
) as mock_oc:
with self.assertRaises(AnsibleExitJson) as cm:
set_containers.run_module()
self.assertEqual(mock_oc.call_count, 2)
self.assertEqual(mock_oc.call_args_list[0][0][1][0], "get")
self.assertEqual(mock_oc.call_args_list[1][0][1][0], "apply")
self.assertTrue(cm.exception.args[0]["changed"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

4. Test doesn't validate merge 🐞 Bug ⚙ Maintainability

The new preserve_unlisted unit test only asserts that oc get/apply are called, but never verifies
that the written CR actually retains the existing image plus the override, so regressions in merge
behavior can slip through.
Agent Prompt
### Issue description
`test_present_preserve_unlisted_calls_oc_get_and_apply` does not assert the merged `customContainerImages` content written to `dest_path`, so it doesn't actually test the preservation behavior.

### Issue Context
The PR’s key behavior is merging existing images with overrides; validating file content would catch breakages in parsing/merge logic.

### Fix Focus Areas
- tests/unit/modules/test_set_containers.py[338-363]

### Suggested fix
- Capture the file write from `mock_open()` and parse the YAML written.
- Assert that `spec.customContainerImages` contains both:
  - the preserved key from the mocked oc get output, and
  - the overridden key from the input `images` list.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 0ccb20d

Add a small edpm_prepare task that calls cifmw.general.set_containers
when jobs provide cifmw_set_containers_images. The legacy
update_containers role is unchanged.

Add preserve_unlisted to set_containers so partial override lists keep
unspecified images from the current OpenStackVersion CR before apply.
Fetch the current CR with oc get -o json and fail on non-NotFound
lookup errors so preservation cannot silently drop existing images.

Assisted-By: Cursor
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Roberto Alfieri <ralfieri@redhat.com>
@rebtoor
rebtoor force-pushed the feature/edpm-set-containers-s2i branch from 0ccb20d to 749545e Compare August 24, 2026 09:53
@openshift-ci openshift-ci Bot removed the lgtm label Aug 24, 2026
@openshift-ci

openshift-ci Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

New changes are detected. LGTM label has been removed.

@rebtoor

rebtoor commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the Qodo comments that still applied after the preserve_unlisted rewrite:

  • Fetch existing images with oc get -o json and extract spec.customContainerImages instead of jsonpath + json.loads.
  • oc get failures are now hard errors, except NotFound (treated as an empty map).
  • Unit tests now assert the merged map, NotFound fallback, and non-NotFound get failure.

Left as-is: check-mode still does a read-only oc get so dry-run can report an accurate changed. The older comments about _edpm_s2i_set_containers_images, patch_merge, and apply-with-empty-images no longer apply to this patch.

@evallesp evallesp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/approve

@openshift-ci

openshift-ci Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: evallesp

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@centosinfra-prod-github-app

Copy link
Copy Markdown

Build failed (check pipeline). Post recheck (without leading slash)
to rerun all jobs. Make sure the failure cause has been resolved before
you rerun jobs.

https://gateway-cloud-softwarefactory.apps.ocp.cloud.ci.centos.org/zuul/t/rdoproject.org/buildset/286a7fbba86343af89f70518a47c1b95

✔️ openstack-k8s-operators-content-provider SUCCESS in 3h 10m 17s
podified-multinode-edpm-deployment-crc FAILURE in 1h 51m 30s
cifmw-crc-podified-edpm-baremetal FAILURE in 2h 01m 19s
cifmw-crc-podified-edpm-baremetal-minor-update FAILURE in 1h 57m 58s
✔️ cifmw-pod-zuul-files SUCCESS in 5m 27s
✔️ openstack-k8s-operators-content-provider-bootc SUCCESS in 3h 35m 53s
cifmw-crc-podified-edpm-baremetal-bootc FAILURE in 2h 03m 50s
adoption-standalone-to-crc-ceph-provider FAILURE in 2h 50m 12s
✔️ noop SUCCESS in 0s
✔️ cifmw-pod-ansible-test SUCCESS in 9m 34s
cifmw-pod-pre-commit FAILURE in 8m 56s
✔️ cifmw-molecule-edpm_prepare SUCCESS in 5m 26s

rebtoor added a commit to openstack-k8s-operators/watcher-operator that referenced this pull request Aug 24, 2026
Inject s2i-built watcher images through cifmw_set_containers_images
during edpm_prepare so they land in OpenStackVersion before the control
plane deploys. github-check uses the non-meta operator content provider
for operator images and appends the s2i registry as an extra insecure
registry so CRC can pull both the operator catalog and watcher service
images.

Fold the s2i provider and deploy jobs into opendev-watcher-edpm-pipeline
and limit them to watcher/ source paths so python-watcherclient and
watcher-tempest-plugin keep EDPM-only.

Depends-On: openstack-k8s-operators/s2i-openstack-containers#91
Depends-On: openstack-k8s-operators/ci-framework#4131
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants