carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "errors"
6 "testing"
7)
8
9// Issue #315: a pass that cannot restart a named unit (the agent, User=carlos)
10// hands that route to root before it stages or repoints anything. The live
11// link keeps naming the running build, the version is not poisoned for the
12// other hosts in the pass, and root's pass then installs it normally.
13func TestRootRestartsUnitsHandsNamedUnitsToRoot(t *testing.T) {
14 ctx := context.Background()
15 s := NewDirStore(t.TempDir())
16 priv, pub := testKeys(t)
17 opt := t.TempDir()
18 reg := testRegistry(t)
19 t.Cleanup(func() { reg.Close() })
20 const consoleHost, appHost = "console.example.com", "shop.example.com"
21 for _, ver := range []string{"v1", "v2"} {
22 shipRelease(t, s, "bab", "myapp", ver, adoptPlatform, []byte(ver))
23 }
24 if err := Promote(ctx, s, priv, "bab", "myapp", "canary/x", "v1"); err != nil {
25 t.Fatal(err)
26 }
27 addRoute(t, reg, consoleHost, "myapp", "canary/x", false)
28 addRoute(t, reg, appHost, "myapp", "canary/x", false)
29 if err := Pass(ctx, reg, s, pub, opt, nil); err != nil {
30 t.Fatal(err)
31 }
32 if err := reg.SetUnit(consoleHost, "console.service"); err != nil {
33 t.Fatal(err)
34 }
35 if err := Promote(ctx, s, priv, "bab", "myapp", "canary/x", "v2"); err != nil {
36 t.Fatal(err)
37 }
38
39 var restarted []string
40 restart := func(host string) error {
41 restarted = append(restarted, host)
42 if host == consoleHost {
43 return errors.New("try-restart console.service: Interactive authentication required")
44 }
45 return nil
46 }
47 type req struct {
48 host, ver string
49 err error
50 }
51 var reqs []req
52 relay := func(host, ver string, err error) { reqs = append(reqs, req{host, ver, err}) }
53
54 if err := Pass(ctx, reg, s, pub, opt, restart, OnStageBlocked(relay), RootRestartsUnits()); err != nil {
55 t.Fatal(err)
56 }
57 if len(reqs) != 1 || reqs[0].host != consoleHost || reqs[0].ver != "v2" || !errors.Is(reqs[0].err, ErrUnitRestartNeedsRoot) {
58 t.Fatalf("relays = %+v, want one for %s v2 with ErrUnitRestartNeedsRoot", reqs, consoleHost)
59 }
60 if len(restarted) != 1 || restarted[0] != appHost {
61 t.Fatalf("restarted = %v, want only %s", restarted, appHost)
62 }
63 if ver, err := LiveVersion(opt, consoleHost); err != nil || ver != "v1" {
64 t.Fatalf("%s live = %q, %v; want v1 (untouched)", consoleHost, ver, err)
65 }
66 if rt, err := reg.Get(consoleHost); err != nil || rt == nil || rt.Version != "v1" {
67 t.Fatalf("%s route = %+v, %v; want stamped v1", consoleHost, rt, err)
68 }
69 if ver, err := LiveVersion(opt, appHost); err != nil || ver != "v2" {
70 t.Fatalf("%s live = %q, %v; want v2 (not poisoned by the hand-off)", appHost, ver, err)
71 }
72 if r, err := ReadRefusal(opt, consoleHost); err != nil || r != nil {
73 t.Fatalf("refusal for %s = %+v, %v; want none", consoleHost, r, err)
74 }
75
76 // Root's pass, without the option, installs it.
77 restarted = nil
78 rootRestart := func(host string) error { restarted = append(restarted, host); return nil }
79 if err := Pass(ctx, reg, s, pub, opt, rootRestart); err != nil {
80 t.Fatal(err)
81 }
82 if ver, err := LiveVersion(opt, consoleHost); err != nil || ver != "v2" || len(restarted) != 1 || restarted[0] != consoleHost {
83 t.Fatalf("root pass: %s live = %q, %v, restarted %v; want v2 and one restart", consoleHost, ver, err, restarted)
84 }
85}
86
87// The option hands nothing over for a route this pass would not restart, and
88// leaves routes with no unit to the pass.
89func TestRootRestartsUnitsNeedsANamedUnitAndARestart(t *testing.T) {
90 ctx := context.Background()
91 s := NewDirStore(t.TempDir())
92 priv, pub := testKeys(t)
93 opt := t.TempDir()
94 reg := testRegistry(t)
95 t.Cleanup(func() { reg.Close() })
96 const host = "console.example.com"
97 shipRelease(t, s, "bab", "myapp", "v1", adoptPlatform, []byte("v1"))
98 if err := Promote(ctx, s, priv, "bab", "myapp", "canary/x", "v1"); err != nil {
99 t.Fatal(err)
100 }
101 addRoute(t, reg, host, "myapp", "canary/x", false)
102 if err := reg.SetUnit(host, "console.service"); err != nil {
103 t.Fatal(err)
104 }
105 relays := 0
106 relay := func(string, string, error) { relays++ }
107 // restart nil: a pure repoint, which needs no unit restart.
108 if err := Pass(ctx, reg, s, pub, opt, nil, OnStageBlocked(relay), RootRestartsUnits()); err != nil {
109 t.Fatal(err)
110 }
111 if ver, err := LiveVersion(opt, host); err != nil || ver != "v1" || relays != 0 {
112 t.Fatalf("live = %q, %v, relays %d; want v1 and no relay", ver, err, relays)
113 }
114}
115
116// Astra review of #315: a route with no unit of its own whose process is a
117// named unit (same account, app, channel and upstream) is root's too. Stamped
118// by the agent it would claim a build console.service does not run.
119func TestRootRestartsUnitsHandsTheProcessGroupToRoot(t *testing.T) {
120 ctx, reg, s, pub, opt := startUnitFixture(t, "console.example.com")
121 if err := reg.SetUnit("console.example.com", ""); err != nil {
122 t.Fatal(err)
123 }
124 groupFixtureRoute(t, reg, "platform.example.com", "", "127.0.0.1:8090", "console.service")
125 if err := reg.SetUpstreams("console.example.com", "127.0.0.1:8090", "", ""); err != nil {
126 t.Fatal(err)
127 }
128 if err := PointLive(opt, "platform.example.com", "bab", "old", "console"); err != nil {
129 t.Fatal(err)
130 }
131 var relayed []string
132 relay := func(host, ver string, err error) {
133 if errors.Is(err, ErrUnitRestartNeedsRoot) {
134 relayed = append(relayed, host)
135 }
136 }
137 restart := func(host string) error {
138 t.Errorf("restart(%s): every host here is the unit's", host)
139 return nil
140 }
141 if err := Pass(ctx, reg, s, pub, opt, restart, OnStageBlocked(relay), RootRestartsUnits()); err != nil {
142 t.Fatal(err)
143 }
144 if len(relayed) != 2 {
145 t.Fatalf("relayed = %v, want both hosts", relayed)
146 }
147 for _, h := range []string{"console.example.com", "platform.example.com"} {
148 if ver, err := LiveVersion(opt, h); err != nil || ver != "old" {
149 t.Fatalf("%s live = %q, %v; want old", h, ver, err)
150 }
151 }
152}
153