blob: b11a05306561f86e7f30887db8f995c6b5e92600 [file] [log] [blame]
Serge Bazanskicb883e22020-07-06 17:47:55 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package consensus
18
19import (
20 "bytes"
21 "context"
Serge Bazanskif05e80a2021-10-12 11:53:34 +020022 "crypto/ed25519"
23 "crypto/rand"
Serge Bazanskicb883e22020-07-06 17:47:55 +020024 "os"
25 "testing"
Serge Bazanskicb883e22020-07-06 17:47:55 +020026
Serge Bazanski31370b02021-01-07 16:31:14 +010027 "source.monogon.dev/metropolis/node/core/localstorage"
28 "source.monogon.dev/metropolis/node/core/localstorage/declarative"
Serge Bazanski31370b02021-01-07 16:31:14 +010029 "source.monogon.dev/metropolis/pkg/supervisor"
Serge Bazanskicb883e22020-07-06 17:47:55 +020030)
31
32type boilerplate struct {
Serge Bazanskif05e80a2021-10-12 11:53:34 +020033 ctx context.Context
34 ctxC context.CancelFunc
35 root *localstorage.Root
36 privkey ed25519.PrivateKey
37 tmpdir string
Serge Bazanskicb883e22020-07-06 17:47:55 +020038}
39
40func prep(t *testing.T) *boilerplate {
Serge Bazanskif05e80a2021-10-12 11:53:34 +020041 t.Helper()
Serge Bazanskicb883e22020-07-06 17:47:55 +020042 ctx, ctxC := context.WithCancel(context.Background())
43 root := &localstorage.Root{}
Serge Bazanskif12bedf2021-01-15 16:58:50 +010044 // Force usage of /tmp as temp directory root, otherwsie TMPDIR from Bazel
45 // returns a path long enough that socket binds in the localstorage fail
46 // (as socket names are limited to 108 characters).
Lorenz Brun764a2de2021-11-22 16:26:36 +010047 tmp, err := os.MkdirTemp("/tmp", "metropolis-consensus-test")
Serge Bazanskicb883e22020-07-06 17:47:55 +020048 if err != nil {
49 t.Fatal(err)
50 }
51 err = declarative.PlaceFS(root, tmp)
52 if err != nil {
53 t.Fatal(err)
54 }
55 os.MkdirAll(root.Data.Etcd.FullPath(), 0700)
56 os.MkdirAll(root.Ephemeral.Consensus.FullPath(), 0700)
57
Serge Bazanskif05e80a2021-10-12 11:53:34 +020058 _, privkey, err := ed25519.GenerateKey(rand.Reader)
59 if err != nil {
60 t.Fatal(err)
61 }
62
Serge Bazanskicb883e22020-07-06 17:47:55 +020063 return &boilerplate{
Serge Bazanskif05e80a2021-10-12 11:53:34 +020064 ctx: ctx,
65 ctxC: ctxC,
66 root: root,
67 privkey: privkey,
68 tmpdir: tmp,
Serge Bazanskicb883e22020-07-06 17:47:55 +020069 }
70}
71
72func (b *boilerplate) close() {
73 b.ctxC()
74 os.RemoveAll(b.tmpdir)
75}
76
Serge Bazanskicb883e22020-07-06 17:47:55 +020077func TestBootstrap(t *testing.T) {
78 b := prep(t)
79 defer b.close()
80 etcd := New(Config{
Serge Bazanskif05e80a2021-10-12 11:53:34 +020081 Data: &b.root.Data.Etcd,
82 Ephemeral: &b.root.Ephemeral.Consensus,
83 NodePrivateKey: b.privkey,
84 testOverrides: testOverrides{
85 externalPort: 1234,
86 },
Serge Bazanskicb883e22020-07-06 17:47:55 +020087 })
88
Serge Bazanskif05e80a2021-10-12 11:53:34 +020089 ctxC, _ := supervisor.TestHarness(t, etcd.Run)
90 defer ctxC()
Serge Bazanskicb883e22020-07-06 17:47:55 +020091
Serge Bazanskif05e80a2021-10-12 11:53:34 +020092 w := etcd.Watch()
93 st, err := w.Get(b.ctx)
94 if err != nil {
95 t.Fatalf("Get: %v", err)
96 }
Serge Bazanski5839e972021-11-16 15:46:19 +010097 cl, err := st.CuratorClient()
Serge Bazanskif05e80a2021-10-12 11:53:34 +020098 if err != nil {
Serge Bazanski5839e972021-11-16 15:46:19 +010099 t.Fatalf("CuratorClient: %v", err)
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200100 }
101 defer cl.Close()
102
103 if _, err := cl.Put(b.ctx, "/foo", "bar"); err != nil {
Serge Bazanskicb883e22020-07-06 17:47:55 +0200104 t.Fatalf("test key creation failed: %v", err)
105 }
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200106 if _, err := cl.Get(b.ctx, "/foo"); err != nil {
Serge Bazanskicb883e22020-07-06 17:47:55 +0200107 t.Fatalf("test key retrieval failed: %v", err)
108 }
109}
110
Serge Bazanskicb883e22020-07-06 17:47:55 +0200111func TestRestartFromDisk(t *testing.T) {
112 b := prep(t)
113 defer b.close()
114
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200115 // Start once.
116 etcd := New(Config{
117 Data: &b.root.Data.Etcd,
118 Ephemeral: &b.root.Ephemeral.Consensus,
119 NodePrivateKey: b.privkey,
120 testOverrides: testOverrides{
121 externalPort: 1235,
122 },
123 })
124 ctxC, _ := supervisor.TestHarness(t, etcd.Run)
125 defer ctxC()
Serge Bazanski79fc1e92021-07-06 16:25:22 +0200126
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200127 w := etcd.Watch()
128 st, err := w.Get(b.ctx)
129 if err != nil {
130 t.Fatalf("status get failed: %v", err)
131 }
Serge Bazanski5839e972021-11-16 15:46:19 +0100132 cl, err := st.CuratorClient()
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200133 if err != nil {
Serge Bazanski5839e972021-11-16 15:46:19 +0100134 t.Fatalf("CuratorClient: %v", err)
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200135 }
136 defer cl.Close()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200137
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200138 if _, err := cl.Put(b.ctx, "/foo", "bar"); err != nil {
139 t.Fatalf("test key creation failed: %v", err)
140 }
141 firstCA, err := etcd.config.Data.PeerPKI.CACertificate.Read()
142 if err != nil {
143 t.Fatalf("could not read CA file: %v", err)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200144 }
145
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200146 // Stop and wait until reported stopped.
Serge Bazanskicb883e22020-07-06 17:47:55 +0200147 ctxC()
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200148 ctxWait, ctxWaitC := context.WithCancel(context.Background())
149 for {
150 if st.stopped {
151 break
152 }
153 st, err = w.Get(ctxWait)
154 if err != nil {
155 t.Fatalf("status get failed: %v", err)
156 }
157 if st.stopped {
158 break
159 }
160 }
161 ctxWaitC()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200162
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200163 // Restart.
164 etcd = New(Config{
165 Data: &b.root.Data.Etcd,
166 Ephemeral: &b.root.Ephemeral.Consensus,
167 NodePrivateKey: b.privkey,
168 testOverrides: testOverrides{
169 externalPort: 1235,
170 },
171 })
172 ctxC, _ = supervisor.TestHarness(t, etcd.Run)
173 defer ctxC()
174
175 w = etcd.Watch()
176 st, err = w.Get(b.ctx)
177 if err != nil {
178 t.Fatalf("status get failed: %v", err)
179 }
Serge Bazanski5839e972021-11-16 15:46:19 +0100180 cl, err = st.CuratorClient()
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200181 if err != nil {
Serge Bazanski5839e972021-11-16 15:46:19 +0100182 t.Fatalf("CuratorClient: %v", err)
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200183 }
184 defer cl.Close()
185
186 res, err := cl.Get(b.ctx, "/foo")
187 if err != nil {
188 t.Fatalf("test key retrieval failed: %v", err)
189 }
190 if len(res.Kvs) != 1 || string(res.Kvs[0].Value) != "bar" {
191 t.Fatalf("test key value missing: %v", res.Kvs)
192 }
193
194 secondCA, err := etcd.config.Data.PeerPKI.CACertificate.Read()
195 if err != nil {
196 t.Fatalf("could not read CA file: %v", err)
197 }
Serge Bazanskicb883e22020-07-06 17:47:55 +0200198 ctxC()
199
200 if bytes.Compare(firstCA, secondCA) != 0 {
201 t.Fatalf("wanted same, got different CAs accross runs")
202 }
203}
204
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200205func TestJoin(t *testing.T) {
Serge Bazanskicb883e22020-07-06 17:47:55 +0200206 b := prep(t)
207 defer b.close()
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200208
209 // Start first node and perform write.
Serge Bazanskicb883e22020-07-06 17:47:55 +0200210 etcd := New(Config{
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200211 Data: &b.root.Data.Etcd,
212 Ephemeral: &b.root.Ephemeral.Consensus,
213 NodePrivateKey: b.privkey,
214 testOverrides: testOverrides{
215 externalPort: 3000,
216 externalAddress: "localhost",
217 },
Serge Bazanskicb883e22020-07-06 17:47:55 +0200218 })
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200219 ctxC, _ := supervisor.TestHarness(t, etcd.Run)
220 defer ctxC()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200221
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200222 w := etcd.Watch()
223 st, err := w.Get(b.ctx)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200224 if err != nil {
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200225 t.Fatalf("could not get status: %v", err)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200226 }
Serge Bazanski5839e972021-11-16 15:46:19 +0100227 cl, err := st.CuratorClient()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200228 if err != nil {
Serge Bazanski5839e972021-11-16 15:46:19 +0100229 t.Fatalf("CuratorClient: %v", err)
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200230 }
231 defer cl.Close()
232 if _, err := cl.Put(b.ctx, "/foo", "bar"); err != nil {
233 t.Fatalf("test key creation failed: %v", err)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200234 }
235
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200236 // Start second node and ensure data is present.
237 b2 := prep(t)
238 defer b2.close()
239
240 join, err := st.AddNode(b.ctx, b2.privkey.Public().(ed25519.PublicKey), &AddNodeOption{
241 externalAddress: "localhost",
242 externalPort: 3001,
243 })
244 if err != nil {
245 t.Fatalf("could not add node: %v", err)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200246 }
247
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200248 etcd2 := New(Config{
249 Data: &b2.root.Data.Etcd,
250 Ephemeral: &b2.root.Ephemeral.Consensus,
251 NodePrivateKey: b2.privkey,
252 JoinCluster: join,
253 testOverrides: testOverrides{
254 externalPort: 3001,
255 externalAddress: "localhost",
256 },
257 })
258 ctxC, _ = supervisor.TestHarness(t, etcd2.Run)
259 defer ctxC()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200260
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200261 w2 := etcd2.Watch()
262 st2, err := w2.Get(b.ctx)
263 if err != nil {
264 t.Fatalf("could not get status: %v", err)
265 }
Serge Bazanski5839e972021-11-16 15:46:19 +0100266 cl2, err := st2.CuratorClient()
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200267 if err != nil {
Serge Bazanski5839e972021-11-16 15:46:19 +0100268 t.Fatalf("CuratorClient: %v", err)
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200269 }
270 defer cl2.Close()
Serge Bazanskicb883e22020-07-06 17:47:55 +0200271
Serge Bazanskif05e80a2021-10-12 11:53:34 +0200272 res, err := cl2.Get(b.ctx, "/foo")
273 if err != nil {
274 t.Fatalf("test key retrieval failed: %v", err)
275 }
276 if len(res.Kvs) != 1 || string(res.Kvs[0].Value) != "bar" {
277 t.Fatalf("test key value missing: %v", res.Kvs)
Serge Bazanskicb883e22020-07-06 17:47:55 +0200278 }
279}