Skip to content

Three drive-adjacent volume spellings Docker accepts are still refused #105

Closed

Description

@lesnik512

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, so split_volume's marker branch
    falls through (it fires only when a target follows) and the source is the bare C.
    test_drive_marker_without_a_target_keeps_its_old_split pins 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_DRIVE requires 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 not a is 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

  1. lesnik512 commented on Sep 20, 2026

    @lesnik512
    MemberAuthor

    Measurement: podman refuses every drive-letter spec, including the one #104 accepts

    Run in the integration job (podman 4.9.3, ubuntu-latest) on a throwaway branch, #106, one podman run --rm -v '<spec>' alpine:3 true per row:

    spec exit podman says
    C:\data:/var 125 invalid option type "/var"
    C:/data:/var 125 invalid option type "/var"
    C:\data 125 invalid container path "\data", must be an absolute path
    C:data:/var 125 invalid option type "/var"
    <project-dir>/C:data:/var 125 invalid option type "/var"
    a:/var 0 — creates the named volume a
    /tmp/C:\data:/var 125 invalid option type "/var"
    /tmp/probe-src:/var (control) 0 —
    probevol:/var (control) 0 —

    The cause is one thing, not three: podman splits a -v spec on colons into at most source:target:options, so any colon inside the source pushes the real target into the options slot. A drive letter cannot be carried through -v at all — not as a Windows path, not as a resolved absolute path, not even when the colon sits inside an otherwise ordinary /tmp source.

    This reaches past the three residuals

    C:\data:/var is the form #86 called an over-rejection and #104 started accepting. #86 asserted "podman can express it"; measured, it cannot. Under the second rule of docs/adr/0006-docker-rejection-parity.md that 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_volume helper #104 added is still what makes an accurate message possible — it is the only place that knows the source is C:\data rather than C.

    a:/var is the one genuine divergence

    podman accepts it and creates a named volume a; docker reads the same entry as an anonymous volume whose target is the literal string a:/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.

  2. lesnik512 commented on Sep 20, 2026

    @lesnik512
    MemberAuthor

    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 config v5.1.2:

    entry top-level volumes: Docker resolves to
    v:/data absent {type: volume, target: v:/data} — anonymous
    v:/data v: declared {type: volume, target: v:/data} — anonymous, declaration ignored
    v:/data:ro v: declared {type: bind, source: v:/data, target: ro}
    vol:/data:ro vol: declared named volume vol at /data, read-only

    The 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:/data the other way: _classify_volume matches v against the volume-name grammar and treats it as a named-volume reference, which _validate_volume_references then 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 TestVolumeDefinitionSchema cases use v:/data as a named volume, so changing the reading is a test-visible decision, not a local fix.

    v:/data:ro is settled by #107 (the source carries a colon, podman cannot express it, refused). The one-colon spelling v:/data is not, and belongs with a:/var in this issue: podman accepts it as the named volume v, Docker means an anonymous volume at target v:/data, and the ruling has to say which one compose2pod is for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions