providerVolumes
Block volumes and shared volumes (network file shares) on a provider account (DigitalOcean Block Storage and Network File Storage first), next to the account's [instances](/docs/reference/api/instances). Every call records what the tenant wants and returns at once, usually `pending`; the `vps.provider-resources` reconciler then converges the provider through `ProviderImpl.volumes` without blocking on provider actions, and writes back what it saw (`state` is `pending`, `ready`, `updating`, `deleting`, `deleted` or `error`, with the provider's `message`). Reading takes `compute:volumes:read`, everything else `compute:volumes:manage`, on `provider-volume/{id}`. A volume books its `sizeGiB` on the tenant's `diskGiB` quota for the pool and bills from `ready` until deleted: a block volume `vps.volume.gib.hours`, a shared volume `vps.share.gib.hours`. Once created, a volume is only read: one deleted at the provider outside better-compute goes to `error` and is never created again (its data is gone; delete it). See the [provider volumes guide](/docs/guides/vps/provider-volumes).
Block volumes and shared volumes (network file shares) on a provider account (DigitalOcean Block Storage and Network File Storage first), next to the account's instances. Every call records what the tenant wants and returns at once, usually pending; the vps.provider-resources reconciler then converges the provider through ProviderImpl.volumes without blocking on provider actions, and writes back what it saw (state is pending, ready, updating, deleting, deleted or error, with the provider's message). Reading takes compute:volumes:read, everything else compute:volumes:manage, on provider-volume/{id}. A volume books its sizeGiB on the tenant's diskGiB quota for the pool and bills from ready until deleted: a block volume vps.volume.gib.hours, a shared volume vps.share.gib.hours. Once created, a volume is only read: one deleted at the provider outside better-compute goes to error and is never created again (its data is gone; delete it). See the provider volumes guide.
All 8 methods are served by @better-compute/server 0.3.0.
| Method | Kind | Authorization | Phase | Status |
|---|---|---|---|---|
create | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
list | read | compute:volumes:read @ compute-tenant + items | v1.0 | registered |
get | read | compute:volumes:read @ compute-provider-volume | v1.0 | registered |
attach | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
detach | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
resize | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
snapshot | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
delete | mutation | compute:volumes:manage @ compute-provider-volume | v1.0 | registered |
create
POST /api/compute/providerVolumes/create
Records a volume in a pool on a provider account and returns it pending, booking its sizeGiB (1 to 16384) on diskGiB.
regionmust be one the account uses (INVALID_INPUT); a pool whose provider offers no volumes isUNSUPPORTED.filesystem(ext4,xfsornone) formats it at create; it is never changed later.kindisblock(the default: a disk on one instance at a time) orshared(a network file share many instances of one tenant network mount at once). A shared volume needsnetworkId, a network of the tenant on the same provider account, provisioned, not being deleted (INVALID_STATE), with a subnet inregion(INVALID_INPUTwithout one,INVALID_STATEwhile it is not ready);networkIdon a block volume isINVALID_INPUT. A shared volume takes nofilesystemand nodeleteWithInstance: true(INVALID_INPUT). A provider that makes no shared volumes isUNSUPPORTED(details.reasonsharedVolumes).- What the provider would refuse before any request is refused here, before quota is booked: for example a DigitalOcean share under 50 GiB is
INVALID_INPUT(details.reasonsize). instanceIdattaches it once both exist: the instance must be the tenant's (ACCESS_DENIED), on the same account and region (INVALID_INPUT), for a shared volume on its network (INVALID_INPUT), and not being deleted (INVALID_STATE). The caller also needscompute:instances:updateon that instance, decided on its own credential.deleteWithInstance: truedeletes the volume with the instance it is attached to (on DigitalOcean, the Droplet's selective destroy names it); otherwise deleting the instance only detaches it.- The name is unique in the tenant among volumes that are not deleted (
CONFLICT). A tenant without room ondiskGiB, or without adiskGiBquota row, isQUOTA_EXCEEDED(fail closed). The sameidempotencyKeywith the same input returns the same volume; with other input it isCONFLICT.
Publishes providerVolume.created, then providerVolume.ready (with its device, the path the guest sees) or providerVolume.error. A create the provider refused (an account volume limit, a name taken by a volume that is not this deployment's) is error and is retried by the periodic pass; deleting it makes no provider call.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
projectId | string | at most 56 characters; matches ^prj_[0-9a-hjkmnp-tv-z]{26,52}$ | |
poolId | string | yes | at most 57 characters; matches ^pool_[0-9a-hjkmnp-tv-z]{26,52}$ |
name | string | yes | at most 42 characters; matches ^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$ |
description | string | at most 256 characters | |
region | string | yes | at most 40 characters; matches ^[a-z][a-z0-9-]{0,38}[a-z0-9]$ |
sizeGiB | integer | yes | 1-16384 |
kind | one of block, shared | default "block" | |
networkId | string | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ | |
filesystem | one of ext4, xfs, none | ||
deleteWithInstance | boolean | default false | |
instanceId | string | at most 57 characters; matches ^inst_[0-9a-hjkmnp-tv-z]{26,52}$ | |
idempotencyKey | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
list
POST /api/compute/providerVolumes/list
Returns the tenant's volumes in the order they were made (at most 1000), optionally of one pool or project, with their attachment (a shared volume: every attachment, as attachments). Deleted ones only with includeDeleted.
- Kind: read
- Authorization:
compute:volumes:read@compute-tenant+ items - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
poolId | string | at most 57 characters; matches ^pool_[0-9a-hjkmnp-tv-z]{26,52}$ | |
projectId | string | at most 56 characters; matches ^prj_[0-9a-hjkmnp-tv-z]{26,52}$ | |
includeDeleted | boolean | default false |
get
POST /api/compute/providerVolumes/get
Returns one volume: its kind, state, observed size, device, and the attachment (instanceId, state, desiredState) when it has one. A shared volume shows its networkId, every attachment as attachments, and mount (source, the NFS source <host>:<path> a guest mounts, and type nfs4) once the provider reports one.
- Kind: read
- Authorization:
compute:volumes:read@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
attach
POST /api/compute/providerVolumes/attach
Attaches the volume to an instance of the tenant on the same provider account and region, and returns it with the attachment pending; the reconciler attaches it at the provider without a reboot and publishes providerVolume.attached. Attaching it again to the same instance returns it unchanged. A block volume on another instance is CONFLICT (detach it first), one being detached INVALID_STATE. A shared volume attaches to any number of instances, each at most once; the instance must be on the volume's network (INVALID_INPUT), and one it is being detached from is INVALID_STATE until that finishes. Also decided as compute:instances:update on the instance, with the caller's own credential.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
instanceId | string | yes | at most 57 characters; matches ^inst_[0-9a-hjkmnp-tv-z]{26,52}$ |
detach
POST /api/compute/providerVolumes/detach
Detaches the volume from an instance and returns it. instanceId names the instance: it is required for a shared volume (INVALID_INPUT without it); for a block volume, naming an instance it is not on changes nothing. Then the reconciler asks the provider even while an attach is still running there, and publishes providerVolume.detached once the provider has let it go. A volume not attached to that instance (or to anything) is returned as it is. A deleteWithInstance volume whose instance is being deleted is INVALID_STATE: it goes with the machine.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
instanceId | string | at most 57 characters; matches ^inst_[0-9a-hjkmnp-tv-z]{26,52}$ |
resize
POST /api/compute/providerVolumes/resize
Grows the volume to sizeGiB, booking the difference on diskGiB, and returns it; the reconciler resizes it at the provider (updating, then ready) and publishes providerVolume.resized. A smaller size is INVALID_INPUT; the same size returns it unchanged. The filesystem is not grown: the guest does that.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
sizeGiB | integer | yes | 1-16384 |
snapshot
POST /api/compute/providerVolumes/snapshot
Records a snapshot of the volume named name and returns it pending (a provider snapshot); a retry with the same idempotencyKey returns the same snapshot. A volume has at most 25 snapshots that are not deleted (QUOTA_EXCEEDED); the name is unique among them (CONFLICT). Decided on the volume.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
name | string | yes | at most 42 characters; matches ^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$ |
idempotencyKey | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
delete
POST /api/compute/providerVolumes/delete
Marks the volume for deletion and returns it deleting; the reconciler deletes it at the provider, refunds its diskGiB and publishes providerVolume.deleted. A volume any instance still has attached is INVALID_STATE (detach it first; a shared volume from every instance). A volume never created at the provider, or whose create was refused, is finished without a provider call; one whose create answer was lost is looked up by name (never created to be deleted). Deleting a volume being deleted returns it.
- Kind: mutation
- Authorization:
compute:volumes:manage@compute-provider-volume - Module:
vps - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
volumeId | string | yes | at most 57 characters; matches ^pvol_[0-9a-hjkmnp-tv-z]{26,52}$ |
Better Compute is created by Sean Filimon
Last updated
providerSnapshots
Snapshots of [provider volumes](/docs/reference/api/provider-volumes), made with `providerVolumes.snapshot` and converged by the `vps.provider-resources` reconciler. Reading takes `compute:volumes:read`, deleting and restoring `compute:volumes:manage`, on `provider-snapshot/{id}`. A snapshot books no quota and bills `vps.snapshot.gib.hours` (the GiB the provider reports) from `ready` until deleted.
quotas
A quota caps how much of one resource a tenant may hold in one pool at the same time, as a `max` and a `used` counter per tenant, pool and resource. A missing row grants 0; `pools.create` and `pools.offer` create the rows, and admissions charge them with `QUOTA_EXCEEDED` when a request does not fit.