vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Probe the container runtime at startup instead of on the first job
f28097af06382d809c64e8ce93e5838e465b58af · Keenan Nemetz · 8h ago · parent 9704d735
Probe the container runtime at startup instead of on the first job Validating the runtime checked the binary: an unsupported name, or not on PATH. That is not the same as the runtime being able to create a container. An unwritable run root, a storage driver the kernel will not load, an OCI runtime that is not installed, and an XDG_RUNTIME_DIR mismatch all pass a LookPath check and none of them can start a container, so a runner came up active, connected, leased a job, and failed it before a step ran. The only party that can know is the machine, and the only cheap way to ask is the runtime itself: `info` fails for each of those states, pulls no image, and answers in milliseconds on a runner that has run a job. Probe once at startup and refuse to serve, so the unit fails, Restart=on-failure retries it, and journalctl carries the reason instead of a job log carrying an exit code. The message names the runtime and repeats its own last line — podman's warnings about the run root are not the cause, and "exited 125" is what made a machine's problem read as a repository whose build broke: cannot run a container with podman: Error: default OCI runtime "crun" not found: invalid argument vole workflow run makes the same check before a local run, so a developer with a stopped daemon hears about it up front rather than at the first step. The probe runs once: a runtime that breaks while a runner is serving fails the job it was building, which is a record the run page has to be able to describe (#246), not something a second probe would have prevented. Closes 01M6Z05PXY0HN6B7Q1XYYH3MQC
ci/agent/sandbox_test.go +26
| @@ -214,3 +214,29 @@ esac | |||
| 214 | 214 | t.Fatalf("a live runner's network was removed:\n%s", calls) | |
| 215 | 215 | } | |
| 216 | 216 | } | |
| 217 | |||
| 218 | // A runtime whose binary is on PATH but which cannot create a container is the | ||
| 219 | // machine's problem, not the repository's: the probe runs before the first lease, so | ||
| 220 | // no job is taken and none is recorded as a failure of a commit that never ran a | ||
| 221 | // step. | ||
| 222 | func TestRunRefusesARuntimeThatCannotCreateAContainer(t *testing.T) { | ||
| 223 | dir := t.TempDir() | ||
| 224 | script := `#!/bin/sh | ||
| 225 | echo 'Error: default OCI runtime "crun" not found: invalid argument' >&2 | ||
| 226 | exit 125 | ||
| 227 | ` | ||
| 228 | if err := os.WriteFile(filepath.Join(dir, "docker"), []byte(script), 0o755); err != nil { | ||
| 229 | t.Fatal(err) | ||
| 230 | } | ||
| 231 | t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH")) | ||
| 232 | |||
| 233 | err := Run(context.Background(), Options{ | ||
| 234 | Remote: "ssh://git@example.invalid:2222", | ||
| 235 | Dir: t.TempDir(), | ||
| 236 | ContainerRuntime: "docker", | ||
| 237 | }) | ||
| 238 | want := `cannot run a container with docker: Error: default OCI runtime "crun" not found: invalid argument` | ||
| 239 | if err == nil || err.Error() != want { | ||
| 240 | t.Fatalf("Run = %v, want %q", err, want) | ||
| 241 | } | ||
| 242 | } | ||