kubernetesClusters
Kubernetes clusters are managed Kubernetes clusters (Google Kubernetes Engine, Standard and Autopilot) on a provider account, served by the `@better-compute/kubernetes` module. The provider runs the control plane; better-compute never runs one itself. A cluster belongs to one provider pool, so to that pool's account, and the account's provider must provide managed Kubernetes (a provider that does not is `UNSUPPORTED`). Two hosting models use the same calls: a platform account's pool offered to the tenant (the cluster lives in the platform's cloud project), or a pool on the tenant's own account (the cluster lives in the tenant's project). The calls record what the tenant wants and return at once; the `kubernetes.clusters` reconciler then creates the cluster at the provider, its node pools once the control plane is up, applies changes in place and deletes what the tenant deleted. `state` shows where it is: `pending`, `provisioning`, `ready` (the control plane and every node pool are ready), `updating`, `deleting`, `deleted` or `error` (with a `message`). The reconciler publishes `kubernetes.cluster.ready` the first time, `kubernetes.cluster.updated` when it is ready again after a change or the provider moved it to another version (a release-channel upgrade), `kubernetes.cluster.error` and `kubernetes.cluster.deleted`. Clusters still converging are looked at every 30 seconds; ready and failed ones are refreshed every five minutes, which retries an error and picks up what changed at the provider.
Kubernetes clusters are managed Kubernetes clusters (Google Kubernetes Engine, Standard and Autopilot) on a provider account, served by the @better-compute/kubernetes module. The provider runs the control plane; better-compute never runs one itself. A cluster belongs to one provider pool, so to that pool's account, and the account's provider must provide managed Kubernetes (a provider that does not is UNSUPPORTED). Two hosting models use the same calls: a platform account's pool offered to the tenant (the cluster lives in the platform's cloud project), or a pool on the tenant's own account (the cluster lives in the tenant's project). The calls record what the tenant wants and return at once; the kubernetes.clusters reconciler then creates the cluster at the provider, its node pools once the control plane is up, applies changes in place and deletes what the tenant deleted. state shows where it is: pending, provisioning, ready (the control plane and every node pool are ready), updating, deleting, deleted or error (with a message). The reconciler publishes kubernetes.cluster.ready the first time, kubernetes.cluster.updated when it is ready again after a change or the provider moved it to another version (a release-channel upgrade), kubernetes.cluster.error and kubernetes.cluster.deleted. Clusters still converging are looked at every 30 seconds; ready and failed ones are refreshed every five minutes, which retries an error and picks up what changed at the provider.
The provider-side name of a cluster is bc-k- and 24 characters of its id, and it carries the label better-compute-cluster with the cluster id. A cluster bills kubernetes.cluster.hours (one per hour, to the tenant) from the moment the provider first accepted it until it is deleted, on pools whose billing mode is metered or platform-fee. Its nodes are not metered here: the provider bills them to the account, and on a platform account they reach the tenant through the account's cost pass-through rules.
All 7 methods are served by @better-compute/server 0.3.0.
| Method | Kind | Authorization | Phase | Status |
|---|---|---|---|---|
create | mutation | compute:kubernetes:create @ compute-kubernetes-cluster | v1.0 | registered |
list | read | compute:kubernetes:read @ compute-tenant + items | v1.0 | registered |
get | read | compute:kubernetes:read @ compute-kubernetes-cluster | v1.0 | registered |
update | mutation | compute:kubernetes:update @ compute-kubernetes-cluster | v1.0 | registered |
delete | mutation | compute:kubernetes:delete @ compute-kubernetes-cluster | v1.0 | registered |
credentials | mutation | compute:kubernetes:connect @ compute-kubernetes-cluster | v1.0 | registered |
versions | read | compute:kubernetes:read @ compute-tenant | v1.0 | registered |
create
POST /api/compute/kubernetesClusters/create
Records a cluster and returns it pending, with its node pools.
- The pool must be visible to the tenant (its own, or offered to it), a
providerpool, and its account's provider must provide managed Kubernetes; otherwiseUNSUPPORTED(NOT_FOUNDfor a pool the tenant cannot see). locationis a zone the account is configured with (a zonal cluster) or the region of one (a regional cluster); anything else isINVALID_INPUTwith the allowed zones and regions indetails.mode:standardtakes 1 to 10nodePools(created with the cluster);autopilottakes none (INVALID_INPUTotherwise), the provider runs its nodes. Node pool fields are as for kubernetesNodePools.create; a node pool'szonesmust be zones of the cluster's region (and of the account).releaseChanneldefaults toregular;version(a minor such as1.33or a full version) pins the control plane, otherwise the channel's default applies.authorizedNetworkslists at most 50 IPv4 CIDRs that may reach the control-plane endpoint (empty: any address; requests are still authenticated).privateNodes: truegives nodes no public addresses.networkIdnames a tenant network (networks) on the same account that has been provisioned and has areadysubnet in the cluster's region; the nodes use it and that subnet (the first by name when there are several). Another tenant's network, or one on another account, isINVALID_INPUT; one not provisioned yet isINVALID_STATE.nameis 1 to 40 characters,[a-z]([-a-z0-9]*[a-z0-9])?, unique among the tenant's clusters that are not deleted (CONFLICTotherwise; a deleted cluster's name is free again).- A tenant that already has 10 clusters that are not deleted (the module's
maxClustersPerTenant; one being deleted still counts) isQUOTA_EXCEEDED, with the cap indetails.limit. - Decided as
compute:kubernetes:createon the cluster it would create (compute-kubernetes-cluster, under the tenant), whose attributes areproviderAccountId,poolId,projectId,modeandlocation. A refusal leaves nothing behind. After the decision the tenant's offer is checked as forinstances.create(a suspended offer or an excluded region refuses), and the tenant's budgets unless the pool's billing mode isnone.
The cluster id derives from the tenant, the caller and idempotencyKey. A retry with the same key returns the same cluster; the same key with any other field changed is CONFLICT. The caller owns the cluster. Publishes kubernetes.cluster.created.
- Kind: mutation
- Authorization:
compute:kubernetes:create@compute-kubernetes-cluster - Module:
kubernetes - 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 40 characters; matches ^[a-z]([-a-z0-9]{0,38}[a-z0-9])?$ |
mode | one of standard, autopilot | yes | |
location | string | yes | at most 40 characters; matches ^[a-z][a-z0-9-]{1,38}[a-z0-9]$ |
releaseChannel | one of rapid, regular, stable, extended | default "regular" | |
version | string | at most 64 characters; matches ^[0-9]{1,3}\.[0-9]{1,3}(\.[0-9]{1,3})?(-[a-z0-9.]{1,40})?$ | |
networkId | string | at most 56 characters; matches ^net_[0-9a-hjkmnp-tv-z]{26,52}$ | |
authorizedNetworks | list of string | 0-50 items | |
privateNodes | boolean | default false | |
description | string | at most 256 characters | |
nodePools | list of object | 0-10 items | |
nodePools[].name | string | yes | at most 40 characters; matches ^[a-z]([-a-z0-9]{0,38}[a-z0-9])?$ |
nodePools[].machineType | string | yes | at most 63 characters; matches ^[a-z0-9][a-z0-9.-]{0,62}$ |
nodePools[].nodeCount | integer | yes | 0-1000 |
nodePools[].autoscaling | object | ||
nodePools[].autoscaling.minNodes | integer | yes | 0-1000 |
nodePools[].autoscaling.maxNodes | integer | yes | 1-1000 |
nodePools[].diskGiB | integer | 10-65536 | |
nodePools[].diskType | string | matches ^[a-z][a-z0-9-]{1,40}$ | |
nodePools[].spot | boolean | default false | |
nodePools[].accelerators | object | ||
nodePools[].accelerators.type | string | yes | matches ^[a-z0-9][a-z0-9-]{0,62}$ |
nodePools[].accelerators.count | integer | yes | 1-16 |
nodePools[].zones | list of string | 0-10 items | |
nodePools[].labels | map of string | ||
nodePools[].taints | list of object | 0-32 items | |
nodePools[].taints[].key | string | yes | |
nodePools[].taints[].value | string | at most 63 characters | |
nodePools[].taints[].effect | one of NoSchedule, PreferNoSchedule, NoExecute | yes | |
idempotencyKey | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
list
POST /api/compute/kubernetesClusters/list
Returns the tenant's clusters with their node pools, oldest first; poolId and projectId narrow it, and includeDeleted: true adds deleted ones. At most 500 are returned. Decided as compute:kubernetes:read on the tenant.
- Kind: read
- Authorization:
compute:kubernetes:read@compute-tenant+ items - Module:
kubernetes - 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/kubernetesClusters/get
Returns one cluster with its node pools, its endpoint and the kubernetesVersion and nodeVersion the provider last reported. A cluster of another tenant is NOT_FOUND here, and is refused by the decision (ACCESS_DENIED) before that. Decided as compute:kubernetes:read on the cluster.
- Kind: read
- Authorization:
compute:kubernetes:read@compute-kubernetes-cluster - Module:
kubernetes - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
clusterId | string | yes | at most 56 characters; matches ^kcl_[0-9a-hjkmnp-tv-z]{26,52}$ |
update
POST /api/compute/kubernetesClusters/update
Changes the control-plane version, the releaseChannel, the authorizedNetworks or the description; name at least one (INVALID_INPUT otherwise). The first three change in place at the provider: a ready or failed cluster goes updating and back to ready once the provider has them (kubernetes.cluster.updated). The description is better-compute's alone and changes nothing at the provider. Every update bumps version. A cluster being deleted is INVALID_STATE. A version the provider refuses (a downgrade, one it does not offer) shows as error with its message. Decided as compute:kubernetes:update on the cluster.
- Kind: mutation
- Authorization:
compute:kubernetes:update@compute-kubernetes-cluster - Module:
kubernetes - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
clusterId | string | yes | at most 56 characters; matches ^kcl_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | string | at most 64 characters; matches ^[0-9]{1,3}\.[0-9]{1,3}(\.[0-9]{1,3})?(-[a-z0-9.]{1,40})?$ | |
releaseChannel | one of rapid, regular, stable, extended | ||
authorizedNetworks | list of string | 0-50 items | |
description | string | at most 256 characters |
delete
POST /api/compute/kubernetesClusters/delete
Marks the cluster and its node pools for deletion and returns it deleting. The reconciler deletes the cluster at the provider (its node pools go with it); the cluster and its pools are then deleted, its authorization row is removed and kubernetes.cluster.deleted is published. Deleting a cluster that is being deleted returns it unchanged. If the provider refuses the delete the cluster is error with its message and the delete is retried with a growing wait (5, 10, 20 minutes and so on, at most every 6 hours); calling delete again tries at once. Publishes kubernetes.cluster.deleting. Decided as compute:kubernetes:delete on the cluster.
- Kind: mutation
- Authorization:
compute:kubernetes:delete@compute-kubernetes-cluster - Module:
kubernetes - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
clusterId | string | yes | at most 56 characters; matches ^kcl_[0-9a-hjkmnp-tv-z]{26,52}$ |
credentials
POST /api/compute/kubernetesClusters/credentials
Returns a short-lived kubeconfig for the caller: { kubeconfig, expiresAt, role }, where kubeconfig is YAML text with one cluster (the control-plane endpoint and its CA), one user with a bearer token, one context and current-context set.
-
roleisview(the default),editoradmin, bound to Kubernetes' built-inview,editandcluster-adminroles. The call is decided ascompute:kubernetes:connecton the cluster;editalso needscompute:kubernetes:updateandadmincompute:kubernetes:deleteon it, decided with the caller's own credential (ACCESS_DENIEDotherwise). -
The provider mints the token for a Kubernetes service account derived from the caller and the role (
bc-u-, 16 hex digits of a hash of the identity,-view/-edit/-admin), so each role keeps its own account and a laterviewkubeconfig never narrows anadminone the caller holds. -
ttlSecondsis 600 to 86400 (default 3600, the module'scredentialTtlSeconds). -
The cluster must be
ready(orupdatingafter it was ready once); otherwiseINVALID_STATE. -
The token is never stored, logged or published. Publishes
kubernetes.cluster.credentials-issuedwith the role, subject, identity, ttl and expiry. -
Kind: mutation
-
Authorization:
compute:kubernetes:connect@compute-kubernetes-cluster -
Module:
kubernetes -
Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
clusterId | string | yes | at most 56 characters; matches ^kcl_[0-9a-hjkmnp-tv-z]{26,52}$ |
role | one of view, edit, admin | default "view" | |
ttlSeconds | integer | 600-86400 |
versions
POST /api/compute/kubernetesClusters/versions
Returns the Kubernetes versions the provider offers in a location of a pool visible to the tenant: defaultVersion, validVersions, and per release channel its default and valid versions. The pool and location are checked as for create. Decided as compute:kubernetes:read on the tenant.
- Kind: read
- Authorization:
compute:kubernetes:read@compute-tenant - Module:
kubernetes - 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}$ |
location | string | yes | at most 40 characters; matches ^[a-z][a-z0-9-]{1,38}[a-z0-9]$ |
Better Compute is created by Sean Filimon
Last updated
joinTokens
Join tokens are single-purpose credentials that let a limited number of daemons join one pool for a limited time. The bearer form `bcjt_<id>.<secret>` is returned once, when the token is created; the control plane stores only the SHA-256 of the secret. Every method is decided on `compute-pool/{poolId}`. See [Joining nodes](/docs/guides/nodes/joining).
kubernetesNodePools
Node pools are the groups of worker nodes of a `standard` Kubernetes cluster ([kubernetesClusters](/docs/reference/api/kubernetes-clusters)); an `autopilot` cluster has none, because the provider runs its nodes. A cluster's first node pools are created with it; these calls add, resize and remove pools later. Every call records what the tenant wants and returns at once: the node pool shows `pending`, `ready`, `updating`, `deleting`, `deleted` or `error` (with a `message`), and its cluster goes `updating` until the reconciler has converged the change, then `ready` again. A node pool the provider refuses is `error`, and so is its cluster, with the pool named in its `message`, until the pool is changed or deleted. The reconciler publishes `kubernetes.nodepool.ready`, `kubernetes.nodepool.error` and `kubernetes.nodepool.deleted` on the cluster's subject. A node pool's name is also its provider-side name.