carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "errors"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "testing"
10
11 "github.com/carlosframework/platform/internal/hostbox"
12 "github.com/carlosframework/platform/internal/hostclaim"
13 "github.com/carlosframework/platform/internal/hostguard"
14 "github.com/carlosframework/platform/internal/provision"
15 "github.com/carlosframework/platform/internal/release"
16)
17
18func TestPlan14FreshInstallProcessCrash(t *testing.T) {
19 t.Parallel()
20 if boundary := os.Getenv("PLAN14_CREATION_CRASH"); boundary != "" {
21 dir := os.Getenv("PLAN14_CREATION_DIR")
22 root, disk := plan14InventoryProcessRoot(t, dir)
23 hs := &rollbackDisk{dir: filepath.Join(dir, "history")}
24 h := &release.RootHistory{Store: hs}
25 root.history = func(_ context.Context, fn func(*release.RootHistory) error) error { return fn(h) }
26 count := 0
27 disk.afterWrite = func(string, []byte) {
28 count++
29 if boundary == "intent" && count == 1 || boundary == "committed" && count == 2 {
30 os.Exit(73)
31 }
32 }
33 hs.afterWrite = func(string, []byte) {
34 if boundary == "creation" {
35 os.Exit(73)
36 }
37 }
38 obs, err := hostclaim.Read(t.Context(), root.Stop.Coordinator.Evidence, recoveryHost)
39 if err != nil {
40 t.Fatal(err)
41 }
42 holder := obs.Claim.Holder
43 _, etag, err := root.Stop.Coordinator.Evidence.Get(t.Context(), holder.Key)
44 if err != nil {
45 t.Fatal(err)
46 }
47 _, client := hostbox.RemoteJournals(root.commitRootInventory)
48 _, err = client.WriteExpected(t.Context(), hostbox.StopInventory{Complete: true, Host: obs.Host, Incarnation: holder.Incarnation, InstallationStamp: holder.Stamp, Box: "box-1", Enrollment: "en", DirectoryRevision: "1"}, hostbox.InventoryExpectation{RecordKey: holder.Key, RecordETag: etag, Target: hostguard.FenceTarget{Host: obs.Host, Incarnation: holder.Incarnation, Stamp: holder.Stamp, Session: holder.Session, Box: "box-1", Enrollment: "en", ClaimETag: obs.ETag, Op: obs.Claim.Op.ID, Phase: obs.Claim.Op.Phase}})
49 t.Fatal("boundary not reached", boundary, err)
50 }
51 for _, boundary := range []string{"intent", "creation", "committed"} {
52 t.Run(boundary, func(t *testing.T) {
53 dir := t.TempDir()
54 for _, name := range []string{"root", "data", "opt", "units", "locks", "child", "history"} {
55 if err := os.Mkdir(filepath.Join(dir, name), 0700); err != nil {
56 t.Fatal(err)
57 }
58 }
59 f := newRecoveryFixture(t)
60 obs := f.read()
61 holder := obs.Claim.Holder
62 plan14PutRecord(t, f.claims, provision.Record{Schema: 1, Host: obs.Host, Account: holder.Account, App: holder.App, Channel: "stable", CreatedAt: f.now})
63 store := release.NewDirStore(filepath.Join(dir, "deployment"))
64 keys, err := f.claims.List(t.Context(), "")
65 if err != nil {
66 t.Fatal(err)
67 }
68 for _, key := range keys {
69 raw, _, err := f.claims.Get(t.Context(), key)
70 if err != nil {
71 t.Fatal(err)
72 }
73 if _, err = store.Put(t.Context(), key, raw, ""); err != nil {
74 t.Fatal(err)
75 }
76 }
77 cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14FreshInstallProcessCrash$")
78 cmd.Env = append(os.Environ(), "PLAN14_CREATION_CRASH="+boundary, "PLAN14_CREATION_DIR="+dir, "TMPDIR="+filepath.Join(dir, "child"))
79 output, err := cmd.CombinedOutput()
80 var exit *exec.ExitError
81 if !errors.As(err, &exit) || exit.ExitCode() != 73 {
82 t.Fatalf("child: %v %s", err, output)
83 }
84 root, _ := plan14InventoryProcessRoot(t, dir)
85 h := &release.RootHistory{Store: &rollbackDisk{dir: filepath.Join(dir, "history")}}
86 root.history = func(_ context.Context, fn func(*release.RootHistory) error) error { return fn(h) }
87 if err := root.recoverRootInventory(t.Context()); err != nil {
88 t.Fatal(err)
89 }
90 view, err := root.Stop.Coordinator.Guard.Root.View(t.Context(), obs.Host, hostguard.Captured{Incarnation: holder.Incarnation, Stamp: holder.Stamp})
91 if err != nil {
92 t.Fatal(err)
93 }
94 b := release.HistoryBinding{Host: obs.Host, Account: holder.Account, App: holder.App}
95 fresh, err := h.FreshInstallation(b, rootHistoryInstallation(view.Installation))
96 if err != nil || !fresh || view.Installation.InventoryRevision != 1 {
97 t.Fatalf("recovered: %+v fresh=%v: %v", view, fresh, err)
98 }
99 })
100 }
101}
102