cs-image-system-docker-runtime-plugin 0.1.6__tar.gz → 0.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/.gitignore +7 -2
  2. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/PKG-INFO +181 -46
  3. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/README.md +177 -42
  4. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/pyproject.toml +9 -4
  5. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/bootstrap.py +28 -8
  6. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/data/image-sources.yml +80 -0
  7. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/docker_names.py +5 -5
  8. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/docker_packer_source.py +2 -2
  9. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/docker_provider_specific_image.py +11 -7
  10. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/docker_runtime_builders.py +124 -24
  11. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/docker_runtime_models.py +9 -9
  12. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/local_target.py +140 -0
  13. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/main.py +1 -0
  14. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_container/main.tf +74 -0
  15. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_container/outputs.tf +23 -0
  16. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_container/provider.tf +11 -0
  17. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_container/variables.tf +82 -0
  18. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_volume/main.tf +23 -0
  19. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_volume/outputs.tf +15 -0
  20. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_volume/provider.tf +11 -0
  21. cs_image_system_docker_runtime_plugin-0.1.8/src/cs_image_system/docker_runtime/tfmodules/docker_volume/variables.tf +29 -0
  22. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/tests/test_docker_runtime.py +16 -10
  23. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/LICENSE +0 -0
  24. {cs_image_system_docker_runtime_plugin-0.1.6 → cs_image_system_docker_runtime_plugin-0.1.8}/src/cs_image_system/docker_runtime/__init__.py +0 -0
@@ -13,16 +13,21 @@ build/
13
13
  .venv/
14
14
  .DS_Store
15
15
  packages/**/*.code-workspace
