Bring up the local mock of the Hetzner substrate and deploy the bex operator into it.
If $ARGUMENTS is scale N: just run bash scripts/mock-cluster.sh scale N, then show kubectl get nodes (with KUBECONFIG=infra/local/bex.kubeconfig) and stop.
Otherwise, run the full bring-up:
bash scripts/mock-cluster.sh— kind infra cluster + Cluster API + CAPD app cluster, plus the app-cluster baseline the operator needs: Calico, coredns/calico-kube-controllers control-plane pins, local-path default StorageClass, cert-manager (prod's pinned version;make deploy's webhook cert requires it). This can take several minutes; it writes the app-cluster kubeconfig toinfra/local/bex.kubeconfig.- Use that kubeconfig for every following command:
export KUBECONFIG=$PWD/infra/local/bex.kubeconfig(or--kubeconfig). Never print its contents. - Build and load the operator image (CAPD nodes can't pull local-only images):
( cd lego/operator && make docker-build IMG=bex-operator:dev )docker save bex-operator:dev -o /tmp/bex-op.tar- For each node (strip the
node/prefix —kubectl get nodes -o namereturnsnode/<name>butdocker cp/docker execneed the bare container name):for n in $(kubectl get nodes -o name | cut -d/ -f2); do docker cp /tmp/bex-op.tar "$n":/op.tar && docker exec "$n" ctr -n k8s.io images import /op.tar done
( cd lego/operator && make deploy IMG=bex-operator:dev )— deploys to nsbex-systemwithBEX_RUNTIME=kubernetes. Thebex-ssh-gatewayIngressRouteTCP fails without Traefik CRDs — expected and harmless on the mock; everything else applies. Thenkubectl apply -f deploy/gitops/base/operator-apps-rbac.yaml— the apps-namespacebex-operator-appsRole/RoleBinding is Argo-managed in prod and absent from the operator's kustomize config; without it the manager crashloops on "failed to wait for … caches to sync" (its namespace-scoped Secret/Job watches indefaultare forbidden; w1/043).- Local-CAPD-only fixes (re-apply all three after every
make deploy— it reverts them; w1/043):- Label the control-plane node into the platform pool (the manifest's nodeSelector is
bex.co/pool=platform, and the operator must sit on the control-plane node because OrbStack/Calico can't route cross-node pod→apiserver; see docs/ADR004-app-deployment.md):kubectl label node <control-plane-node> bex.co/pool=platform --overwrite - Pin the operator to the control-plane node:
kubectl -n bex-system patch deploy bex-controller-manager --type merge -p \ '{"spec":{"template":{"spec":{"nodeSelector":{"node-role.kubernetes.io/control-plane":""}, "tolerations":[{"key":"node-role.kubernetes.io/control-plane","effect":"NoSchedule"}]}}}}' - The manifest sets
imagePullPolicy: Always, which can never be satisfied by the ctr-imported local image (ErrImagePull) — override it:kubectl -n bex-system patch deploy bex-controller-manager --type strategic -p \ '{"spec":{"template":{"spec":{"containers":[{"name":"manager","imagePullPolicy":"IfNotPresent"}]}}}}'
- Label the control-plane node into the platform pool (the manifest's nodeSelector is
kubectl -n bex-system rollout status deploy/bex-controller-manager— wait for Ready.
Finish by reporting: node list, operator pod status, and the one-liners to deploy an App (/deploy-app) and to scale machines (/mock-cluster scale N).