Minimal controller that keeps a managed EndpointSlice in sync with the current private worker IPs of a remote Kubernetes cluster.
The project exists to keep a selector-less Service stable even when the upstream cluster rotates or replaces nodes.
It periodically:
- discovers eligible worker nodes in a remote cluster
- resolves their private IPs via IONOS NIC data
- health-checks candidate backends
- updates a managed EndpointSlice in the local cluster with minimal churn
- core reconciliation logic
- source abstraction for secured node IP discovery
- store abstraction for EndpointSlice reads and writes
- fail-open protection for empty source results
kubectl-backed EndpointSlice persistence- JSON inventory file source
- command-backed inventory source
- unit tests for update semantics
- direct Kubernetes client integration
- direct IONOS API integration
- metrics and Kubernetes Events
- leader election
go test ./...The binary reads configuration from environment variables:
SOURCE_POLL_INTERVALTARGET_NAMESPACETARGET_SERVICE_NAMETARGET_ENDPOINTSLICE_NAMETARGET_MANAGED_BYTARGET_PORTSECURED_SUBNET_CIDREMPTY_RESULT_THRESHOLDSOURCE_KIND(static,file, orcommand)STORE_KIND(memoryorkubectl)INVENTORY_FILE_PATHINVENTORY_COMMANDINVENTORY_COMMAND_ARGSHEALTH_CHECK_MODE(off,tcp, orhttp)HEALTH_CHECK_PATHHEALTH_CHECK_TIMEOUT
SOURCE_KIND=file reads a JSON inventory with records like:
[
{
"name": "target-worker-1",
"private_ip": "10.0.0.12",
"ready": true,
"worker": true
}
]STORE_KIND=kubectl persists the managed EndpointSlice by shelling out to
kubectl get and kubectl apply. This is a practical integration path for
early rollout without pulling in client-go.
SOURCE_KIND=command runs an external command and expects the same JSON array
on stdout. This is the cleanest current bridge to an IONOS inventory script.
The controller can probe candidate backend IPs before writing them into the
managed EndpointSlice.
HEALTH_CHECK_MODE=offdisables probingHEALTH_CHECK_MODE=tcprequires a successful TCP connect toTARGET_PORTHEALTH_CHECK_MODE=httprequires an HTTP success response onTARGET_PORT
For the current http-echo backend, the recommended setting is:
SOURCE_POLL_INTERVAL=5s
HEALTH_CHECK_MODE=http
HEALTH_CHECK_PATH=/
HEALTH_CHECK_TIMEOUT=3sdeploy/ contains cluster manifests only:
service.example.yamlrbac.example.yamlcontroller-config.example.yamlcontroller-deployment.example.yamlscripts-configmap.ionos.example.yamlinventory.example.jsonionos-token-secret.example.yamlkubeconfigs-secret.example.yaml
helm/ contains a Helm chart for production-style installation:
helm/ionos-endpointslice-controller
hack/ contains local and operational helper scripts:
build-and-push.shset-image.shapply-order.shrestart-controller.shlogs-controller.shrun-maintenance-curl.shrecreate-ionos-node.shverify-endpointslice.shrun-local-inventory.shcheck-secured-nodes.sh
docs/ contains runbooks and checklists:
first-run-checklist.md
Copy the example and load it with direnv:
cp .envrc.example .envrc
direnv allowThen validate the secured cluster view first:
./hack/check-secured-nodes.shThen run the inventory helper locally:
./hack/run-local-inventory.shThe expected result is a JSON array with only Ready worker nodes and
private IPs in the configured private subnet.
The repository now includes scripts/render_inventory.py.
It is intended to run behind SOURCE_KIND=command and emits the JSON inventory
format expected by the controller.
Current behavior:
- reads
Readyworker nodes fromtarget-k8susingkubectl - maps each node to an IONOS server using node name,
providerID, orsystemUUID - resolves the private NIC IP from the configured IONOS LAN
- emits only IPs matching the configured subnet prefix
- writes EndpointSlices in the target cluster using in-cluster service account access
Required environment:
IONOS_TOKENIONOS_DATACENTER_IDIONOS_PRIVATE_LAN_IDSECURED_KUBECONFIG
Optional environment:
SECURED_KUBECTL_CONTEXTSECURED_SUBNET_PREFIXdefault:10.0.0.
The intended runtime model is:
target-k8sread access via mounted kubeconfig and optionalSECURED_KUBECTL_CONTEXTsource-k8swrite access via in-cluster service account and RBAC
The controller does not require a separate target-cluster kubeconfig in the recommended deployment shape.
The repository includes Dockerfile. The runtime image contains:
- the controller binary
kubectlpython3
This matches the current pragmatic architecture where:
- inventory generation uses Python
- cluster read/write integration uses
kubectl
Build the image:
./hack/build-and-push.sh 0.0.1This helper uses:
docker buildx build--platform linux/amd64- explicit tags such as
0.0.1,0.0.2
Apply the baseline manifests:
./hack/set-image.sh 0.0.1
vim deploy/controller-config.example.yaml
./hack/apply-order.shInstall via Helm instead:
helm upgrade --install ionos-endpointslice-controller ./helm/ionos-endpointslice-controller \
--namespace upstream-system \
--create-namespace \
--set image.tag=0.0.1 \
--set controller.ionosDatacenterId=REPLACE_ME \
--set controller.ionosPrivateLanId=REPLACE_MERestart the controller after config or image changes:
./hack/restart-controller.shFollow controller logs:
./hack/logs-controller.shInspect the resulting managed EndpointSlice:
./hack/verify-endpointslice.sh