| 1 | package activator |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "fmt" |
| 7 | "os/exec" |
| 8 | "strings" |
| 9 | "testing" |
| 10 | |
| 11 | "github.com/carlosframework/platform/internal/cgroup" |
| 12 | "github.com/carlosframework/platform/internal/instancectl" |
| 13 | "github.com/carlosframework/platform/internal/registry" |
| 14 | ) |
| 15 | |
| 16 | func TestPlan14GenerationBackendDirectBoundary(t *testing.T) { |
| 17 | for _, claimed := range []bool{false, true} { |
| 18 | for _, strict := range []string{"unset", "ordinary", "strict"} { |
| 19 | for _, child := range []string{"", "g7", "role-seed", "role-sidecar"} { |
| 20 | t.Run(fmt.Sprintf("claim=%t/%s/%s", claimed, strict, child), func(t *testing.T) { |
| 21 | b := testExecBackend(t) |
| 22 | b.Cfg.HostWrites.Enabled = claimed |
| 23 | prepared, built, gates := 0, 0, 0 |
| 24 | prepareErr := errors.New("strict preparation reached") |
| 25 | if strict != "unset" { |
| 26 | b.Strict = func(string) bool { return strict == "strict" } |
| 27 | b.prepareStrict = func(string, cgroup.Limit) (strictProc, error) { |
| 28 | prepared++ |
| 29 | return nil, prepareErr |
| 30 | } |
| 31 | } |
| 32 | build := func() *exec.Cmd { built++; return exec.Command("true") } |
| 33 | before := func() error { gates++; return nil } |
| 34 | var cmd *exec.Cmd |
| 35 | var settle func() |
| 36 | var err error |
| 37 | if child == "" { |
| 38 | cmd, settle, err = b.spawnInCgroupGated(registry.Route{Host: "direct.example"}, cgroup.Limit{}, build, before) |
| 39 | } else { |
| 40 | cmd, settle, err = b.spawnInCgroupAtTier(registry.Route{Host: "direct.example"}, child, true, cgroup.Limit{}, build, before) |
| 41 | } |
| 42 | if cmd != nil { |
| 43 | if wait := cmd.Wait(); wait != nil { |
| 44 | t.Fatal(wait) |
| 45 | } |
| 46 | } |
| 47 | if settle != nil { |
| 48 | settle() |
| 49 | } |
| 50 | if claimed { |
| 51 | if !errors.Is(err, ErrClaimIsolationUnavailable) || cmd != nil || prepared != 0 || built != 0 || gates != 0 { |
| 52 | t.Fatalf("unreserved direct launch: cmd=%v prepared=%d built=%d gates=%d err=%v", cmd, prepared, built, gates, err) |
| 53 | } |
| 54 | } else if strict == "strict" { |
| 55 | if !errors.Is(err, ErrStrictRequired) || built != 0 || gates != 0 { |
| 56 | t.Fatalf("legacy strict boundary: %v", err) |
| 57 | } |
| 58 | } else if err != nil || cmd == nil || built != 1 || gates != 1 { |
| 59 | t.Fatalf("legacy spawn: cmd=%v built=%d gates=%d err=%v", cmd, built, gates, err) |
| 60 | } |
| 61 | }) |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | type generationBackendControl struct{ identityControl } |
| 68 | |
| 69 | func (c generationBackendControl) StartGen(_ context.Context, host string, n uint64, max int64) (string, error) { |
| 70 | c.rec(fmt.Sprintf("startgen %s %d %d", host, n, max)) |
| 71 | return c.inv, nil |
| 72 | } |
| 73 | |
| 74 | func TestPlan14GenerationBackendControlBoundary(t *testing.T) { |
| 75 | for _, claimed := range []bool{false, true} { |
| 76 | for _, gen := range []uint64{0, 7} { |
| 77 | for _, pinned := range []bool{false, true} { |
| 78 | t.Run(fmt.Sprintf("claim=%t/gen=%d/pinned=%t", claimed, gen, pinned), func(t *testing.T) { |
| 79 | ctl := &fakeControl{inv: "inv1"} |
| 80 | b := testExecBackend(t) |
| 81 | b.Cfg.HostWrites.Enabled = claimed |
| 82 | b.Control = generationBackendControl{identityControl{ctl}} |
| 83 | var exe *instancectl.StagedExe |
| 84 | if pinned { |
| 85 | exe = &instancectl.StagedExe{Path: "/unused/staged", Version: "v1", Digest: strings.Repeat("a", 64)} |
| 86 | } |
| 87 | inv, err := b.startRole(t.Context(), registry.Route{Host: "direct.example"}, instancectl.RoleRun, gen, exe, cgroup.Limit{}) |
| 88 | if claimed { |
| 89 | if !errors.Is(err, ErrClaimIsolationUnavailable) || inv != "" || ctl.joined() != "" { |
| 90 | t.Fatalf("unreserved control launch: inv=%q calls=%q err=%v", inv, ctl.joined(), err) |
| 91 | } |
| 92 | } else if err != nil || inv != "inv1" || ctl.joined() == "" { |
| 93 | t.Fatalf("legacy control launch: inv=%q calls=%q err=%v", inv, ctl.joined(), err) |
| 94 | } |
| 95 | }) |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | func TestPlan14GenerationBackendAuxiliaryBoundary(t *testing.T) { |
| 102 | t.Parallel() |
| 103 | for _, claimed := range []bool{false, true} { |
| 104 | for _, role := range []string{"seed", "sidecar"} { |
| 105 | for _, pinned := range []bool{false, true} { |
| 106 | t.Run(fmt.Sprintf("claim=%t/%s/pinned=%t", claimed, role, pinned), func(t *testing.T) { |
| 107 | ctl := &fakeControl{inv: "inv1", seed: instancectl.SeedResponse{InvocationID: "s1", Result: "success", Status: "0", Code: "exited"}} |
| 108 | unit := &fakeUnit{} |
| 109 | b, rt := nestedBackend(t, ctl, unit) |
| 110 | b.Cfg.HostWrites.Enabled = claimed |
| 111 | b.Control = identityControl{ctl} |
| 112 | rt.Seed = `["seed"]` |
| 113 | stageVersion(t, b, rt.Host, "v1", "exit 0\n") |
| 114 | id, err := b.LiveIdentity(t.Context(), rt) |
| 115 | if err != nil { |
| 116 | t.Fatal(err) |
| 117 | } |
| 118 | var exe *instancectl.StagedExe |
| 119 | if pinned { |
| 120 | exe = stagedExeOf(id) |
| 121 | } |
| 122 | var p Proc |
| 123 | if role == "seed" { |
| 124 | err = b.seedUnit(t.Context(), rt, exe, cgroup.Limit{}) |
| 125 | } else { |
| 126 | p, err = b.startSidecarUnitFrom(t.Context(), rt, exe, cgroup.Limit{}) |
| 127 | } |
| 128 | calls := ctl.joined() |
| 129 | if p != nil { |
| 130 | unit.set("inactive", "inv1", false, false) |
| 131 | waitFor(t, "sidecar exit", p.Exited) |
| 132 | if stop := p.Stop(t.Context()); stop != nil { |
| 133 | t.Fatal(stop) |
| 134 | } |
| 135 | } |
| 136 | if claimed { |
| 137 | if !errors.Is(err, ErrClaimIsolationUnavailable) || p != nil || calls != "" { |
| 138 | t.Fatalf("unreserved auxiliary launch: proc=%v calls=%q err=%v", p, calls, err) |
| 139 | } |
| 140 | } else if err != nil || calls == "" { |
| 141 | t.Fatalf("legacy auxiliary launch: calls=%q err=%v", calls, err) |
| 142 | } |
| 143 | }) |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | |