magus v0.4.2 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
3 min read

MGS3004: tool not ready

The binary is present, but the thing it talks to is not. This is MGS3003 one level deeper: the same category of failure - your environment rather than your code - caught for a client whose server is down.

[MGS3004] docker is installed but not ready. The op "docker" needs it running, and
`docker info` said:
  Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
  see: https://eli.gladman.cc/magus/reference/codes/sandbox/MGS3004/

The probe's own output is carried through rather than paraphrased. docker info names the socket and asks the right question; magus knows less about docker than docker does. The underlying failure is wrapped, so errors.Is still reaches it.

Why a version probe cannot catch this

A spell's version probe answers "what version is installed", and for a client/server tool that question is answerable with the server switched off. docker --version is client-only: it prints a version and exits 0 with no daemon running at all.

So the probe magus already ran was structurally incapable of noticing. The op forked, docker failed, and the run reported a build failure for a project with nothing wrong with it - the real explanation sitting in a captured log someone had to go open.

A readiness probe is the third question, distinct from the other two:

question declared by docker
does it exist? nothing - PATH lookup (MGS3003) docker
what version? mgs_getVersionProbe docker --version
is it usable now? mgs_getReadinessProbes docker info

At a terminal, magus waits first

When stdin is a TTY, a failed probe is retried for 30 seconds before the run gives up, so starting the daemon in another window lets the run continue instead of needing a re-run.

That grace period is interactive-only. Under CI or an agent nobody starts a daemon mid-run, so waiting there would burn 30 seconds per project to reach the same failure - and it would make a deterministic failure depend on how fast something else happened to start. Without a TTY the probe runs once and fails immediately.

Failing fast stays cheap either way: the check runs before the op forks, so nothing was consumed and a re-run replays from cache.

Resolution

The probe's own output is the instruction: start the service it names - the socket in docker info's complaint above - and run again. Nothing was consumed, because the check runs before the op forks, so the re-run replays from cache. At a terminal you do not need a re-run at all: start the service in another window inside the 30-second grace period and the run continues.

If the failure instead reached you as a build error out of the tool itself, no readiness probe is declared for it. Declare one, as below.

Declaring one

Keyed by tool name, the same convention the version probes use, and resolved through the op's own Command.bin - so no op restates which tool it runs.

export fun mgs_getReadinessProbes() > {str: Command} {
    return {"docker": Command{bin = "docker", args = ["info"]}};
}

Scoping is per tool, not per spell, and the docker spell is why. It drives docker and hadolint, and linting a Dockerfile talks to no daemon. A spell-scoped probe would make a lint wait on a service it never uses, which is a worse version of the bug this exists to fix.

Most spells need none. go, rustc, and node are self-contained; a spell that declares nothing is never gated.

Readiness never keys the cache

A readiness result is a precondition, not an input. docker info reports running containers and disk usage, so mixing it into a cache key would invalidate every entry on every run.

This is worth stating because the neighboring mechanism does the opposite: a version key exists precisely to enter the key. The two probes look alike and mean opposite things.

MGS3004readinesstoolsdockerdaemonerrorsmagusfile
Last updated (d120b58e)
Earlier changes on this page (2)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Sandbox

The restricted filesystem and environment a target runs in, so builds stay reproducible and side-effect-free. See sandbox.

Service

A long-running or shared process magus manages across runs, distinct from a one-shot target. See services.

Daemon

The background magus host that owns shared state such as services and the warm knowledge graph. See daemon.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.