Architecture¶
Overview¶
bootchain-operator is a Kubernetes operator built with Kubebuilder and controller-runtime. It consists of three components that work together:
┌─────────────────────────────────────────────────────────┐
│ Kubernetes API Server │
└──────────┬──────────────────────┬───────────────────────┘
│ watch │ admission review
▼ ▼
┌─────────────────┐ ┌─────────────────────────────────┐
│ Controller │ │ Webhook Server │
│ │ │ │
│ Reconcile loop │ │ MutatingWebhook (Deployment) │
│ TCP/HTTP check │ │ ValidatingWebhook (BootDep) │
│ Status update │ │ │
└─────────────────┘ └─────────────────────────────────┘
Components¶
Controller (internal/controller)¶
The BootDependencyReconciler runs a reconciliation loop that:
- Fetches the
BootDependencyresource - Probes each declared dependency (3-second timeout per check):
- If
httpPathis set: performs an HTTP(S) request to{httpScheme}://{target}:{port}{httpPath}. The method defaults toGET(override withhttpMethod). Custom headers can be injected viahttpHeaders. The accepted status codes default to any2xx; override withhttpExpectedStatuses. Wheninsecure: true, TLS certificate verification is skipped - Otherwise: TCP-dials the address —
serviceentries resolve as{service}.{namespace}.svc.cluster.local:{port},hostentries are dialled as{host}:{port} - Updates
status.resolvedDependencies(e.g."2/3") and theReadycondition - Emits Kubernetes events for reachable/unreachable dependencies
- Records Prometheus metrics
- Requeues after 30s if all ready, 10s if not
Mutating Webhook (internal/webhook/v1)¶
The DeploymentCustomDefaulter fires on CREATE and UPDATE of any apps/v1 Deployment:
- Looks up a
BootDependencywith the samenameandnamespaceas the Deployment - If found, prepends a
wait-for-{target}init container for eachspec.dependsOnentry - The init container target is the
servicename (cluster DNS) orhostvalue (used directly) - Injection is idempotent — existing init containers with the same name are skipped
The init containers use the ghcr.io/user-cube/bootchain-operator/minimal-tools image — a custom minimal image that bundles netcat, wget, and curl. The polling command depends on whether httpPath is set and which advanced fields are in use:
- TCP check (default):
timeout {timeout} sh -c 'until nc -z {target} {port}; do sleep 1; done' - HTTP/HTTPS check (basic —
httpPathset, no advanced fields): useswget --spider. Withinsecure: true, adds--no-check-certificate - Advanced HTTP/HTTPS check (
httpMethod,httpHeaders, orhttpExpectedStatusesset): switches tocurl, which supports custom methods (-X), headers (--header), and status code extraction (-w '%{http_code}'). Withinsecure: true, adds-k
Validating Webhook (internal/webhook/v1alpha1)¶
The BootDependencyCustomValidator fires on CREATE and UPDATE of any BootDependency:
- Validates that each
spec.dependsOnentry specifies exactly one ofserviceorhost - Builds a directed dependency graph from all
BootDependencyresources in the namespace (serviceentries only —hostentries are external leaf nodes and cannot form aBootDependencycycle) - Adds the incoming resource to the graph
- Runs a depth-first search (DFS) from the incoming resource's name
- Rejects the request if a back-edge (cycle) is detected, including the full cycle path in the error message
Init container image: minimal-tools¶
The init containers injected by the mutating webhook use a custom image — ghcr.io/user-cube/bootchain-operator/minimal-tools — instead of a generic busybox. This image is purpose-built to include exactly the tools needed for health probing:
| Tool | Used for |
|---|---|
netcat (nc) |
TCP connection checks (default probe) |
wget |
HTTP and HTTPS health checks (httpPath) |
curl |
Available for manual debugging inside init containers |
Using a dedicated image rather than a large general-purpose one keeps the image footprint small while providing all the probing primitives the operator needs. The image is versioned and published to GitHub Container Registry alongside the operator.
TLS and cert-manager¶
The webhook server requires TLS. In production (Helm install), cert-manager automatically provisions a self-signed Certificate and injects the CA bundle into both MutatingWebhookConfiguration and ValidatingWebhookConfiguration via the cert-manager.io/inject-ca-from annotation.
cert-manager Issuer (self-signed)
│
└─► Certificate → Secret (bootchain-operator-webhook-tls)
│
Deployment (volume mount)
│
Webhook Server (:9443)
Data flow: init container injection¶
kubectl apply -f deployment.yaml
│
▼
API Server → MutatingWebhookConfiguration
│
▼
bootchain-operator webhook handler
│
├─ GET BootDependency (same name, same namespace)
│ │
│ found? ──yes──► inject wait-for-* init containers
│ │
│ not found? ──► pass through unchanged
│
▼
API Server stores Deployment with injected init containers
Data flow: cycle detection¶
kubectl apply -f bootdependency.yaml
│
▼
API Server → ValidatingWebhookConfiguration
│
▼
bootchain-operator webhook handler
│
├─ LIST all BootDependencies in namespace
├─ Build directed graph (name → [dependsOn services])
├─ Add incoming resource to graph
├─ DFS from incoming resource's name
│
├─ cycle found? ──► 403 Forbidden with cycle path
└─ no cycle? ──────► 200 Allowed