Skip to content

ART-21305: run rpm-lockfile-prototype in podman container#3031

Draft
fgallott wants to merge 1 commit into
openshift-eng:mainfrom
fgallott:rpm-lockfile-prototype-containerized
Draft

ART-21305: run rpm-lockfile-prototype in podman container#3031
fgallott wants to merge 1 commit into
openshift-eng:mainfrom
fgallott:rpm-lockfile-prototype-containerized

Conversation

@fgallott

@fgallott fgallott commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Replace host system Python subprocess with podman container execution. The container image is built on demand from a bundled Containerfile (Fedora base, pinned to v0.22.0) if not already present locally, removing the dependency on host-installed python3-dnf and rpm-lockfile-prototype.

The container mounts host entitlement certs for CDN repo access and bakes in Red Hat IT Root CA certs for internal repo connectivity.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED

Summary by CodeRabbit

  • New Features
    • RPM lockfile resolution now runs in a containerized environment instead of a local runtime process.
    • Support added for selecting a custom container image, with a default image provided.
    • Persistent caching added for package metadata and an RPM database cache inside the container.
    • Registry authentication and host entitlement/certificate files are now mounted into the container when provided.
  • Bug Fixes
    • Improved retry behavior when the RPM database cache is corrupted, re-attempting using the container workflow.

@openshift-ci

openshift-ci Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 8, 2026
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

RpmResolver now executes rpm-lockfile-prototype inside a configurable podman image, mounting working, DNF, RPMDB, entitlement, certificate, and registry-auth paths. Constants and tests were updated for image selection, command arguments, caches, authentication, lockfile output, and RPMDB retry behavior.

Changes

RPM lockfile containerization

Layer / File(s) Summary
Container configuration and resolver setup
doozer/doozerlib/lockfile_prototype/constants.py, doozer/doozerlib/lockfile_prototype/resolver.py
Container image and RPMDB cache constants replace local execution constants, and RpmResolver accepts an optional image override.
Podman command and retry execution
doozer/doozerlib/lockfile_prototype/resolver.py
The resolver constructs podman commands with work, cache, credential, certificate, entitlement, and registry-auth mounts, while preserving corrupt-RPMDB cleanup and retry behavior.
Podman behavior validation
doozer/tests/lockfile_prototype/test_resolver.py
Tests validate podman invocation, image selection, cache and auth mounts, mounted lockfile output, and RPMDB retry handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RpmResolver
  participant podman
  participant rpm-lockfile-prototype
  RpmResolver->>podman: Run configured image with mounted paths
  podman->>rpm-lockfile-prototype: Execute lockfile resolution
  rpm-lockfile-prototype-->>RpmResolver: Write lockfile and return status
  RpmResolver->>podman: Retry after clearing corrupt RPMDB cache
Loading

Possibly related PRs

Suggested labels: approved, lgtm

Suggested reviewers: ashwindasr, joepvd, rayfordj


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error cmd_gather_async logs the full podman command, and resolver now embeds an internal registry image plus auth-file mount/env in that command. Redact or stop logging the full command, or strip the image pullspec/auth mount details before passing it to cmd_gather_async.
Ai-Attribution ⚠️ Warning PR description uses Co-Authored-By for Claude Opus 4.6; no Assisted-by/Generated-by trailers were found in the diff or commit message. Replace the AI Co-Authored-By trailer with a Red Hat-style Assisted-by or Generated-by trailer, or remove the AI attribution if not required.
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: running rpm-lockfile-prototype in a Podman container.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret/token comparisons appear in the changed resolver/constants/tests.
Container-Privileges ✅ Passed The PR only changes Python resolver/tests; no container/K8s manifests or privileged/hostPID/hostNetwork/allowPrivilegeEscalation settings were added.
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets, credentials, or long base64 strings were added; the new registry URL and auth-file paths are non-secret.
No-Injection-Vectors ✅ Passed Resolver builds an argv list for create_subprocess_exec, uses yaml.safe_load, and no shell=True/eval/os.system patterns were added in the touched files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Comment on lines +82 to +97
# Work directory: rpms.in.yaml input and rpms.lock.yaml output
cmd.extend(["-v", f"{tmpdir}:/work:Z"])

