Repository navigation
kind: run the Direct substrate on the Kind cluster (SPACE_KIND_SUBSTRATE=direct) - #1168
Conversation
|
Thanks for opening this pull request! A SPACE maintainer will take a look. CONTRIBUTING.md explains how we review outside contributions. |
2c5cceb to
65637c2
Compare
Nothing exercised the Direct substrate end to end. Add a Direct lane to
the Kind harness: the same cluster and images, with the API and cluster
controller on the Direct substrate instead of Kubernetes.
Selection and refusals. SPACE_KIND_SUBSTRATE=kubernetes|direct
(infra/space/kind/lib/substrate.sh; unset means kubernetes) is read by
the Tiltfile, up.sh and the bootstrap library. The deployed substrate is
read from the API Deployment's SPACE_K8S_CONTROL_PLANE ("true" is
Kubernetes, "false" Direct, anything else fails), with kubectl's stderr
kept out of the value. A cluster cannot switch substrate, since its
database holds that substrate's node and sandbox rows: the bring-up,
up.sh and prepare-cluster.sh refuse an existing cluster on the other
one, the bring-up under SKIP_BOOTSTRAP=1 too. An invalid value, or
direct without SPACE_KIND_STORAGE_CREDENTIALS=static, is refused before
a running Tilt is stopped or a cluster created. The lane needs static
credentials because the credential-manager authority accepts a node
service only through its Kubernetes node registration, which a Direct
node lacks.
Charts. space-api-local and space-cluster-controller-local take
substrate: kubernetes|direct. On direct the controller runs one replica
and the chart refuses any other count, as leader election currently
follows the substrate. The node-service charts take node.substrate; on
direct they leave SPACE_NODE_ID unset; node-api and node-ctl read the
UUID in /space/etc/node-id instead, as on a Direct host, and
sandbox-manager reads it for its activation identity only. Default renders keep every field. The space-cluster
umbrella, whose subcharts these are, refuses any substrate other than
kubernetes: the lane is Kind-only.
Registration. register-direct-nodes.sh seeds each sandbox and builder
node's id file before the node services start, failing if it cannot
list the nodes, then registers each node through
POST /admin/api/v1/nodes with verify: true at its InternalIP. Both steps
are parallel Tilt resources on the Direct lane only.
wait-node-publication.sh matches a Direct registration by name.
Smoke runner. Scenarios get a substrates attribute derived from their
fixture: Ecw, CommandSandboxFixture and NativeHardeningFixture serve
both, KindEcw and KindNativeHardeningFixture Kubernetes only. The two
fork scenarios and the pod-asserting encrypted double-restore scenario
narrow to Kubernetes. On Kind the suite reads the bench's substrate
right after its bootstrap, before the other bench-mode filters, through
a bench reader built whatever the first scenario's fixture (it also
serves the telemetry, storage and Rust-build reads), and prints each
scenario it skips. A SPACE_SMOKE_SUBSTRATE or SPACE_KIND_SUBSTRATE that
names the substrate the bench does not run, or names one when the bench
cannot be read, fails the run before anything runs, a shard included.
On the deployed backend SPACE_SMOKE_SUBSTRATE is the only selector.
Not covered. No CI job runs the lane. Known Direct failures remain
(idle wake and delete of idle-suspended sandboxes, a cleanup check that
finds the worker through the sandbox pod). Storage-credential exchange,
volume keys, network credentials and private template reads refuse
Direct nodes; sandbox-manager's restore placement and quarantine
recovery read only SPACE_NODE_ID; node services still get
SPACE_K8S_NODE_NAME. No production code changes.
65637c2 to
f5c7ecf
Compare
Review — 2 findingsLow
Checked and found sound:
Verified against
Not run: the Direct lane or any Kind smoke. |
the-gigi-pplx
left a comment
There was a problem hiding this comment.
Approved. 2 non-blocking findings in the review comment above.
|
One follow-up on the README for the Direct lane.
|
Summary
Adds a Direct-substrate lane to the Kind harness: the same cluster and images, with the API and cluster controller on the Direct substrate (
crates/libs/api/src/controllers/substrate/direct.rs) instead of Kubernetes. Until now nothing exercised Direct end to end.SPACE_KIND_SUBSTRATE=kubernetes|direct(infra/space/kind/lib/substrate.sh) selects it. The Tiltfile,up.shandscripts/tests/lib/bootstrap-kind-stack.shread it, and the typed smoke runner reads the bench's substrate the same way.up.shandprepare-cluster.sh(CI's prepare step) refuse one running the other substrate, the bring-up underSKIP_BOOTSTRAP=1too.directwithout static storage credentials, before a running Tilt is stopped or a cluster created.SPACE_K8S_CONTROL_PLANE:trueis Kubernetes,falseis Direct, and any other value fails. kubectl's stderr is kept out of the value.space-api-localandspace-cluster-controller-localtakesubstrate: kubernetes|direct.directthe controller runs one replica, and the chart refuses any other count. Leader election currently follows the substrate and doesn't contend for the Lease there.values-direct.yaml'sreplicaCount: 1.node.substrate. Ondirectthey leaveSPACE_NODE_IDunset, because ClusterApi resolves a kube node name only to a Kubernetes registration./space/etc/node-id(mount.nodeId), as on a Direct host./space/etcmount, for its activation identity only (see Not covered).infra/space/kind/scripts/register-direct-nodes.shseeds that UUID before the node services start, and fails if it can't list the nodes.POST /admin/api/v1/nodeswithverify: true. The address is the node's InternalIP, which ClusterApi checks its calls against.SPACE_KIND_STORAGE_CREDENTIALS=static. Inexchangemode the credential-manager authority accepts a node service only through its Kubernetes node registration (node_k8s). A Direct node has none, so every template pull is refused.wait-node-publication.shmatches a Direct registration by its name.substratesattribute, derived from the fixture type.Ecw,CommandSandboxFixture,NativeHardeningFixture) serve both substrates.KindEcwandKindNativeHardeningFixtureserve Kubernetes only.SPACE_SMOKE_SUBSTRATEorSPACE_KIND_SUBSTRATEnames the substrate the bench does not run, or names one when the bench can't be read. That holds for a shard the other filters would empty too.SPACE_SMOKE_SUBSTRATEis the explicit selector; unset, it filters nothing.Unsupported), andnative.encrypted_double_restore_retires_prior_pods, which asserts sandbox pods.space-clusterumbrella (infra/space/cluster), whose subcharts are these charts, refuses anysubstrateornode.substrateother thankubernetes: the Direct substrate lane is Kind-only.No production code changes. No CI job is added.
Validation
On this head's code (a final amend changed only an
up.shcomment and one README sentence):bash ci/scripts/run-shell-tests.sh: all 41 tests passed (exit 0).PATH:cargowas the host's.mksquashfsandunsquashfsran from an Alpine container with-no-xattrs, withTMPDIRunder the home directory, because the Docker daemon doesn't share the host's/tmp.gpgvwas mapped togpg --verify.test-kind-up-restart.shhas new cases. A reused cluster whose API runs Direct is refused before Tilt is stopped.Direct) anddirectwithexchangecredentials are refused before the bring-up. The misspelt case failed before the fix: the run went on to the bring-up.vendor-copy-matchesbefore the stderr fix: the Kubernetes cluster was refused as Direct.uv run --frozen --python 3.11 --group test python -m pytest space_smoke/tests scripts/tests/test_kind_*.py scripts/tests/test_cluster_chart_validate.py: 1076 passed, 1 skipped.BenchSubstrateTestscover these cases:SPACE_SMOKE_SUBSTRATE.SPACE_SMOKE_SUBSTRATE. Before the fix it exited 0._apply_substratedidn't exist.kind_stack_prepare_cluster, in a disposable stubbed reproduction (no shell test covers it): before the fixDirect, anddirectwithexchange, created a cluster, and an existing cluster on the other substrate passed. After it, all three are refused and matching requests pass.shellcheck -x) on every changed script (lib/substrate.sh,register-direct-nodes.sh,up.sh,wait-node-publication.sh, the bootstrap library and the three tests): no new findings.source), for thesubstrate.shline, like its eight siblingsourcelines.helm template(helm v3), compared against agit archiveof the merge base:space, withvalues.example.yaml;space-builder, withchart/values-builder.yamlthenvalues.example.yaml;--kube-version 1.31.0;space-api-localwithvalues-kind.yaml,space-cluster-controller-local, and every node-service release, including the builder ones.spacerenders.space-api-local.substrate=direct,space-node-api-local.node.substrate=direct,space-node-ctl-local.node.substrate=directand (onspace-builder)space-build-manager-local.node.substrate=direct. The message is "the Direct substrate lane is Kind-only; see infra/space/kind/README.md".space-cluster-controller-local.substrate=directfails on the controller's replica guard, whose message now says the same. WithreplicaCount=1it fails on the umbrella's check.substrate=directrenderSPACE_K8S_CONTROL_PLANE: "false", dropSPACE_NODE_ID, add the node-id mounts and run one controller replica.REGISTRY.select, quarantined and ignored excluded):kubernetes(89 ECW, 35 native), unchanged.direct: 70 (49 ECW, 27 native). This was 71 before the pod-asserting scenario narrowed to Kubernetes.SPACE_SMOKE_SUBSTRATE=direct.kubernetes. Every eligible scenario runs once.prepare-cluster.sh(not rerun since), on a new cluster:SPACE_KIND_SUBSTRATE=direct SPACE_KIND_STORAGE_CREDENTIALS=static bash infra/space/kind/scripts/up.sh: exit 0.seed-direct-node-idsseeded all four sandbox and builder nodes.register-direct-nodesregistered each node on its first attempt.SPACE_K8S_CONTROL_PLANE=falsewith one controller replica.bash infra/space/kind/lib/substrate.sh checkwith the default request refused the cluster and printed the remedy.python3 examples/first_sandbox.pyprintedHello from SPACE, and no pod was created in thesandboxesnamespace. The script then stopped at its interactive delete prompt, andfirst_sandbox.py deleteremoved the sandbox.SKIP_BOOTSTRAP=1 SPACE_KIND_SUBSTRATE=direct SPACE_KIND_STORAGE_CREDENTIALS=static bash scripts/tests/smoke-kind-typed.sh all, withSPACE_SMOKE_SUBSTRATEunset:SPACE_SMOKE_SUBSTRATE=kubernetesexited 2 before running any scenario:SPACE_SMOKE_SUBSTRATE=kubernetes, but the bench runs the direct substrate.--category native_contractrun), the default lane's bring-up and typed suite, andcargo arch. The identical renders, the unchanged inventory, and the shell and Python tests above cover the default lane.Severity and risk
Severity: does not apply (test harness only).
Risk: Kind, smoke-harness and chart paths only.
space-clusterumbrella's subcharts. Its default renders keep every field (checked above), and it refuses the Direct values.To roll back, revert the commits.
Not covered
Direct failures from the typed suite, none of them fixed here:
ecw.access_idle_reconnect_preserves_workspacefails on the idle wake (api: wake and delete a Direct sandbox the node suspended for idleness #1169).ecw.pause_timeout_requires_connectshows the same symptom ("explicit connect did not resume the paused sandbox"). It has been reported as intermittent, and it failed in both Direct runs so far.native_contract.*idle-wake scenarios fail on the idle wake (api: wake and delete a Direct sandbox the node suspended for idleness #1169).native.stopped_restart_preserves_rootfslooks up the worker for its cleanup check through the sandbox pod, which Direct doesn't have ("cannot locate the fixture sandbox worker"). This is a harness gap; its rootfs check applies to Direct.native.encrypted_double_restore_retires_prior_podswas inconclusive: the volume key was refused for lack ofnode_k8s.Known gaps:
static.crates/libs/sbx/mgr/server/src/service/start.rs) and quarantine recovery (crates/libs/sbx/mgr/server/src/snapshot_restore.rs) read onlySPACE_NODE_ID, which is unset on this lane ([sbx-mgr] read the node id from SPACE_NODE_ID, then the node-id file, everywhere #1160). Neither explains a failure listed above.SPACE_K8S_NODE_NAME. So node-ctl starts its Kubernetes node-local controller (crates/libs/node/ctl/server/src/kubernetes_controller/mod.rs), and node-manager sees a Kubernetes signal for ingress isolation. The lane is Direct for node identity and placement, not for every node-service behaviour.registerchecks only a node's id, so it keeps a stale address after a Kind node's IP changes. It retries 403, 409 and 422 until its deadline.SPACE_KIND_SUBSTRATE=direct SPACE_KIND_STORAGE_CREDENTIALS=static.quarantined=withholds a scenario on every substrate, the required Kubernetes lane included.AI assistance: Claude Code wrote the change and its tests, ran the validation and wrote this description.