Documentation
¶
Overview ¶
Package kube is the production controlplane.Kubernetes adapter, built on the official client-go SDK (ADR-0011). It translates the workload seam into Kubernetes Deployments and reads their status, scales, streams logs, and deletes them. It is a thin translation layer — no orchestration logic, which lives in the engine. v0.1 supports only WorkloadDeployment.
It lives under controlplane/ (not controlplane/internal) so cmd/burrowd and the managed module can wire it; it is licensed Apache-2.0.
Index ¶
- Constants
- Variables
- func BuilderImageForVersion(version string) string
- func ConfigFromKubeconfig(path string) (*rest.Config, error)
- func DetectCapabilities(ctx context.Context, client kubernetes.Interface) (controlplane.ClusterCapabilities, error)
- func LoadConfig() (*rest.Config, error)
- func ReadResourceState(ctx context.Context, client kubernetes.Interface) (controlplane.ClusterResourceState, error)
- type Adapter
- func (a *Adapter) AddonReady(ctx context.Context, name string) (bool, error)
- func (a *Adapter) ApplyAutoscaler(ctx context.Context, app string, spec controlplane.AutoscaleSpec) error
- func (a *Adapter) ApplyWorkload(ctx context.Context, spec controlplane.WorkloadSpec) error
- func (a *Adapter) AutoscalerActive(ctx context.Context, app string) (bool, error)
- func (a *Adapter) DeleteAddon(ctx context.Context, name string) error
- func (a *Adapter) DeleteAutoscaler(ctx context.Context, app string) error
- func (a *Adapter) DeleteWorkload(ctx context.Context, app string) error
- func (a *Adapter) DeployAddon(ctx context.Context, spec controlplane.AddonSpec) (controlplane.AddonInfo, error)
- func (a *Adapter) Expose(ctx context.Context, spec controlplane.ExposeSpec) error
- func (a *Adapter) ExposureStatus(ctx context.Context, app string) (controlplane.ExposureStatus, error)
- func (a *Adapter) ListWorkloads(ctx context.Context) ([]controlplane.WorkloadStatus, error)
- func (a *Adapter) Logs(ctx context.Context, app string, opts controlplane.LogOptions) ([]controlplane.LogLine, error)
- func (a *Adapter) MetricsAPIAvailable(ctx context.Context) (bool, error)
- func (a *Adapter) RestartWorkload(ctx context.Context, app string, at time.Time) error
- func (a *Adapter) RunBackupJob(ctx context.Context, app, backupID string) (int64, error)
- func (a *Adapter) RunJob(ctx context.Context, spec controlplane.RunSpec) (controlplane.RunResult, error)
- func (a *Adapter) RunRestoreJob(ctx context.Context, app, backupID string) error
- func (a *Adapter) ScaleWorkload(ctx context.Context, app string, replicas int32) error
- func (a *Adapter) SecretKeys(ctx context.Context, app string) ([]string, error)
- func (a *Adapter) SetSecretValue(ctx context.Context, app, key, value string) error
- func (a *Adapter) Unexpose(ctx context.Context, app string) error
- func (a *Adapter) UnsetSecretKey(ctx context.Context, app, key string) error
- func (a *Adapter) WithAddonNamespace(ns string) *Adapter
- func (a *Adapter) WithNamespace(ns string) controlplane.Kubernetes
- func (a *Adapter) WorkloadStatus(ctx context.Context, app string) (controlplane.WorkloadStatus, error)
- type BuildAdapter
- func (b *BuildAdapter) Build(ctx context.Context, source controlplane.SourceRef, targetImage string, ...) (string, error)
- func (b *BuildAdapter) WithBuildImage(image string) *BuildAdapter
- func (b *BuildAdapter) WithCapacityProber(p controlplane.CapacityProber) *BuildAdapter
- func (b *BuildAdapter) WithGitImage(image string) *BuildAdapter
- type Credentials
- type PostgresProvisioner
- type Prober
Constants ¶
const DefaultCredentialsSecret = "burrow-credentials"
DefaultCredentialsSecret is the one Secret in the control-plane namespace that holds every vendor token, one key per provider (ADR-0023).
const PostgresInitConfigMap = "burrow-postgres-init"
PostgresInitConfigMap is the ConfigMap whose first-boot SQL the official postgres image runs during initdb (mounted into /docker-entrypoint-initdb.d). It creates the pg_stat_statements extension so the metrics exporter's slow-query collector has data (ADR-0051). It lives in the add-on namespace and is removed with the add-on.
const PostgresPasswordKey = "password"
PostgresPasswordKey is the key under which the superuser password is stored in PostgresSecretName.
const PostgresSecretName = "burrow-postgres"
PostgresSecretName is the Secret in the add-on namespace that holds the generated superuser password (ADR-0031). It lives in the add-on namespace — not the control-plane credentials Secret — because a pod can only mount a Secret in its own namespace.
const PostgresSuperuser = "burrow_admin"
PostgresSuperuser is the fixed superuser role burrowd provisions the add-on Postgres instance with and connects as to run admin SQL (ADR-0031). It is deliberately not the built-in "postgres" role: a distinct, Burrow-owned admin role keeps the boundary clear.
Variables ¶
var LeanPostgresSettings = []string{
"shared_buffers=64MB",
"max_connections=30",
"work_mem=4MB",
"maintenance_work_mem=32MB",
"effective_cache_size=256MB",
}
LeanPostgresSettings are the server settings Burrow runs its Postgres instances with — both the control-plane state database (ADR-0012, rendered into cmd/burrow/manifests/install.yaml.tmpl) and the shared Postgres add-on. Burrow's databases are low-traffic control-plane/metadata stores, so the stock postgres defaults (128MB shared_buffers, 100 max_connections, default work_mem) are wildly generous; these lean values let the whole stack (k3s + burrowd + Postgres) fit a 1-2GB VPS with real headroom. The install manifest hard-codes the SAME values as postgres args — keep the two in step.
Functions ¶
func BuilderImageForVersion ¶ added in v0.13.0
BuilderImageForVersion returns the pinned builder image reference for a stamped release version, so a released burrowd pulls the builder image published under the SAME release tag (reproducible) rather than the floating :latest. For an unstamped dev build (version "" or "v0.0.0") it returns "" — the caller then leaves the :latest default (or an explicit BURROW_BUILD_IMAGE override) in place.
func ConfigFromKubeconfig ¶
ConfigFromKubeconfig builds a REST config from an explicit kubeconfig file path. It is used by the integration tests, which point at a disposable cluster rather than the ambient one.
func DetectCapabilities ¶
func DetectCapabilities(ctx context.Context, client kubernetes.Interface) (controlplane.ClusterCapabilities, error)
DetectCapabilities reads a cluster's capabilities read-only over the given clientset (ADR-0034): the ingress controller (a ready ingress-nginx controller Deployment) and its IngressClasses, the default and all StorageClasses, the cloud provider (from node providerIDs/labels), cert-manager and metrics-server (via API-group discovery), and detects LoadBalancer support from whatever actually services LoadBalancers — a recognized cloud provider, k3s's built-in servicelb, or MetalLB (ADR-0043). It performs only get/list reads and API-group discovery — it never writes. It is a free function so the same detection runs whether driven by the kubeconfig client (install) or burrowd's in-cluster client (live). The returned report omits the DNS capability, which is a control-plane registry fact filled by the engine.
func LoadConfig ¶
LoadConfig resolves the cluster connection the way a control plane should: the in-cluster service account when running inside Kubernetes, otherwise the ambient kubeconfig (KUBECONFIG or ~/.kube/config). burrowd uses this.
func ReadResourceState ¶ added in v0.13.0
func ReadResourceState(ctx context.Context, client kubernetes.Interface) (controlplane.ClusterResourceState, error)
ReadResourceState reads node allocatable and pod requests read-only over the given clientset (issue #275): each schedulable node's .status.allocatable CPU/memory, and every non-terminal pod's summed resource requests with the node it is scheduled on. It performs only get/list reads — it never writes — and needs read on nodes (already granted for capability detection) plus a cluster-wide read on pods. It is a free function so the same read runs whether driven by the kubeconfig client or burrowd's in-cluster client. All of this comes from the Kubernetes API alone; no metrics-server is involved (that would add the separate live-usage layer, issue #276).
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter operates Burrow workloads in a single app namespace, and provisions add-ons in a separate add-on namespace (ADR-0025) so backing services don't mix with user workloads.
func New ¶
func New(client kubernetes.Interface, namespace string) *Adapter
New returns an Adapter over the given clientset and namespace (defaulting to "default"). Tests inject a fake clientset; production injects a real one (see NewFromConfig).
func NewFromConfig ¶
NewFromConfig builds an Adapter from a REST config and namespace.
func (*Adapter) AddonReady ¶
AddonReady reports whether the named add-on's backing Deployment is available (ADR-0025). Readiness is a live property of the cluster — the registry of what add-ons exist lives in the database — so this is a cheap single-Deployment probe. A missing Deployment is reported as not ready (false, nil); only a real API error is returned.
func (*Adapter) ApplyAutoscaler ¶ added in v0.8.0
func (a *Adapter) ApplyAutoscaler(ctx context.Context, app string, spec controlplane.AutoscaleSpec) error
ApplyAutoscaler creates or updates an autoscaling/v2 HorizontalPodAutoscaler named after app, targeting app's Deployment, with the requested replica band and utilization targets (ADR-0006). It mirrors ApplyWorkload's create-or-update-under-conflict-retry: the HPA controller continuously writes the object's status, so a get-then-update can lose the resourceVersion race and 409; we re-read and retry on conflict.
func (*Adapter) ApplyWorkload ¶
func (a *Adapter) ApplyWorkload(ctx context.Context, spec controlplane.WorkloadSpec) error
func (*Adapter) AutoscalerActive ¶ added in v0.8.0
AutoscalerActive reports whether app has an active HorizontalPodAutoscaler owning its replica count. It gets the autoscaling/v2 HPA named after app: present means active, NotFound means inactive (false, nil, not an error). A workload apply consults it so it leaves the HPA-managed count untouched.
func (*Adapter) DeleteAutoscaler ¶ added in v0.8.0
DeleteAutoscaler removes app's HorizontalPodAutoscaler. A missing HPA is a no-op, not an error, so turning autoscaling off is idempotent.
func (*Adapter) DeleteWorkload ¶
func (*Adapter) DeployAddon ¶
func (a *Adapter) DeployAddon(ctx context.Context, spec controlplane.AddonSpec) (controlplane.AddonInfo, error)
func (*Adapter) Expose ¶
func (a *Adapter) Expose(ctx context.Context, spec controlplane.ExposeSpec) error
func (*Adapter) ExposureStatus ¶
func (a *Adapter) ExposureStatus(ctx context.Context, app string) (controlplane.ExposureStatus, error)
func (*Adapter) ListWorkloads ¶
func (a *Adapter) ListWorkloads(ctx context.Context) ([]controlplane.WorkloadStatus, error)
func (*Adapter) Logs ¶
func (a *Adapter) Logs(ctx context.Context, app string, opts controlplane.LogOptions) ([]controlplane.LogLine, error)
func (*Adapter) MetricsAPIAvailable ¶ added in v0.8.0
MetricsAPIAvailable reports whether the metrics.k8s.io API group is served, i.e. metrics-server is installed. It reads API-group discovery, which needs no RBAC (ADR-0034). A discovery error is returned so the caller can decide; the engine treats it as "absent" and warns rather than failing, so a probe hiccup never blocks applying an HPA.
func (*Adapter) RestartWorkload ¶
RestartWorkload bumps app's pod-template restarted-at annotation to at, triggering a rolling update so a running app picks up a secret change that envFrom reads only at pod start (ADR-0028). A missing Deployment is ErrNotFound — nothing running to roll.
func (*Adapter) RunBackupJob ¶
RunBackupJob pg_dumps app's database to the backup PVC via a one-shot Job and waits for it to finish (ADR-0032). The dump command names no host or password — those come from libpq env, the password via secretKeyRef. The container also writes the dump's byte size to the termination-log so burrowd can read size_bytes from the pod's terminated state without mounting the volume.
func (*Adapter) RunJob ¶ added in v0.12.0
func (a *Adapter) RunJob(ctx context.Context, spec controlplane.RunSpec) (controlplane.RunResult, error)
RunJob runs spec.Command in a one-shot Job in the app namespace, built from the app's own current image and its config env plus per-app Secret via envFrom (ADR-0048 §2), then waits for it to finish and captures the pod's output and the container's exit code into a RunResult (ADR-0048 §3). A non-zero exit is a normal structured outcome, NOT an error: the error return is reserved for a launch, poll, or timeout failure. The finished Job is garbage-collected by Kubernetes' native ttlSecondsAfterFinished (ADR-0048 §7), set from spec.TTLSeconds — no imperative reap.
func (*Adapter) RunRestoreJob ¶
RunRestoreJob pg_restores app's dump from the backup PVC into its database via a one-shot Job and waits for it (ADR-0032). --clean --if-exists replaces current contents. Like backup, the command names no host or password.
func (*Adapter) ScaleWorkload ¶
func (*Adapter) SecretKeys ¶
SecretKeys returns the env-var names in app's per-app Secret, sorted, never the values (ADR-0028/0004). A missing Secret is an app with no secrets set: empty slice, no error.
func (*Adapter) SetSecretValue ¶
SetSecretValue upserts key=value into app's per-app Secret (controlplane.AppSecretName(app)) in the app namespace, creating the Secret (Opaque, Burrow labels) if absent (ADR-0029). The value arrives here over burrowd's authenticated control-plane API and is written to the Kubernetes Secret; it never reaches a log, the audit log, Postgres, or MCP (ADR-0029/0004). The returned error names the app and key only, never the value. It retries on conflict since a concurrent set/unset can race the resourceVersion.
func (*Adapter) UnsetSecretKey ¶
UnsetSecretKey removes one key from app's per-app Secret (get, delete the key, update). A missing Secret or a missing key is a no-op, not an error. The value never crosses here — the caller passes only the key name (ADR-0004).
func (*Adapter) WithAddonNamespace ¶
WithAddonNamespace sets the namespace Burrow deploys add-ons (and their collectors) into, kept separate from the app namespace and the credential-holding control-plane namespace (ADR-0025). An empty value leaves the default. Returns the Adapter for chaining.
func (*Adapter) WithNamespace ¶ added in v0.7.0
func (a *Adapter) WithNamespace(ns string) controlplane.Kubernetes
WithNamespace returns a copy of the Adapter whose app-resource operations act in ns instead of the configured app namespace — the mechanism that routes an operation to a named environment's namespace (ADR-0035 phase 2). The add-on namespace is unchanged, so add-ons still land in their own namespace. An empty ns, or ns equal to the current app namespace, returns the receiver unchanged, so default-environment behavior is identical to before environments existed. The copy is shallow: it shares the same client, so no new connection is made per operation.
func (*Adapter) WorkloadStatus ¶
func (a *Adapter) WorkloadStatus(ctx context.Context, app string) (controlplane.WorkloadStatus, error)
type BuildAdapter ¶ added in v0.13.0
type BuildAdapter struct {
// contains filtered or unexported fields
}
BuildAdapter is the production controlplane.Builder: it runs an in-cluster build as a Kubernetes Job in the dedicated burrow-builds namespace (issue #278, ADR-0053 §4). It clones the git reference inside the cluster, builds with buildah or Cloud Native Buildpacks, pushes to the target registry reference, and returns the resulting image digest — the immutable identity the resulting guarded deploy pins (ADR-0053 §4). Isolation lives INSIDE this implementation, not on the seam (ADR-0053 §6): the OSS path is single-tenant (§7), so the build runs under restricted PodSecurity as defense in depth, not as an adversary boundary — a hardened, sandboxed executor is the commercial product's job behind the same seam.
It lives under controlplane/ (not controlplane/internal) so cmd/burrowd and the managed module can wire it; it is licensed Apache-2.0.
func NewBuilder ¶ added in v0.13.0
func NewBuilder(client kubernetes.Interface) *BuildAdapter
NewBuilder returns a BuildAdapter over the given clientset. The build always runs in the dedicated burrow-builds namespace (issue #278), isolated from both the app and control-plane namespaces — the caller no longer chooses it. Tests inject a fake clientset; production injects a real one (see NewBuilderFromConfig).
func NewBuilderFromConfig ¶ added in v0.13.0
func NewBuilderFromConfig(cfg *rest.Config) (*BuildAdapter, error)
NewBuilderFromConfig builds a BuildAdapter from a REST config — the production wiring path, mirroring NewFromConfig for the Kubernetes seam.
func (*BuildAdapter) Build ¶ added in v0.13.0
func (b *BuildAdapter) Build(ctx context.Context, source controlplane.SourceRef, targetImage string, insecure bool, cred controlplane.SourceCredential) (string, error)
Build runs the in-cluster build to completion and returns the pushed image's content digest (ADR-0053 §4). Only the git reference and the target reference cross into the builder; the source is cloned inside the cluster, so no code travels over the control channel (ADR-0004, ADR-0053 §3). A clone, build, or push failure is returned as a structured error and nothing is pushed; the caller does NOT touch the deploy path on error (ADR-0053 §4). It blocks until the Job succeeds or fails, or the build timeout elapses.
func (*BuildAdapter) WithBuildImage ¶ added in v0.13.0
func (b *BuildAdapter) WithBuildImage(image string) *BuildAdapter
WithBuildImage overrides the build image (the buildah + Buildpacks bundle). An empty value leaves the default. Returns the adapter for chaining.
func (*BuildAdapter) WithCapacityProber ¶ added in v0.13.0
func (b *BuildAdapter) WithCapacityProber(p controlplane.CapacityProber) *BuildAdapter
WithCapacityProber enables the pre-build scheduling-headroom check (issue #274): before creating a build Job, the adapter reads the cluster's capacity through the prober and refuses with an actionable error when no node has room for the build's request. A nil prober (the default) leaves the check off and the build proceeds. Returns the adapter for chaining.
func (*BuildAdapter) WithGitImage ¶ added in v0.13.0
func (b *BuildAdapter) WithGitImage(image string) *BuildAdapter
WithGitImage overrides the clone init-container image. An empty value leaves the default. Returns the adapter for chaining.
type Credentials ¶
type Credentials struct {
// contains filtered or unexported fields
}
Credentials is the production controlplane.Credentials adapter: it reads and writes vendor tokens in the single burrow-credentials Secret in the control-plane namespace (ADR-0023, ADR-0030). It reads on every call so a rotated token is picked up without a restart, and it writes a token value the engine received over burrowd's authenticated control-plane API. burrowd's Role grants `get` and `update` on exactly this one object, so this is its sole access to a Secret's contents.
func NewCredentials ¶
func NewCredentials(client kubernetes.Interface, namespace, secret string) *Credentials
NewCredentials returns a Credentials reader over the given clientset, control-plane namespace, and Secret name (defaulting to burrow-credentials). Tests inject a fake clientset; production injects a real one (see NewCredentialsFromConfig).
func NewCredentialsFromConfig ¶
func NewCredentialsFromConfig(cfg *rest.Config, namespace, secret string) (*Credentials, error)
NewCredentialsFromConfig builds a Credentials reader from a REST config.
func (*Credentials) SetToken ¶
func (c *Credentials) SetToken(ctx context.Context, key, value string) error
SetToken upserts key=value into burrow-credentials, creating the Secret (Opaque) if absent so the command works against an install that has not yet created it (ADR-0030). The value arrives over burrowd's authenticated control-plane API and is written straight to the Secret; it never reaches a log, Postgres, or an API response — the returned error names the key only, never the value. It retries on conflict since a concurrent set can race the resourceVersion.
type PostgresProvisioner ¶
type PostgresProvisioner struct {
// contains filtered or unexported fields
}
PostgresProvisioner is the production controlplane.DatabaseProvisioner: it connects to the installed Postgres add-on as the burrow_admin superuser and gives each app its own database and login role (ADR-0031). It reads the superuser password from the burrow-postgres Secret in the add-on namespace through a Kubernetes client (a pod can only mount a Secret in its own namespace, so the password lives there), and reaches the instance in-cluster at burrow-postgres.<addon-ns>.svc:5432. It holds no long-lived database handle — it opens a short-lived connection per operation so a rotated superuser password is always picked up.
func NewPostgresProvisioner ¶
func NewPostgresProvisioner(client kubernetes.Interface, addonNamespace string) *PostgresProvisioner
NewPostgresProvisioner returns a provisioner over the given clientset and add-on namespace.
func NewPostgresProvisionerFromConfig ¶
func NewPostgresProvisionerFromConfig(cfg *rest.Config, addonNamespace string) (*PostgresProvisioner, error)
NewPostgresProvisionerFromConfig builds a provisioner from a REST config.
func (*PostgresProvisioner) DropAppDatabase ¶
func (p *PostgresProvisioner) DropAppDatabase(ctx context.Context, app string) error
DropAppDatabase drops app's database and login role from the shared instance (ADR-0031). It validates app and quotes identifiers before any SQL. Dropping an already-absent database or role is a no-op (IF EXISTS), not an error. The database is dropped WITH (FORCE) so live sessions do not block teardown.
func (*PostgresProvisioner) EnsureAppDatabase ¶
EnsureAppDatabase provisions (idempotently) an isolated database and login role for app and returns its DATABASE_URL with a freshly generated password (ADR-0031). It validates app against the strict identifier pattern and quotes every identifier BEFORE any SQL runs. On a fresh attach it CREATEs the role and database and locks the database down to that role; on a re-attach (role or database already present) it ALTERs the role's password to rotate, so the returned URL is always current. The returned connection string is a SECRET value — the caller writes it straight into the app's Secret and never logs, audits, or returns it.
func (*PostgresProvisioner) WithAdminEndpoint ¶
func (p *PostgresProvisioner) WithAdminEndpoint(hostPort string) *PostgresProvisioner
WithAdminEndpoint overrides the host:port the provisioner dials for admin SQL (see adminEndpoint). It is for tests that reach the instance through a port-forward; production leaves it unset.
type Prober ¶
type Prober struct {
// contains filtered or unexported fields
}
Prober is the production controlplane.ClusterProber: it detects a cluster's read-only capabilities over a client-go clientset (ADR-0034). It wraps the same clientset burrowd already holds, so the live read needs only the narrow read-only ClusterRole the install grants (get/list on nodes, storageclasses, ingressclasses, and deployments) plus API-group discovery (no RBAC). It never writes.
func NewProber ¶
func NewProber(client kubernetes.Interface) *Prober
NewProber returns a Prober over the given clientset.
func NewProberFromConfig ¶
NewProberFromConfig builds a Prober from a REST config — burrowd's in-cluster config, so the live capability read uses the narrow read-only ClusterRole the install grants (ADR-0034).
func (*Prober) DetectCapabilities ¶
func (p *Prober) DetectCapabilities(ctx context.Context) (controlplane.ClusterCapabilities, error)
DetectCapabilities reads the cluster's capabilities read-only.
func (*Prober) ReadResourceState ¶ added in v0.13.0
func (p *Prober) ReadResourceState(ctx context.Context) (controlplane.ClusterResourceState, error)
ReadResourceState reads the cluster's scheduling-capacity facts read-only (issue #275).