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 "math"
5 "strings"
6 "testing"
7
8 "github.com/carlosframework/platform/internal/provision"
9)
10
11func TestGenerationUnitNames(t *testing.T) {
12 const host = "bill-2.example.com"
13 if got := GenInstance(host, 7); got != "bill-2.example.com:7" {
14 t.Errorf("GenInstance = %q", got)
15 }
16 if got := GenUnitName(host, 7); got != `carlos-instance-gen@bill\x2d2.example.com:7.service` {
17 t.Errorf("GenUnitName = %q", got)
18 }
19 esc, n, ok := ParseGenUnitName(GenUnitName(host, 7))
20 if !ok || esc != `bill\x2d2.example.com` || n != 7 {
21 t.Errorf("ParseGenUnitName = %q %d %v", esc, n, ok)
22 }
23 // The sweep's parser knows the generation template, as a run unit in
24 // the host's slice.
25 role, esc, ok := ParseUnitName(GenUnitName(host, 7))
26 if !ok || role != RoleRun || esc != `bill\x2d2.example.com` {
27 t.Errorf("ParseUnitName(gen) = %q %q %v", role, esc, ok)
28 }
29 // Flag off: the original names are unchanged.
30 if got := UnitName(RoleRun, host); got != `carlos-instance@bill\x2d2.example.com.service` {
31 t.Errorf("UnitName(run) = %q", got)
32 }
33 if role, esc, ok := ParseUnitName(`carlos-instance@bill\x2d2.example.com.service`); !ok || role != RoleRun || esc != `bill\x2d2.example.com` {
34 t.Errorf("ParseUnitName(run) = %q %q %v", role, esc, ok)
35 }
36 for _, bad := range []string{"carlos-instance-gen@.service", `carlos-instance-gen@bill.example.com.service`, "carlos-instance-gen@bill.example.com:0.service"} {
37 if _, _, ok := ParseUnitName(bad); ok {
38 t.Errorf("ParseUnitName(%q) accepted", bad)
39 }
40 }
41}
42
43func TestParseGenInstanceIsStrict(t *testing.T) {
44 if iid, n, ok := ParseGenInstance("_abc-123:42"); !ok || iid != "_abc-123" || n != 42 {
45 t.Errorf("ParseGenInstance = %q %d %v", iid, n, ok)
46 }
47 for _, bad := range []string{"x", "x:", ":7", "x:0", "x:07", "x:7a", "a/b:7", "..:7", "x:18446744073709551616", "a:b:7"} {
48 if _, _, ok := ParseGenInstance(bad); ok {
49 t.Errorf("ParseGenInstance(%q) accepted", bad)
50 }
51 }
52}
53
54func TestGenerationFileNames(t *testing.T) {
55 const host = "bill-2.example.com"
56 for got, want := range map[string]string{
57 GenKey(host, 7): "bill-2.example.com@7",
58 GenLogSocket(host, 7): "/run/carlos/logs/bill-2.example.com@7.run.sock",
59 GenLaunchFileName(7): "launch.gen.7.json",
60 GenExitRecordName(7, "abc"): "exit.gen.7.abc.json",
61 GenExitPrefix(7): "exit.gen.7.",
62 } {
63 if got != want {
64 t.Errorf("got %q, want %q", got, want)
65 }
66 }
67 // Never under a legacy prefix: prepare's stale sweep for the run role
68 // removes "exit.run.*" and must not touch a live generation's record.
69 if strings.HasPrefix(GenExitRecordName(7, "abc"), "exit.run.") || GenLaunchFileName(7) == LaunchFileName(RoleRun) {
70 t.Error("a generation file shares a legacy name")
71 }
72}
73
74// D2: no instance ID holds '@', so a generation key never equals a legacy
75// instance ID, whatever the host.
76func TestGenKeyNeverMeetsALegacyName(t *testing.T) {
77 for _, host := range []string{"shop.example.com", "shop.example.com.g7", strings.Repeat("x", 60) + ".example.com"} {
78 if iid := provision.InstanceID(host); strings.Contains(iid, "@") {
79 t.Fatalf("InstanceID(%q) = %q holds '@'", host, iid)
80 }
81 if strings.Count(GenKey(host, 7), "@") != 1 {
82 t.Errorf("GenKey(%q) = %q", host, GenKey(host, 7))
83 }
84 }
85 if GenLogSocket("shop.example.com", 7) == provision.LogSocket("shop.example.com.g7", "run") {
86 t.Error("a generation log socket met a legacy one")
87 }
88}
89
90// Review focus: the longest host and the largest generation stay inside
91// sockaddr_un and systemd's unit-name limit (UNIT_NAME_MAX 256 with NUL).
92func TestGenerationNamesStayBoundedForTheLongestHost(t *testing.T) {
93 host := strings.Repeat("a-b.", 60) + "example.com"
94 n := uint64(math.MaxUint64)
95 if got := GenLogSocket(host, n); len(got) > provision.MaxUnixPath {
96 t.Errorf("GenLogSocket is %d bytes: %q", len(got), got)
97 }
98 unit := GenUnitName(host, n)
99 if len(unit) > 255 {
100 t.Errorf("GenUnitName is %d bytes: %q", len(unit), unit)
101 }
102 if !strings.HasPrefix(GenInstance(host, n), "_") {
103 t.Errorf("GenInstance(long) = %q, want the hashed instance ID", GenInstance(host, n))
104 }
105 esc, m, ok := ParseGenUnitName(unit)
106 iid, uok := Unescape(esc)
107 if !ok || m != n || !uok || iid != provision.InstanceID(host) {
108 t.Errorf("round trip: %q %d %v -> %q %v", esc, m, ok, iid, uok)
109 }
110}
111
112func TestUnescapeInvertsEscape(t *testing.T) {
113 for _, s := range []string{"bill-2.example.com", "_abcdefghijklmnopqrstuv-0123456789abcdef", "a.b"} {
114 if got, ok := Unescape(Escape(s)); !ok || got != s {
115 t.Errorf("Unescape(Escape(%q)) = %q %v", s, got, ok)
116 }
117 }
118 for _, bad := range []string{`a\x2`, `a\y2d`, `a\xzz`} {
119 if _, ok := Unescape(bad); ok {
120 t.Errorf("Unescape(%q) accepted", bad)
121 }
122 }
123}
124
125// ParseGenKey reads a generation's exe directory name back (Task 15's
126// sweep): it inverts GenKey, for a long host's hashed identifier too, and
127// refuses a host's own directory and every name GenKey never makes.
128func TestParseGenKeyInvertsGenKey(t *testing.T) {
129 for _, host := range []string{"bill-2.example.com", strings.Repeat("a", 60) + ".example.com"} {
130 iid, n, ok := ParseGenKey(GenKey(host, 7))
131 if !ok || iid != provision.InstanceID(host) || n != 7 {
132 t.Errorf("ParseGenKey(GenKey(%q, 7)) = %q, %d, %v", host, iid, n, ok)
133 }
134 if got := GenUnitNameOf(iid, n); got != GenUnitName(host, 7) {
135 t.Errorf("GenUnitNameOf = %q, want %q", got, GenUnitName(host, 7))
136 }
137 if unit, ok := genUnitOfExeKey(GenKey(host, 7)); !ok || unit != GenUnitName(host, 7) {
138 t.Errorf("genUnitOfExeKey(GenKey(%q, 7)) = %q, %v", host, unit, ok)
139 }
140 }
141 for _, bad := range []string{"bill-2.example.com", "@7", "a@0", "a@07", "a@x", "a@", "a@b@7", "a:b@7", "a@7:1", ".@7", "..@7"} {
142 if iid, n, ok := ParseGenKey(bad); ok {
143 t.Errorf("ParseGenKey(%q) = %q, %d, true; want refused", bad, iid, n)
144 }
145 }
146}
147