Repository navigation
Three drive-adjacent volume spellings Docker accepts are still refused #105
Description
split_volume (#104) attaches the Windows drive marker to a volume source, which closed
C:\data:/var. Three neighbouring spellings Docker accepts are still refused. All three are
rule-two over-rejections against docs/adr/0006-docker-rejection-parity.md, all pre-existing, none
introduced by that change. Measured against docker compose config v5.1.2, one probe per spelling,
no top-level volumes: block unless stated:
C:\data(drive source, no target). Docker ACCEPTS as{type: volume, target: C:\data}— an
anonymous volume whose target is the whole string. compose2pod REJECTS with
refers to undefined volume 'C': the entry carries one colon, sosplit_volume's marker branch
falls through (it fires only when a target follows) and the source is the bareC.
test_drive_marker_without_a_target_keeps_its_old_splitpins the current verdict.C:data:/var(drive-qualified relative path). Docker ACCEPTS as a bind whose source resolves
against the project directory (<project-dir>/C:data). compose2pod REJECTS with
refers to undefined volume 'C':_WINDOWS_DRIVErequires a\or/after the colon, so a
drive-relative path is not recognised as drive-qualified at all.a:/var(single letter, one colon). Docker ACCEPTS as{type: volume, target: a:/var},
whether or notais declared top-level — the leading<letter>:is read as a drive marker, so a
one-character named volume is not expressible in the short syntax. compose2pod REJECTS with
refers to undefined volume 'a'.
Why one issue and not three. All three are the same fact from different angles: Docker attaches
the drive marker first and then counts the parts that remain, and a one-part result is an anonymous
volume whose target is the entire entry, however unusable that target looks. compose2pod instead
asks "does the source match the volume-name grammar", which has no answer for an entry with no
target and so falls back to a verdict about C — a volume name the user never wrote.
Why it is open rather than fixed. Closing these needs a ruling before a parser change, and the
ruling needs a measurement this repo does not have yet: what real podman does with the flag each
form would emit (-v 'C:\data' is an anonymous volume whose container path is not absolute). If
podman cannot express them, these are legitimate rule-two refusals and the work is to say so with
an accurate message; if it can, they are accepts to be deferred to script-run time like every other
host fact. Under either ruling today's message is wrong, because it names a phantom volume instead
of the entry.
Proposed shape: measure the three against real podman, record the ruling, then either accept
them or refuse them from the place that can name the entry — the named-volume reference check is
the wrong site to answer an entry that names no volume.
Revisit trigger: a user reports any of the three, most likely C:\data pasted from a Windows
compose file alongside the bind #86 closed.
Not in scope of #86 or #104, both of which were scoped to the drive-letter bind with a target;
neither changes these verdicts.
Activity
lesnik512 commentedon Sep 20, 2026
Measurement: podman refuses every drive-letter spec, including the one #104 accepts
Run in the
integrationjob (podman 4.9.3, ubuntu-latest) on a throwaway branch, #106, onepodman run --rm -v '<spec>' alpine:3 trueper row:C:\data:/varinvalid option type "/var"C:/data:/varinvalid option type "/var"C:\datainvalid container path "\data", must be an absolute pathC:data:/varinvalid option type "/var"<project-dir>/C:data:/varinvalid option type "/var"a:/vara/tmp/C:\data:/varinvalid option type "/var"/tmp/probe-src:/var(control)probevol:/var(control)The cause is one thing, not three: podman splits a
-vspec on colons into at mostsource:target:options, so any colon inside the source pushes the real target into the options slot. A drive letter cannot be carried through-vat all — not as a Windows path, not as a resolved absolute path, not even when the colon sits inside an otherwise ordinary/tmpsource.This reaches past the three residuals
C:\data:/varis the form #86 called an over-rejection and #104 started accepting. #86 asserted "podman can express it"; measured, it cannot. Under the second rule ofdocs/adr/0006-docker-rejection-parity.mdthat makes the original refusal correct in outcome — and its message (refers to undefined volume 'C') wrong, because it names a volume the user never wrote instead of citing podman.So the shape of the work changes: not "accept the three residuals too", but "refuse the whole drive-letter family for the reason that is actually true". The
split_volumehelper #104 added is still what makes an accurate message possible — it is the only place that knows the source isC:\datarather thanC.a:/varis the one genuine divergencepodman accepts it and creates a named volume
a; docker reads the same entry as an anonymous volume whose target is the literal stringa:/var. Both oracles "accept", and they mean different things. Accepting it here would silently mount a volume docker never asked for, so it needs its own ruling rather than being folded into the drive-letter one.Not yet released
0.4.4 has not been tagged, so #104's acceptance has not shipped to PyPI.
lesnik512 commentedon Sep 20, 2026
Follow-on measurement: a single-letter named volume is not expressible in Docker's short syntax
Came out of scoping the refusal in #107. Measured against
docker compose configv5.1.2:volumes:v:/data{type: volume, target: v:/data}— anonymousv:/datav:declared{type: volume, target: v:/data}— anonymous, declaration ignoredv:/data:rov:declared{type: bind, source: v:/data, target: ro}vol:/data:rovol:declaredvolat/data, read-onlyThe drive rule applies to any single letter, so a one-character volume name cannot be referenced in the short syntax at all — the letter is always read as a drive.
compose2pod reads
v:/datathe other way:_classify_volumematchesvagainst the volume-name grammar and treats it as a named-volume reference, which_validate_volume_referencesthen requires to be declared. Both oracles accept the document and mean different things, so this is a semantic divergence rather than a rule-one or rule-two case, and it is invisible to the conformance harness, which compares verdicts and not meanings.It is also load-bearing in the current test suite: several
TestVolumeDefinitionSchemacases usev:/dataas a named volume, so changing the reading is a test-visible decision, not a local fix.v:/data:rois settled by #107 (the source carries a colon, podman cannot express it, refused). The one-colon spellingv:/datais not, and belongs witha:/varin this issue: podman accepts it as the named volumev, Docker means an anonymous volume at targetv:/data, and the ruling has to say which one compose2pod is for.