Repository navigation
Conversation
#619 made every operator init container that writes pod storage use the gateway's effective UID. Do the same for init-tmp-dir so the sticky /tmp directory belongs to the user that writes to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2e-pod-diagnostics
Release Please has failed on every push since the repository moved from paperclipinc to stubbi: vars.RELEASE_BOT_APP_ID and secrets.RELEASE_BOT_PRIVATE_KEY were settings of the old organization, so actions/create-github-app-token aborts with "Input required and not supplied: app-id" before release-please runs. - release-please.yaml and release.yaml mint the App token only when the App is configured, then fall back to RELEASE_PLEASE_TOKEN and finally GITHUB_TOKEN. With GITHUB_TOKEN, tag pushes and release publishing do not trigger other workflows, so release.yaml and operatorhub.yaml are dispatched explicitly (release.yaml gains a workflow_dispatch trigger and only runs on tag refs). - The GHCR namespace for the image and the chart comes from one place, vars.IMAGE_NAMESPACE (default paperclipinc), used by release.yaml and .goreleaser.yaml. release.yaml previously used the repository owner (now stubbi) for signing and the chart while GoReleaser pushed to paperclipinc. A preflight fails the release if the namespace does not match the chart's default image.repository (#536). - GHCR login accepts GHCR_PUSH_TOKEN / GHCR_PUSH_USER, needed to push into a namespace the repository owner does not own. - GoReleaser creates the release in stubbi/openclaw-operator. - make verify-signing accepts both the old and the new workflow identity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge order note (maintenance pass, 2026-10-11). The open stubbi PRs are stacked so they apply cleanly in this order: #623 → #627 → #620 → #618 → #617 → #628 → #629. This is position 3 of 7. This branch has #627 (and everything below it) merged in, so until those land the diff here also shows their changes; once they are squash-merged, "Update branch" reduces it to this PR's own changes without conflicts. #623 is at the bottom because the required E2E check cannot pass without its sticky /tmp fix (OpenClaw >= 2026.9.6 crash-loops otherwise). Branch protection requires an up-to-date branch, so after each merge click "Update branch" on the next PR and let CI re-run before merging it. |
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. If this PR is still relevant, please push updates or leave a comment. |
|
Not stale |
## Problem The required E2E check is red on `main` and on every same-repo PR: the `Chromium Full Integration Tests` `BeforeAll` times out because the `openclaw` container never becomes ready. The diagnostics from #620 show why. The container exits with code 1 and goes into CrashLoopBackOff, and its log says: ``` Doctor could not complete maintenance. Check the reported service state and resolve the failure. temp workspace ancestor is group/world writable without sticky protection ``` The main container has a read-only root filesystem, so its `/tmp` is an emptyDir. With `fsGroup` set, the kubelet gives the root of an emptyDir mode 2777: world-writable, setgid, no sticky bit. OpenClaw 2026.9.6 and later refuse to create a temporary workspace below such a directory. The startup doctor needs that workspace whenever it has to install a configured plugin. This is the same check #616 fixed for `init-plugins`. That container could fall back to the image's own `/tmp` because its root filesystem is writable; the main container cannot. Only the Full Integration suite hits it because it is the only one that passes `OPENROUTER_API_KEY`, which makes OpenClaw install its Perplexity plugin at startup. Fork PRs skip that suite (no secret), and the smoke test has no key, so both stay green. Real instances are affected whenever the doctor has to install a plugin at startup. ## Change - New init container `init-tmp-dir` creates a subdirectory in the `tmp` emptyDir and sets it to mode 1777. - The main container mounts that subdirectory at `/tmp` via `subPath`, which hides the 2777 volume root from its path ancestry. - The init container runs as the pod UID with all capabilities dropped and a read-only root filesystem, so it is admitted under the restricted Pod Security Standard. It is always rendered, including when `storage.fixOwnership` is false. - `init-tmp-dir` is added to the reserved init container names in the webhook. Nothing in the pod spec can set the mode of a volume root, and the pod UID does not own it, so it cannot chmod the root itself. A directory it creates inside the volume is its own. ## Behavior change to note Every instance gets one more init container, so pods roll on operator upgrade. ## Testing - `make test` passes locally (unit + envtest). New unit tests in `internal/resources/tmp_dir_test.go`, new e2e spec `test/e2e/e2e_tmp_dir_test.go`. - Reproduced outside the cluster with `ghcr.io/openclaw/openclaw:latest` (2026.9.8) under Docker, using the operator's rendered config, volume layout, read-only root and dropped capabilities: - `/tmp` as a 2777 root-owned directory with `OPENROUTER_API_KEY` set: exits 1 with the message above. - Same, without the key: boots normally, which matches the smoke test passing. - The generated init script run as 1000:1000 against a 2777 root:1000 volume produces a 1777 directory owned by 1000, and is idempotent. With that directory mounted at `/tmp` and the key set, the gateway reaches `readyz 200`. - Not run on a real cluster before this PR; this PR's own E2E run is the first. I could not run golangci-lint locally (it runs out of memory in my environment), so Lint is covered by CI only. ## Follow-ups - `init-pip` still mounts the root of the same emptyDir at `/tmp`. It does not run the doctor, so it is left unchanged here. - #617, #618 and #620 need "Update branch" after this lands before their E2E can pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011HrjPjDpPgTdCFicDWQpvH --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. If this PR is still relevant, please push updates or leave a comment. |
Why
The required E2E check has been failing on
main(and on every same-repo PR) in theChromium Full Integration TestsBeforeAll: the pod reaches Running, but theopenclawcontainer never becomes ready within 10 minutes. The only output is a repeatedContainer openclaw not ready, andAfterAlldeletes the namespace before the workflow's failure step runs, so there is nothing to diagnose from. Fork PRs skip this suite because they have noOPENROUTER_API_KEY, which is why they can still go green.I could not reproduce the failure outside the cluster: the same rendered config boots to
readyz=200onghcr.io/openclaw/openclaw:latest(2026.9.8) under plain Docker with the operator's volume layout, read-only root, dropped capabilities and a real headless-shell sidecar sharing the network namespace.What
test/e2e/diagnostics_test.go:dumpPodDiagnostics(namespace, pod)prints pod conditions, per-container state with restart counts and last termination, namespace events, and the tail of current and previous logs for containers that are not ready.BeforeAllcalls it from a deferred function when the readiness wait fails, before the namespace is deleted.It deliberately does not use
kubectl describe podor print the pod spec, because this suite passes the OpenRouter key as a plain env value.Expected result of this PR's own CI
E2E will still fail. The point is that the failing run now shows why. No operator code changes.
🤖 Generated with Claude Code
https://claude.ai/code/session_011HrjPjDpPgTdCFicDWQpvH