github.com/Iqoqo/consul@v1.4.5/agent/consul/rtt_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"net/rpc"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hashicorp/consul/agent/structs"
    12  	"github.com/hashicorp/consul/lib"
    13  	"github.com/hashicorp/consul/testrpc"
    14  	"github.com/hashicorp/net-rpc-msgpackrpc"
    15  )
    16  
    17  // verifyNodeSort makes sure the order of the nodes in the slice is the same as
    18  // the expected order, expressed as a comma-separated string.
    19  func verifyNodeSort(t *testing.T, nodes structs.Nodes, expected string) {
    20  	vec := make([]string, len(nodes))
    21  	for i, node := range nodes {
    22  		vec[i] = node.Node
    23  	}
    24  	actual := strings.Join(vec, ",")
    25  	if actual != expected {
    26  		t.Fatalf("bad sort: %s != %s", actual, expected)
    27  	}
    28  }
    29  
    30  // verifyServiceNodeSort makes sure the order of the nodes in the slice is the
    31  // same as the expected order, expressed as a comma-separated string.
    32  func verifyServiceNodeSort(t *testing.T, nodes structs.ServiceNodes, expected string) {
    33  	vec := make([]string, len(nodes))
    34  	for i, node := range nodes {
    35  		vec[i] = node.Node
    36  	}
    37  	actual := strings.Join(vec, ",")
    38  	if actual != expected {
    39  		t.Fatalf("bad sort: %s != %s", actual, expected)
    40  	}
    41  }
    42  
    43  // verifyHealthCheckSort makes sure the order of the nodes in the slice is the
    44  // same as the expected order, expressed as a comma-separated string.
    45  func verifyHealthCheckSort(t *testing.T, checks structs.HealthChecks, expected string) {
    46  	vec := make([]string, len(checks))
    47  	for i, check := range checks {
    48  		vec[i] = check.Node
    49  	}
    50  	actual := strings.Join(vec, ",")
    51  	if actual != expected {
    52  		t.Fatalf("bad sort: %s != %s", actual, expected)
    53  	}
    54  }
    55  
    56  // verifyCheckServiceNodeSort makes sure the order of the nodes in the slice is
    57  // the same as the expected order, expressed as a comma-separated string.
    58  func verifyCheckServiceNodeSort(t *testing.T, nodes structs.CheckServiceNodes, expected string) {
    59  	vec := make([]string, len(nodes))
    60  	for i, node := range nodes {
    61  		vec[i] = node.Node.Node
    62  	}
    63  	actual := strings.Join(vec, ",")
    64  	if actual != expected {
    65  		t.Fatalf("bad sort: %s != %s", actual, expected)
    66  	}
    67  }
    68  
    69  // seedCoordinates uses the client to set up a set of nodes with a specific
    70  // set of distances from the origin. We also include the server so that we
    71  // can wait for the coordinates to get committed to the Raft log.
    72  //
    73  // Here's the layout of the nodes:
    74  //
    75  //       node3 node2 node5                         node4       node1
    76  //   |     |     |     |     |     |     |     |     |     |     |
    77  //   0     1     2     3     4     5     6     7     8     9     10  (ms)
    78  //
    79  func seedCoordinates(t *testing.T, codec rpc.ClientCodec, server *Server) {
    80  	// Register some nodes.
    81  	for i := 0; i < 5; i++ {
    82  		req := structs.RegisterRequest{
    83  			Datacenter: "dc1",
    84  			Node:       fmt.Sprintf("node%d", i+1),
    85  			Address:    "127.0.0.1",
    86  		}
    87  		var reply struct{}
    88  		if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply); err != nil {
    89  			t.Fatalf("err: %v", err)
    90  		}
    91  	}
    92  
    93  	// Seed the fixed setup of the nodes.
    94  	updates := []structs.CoordinateUpdateRequest{
    95  		structs.CoordinateUpdateRequest{
    96  			Datacenter: "dc1",
    97  			Node:       "node1",
    98  			Coord:      lib.GenerateCoordinate(10 * time.Millisecond),
    99  		},
   100  		structs.CoordinateUpdateRequest{
   101  			Datacenter: "dc1",
   102  			Node:       "node2",
   103  			Coord:      lib.GenerateCoordinate(2 * time.Millisecond),
   104  		},
   105  		structs.CoordinateUpdateRequest{
   106  			Datacenter: "dc1",
   107  			Node:       "node3",
   108  			Coord:      lib.GenerateCoordinate(1 * time.Millisecond),
   109  		},
   110  		structs.CoordinateUpdateRequest{
   111  			Datacenter: "dc1",
   112  			Node:       "node4",
   113  			Coord:      lib.GenerateCoordinate(8 * time.Millisecond),
   114  		},
   115  		structs.CoordinateUpdateRequest{
   116  			Datacenter: "dc1",
   117  			Node:       "node5",
   118  			Coord:      lib.GenerateCoordinate(3 * time.Millisecond),
   119  		},
   120  	}
   121  
   122  	// Apply the updates and wait a while for the batch to get committed to
   123  	// the Raft log.
   124  	for _, update := range updates {
   125  		var out struct{}
   126  		if err := msgpackrpc.CallWithCodec(codec, "Coordinate.Update", &update, &out); err != nil {
   127  			t.Fatalf("err: %v", err)
   128  		}
   129  	}
   130  	time.Sleep(2 * server.config.CoordinateUpdatePeriod)
   131  }
   132  
   133  func TestRTT_sortNodesByDistanceFrom(t *testing.T) {
   134  	t.Parallel()
   135  	dir, server := testServer(t)
   136  	defer os.RemoveAll(dir)
   137  	defer server.Shutdown()
   138  
   139  	codec := rpcClient(t, server)
   140  	defer codec.Close()
   141  	testrpc.WaitForTestAgent(t, server.RPC, "dc1")
   142  
   143  	seedCoordinates(t, codec, server)
   144  	nodes := structs.Nodes{
   145  		&structs.Node{Node: "apple"},
   146  		&structs.Node{Node: "node1"},
   147  		&structs.Node{Node: "node2"},
   148  		&structs.Node{Node: "node3"},
   149  		&structs.Node{Node: "node4"},
   150  		&structs.Node{Node: "node5"},
   151  	}
   152  
   153  	// The zero value for the source should not trigger any sorting.
   154  	var source structs.QuerySource
   155  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   156  		t.Fatalf("err: %v", err)
   157  	}
   158  	verifyNodeSort(t, nodes, "apple,node1,node2,node3,node4,node5")
   159  
   160  	// Same for a source in some other DC.
   161  	source.Node = "node1"
   162  	source.Datacenter = "dc2"
   163  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   164  		t.Fatalf("err: %v", err)
   165  	}
   166  	verifyNodeSort(t, nodes, "apple,node1,node2,node3,node4,node5")
   167  
   168  	// Same for a source node in our DC that we have no coordinate for.
   169  	source.Node = "apple"
   170  	source.Datacenter = "dc1"
   171  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   172  		t.Fatalf("err: %v", err)
   173  	}
   174  	verifyNodeSort(t, nodes, "apple,node1,node2,node3,node4,node5")
   175  
   176  	// Now sort relative to node1, note that apple doesn't have any seeded
   177  	// coordinate info so it should end up at the end, despite its lexical
   178  	// hegemony.
   179  	source.Node = "node1"
   180  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   181  		t.Fatalf("err: %v", err)
   182  	}
   183  	verifyNodeSort(t, nodes, "node1,node4,node5,node2,node3,apple")
   184  }
   185  
   186  func TestRTT_sortNodesByDistanceFrom_Nodes(t *testing.T) {
   187  	t.Parallel()
   188  	dir, server := testServer(t)
   189  	defer os.RemoveAll(dir)
   190  	defer server.Shutdown()
   191  
   192  	codec := rpcClient(t, server)
   193  	defer codec.Close()
   194  	testrpc.WaitForTestAgent(t, server.RPC, "dc1")
   195  
   196  	seedCoordinates(t, codec, server)
   197  	nodes := structs.Nodes{
   198  		&structs.Node{Node: "apple"},
   199  		&structs.Node{Node: "node1"},
   200  		&structs.Node{Node: "node2"},
   201  		&structs.Node{Node: "node3"},
   202  		&structs.Node{Node: "node4"},
   203  		&structs.Node{Node: "node5"},
   204  	}
   205  
   206  	// Now sort relative to node1, note that apple doesn't have any
   207  	// seeded coordinate info so it should end up at the end, despite
   208  	// its lexical hegemony.
   209  	var source structs.QuerySource
   210  	source.Node = "node1"
   211  	source.Datacenter = "dc1"
   212  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   213  		t.Fatalf("err: %v", err)
   214  	}
   215  	verifyNodeSort(t, nodes, "node1,node4,node5,node2,node3,apple")
   216  
   217  	// Try another sort from node2. Note that node5 and node3 are the
   218  	// same distance away so the stable sort should preserve the order
   219  	// they were in from the previous sort.
   220  	source.Node = "node2"
   221  	source.Datacenter = "dc1"
   222  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   223  		t.Fatalf("err: %v", err)
   224  	}
   225  	verifyNodeSort(t, nodes, "node2,node5,node3,node4,node1,apple")
   226  
   227  	// Let's exercise the stable sort explicitly to make sure we didn't
   228  	// just get lucky.
   229  	nodes[1], nodes[2] = nodes[2], nodes[1]
   230  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   231  		t.Fatalf("err: %v", err)
   232  	}
   233  	verifyNodeSort(t, nodes, "node2,node3,node5,node4,node1,apple")
   234  }
   235  
   236  func TestRTT_sortNodesByDistanceFrom_ServiceNodes(t *testing.T) {
   237  	t.Parallel()
   238  	dir, server := testServer(t)
   239  	defer os.RemoveAll(dir)
   240  	defer server.Shutdown()
   241  	testrpc.WaitForTestAgent(t, server.RPC, "dc1")
   242  
   243  	codec := rpcClient(t, server)
   244  	defer codec.Close()
   245  
   246  	seedCoordinates(t, codec, server)
   247  	nodes := structs.ServiceNodes{
   248  		&structs.ServiceNode{Node: "apple"},
   249  		&structs.ServiceNode{Node: "node1"},
   250  		&structs.ServiceNode{Node: "node2"},
   251  		&structs.ServiceNode{Node: "node3"},
   252  		&structs.ServiceNode{Node: "node4"},
   253  		&structs.ServiceNode{Node: "node5"},
   254  	}
   255  
   256  	// Now sort relative to node1, note that apple doesn't have any
   257  	// seeded coordinate info so it should end up at the end, despite
   258  	// its lexical hegemony.
   259  	var source structs.QuerySource
   260  	source.Node = "node1"
   261  	source.Datacenter = "dc1"
   262  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   263  		t.Fatalf("err: %v", err)
   264  	}
   265  	verifyServiceNodeSort(t, nodes, "node1,node4,node5,node2,node3,apple")
   266  
   267  	// Try another sort from node2. Note that node5 and node3 are the
   268  	// same distance away so the stable sort should preserve the order
   269  	// they were in from the previous sort.
   270  	source.Node = "node2"
   271  	source.Datacenter = "dc1"
   272  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   273  		t.Fatalf("err: %v", err)
   274  	}
   275  	verifyServiceNodeSort(t, nodes, "node2,node5,node3,node4,node1,apple")
   276  
   277  	// Let's exercise the stable sort explicitly to make sure we didn't
   278  	// just get lucky.
   279  	nodes[1], nodes[2] = nodes[2], nodes[1]
   280  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   281  		t.Fatalf("err: %v", err)
   282  	}
   283  	verifyServiceNodeSort(t, nodes, "node2,node3,node5,node4,node1,apple")
   284  }
   285  
   286  func TestRTT_sortNodesByDistanceFrom_HealthChecks(t *testing.T) {
   287  	t.Parallel()
   288  	dir, server := testServer(t)
   289  	defer os.RemoveAll(dir)
   290  	defer server.Shutdown()
   291  
   292  	codec := rpcClient(t, server)
   293  	defer codec.Close()
   294  	testrpc.WaitForLeader(t, server.RPC, "dc1")
   295  
   296  	seedCoordinates(t, codec, server)
   297  	checks := structs.HealthChecks{
   298  		&structs.HealthCheck{Node: "apple"},
   299  		&structs.HealthCheck{Node: "node1"},
   300  		&structs.HealthCheck{Node: "node2"},
   301  		&structs.HealthCheck{Node: "node3"},
   302  		&structs.HealthCheck{Node: "node4"},
   303  		&structs.HealthCheck{Node: "node5"},
   304  	}
   305  
   306  	// Now sort relative to node1, note that apple doesn't have any
   307  	// seeded coordinate info so it should end up at the end, despite
   308  	// its lexical hegemony.
   309  	var source structs.QuerySource
   310  	source.Node = "node1"
   311  	source.Datacenter = "dc1"
   312  	if err := server.sortNodesByDistanceFrom(source, checks); err != nil {
   313  		t.Fatalf("err: %v", err)
   314  	}
   315  	verifyHealthCheckSort(t, checks, "node1,node4,node5,node2,node3,apple")
   316  
   317  	// Try another sort from node2. Note that node5 and node3 are the
   318  	// same distance away so the stable sort should preserve the order
   319  	// they were in from the previous sort.
   320  	source.Node = "node2"
   321  	source.Datacenter = "dc1"
   322  	if err := server.sortNodesByDistanceFrom(source, checks); err != nil {
   323  		t.Fatalf("err: %v", err)
   324  	}
   325  	verifyHealthCheckSort(t, checks, "node2,node5,node3,node4,node1,apple")
   326  
   327  	// Let's exercise the stable sort explicitly to make sure we didn't
   328  	// just get lucky.
   329  	checks[1], checks[2] = checks[2], checks[1]
   330  	if err := server.sortNodesByDistanceFrom(source, checks); err != nil {
   331  		t.Fatalf("err: %v", err)
   332  	}
   333  	verifyHealthCheckSort(t, checks, "node2,node3,node5,node4,node1,apple")
   334  }
   335  
   336  func TestRTT_sortNodesByDistanceFrom_CheckServiceNodes(t *testing.T) {
   337  	t.Parallel()
   338  	dir, server := testServer(t)
   339  	defer os.RemoveAll(dir)
   340  	defer server.Shutdown()
   341  
   342  	codec := rpcClient(t, server)
   343  	defer codec.Close()
   344  	testrpc.WaitForTestAgent(t, server.RPC, "dc1")
   345  
   346  	seedCoordinates(t, codec, server)
   347  	nodes := structs.CheckServiceNodes{
   348  		structs.CheckServiceNode{Node: &structs.Node{Node: "apple"}},
   349  		structs.CheckServiceNode{Node: &structs.Node{Node: "node1"}},
   350  		structs.CheckServiceNode{Node: &structs.Node{Node: "node2"}},
   351  		structs.CheckServiceNode{Node: &structs.Node{Node: "node3"}},
   352  		structs.CheckServiceNode{Node: &structs.Node{Node: "node4"}},
   353  		structs.CheckServiceNode{Node: &structs.Node{Node: "node5"}},
   354  	}
   355  
   356  	// Now sort relative to node1, note that apple doesn't have any
   357  	// seeded coordinate info so it should end up at the end, despite
   358  	// its lexical hegemony.
   359  	var source structs.QuerySource
   360  	source.Node = "node1"
   361  	source.Datacenter = "dc1"
   362  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   363  		t.Fatalf("err: %v", err)
   364  	}
   365  	verifyCheckServiceNodeSort(t, nodes, "node1,node4,node5,node2,node3,apple")
   366  
   367  	// Try another sort from node2. Note that node5 and node3 are the
   368  	// same distance away so the stable sort should preserve the order
   369  	// they were in from the previous sort.
   370  	source.Node = "node2"
   371  	source.Datacenter = "dc1"
   372  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   373  		t.Fatalf("err: %v", err)
   374  	}
   375  	verifyCheckServiceNodeSort(t, nodes, "node2,node5,node3,node4,node1,apple")
   376  
   377  	// Let's exercise the stable sort explicitly to make sure we didn't
   378  	// just get lucky.
   379  	nodes[1], nodes[2] = nodes[2], nodes[1]
   380  	if err := server.sortNodesByDistanceFrom(source, nodes); err != nil {
   381  		t.Fatalf("err: %v", err)
   382  	}
   383  	verifyCheckServiceNodeSort(t, nodes, "node2,node3,node5,node4,node1,apple")
   384  }