# DNF repodata cache
cmd.extend(["-v", f"{self._cache_path}:/cache:Z"])
cmd.extend(["-e", "RPM_LOCKFILE_PROTOTYPE_DNF_CACHE=/cache"])

# RPMDB cache: host path → container /root/.cache/...
RPMDB_CACHE_PATH.mkdir(parents=True, exist_ok=True)
cmd.extend(["-v", f"{RPMDB_CACHE_PATH}:{CONTAINER_RPMDB_CACHE_PATH}:Z"])

# Mount host entitlement certs for accessing protected repos.
# Use :ro without :Z — SELinux won't allow relabeling system dirs.
for host_path in ("/etc/pki/entitlement", "/etc/rhsm/ca", "/etc/pki/rpm-gpg"):
if Path(host_path).is_dir():
cmd.extend(["-v", f"{host_path}:{host_path}:ro"])

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.

Suggested change
# Work directory: rpms.in.yaml input and rpms.lock.yaml output
cmd.extend(["-v", f"{tmpdir}:/work:Z"])
# DNF repodata cache
cmd.extend(["-v", f"{self._cache_path}:/cache:Z"])
cmd.extend(["-e", "RPM_LOCKFILE_PROTOTYPE_DNF_CACHE=/cache"])
# RPMDB cache: host path → container /root/.cache/...
RPMDB_CACHE_PATH.mkdir(parents=True, exist_ok=True)
cmd.extend(["-v", f"{RPMDB_CACHE_PATH}:{CONTAINER_RPMDB_CACHE_PATH}:Z"])
# Mount host entitlement certs for accessing protected repos.
# Use :ro without :Z — SELinux won't allow relabeling system dirs.
for host_path in ("/etc/pki/entitlement", "/etc/rhsm/ca", "/etc/pki/rpm-gpg"):
if Path(host_path).is_dir():
cmd.extend(["-v", f"{host_path}:{host_path}:ro"])
# Work directory: rpms.in.yaml input and rpms.lock.yaml output
cmd.extend(["-v", f"{tmpdir}:/work:z"])
# DNF repodata cache
cmd.extend(["-v", f"{self._cache_path}:/cache:z"])
cmd.extend(["-e", "RPM_LOCKFILE_PROTOTYPE_DNF_CACHE=/cache"])
# RPMDB cache: host path → container /root/.cache/...
RPMDB_CACHE_PATH.mkdir(parents=True, exist_ok=True)
cmd.extend(["-v", f"{RPMDB_CACHE_PATH}:{CONTAINER_RPMDB_CACHE_PATH}:z"])
# Mount host entitlement certs for accessing protected repos.
# Use :ro without :z — SELinux won't allow relabeling system dirs.
for host_path in ("/etc/pki/entitlement", "/etc/rhsm/ca", "/etc/pki/rpm-gpg"):
if Path(host_path).is_dir():
cmd.extend(["-v", f"{host_path}:{host_path}:ro"])

I'm a bit rusty from my pre-ART days when I was doing similar with a container, but thought I needed z instead of Z for reasons (exclusive vs shared). We should evaluate what this looks like when running 2+ podman run simultaneously.

Comment on lines +55 to +62
rc, _, _ = await cmd_gather_async(["podman", "image", "exists", self._image], check=False)
if rc == 0:
return
self.logger.info("Building rpm-lockfile-prototype container image: %s", self._image)
rc, _, stderr = await cmd_gather_async(
["podman", "build", "-t", self._image, "-f", str(RPM_LOCKFILE_CONTAINERFILE), "."],
check=False,
)

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.

Thoughts on guarding this for risk of parallel executions due to potential timing of 2+ determining it doesn't exist and executing image builds?

Not sure what the real-world likelihood might be (low to non-existent?) nor what the impact might be in application (build it a couple times?) when it doesn't exist.

Comment thread doozer/doozerlib/lockfile_prototype/Containerfile Outdated
Comment thread doozer/doozerlib/lockfile_prototype/Containerfile Outdated
@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 20, 2026
@fgallott fgallott changed the title feat(lockfile): run rpm-lockfile-prototype in podman container ART-21305: run rpm-lockfile-prototype in podman container Jun 26, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 26, 2026
@openshift-ci-robot

openshift-ci-robot commented Jun 26, 2026

Copy link
Copy Markdown

