Skip to main content

A Tenant (tenants.k7.katakate.org/v1alpha1, cluster-scoped) claims namespaces, dedicated nodes, a default sandbox size, quotas and per-VM limits. Concepts and semantics: Tenants, roles and OIDC.

Tenants#

MethodPathRole
GET/api/v1/tenantsoperator, admin
GET/api/v1/tenants/{name}operator, admin
POST/api/v1/tenantsadmin
PATCH/api/v1/tenants/{name}admin
DELETE/api/v1/tenants/{name}[?cascade=true]admin
  • spec.namespaces (required, ≥ 1): created if missing and labelled k7.katakate.org/tenant. A namespace another tenant already claims is 400.
  • spec.nodes: the tenant’s sandboxes land only on these nodes (selector + toleration). Dedicate them with the node routes below so other tenants stay off.
  • spec.defaultSize (cpu, memory, disk): filled into creates without limits. Production mode requires CPU and memory on every sandbox, so set it or make callers pass limits.
  • spec.quotas (sandboxes, vcpu, memory, disk, snapshotStorage): checked at the Deployment and VolumeSnapshot creates under a per-tenant Lease, so concurrent creates on several API replicas cannot overshoot. Usage is reserved (a paused sandbox keeps its vCPU/memory/disk). Over-quota: 403 QuotaExceeded. status.usage is rewritten every 60 s by the leader replica.
  • spec.limits.maxSandboxSize: a request above it is 400. limits.network (egressBps/ingressBps, bits/s) is enforced on kfd and kql; limits.diskIO is accepted by the CRD but no backend enforces it yet — a tenant that sets it (or network / a disk quota on k7d / k7d-fc) gets 400 LimitNotEnforceable on create rather than running unlimited.
  • PATCH replaces whole sections; null removes one. DELETE refuses a tenant that still has sandboxes unless cascade=true.

For a single-namespace tenant K7 also writes a Kubernetes ResourceQuota (k7-tenant-quota) as a backstop against kubectl apply bypassing the API; multi-namespace tenants have none.

Usage (showback)#

Endpoint: GET /api/v1/usage?from=&to=&tenant=&group_by=

fromstringrequired#
Unix seconds or RFC 3339 with offset
tostringrequired#
Same formats
tenantstring#
A tenant key must name its own; omit (operator/admin) for all tenants
group_bystring#
Subset of tenant,namespace,backend,state

Returns rows of sandbox_seconds, vcpu_seconds, memory_gib_seconds, egress_bytes, disk_gib_seconds and snapshot_gib_seconds per group, read from the Prometheus at metering.prometheusUrl (k7 install --monitoring or --metering-prometheus-url). Without one the route answers 400 naming the key; an unreachable Prometheus is 502 PrometheusUnavailable. K7 keeps no usage database — the CloudEvents usage stream is the record for billing. See Observability and metering.

Node dedication#

MethodPathBodyRole
POST/api/v1/nodes/{name}/dedicate{"tenant": "acme"}operator, admin
POST/api/v1/nodes/{name}/undedicate—operator, admin

Dedicating writes the k7.katakate.org/tenant label and a NoSchedule taint on the node and adds it to Tenant.spec.nodes; undedicating reverses all three. {name} is the Kubernetes node name (k7 nodes list); an unknown node or tenant is 404.

Remote install#

MethodPathRole
POST/api/v1/installadmin
GET/api/v1/install/{operation}admin

Off by default (404). k7 install --api-remote-install (development mode only) deploys the k7-node-installer DaemonSet in k7-system, configured from the first master and pinned to the digest in images.lock.yaml. The API never runs Ansible: POST takes exactly {"nodes": ["name", …]}, labels those nodes into the installer pool and returns 202 with an operation id; GET reports each node pending / running / succeeded / failed with the installer’s own failure line.

Any other body key (playbook, image, inventory, config) is 400; an unknown node 404; a K3s server node without --node-installer-allow-server-restart 409; a DaemonSet whose image drifted from the pin 500. SDK: Client.install(nodes) / install_status(operation).