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 "testing"
7 "time"
8)
9
10func TestScheduledTokenFenceRefusesPreparation(t *testing.T) {
11 a := &Service{}
12 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
13 defer cancel()
14 if _, err := a.FenceAndStop(ctx, "token.example.com", 1); !errors.Is(err, ErrStopUnproven) {
15 t.Fatalf("missing entry: %v", err)
16 }
17 called := false
18 tok, err := a.PrepareScheduledToken("token.example.com", func() (string, error) {
19 called = true
20 return "secret", nil
21 })
22 if called || tok != "" || !errors.Is(err, ErrFenced) {
23 t.Fatalf("fenced preparation: called=%v token-present=%v err=%v", called, tok != "", err)
24 }
25 want := errors.New("prepare failed")
26 _, err = a.PrepareScheduledToken("sibling.example.com", func() (string, error) { return "", want })
27 if !errors.Is(err, want) {
28 t.Fatalf("unfenced sibling: %v", err)
29 }
30}
31
32func TestScheduledTokenPreparationOrdersFence(t *testing.T) {
33 t.Parallel()
34 const host = "token.example.com"
35 a := &Service{stopHistory: map[string]*stopObservation{host: {
36 mainStop: true, mainExited: true, sidecarStop: true, sidecarExited: true, replicaClosed: true,
37 }}}
38 entered, release := make(chan struct{}), make(chan struct{})
39 done := make(chan error, 1)
40 go func() {
41 _, err := a.PrepareScheduledToken(host, func() (string, error) {
42 close(entered)
43 <-release
44 return "token", nil
45 })
46 done <- err
47 }()
48 select {
49 case <-entered:
50 case <-time.After(time.Second):
51 t.Fatal("preparation did not start")
52 }
53 // Even with prior checked-stop history, a deadline cannot establish a
54 // fence while previously admitted token I/O is still running.
55 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
56 proof, err := a.FenceAndStop(ctx, host, 1)
57 cancel()
58 a.mu.Lock()
59 installed := a.fences[host]
60 a.mu.Unlock()
61 close(release)
62 if installed != 0 {
63 t.Fatal("fence installed before admitted IO finished")
64 }
65 if proof != nil || !errors.Is(err, ErrFenceNotInstalled) {
66 t.Fatalf("fence crossed active preparation: proof=%v err=%v", proof != nil, err)
67 }
68 select {
69 case err := <-done:
70 if err != nil {
71 t.Fatal(err)
72 }
73 case <-time.After(time.Second):
74 t.Fatal("preparation did not finish")
75 }
76 ctx, cancel = context.WithTimeout(context.Background(), time.Second)
77 defer cancel()
78 proof, err = a.FenceAndStop(ctx, host, 1)
79 if err != nil || !proof.Matches(host, 1) {
80 t.Fatalf("fence after preparation: %v", err)
81 }
82 _, err = a.PrepareScheduledToken(host, func() (string, error) {
83 t.Error("post-fence callback ran")
84 return "", nil
85 })
86 if !errors.Is(err, ErrFenced) {
87 t.Fatal(err)
88 }
89}
90
91func TestScheduledTokenInvalidCallback(t *testing.T) {
92 for _, a := range []*Service{nil, {}} {
93 if _, err := a.PrepareScheduledToken("token.example.com", nil); !errors.Is(err, ErrFenced) {
94 t.Fatalf("nil callback: %v", err)
95 }
96 }
97}
98