Skip to content

Commit 9b0d041

Browse files
ehelmsclaude
andcommitted
Add Podman quadlet .image file support
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/foremanctl/images.d/) > vendor RPMs (/usr/share/foremanctl/images.d/) > generated defaults. Resolves: #277 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cef4484 commit 9b0d041

68 files changed

Lines changed: 483 additions & 272 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
vagrant ssh quadlet -- sudo systemctl restart fapolicyd
120120
- name: Run image pull
121121
run: |
122-
./foremanctl pull-images
122+
./foremanctl pull-images ${{ matrix.database == 'external' && '--database-mode=external' || '' }}
123123
- name: Run deployment
124124
run: |
125125
./foremanctl deploy \

docs/developer/deployment.md

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,84 @@ IOP (Insights Operating Platform) deploys on-premise Insights services for advis
5252

5353
See [IOP Architecture](iop.md) for details on the services deployed and configuration options.
5454

55-
### Authenticated Registry Handling
55+
### Image Management
5656

57-
If you need to pull images from private or authenticated container registries, you can configure registry authentication using Podman's auth file.
57+
foremanctl uses Podman quadlet `.image` units to separate image sourcing from container definitions. Each unique container image (foreman, candlepin, pulp, etc.) gets a corresponding `.image` file deployed to `/etc/containers/systemd/`. Container roles reference these by name rather than by full image URL:
5858

59-
#### Setting up Registry Authentication
59+
```ini
60+
# /etc/containers/systemd/foreman.image
61+
[Image]
62+
Image=quay.io/foreman/foreman:nightly
63+
```
64+
65+
```ini
66+
# /etc/containers/systemd/foreman.container (excerpt)
67+
[Container]
68+
Image=foreman.image
69+
```
70+
71+
All containers that share a base image (e.g., foreman, dynflow-sidekiq, foreman-recurring) reference the same `.image` unit. systemd ensures the image is pulled before any dependent container starts.
72+
73+
#### Image Overrides via Drop-ins
74+
75+
foremanctl uses quadlet's native drop-in mechanism for image overrides. Each `.image` file can have a corresponding `.image.d/` directory containing numbered drop-in configuration files. Drop-ins are applied in lexicographic order (last wins):
76+
77+
```
78+
/etc/containers/systemd/
79+
foreman.image # base, always templated by foremanctl
80+
foreman.image.d/
81+
90-user.conf # optional: user override
82+
```
83+
84+
Precedence (last wins):
85+
86+
1. `foreman.image` — foremanctl default from `images.yml`
87+
2. `90-user.conf` — user-provided layer (not managed by foremanctl)
88+
89+
Numbered prefixes enforce ordering with gaps for future layers.
90+
91+
#### Use Cases
6092

61-
1. **Login to your registry** using Podman and save credentials to the default auth file location:
93+
**Upstream default (no user action):** foremanctl generates `.image` files from its built-in `images.yml`:
94+
95+
```ini
96+
# /etc/containers/systemd/foreman.image (generated by foremanctl)
97+
[Image]
98+
Image=quay.io/foreman/foreman:nightly
99+
```
100+
101+
**User's own registry:** The user creates a `90-user.conf` drop-in to point at their own registry. This overrides the base layer:
102+
103+
```ini
104+
# /etc/containers/systemd/foreman.image.d/90-user.conf
105+
[Image]
106+
Image=katello.example.com/Default_Organization/foreman:6.17
107+
AuthFile=/etc/foreman/registry-auth.json
108+
```
109+
110+
**Developer testing a container build:** The developer creates a `90-user.conf` drop-in to test a specific image build:
111+
112+
```ini
113+
# /etc/containers/systemd/foreman.image.d/90-user.conf
114+
[Image]
115+
Image=quay.io/foreman/foreman:pr-12345
116+
```
117+
118+
#### Authenticated Registry Handling
119+
120+
If you need to pull images from private or authenticated container registries:
121+
122+
1. **Login to your registry** using Podman and save credentials to an auth file:
62123
```bash
63124
podman login <registry> --authfile=/etc/foreman/registry-auth.json
64125
```
65126

66-
2. **Deploy as usual** - foremanctl will automatically detect and use the authentication file:
127+
2. **Set the global auth file** so all generated `.image` units include it:
67128
```bash
68-
./foremanctl deploy
129+
./foremanctl deploy --registry-auth-file=/etc/foreman/registry-auth.json
69130
```
70131

71-
This approach integrates seamlessly with both the happy path and advanced deployment paths described above. The authentication is handled transparently during image pulling operations.
132+
This affects the base `.image` files. Drop-in overrides (disconnected or user) manage their own `AuthFile=` directives independently.
72133

73134
## Deployer Stages
74135

@@ -81,7 +142,7 @@ Some of the stages will be made available to the user to run independently.
81142
a. system requirements
82143
b. tuning requirements
83144
c. certificate requirements
84-
4. Place `.container` files
145+
4. Place `.image` and `.container` files
85146
5. Create podman secrets
86147
6. Reload systemd
87148
7. (re)start services
@@ -103,7 +164,9 @@ When the user provides parameters to alter the deployment, the deployment utilit
103164

104165
## Container changes (Upgrades)
105166

106-
When the running containers change because the stream was changed in the configuration, the deployment utility will pull the new images and use the new images when starting services.
167+
When the running containers change because the stream was changed in the configuration, the deployment utility regenerates `.image` units with the new image references and restarts services to pull and use the updated images.
168+
169+
User drop-in overrides in `.image.d/90-user.conf` take precedence over the base `.image` values — if a user-provided drop-in pins a specific tag, it will not be changed by an upgrade.
107170

108171
As there is currently no way for the deployment utility to verify which image version is used by a running service, the user is advised to stop all services before performing an upgrade.
109172

src/playbooks/pull-images/pull-images.yaml

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,49 @@
1111
roles:
1212
- role: pre_install
1313
post_tasks:
14-
- name: Pull an image
15-
containers.podman.podman_image:
14+
- name: Deploy core image units
15+
ansible.builtin.include_role:
1616
name: "{{ item }}"
17-
environment:
18-
REGISTRY_AUTH_FILE: "{{ registry_auth_file }}"
19-
loop: "{{ images }}"
17+
tasks_from: image.yaml
18+
loop:
19+
- foreman
20+
- candlepin
21+
- pulp
22+
- redis
2023

21-
- name: Pull foreman_proxy images
22-
containers.podman.podman_image:
23-
name: "{{ item }}"
24-
environment:
25-
REGISTRY_AUTH_FILE: "{{ registry_auth_file }}"
26-
loop: "{{ foreman_proxy_images }}"
27-
when:
28-
- "'foreman-proxy' in enabled_features"
24+
- name: Deploy database image units
25+
ansible.builtin.include_role:
26+
name: postgresql
27+
tasks_from: image.yaml
28+
when: database_mode == 'internal'
29+
30+
- name: Deploy proxy image units
31+
ansible.builtin.include_role:
32+
name: foreman_proxy
33+
tasks_from: image.yaml
34+
when: "'foreman-proxy' in enabled_features"
2935

30-
- name: Pull database images
31-
containers.podman.podman_image:
36+
- name: Deploy IOP image units
37+
ansible.builtin.include_role:
3238
name: "{{ item }}"
33-
environment:
34-
REGISTRY_AUTH_FILE: "{{ registry_auth_file }}"
35-
loop: "{{ database_images }}"
36-
when:
37-
- database_mode == 'internal'
39+
tasks_from: image.yaml
40+
loop:
41+
- iop_kafka
42+
- iop_ingress
43+
- iop_puptoo
44+
- iop_yuptoo
45+
- iop_engine
46+
- iop_gateway
47+
- iop_inventory
48+
- iop_advisor
49+
- iop_remediation
50+
- iop_vmaas
51+
- iop_vulnerability
52+
- iop_advisor_frontend
53+
- iop_inventory_frontend
54+
- iop_vulnerability_frontend
55+
when: "'iop' in enabled_features"
56+
57+
- name: Run daemon reload
58+
ansible.builtin.systemd:
59+
daemon_reload: true

src/roles/candlepin/defaults/main.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ candlepin_ciphers:
1414
- TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
1515
candlepin_container_image: quay.io/foreman/candlepin
1616
candlepin_container_tag: "4.4.14"
17-
candlepin_registry_auth_file: /etc/foreman/registry-auth.json
1817

