Repository navigation
Conversation
d0add72 to
5efe3c9
Compare
There was a problem hiding this comment.
Claude Review: Reviewed with a focus on the object shape and how it holds up against the later changes in the series (template reference, actor references on the volume, delete trigger). The handler, store, authz and CLI code follow the Tag precedent closely and read fine.
c1365a3 to
5d9d73e
Compare
Alexis MacAskill (amacaskill)
left a comment
There was a problem hiding this comment.
Note on syncing the Shared External Volumes design doc:
This PR is up to date on volume_type, volumes[].external.template (in docs/guides/csi-volumes.md), --volume-context / --preview, and DeleteExternalVolume leaving underlying storage intact, and you noted on ateapi.proto that the ExternalVolumeStatus / storage_volume_id alignment will be handled in the follow-up PR. However, the Shared External Volumes design doc is still outdated on these — could you update the design doc when you get a chance so other docs referencing it stay in sync? Specifically:
volumes[].externalnesting (from PR #2653): The design doc (Tab 1 "Changes to ActorTemplate" & Tab 2 CUJsS1,S3,C3,C4) still uses top-levelvolumes[].externalVolumeTemplateandvolumes[].externalVolumeRefinstead ofvolumes[].external.templateandvolumes[].external.ref(which you flagged on our FusionFS doc).ExternalVolumefields (from PR #3744): In Tab 1 ("New API Type: ExternalVolume"), updateDriver string→volume_type(to match this PR), and addvolume_context(map<string, string>).- CLI flags & delete behavior (from PR #3744): Add
--volume-contextand--previewto thekubectl ate create externalvolumeexamples, and update Tab 2S1(which currently says "the user deletes the ExternalVolume, which deletes the disk", whereasDeleteExternalVolumeonly removes the Substrate DB record and leaves storage in place). ExternalVolume.status&ActorStatusvolume status (for PR 2): Update theExternalVolumesketch to nest output-only fields (assigned_actorswithObjectRef actor+string actor_uid, andstate) underExternalVolume.statusper API Style Guide §8, note the rename of the existing per-actorExternalVolumeStatusmessage (storage_volume_id→volume_id) to avoid colliding withExternalVolume's status type, and reconcile.status.externalVolumes.volumes[](Tab 1) vs..status.actorVolumes[](Tab 2S3/D6).
Cross-resource references are named after the resource they point to (docs/api-style-guide.md 2.3), as agent-substrate#3744 does for its references to the same resource. Rename ExternalVolumeSource.ref to external_volume before it is served. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5d9d73e to
c94af04
Compare
Alexis MacAskill (amacaskill)
left a comment
There was a problem hiding this comment.
LGTM! Thanks for addressing all the review comments.
Cross-resource references are named after the resource they point to (docs/api-style-guide.md 2.3), as agent-substrate#3744 does for its references to the same resource. Rename ExternalVolumeSource.ref to external_volume before it is served. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Cross-resource references are named after the resource they point to (docs/api-style-guide.md 2.3), as agent-substrate#3744 does for its references to the same resource. Rename ExternalVolumeSource.ref to external_volume before it is served. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
c94af04 to
731ccef
Compare
| uid text NOT NULL, | ||
| version bigint NOT NULL, | ||
| proto bytea NOT NULL, | ||
| PRIMARY KEY (atespace, name), |
There was a problem hiding this comment.
🤖 Multiple ExternalVolume copies for the same (volume_type, volume_id):
With ExternalVolume being atespace-scoped and external_volumes only enforcing PRIMARY KEY (atespace, name), the same underlying CSI (volume_type, volume_id) can be registered multiple times (e.g. across multiple atespaces to share a volume, or under multiple names in the same atespace). Because the design treats each ExternalVolume row as the single source of truth for that storage volume, having multiple copies for the same (volume_type, volume_id) creates split-brain issues once lifecycle/deletion policies and control-plane locking land:
- Premature storage deletion (
ReclaimPolicy=Delete/DeleteTrigger):DeleteTrigger=LAST_ACTORand manualDeleteExternalVolumeonly inspectactor_refson that single(atespace, name)row. Deleting one copy (or its last bound actor inteam-a) can triggerCSI.DeleteVolume(volume_id)while another copy (team-b/vol) still has active or suspended actors using the same underlying volume. - Broken control-plane serialization & detach safety: Per-row locking (
SELECT ... FOR UPDATEonexternal_volumes) andactor_refsare scoped to(atespace, name):- Two actors in different atespaces can simultaneously pass
RWO(SINGLE_NODE_WRITER) checks against separate rows for the samevolume_id. - When suspending an actor on node
N, checkingactor_refsonteam-a/volwill not see active actors on nodeNbound viateam-b/vol, causingateapito callCSI.ControllerUnpublishVolumewhileteam-b's actor is still running on that node.
- Two actors in different atespaces can simultaneously pass
- Conflicting per-volume configuration: Separate copies can declare divergent
volume_context,access_mode(RWOvsRWX),delete_trigger, andreclaim_policyfor the same underlying CSI volume.
Proposed solution:
- Enforce 1:1
(volume_type, volume_id)uniqueness inexternal_volumes(in this PR): Liftvolume_typeandvolume_idinto columns onexternal_volumeswith aUNIQUE (volume_type, volume_id)constraint so each underlying CSI volume is represented by at most one authoritativeExternalVolumerecord (CreateExternalVolumealready maps unique violations tostore.ErrAlreadyExists). - Support cross-atespace references via
ObjectRefinstead of duplicating records (in the design doc & follow-up PRs): UseObjectRef(atespace+name) rather than a barenamestring forExternalVolumeRef,Actor.external_volume_bindings, andActorRef(similar toTag). Even if M1 initially restricts references to the actor's own atespace, usingObjectRefkeeps the API forward-compatible so cross-atespace sharing can be granted via OpenFGA permissions on the singleExternalVolumeobject rather than creating duplicate copies.
There was a problem hiding this comment.
I'm not sure we want to support cross-atespace references. If ExternalVolume is an atespaces resource, we would suddenly have an object in some atespace influencing another (i.e blocking actors from resuming, or from terminating an atespace). It's something we can discuss as a follow up but will pass on this PR.
AS for volume ID uniqueness that something we can enforce now.
AI: Enforce 1:1 (volume_type, volume_id) uniqueness in external_volumes in PostGres store.
Part 1 of Shared Volumes #1637 (design: Substrate Shared External Volumes).
This PR introduces a new atespaced ExternalVolume object, whose functionality will be added in a follow up PR. An
ExternalVolumeis an atespace-scoped record of a CSI volume that already exists in the storage system:volumeType(CSI driver),volumeIdandvolumeContext. Its lifetime is independent of any actor. Later changes let templates reference one, record actor references on it, and add its delete trigger.000002_external_volumes.sql,ON DELETE RESTRICTto atespaces), store methods and contract tests.Create/Get/List/DeleteExternalVolume. Create needs theExternalVolumespreview gate; Get, List and Delete work with the gate off, so turning it off cannot strand a record. Delete removes the record only; the storage stays.external_volumetype. Registering a volume is reserved for global owners, as the design's authorization section asks: providers do not scope volume IDs to atespaces, so an atespace editor could otherwise name storage another atespace uses. Viewers get and list, editors delete.kubectl ate {create,get,delete} externalvolume, docs.This will be followed up with a series of other PRs:
go testfor apivalidation, authz, store (Postgres), controlapi and the functional suite;hack/verify-all.sh;apitool validate(no new exemptions);hack/verify/proto-breaking.sh;hack/verify/postgresql-migrations.sh main. No e2e: the resource has no behavior on a cluster yet.