Skip to content

fix(volumebind): create a missing read-write sub_path at the bind - #239

Merged
teemow merged 2 commits into
giantswarmfrom
fix/238-create-missing-subpath
Oct 9, 2026
Merged

teemow merged 2 commits into
giantswarmfrom
fix/238-create-missing-subpath

Conversation

@teemow

@teemow teemow commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Problem

Since the existing-volume mounts (#234), CreateActor takes a sub_path per existing volume, but the bind refuses one that does not exist yet (internal/volumebind, "sub_path does not exist on the volume"). A caller that assigns the directory name inside its own create call, kagent's CreateSession for one (it picks the Session id and mounts sessions/<id>/ of the workspace volume), cannot create the directory before the actor exists, so every such actor fails at its first resume.

Beside it, validateTemplateVolumesUnchanged treated an existing-volume declaration like a durable volume: adding an existing-volume slot to a template made every actor's repoint FailedPrecondition, although such a volume holds no snapshot data.

Proposed solution

  • The bind creates a missing read-write directory. For a read-write mount whose sub_path's last component is missing, ateom's volumebind.Prepare creates it: mkdirat beneath the parent, which is opened the way the bind resolves a sub-path (RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_XDEV), then owned by the user the mounting container runs as (read from its OCI bundle, so an image that runs as a non-root USER can write it) and 0770 whatever ateom's umask. The parent must exist: an actor is given a directory of its own, not a tree. A missing parent, a symbolic link in the path, a file under the name, a read-only mount, a READ_ONLY_MANY volume and a read-only file system keep failing with their reason. Every creation runs before any bind, so a read-only mount of a directory a read-write mount creates finds it whatever the order of the mounts.
  • MountDir moves from volumebind to ocispec, which volumebind now imports for the bundle's spec (the other direction was a cycle).
  • Repoint: existing-volume declarations and their mounts are exempt from the unchanged-volumes check, like system_info volumes: the actor supplies them at create or not at all, and an actor without a reference to a newly declared one gets no mount of it.
  • docs/csi-volumes.md and the VolumeMount.sub_path comment say so; the existingvolumes e2e leaves session a's directory for Substrate to create and checks its mode, session b's stays made ahead.

Upstream: follows agent-substrate#1637's shape like #234; the same change is prepared for upstream once that lands.

Acceptance criteria

  • CreateActor with an existing READ_WRITE_MANY volume and a sub_path whose last component is missing creates it (0770, the container's user) and binds it; a missing parent, a symlink in the path, a file, an unwritable parent and a path outside the volume are refused with the reason: TestCreateVolumeDir (eleven cases), TestProcessUser, TestBoundMounts
  • The e2e that mounts a session directory nothing created passes: TestExistingVolumes/MountsSubPathsAtDeclaredPaths PASS at head 754f595 on the e2e-test lane (https://github.com/giantswarm/substrate/actions/runs/37979928552/job/113987543465) and both runtimes of the E2E job (https://github.com/giantswarm/substrate/actions/runs/37979928804/job/113987745031); the unit tests in run-tests of the same run
  • A template that adds an existing-volume slot repoints existing actors without FailedPrecondition: TestValidateTemplateVolumesUnchanged (six new cases), TestUpdateActor_RepointTemplate (tmpl-g)
  • Carried as one upstream-ready commit (782df2a) with its FORK.md row (754f595, fork-ledger green); released as v1.7.0-rc.2 (tag at the rebase merge 8e4fb7e; images and charts on gsoci, CircleCI pipeline 556)

Closes #238.


This pull request was written by an agent.

A caller that names the actor's directory inside its own create call
(a session id chosen at create, the directory sessions/<id> of a
shared workspace volume) could not make the directory before the actor
existed, so every such actor failed at its first resume with
"sub_path does not exist on the volume".

ateom now creates the last component of a read-write mount's sub_path
when it is missing, with mkdirat beneath the parent opened the way the
bind opens a sub-path (RESOLVE_BENEATH, RESOLVE_NO_SYMLINKS,
RESOLVE_NO_XDEV). The directory belongs to the user the mounting
container runs as, read from its OCI bundle, and is 0770 whatever
ateom's umask, so a container that runs as its image's USER can write
it. The parent must exist, since an actor is given a directory of its
own and not a tree. A symbolic link in the path, a file under the name,
a read-only mount, a READ_ONLY_MANY volume and a read-only file system
keep failing with their reason, and every creation happens before any
bind, so a read-only mount of the directory a read-write mount creates
finds it whatever the order of the mounts. MountDir moves to ocispec,
which volumebind now imports for the bundle's spec.

A template repoint no longer requires the existing-volume declarations
and their mounts to be unchanged, the way system_info volumes are
exempt: their data lives outside Substrate, the actor supplies them at
create or not at all, and no snapshot carries their content. Adding an
existing-volume slot to a template therefore repoints the template's
actors instead of failing every one with FailedPrecondition.

The existingvolumes e2e now leaves one session directory for Substrate
to create and checks its mode.

Signed-off-by: Timo Derstappen <teemow@gmail.com>
@teemow
teemow requested a review from a team as a code owner October 9, 2026 19:22
Signed-off-by: Timo Derstappen <teemow@gmail.com>
@teemow
teemow merged commit 8e4fb7e into giantswarm Oct 9, 2026
19 checks passed
@teemow
teemow deleted the fix/238-create-missing-subpath branch October 9, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(volumebind): create a missing READ_WRITE_MANY sub_path at actor create

1 participant