16
- # the IDE rewrites the root workspace file whenever folders are opened: it is the editor's, not the repository's (hygiene XXI)
16
+ # the IDE rewrites the root workspace file whenever folders are opened: it is the editor's, not the repository's
17
17
  /*.code-workspace
18
18
  trial
19
19
  tfmodules/tftest
20
20
  YAML_DUMP.yaml
21
+ # a configuration tree's run-local plaintexts and debug output, wherever a tree
22
+ # sits in this repository (a copy of the frozen fixture included)
23
+ _private/
21
24
  .obsidian
22
25
  .tofu-plugin-cache/
23
26
  _uncommitted/
24
27
  target/
25
- # never publishable, whatever the root (stage 35 refuses them; stage 40's publish-tree checks the policy)
28
+ # never publishable, whatever the root (the public-safe hook refuses them; `just publish-tree` checks the policy)
26
29
  tfplan
27
30
  *.tfstate
28
31
  *.tfstate.backup
32
+ REEXAMINE*
33
+ TODO2.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cs-image-system-docker-runtime-plugin
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: The docker runtime type: a durable host's container daemon as a place where images bake and instances run
5
5
  Project-URL: Repository, https://github.com/infrastructurebuilder/cs-image-system-3
6
6
  Project-URL: Documentation, https://github.com/infrastructurebuilder/cs-image-system-3/blob/develop/docs/OPERATIONS.md
@@ -9,9 +9,9 @@ Author-email: Mykel Alvis <mykelalvis@infrastructurebuilder.org>
9
9
  License-Expression: Apache-2.0
10
10
  License-File: LICENSE
11
11
  Requires-Python: >=3.13
12
- Requires-Dist: cs-image-system-base==0.1.6
13
- Requires-Dist: cs-image-system-contract==0.1.6
14
- Requires-Dist: cs-image-system-hashicorp-utils==0.1.6
12
+ Requires-Dist: cs-image-system-base==0.1.8
13
+ Requires-Dist: cs-image-system-contract<0.5,>=0.4
14
+ Requires-Dist: cs-image-system-hashicorp-utils==0.1.8
15
15
  Requires-Dist: pydantic>=2.13
16
16
  Requires-Dist: python-hcl2>=8.1
17
17
  Description-Content-Type: text/markdown
@@ -24,7 +24,13 @@ SPDX-License-Identifier: Apache-2.0
24
24
 
25
25
  # cs-image-system-docker-runtime-plugin
26
26
 
27
- The `docker` runtime type (stage 103): a durable host running a daemon that
27
+ This plugin ships the docker terraform modules as package data
28
+ (`docker_container`, `docker_volume`, under
29
+ [src/cs_image_system/docker_runtime/tfmodules/](src/cs_image_system/docker_runtime/tfmodules)),
30
+ declared through the `cs_image_system.modules` entry point;
31
+ `cs-image-system modules` lists them.
32
+
33
+ The `docker` runtime type: a durable host running a daemon that
28
34
  speaks the Docker Engine API, as a place where images bake and instances
29
35
  run. Docker Engine, Docker Desktop, OrbStack, colima and Podman's API
30
36
  service all qualify; the system reaches the daemon through the `docker` CLI
@@ -44,14 +50,17 @@ differs from them in three ways the design records as decisions:
44
50
  (`sha256:...`), and its names are tags: `<repository>:<final name>` and
45
51
  the series' floating `<repository>:<prefix>`, which a child image bakes
46
52
  from when its parent is deferred (the daemon has no "newest image matching
47
- a pattern" query; the tag is that query). A label is fixed when an image
53
+ a pattern" query; the tag is that query). The runtime's builder gives
54
+ each image it makes its repository (`prepare_provider_specific_image`),
55
+ so `DockerProviderSpecificImage.floating_tag()` reads the image's own
56
+ `repository` and asks no run for it. A label is fixed when an image
48
57
  is built, so what the core stamps afterwards (the parent bound after the
49
58
  bake, the fingerprint, a release) is carried by tags that MOVE:
50
59
  `<repository>:<short id>--<key>--<value>`, one per key.
51
60
  - **POSIX accounts only.** Access is through the daemon (`docker exec -u`);
52
61
  whoever can reach the daemon is root on the host. An OPA group on a docker
53
- runtime is refused at validation; enrolling `sftd` in a container is a
54
- later stage.
62
+ runtime is refused at validation; enrolling `sftd` in a container is
63
+ not yet supported.
55
64
 
56
65
  ## What it registers
57
66
 
@@ -61,8 +70,8 @@ differs from them in three ways the design records as decisions:
61
70
  | `DockerContainerBuilder` ([docker_runtime_builders.py](src/cs_image_system/docker_runtime/docker_runtime_builders.py)) | `docker` | `VCT.RUNTIME_BUILDER` | Every runtime hook, answered against the daemon through the CLI. |
62
71
  | `DockerCLIVersionChecker` (same file) | `docker` | `VCT.VERSION_CHECKER` | `docker --version` for the `docker` entry of `cfg/executables.yml`. |
63
72
 
64
- Entry points: `cs_image_system.plugins.runtime` → `docker`, and (stage 103
65
- item 15) `cs_image_system.bootstrap` → `docker`, the container-host section of
73
+ Entry points: `cs_image_system.plugins.runtime` → `docker`, and
74
+ `cs_image_system.bootstrap` → `docker`, the container-host section of
66
75
  the bootstrap interview ([bootstrap.py](src/cs_image_system/docker_runtime/bootstrap.py)):
67
76
 
68
77
  | Class / function | Role |
@@ -74,8 +83,9 @@ Its render says what remains by hand (the daemon and its host, the network,
74
83
  CI's ssh key and `known_hosts` line, or, when CI cannot reach the daemon, to
75
84
  name the runtime in `GUARD_RUNTIME`) and its audit lines are all BY HAND or
76
85
  READ. The two secrets it concerns, `DOCKER_HOST_SSH_KEY` and
77
- `DOCKER_HOST_KNOWN_HOSTS`, are among the GitHub section's, set by its script
78
- from files.
86
+ `DOCKER_HOST_KNOWN_HOSTS`, are the section's own (`Section.secrets`); the
87
+ GitHub section's script sets them from files with the rest, so the core never
88
+ names them.
79
89
 
80
90
  ## Models
81
91
 
@@ -84,20 +94,34 @@ from files.
84
94
  Extends the core's `ContainerBuilderModel` (`host`, `context`, `networking`,
85
95
  `machine_types`) and the runtime model every runtime shares
86
96
  (`default_machine_type`, `default_image_builder`, `credentials`, `ephemeral`,
87
- `retention_keep`, ...).
88
-
89
- | Field | Default | Meaning |
90
- |---|---|---|
91
- | `host` | none | The daemon's address as a `DOCKER_HOST` URL: `unix:///var/run/docker.sock`, `ssh://user@host`, `tcp://host:2376`. May be an `ENC[age:...]` marker when the address itself is private. Declare `host` or `context`, not both. |
92
- | `context` | none | A docker context name (`docker context ls`), set as `DOCKER_CONTEXT`. |
93
- | `executable` | `docker` | The `cfg/executables.yml` entry whose `binary` every docker call on this runtime runs through; `validate` version-checks it. |
94
- | `repository` | `csis/<runtime name>` | Where this runtime's builds are tagged, made a valid repository name (lowercase). |
95
- | `tls_cert_path`, `tls_verify` | none, false | `DOCKER_CERT_PATH` and `DOCKER_TLS_VERIFY` for a `tcp://` host: TLS material by PATH, never a value. |
96
- | `networking.network` | `bridge` | The network the bake container (and, later, every instance) joins, by name; referenced, never created or changed. No subnets, no zones. |
97
- | `machine_types` | `{}` | A map from a name to `{cpus, memory}` (`--cpus 2`, `--memory 4g`). When any are declared, `default_machine_type` must name one. |
98
- | `state_configuration` | the default state manager | The state manager this runtime's roots use; a tree with no cloud declares `local`. |
99
-
100
- Nothing in the model is a credential.
97
+ `retention_keep`, ...). The field table is
98
+ [CONFIGURATION section 4.6](../../docs/CONFIGURATION.md#46-docker); what is
99
+ particular to this runtime:
100
+
101
+ - **Where the daemon is** is `host` (a `DOCKER_HOST` URL:
102
+ `unix:///var/run/docker.sock`, `ssh://user@host`, `tcp://host:2376`,
103
+ which may be an `ENC[age:...]` marker when the address itself is
104
+ private) or `context` (a docker context name, `docker context ls`, set
105
+ as `DOCKER_CONTEXT`). Declare one, not both. `tls_cert_path` and
106
+ `tls_verify` are `DOCKER_CERT_PATH` and `DOCKER_TLS_VERIFY` for a
107
+ `tcp://` host: TLS material by PATH, never a value.
108
+ - **How it is reached** is `executable`, the `cfg/executables.yml` entry
109
+ whose `binary` every docker call on this runtime runs through;
110
+ `validate` version-checks it.
111
+ - **Where builds go** is `repository` (default `csis/<runtime name>`,
112
+ made a valid repository name, lowercase): where this runtime's builds
113
+ are tagged.
114
+ - **What a container gets** is `networking.network` (default `bridge`:
115
+ the network the bake container and every instance joins, by name;
116
+ referenced, never created or changed; no subnets, no zones) and
117
+ `machine_types` (a map from a name to `{cpus, memory}`, `--cpus 2`,
118
+ `--memory 4g`; when any are declared, `default_machine_type` must name
119
+ one).
120
+ - **Where its roots keep state** is `state_configuration`; a tree with no
121
+ cloud declares `local`.
122
+
123
+ Nothing in the model is a credential: whoever reaches the daemon is root
124
+ on the host.
101
125
 
102
126
  ## The builder
103
127
 
@@ -107,12 +131,79 @@ The daemon answers the keys `name` (a repository), `version` (a tag),
107
131
  `publisher` (a registry namespace; `library` is Docker Hub's and is
108
132
  dropped), `architecture` (a platform, `x86_64` → `linux/amd64`) and
109
133
  `native.image` (a reference verbatim). The release catalog's `docker` block
110
- gives every family one (`almalinux:{version}`, `debian:{version}`, ...). The
111
- query PULLS the reference and records its digest reference
134
+ gives every family one (`almalinux:{version}`, `debian:{version}`, ...);
135
+ those blocks are this plugin's, shipped as package data in
136
+ [`data/image-sources.yml`](src/cs_image_system/docker_runtime/data/image-sources.yml)
137
+ and read by `DockerContainerBuilder.image_source_entries()`, so the
138
+ release catalog a tree sees carries a `docker` block exactly when this
139
+ plugin is installed. The query PULLS the reference and records its digest reference
112
140
  (`<repository>@sha256:...`) as the identifier, so a vendor moving a tag shows
113
141
  as a move. An entry's `image_id` pins a digest reference; a tag is refused
114
142
  there, because a tag moves.
115
143
 
144
+ ### Image query translation
145
+
146
+ What `translate_image_query()` does with each key of the system's
147
+ vocabulary (`supported_query_keys()` is the set of rows not marked
148
+ refused, the same table the cloud runtime READMEs carry). The answer is
149
+ `{"reference": "<repository>:<tag>", "platform": "<os/arch>" | None}`:
150
+ what `docker pull` takes. `validate` refuses a query that uses a refused
151
+ key before anything runs; a query that names neither `name` nor
152
+ `native.image` is refused too, because the daemon needs a reference.
153
+
154
+ | System key | Daemon's word | Note |
155
+ | --- | --- | --- |
156
+ | `name` | the repository | `almalinux`, or `<publisher>/<name>` when a publisher is named |
157
+ | `family` | refused | a registry has no image families; name the repository |
158
+ | `version` | the tag | `<repository>:<version>`; unset, the reference carries no tag and the daemon's default tag applies |
159
+ | `architecture` | `--platform` | `x86_64` and `amd64` become `linux/amd64`, `arm64` and `aarch64` become `linux/arm64`; any other word passes no platform |
160
+ | `publisher` | the registry namespace | the first value prefixes the repository; `library` (Docker Hub's own) is dropped; it is also recorded as the image's owner, `library` when none is named |
161
+ | `state` | refused | a pulled image has no state |
162
+ | `virtualization` | refused | an EC2 particular |
163
+ | `root_device` | refused | an EC2 particular |
164
+ | `tags` | refused | a pull cannot search by label |
165
+ | `newest` | refused | a tag names one image; the pull resolves it |
166
+ | `native` | `native.image` verbatim | a complete reference, used instead of `name`, `publisher` and `version`; no other `native` member is read |
167
+
168
+ ### The local test target
169
+
170
+ The core's local modification tests (`cs-image-system test-mods`; the
171
+ harness is [`mod_tests.py`](../base/src/cs_image_system/base/mod_tests.py)
172
+ in the base package) run every modification of an instance image twice on
173
+ a throwaway container and compare the filesystem diffs. The harness
174
+ itself names no runtime: it asks the registered runtime builder classes
175
+ which one offers a local test target, and this plugin is the one that
176
+ does. `DockerContainerBuilder.local_test_target()` returns the factory
177
+ `make_local_target(image, executables)` from
178
+ [`local_target.py`](src/cs_image_system/docker_runtime/local_target.py),
179
+ and everything that is docker's about the test container lives there:
180
+
181
+ - the CLI: the `binary` of the tree's `docker` entry in
182
+ `cfg/executables.yml` (its `name` when no binary is given), else
183
+ `docker` from the host's PATH;
184
+ - the daemon probe: `docker info`. When the daemon does not answer the
185
+ factory returns nothing and the harness records every modification as
186
+ `skipped` with the detail `docker: the daemon is not available`, never
187
+ as failed;
188
+ - the pull, retried (three attempts, 5 then 20 seconds apart) so that a
189
+ registry that is down is named as such; a pull that fails every attempt
190
+ or a `docker run` that refuses raises the core's `ContainerStartError`
191
+ with docker's own last line, and the harness records the modifications
192
+ as `unstarted` (the container, not the modification);
193
+ - the container itself, `DockerTarget`: `docker run -d --rm <image> sh -c
194
+ 'sleep infinity'`, then `exec`, `cp` and `diff` against it, and `rm -f`
195
+ at close. Ansible modifications reach it through ansible's `docker`
196
+ connection plugin (`ansible_connection`), and the target knows which
197
+ `docker diff` lines are noise (`/tmp`, `/var/log`, the package caches,
198
+ ansible's own directories, `/run`) and tells the harness to ignore them.
199
+
200
+ Which image the container starts from is the catalog's stand-in block,
201
+ keyed by this runtime's type: the family's own `docker` block, else the
202
+ block of the family it IS, with the OS builder's `local_test_image`
203
+ overriding both. The daemon this runs on is the host's own (the
204
+ local `docker` CLI's current context), not the daemon a `docker` runtime
205
+ entry in the tree declares: the tests are local by design.
206
+
116
207
  ### Bakes
117
208
 
118
209
  `packer_source_type()` is `docker`, HashiCorp's docker plugin. The source
@@ -154,9 +245,38 @@ per-key tags; `release_commands` moves `<repository>:<series>--<model>`.
154
245
 
155
246
  ### Instances
156
247
 
157
- The instance hooks (power state, identity, verification, sessions,
158
- relabel) are the next branch of stage 103; on this branch they answer as the
159
- core's defaults do ("cannot").
248
+ Every instance hook is answered against the daemon, by the container
249
+ that bears the instance's name (a container's name is unique on a
250
+ daemon): `docker container inspect <name>`, where a container of that
251
+ name labelled as another configuration's reads as absent.
252
+
253
+ - **Launch delivery** is by session: a container has no first boot, so
254
+ the instance root uploads the launch script at creation and the system
255
+ runs it by exec after the apply.
256
+ - **Power state** is `.State.Status` in the system's words: `running` is
257
+ running; `created` and `exited` are stopped (the container exists and
258
+ can be started); `paused` is suspended; `restarting` is starting;
259
+ `removing` is stopping; `dead` is unknown; no such container is absent.
260
+ `start_instance` and `stop_instance` are `docker start` and `docker
261
+ stop`, each confirmed by reading the state back.
262
+ - **Identity** is the container id (a re-create is a new id, so a new
263
+ generation, as the ledger expects) and the hostname the container was
264
+ given. The **boot image** is the image id the container was created
265
+ from (`.Image`), which on this runtime is the build id itself.
266
+ - **Sessions** are `docker exec -i -u root <name> bash -s` with the
267
+ script on stdin; there is no ssh proxy command, because login is by
268
+ exec.
269
+ - **Verification** runs one exec that reports the launch marker (the
270
+ script's last act, so its presence means every launch parameter
271
+ applied), each declared mount point's own line from the container's
272
+ mount table (`/proc/mounts`), and reads the image the container was
273
+ created from against the expected build; a declared mount count with
274
+ no mount points named fails, because a container's mounts are checked
275
+ by name.
276
+ - **Relabel** re-creates nothing: a container's labels are fixed when it
277
+ is created, so `retag_instance` refuses, naming the labels the
278
+ container would carry after a re-create (an upgrade, or a decommission
279
+ and redeclaration), which is the way to get them.
160
280
 
161
281
  ## Example configuration
162
282
 
@@ -194,7 +314,8 @@ image_builders:
194
314
 
195
315
  - A daemon that speaks the Docker Engine API, reachable from the machine
196
316
  that runs the system: `docker version` against the runtime's `host` or
197
- `context` must answer (preflight says so in one line per daemon).
317
+ `context` must answer (preflight says so in one line per daemon; the
318
+ probe is this plugin's own `session_status` hook).
198
319
  - The `docker` CLI, declared in `cfg/executables.yml` (`version: ">=24"`
199
320
  is the floor the system runs with); `--init` needs the daemon's
200
321
  `docker-init`, which every supported daemon ships.
@@ -202,14 +323,16 @@ image_builders:
202
323
  the image builder), and `ansible-playbook` on the machine that bakes:
203
324
  ansible's docker connection runs the local `docker` CLI against the same
204
325
  daemon.
205
- - Podman's API service works as the daemon (proved 2026-11-01 against Podman
206
- 5.8.2 rootless on AlmaLinux 10, through `docker` over ssh; the setup and what
207
- differed are in [docs/OPERATIONS.md](../../docs/OPERATIONS.md), "Podman as
208
- the daemon"). A rootless Podman user needs `/etc/subuid` and `/etc/subgid`
209
- ranges. Podman Desktop's machine on macOS works too, rootful and beside
210
- OrbStack (proved 2026-11-01 with Podman 5.8.9; `just
211
- test-podman-desktop-cycle`, which skips when no machine runs; Podman 6
212
- dropped Intel Macs, so on one the proof lasts until June 2027).
326
+ - Podman's API service works as the daemon: Podman 5.8 or later, rootless
327
+ on AlmaLinux 10 through `docker` over ssh is the proved shape, and the
328
+ setup and what differs from Docker are in
329
+ [docs/OPERATIONS.md](../../docs/OPERATIONS.md), "Podman as the daemon".
330
+ A rootless Podman user needs `/etc/subuid` and `/etc/subgid` ranges.
331
+ Podman Desktop's machine on macOS works too, rootful and beside
332
+ OrbStack (`just test-podman-desktop-cycle` runs the cycle on it and
333
+ skips when no machine runs). Podman 6 dropped Intel Macs, so an Intel
334
+ host stays on Podman 5 and the proof there has an end date, kept in
335
+ [REMINDERS.md](../../REMINDERS.md).
213
336
  - The posix identity plugin for the groups an instance image activates; an
214
337
  OPA group is refused on this runtime.
215
338
  - A tree whose only runtime is docker declares a `local` state manager; a
@@ -223,7 +346,7 @@ builder's reference in the [packer plugin](../packer-plugin/README.md) for
223
346
  `type: packer-docker`. An OS builder's `runtimes[]` entry on a docker
224
347
  runtime takes the usual keys; its `query` may use `name`, `version`,
225
348
  `publisher`, `architecture` and `native.image`, and its `image_id` is a
226
- digest reference. `output_image_name` (stage 105) is the tag prefix.
349
+ digest reference. `output_image_name` is the tag prefix.
227
350
 
228
351
  ## What it tests and verifies
229
352
 
@@ -231,13 +354,21 @@ digest reference. `output_image_name` (stage 105) is the tag prefix.
231
354
  the daemon cannot answer, a label key it cannot carry, a build name longer
232
355
  than a tag, a `default_machine_type` that names no declared machine type,
233
356
  and a runtime that declares both `host` and `context`.
234
- - `preflight` runs `docker version` against each docker runtime's daemon
235
- and prints whether it answers.
357
+ - `preflight` prints whether each docker runtime's daemon answers. The
358
+ probe is this plugin's: the builder class's `session_status` hook runs
359
+ `docker version` with the declared executable's binary against the
360
+ runtime's `host` or `context` (`probe_daemon`), and answers the core
361
+ with the engine and API versions or the first line of why not; a daemon
362
+ that answers is present and judged, one that does not is absent, with
363
+ the hint to start it or fix the address. The class also declares
364
+ `DOCKER_*` as its credential prefix (set-but-empty is refused) and
365
+ classifies `DockerUnreachable` into the one-line load failure below.
236
366
  - The unit tests ([tests/](tests/)) pin the names module (tags, repository
237
367
  names, movable tags), the query translation, the model's environment and
238
368
  the packer source and post-processor shapes without a daemon; the
239
- system's stage 103 tests drive a dry `run --all` over an overlay of the
240
- frozen fixture and check the emitted docker sources.
369
+ system's [tests/test_container_runtime.py](../../tests/test_container_runtime.py)
370
+ drives a dry `run --all` over an overlay of the frozen fixture and
371
+ checks the emitted docker sources.
241
372
  - `just test-docker-bake`, a leg of `just full-test`, bakes an AlmaLinux 10
242
373
  and a Debian 12 base image and an instance image on the local daemon,
243
374
  checks the lineage and the tags, disposes them, and skips loudly without
@@ -251,6 +382,10 @@ digest reference. `output_image_name` (stage 105) is the tag prefix.
251
382
 
252
383
  ## When it fails
253
384
 
385
+ Incidents that have happened, with their cause and fix, are recorded on
386
+ [INCIDENTS.md](../../docs/INCIDENTS.md); what follows keeps each
387
+ failure's symptom, its meaning and what to do.
388
+
254
389
  | Symptom | Cause | Remedy |
255
390
  |---|---|---|
256
391
  | `the configuration cannot load: docker runtime X: the daemon at ... cannot be reached` | The daemon is not running, or `host`/`context` names the wrong one. | Start it (OrbStack, Docker Desktop, the engine on the host) or fix the address; `preflight` shows which daemon answers. |
@@ -259,7 +394,7 @@ digest reference. `output_image_name` (stage 105) is the tag prefix.
259
394
  | `image ... is in use by container(s) ...` at disposal | A container stands on the build. | Decommission the container first; the image is never pulled from under it. |
260
395
  | An ansible step cannot reach the container | The bake ran against a daemon other than the one the local `docker` CLI uses. | The runtime's `host`/`context` is what both read; check `DOCKER_HOST` is not also set in the shell to something else. |
261
396
  | The bootstrap says `docker.runtime: ... -- no default can be derived` under `--quiet` | The tree declares no runtime of type `docker`, or the section was kept from an earlier interview of a tree that had one. | Declare the runtime, or answer `want_docker: no`. |
262
- | `parent: lineage=<id> tag=series:<name>` in the state query on a Podman daemon | A release before 2026-11-01 compared `RepoTags` as written; Podman spells them `docker.io/<repository>:<tag>`. | Take a later release; nothing on the daemon needs repairing, the movable tags were there. |
397
+ | `parent: lineage=<id> tag=series:<name>` in the state query on a Podman daemon | Podman spells `RepoTags` as `docker.io/<repository>:<tag>`, so a tag must be compared with its registry prefix stripped; a release that compares `RepoTags` as written reads every movable tag as missing ([the stage index](../../docs/CHANGES.md#s109) names the release that strips it). | Take a release that strips the prefix; nothing on the daemon needs repairing, the movable tags are there. |
263
398
  | `a docker tag is at most 128 characters` at validate | A declared `output_image_name` is too long once the timestamp is appended. | Shorten it. |
264
399
 
265
400
  ## Related