networks
Networks are a tenant's private networks (VPCs) on a provider account, with their subnets and firewall rules ([firewallRules](/docs/reference/api/firewall-rules)). A network belongs to one provider pool, so to that pool's account, and the provider must support tenant networks (Google Cloud does; a provider that does not is `UNSUPPORTED`). The calls record what the tenant wants and return at once. The `vps.networks` reconciler then converges the provider: it creates the VPC, then the subnets and the firewall rules, then tags the network's instances, and removes what the tenant deleted, rules first, then subnets, then the network. `state` shows where it is: `pending`, `ready`, `deleting`, `deleted` or `error` (with a `message`). The network's `state` covers the VPC and its subnets (`ready` when all are; `error` with the failing resource in `message`); each subnet and each rule also has its own `state`, and a rule the provider refuses is `error` on the rule without taking the network out of `ready`. The reconciler publishes `network.ready`, `network.error`, `network.subnet.ready`, `network.rule.ready`, `network.rule.error` and `network.deleted`. An `error` is retried by the periodic pass, which runs every five minutes and also re-applies what drifted at the provider (a firewall deleted by hand).
Networks are a tenant's private networks (VPCs) on a provider account, with their subnets and firewall rules (firewallRules). A network belongs to one provider pool, so to that pool's account, and the provider must support tenant networks (Google Cloud does; a provider that does not is UNSUPPORTED). The calls record what the tenant wants and return at once. The vps.networks reconciler then converges the provider: it creates the VPC, then the subnets and the firewall rules, then tags the network's instances, and removes what the tenant deleted, rules first, then subnets, then the network. state shows where it is: pending, ready, deleting, deleted or error (with a message). The network's state covers the VPC and its subnets (ready when all are; error with the failing resource in message); each subnet and each rule also has its own state, and a rule the provider refuses is error on the rule without taking the network out of ready. The reconciler publishes network.ready, network.error, network.subnet.ready, network.rule.ready, network.rule.error and network.deleted. An error is retried by the periodic pass, which runs every five minutes and also re-applies what drifted at the provider (a firewall deleted by hand).
Only instances that own their machine are tagged: not deleting, lost or failed, and an attached one once it is claimed and while it has no OwnershipLost, AttachConflict or DuplicateMachine condition; the provider is told the instance id and leaves a machine labelled for another instance alone. Tags are set after the rules, and a machine whose tags were set within the last hour is skipped, so a network with more machines than one run can tag gets its rules at once and its tags over the next runs; a tag removed by hand comes back within the hour.
The provider-side name of a network, a subnet and a rule is bc- and the resource's own id, lower-cased. A machine on the network carries the tag bc- and 16 hex digits of the sha256 of its instance id, so a rule can target single instances.
Every tenant's networks on an account live in that account's one provider project, so they share its quotas (VPC networks, subnetworks and firewall rules per project): one tenant's networks use up what the others on the account can get. To bound that, a tenant has at most 10 networks that are not deleted, and a network at most 16 subnets and 100 firewall rules (QUOTA_EXCEEDED, with the cap in details.limit). The project's own quotas still apply on top, and a refusal of the provider shows as error with its message.
All 11 methods are served by @better-compute/server 0.3.0.
| Method | Kind | Authorization | Phase | Status |
|---|---|---|---|---|
create | mutation | compute:networks:create @ compute-network | v1.0 | registered |
list | read | compute:networks:read @ compute-tenant + items | v1.0 | registered |
get | read | compute:networks:read @ compute-network | v1.0 | registered |
update | mutation | compute:networks:update @ compute-network | v1.0 | registered |
delete | mutation | compute:networks:delete @ compute-network | v1.0 | registered |
addSubnet | mutation | compute:networks:update @ compute-network | v1.0 | registered |
removeSubnet | mutation | compute:networks:update @ compute-network | v1.0 | registered |
adopt | mutation | compute:providers:manage @ compute-fleet | v1.0 | registered |
natGateway | mutation | compute:networks:update @ compute-network | v1.0 | registered |
peer | mutation | compute:networks:update @ compute-network | v1.0 | registered |
routes | mutation | compute:networks:update @ compute-network | v1.0 | registered |
create
POST /api/compute/networks/create
Records a network on the pool's provider account and returns it pending, with no subnets yet.
- The pool must be offered to the tenant, a
providerpool, and its account's provider must support networks; otherwise the call isUNSUPPORTED(NOT_FOUNDfor a pool the tenant cannot see). nameis 1 to 42 characters,[a-z]([-a-z0-9]*[a-z0-9])?, unique among the tenant's networks that are not deleted (CONFLICTotherwise; the name of a deleted network is free again).- A tenant that already has 10 networks that are not deleted (one being deleted still counts) is
QUOTA_EXCEEDED. - Decided as
compute:networks:createon the network it would create (compute-network, under the tenant), whose attributes areproviderAccountId,poolIdandprojectId. A refusal leaves nothing behind. - On a pool whose billing mode is
metered, the tenant's budgets are checked after the decision, as forinstances.create: when the pool asks for it or the tenant's offer isstop-new,drainorsuspend, a spent budget isSPEND_LIMIT_REACHED. A root caller creating a network for a tenant is checked against that tenant's budgets. A suspended offer or a region itsallowedRegionsexclude refuses the create on any pool.
The network id derives from the tenant, the caller and idempotencyKey. A retry with the same key returns the same network; the same key with another pool, name, project or description is CONFLICT. The caller owns the network. Publishes network.created.
- Kind: mutation
- Authorization:
compute:networks:create@compute-network - 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 | |
cidr | string | ||
idempotencyKey | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
list
POST /api/compute/networks/list
Returns the tenant's networks with their subnets, oldest first; poolId and projectId narrow it, and includeDeleted: true adds deleted ones. At most 500 are returned. Decided as compute:networks:read on the tenant.
- Kind: read
- Authorization:
compute:networks: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/networks/get
Returns one network with its subnets (pending, ready, deleting, deleted or error). A network of another tenant is NOT_FOUND here, and is refused by the decision (ACCESS_DENIED) before that. Decided as compute:networks:read on the network.
- Kind: read
- Authorization:
compute:networks:read@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
update
POST /api/compute/networks/update
Changes the network's description. The name, pool and account never change, because the provider cannot rename a network. A network being deleted is INVALID_STATE. Decided as compute:networks:update on the network.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
description | string | yes | at most 256 characters |
delete
POST /api/compute/networks/delete
Marks the network, its subnets and its rules for deletion and returns it deleting, along with its routes, peerings (also those another network holds with it) and NAT gateways. The reconciler deletes the routes, peerings and gateways, then the rules, then the subnets, then the network, and then the network is deleted and publishes network.deleted.
INVALID_STATEwhile instances that are not deleted are on the network, with their ids indetails.instanceIds: delete or move them first. Likewise while load balancers are on one of its subnets (details.loadBalancerIds).INVALID_STATEwhile filesystems that are not deleted are on the network, with their ids indetails.filesystemIds: delete them first.INVALID_STATEwhile shared provider volumes that are not deleted are reached over the network, with their ids indetails.volumeIds: delete them first.- Deleting a network that is being deleted returns it unchanged.
Publishes network.deleting. Decided as compute:networks:delete on the network.
- Kind: mutation
- Authorization:
compute:networks:delete@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
addSubnet
POST /api/compute/networks/addSubnet
Adds a subnet and returns the network with it pending.
cidris an IPv4 range inside 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16 (RFC 1918), with a prefix from /8 to /29, written as its network address (10.0.0.0/20). Anything else isINVALID_INPUT.- It must not overlap another subnet of the network, in any region: that is
CONFLICT, and the database refuses it too. regionmust be a region of the zones the account is configured with (us-central1for the zoneus-central1-a); otherwiseINVALID_INPUT, with the allowed regions indetails.- The name is unique in the network; the name of a deleted subnet is free again.
- A network that already has 16 subnets (not counting removed ones) is
QUOTA_EXCEEDED. - A network being deleted is
INVALID_STATE.
Publishes network.subnet.ready once the provider has the subnet. Decided as compute:networks:update on the network.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[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])?$ |
region | string | yes | matches ^[a-z][a-z0-9-]{1,38}[a-z0-9]$ |
zone | string | matches ^[a-z][a-z0-9-]{1,62}[a-z0-9]$ | |
cidr | string | yes | |
privateAccess | boolean |
removeSubnet
POST /api/compute/networks/removeSubnet
Marks a subnet for deletion and returns the network with the subnet deleting. INVALID_STATE while instances that are not deleted use the subnet, with their ids in details.instanceIds, or load balancers are on it (details.loadBalancerIds), and while shared provider volumes of the network that are not deleted are in the subnet's region (a share lives in one of the network's VPCs there), with their ids in details.volumeIds. Its range is free for a new subnet at once. Decided as compute:networks:update on the network.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
subnetId | string | yes | at most 57 characters; matches ^snet_[0-9a-hjkmnp-tv-z]{26,52}$ |
adopt
POST /api/compute/networks/adopt
Registers a VPC that already exists in the pool's provider account as a network of the tenant, so attached VMs whose NIC is on it are linked to it and get its firewall rules; returns the network pending, with externalNetwork set. Only the platform (root callers) adopts.
-
Input.
externalNetworkis the VPC's provider-side name, 1 to 63 characters[a-z]([-a-z0-9]*[a-z0-9])?; abc-name (a network better-compute made) isINVALID_INPUT.defaultis shared with every VM of the project: adopting it needssharedTagScoped: true(INVALID_INPUTwithdetails.field: 'sharedTagScoped'otherwise), which the network records and shows.nameanddescriptionare as forcreate, and the 10-network cap applies. -
The VPC. better-compute never creates, changes or deletes an adopted VPC: the reconciler skips it, provisions only the subnets and rules added to the network, and
deleteremoves those and leaves the VPC. Its provider-side name isexternalNetwork, notbc-and the id, also for instances created on it. -
Firewall rules. A rule on an adopted network must name
targets.instanceIds(INVALID_INPUTonfirewallRules.createandupdateotherwise), so it always targets instance network tags and never the whole VPC. A rule without targets that reaches the reconciler anyway goes toerrorinstead of being applied. -
Attach.
instances.attachlinks a machine whose NIC is on the VPC (by its name, or a reference ending in it) to the network, as it does for networks better-compute made. -
Idempotency. The same adoption again (same pool, VPC, name, flag and description) returns the network. A VPC already adopted in the tenant under another request is
CONFLICTwithdetails.networkId; one adopted by another tenant isCONFLICTunless both adoptions aresharedTagScoped. -
IAM. Decided as
compute:providers:manageon the fleet (compute-fleet, in the root tenant); anyone but the platform isACCESS_DENIED. The adopting identity owns the network. -
Events.
network.adoptedwithpoolId,providerAccountId,name,externalNetworkandsharedTagScoped. -
Kind: mutation
-
Authorization:
compute:providers:manage@compute-fleet -
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 | yes | at most 57 characters; matches ^pool_[0-9a-hjkmnp-tv-z]{26,52}$ |
externalNetwork | string | yes | at most 63 characters; matches ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$ |
name | string | yes | at most 42 characters; matches ^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$ |
sharedTagScoped | boolean | default false | |
description | string | at most 256 characters |
natGateway
POST /api/compute/networks/natGateway
Turns a subnet's NAT gateway on (enabled: true) or off (enabled: false) and returns the network; the gateway appears in natGateways (pending, then ready with its public egress addresses). A NAT gateway carries the subnet's outbound traffic, so its machines need no public address of their own (isolated pools, and DOKS isolated worker nodes). Only providers with NAT gateways take it (DigitalOcean does; Google Cloud is UNSUPPORTED here).
- One gateway per subnet: asking again returns the network unchanged, and turning off a subnet without one is a no-op.
- Turning it off is
INVALID_STATE, with the route names indetails.routes, while a route of the subnet sends traffic to it (networks.routes): change those routes first. - The subnet must belong to the network and not be being removed (
NOT_FOUND,INVALID_STATE); a network being deleted isINVALID_STATE. - Removing the subnet or deleting the network removes the gateway first: the reconciler deletes the subnet only once the gateway is gone.
Publishes network.natGateway.ready (and network.natGateway.error) with edgeId and subnetId. Decided as compute:networks:update on the network.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
subnetId | string | yes | at most 57 characters; matches ^snet_[0-9a-hjkmnp-tv-z]{26,52}$ |
enabled | boolean | yes |
peer
POST /api/compute/networks/peer
Peers a subnet of the network (subnetId) with a subnet of another network of the tenant (peerNetworkId, peerSubnetId), so their machines reach each other on private addresses; enabled: false removes the peering. Two subnets of the same network may be peered too: on DigitalOcean every subnet is a VPC of its own, and two of them do not reach each other otherwise. Only providers with peerings take it (UNSUPPORTED otherwise).
- The peering appears in
peeringson both networks, each seen from its own side (subnetIdis that network's subnet). The same pair asked again, from either network, is the same peering; either network can remove it. - Both networks must be the tenant's (another tenant's network is
NOT_FOUND), on the same provider account (INVALID_INPUT), and not being deleted (INVALID_STATE). A subnet is never peered with itself (INVALID_INPUT). - A network takes part in at most 16 peerings (
QUOTA_EXCEEDED, withdetails.limitand the network indetails.networkId). - Removing either subnet, or deleting either network, removes the peering first; the subnet goes once the peering is gone.
Publishes network.peering.ready (and network.peering.error) on the network that asked first. Decided as compute:networks:update on the network, and, on the caller's own credential, on the other network too.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
subnetId | string | yes | at most 57 characters; matches ^snet_[0-9a-hjkmnp-tv-z]{26,52}$ |
peerNetworkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
peerSubnetId | string | yes | at most 57 characters; matches ^snet_[0-9a-hjkmnp-tv-z]{26,52}$ |
enabled | boolean | default true |
routes
POST /api/compute/networks/routes
Replaces the static routes of a subnet with routes (at most 32) and returns the network; the routes appear in routes. A route kept exactly as it was stays; a changed one is replaced (deleted at the provider, then made anew); one left out is deleted; an empty list removes them all. Each route has a name (unique in the subnet), a destination IPv4 CIDR written as its network address (0.0.0.0/0 for everything), and exactly one nextHop:
{ natGateway: true }: the subnet's NAT gateway, which must exist (INVALID_STATEotherwise). The reconciler makes the route once the gateway isready.{ instanceId }: an instance of the tenant on this subnet (NOT_FOUND, orINVALID_INPUTwhen it is on another subnet). The route waits until the instance has a machine; an instance that is gone later puts the route inerror. Decided, besides the network, ascompute:instances:updateon the instance, on the caller's own credential.{ address }: a host address inside the subnet (INVALID_INPUTotherwise).
Routes are a preview at DigitalOcean (VPC Routes): the account needs previews: true, or the route goes to error with the provider's refusal. Only providers with static routes take a non-empty list (UNSUPPORTED otherwise).
Publishes network.route.ready (and network.route.error). Decided as compute:networks:update on the network.
- Kind: mutation
- Authorization:
compute:networks:update@compute-network - 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}$ |
networkId | string | yes | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ |
subnetId | string | yes | at most 57 characters; matches ^snet_[0-9a-hjkmnp-tv-z]{26,52}$ |
routes | list of object | yes | 0-32 items |
routes[].name | string | yes | at most 42 characters; matches ^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$ |
routes[].destination | string | yes | |
routes[].nextHop | object | yes | |
routes[].nextHop.natGateway | any | ||
routes[].nextHop.instanceId | string | at most 57 characters; matches ^inst_[0-9a-hjkmnp-tv-z]{26,52}$ | |
routes[].nextHop.address | string |
Better Compute is created by Sean Filimon
Last updated
metal
The metal group enrolls machines by their BMCs and moves them through the provision state machine: inspect, commission, deploy, rescue, release and retire. Each verb checks the move against the provision state table, records it in one transaction and queues the metal reconciler, which runs the workflow of the new state; no verb touches the machine itself. Every method returns the node's metal view, which reports whether BMC credentials are set but never returns them. See [Bare metal](/docs/guides/bare-metal) and [BMC drivers and credentials](/docs/guides/bare-metal/bmc).
nodes
Nodes are machines that joined the control plane, as their tenant sees them: a tenant sees the nodes the authorization projection holds for it. Every method except `list` is decided on `compute-node/{nodeId}`. Mutations take an optional `version`; when the node has moved past it, the call is `CONFLICT`. See [Operating nodes](/docs/guides/nodes/operating-nodes).