2828- `STUB_FLAGS` -- the flag exists and accepts deliberate nonsense, so emitting it would
2929 exit 0 having done nothing, which is worse than refusing.
3030
31+ A row measuring a refusal whose message draws a clause from `compose2pod/podman.py` names
32+ the `site` that makes the claim and the `claim` it makes, which is what
33+ `tests/test_podman_claim_coverage.py` gates on. A row whose refusal makes no such claim --
34+ a relative target, an unsupported long-form `type` -- leaves both empty: the reason is
35+ podman's, but the message keeps it to itself, which is issue #121's subject.
36+
3137Four claims, four experiments. `network_mode` alone has no row: it is refused under
3238ADR-0003, not rule two, and podman honours it (#115). The gate that every rule-two site
3339has a row is issue #109 phase 3, and that is the exemption it has to know about.
@@ -54,6 +60,8 @@ class Refusal:
5460 refusal_match : str
5561 podman_argv : list [str ]
5662 control_argv : list [str ]
63+ site : str = ""
64+ claim : str = ""
5765
5866
5967@dataclass (frozen = True )
@@ -71,6 +79,8 @@ class Limitation:
7179 refusal_match : str
7280 podman_argv : list [str ]
7381 host_dir : str = ""
82+ site : str = ""
83+ claim : str = ""
7484
7585
7686@dataclass (frozen = True )
@@ -85,6 +95,8 @@ class AbsentFlag:
8595 compose : dict [str , Any ]
8696 refusal_match : str
8797 unknown_argv : list [list [str ]]
98+ site : str = ""
99+ claim : str = ""
88100
89101
90102@dataclass (frozen = True )
@@ -99,6 +111,8 @@ class StubFlag:
99111 compose : dict [str , Any ]
100112 refusal_match : str
101113 nonsense_argv : list [str ]
114+ site : str = ""
115+ claim : str = ""
102116
103117
104118def _one_volume (entry : "str | dict[str, Any]" ) -> dict [str , Any ]:
@@ -126,20 +140,26 @@ def _one_volume(entry: "str | dict[str, Any]") -> dict[str, Any]:
126140 ),
127141 Refusal (
128142 id = "drive-shaped-source-no-target" ,
143+ site = "parsing._reject_drive_shaped_volume" ,
144+ claim = "REFUSES_RELATIVE_CONTAINER_PATH" ,
129145 compose = _one_volume ("C:\\ data" ),
130146 refusal_match = _NOT_ABSOLUTE ,
131147 podman_argv = ["--mount" , "type=volume,dst=C:\\ data" ],
132148 control_argv = _ANONYMOUS_CONTROL ,
133149 ),
134150 Refusal (
135151 id = "single-letter-source-with-path" ,
152+ site = "parsing._reject_drive_shaped_volume" ,
153+ claim = "REFUSES_RELATIVE_CONTAINER_PATH" ,
136154 compose = _one_volume ("v:/data" ),
137155 refusal_match = _NOT_ABSOLUTE ,
138156 podman_argv = ["--mount" , "type=volume,dst=v:/data" ],
139157 control_argv = _ANONYMOUS_CONTROL ,
140158 ),
141159 Refusal (
142160 id = "single-letter-source-empty-target" ,
161+ site = "parsing._reject_drive_shaped_volume" ,
162+ claim = "REFUSES_RELATIVE_CONTAINER_PATH" ,
143163 compose = _one_volume ("v:" ),
144164 refusal_match = _NOT_ABSOLUTE ,
145165 podman_argv = ["--mount" , "type=volume,dst=v:" ],
@@ -169,6 +189,8 @@ def _one_volume(entry: "str | dict[str, Any]") -> dict[str, Any]:
169189 Refusal (
170190 # Measured: podman creates a missing bind source in no spelling, `-v` or `--mount`.
171191 id = "bind-create-host-path" ,
192+ site = "parsing._validate_bind_options" ,
193+ claim = "CANNOT_EXPRESS" ,
172194 compose = _one_volume ({"type" : "bind" , "source" : "./src" , "target" : "/var" , "bind" : {"create_host_path" : True }}),
173195 refusal_match = "bind 'create_host_path' is not supported" ,
174196 podman_argv = ["--mount" , "type=bind,src={host}/absent,dst=/var" ],
@@ -177,6 +199,8 @@ def _one_volume(entry: "str | dict[str, Any]") -> dict[str, Any]:
177199 Refusal (
178200 # `source=`/`target=` rather than the `src=`/`dst=` above: the spelling #114 measured.
179201 id = "image-subpath" ,
202+ site = "parsing._reject_subpath" ,
203+ claim = "adds_the_mount_option_in" ,
180204 compose = _one_volume (
181205 {"type" : "image" , "source" : "busybox:1.36" , "target" : "/mnt" , "image" : {"subpath" : "/bin" }}
182206 ),
@@ -186,6 +210,8 @@ def _one_volume(entry: "str | dict[str, Any]") -> dict[str, Any]:
186210 ),
187211 Refusal (
188212 id = "volume-subpath" ,
213+ site = "parsing._reject_subpath" ,
214+ claim = "adds_the_mount_option_in" ,
189215 compose = _one_volume ({"type" : "volume" , "target" : "/mnt" , "volume" : {"subpath" : "/sub" }}),
190216 refusal_match = "volume 'subpath' is not supported" ,
191217 podman_argv = ["--mount" , "type=volume,target=/mnt,subpath=/sub" ],
@@ -203,6 +229,8 @@ def _reservation(field: str, value: object) -> dict[str, Any]:
203229 # Measured: `-v vol:/etc:nocopy` leaves only podman's own hosts/hostname/resolv.conf
204230 # in the volume, so the image copy-up really is suppressed.
205231 id = "volume-nocopy" ,
232+ site = "parsing._validate_volume_type_options" ,
233+ claim = "NOCOPY_NEEDS_THE_SHORT_FORM" ,
206234 compose = {
207235 "services" : {
208236 "app" : {
@@ -235,6 +263,8 @@ def _reservation(field: str, value: object) -> dict[str, Any]:
235263ABSENT_FLAGS : list [AbsentFlag ] = [
236264 AbsentFlag (
237265 id = "reservations-cpus" ,
266+ site = "resources._RESERVATION_REFUSALS" ,
267+ claim = "NO_RESERVATION_FLAG" ,
238268 compose = _reservation ("cpus" , "0.5" ),
239269 refusal_match = "podman run has no reservation flag for it" ,
240270 unknown_argv = [["--cpu-reservation" , "1" ], ["--cpus-reservation" , "1" ]],
@@ -245,6 +275,8 @@ def _reservation(field: str, value: object) -> dict[str, Any]:
245275STUB_FLAGS : list [StubFlag ] = [
246276 StubFlag (
247277 id = "reservations-devices" ,
278+ site = "resources._RESERVATION_REFUSALS" ,
279+ claim = "GPUS_RESERVES_NOTHING" ,
248280 compose = _reservation ("devices" , [{"capabilities" : ["gpu" ]}]),
249281 refusal_match = "--gpus accepts any value and reserves nothing" ,
250282 nonsense_argv = ["--gpus" , "nonsense" ],
0 commit comments