github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/coordinate_test.go (about) 1 package state 2 3 import ( 4 "math" 5 "math/rand" 6 "testing" 7 8 "github.com/hashicorp/consul/agent/structs" 9 "github.com/hashicorp/consul/lib" 10 "github.com/hashicorp/go-memdb" 11 "github.com/hashicorp/serf/coordinate" 12 "github.com/pascaldekloe/goe/verify" 13 ) 14 15 // generateRandomCoordinate creates a random coordinate. This mucks with the 16 // underlying structure directly, so it's not really useful for any particular 17 // position in the network, but it's a good payload to send through to make 18 // sure things come out the other side or get stored correctly. 19 func generateRandomCoordinate() *coordinate.Coordinate { 20 config := coordinate.DefaultConfig() 21 coord := coordinate.NewCoordinate(config) 22 for i := range coord.Vec { 23 coord.Vec[i] = rand.NormFloat64() 24 } 25 coord.Error = rand.NormFloat64() 26 coord.Adjustment = rand.NormFloat64() 27 return coord 28 } 29 30 func TestStateStore_Coordinate_Updates(t *testing.T) { 31 s := testStateStore(t) 32 33 // Make sure the coordinates list starts out empty, and that a query for 34 // a per-node coordinate for a nonexistent node doesn't do anything bad. 35 ws := memdb.NewWatchSet() 36 idx, all, err := s.Coordinates(ws) 37 if err != nil { 38 t.Fatalf("err: %s", err) 39 } 40 if idx != 0 { 41 t.Fatalf("bad index: %d", idx) 42 } 43 verify.Values(t, "", all, structs.Coordinates{}) 44 45 coordinateWs := memdb.NewWatchSet() 46 _, coords, err := s.Coordinate("nope", coordinateWs) 47 if err != nil { 48 t.Fatalf("err: %s", err) 49 } 50 verify.Values(t, "", coords, lib.CoordinateSet{}) 51 52 // Make an update for nodes that don't exist and make sure they get 53 // ignored. 54 updates := structs.Coordinates{ 55 &structs.Coordinate{ 56 Node: "node1", 57 Coord: generateRandomCoordinate(), 58 }, 59 &structs.Coordinate{ 60 Node: "node2", 61 Coord: generateRandomCoordinate(), 62 }, 63 } 64 if err := s.CoordinateBatchUpdate(1, updates); err != nil { 65 t.Fatalf("err: %s", err) 66 } 67 if watchFired(ws) || watchFired(coordinateWs) { 68 t.Fatalf("bad") 69 } 70 71 // Should still be empty, though applying an empty batch does bump 72 // the table index. 73 ws = memdb.NewWatchSet() 74 idx, all, err = s.Coordinates(ws) 75 if err != nil { 76 t.Fatalf("err: %s", err) 77 } 78 if idx != 1 { 79 t.Fatalf("bad index: %d", idx) 80 } 81 verify.Values(t, "", all, structs.Coordinates{}) 82 83 coordinateWs = memdb.NewWatchSet() 84 idx, coords, err = s.Coordinate("node1", coordinateWs) 85 if err != nil { 86 t.Fatalf("err: %s", err) 87 } 88 if idx != 1 { 89 t.Fatalf("bad index: %d", idx) 90 } 91 92 // Register the nodes then do the update again. 93 testRegisterNode(t, s, 1, "node1") 94 testRegisterNode(t, s, 2, "node2") 95 if err := s.CoordinateBatchUpdate(3, updates); err != nil { 96 t.Fatalf("err: %s", err) 97 } 98 if !watchFired(ws) || !watchFired(coordinateWs) { 99 t.Fatalf("bad") 100 } 101 102 // Should go through now. 103 ws = memdb.NewWatchSet() 104 idx, all, err = s.Coordinates(ws) 105 if err != nil { 106 t.Fatalf("err: %s", err) 107 } 108 if idx != 3 { 109 t.Fatalf("bad index: %d", idx) 110 } 111 verify.Values(t, "", all, updates) 112 113 // Also verify the per-node coordinate interface. 114 nodeWs := make([]memdb.WatchSet, len(updates)) 115 for i, update := range updates { 116 nodeWs[i] = memdb.NewWatchSet() 117 idx, coords, err := s.Coordinate(update.Node, nodeWs[i]) 118 if err != nil { 119 t.Fatalf("err: %s", err) 120 } 121 if idx != 3 { 122 t.Fatalf("bad index: %d", idx) 123 } 124 expected := lib.CoordinateSet{ 125 "": update.Coord, 126 } 127 verify.Values(t, "", coords, expected) 128 } 129 130 // Update the coordinate for one of the nodes. 131 updates[1].Coord = generateRandomCoordinate() 132 if err := s.CoordinateBatchUpdate(4, updates); err != nil { 133 t.Fatalf("err: %s", err) 134 } 135 if !watchFired(ws) { 136 t.Fatalf("bad") 137 } 138 for _, ws := range nodeWs { 139 if !watchFired(ws) { 140 t.Fatalf("bad") 141 } 142 } 143 144 // Verify it got applied. 145 idx, all, err = s.Coordinates(nil) 146 if err != nil { 147 t.Fatalf("err: %s", err) 148 } 149 if idx != 4 { 150 t.Fatalf("bad index: %d", idx) 151 } 152 verify.Values(t, "", all, updates) 153 154 // And check the per-node coordinate version of the same thing. 155 for _, update := range updates { 156 idx, coords, err := s.Coordinate(update.Node, nil) 157 if err != nil { 158 t.Fatalf("err: %s", err) 159 } 160 if idx != 4 { 161 t.Fatalf("bad index: %d", idx) 162 } 163 expected := lib.CoordinateSet{ 164 "": update.Coord, 165 } 166 verify.Values(t, "", coords, expected) 167 } 168 169 // Apply an invalid update and make sure it gets ignored. 170 badUpdates := structs.Coordinates{ 171 &structs.Coordinate{ 172 Node: "node1", 173 Coord: &coordinate.Coordinate{Height: math.NaN()}, 174 }, 175 } 176 if err := s.CoordinateBatchUpdate(5, badUpdates); err != nil { 177 t.Fatalf("err: %s", err) 178 } 179 180 // Verify we are at the previous state, though the empty batch does bump 181 // the table index. 182 idx, all, err = s.Coordinates(nil) 183 if err != nil { 184 t.Fatalf("err: %s", err) 185 } 186 if idx != 5 { 187 t.Fatalf("bad index: %d", idx) 188 } 189 verify.Values(t, "", all, updates) 190 } 191 192 func TestStateStore_Coordinate_Cleanup(t *testing.T) { 193 s := testStateStore(t) 194 195 // Register a node and update its coordinate. 196 testRegisterNode(t, s, 1, "node1") 197 updates := structs.Coordinates{ 198 &structs.Coordinate{ 199 Node: "node1", 200 Segment: "alpha", 201 Coord: generateRandomCoordinate(), 202 }, 203 &structs.Coordinate{ 204 Node: "node1", 205 Segment: "beta", 206 Coord: generateRandomCoordinate(), 207 }, 208 } 209 if err := s.CoordinateBatchUpdate(2, updates); err != nil { 210 t.Fatalf("err: %s", err) 211 } 212 213 // Make sure it's in there. 214 _, coords, err := s.Coordinate("node1", nil) 215 if err != nil { 216 t.Fatalf("err: %s", err) 217 } 218 expected := lib.CoordinateSet{ 219 "alpha": updates[0].Coord, 220 "beta": updates[1].Coord, 221 } 222 verify.Values(t, "", coords, expected) 223 224 // Now delete the node. 225 if err := s.DeleteNode(3, "node1"); err != nil { 226 t.Fatalf("err: %s", err) 227 } 228 229 // Make sure the coordinate is gone. 230 _, coords, err = s.Coordinate("node1", nil) 231 if err != nil { 232 t.Fatalf("err: %s", err) 233 } 234 verify.Values(t, "", coords, lib.CoordinateSet{}) 235 236 // Make sure the index got updated. 237 idx, all, err := s.Coordinates(nil) 238 if err != nil { 239 t.Fatalf("err: %s", err) 240 } 241 if idx != 3 { 242 t.Fatalf("bad index: %d", idx) 243 } 244 verify.Values(t, "", all, structs.Coordinates{}) 245 } 246 247 func TestStateStore_Coordinate_Snapshot_Restore(t *testing.T) { 248 s := testStateStore(t) 249 250 // Register two nodes and update their coordinates. 251 testRegisterNode(t, s, 1, "node1") 252 testRegisterNode(t, s, 2, "node2") 253 updates := structs.Coordinates{ 254 &structs.Coordinate{ 255 Node: "node1", 256 Coord: generateRandomCoordinate(), 257 }, 258 &structs.Coordinate{ 259 Node: "node2", 260 Coord: generateRandomCoordinate(), 261 }, 262 } 263 if err := s.CoordinateBatchUpdate(3, updates); err != nil { 264 t.Fatalf("err: %s", err) 265 } 266 267 // Manually put a bad coordinate in for node3. 268 testRegisterNode(t, s, 4, "node3") 269 badUpdate := &structs.Coordinate{ 270 Node: "node3", 271 Coord: &coordinate.Coordinate{Height: math.NaN()}, 272 } 273 tx := s.db.Txn(true) 274 if err := tx.Insert("coordinates", badUpdate); err != nil { 275 t.Fatalf("err: %v", err) 276 } 277 tx.Commit() 278 279 // Snapshot the coordinates. 280 snap := s.Snapshot() 281 defer snap.Close() 282 283 // Alter the real state store. 284 trash := structs.Coordinates{ 285 &structs.Coordinate{ 286 Node: "node1", 287 Coord: generateRandomCoordinate(), 288 }, 289 &structs.Coordinate{ 290 Node: "node2", 291 Coord: generateRandomCoordinate(), 292 }, 293 } 294 if err := s.CoordinateBatchUpdate(5, trash); err != nil { 295 t.Fatalf("err: %s", err) 296 } 297 298 // Verify the snapshot. 299 if idx := snap.LastIndex(); idx != 4 { 300 t.Fatalf("bad index: %d", idx) 301 } 302 iter, err := snap.Coordinates() 303 if err != nil { 304 t.Fatalf("err: %s", err) 305 } 306 var dump structs.Coordinates 307 for coord := iter.Next(); coord != nil; coord = iter.Next() { 308 dump = append(dump, coord.(*structs.Coordinate)) 309 } 310 311 // The snapshot will have the bad update in it, since we don't filter on 312 // the read side. 313 verify.Values(t, "", dump, append(updates, badUpdate)) 314 315 // Restore the values into a new state store. 316 func() { 317 s := testStateStore(t) 318 restore := s.Restore() 319 if err := restore.Coordinates(6, dump); err != nil { 320 t.Fatalf("err: %s", err) 321 } 322 restore.Commit() 323 324 // Read the restored coordinates back out and verify that they match. 325 idx, res, err := s.Coordinates(nil) 326 if err != nil { 327 t.Fatalf("err: %s", err) 328 } 329 if idx != 6 { 330 t.Fatalf("bad index: %d", idx) 331 } 332 verify.Values(t, "", res, updates) 333 334 // Check that the index was updated (note that it got passed 335 // in during the restore). 336 if idx := s.maxIndex("coordinates"); idx != 6 { 337 t.Fatalf("bad index: %d", idx) 338 } 339 }() 340 341 }