inspect by kind, not the ambiguous bare form — a same-named network stops a container from ever being found
docker inspect <name> resolves across every object kind, not just
containers. A module regularly names a network the same as the
container that joins it (keycloak does this today, ordinarily) — so
when the container does not exist yet but the same-named network
already does, the bare form answers with the network's JSON instead
of reporting the container absent, and the template these callers use
(.State.Running) fails to execute against it entirely.
Live on novox tonight: minio's LB container, named the same as its
network ("minio"), could never be created — every apply crashed on
"the container runtime could not say whether minio is here", stuck
since first push, because the check itself never got a clean answer.
Fixed at every call site asking a container's state by name
(containerState, inspectFound, NamesFree, raiseGiteaServer,
containerRunning) by scoping to `docker container inspect`, matching
the type-scoped form this codebase already uses correctly for
networks, volumes and images elsewhere. Also scoped the one image
inspect that was still bare (publish.go), for the same reason.
mesh-host runs as a host-level service (nox-mesh-host.service), not a
Docker module — merging this does not redeploy it. The live novox
failure persists until the service itself is rebuilt and updated.
This commit is contained in:
@@ -35,7 +35,7 @@ func (l *labelled) run(_ context.Context, _ string, args ...string) (string, err
|
||||
switch args[0] {
|
||||
case "info":
|
||||
return "27.0\n", nil
|
||||
case "inspect":
|
||||
case "container":
|
||||
spec, ok := l.spec[args[len(args)-1]]
|
||||
if !ok {
|
||||
return "", errors.New("no such container")
|
||||
|
||||
@@ -230,8 +230,9 @@ func raiseGiteaServer(ctx context.Context, run Runner, timeout time.Duration, db
|
||||
defer cancel()
|
||||
|
||||
// Already there: a re-run does not raise a second one. `docker start` is a no-op on a running
|
||||
// container and revives a stopped one.
|
||||
if out, _ := run(asking, "docker", "inspect", "--format", "{{.Id}}", giteaBootstrap); strings.TrimSpace(out) != "" {
|
||||
// container and revives a stopped one. `container inspect`, not the bare form: a name is not
|
||||
// unique across object kinds, and `.Id` resolves on a network or volume too.
|
||||
if out, _ := run(asking, "docker", "container", "inspect", "--format", "{{.Id}}", giteaBootstrap); strings.TrimSpace(out) != "" {
|
||||
_, _ = run(asking, "docker", "start", giteaBootstrap)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -431,7 +431,9 @@ func NamesFree(ctx context.Context, run Runner, names []string, known store.Stat
|
||||
sorted := append([]string{}, names...)
|
||||
sort.Strings(sorted)
|
||||
for _, name := range sorted {
|
||||
out, err := run(ctx, "docker", "inspect", "--format",
|
||||
// `container inspect`: a name is not unique across object kinds, and the bare form can
|
||||
// resolve to a same-named network or volume instead of reporting the container absent.
|
||||
out, err := run(ctx, "docker", "container", "inspect", "--format",
|
||||
"{{index .Config.Labels \"mesh-host.spec\"}}", name)
|
||||
if err != nil {
|
||||
continue // no such container
|
||||
|
||||
@@ -145,7 +145,7 @@ func (m machineRunner) run(_ context.Context, name string, args ...string) (stri
|
||||
return m.ss, nil
|
||||
case name == "docker" && args[0] == "ps":
|
||||
return m.ps, nil
|
||||
case name == "docker" && args[0] == "inspect":
|
||||
case name == "docker" && args[0] == "container":
|
||||
n := args[len(args)-1]
|
||||
if m.labelled[n] {
|
||||
return "abc\n", nil
|
||||
|
||||
@@ -150,7 +150,7 @@ func digestOf(ctx context.Context, o Options, d Deps, remote string) (string, er
|
||||
|
||||
// The registry has it. What digest, according to the runtime that pushed it.
|
||||
reading, cancel := context.WithTimeout(ctx, o.Timeout)
|
||||
out, err := d.Run(reading, "docker", "inspect", "--format", "{{json .RepoDigests}}",
|
||||
out, err := d.Run(reading, "docker", "image", "inspect", "--format", "{{json .RepoDigests}}",
|
||||
remote+":"+genesisTag)
|
||||
cancel()
|
||||
if err != nil {
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestAnImageNoRegistryHasEverHeldIsPushed(t *testing.T) {
|
||||
case "push":
|
||||
pushed = true
|
||||
return "", nil
|
||||
case "inspect":
|
||||
case "image":
|
||||
return `["127.0.0.1:5000/mesh-controller@sha256:` + strings.Repeat("a", 64) + `"]`, nil
|
||||
}
|
||||
return "", fmt.Errorf("unexpected: %v", args)
|
||||
@@ -80,7 +80,7 @@ func TestAnImageTheRegistryAlreadyServesIsNotPushedAgain(t *testing.T) {
|
||||
return http.StatusOK, `{"name":"mesh-controller","tags":["genesis"]}`, nil
|
||||
},
|
||||
func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "image" {
|
||||
return `["127.0.0.1:5000/mesh-controller@sha256:` + strings.Repeat("b", 64) + `"]`, nil
|
||||
}
|
||||
return "", fmt.Errorf("unexpected: %v", args)
|
||||
@@ -111,7 +111,7 @@ func TestTheDigestComesFromThisMeshsOwnRegistry(t *testing.T) {
|
||||
return http.StatusOK, `{"tags":["genesis"]}`, nil
|
||||
},
|
||||
func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "image" {
|
||||
return `["` + elsewhere + `","` + ours + `"]`, nil
|
||||
}
|
||||
return "", fmt.Errorf("unexpected: %v", args)
|
||||
@@ -166,7 +166,7 @@ func TestATagIsNotAPin(t *testing.T) {
|
||||
return http.StatusOK, `{"tags":["genesis"]}`, nil
|
||||
},
|
||||
func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "image" {
|
||||
return `["127.0.0.1:5000/mesh-controller:genesis"]`, nil
|
||||
}
|
||||
return "", nil
|
||||
|
||||
@@ -62,7 +62,7 @@ func aMeshThatAgrees(answers map[string]string) func(string, []string) (string,
|
||||
return "", fmt.Errorf("unexpected program %q", name)
|
||||
case args[0] == "cp":
|
||||
return "", nil
|
||||
case args[0] == "inspect":
|
||||
case args[0] == "container":
|
||||
return "true running\n", nil
|
||||
case args[0] == "exec":
|
||||
return "", nil
|
||||
|
||||
@@ -129,8 +129,9 @@ func containerRunning(ctx context.Context, run Runner, probe time.Duration, name
|
||||
defer cancel()
|
||||
|
||||
// Both facts in one answer, so a container that is not running is reported with what it IS
|
||||
// rather than with the absence of what it should be.
|
||||
out, err := run(asking, "docker", "inspect", "--format", "{{.State.Running}} {{.State.Status}}", name)
|
||||
// rather than with the absence of what it should be. `container inspect`, not the bare form:
|
||||
// a same-named network or volume would otherwise answer in the container's place.
|
||||
out, err := run(asking, "docker", "container", "inspect", "--format", "{{.State.Running}} {{.State.Status}}", name)
|
||||
if err != nil {
|
||||
return containerState{}, fmt.Errorf(
|
||||
"the container %q is not there at all, and the apply reported it applied: %w", name, err)
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestAContainerThatIsUpIsNotAControlPlaneThatReplies(t *testing.T) {
|
||||
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
switch args[0] {
|
||||
case "inspect":
|
||||
case "container":
|
||||
return "true running\n", nil
|
||||
case "exec":
|
||||
// Up, and saying nothing. The program inside is not answering.
|
||||
@@ -59,7 +59,7 @@ func TestAControlPlaneThatSaysNothingHasNotAnswered(t *testing.T) {
|
||||
defer func() { answerEvery = previous }()
|
||||
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "container" {
|
||||
return "true running\n", nil
|
||||
}
|
||||
return " \n", nil
|
||||
@@ -74,7 +74,7 @@ func TestAControlPlaneThatSaysNothingHasNotAnswered(t *testing.T) {
|
||||
// The foundation answering is the whole point, and what it said is reported rather than asserted.
|
||||
func TestAFoundationThatIsUpAndAnsweringIsAccepted(t *testing.T) {
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "container" {
|
||||
return "true running\n", nil
|
||||
}
|
||||
return "1 node, 0 waiting\n", nil
|
||||
@@ -112,7 +112,7 @@ func TestAControlPlaneThatIsStillStartingIsWaitedFor(t *testing.T) {
|
||||
|
||||
attempts := 0
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "container" {
|
||||
return "true running\n", nil
|
||||
}
|
||||
attempts++
|
||||
@@ -132,10 +132,10 @@ func TestAControlPlaneThatIsStillStartingIsWaitedFor(t *testing.T) {
|
||||
// than being told only that something is not what it should be.
|
||||
func TestAContainerThatExitedIsNamedWithItsState(t *testing.T) {
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" && args[len(args)-1] == "mesh-broker" {
|
||||
if args[0] == "container" && args[len(args)-1] == "mesh-broker" {
|
||||
return "false exited\n", nil
|
||||
}
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "container" {
|
||||
return "true running\n", nil
|
||||
}
|
||||
return "", fmt.Errorf("unexpected command: %v", args)
|
||||
@@ -155,7 +155,7 @@ func TestAContainerThatExitedIsNamedWithItsState(t *testing.T) {
|
||||
// `FROM scratch` and has no shell for a command line to be interpreted by.
|
||||
func TestTheControlPlaneIsAskedByRunningItsOwnBinary(t *testing.T) {
|
||||
runtime := &asked{answer: func(_ string, args []string) (string, error) {
|
||||
if args[0] == "inspect" {
|
||||
if args[0] == "container" {
|
||||
return "true running\n", nil
|
||||
}
|
||||
return "1 node\n", nil
|
||||
|
||||
Reference in New Issue
Block a user