carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package activator
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/cgroup"
11)
12
13func (a *Service) parentRaisedForTest(host string) bool {
14 a.mu.Lock()
15 defer a.mu.Unlock()
16 e := a.entries[host]
17 return e != nil && e.ops.raised
18}
19
20// Final review F11(a): a raise that failed partway (memory.max written, a
21// later write refused) may have changed the parent, so the overlap's
22// cleanup restores it. The parent is marked potentially changed before the
23// raise is asked.
24func TestSwapOverlapRestoresAPartlyRaisedParent(t *testing.T) {
25 f := newSwapFixture(t)
26 f.b.gmu.Lock()
27 f.b.raiseErr = errors.New("cgroup: parent memory.swap.max: invalid argument")
28 f.b.gmu.Unlock()
29 res := f.a.Swap(context.Background(), f.attempt(ModeOverlap), f.newest)
30 if res.Outcome != SwapAbandoned || !strings.Contains(res.Reason, "raise parent limit") {
31 t.Fatalf("Swap = %+v, want abandoned at the raise", res)
32 }
33 gens, raises, restores := f.b.calls()
34 if gens != 0 || raises != 1 || restores != 1 {
35 t.Fatalf("StartStaged %d, raises %d, restores %d: a partly raised parent must be restored", gens, raises, restores)
36 }
37 if f.a.parentRaisedForTest(f.host) {
38 t.Fatal("the restored parent is still marked raised")
39 }
40}
41
42// Final review F11(b): a restore that fails after the last member left
43// keeps its obligation. The parent stays marked raised, no new overlap is
44// admitted onto it (it would raise again under a pending restore), and
45// the sweep retries the restore, outside a.mu, until it lands.
46func TestSwapOverlapRetriesAFailedParentRestore(t *testing.T) {
47 t.Parallel()
48 f := newSwapFixture(t)
49 f.b.gmu.Lock()
50 f.b.restoreFails = 1
51 f.b.gmu.Unlock()
52 if res := f.a.Swap(context.Background(), f.attempt(ModeOverlap), f.newest); res.Outcome != SwapDone {
53 t.Fatalf("Swap = %+v", res)
54 }
55 if _, _, restores := f.b.calls(); restores != 1 {
56 t.Fatalf("restores = %d, want the one that failed", restores)
57 }
58 if !f.a.parentRaisedForTest(f.host) {
59 t.Fatal("a failed restore dropped its obligation: the parent is no longer marked raised")
60 }
61 if _, members := f.a.genStateForTest(f.host); len(members) != 0 {
62 t.Fatalf("members = %v: the retired generation is proven, it leaves the set", members)
63 }
64 f.rev.Add(1)
65 if res := f.a.Swap(context.Background(), f.attempt(ModeOverlap), f.newest); res.Outcome != SwapAbandoned || !strings.Contains(res.Reason, errSwapBusy.Error()) {
66 t.Fatalf("Swap over a pending restore = %+v, want abandoned: busy", res)
67 }
68 f.a.sweepOnce()
69 waitFor(t, "the restore retry", func() bool { return !f.a.parentRaisedForTest(f.host) })
70 if _, raises, restores := f.b.calls(); raises != 1 || restores != 2 {
71 t.Fatalf("raises %d, restores %d: want one raise and the retried restore", raises, restores)
72 }
73}
74
75// A raise refused before any write (no generation containment, no
76// delegated manager) changed nothing: no restore is owed, and the next
77// overlap is not held busy by an obligation that could never clear.
78func TestSwapOverlapOwesNoRestoreForARaiseRefusedBeforeAnyWrite(t *testing.T) {
79 t.Parallel()
80 for _, refusal := range []error{errNoGenContainment, cgroup.ErrNoDelegation} {
81 f := newSwapFixture(t)
82 f.b.gmu.Lock()
83 f.b.raiseErr = fmt.Errorf("parent: %w", refusal)
84 f.b.gmu.Unlock()
85 if res := f.a.Swap(context.Background(), f.attempt(ModeOverlap), f.newest); res.Outcome != SwapAbandoned {
86 t.Fatalf("%v: Swap = %+v, want abandoned", refusal, res)
87 }
88 if _, raises, restores := f.b.calls(); raises != 1 || restores != 0 {
89 t.Fatalf("%v: raises %d, restores %d: want no restore", refusal, raises, restores)
90 }
91 if f.a.parentRaisedForTest(f.host) {
92 t.Fatalf("%v: an untouched parent is marked raised", refusal)
93 }
94 }
95}
96