package host import ( "encoding/base64" "os" "os/exec" "path/filepath" "strings" "testing" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14Fix4AdoptPerHostFailureExitsZero(t *testing.T) { if os.Getenv("PLAN14_FIX4_ADOPT_CHILD") == "1" { args := []string{"-registry", os.Getenv("PLAN14_FIX4_REGISTRY")} if os.Getenv("PLAN14_FIX4_STAGE") == "1" { args = append(args, "-stage-only") } runAdoptWithEnsureSystemUnits(args, nil, func() {}, noCensusAdoptSource(func() (rootstate.Store, error) { return &rollbackMemory{files: map[string][]byte{}}, nil })) return } if os.Geteuid() == 0 { t.Skip("fixture exercises the local registry as an unprivileged user") } for _, stage := range []bool{false, true} { for _, crash := range []bool{false, true} { t.Run(map[bool]string{false: "hourly", true: "stage"}[stage]+map[bool]string{false: "/restart-error", true: "/rollback-error"}[crash], func(t *testing.T) { e, rt, u, _, pub := plan14AdoptionFixture(t) rt.Unit = activator.UnitName(rt.Host) if err := e.root.reg.Upsert(rt); err != nil { t.Fatal(err) } dir := t.TempDir() if err := os.Mkdir(filepath.Join(dir, "data"), 0755); err != nil { t.Fatal(err) } bucket := filepath.Join(dir, "bucket") store := release.NewDirStore(bucket) keys, err := e.store.List(t.Context(), "") if err != nil { t.Fatal(err) } for _, key := range keys { raw, _, err := e.store.Get(t.Context(), key) if err != nil { t.Fatal(err) } if _, err := store.Put(t.Context(), key, raw, ""); err != nil { t.Fatal(err) } } stub := `#!/bin/sh case "$1" in show) if [ -f "$PLAN14_FIX4_EFFECT" ]; then printf 'ActiveState=failed\nSubState=failed\nMainPID=0\n' else printf 'Id=console.service\nActiveState=active\nSubState=running\nMainPID=1\nInvocationID=old\nExecStart={ path=%s ; }\n' "$PLAN14_FIX4_LIVE" fi ;; try-restart) if [ "$PLAN14_FIX4_CRASH" = 1 ]; then : > "$PLAN14_FIX4_EFFECT"; exit 0; fi exit 1 ;; start|restart) exit 1 ;; *) exit 0 ;; esac ` if err := os.WriteFile(filepath.Join(dir, "systemctl"), []byte(stub), 0755); err != nil { t.Fatal(err) } cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14Fix4AdoptPerHostFailureExitsZero$", "-test.v") cmd.Dir = dir bit := func(v bool) string { if v { return "1" } return "0" } cmd.Env = isolatedAdoptOrderEnv(t, "PATH="+dir+":"+os.Getenv("PATH"), "PLAN14_FIX4_ADOPT_CHILD=1", "PLAN14_FIX4_STAGE="+bit(stage), "PLAN14_FIX4_CRASH="+bit(crash), "PLAN14_FIX4_REGISTRY="+e.root.reg.Path(), "PLAN14_FIX4_EFFECT="+filepath.Join(dir, "effect"), "PLAN14_FIX4_LIVE="+release.LiveLink(u.opt, rt.Host), "CARLOS_DATA_DIR="+filepath.Join(dir, "data"), "CARLOS_OPT_DIR="+u.opt, "CARLOS_DEPLOYMENT_DIR="+bucket, "CARLOS_DEPLOYMENT_BUCKET=", "CARLOS_HOST_CLAIM=", "CARLOS_REGION_HA=", "CARLOS_MOVES_PHASE1=", "CARLOS_SECRETS_KEY=", "CARLOS_RELEASE_PUBKEY="+base64.StdEncoding.EncodeToString(pub), ) out, err := cmd.CombinedOutput() if err != nil || !strings.Contains(string(out), "update to v2 failed") || !strings.Contains(string(out), "may be serving nothing") { t.Fatalf("per-host failure exit=%v\n%s", err, out) } if crash && !strings.Contains(string(out), "did not come up") { t.Fatal("crash did not take D1 rollback", string(out)) } if live, err := release.LiveVersion(u.opt, rt.Host); err != nil || live != "v1" { t.Fatal(live, err) } }) } } }