Platform operator
The platform operator is a Go operator that turns a namespaced PlatformInstance (platform.di-framework.dev/v1alpha1) into a running, isolated copy of the di-framework platform. v1alpha1 has one target: a vCluster on the operator's own cluster. Each install is the platform's existing-cluster program (@di-framework/platform/existing), run through the Pulumi Automation API, at the exact @di-framework/platform version the instance names.
The operator, its Helm chart and its Headlamp plugin publish from di-framework/platform and are released together with the platform. This page describes release 6.0.22.
Page | Covers |
|---|---|
This page | Host requirements, installing the chart, Headlamp and its plugin, a first instance, uninstalling. |
Every | |
Upgrades, repair, |
Host requirements
The documented host is a single-node KubeSolo cluster. The operator embeds the vCluster chart (0.37.3, digest-checked) with values tested on KubeSolo, and its default storageRootBase is /var/lib/kubesolo/instances. The admission policy that protects each instance was verified on KubeSolo v1.2.0 (Kubernetes v1.35.7). v1alpha1 is single node only; multi-node is out of scope.
The operator needs:
cluster-admin. The vCluster chart creates RBAC, workloads and Services in each instance's host namespace, and Kubernetes only lets Helm grant permissions it already holds. The chart binds
cluster-adminby default (rbac.clusterAdmin: true). Install it only on a cluster where a cluster-admin operator is acceptable; instance owners are trusted in v1alpha1.A host NetworkPolicy engine. Instances run with
networkPolicyEngine: existing, so the host cluster must enforce NetworkPolicy.Kubernetes 1.30 or later for the
ValidatingAdmissionPolicythat protects each instance'sservice/vcluster. On an API server without admission policies the operator carries on and reportsOwnerServiceProtected=False.Egress to npm, the Pulumi plugin host and the platform's images. Each install runs
npm installand fetches the Pulumi Kubernetes provider plugin; the image doesn't carry them yet, so every instance's first install downloads both.A namespace that admits
hostPathpods for the storage cleanup Job that runs when an instance withdeletionPolicy: Deleteis deleted. It is the release namespace unlesscleanupNamespacenames another.
Install the operator
The chart installs once per cluster: the PlatformInstance CRD is cluster-scoped and its ClusterRole is always named platform-operator. Every v* platform release publishes the chart at the release's version.
Allow privileged pods in the namespace the cleanup Jobs run in (the release namespace by default), so Pod Security admission admits their hostPath mount:
The chart installs the CRD, the operator Deployment and its RBAC, the operator's Pulumi state claim and its passphrase Secret. The image is always pulled by digest (image.digest). The vCluster chart version, its values and the platform version are not chart values: they come with the operator image, and each PlatformInstance names its own spec.version.
The passphrase and offline renders
The passphrase Secret encrypts the secrets in the operator's Pulumi state. If it ever changes, every existing instance's state becomes unreadable at the operator's next restart, permanently.
With
passphrase.existingSecretempty (the default) the chart reads the cluster withlookup. It reuses an existing passphrase and generates one only on a first install.helm install,helm upgradeand--dry-run=serverwork. A render that cannot see the cluster fails instead of generating a new passphrase.helm template, client-side--dry-run, Argo CD and other GitOps tools needpassphrase.existingSecret: create the Secret (keypassphrase) yourself and name it.kubectl -n platform-operator create secret generic platform-operator-passphrase \ --from-literal=passphrase="$(openssl rand -base64 36)" helm template platform-operator <chart> -n platform-operator \ --set passphrase.existingSecret=platform-operator-passphrasestate.existingClaimrequirespassphrase.existingSecret: the chart can't know the passphrase of state in a claim it didn't create.
Chart values
Value | Default | Operator flag |
|---|---|---|
|
| |
| the operator image published with the release; must match | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| empty: the release namespace |
|
|
| any other flag, such as |
|
|
|
| none; requires |
|
| none (generated; offline renders fail) |
|
|
| |
| 100m CPU / 256Mi memory / 1Gi ephemeral requests; 2Gi memory / 6Gi ephemeral limits | |
|
| |
|
|
The state claim is ReadWriteOnce. On a multi-node cluster a standby replica on another node stays in ContainerCreating, so keep one replica or use a ReadWriteMany class. The operator pod runs as uid 1000 with a read-only root filesystem.
Headlamp and the PlatformInstance plugin
Headlamp is the operator's optional UI. Turn it on in the same chart:
headlamp.enabled=true installs the upstream Headlamp chart 0.46.0, vendored in the operator chart. Everything under headlamp is passed to that chart, so its service, ingress and authentication values apply as documented upstream. Upstream binds Headlamp's own ServiceAccount to cluster-admin; users still sign in with their own token unless headlamp.config.unsafeUseServiceAccountToken is set.
Headlamp's plugin manager sidecar (headlamp.pluginsManager, on by default) installs the di-framework Platform Instances plugin from its Artifact Hub package at the plugin version, which is the platform release version:
pluginctl downloads the tarball attached to the platform's GitHub release and checks its sha256. The sidecar needs egress to npm, artifacthub.io and github.com. Keep headlamp.config.watchPlugins at true (the chart's setting): the sidecar writes the plugin after Headlamp has started, and without the watch Headlamp never serves it. The plugin requires Headlamp 0.46.0 or later.
The plugin is a client of the Kubernetes API only; anything it does, kubectl apply of the same YAML does too. It adds:
Platform Instances in the sidebar: name, namespace, phase, version (with the installed version while an upgrade is in progress), Ready and age.
Create and Edit spec forms, with sections for tenants, users, tenant auth and tenant hosts. The forms check what the CRD would before sending. Show YAML shows the exact object that will be sent. Edit sends a JSON Patch of only the changed fields (Show patch shows it) and refuses to overwrite a concurrent change: reopen the form and retry.
Detail view: spec and status, what the last install applied, endpoints, conditions with reasons and messages, recent Events, and the instance kubeconfig. The kubeconfig Secret is read only when you click Show or Download, with your own credentials, so RBAC on the Secret decides.
Create an instance
kubectl get pi shows the requested and installed versions and the phase. The phase moves through Provisioning (the vCluster is coming up) and Installing to Ready. The instance's admin kubeconfig is in the Secret named by status.kubeconfigSecretRef (<name>-kubeconfig, key kubeconfig), in the instance's namespace.
The kubeconfig's server is the in-cluster address https://vcluster.<host namespace>.svc:443, and the instance's host namespace admits only the operator's namespace to that API. From anywhere else, use kubectl port-forward to svc/vcluster in the host namespace (pi-<namespace>-<name>-<hash>, annotated platform.di-framework.dev/instance: <namespace>/<name>) and point the kubeconfig at the local port; the proxy certificate names 127.0.0.1 and localhost.
Tenants, users and tenant auth
spec.platform also carries the instance's tenants, users, tenantAuth and tenant host image, so an instance comes up with a working console and /v1 API. The OAuth client secret is never inline: clientSecretRef names a Secret and key in the instance's namespace.
Anyone who may create or edit a PlatformInstance in a namespace can have the operator read any Secret in that namespace. Grant PlatformInstance write access only to those who may read that namespace's Secrets. Field-by-field limits are in the reference.
Uninstall
Delete the instances you no longer want before uninstalling the operator. Every PlatformInstance carries the platform.di-framework.dev/instance finalizer, which only the operator removes; while the operator is uninstalled, deleting an instance (or the CRD) hangs until it is installed again.
helm uninstall removes the Deployment, the ServiceAccount and the RBAC. It keeps the PlatformInstance CRD (and so every instance), the state claim platform-operator-state and the passphrase Secret platform-operator-passphrase. Installing again with the same release name and namespace adopts all three. The operator, not the chart, creates the admission policy platform-operator-protect-vcluster-service and its binding; remove them by hand once no instance is left:
Next steps
Platform for building and deploying applications to an instance's tenants