A doctor command looks at the machine and says what is wrong and what to do
about it. doctor.Check holds one check: a name, a status, a
detail and a remediation.
Procedure#
- Make a
Checkfor each thing that you look at. Usedoctor.StatusOK,doctor.StatusWarningordoctor.StatusFailed. - Write the checks for a person with
doctor.Write, or as JSON withcliout.WriteJSON. - Pick the exit status from
doctor.Worst.
const pull = "Pull the node image:\n\ndocker pull ghcr.io/gsi-hpc/sind-node"
checks := []doctor.Check{
{Name: "Docker Engine", Status: doctor.StatusOK, Detail: "28.1.1 (>= 25.0)"},
{Name: "node image", Status: doctor.StatusFailed, Detail: "not found\x1b[2J", Remediation: pull},
{Name: "inotify", Status: doctor.StatusWarning, Detail: "max_user_instances 128 (optional)"},
{Name: "sshuttle", Status: doctor.StatusWarning, Detail: "not in PATH"},
}
if err := doctor.Write(os.Stdout, checks); err != nil {
fmt.Println(err)
}
failed, warned := doctor.Count(checks)
fmt.Printf("\n%d checks, %d failed, %d warnings\n", len(checks), failed, warned)
if doctor.Worst(checks) == doctor.StatusFailed {
fmt.Println("exit status 1")
}✓ Docker Engine: 28.1.1 (>= 25.0)
✗ node image: not found\x1b[2J
! inotify: max_user_instances 128 (optional)
! sshuttle: not in PATH
Pull the node image:
docker pull ghcr.io/gsi-hpc/sind-node
4 checks, 1 failed, 2 warnings
exit status 1doctor/example_test.go. CI runs it and checks its output.check := doctor.Check{Name: "inotify", Status: doctor.StatusWarning, Detail: "max_user_instances 128"}
out, err := json.Marshal(check)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out)){"name":"inotify","status":"warning","detail":"max_user_instances 128"}doctor/example_test.go. CI runs it and checks its output.CAUTION
Worst fails closed. A status that is none of the three words counts as
failed.
The status words, the JSON keys and the text that Write writes are an
interface (decision 38). A script can read them.