Add Podman quadlet .image file support#501
Conversation
|
Do you intend to resolve #277 with this? |
I'm wondering if we can leverage the built in Also not sure if you can use |
As we stated before, tieing ourselves to an RPM laying down some base container will slow us down as any time it requires a change we have to wait for the RPM to cycle and it would split the base from any overrides. I think we would end up just deploying everything via overrides. Just feel like friction right now. I did think about this and here is an alternative using this concept: Updated ProposalPlace .image files alongside .container files in /etc/containers/systemd/, using quadlet's native drop-in mechanism for overrides. Layout: /etc/containers/systemd/ Precedence (last wins):
Numbered prefixes enforce ordering with gaps for future layers. What this eliminates:
What stays the same:
|
This is what I had in mind.
This is IMHO the big benefit. |
|
i like this approach too and will change my PR to only have the feature addition but no changes to image locations. looking forward to this getting merged 🙂 |
|
Being able to override the container images easily would be really beneficial for development. If we eventually have containers built from PRs much like packit does with RPMs today, you could just drop those in. Or, when we need to test new versions of Pulp, we could simply drop that new Pulp container in (theforeman/pulp-oci-images#13). |
9b0d041 to
2f9968f
Compare
2f9968f to
e392b79
Compare
e392b79 to
f0e2546
Compare
f0e2546 to
6057acc
Compare
|
I'm not yet sure what change in the design is not leading to the failure. I will need to follow up with deeper investigation. |
06aa84d to
de726ff
Compare
|
I have reworked this PR based on testing and learning between the use of |
|
|
||
| Both `registries.conf` and `.image.d` drop-ins can redirect where an image is pulled from, but they behave differently and suit different use cases. | ||
|
|
||
| `registries.conf` applies a transparent redirect at pull time — the image is fetched from the `location` registry but stored in local storage under the original `prefix` name. This means `podman images` shows the upstream name (e.g., `quay.io/foreman/foreman:nightly`), and the `.image` quadlet continues to reference that same name. This works well when the private registry mirrors upstream image names and tags exactly. |
There was a problem hiding this comment.
Link to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md as the official docs?
|
|
||
| #### registries.conf vs .image.d drop-ins | ||
|
|
||
| Both `registries.conf` and `.image.d` drop-ins can redirect where an image is pulled from, but they behave differently and suit different use cases. |
There was a problem hiding this comment.
Documenting both use cases is excellent.
|
|
||
| `registries.conf` applies a transparent redirect at pull time — the image is fetched from the `location` registry but stored in local storage under the original `prefix` name. This means `podman images` shows the upstream name (e.g., `quay.io/foreman/foreman:nightly`), and the `.image` quadlet continues to reference that same name. This works well when the private registry mirrors upstream image names and tags exactly. | ||
|
|
||
| `.image.d` drop-ins directly replace the `Image=` value in the quadlet unit. The image is pulled from and stored under the new reference. This is required when the image name or tag changes completely (e.g., `quay.io/foreman/foreman:nightly` → `registry.example.com/org/foreman-rhel9:stream`), since `registries.conf` cannot remap image names — only registry/namespace locations. |
There was a problem hiding this comment.
Should this point to https://docs.podman.io/en/latest/markdown/podman-image.unit.5.html?
| podman login registry.example.com --authfile=/etc/foreman/registry-auth.json | ||
| ``` | ||
|
|
||
| ##### Disconnected install from ISO |
There was a problem hiding this comment.
ISO is a very specific distribution format.
| ##### Disconnected install from ISO | |
| ##### Disconnected install from local media |
|
|
||
| ```bash | ||
| ./foremanctl deploy | ||
| podman login <registry> --authfile=/etc/foreman/registry-auth.json |
There was a problem hiding this comment.
Does the order matter or can you write it like this? Removing something from the end is easier than the middle.
| podman login <registry> --authfile=/etc/foreman/registry-auth.json | |
| podman login --authfile=/etc/foreman/registry-auth.json <registry> |
|
|
||
| The `foremanctl pull-images` command is an optional pre-deployment step that pulls all container images before running `foremanctl deploy`. This reduces deploy time and allows pre-staging images separately from deployment. | ||
|
|
||
| `pull-images` deploys the `.image` unit files (making them available for quadlet to merge with any existing drop-ins from installed RPMs), then starts each `*-image.service` to perform the actual pull. To ensure mutable tags (such as `nightly`, `latest`, or `stream`) are always refreshed, `pull-images` temporarily creates a `Policy=always` drop-in before starting each service and removes it afterward, restoring `Policy=missing` for normal operation: |
There was a problem hiding this comment.
Temporary files feel a bit nasty because it it's interrupted then they may not be cleaned up. I don't think we need to solve it right now, but I wonder if we could somehow leverage podman auto-update to pull images.
Shall we track that in a follow up issue?
There was a problem hiding this comment.
No. I previously researched it and it only updates based on the policy and there is no way to "temporarily" set the policy. While I agree temporary files are not the best, there really isn't another way to do this.
Spit-balling, you could theoretically have two separate services but then you gotta manage the drop-in for both. Another idea possibly, would be to use podman pull by printing the service and parsing the conf file for the image name, or using podman quadlet print.
There was a problem hiding this comment.
I wonder if we should file a podman issue to see if there's tooling to support this.
de726ff to
94884cf
Compare
Introduce .image quadlet units to decouple image sourcing from container definitions. Container roles now reference Image=<name>.image instead of full registry URLs, and a new images role handles deployment with a three-tier precedence model: admin overrides (/etc/containers/systemd/<name>.image.d/) > vendor RPMs (/usr/share/containers/systemd/<name>.image.d/) > generated defaults (/etc/containers/systemd/<name>.image). Resolves: theforeman#277 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
94884cf to
82c4815
Compare
| In air-gapped environments, container images must be brought in without network access. `registries.conf` cannot express non-registry sources, but Podman can read images from local archive files. Images can be transported via USB or other local media using `podman save` / `podman load`, then referenced via drop-ins: | ||
|
|
||
| See [podman-export(1)](https://docs.podman.io/en/latest/markdown/podman-export.1.html) for producing archive files. |
There was a problem hiding this comment.
If you use the example below you don't need podman load because it's implicit. I also wonder what the difference between podman save and podman export is. Perhaps drop the last part?
| In air-gapped environments, container images must be brought in without network access. `registries.conf` cannot express non-registry sources, but Podman can read images from local archive files. Images can be transported via USB or other local media using `podman save` / `podman load`, then referenced via drop-ins: | |
| See [podman-export(1)](https://docs.podman.io/en/latest/markdown/podman-export.1.html) for producing archive files. | |
| In air-gapped environments, container images must be brought in without network access. `registries.conf` cannot express non-registry sources, but Podman can read images from local archive files. These images can be transported via USB or other local media, then referenced via drop-ins. | |
| See [podman-export(1)](https://docs.podman.io/en/latest/markdown/podman-export.1.html) for producing archive files. |
Why are you introducing these changes? (Problem description, related links)
Container image references were embedded directly in each service role as variable defaults, making it difficult to override images for product builds, disconnected installs, or developer testing without modifying foremanctl-managed files.
Resolves: #277
What are the changes introduced in this pull request?
Image unit generation
imagesrole withdeploy_image.yamlthat generates a.imagequadlet unit file and.image.d/drop-in directory for each container imageimage.yamltask that calls theimagesrole with its image name and tag, removing the per-role*_container_imagevariable defaultsImage=<name>.imageinstead of full registry URLs — systemd ensures the image unit is active before any dependent container startsDrop-in override hierarchy
Three tiers of override are supported without touching foremanctl-managed files:
/usr/share/containers/systemd/<name>.image.d/10-product.conf/usr/share/containers/systemd/<name>.image.d/20-archive.conf/etc/containers/systemd/<name>.image.d/90-user.confThe last
.image.ddrop-in wins.registries.confis the right tool when a private registry mirrors upstream names exactly;.image.ddrop-ins are required when image names or tags change (e.g.foreman:nightly→foreman-rhel9:stream).Authenticated registry handling
Each image unit gets a systemd service drop-in at
/etc/systemd/system/<name>-image.service.d/auth.confthat setsREGISTRY_AUTH_FILE=/etc/foreman/registry-auth.json. Users with authenticated registries runpodman login <registry> --authfile=/etc/foreman/registry-auth.jsonbefore deploying; podman silently ignores the file if it does not exist.pull-images
pull-imagesdeploys image units (respecting any vendor drop-ins already in place), then creates a temporaryPolicy=alwaysdrop-in (00-pull-always.conf) for each image, performs adaemon_reload, restarts all*-image.serviceunits in parallel, and removes the drop-ins in analways:block so cleanup happens even on failure. The pull honours any.image.doverrides already present.Migration containers converted to quadlet oneshot services
foreman-db-migrateandpulpcore-manager-migrateare now declared as quadlet.containerunits withType=oneshotrather than being run as ephemeral containers viapodman_containerwithdetach: false. Ansible starts them withstate: startedand waits viaasync/async_status. This keeps migration runs in the systemd journal and makes failure state visible viasystemctl status.Tests
tests/images_test.py— core image files,.image.d/drop-in directories,*-imageservice existence, auth drop-in presence and content, postgresql conditional ondatabase_mode, foreman-proxy conditional onenabled_featurestests/iop/images_test.py— same checks for all IOP image units, gated on theiopfeature markerHow to test this pull request
./foremanctl deploy.imagefiles exist under/etc/containers/systemd/for each service and that<name>-image.serviceunits are present/etc/systemd/system/<name>-image.service.d/auth.confexists for each image./foremanctl pull-imagesand verify images are pulled; re-run and confirm it pulls again (Policy=always enforced, not just missing)systemctl daemon-reloadand confirmsystemctl cat foreman-image.servicereflects the override./forge testand confirmimages_test.pyandiop/images_test.pypassChecklist