@fgallott: This pull request references ART-21305 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Replace host system Python subprocess with podman container execution. The container image is built on demand from a bundled Containerfile (Fedora base, pinned to v0.22.0) if not already present locally, removing the dependency on host-installed python3-dnf and rpm-lockfile-prototype.

The container mounts host entitlement certs for CDN repo access and bakes in Red Hat IT Root CA certs for internal repo connectivity.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED

Instructions 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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign rayfordj for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@fgallott fgallott force-pushed the rpm-lockfile-prototype-containerized branch from 2ecc7f7 to 5758efb Compare July 13, 2026 16:08
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 13, 2026
rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
@fgallott fgallott force-pushed the rpm-lockfile-prototype-containerized branch from 5758efb to 9975723 Compare July 13, 2026 16:09

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
doozer/tests/lockfile_prototype/test_resolver.py (1)

61-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

host_tmpdir can be unbound if no /work mount is found.

If cmd ever lacks a -v ...:/work:... entry (e.g. a future refactor of _build_podman_cmd), the loop falls through without assigning host_tmpdir, raising a confusing UnboundLocalError instead of a clear assertion failure.

♻️ Proposed fix
+        host_tmpdir = None
         for i, arg in enumerate(cmd):
             if arg == "-v" and ":/work:" in cmd[i + 1]:
                 host_tmpdir = cmd[i + 1].split(":")[0]
                 break
+        self.assertIsNotNone(host_tmpdir, "Expected a /work volume mount in podman command")
         host_outfile = os.path.join(host_tmpdir, DEFAULT_RPM_LOCKFILE_NAME)
🤖 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 `@doozer/tests/lockfile_prototype/test_resolver.py` around lines 61 - 65,
Initialize or validate host_tmpdir in the command-processing logic around
_build_podman_cmd so the absence of a -v ...:/work:... mount produces a clear
assertion failure before host_outfile is constructed. Preserve the existing
extraction and break behavior when the work mount is present.
doozer/doozerlib/lockfile_prototype/resolver.py (1)

97-107: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Auth file mount isn't existence-checked, unlike the entitlement cert mounts.

Lines 99-101 guard entitlement/cert mounts with Path(host_path).is_dir(), but auth_file (lines 104-107) is mounted unconditionally once the env var is set, with no Path(auth_file).is_file() check. A stale/misconfigured QUAY_AUTH_FILE/REGISTRY_AUTH_FILE will fail loudly at podman run time, but adding the same defensive check would make behavior consistent and the failure clearer.

♻️ Proposed fix
         auth_file = os.environ.get("QUAY_AUTH_FILE") or os.environ.get("REGISTRY_AUTH_FILE")
-        if auth_file:
+        if auth_file and Path(auth_file).is_file():
             cmd.extend(["-v", f"{auth_file}:/auth/auth.json:ro,z"])
             cmd.extend(["-e", "REGISTRY_AUTH_FILE=/auth/auth.json"])
🤖 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 `@doozer/doozerlib/lockfile_prototype/resolver.py` around lines 97 - 107,
Update the Registry auth handling around auth_file to mount the file only when
Path(auth_file).is_file() is true, while preserving the existing
environment-variable precedence and mount arguments. Do not set
REGISTRY_AUTH_FILE or add the mount for missing or invalid paths.
🤖 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.

Nitpick comments:
In `@doozer/doozerlib/lockfile_prototype/resolver.py`:
- Around line 97-107: Update the Registry auth handling around auth_file to
mount the file only when Path(auth_file).is_file() is true, while preserving the
existing environment-variable precedence and mount arguments. Do not set
REGISTRY_AUTH_FILE or add the mount for missing or invalid paths.

In `@doozer/tests/lockfile_prototype/test_resolver.py`:
- Around line 61-65: Initialize or validate host_tmpdir in the
command-processing logic around _build_podman_cmd so the absence of a -v
...:/work:... mount produces a clear assertion failure before host_outfile is
constructed. Preserve the existing extraction and break behavior when the work
mount is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8e480a51-ef3b-405d-b3ea-daf3e06d341e

📥 Commits

Reviewing files that changed from the base of the PR and between 0a045b0 and 9975723.

📒 Files selected for processing (3)
  • doozer/doozerlib/lockfile_prototype/constants.py
  • doozer/doozerlib/lockfile_prototype/resolver.py
  • doozer/tests/lockfile_prototype/test_resolver.py

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

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants