github.com/clly/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  }