carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "errors"
6 "net/http"
7 "os"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/hostclaim"
13 "github.com/carlosframework/platform/internal/provision"
14)
15
16// mc19 is M-C19 for the fixture's host.
17const mc19 = "acme.go.tito.io is being moved. Delete it after the move finishes, or abort the move first."
18
19func recordExists(t *testing.T, fx *consoleFixture) bool {
20 t.Helper()
21 _, err := provision.GetRecord(context.Background(), fx.rel, "bab", "titogo", "acme.go.tito.io")
22 if errors.Is(err, os.ErrNotExist) {
23 return false
24 }
25 if err != nil {
26 t.Fatal(err)
27 }
28 return true
29}
30
31// Task 2b (preflight L6): with the switch on, a console without the host
32// claim refuses to delete an instance whose record carries a live move,
33// with M-C19, and deletes nothing. Once the move has closed the delete is
34// main's.
35func TestMoveDeleteRefusesDuringAMove(t *testing.T) {
36 fx, token, moveURL := moveDoorWithClaim(t, "code-inst-move-del")
37 instURL := strings.TrimSuffix(moveURL, "/move")
38 readMoveAnswer(t, http.MethodPost, moveURL, token, `{"region":"uk","cut":"manual"}`, http.StatusAccepted)
39 for _, state := range []string{provision.MovePreparing, provision.MoveCommitted, provision.MoveStuck} {
40 setMove(t, fx, func(rec *provision.Record) { rec.Move.State = state })
41 if code, _, out := rawReq(t, http.MethodDelete, instURL, token, ""); code != http.StatusConflict || errorText(out) != mc19 {
42 t.Fatalf("delete during a move in %s = %d %s, want M-C19", state, code, out)
43 }
44 if !recordExists(t, fx) {
45 t.Fatalf("a refused delete in %s removed the record", state)
46 }
47 }
48 mv := recordMove(t, fx)
49 setMove(t, fx, func(rec *provision.Record) { rec.Move.State = provision.MoveAborting })
50 if _, err := provision.SetRecordMove(context.Background(), fx.rel, "bab", "titogo", "acme.go.tito.io",
51 provision.MoveGuardedAbortComplete(provision.MoveGuard{ID: mv.ID, Incarnation: mv.Incarnation}, time.Now())); err != nil {
52 t.Fatal(err)
53 }
54 if code, _, out := rawReq(t, http.MethodDelete, instURL, token, ""); code != http.StatusOK {
55 t.Fatalf("delete after the move closed = %d %s", code, out)
56 }
57 if recordExists(t, fx) {
58 t.Fatal("the delete after the move closed left the record")
59 }
60}
61
62// With the switch off the delete door is main's: a record with a move is
63// deleted as before.
64func TestMoveDeleteSwitchOffIsMains(t *testing.T) {
65 fx, token, moveURL := moveDoorWithClaim(t, "code-inst-move-del-off")
66 instURL := strings.TrimSuffix(moveURL, "/move")
67 setMove(t, fx, func(rec *provision.Record) {
68 rec.Move = &provision.MoveState{To: "uk", State: provision.MovePreparing, RequestedAt: time.Now().UTC()}
69 })
70 fx.srv.cfg.MovesEnabled = false
71 if code, _, out := rawReq(t, http.MethodDelete, instURL, token, ""); code != http.StatusOK {
72 t.Fatalf("switch-off delete during a legacy move = %d %s", code, out)
73 }
74 if recordExists(t, fx) {
75 t.Fatal("the switch-off delete left the record")
76 }
77}
78
79// On a host-claim console a delete during a move meets the claim's pending
80// refusal; the door says M-C19 for it, not the generic busy sentence, when
81// the claim's operation is the move's.
82func TestMoveDeleteUnderHostClaimSaysItIsMoving(t *testing.T) {
83 fx, token, moveURL, _ := j17StartedMove(t, "code-inst-move-del-claim")
84 instURL := strings.TrimSuffix(moveURL, "/move")
85 code, _, out := rawReq(t, http.MethodDelete, instURL, token, "")
86 if code != http.StatusConflict || errorText(out) != mc19 {
87 t.Fatalf("delete under the host claim during a move = %d %s, want M-C19", code, out)
88 }
89 if !recordExists(t, fx) {
90 t.Fatal("a refused delete removed the record")
91 }
92}
93
94// A host-claim console says M-C19 only for a move's claim operation: a
95// serving claim, and a relocation with no move journaled on the record (a
96// region HA relocation of its own), go on to the claim's retire.
97func TestMoveDeleteUnderHostClaimOnlyForAMove(t *testing.T) {
98 fx, token, moveURL := moveDoorWithClaim(t, "code-inst-move-del-claim2")
99 armClaimMoves(t, fx, true, true, nil)
100 instURL := strings.TrimSuffix(moveURL, "/move")
101 if code, _, out := rawReq(t, http.MethodDelete, instURL, token, ""); errorText(out) == mc19 {
102 t.Fatalf("delete of a serving host under the host claim = %d %s", code, out)
103 }
104 rewriteClaim(t, fx, "acme.go.tito.io", func(c *hostclaim.Claim) {
105 c.Op = hostclaim.Op{Kind: hostclaim.KindRelocating, ID: "op-ha", Phase: "pinning", Exec: "none", Intent: map[string]string{"region": "ie", "source_box": "ie-box"}}
106 })
107 if code, _, out := rawReq(t, http.MethodDelete, instURL, token, ""); errorText(out) == mc19 {
108 t.Fatalf("delete during a relocation that is not a move = %d %s", code, out)
109 }
110}
111