carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package instancectl
2
3import (
4 "context"
5 "net/http"
6 "strings"
7 "testing"
8 "time"
9)
10
11// #292, Astra round 2 P1: cancelling the agent's /start does not join
12// root's start handler, which can still submit its systemd start after a
13// /stop for the same host has answered, if it has not yet seen the
14// disconnect. The agent then proves the unit stopped, gives the lease
15// back, and the late job launches a writer no lease covers. Here the
16// handler is held before its submit, past the client's cancellation, and
17// does not see that cancellation (unseenCancel): no start may be submitted
18// once the /stop has returned.
19func TestALegacyStartDelayedPastItsCancellationNeverSubmitsAfterAStop(t *testing.T) {
20 host := "late.example.com"
21 unit := UnitName(RoleRun, host)
22 entered, gate := make(chan struct{}), make(chan struct{})
23 ctl := &fakeCtl{
24 answers: map[string]string{"show -p InvocationID --value": "inv1\n"},
25 unseenCancel: true,
26 before: map[string]func(){"set-property": func() {
27 close(entered)
28 <-gate
29 }},
30 }
31 _, c := newServer(t, ctl)
32
33 startCtx, cancelStart := context.WithCancel(context.Background())
34 startDone := make(chan struct{})
35 go func() {
36 defer close(startDone)
37 req, _ := http.NewRequestWithContext(startCtx, http.MethodPost, "http://unix/start?host="+host+"&role=run&memory_max=536870912", nil)
38 if resp, err := c.Do(req); err == nil {
39 resp.Body.Close()
40 }
41 }()
42 select {
43 case <-entered:
44 case <-time.After(5 * time.Second):
45 t.Fatal("the start handler never reached its tier step")
46 }
47 cancelStart() // the client gives up; the handler has not noticed
48 <-startDone
49
50 stopped := make(chan int, 1)
51 go func() {
52 resp := post(t, c, "/stop?host="+host+"&role=run")
53 resp.Body.Close()
54 stopped <- resp.StatusCode
55 }()
56 // The held handler goes on past the cancellation it has not seen.
57 time.Sleep(100 * time.Millisecond)
58 close(gate)
59 var status int
60 select {
61 case status = <-stopped:
62 case <-time.After(15 * time.Second):
63 t.Fatal("/stop never returned")
64 }
65 if status != http.StatusNoContent {
66 t.Fatalf("/stop status %d, want 204", status)
67 }
68 stopAt := len(strings.Split(ctl.joined(), "\n"))
69
70 // Give the held handler every chance to submit late.
71 deadline := time.Now().Add(time.Second)
72 for time.Now().Before(deadline) {
73 calls := strings.Split(ctl.joined(), "\n")
74 for _, line := range calls[stopAt:] {
75 if line == "start "+unit {
76 t.Fatalf("a start was submitted after /stop returned; calls:\n%s", ctl.joined())
77 }
78 }
79 time.Sleep(20 * time.Millisecond)
80 }
81 calls := ctl.joined()
82 stopIdx := strings.Index(calls, "stop "+unit)
83 if stopIdx < 0 {
84 t.Fatalf("the unit was never stopped; calls:\n%s", calls)
85 }
86 if i := strings.LastIndex(calls, "start "+unit); i > stopIdx {
87 t.Fatalf("a start was submitted after the stop; calls:\n%s", calls)
88 }
89}
90
91// A start whose own request was accepted before a /stop for the host is an
92// earlier request: refused once that stop has run, even if its handler only
93// gets going afterwards.
94func TestALegacyStartFromBeforeAStopIsRefused(t *testing.T) {
95 host := "early.example.com"
96 ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "inv1\n"}}
97 s, _ := newServer(t, ctl)
98 // Stamp two connections in accept order: the start's first.
99 startSeq := s.nextConnSeq()
100 _ = s.nextConnSeq() // the stop's own connection
101 release, err := s.fenceLegacyStarts(context.Background(), host)
102 if err != nil {
103 t.Fatalf("fence: %v", err)
104 }
105 release()
106 if _, _, err := s.beginLegacyStart(context.Background(), host, startSeq); err == nil {
107 t.Fatal("a start accepted before the stop was admitted after it")
108 }
109 if _, finish, err := s.beginLegacyStart(context.Background(), host, s.nextConnSeq()); err != nil {
110 t.Fatalf("a start accepted after the stop was refused: %v", err)
111 } else {
112 finish()
113 }
114}
115