Skip to content

ateapi: record each actor-provisioned volume as an ExternalVolume - #3746

Draft
hajiler wants to merge 3 commits into
agent-substrate:mainfrom
hajiler:shared-volumes/pr3-provisioned-external-volumes
Draft

hajiler wants to merge 3 commits into
agent-substrate:mainfrom
hajiler:shared-volumes/pr3-provisioned-external-volumes

Conversation

@hajiler

@hajiler hajiler commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Part of #1637. Stacked on #3744: the first commit is that PR, review the second (ateapi: record each actor-provisioned volume as an ExternalVolume). I will rebase onto main once #3744 merges.

Moves the per-actor volume bookkeeping onto the ExternalVolume resource without changing what an actor mounts: still one volume per actor, provisioned from external.template, deleted with the actor.

  • Resume records each provisioned volume as an ExternalVolume in the actor's atespace right after CreateVolume returns, and names it in the new status.externalVolumes.volumes[].externalVolumeName. The status keeps its copy of the handle, so the hot paths read nothing new.
  • Delete removes the record after the storage. A missing record (actors from before this change, or a status that never got written) is not an error.
  • A retry after a crash between the CSI call and the actor write reuses the record it left when it names the same volume; a record naming other storage fails the resume with FAILED_PRECONDITION.
  • Record names are <volume>-<hash of actor UID and volume>: deterministic, unique across actors and recreations, and within a DNS label whatever the volume name's length.

Known gap until actor references land: DeleteExternalVolume does not yet refuse a record a live actor provisioned. The actor keeps working from its status copy and its delete still removes the storage.

AI use: drafted with an AI agent under my direction.

  • Tests pass: controlapi unit tests for the record keeping and the name, the functional lifecycle test extended to check the record after resume and after delete, hack/verify-all.sh, apitool validate, hack/verify/proto-breaking.sh. The e2e sub-test combinedvolumes/ExternalVolumeRecorded is new and has not been run on a cluster yet.
  • Docs updated

@hajiler
hajiler force-pushed the shared-volumes/pr3-provisioned-external-volumes branch 2 times, most recently from c49c5b2 to 177fdb4 Compare November 9, 2026 22:17
@hajiler

hajiler commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto the current #3744 head (adds its apitool example/annotation fix); the last commit is still the one to review.

An ExternalVolume is an atespace-scoped record of a CSI volume that already
exists in the storage system: the driver, the volume ID and the volume
context the node plugin needs to mount it. Its lifetime is independent of
any actor, and an atespace cannot be deleted while it holds one.

This adds the resource, its Postgres table and store methods, the OpenFGA
type and RPC permissions, the Create, Get, List and Delete RPCs, and the
kubectl-ate commands. Create requires the ExternalVolumes preview gate and
a global owner. Get, List and Delete work with the gate off, so turning it
off cannot strand a record. Delete removes the record and leaves the
storage in place.

Nothing references an ExternalVolume yet. Actor templates still provision
a volume per actor from external.template.

Signed-off-by: Elijah Rodriguez-Beltran <elijahrb@google.com>
Every volume provisioned for an actor from external.template now has an
ExternalVolume in the actor's atespace, named in the actor's
ExternalVolumeStatus.external_volume_name. The record is written right
after CreateVolume returns and deleted by the actor's delete workflow
after the storage, so a retry after a crash in between converges: the
retried CreateVolume returns the same volume and the record it left is
reused when it names that volume.

The actor status keeps its copy of the volume handle, so resume, suspend
and delete read nothing new. The record name is the volume name with a
hash of the actor UID and volume name, which keeps it a DNS label and
unique across actors and across a namesake created after the actor is
deleted. Actors provisioned before this change have no record, and
deleting them tolerates that.

Signed-off-by: Elijah Rodriguez-Beltran <elijahrb@google.com>
@hajiler
hajiler force-pushed the shared-volumes/pr3-provisioned-external-volumes branch from 177fdb4 to 28ce447 Compare November 11, 2026 22:54
Cover a failed storage delete keeping the record, a volume already gone
from storage still losing its record, and store errors while recording
or deleting one. Validate external_volume_name's format and update
rules. The partial-failure functional test checks that only the
provisioned volume gets a record and that none is left after delete.

Rename the maxPrefix variable in provisionedExternalVolumeName, which
shadowed the builtin max. Note in the resource model that Substrate
writes ExternalVolumes too.

This branch has not been deployed

No deployments
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.

1 participant