Skip to content

Commit 197b413

Browse files
authored
fix(dev): harden local cluster and gateway startup (#2993)
* fix(helm): refresh kubeconfig for existing k3d clusters Docker can recreate the k3d load balancer on a new API port. Start existing clusters and prefer fresh k3d entries so create does not retain a stale endpoint. Signed-off-by: Kris Hicks <khicks@nvidia.com> * fix(dev): conditionally enable local OTLP export Probe port 4317 before adding OTLP configuration for the VM, Docker, and Podman gateway tasks. Document the startup behavior and troubleshooting for local collector availability. Signed-off-by: Kris Hicks <khicks@nvidia.com> --------- Signed-off-by: Kris Hicks <khicks@nvidia.com>
1 parent f68867b commit 197b413

9 files changed

Lines changed: 73 additions & 14 deletions

File tree

.agents/skills/debug-openshell-cluster/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ openshell logs <sandbox-name>
600600
| Symptom | Likely cause | Check |
601601
|---|---|---|
602602
| `openshell status` fails | Gateway endpoint unreachable or auth mismatch | `openshell gateway info`, gateway logs |
603+
| `BatchSpanProcessor.ExportError` repeatedly reports connection refused on `127.0.0.1:4317` | The local gateway started with OTLP configured but the collector forwarding task later stopped, or the config was created manually | Restart `gateway:docker`, `gateway:podman`, or `gateway:vm` so it re-detects the listener; inspect the generated `gateway.toml` for `[openshell.gateway.otlp]` |
603604
| Gateway starts but sandbox create fails | Compute driver cannot reach runtime | Docker/Podman/Kubernetes/VM driver logs |
604605
| Gateway exits while resolving compute-driver listener requirements | Callback alias topology is unsupported, the Podman network cannot be inspected, or the selected address is not private/authorized | Gateway startup error, `podman info --debug`, Podman network inspection, host IPv4 default route |
605606
| Admin, health, reflection, or HTTP request is denied on an additional Docker/Podman callback-only listener | Additional callback listeners intentionally expose only sandbox-callable gRPC methods | Retry through the gateway's primary endpoint; inspect the listener-purpose startup log if the address was unexpected |

.agents/skills/helm-dev-environment/SKILL.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ mise run helm:k3s:create
2626
```
2727

2828
Creates a k3d cluster and merges its kubeconfig into the worktree-local `kubeconfig` file.
29+
When the named cluster already exists, the task starts any stopped containers and refreshes
30+
same-named kubeconfig entries so a recreated load balancer's current API port takes effect.
2931
Also applies the upstream agent-sandbox CRDs/controller (pinned via `AGENT_SANDBOX_VERSION`
3032
in `tasks/scripts/helm-k3s-local.sh`, fetched from `github.com/kubernetes-sigs/agent-sandbox`
3133
releases), enables its OTLP tracing on v0.5 and later, installs an OTLP trace
@@ -118,8 +120,9 @@ Kubernetes compute-driver spans under their distinct service names, along with
118120
Agent Sandbox controller reconciliation spans linked through the Sandbox
119121
trace-context annotation. The same command exposes OTLP/gRPC on
120122
`http://127.0.0.1:4317` and, when deployed, the Kubernetes gateway on
121-
`http://127.0.0.1:8090`; the local `gateway`, `gateway:docker`, and `gateway:vm`
122-
tasks export to the collector endpoint automatically.
123+
`http://127.0.0.1:8090`. The local `gateway:docker`, `gateway:podman`, and
124+
`gateway:vm` tasks detect the collector listener at startup and enable trace
125+
export only while it is reachable.
123126

124127
**HA test deploy** (two gateway replicas + external PostgreSQL Secret): uncomment
125128
`#- ci/values-high-availability.yaml` in `deploy/helm/openshell/skaffold.yaml`,

crates/openshell-driver-docker/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ spans export to the same OTLP/gRPC collector with the service name
88
context and emits the compute-driver RPC boundary that a standalone driver
99
would expose.
1010

11+
`mise run gateway:docker` enables this export only when a local collector is
12+
listening on `127.0.0.1:4317`. Otherwise, it omits the gateway OTLP configuration
13+
so the development gateway does not repeatedly report export failures.
14+
1115
The standalone `openshell-driver-docker` binary accepts
1216
`OPENSHELL_OTLP_ENDPOINT`. When set, it exports Docker driver spans to that
1317
collector, continues W3C trace context from gateway RPC metadata, and flushes

crates/openshell-driver-podman/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ spans export to the same OTLP/gRPC collector with the service name
1313
uses the same compute-driver RPC span names in its in-process and standalone
1414
forms.
1515

16+
`mise run gateway:podman` enables this export only when a local collector is
17+
listening on `127.0.0.1:4317`. Otherwise, it omits the gateway OTLP configuration
18+
so the development gateway does not repeatedly report export failures.
19+
1620
Before creating the container, the driver inspects the final sandbox image and
1721
captures its immutable image ID and raw OCI `Config.User`. Container creation
1822
uses that image ID with pulling disabled, preventing a mutable tag from changing

crates/openshell-driver-vm/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ By default `mise run gateway:vm`:
4747
- Persists the gateway SQLite DB under `.cache/gateway-vm/gateway.db`.
4848
- Places the VM driver state (per-sandbox `overlay.ext4`, image cache, and `run/compute-driver.sock`) under `/tmp/openshell-vm-driver-$USER-vm-dev/` so the AF_UNIX socket path stays under macOS `SUN_LEN`.
4949
- Writes `.cache/gateway-vm/gateway.toml` with `[openshell.drivers.vm].driver_dir = "$PWD/target/debug"` so the freshly built `openshell-driver-vm` is used instead of an older installed copy from `~/.local/libexec/openshell`, `/usr/libexec/openshell`, or `/usr/local/libexec`.
50+
- Enables OTLP trace export to `http://127.0.0.1:4317` only when a local collector is listening there. Otherwise, it omits the OTLP configuration to avoid repeated export failures.
5051

5152
For GPU passthrough (VFIO), pass `-- --gpu` and run with root privileges:
5253

tasks/scripts/gateway-docker.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ port_is_in_use() {
6565
(echo >/dev/tcp/127.0.0.1/"${port}") >/dev/null 2>&1
6666
}
6767

68+
append_local_otlp_config_if_available() {
69+
local config_path=$1
70+
if ! port_is_in_use 4317; then
71+
echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled."
72+
return
73+
fi
74+
75+
cat >>"${config_path}" <<'EOF'
76+
77+
[openshell.gateway.otlp]
78+
endpoint = "http://127.0.0.1:4317"
79+
EOF
80+
echo "OTLP trace export enabled for http://127.0.0.1:4317."
81+
}
82+
6883
register_gateway_metadata() {
6984
local name=$1
7085
local endpoint=$2
@@ -203,9 +218,6 @@ name = "${GATEWAY_NAME}"
203218
compute_drivers = ["docker"]
204219
disable_tls = true
205220
206-
[openshell.gateway.otlp]
207-
endpoint = "http://127.0.0.1:4317"
208-
209221
[openshell.gateway.auth]
210222
allow_unauthenticated_users = true
211223
@@ -224,6 +236,8 @@ grpc_endpoint = "${GRPC_ENDPOINT}"
224236
supervisor_bin = "${SUPERVISOR_BIN}"
225237
EOF
226238

239+
append_local_otlp_config_if_available "${CONFIG_PATH}"
240+
227241
GATEWAY_ENDPOINT="http://127.0.0.1:${PORT}"
228242
register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}"
229243

tasks/scripts/gateway-podman.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,21 @@ port_is_in_use() {
129129
(echo >/dev/tcp/127.0.0.1/"${port}") >/dev/null 2>&1
130130
}
131131

132+
append_local_otlp_config_if_available() {
133+
local config_path=$1
134+
if ! port_is_in_use 4317; then
135+
echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled."
136+
return
137+
fi
138+
139+
cat >>"${config_path}" <<'EOF'
140+
141+
[openshell.gateway.otlp]
142+
endpoint = "http://127.0.0.1:4317"
143+
EOF
144+
echo "OTLP trace export enabled for http://127.0.0.1:4317."
145+
}
146+
132147
register_gateway_metadata() {
133148
local name=$1
134149
local endpoint=$2
@@ -208,9 +223,6 @@ compute_drivers = ["podman"]
208223
default_image = "${SANDBOX_IMAGE}"
209224
disable_tls = true
210225
211-
[openshell.gateway.otlp]
212-
endpoint = "http://127.0.0.1:4317"
213-
214226
[openshell.gateway.auth]
215227
allow_unauthenticated_users = true
216228
@@ -267,6 +279,8 @@ if [[ -n "${OPENSHELL_SANDBOX_PROXY_CA_BUNDLE+x}" ]]; then
267279
printf 'proxy_ca_bundle = "%s"\n' "$(toml_escape "${OPENSHELL_SANDBOX_PROXY_CA_BUNDLE}")" >>"${CONFIG_PATH}"
268280
fi
269281

282+
append_local_otlp_config_if_available "${CONFIG_PATH}"
283+
270284
GATEWAY_ENDPOINT="http://${CLI_ENDPOINT_HOST}:${PORT}"
271285
register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}"
272286

tasks/scripts/gateway-vm.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,21 @@ port_is_in_use() {
8383
(echo >/dev/tcp/127.0.0.1/"${port}") >/dev/null 2>&1
8484
}
8585

86+
append_local_otlp_config_if_available() {
87+
local config_path=$1
88+
if ! port_is_in_use 4317; then
89+
echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled."
90+
return
91+
fi
92+
93+
cat >>"${config_path}" <<'EOF'
94+
95+
[openshell.gateway.otlp]
96+
endpoint = "http://127.0.0.1:4317"
97+
EOF
98+
echo "OTLP trace export enabled for http://127.0.0.1:4317."
99+
}
100+
86101
invoking_user() {
87102
if [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER}" != "root" ]; then
88103
printf '%s\n' "${SUDO_USER}"
@@ -328,9 +343,6 @@ name = "${GATEWAY_NAME}"
328343
compute_drivers = ["vm"]
329344
disable_tls = ${DISABLE_TLS}
330345
331-
[openshell.gateway.otlp]
332-
endpoint = "http://127.0.0.1:4317"
333-
334346
[openshell.gateway.auth]
335347
allow_unauthenticated_users = true
336348
@@ -349,6 +361,8 @@ driver_dir = "${DRIVER_DIR}"
349361
state_dir = "${VM_DRIVER_STATE_DIR}"
350362
EOF
351363

364+
append_local_otlp_config_if_available "${CONFIG_PATH}"
365+
352366
GATEWAY_ENDPOINT="http://127.0.0.1:${PORT}"
353367
register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" "${VM_DRIVER_STATE_DIR}"
354368
save_active_gateway "${GATEWAY_NAME}"

tasks/scripts/helm-k3s-local.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,15 @@ k3d_cluster_exists() {
142142

143143
merge_kubeconfig() {
144144
require_kubectl
145-
local tmp k3d_cfg merged_dir
145+
local tmp merged_dir
146146
tmp="$(mktemp)"
147147
k3d kubeconfig get "${CLUSTER_NAME}" >"${tmp}"
148148

149149
if [[ -s "${KUBECONFIG_TARGET}" ]]; then
150-
KUBECONFIG="${KUBECONFIG_TARGET}:${tmp}" kubectl config view --flatten >"${tmp}.out"
150+
# Put the freshly generated k3d config first so its cluster, context, and
151+
# user entries replace stale entries with the same names. The API server's
152+
# random host port can change when Docker recreates the load balancer.
153+
KUBECONFIG="${tmp}:${KUBECONFIG_TARGET}" kubectl config view --flatten >"${tmp}.out"
151154
mv "${tmp}.out" "${KUBECONFIG_TARGET}"
152155
else
153156
merged_dir="$(dirname "${KUBECONFIG_TARGET}")"
@@ -394,7 +397,8 @@ EOF
394397
local lb_port_map="${HOST_LB_PORT}:80@loadbalancer"
395398

396399
if k3d_cluster_exists; then
397-
echo "k3d cluster '${CLUSTER_NAME}' already exists; merging kubeconfig."
400+
echo "k3d cluster '${CLUSTER_NAME}' already exists; ensuring it is running."
401+
k3d cluster start "${CLUSTER_NAME}"
398402
else
399403
echo "Creating k3d cluster '${CLUSTER_NAME}'..."
400404
k3d cluster create "${CLUSTER_NAME}" \

0 commit comments

Comments
 (0)