1918
candlepin_database_host: localhost
2019
candlepin_database_port: 5432
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
- name: Deploy candlepin image unit
3+
ansible.builtin.include_role:
4+
name: images
5+
tasks_from: deploy_image.yaml
6+
vars:
7+
images_definition:
8+
name: candlepin
9+
image: "{{ candlepin_container_image }}:{{ candlepin_container_tag }}"

src/roles/candlepin/tasks/main.yml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
---
2+
- name: Deploy candlepin image
3+
ansible.builtin.include_tasks: image.yaml
4+
25
- name: Create log directories
36
ansible.builtin.file:
47
path: "{{ item }}"
@@ -55,17 +58,10 @@
5558
notify:
5659
- Restart candlepin
5760

58-
- name: Pull the Candlepin container image
59-
containers.podman.podman_image:
60-
name: "{{ candlepin_container_image }}:{{ candlepin_container_tag }}"
61-
state: present
62-
environment:
63-
REGISTRY_AUTH_FILE: "{{ candlepin_registry_auth_file }}"
64-
6561
- name: Deploy Candlepin quadlet
6662
containers.podman.podman_container:
6763
name: "candlepin"
68-
image: "{{ candlepin_container_image }}:{{ candlepin_container_tag }}"
64+
image: candlepin.image
6965
state: quadlet
7066
network: host
7167
hostname: "{{ ansible_facts['hostname'] }}.local"

src/roles/foreman/defaults/main.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
foreman_container_image: "quay.io/foreman/foreman"
33
foreman_container_tag: "nightly"
4-
foreman_registry_auth_file: /etc/foreman/registry-auth.json
54

65
foreman_database_name: foreman
76
foreman_database_user: foreman

src/roles/foreman/tasks/image.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
- name: Deploy foreman image unit
3+
ansible.builtin.include_role:
4+
name: images
5+
tasks_from: deploy_image.yaml
6+
vars:
7+
images_definition:
8+
name: foreman
9+
image: "{{ foreman_container_image }}:{{ foreman_container_tag }}"

src/roles/foreman/tasks/main.yaml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
---
2-
- name: Pull the Foreman container image
3-
containers.podman.podman_image:
4-
name: "{{ foreman_container_image }}:{{ foreman_container_tag }}"
5-
state: present
6-
environment:
7-
REGISTRY_AUTH_FILE: "{{ foreman_registry_auth_file }}"
2+
- name: Deploy foreman image
3+
ansible.builtin.include_tasks: image.yaml
84

95
- name: Create secret for DATABASE_URL
106
containers.podman.podman_secret:
@@ -98,7 +94,7 @@
9894
- name: Deploy Foreman Container
9995
containers.podman.podman_container:
10096
name: "foreman"
101-
image: "{{ foreman_container_image }}:{{ foreman_container_tag }}"
97+
image: foreman.image
10298
state: quadlet
10399
sdnotify: true
104100
network: host
@@ -136,7 +132,7 @@
136132
containers.podman.podman_container:
137133
name: "dynflow-sidekiq-%i"
138134
quadlet_filename: "dynflow-sidekiq@"
139-
image: "{{ foreman_container_image }}:{{ foreman_container_tag }}"
135+
image: foreman.image
140136
state: quadlet
141137
sdnotify: true
142138
network: host
@@ -191,7 +187,7 @@
191187
name: "foreman-recurring-{{ item.instance }}"
192188
quadlet_filename: "foreman-recurring@{{ item.instance }}"
193189
state: quadlet
194-
image: "{{ foreman_container_image }}:{{ foreman_container_tag }}"
190+
image: foreman.image
195191
sdnotify: false
196192
network: host
197193
hostname: "{{ ansible_facts['hostname'] }}.local"

src/roles/foreman_proxy/defaults/main.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
foreman_proxy_container_image: "quay.io/foreman/foreman-proxy"
33
foreman_proxy_container_tag: "nightly"
4-
foreman_proxy_registry_auth_file: /etc/foreman/registry-auth.json
54

65
foreman_proxy_name: "{{ ansible_facts['fqdn'] }}"
76
foreman_proxy_https_port: 8443

0 commit comments

Comments
 (0)