Kubernetes Controller

A standalone native binary that holds the fabric8 clients, runs the Helm CLI, and streams logs and events. Bosca's only stateful kubernetes-aware runtime.

Why a Separate Service?

The kubernetes-controller is split out of bosca-server because it holds long-lived state that the rest of the platform doesn't need to know about: open fabric8 watch connections, in-flight Helm subprocesses, kubelet log streams, and the decrypted kubeconfigs feeding all of them. Keeping those connections in their own process means bosca-server can restart freely without dropping every cluster watch, and the controller can be scaled horizontally for clusters that need more headroom.

Runtime

  • Bosca Application framework (Netty-based) — the same server framework bosca-server and git-server use. Provides @RouteController + KSP-generated route dispatchers, JWT auth middleware, connection-pooled JDBC, and the standard observability stack.
  • GraalVM native image — compiled via ./gradlew :kubernetes-controller:nativeCompile. The Docker image (artifacts.sowers.bosca.io/bosca-docker/kubernetes-controller) is debian:bookworm-slim with the native binary plus a recent helm binary on PATH.
  • fabric8 kubernetes-client — typed Java client. The controller holds one client per registered cluster in ClusterClientPool (lazily constructed, cached by cluster UUID).
  • Helm CLI subprocess — the official binary, shelled out per install / upgrade / rollback / uninstall / values / manifest call. See Helm Lifecycle for the security model.

Port and Transport

The controller listens on :8082. bosca-server talks to it over the internal Docker network at http://kubernetes-controller:8082 (sync) and ws://kubernetes-controller:8082/stream (long-lived streams). The browser never reaches the controller directly — all kubernetes traffic flows through the studio's /graphql and /graphqlws proxies into bosca-server, which then proxies to the controller.

Override the URL via the KUBERNETES_CONTROLLER_URL environment variable on bosca-server when running outside Docker (e.g. local dev with ./gradlew :server:bosca-server:run).

Authentication

Every controller route requires a JWT (the same JWT_SECRET shared with bosca-server) and re-checks admin group membership. The controller doesn't trust bosca-server's "this is an admin" assertion — bosca-server mints a fresh JWT representing the original caller for every outbound request, and the controller validates that JWT independently. Credential revocation between sessions takes effect on the very next controller call.

Configuration

Configuration is YAML-based, loaded by the Bosca Application framework. The shape mirrors bosca-server's application.yaml and is environment-variable-driven for everything operators tune at deploy time:

Env varDefaultPurpose
DATABASE_URLjdbc:postgresql://localhost:5432/boscaPostgres for cluster credentials.
JWT_SECRET(required)Shared with bosca-server.
SECURITY_ENCRYPTION_KEYchangemeShared with bosca-server — used to decrypt kubeconfigs.
NATS_HOST / NATS_TOKENlocalhost:4222 / (shared)For pub/sub between controller and bosca-server.
PUBSUB_TYPEnatsPub/sub backend (also accepts redis).
REDIS_HOST / REDIS_PORTlocalhost / 6380Cache + fallback pub/sub.
ANALYTICS_SERVER_URLhttp://localhost:8081Analytics ingestion endpoint.
KUBERNETES_CONTROLLER_URLhttp://kubernetes-controller:8082Read on bosca-server, not the controller itself.

Cluster Health Probe

A daemon coroutine (ClusterHealthProbe) iterates over every registered cluster every 30 seconds and pings the API server. Failures bump a per-cluster failure counter and transition the cluster's health badge in the studio. The probe is fail-isolated — a broken cluster doesn't block the probe loop for healthy clusters.

Resource Shape

The controller is small by design: one Postgres table (kubernetes.cluster_credential), one Helm repos table (kubernetes.helm_repo), one fabric8 client per cluster, one in-flight Helm subprocess per concurrent install (timeout 300s), one watch per active subscription multiplexed across studio sessions. No persistent caches beyond what fabric8's informers hold; restarting the controller is safe and fast.