package apply import ( "context" "errors" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/declaration" "github.com/novox/mesh-host/internal/store" ) // Issue 1742: the engine removed a running container and only then found the replacement image // unobtainable, leaving the service down. The replacement image comes first. func TestARunningContainerIsKeptWhenItsReplacementImageCannotBeObtained(t *testing.T) { d := parseTrusted(t, `{"declaration":1,"resources":[ {"id":"store","type":"container","name":"store","image":"`+pinned+`","env":{"PGDATA":"/data"}} ]}`) var removed, created bool run := func(ctx context.Context, name string, args ...string) (string, error) { args = oldShape(name, args) switch args[0] { case "info": return "27.0\n", nil case "container": return "true\tsome-older-spec", nil case "image": return "", errors.New("no such image") case "pull": return "", errors.New("manifest unknown") case "rm": removed = true return "", nil case "run": created = true return "deadbeef\n", nil } return "", nil } _, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil) if removed || created { t.Fatalf("the running container was touched although its image is unobtainable (removed=%v created=%v)", removed, created) } if err == nil { t.Fatal("an unobtainable image failed nothing") } if !strings.Contains(err.Error(), pinned) { t.Errorf("the failure does not name the image %s: %v", pinned, err) } } func TestAReplacementImageIsPresentBeforeTheRunningContainerIsRemoved(t *testing.T) { d := parseTrusted(t, `{"declaration":1,"resources":[ {"id":"store","type":"container","name":"store","image":"`+pinned+`","env":{"PGDATA":"/data"}} ]}`) want := containerSpec(d.Resources[0].(*declaration.Container), inputs{}) var order []string created := false run := func(ctx context.Context, name string, args ...string) (string, error) { args = oldShape(name, args) switch args[0] { case "info": return "27.0\n", nil case "container": if created { return "true\t" + want, nil } return "true\tsome-older-spec", nil case "image": order = append(order, "inspect") if len(order) == 1 { return "", errors.New("no such image") } return "", nil case "pull": order = append(order, "pull") case "rm": order = append(order, "rm") case "run": order = append(order, "run") created = true return "deadbeef\n", nil } return "", nil } if _, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil); err != nil { t.Fatalf("apply failed: %v", err) } got := strings.Join(order, ",") if !strings.HasPrefix(got, "inspect,pull,inspect,rm,run") { t.Errorf("order of runtime calls = %s, want the image pulled before rm", got) } }