Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 1 | // 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 | |
| 17 | package consensus |
| 18 | |
| 19 | import ( |
| 20 | "bytes" |
| 21 | "context" |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 22 | "crypto/ed25519" |
| 23 | "crypto/rand" |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 24 | "os" |
| 25 | "testing" |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 26 | |
Serge Bazanski | 31370b0 | 2021-01-07 16:31:14 +0100 | [diff] [blame] | 27 | "source.monogon.dev/metropolis/node/core/localstorage" |
| 28 | "source.monogon.dev/metropolis/node/core/localstorage/declarative" |
Serge Bazanski | 31370b0 | 2021-01-07 16:31:14 +0100 | [diff] [blame] | 29 | "source.monogon.dev/metropolis/pkg/supervisor" |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 30 | ) |
| 31 | |
| 32 | type boilerplate struct { |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 33 | ctx context.Context |
| 34 | ctxC context.CancelFunc |
| 35 | root *localstorage.Root |
| 36 | privkey ed25519.PrivateKey |
| 37 | tmpdir string |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 38 | } |
| 39 | |
| 40 | func prep(t *testing.T) *boilerplate { |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 41 | t.Helper() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 42 | ctx, ctxC := context.WithCancel(context.Background()) |
| 43 | root := &localstorage.Root{} |
Serge Bazanski | f12bedf | 2021-01-15 16:58:50 +0100 | [diff] [blame] | 44 | // 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 Brun | 764a2de | 2021-11-22 16:26:36 +0100 | [diff] [blame] | 47 | tmp, err := os.MkdirTemp("/tmp", "metropolis-consensus-test") |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 48 | 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 Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 58 | _, privkey, err := ed25519.GenerateKey(rand.Reader) |
| 59 | if err != nil { |
| 60 | t.Fatal(err) |
| 61 | } |
| 62 | |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 63 | return &boilerplate{ |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 64 | ctx: ctx, |
| 65 | ctxC: ctxC, |
| 66 | root: root, |
| 67 | privkey: privkey, |
| 68 | tmpdir: tmp, |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 69 | } |
| 70 | } |
| 71 | |
| 72 | func (b *boilerplate) close() { |
| 73 | b.ctxC() |
| 74 | os.RemoveAll(b.tmpdir) |
| 75 | } |
| 76 | |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 77 | func TestBootstrap(t *testing.T) { |
| 78 | b := prep(t) |
| 79 | defer b.close() |
| 80 | etcd := New(Config{ |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 81 | Data: &b.root.Data.Etcd, |
| 82 | Ephemeral: &b.root.Ephemeral.Consensus, |
| 83 | NodePrivateKey: b.privkey, |
| 84 | testOverrides: testOverrides{ |
| 85 | externalPort: 1234, |
| 86 | }, |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 87 | }) |
| 88 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 89 | ctxC, _ := supervisor.TestHarness(t, etcd.Run) |
| 90 | defer ctxC() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 91 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 92 | w := etcd.Watch() |
| 93 | st, err := w.Get(b.ctx) |
| 94 | if err != nil { |
| 95 | t.Fatalf("Get: %v", err) |
| 96 | } |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 97 | cl, err := st.CuratorClient() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 98 | if err != nil { |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 99 | t.Fatalf("CuratorClient: %v", err) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 100 | } |
| 101 | defer cl.Close() |
| 102 | |
| 103 | if _, err := cl.Put(b.ctx, "/foo", "bar"); err != nil { |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 104 | t.Fatalf("test key creation failed: %v", err) |
| 105 | } |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 106 | if _, err := cl.Get(b.ctx, "/foo"); err != nil { |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 107 | t.Fatalf("test key retrieval failed: %v", err) |
| 108 | } |
| 109 | } |
| 110 | |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 111 | func TestRestartFromDisk(t *testing.T) { |
| 112 | b := prep(t) |
| 113 | defer b.close() |
| 114 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 115 | // 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 Bazanski | 79fc1e9 | 2021-07-06 16:25:22 +0200 | [diff] [blame] | 126 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 127 | w := etcd.Watch() |
| 128 | st, err := w.Get(b.ctx) |
| 129 | if err != nil { |
| 130 | t.Fatalf("status get failed: %v", err) |
| 131 | } |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 132 | cl, err := st.CuratorClient() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 133 | if err != nil { |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 134 | t.Fatalf("CuratorClient: %v", err) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 135 | } |
| 136 | defer cl.Close() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 137 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 138 | 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 144 | } |
| 145 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 146 | // Stop and wait until reported stopped. |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 147 | ctxC() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 148 | 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 162 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 163 | // 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 Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 180 | cl, err = st.CuratorClient() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 181 | if err != nil { |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 182 | t.Fatalf("CuratorClient: %v", err) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 183 | } |
| 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 198 | ctxC() |
| 199 | |
| 200 | if bytes.Compare(firstCA, secondCA) != 0 { |
| 201 | t.Fatalf("wanted same, got different CAs accross runs") |
| 202 | } |
| 203 | } |
| 204 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 205 | func TestJoin(t *testing.T) { |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 206 | b := prep(t) |
| 207 | defer b.close() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 208 | |
| 209 | // Start first node and perform write. |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 210 | etcd := New(Config{ |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 211 | 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 218 | }) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 219 | ctxC, _ := supervisor.TestHarness(t, etcd.Run) |
| 220 | defer ctxC() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 221 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 222 | w := etcd.Watch() |
| 223 | st, err := w.Get(b.ctx) |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 224 | if err != nil { |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 225 | t.Fatalf("could not get status: %v", err) |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 226 | } |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 227 | cl, err := st.CuratorClient() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 228 | if err != nil { |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 229 | t.Fatalf("CuratorClient: %v", err) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 230 | } |
| 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 234 | } |
| 235 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 236 | // 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 246 | } |
| 247 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 248 | 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 260 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 261 | 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 Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 266 | cl2, err := st2.CuratorClient() |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 267 | if err != nil { |
Serge Bazanski | 5839e97 | 2021-11-16 15:46:19 +0100 | [diff] [blame] | 268 | t.Fatalf("CuratorClient: %v", err) |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 269 | } |
| 270 | defer cl2.Close() |
Serge Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 271 | |
Serge Bazanski | f05e80a | 2021-10-12 11:53:34 +0200 | [diff] [blame] | 272 | 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 Bazanski | cb883e2 | 2020-07-06 17:47:55 +0200 | [diff] [blame] | 278 | } |
| 279 | } |