github.com/clly/consul@v1.4.5/agent/consul/health_endpoint_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"os"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/consul/agent/structs"
     9  	"github.com/hashicorp/consul/api"
    10  	"github.com/hashicorp/consul/lib"
    11  	"github.com/hashicorp/consul/testrpc"
    12  	"github.com/hashicorp/net-rpc-msgpackrpc"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestHealth_ChecksInState(t *testing.T) {
    18  	t.Parallel()
    19  	dir1, s1 := testServer(t)
    20  	defer os.RemoveAll(dir1)
    21  	defer s1.Shutdown()
    22  	codec := rpcClient(t, s1)
    23  	defer codec.Close()
    24  
    25  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
    26  
    27  	arg := structs.RegisterRequest{
    28  		Datacenter: "dc1",
    29  		Node:       "foo",
    30  		Address:    "127.0.0.1",
    31  		Check: &structs.HealthCheck{
    32  			Name:   "memory utilization",
    33  			Status: api.HealthPassing,
    34  		},
    35  	}
    36  	var out struct{}
    37  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
    38  		t.Fatalf("err: %v", err)
    39  	}
    40  
    41  	var out2 structs.IndexedHealthChecks
    42  	inState := structs.ChecksInStateRequest{
    43  		Datacenter: "dc1",
    44  		State:      api.HealthPassing,
    45  	}
    46  	if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil {
    47  		t.Fatalf("err: %v", err)
    48  	}
    49  
    50  	checks := out2.HealthChecks
    51  	if len(checks) != 2 {
    52  		t.Fatalf("Bad: %v", checks)
    53  	}
    54  
    55  	// Serf check is automatically added
    56  	if checks[0].Name != "memory utilization" {
    57  		t.Fatalf("Bad: %v", checks[0])
    58  	}
    59  	if checks[1].CheckID != structs.SerfCheckID {
    60  		t.Fatalf("Bad: %v", checks[1])
    61  	}
    62  }
    63  
    64  func TestHealth_ChecksInState_NodeMetaFilter(t *testing.T) {
    65  	t.Parallel()
    66  	dir1, s1 := testServer(t)
    67  	defer os.RemoveAll(dir1)
    68  	defer s1.Shutdown()
    69  	codec := rpcClient(t, s1)
    70  	defer codec.Close()
    71  
    72  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
    73  
    74  	arg := structs.RegisterRequest{
    75  		Datacenter: "dc1",
    76  		Node:       "foo",
    77  		Address:    "127.0.0.1",
    78  		NodeMeta: map[string]string{
    79  			"somekey": "somevalue",
    80  			"common":  "1",
    81  		},
    82  		Check: &structs.HealthCheck{
    83  			Name:   "memory utilization",
    84  			Status: api.HealthPassing,
    85  		},
    86  	}
    87  	var out struct{}
    88  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
    89  		t.Fatalf("err: %v", err)
    90  	}
    91  	arg = structs.RegisterRequest{
    92  		Datacenter: "dc1",
    93  		Node:       "bar",
    94  		Address:    "127.0.0.2",
    95  		NodeMeta: map[string]string{
    96  			"common": "1",
    97  		},
    98  		Check: &structs.HealthCheck{
    99  			Name:   "disk space",
   100  			Status: api.HealthPassing,
   101  		},
   102  	}
   103  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   104  		t.Fatalf("err: %v", err)
   105  	}
   106  
   107  	cases := []struct {
   108  		filters    map[string]string
   109  		checkNames []string
   110  	}{
   111  		// Get foo's check by its unique meta value
   112  		{
   113  			filters:    map[string]string{"somekey": "somevalue"},
   114  			checkNames: []string{"memory utilization"},
   115  		},
   116  		// Get both foo/bar's checks by their common meta value
   117  		{
   118  			filters:    map[string]string{"common": "1"},
   119  			checkNames: []string{"disk space", "memory utilization"},
   120  		},
   121  		// Use an invalid meta value, should get empty result
   122  		{
   123  			filters:    map[string]string{"invalid": "nope"},
   124  			checkNames: []string{},
   125  		},
   126  		// Use multiple filters to get foo's check
   127  		{
   128  			filters: map[string]string{
   129  				"somekey": "somevalue",
   130  				"common":  "1",
   131  			},
   132  			checkNames: []string{"memory utilization"},
   133  		},
   134  	}
   135  
   136  	for _, tc := range cases {
   137  		var out structs.IndexedHealthChecks
   138  		inState := structs.ChecksInStateRequest{
   139  			Datacenter:      "dc1",
   140  			NodeMetaFilters: tc.filters,
   141  			State:           api.HealthPassing,
   142  		}
   143  		if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out); err != nil {
   144  			t.Fatalf("err: %v", err)
   145  		}
   146  
   147  		checks := out.HealthChecks
   148  		if len(checks) != len(tc.checkNames) {
   149  			t.Fatalf("Bad: %v, %v", checks, tc.checkNames)
   150  		}
   151  
   152  		for i, check := range checks {
   153  			if tc.checkNames[i] != check.Name {
   154  				t.Fatalf("Bad: %v %v", checks, tc.checkNames)
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  func TestHealth_ChecksInState_DistanceSort(t *testing.T) {
   161  	t.Parallel()
   162  	dir1, s1 := testServer(t)
   163  	defer os.RemoveAll(dir1)
   164  	defer s1.Shutdown()
   165  	codec := rpcClient(t, s1)
   166  	defer codec.Close()
   167  
   168  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   169  	if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil {
   170  		t.Fatalf("err: %v", err)
   171  	}
   172  	if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil {
   173  		t.Fatalf("err: %v", err)
   174  	}
   175  	updates := structs.Coordinates{
   176  		{Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)},
   177  		{Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)},
   178  	}
   179  	if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil {
   180  		t.Fatalf("err: %v", err)
   181  	}
   182  
   183  	arg := structs.RegisterRequest{
   184  		Datacenter: "dc1",
   185  		Node:       "foo",
   186  		Address:    "127.0.0.1",
   187  		Check: &structs.HealthCheck{
   188  			Name:   "memory utilization",
   189  			Status: api.HealthPassing,
   190  		},
   191  	}
   192  
   193  	var out struct{}
   194  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   195  		t.Fatalf("err: %v", err)
   196  	}
   197  
   198  	arg.Node = "bar"
   199  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   200  		t.Fatalf("err: %v", err)
   201  	}
   202  
   203  	// Query relative to foo to make sure it shows up first in the list.
   204  	var out2 structs.IndexedHealthChecks
   205  	inState := structs.ChecksInStateRequest{
   206  		Datacenter: "dc1",
   207  		State:      api.HealthPassing,
   208  		Source: structs.QuerySource{
   209  			Datacenter: "dc1",
   210  			Node:       "foo",
   211  		},
   212  	}
   213  	if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil {
   214  		t.Fatalf("err: %v", err)
   215  	}
   216  	checks := out2.HealthChecks
   217  	if len(checks) != 3 {
   218  		t.Fatalf("Bad: %v", checks)
   219  	}
   220  	if checks[0].Node != "foo" {
   221  		t.Fatalf("Bad: %v", checks[1])
   222  	}
   223  
   224  	// Now query relative to bar to make sure it shows up first.
   225  	inState.Source.Node = "bar"
   226  	if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil {
   227  		t.Fatalf("err: %v", err)
   228  	}
   229  	checks = out2.HealthChecks
   230  	if len(checks) != 3 {
   231  		t.Fatalf("Bad: %v", checks)
   232  	}
   233  	if checks[0].Node != "bar" {
   234  		t.Fatalf("Bad: %v", checks[1])
   235  	}
   236  }
   237  
   238  func TestHealth_NodeChecks(t *testing.T) {
   239  	t.Parallel()
   240  	dir1, s1 := testServer(t)
   241  	defer os.RemoveAll(dir1)
   242  	defer s1.Shutdown()
   243  	codec := rpcClient(t, s1)
   244  	defer codec.Close()
   245  
   246  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   247  
   248  	arg := structs.RegisterRequest{
   249  		Datacenter: "dc1",
   250  		Node:       "foo",
   251  		Address:    "127.0.0.1",
   252  		Check: &structs.HealthCheck{
   253  			Name:   "memory utilization",
   254  			Status: api.HealthPassing,
   255  		},
   256  	}
   257  	var out struct{}
   258  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   259  		t.Fatalf("err: %v", err)
   260  	}
   261  
   262  	var out2 structs.IndexedHealthChecks
   263  	node := structs.NodeSpecificRequest{
   264  		Datacenter: "dc1",
   265  		Node:       "foo",
   266  	}
   267  	if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &node, &out2); err != nil {
   268  		t.Fatalf("err: %v", err)
   269  	}
   270  
   271  	checks := out2.HealthChecks
   272  	if len(checks) != 1 {
   273  		t.Fatalf("Bad: %v", checks)
   274  	}
   275  	if checks[0].Name != "memory utilization" {
   276  		t.Fatalf("Bad: %v", checks)
   277  	}
   278  }
   279  
   280  func TestHealth_ServiceChecks(t *testing.T) {
   281  	t.Parallel()
   282  	dir1, s1 := testServer(t)
   283  	defer os.RemoveAll(dir1)
   284  	defer s1.Shutdown()
   285  	codec := rpcClient(t, s1)
   286  	defer codec.Close()
   287  
   288  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   289  
   290  	arg := structs.RegisterRequest{
   291  		Datacenter: "dc1",
   292  		Node:       "foo",
   293  		Address:    "127.0.0.1",
   294  		Service: &structs.NodeService{
   295  			ID:      "db",
   296  			Service: "db",
   297  		},
   298  		Check: &structs.HealthCheck{
   299  			Name:      "db connect",
   300  			Status:    api.HealthPassing,
   301  			ServiceID: "db",
   302  		},
   303  	}
   304  	var out struct{}
   305  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   306  		t.Fatalf("err: %v", err)
   307  	}
   308  
   309  	var out2 structs.IndexedHealthChecks
   310  	node := structs.ServiceSpecificRequest{
   311  		Datacenter:  "dc1",
   312  		ServiceName: "db",
   313  	}
   314  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil {
   315  		t.Fatalf("err: %v", err)
   316  	}
   317  
   318  	checks := out2.HealthChecks
   319  	if len(checks) != 1 {
   320  		t.Fatalf("Bad: %v", checks)
   321  	}
   322  	if checks[0].Name != "db connect" {
   323  		t.Fatalf("Bad: %v", checks)
   324  	}
   325  }
   326  
   327  func TestHealth_ServiceChecks_NodeMetaFilter(t *testing.T) {
   328  	t.Parallel()
   329  	dir1, s1 := testServer(t)
   330  	defer os.RemoveAll(dir1)
   331  	defer s1.Shutdown()
   332  	codec := rpcClient(t, s1)
   333  	defer codec.Close()
   334  
   335  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   336  
   337  	arg := structs.RegisterRequest{
   338  		Datacenter: "dc1",
   339  		Node:       "foo",
   340  		Address:    "127.0.0.1",
   341  		NodeMeta: map[string]string{
   342  			"somekey": "somevalue",
   343  			"common":  "1",
   344  		},
   345  		Service: &structs.NodeService{
   346  			ID:      "db",
   347  			Service: "db",
   348  		},
   349  		Check: &structs.HealthCheck{
   350  			Name:      "memory utilization",
   351  			Status:    api.HealthPassing,
   352  			ServiceID: "db",
   353  		},
   354  	}
   355  	var out struct{}
   356  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   357  		t.Fatalf("err: %v", err)
   358  	}
   359  	arg = structs.RegisterRequest{
   360  		Datacenter: "dc1",
   361  		Node:       "bar",
   362  		Address:    "127.0.0.2",
   363  		NodeMeta: map[string]string{
   364  			"common": "1",
   365  		},
   366  		Service: &structs.NodeService{
   367  			ID:      "db",
   368  			Service: "db",
   369  		},
   370  		Check: &structs.HealthCheck{
   371  			Name:      "disk space",
   372  			Status:    api.HealthPassing,
   373  			ServiceID: "db",
   374  		},
   375  	}
   376  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   377  		t.Fatalf("err: %v", err)
   378  	}
   379  
   380  	cases := []struct {
   381  		filters    map[string]string
   382  		checkNames []string
   383  	}{
   384  		// Get foo's check by its unique meta value
   385  		{
   386  			filters:    map[string]string{"somekey": "somevalue"},
   387  			checkNames: []string{"memory utilization"},
   388  		},
   389  		// Get both foo/bar's checks by their common meta value
   390  		{
   391  			filters:    map[string]string{"common": "1"},
   392  			checkNames: []string{"disk space", "memory utilization"},
   393  		},
   394  		// Use an invalid meta value, should get empty result
   395  		{
   396  			filters:    map[string]string{"invalid": "nope"},
   397  			checkNames: []string{},
   398  		},
   399  		// Use multiple filters to get foo's check
   400  		{
   401  			filters: map[string]string{
   402  				"somekey": "somevalue",
   403  				"common":  "1",
   404  			},
   405  			checkNames: []string{"memory utilization"},
   406  		},
   407  	}
   408  
   409  	for _, tc := range cases {
   410  		var out structs.IndexedHealthChecks
   411  		args := structs.ServiceSpecificRequest{
   412  			Datacenter:      "dc1",
   413  			NodeMetaFilters: tc.filters,
   414  			ServiceName:     "db",
   415  		}
   416  		if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &args, &out); err != nil {
   417  			t.Fatalf("err: %v", err)
   418  		}
   419  
   420  		checks := out.HealthChecks
   421  		if len(checks) != len(tc.checkNames) {
   422  			t.Fatalf("Bad: %v, %v", checks, tc.checkNames)
   423  		}
   424  
   425  		for i, check := range checks {
   426  			if tc.checkNames[i] != check.Name {
   427  				t.Fatalf("Bad: %v %v", checks, tc.checkNames)
   428  			}
   429  		}
   430  	}
   431  }
   432  
   433  func TestHealth_ServiceChecks_DistanceSort(t *testing.T) {
   434  	t.Parallel()
   435  	dir1, s1 := testServer(t)
   436  	defer os.RemoveAll(dir1)
   437  	defer s1.Shutdown()
   438  	codec := rpcClient(t, s1)
   439  	defer codec.Close()
   440  
   441  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   442  	if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil {
   443  		t.Fatalf("err: %v", err)
   444  	}
   445  	if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil {
   446  		t.Fatalf("err: %v", err)
   447  	}
   448  	updates := structs.Coordinates{
   449  		{Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)},
   450  		{Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)},
   451  	}
   452  	if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil {
   453  		t.Fatalf("err: %v", err)
   454  	}
   455  
   456  	arg := structs.RegisterRequest{
   457  		Datacenter: "dc1",
   458  		Node:       "foo",
   459  		Address:    "127.0.0.1",
   460  		Service: &structs.NodeService{
   461  			ID:      "db",
   462  			Service: "db",
   463  		},
   464  		Check: &structs.HealthCheck{
   465  			Name:      "db connect",
   466  			Status:    api.HealthPassing,
   467  			ServiceID: "db",
   468  		},
   469  	}
   470  
   471  	var out struct{}
   472  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   473  		t.Fatalf("err: %v", err)
   474  	}
   475  
   476  	arg.Node = "bar"
   477  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   478  		t.Fatalf("err: %v", err)
   479  	}
   480  
   481  	// Query relative to foo to make sure it shows up first in the list.
   482  	var out2 structs.IndexedHealthChecks
   483  	node := structs.ServiceSpecificRequest{
   484  		Datacenter:  "dc1",
   485  		ServiceName: "db",
   486  		Source: structs.QuerySource{
   487  			Datacenter: "dc1",
   488  			Node:       "foo",
   489  		},
   490  	}
   491  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil {
   492  		t.Fatalf("err: %v", err)
   493  	}
   494  	checks := out2.HealthChecks
   495  	if len(checks) != 2 {
   496  		t.Fatalf("Bad: %v", checks)
   497  	}
   498  	if checks[0].Node != "foo" {
   499  		t.Fatalf("Bad: %v", checks)
   500  	}
   501  	if checks[1].Node != "bar" {
   502  		t.Fatalf("Bad: %v", checks)
   503  	}
   504  
   505  	// Now query relative to bar to make sure it shows up first.
   506  	node.Source.Node = "bar"
   507  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil {
   508  		t.Fatalf("err: %v", err)
   509  	}
   510  	checks = out2.HealthChecks
   511  	if len(checks) != 2 {
   512  		t.Fatalf("Bad: %v", checks)
   513  	}
   514  	if checks[0].Node != "bar" {
   515  		t.Fatalf("Bad: %v", checks)
   516  	}
   517  	if checks[1].Node != "foo" {
   518  		t.Fatalf("Bad: %v", checks)
   519  	}
   520  }
   521  
   522  func TestHealth_ServiceNodes(t *testing.T) {
   523  	t.Parallel()
   524  	dir1, s1 := testServer(t)
   525  	defer os.RemoveAll(dir1)
   526  	defer s1.Shutdown()
   527  	codec := rpcClient(t, s1)
   528  	defer codec.Close()
   529  
   530  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   531  
   532  	arg := structs.RegisterRequest{
   533  		Datacenter: "dc1",
   534  		Node:       "foo",
   535  		Address:    "127.0.0.1",
   536  		Service: &structs.NodeService{
   537  			ID:      "db",
   538  			Service: "db",
   539  			Tags:    []string{"master"},
   540  		},
   541  		Check: &structs.HealthCheck{
   542  			Name:      "db connect",
   543  			Status:    api.HealthPassing,
   544  			ServiceID: "db",
   545  		},
   546  	}
   547  	var out struct{}
   548  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   549  		t.Fatalf("err: %v", err)
   550  	}
   551  
   552  	arg = structs.RegisterRequest{
   553  		Datacenter: "dc1",
   554  		Node:       "bar",
   555  		Address:    "127.0.0.2",
   556  		Service: &structs.NodeService{
   557  			ID:      "db",
   558  			Service: "db",
   559  			Tags:    []string{"slave"},
   560  		},
   561  		Check: &structs.HealthCheck{
   562  			Name:      "db connect",
   563  			Status:    api.HealthWarning,
   564  			ServiceID: "db",
   565  		},
   566  	}
   567  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   568  		t.Fatalf("err: %v", err)
   569  	}
   570  
   571  	var out2 structs.IndexedCheckServiceNodes
   572  	req := structs.ServiceSpecificRequest{
   573  		Datacenter:  "dc1",
   574  		ServiceName: "db",
   575  		ServiceTags: []string{"master"},
   576  		TagFilter:   false,
   577  	}
   578  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil {
   579  		t.Fatalf("err: %v", err)
   580  	}
   581  
   582  	nodes := out2.Nodes
   583  	if len(nodes) != 2 {
   584  		t.Fatalf("Bad: %v", nodes)
   585  	}
   586  	if nodes[0].Node.Node != "bar" {
   587  		t.Fatalf("Bad: %v", nodes[0])
   588  	}
   589  	if nodes[1].Node.Node != "foo" {
   590  		t.Fatalf("Bad: %v", nodes[1])
   591  	}
   592  	if !lib.StrContains(nodes[0].Service.Tags, "slave") {
   593  		t.Fatalf("Bad: %v", nodes[0])
   594  	}
   595  	if !lib.StrContains(nodes[1].Service.Tags, "master") {
   596  		t.Fatalf("Bad: %v", nodes[1])
   597  	}
   598  	if nodes[0].Checks[0].Status != api.HealthWarning {
   599  		t.Fatalf("Bad: %v", nodes[0])
   600  	}
   601  	if nodes[1].Checks[0].Status != api.HealthPassing {
   602  		t.Fatalf("Bad: %v", nodes[1])
   603  	}
   604  
   605  	// Same should still work for <1.3 RPCs with singular tags
   606  	// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
   607  	// with 1.2.x is not required.
   608  	{
   609  		var out2 structs.IndexedCheckServiceNodes
   610  		req := structs.ServiceSpecificRequest{
   611  			Datacenter:  "dc1",
   612  			ServiceName: "db",
   613  			ServiceTag:  "master",
   614  			TagFilter:   false,
   615  		}
   616  		if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil {
   617  			t.Fatalf("err: %v", err)
   618  		}
   619  
   620  		nodes := out2.Nodes
   621  		if len(nodes) != 2 {
   622  			t.Fatalf("Bad: %v", nodes)
   623  		}
   624  		if nodes[0].Node.Node != "bar" {
   625  			t.Fatalf("Bad: %v", nodes[0])
   626  		}
   627  		if nodes[1].Node.Node != "foo" {
   628  			t.Fatalf("Bad: %v", nodes[1])
   629  		}
   630  		if !lib.StrContains(nodes[0].Service.Tags, "slave") {
   631  			t.Fatalf("Bad: %v", nodes[0])
   632  		}
   633  		if !lib.StrContains(nodes[1].Service.Tags, "master") {
   634  			t.Fatalf("Bad: %v", nodes[1])
   635  		}
   636  		if nodes[0].Checks[0].Status != api.HealthWarning {
   637  			t.Fatalf("Bad: %v", nodes[0])
   638  		}
   639  		if nodes[1].Checks[0].Status != api.HealthPassing {
   640  			t.Fatalf("Bad: %v", nodes[1])
   641  		}
   642  	}
   643  }
   644  
   645  func TestHealth_ServiceNodes_MultipleServiceTags(t *testing.T) {
   646  	t.Parallel()
   647  	dir1, s1 := testServer(t)
   648  	defer os.RemoveAll(dir1)
   649  	defer s1.Shutdown()
   650  	codec := rpcClient(t, s1)
   651  	defer codec.Close()
   652  
   653  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   654  
   655  	arg := structs.RegisterRequest{
   656  		Datacenter: "dc1",
   657  		Node:       "foo",
   658  		Address:    "127.0.0.1",
   659  		Service: &structs.NodeService{
   660  			ID:      "db",
   661  			Service: "db",
   662  			Tags:    []string{"master", "v2"},
   663  		},
   664  		Check: &structs.HealthCheck{
   665  			Name:      "db connect",
   666  			Status:    api.HealthPassing,
   667  			ServiceID: "db",
   668  		},
   669  	}
   670  	var out struct{}
   671  	require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out))
   672  
   673  	arg = structs.RegisterRequest{
   674  		Datacenter: "dc1",
   675  		Node:       "bar",
   676  		Address:    "127.0.0.2",
   677  		Service: &structs.NodeService{
   678  			ID:      "db",
   679  			Service: "db",
   680  			Tags:    []string{"slave", "v2"},
   681  		},
   682  		Check: &structs.HealthCheck{
   683  			Name:      "db connect",
   684  			Status:    api.HealthWarning,
   685  			ServiceID: "db",
   686  		},
   687  	}
   688  	require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out))
   689  
   690  	var out2 structs.IndexedCheckServiceNodes
   691  	req := structs.ServiceSpecificRequest{
   692  		Datacenter:  "dc1",
   693  		ServiceName: "db",
   694  		ServiceTags: []string{"master", "v2"},
   695  		TagFilter:   true,
   696  	}
   697  	require.NoError(t, msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2))
   698  
   699  	nodes := out2.Nodes
   700  	require.Len(t, nodes, 1)
   701  	require.Equal(t, nodes[0].Node.Node, "foo")
   702  	require.Contains(t, nodes[0].Service.Tags, "v2")
   703  	require.Contains(t, nodes[0].Service.Tags, "master")
   704  	require.Equal(t, nodes[0].Checks[0].Status, api.HealthPassing)
   705  }
   706  
   707  func TestHealth_ServiceNodes_NodeMetaFilter(t *testing.T) {
   708  	t.Parallel()
   709  	dir1, s1 := testServer(t)
   710  	defer os.RemoveAll(dir1)
   711  	defer s1.Shutdown()
   712  	codec := rpcClient(t, s1)
   713  	defer codec.Close()
   714  
   715  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   716  
   717  	arg := structs.RegisterRequest{
   718  		Datacenter: "dc1",
   719  		Node:       "foo",
   720  		Address:    "127.0.0.1",
   721  		NodeMeta: map[string]string{
   722  			"somekey": "somevalue",
   723  			"common":  "1",
   724  		},
   725  		Service: &structs.NodeService{
   726  			ID:      "db",
   727  			Service: "db",
   728  		},
   729  		Check: &structs.HealthCheck{
   730  			Name:      "memory utilization",
   731  			Status:    api.HealthPassing,
   732  			ServiceID: "db",
   733  		},
   734  	}
   735  	var out struct{}
   736  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   737  		t.Fatalf("err: %v", err)
   738  	}
   739  
   740  	arg = structs.RegisterRequest{
   741  		Datacenter: "dc1",
   742  		Node:       "bar",
   743  		Address:    "127.0.0.2",
   744  		NodeMeta: map[string]string{
   745  			"common": "1",
   746  		},
   747  		Service: &structs.NodeService{
   748  			ID:      "db",
   749  			Service: "db",
   750  		},
   751  		Check: &structs.HealthCheck{
   752  			Name:      "disk space",
   753  			Status:    api.HealthWarning,
   754  			ServiceID: "db",
   755  		},
   756  	}
   757  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   758  		t.Fatalf("err: %v", err)
   759  	}
   760  
   761  	cases := []struct {
   762  		filters map[string]string
   763  		nodes   structs.CheckServiceNodes
   764  	}{
   765  		// Get foo's check by its unique meta value
   766  		{
   767  			filters: map[string]string{"somekey": "somevalue"},
   768  			nodes: structs.CheckServiceNodes{
   769  				structs.CheckServiceNode{
   770  					Node:   &structs.Node{Node: "foo"},
   771  					Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}},
   772  				},
   773  			},
   774  		},
   775  		// Get both foo/bar's checks by their common meta value
   776  		{
   777  			filters: map[string]string{"common": "1"},
   778  			nodes: structs.CheckServiceNodes{
   779  				structs.CheckServiceNode{
   780  					Node:   &structs.Node{Node: "bar"},
   781  					Checks: structs.HealthChecks{&structs.HealthCheck{Name: "disk space"}},
   782  				},
   783  				structs.CheckServiceNode{
   784  					Node:   &structs.Node{Node: "foo"},
   785  					Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}},
   786  				},
   787  			},
   788  		},
   789  		// Use an invalid meta value, should get empty result
   790  		{
   791  			filters: map[string]string{"invalid": "nope"},
   792  			nodes:   structs.CheckServiceNodes{},
   793  		},
   794  		// Use multiple filters to get foo's check
   795  		{
   796  			filters: map[string]string{
   797  				"somekey": "somevalue",
   798  				"common":  "1",
   799  			},
   800  			nodes: structs.CheckServiceNodes{
   801  				structs.CheckServiceNode{
   802  					Node:   &structs.Node{Node: "foo"},
   803  					Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}},
   804  				},
   805  			},
   806  		},
   807  	}
   808  
   809  	for _, tc := range cases {
   810  		var out structs.IndexedCheckServiceNodes
   811  		req := structs.ServiceSpecificRequest{
   812  			Datacenter:      "dc1",
   813  			NodeMetaFilters: tc.filters,
   814  			ServiceName:     "db",
   815  		}
   816  		if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out); err != nil {
   817  			t.Fatalf("err: %v", err)
   818  		}
   819  
   820  		if len(out.Nodes) != len(tc.nodes) {
   821  			t.Fatalf("bad: %v, %v, filters: %v", out.Nodes, tc.nodes, tc.filters)
   822  		}
   823  
   824  		for i, node := range out.Nodes {
   825  			checks := tc.nodes[i].Checks
   826  			if len(node.Checks) != len(checks) {
   827  				t.Fatalf("bad: %v, %v, filters: %v", node.Checks, checks, tc.filters)
   828  			}
   829  			for j, check := range node.Checks {
   830  				if check.Name != checks[j].Name {
   831  					t.Fatalf("bad: %v, %v, filters: %v", check, checks[j], tc.filters)
   832  				}
   833  			}
   834  		}
   835  	}
   836  }
   837  
   838  func TestHealth_ServiceNodes_DistanceSort(t *testing.T) {
   839  	t.Parallel()
   840  	dir1, s1 := testServer(t)
   841  	defer os.RemoveAll(dir1)
   842  	defer s1.Shutdown()
   843  	codec := rpcClient(t, s1)
   844  	defer codec.Close()
   845  
   846  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   847  	if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil {
   848  		t.Fatalf("err: %v", err)
   849  	}
   850  	if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil {
   851  		t.Fatalf("err: %v", err)
   852  	}
   853  	updates := structs.Coordinates{
   854  		{Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)},
   855  		{Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)},
   856  	}
   857  	if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil {
   858  		t.Fatalf("err: %v", err)
   859  	}
   860  
   861  	arg := structs.RegisterRequest{
   862  		Datacenter: "dc1",
   863  		Node:       "foo",
   864  		Address:    "127.0.0.1",
   865  		Service: &structs.NodeService{
   866  			ID:      "db",
   867  			Service: "db",
   868  		},
   869  		Check: &structs.HealthCheck{
   870  			Name:      "db connect",
   871  			Status:    api.HealthPassing,
   872  			ServiceID: "db",
   873  		},
   874  	}
   875  
   876  	var out struct{}
   877  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   878  		t.Fatalf("err: %v", err)
   879  	}
   880  
   881  	arg.Node = "bar"
   882  	if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
   883  		t.Fatalf("err: %v", err)
   884  	}
   885  
   886  	// Query relative to foo to make sure it shows up first in the list.
   887  	var out2 structs.IndexedCheckServiceNodes
   888  	req := structs.ServiceSpecificRequest{
   889  		Datacenter:  "dc1",
   890  		ServiceName: "db",
   891  		Source: structs.QuerySource{
   892  			Datacenter: "dc1",
   893  			Node:       "foo",
   894  		},
   895  	}
   896  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil {
   897  		t.Fatalf("err: %v", err)
   898  	}
   899  	nodes := out2.Nodes
   900  	if len(nodes) != 2 {
   901  		t.Fatalf("Bad: %v", nodes)
   902  	}
   903  	if nodes[0].Node.Node != "foo" {
   904  		t.Fatalf("Bad: %v", nodes[0])
   905  	}
   906  	if nodes[1].Node.Node != "bar" {
   907  		t.Fatalf("Bad: %v", nodes[1])
   908  	}
   909  
   910  	// Now query relative to bar to make sure it shows up first.
   911  	req.Source.Node = "bar"
   912  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil {
   913  		t.Fatalf("err: %v", err)
   914  	}
   915  	nodes = out2.Nodes
   916  	if len(nodes) != 2 {
   917  		t.Fatalf("Bad: %v", nodes)
   918  	}
   919  	if nodes[0].Node.Node != "bar" {
   920  		t.Fatalf("Bad: %v", nodes[0])
   921  	}
   922  	if nodes[1].Node.Node != "foo" {
   923  		t.Fatalf("Bad: %v", nodes[1])
   924  	}
   925  }
   926  
   927  func TestHealth_ServiceNodes_ConnectProxy_ACL(t *testing.T) {
   928  	t.Parallel()
   929  
   930  	assert := assert.New(t)
   931  	dir1, s1 := testServerWithConfig(t, func(c *Config) {
   932  		c.ACLDatacenter = "dc1"
   933  		c.ACLsEnabled = true
   934  		c.ACLMasterToken = "root"
   935  		c.ACLDefaultPolicy = "deny"
   936  		c.ACLEnforceVersion8 = false
   937  	})
   938  	defer os.RemoveAll(dir1)
   939  	defer s1.Shutdown()
   940  	codec := rpcClient(t, s1)
   941  	defer codec.Close()
   942  
   943  	testrpc.WaitForLeader(t, s1.RPC, "dc1")
   944  
   945  	// Create the ACL.
   946  	arg := structs.ACLRequest{
   947  		Datacenter: "dc1",
   948  		Op:         structs.ACLSet,
   949  		ACL: structs.ACL{
   950  			Name: "User token",
   951  			Type: structs.ACLTokenTypeClient,
   952  			Rules: `
   953  service "foo" {
   954  	policy = "write"
   955  }
   956  `,
   957  		},
   958  		WriteRequest: structs.WriteRequest{Token: "root"},
   959  	}
   960  	var token string
   961  	assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", arg, &token))
   962  
   963  	{
   964  		var out struct{}
   965  
   966  		// Register a service
   967  		args := structs.TestRegisterRequestProxy(t)
   968  		args.WriteRequest.Token = "root"
   969  		args.Service.ID = "foo-proxy-0"
   970  		args.Service.Service = "foo-proxy"
   971  		args.Service.Proxy.DestinationServiceName = "bar"
   972  		args.Check = &structs.HealthCheck{
   973  			Name:      "proxy",
   974  			Status:    api.HealthPassing,
   975  			ServiceID: args.Service.ID,
   976  		}
   977  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
   978  
   979  		// Register a service
   980  		args = structs.TestRegisterRequestProxy(t)
   981  		args.WriteRequest.Token = "root"
   982  		args.Service.Service = "foo-proxy"
   983  		args.Service.Proxy.DestinationServiceName = "foo"
   984  		args.Check = &structs.HealthCheck{
   985  			Name:      "proxy",
   986  			Status:    api.HealthPassing,
   987  			ServiceID: args.Service.Service,
   988  		}
   989  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
   990  
   991  		// Register a service
   992  		args = structs.TestRegisterRequestProxy(t)
   993  		args.WriteRequest.Token = "root"
   994  		args.Service.Service = "another-proxy"
   995  		args.Service.Proxy.DestinationServiceName = "foo"
   996  		args.Check = &structs.HealthCheck{
   997  			Name:      "proxy",
   998  			Status:    api.HealthPassing,
   999  			ServiceID: args.Service.Service,
  1000  		}
  1001  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
  1002  	}
  1003  
  1004  	// List w/ token. This should disallow because we don't have permission
  1005  	// to read "bar"
  1006  	req := structs.ServiceSpecificRequest{
  1007  		Connect:      true,
  1008  		Datacenter:   "dc1",
  1009  		ServiceName:  "bar",
  1010  		QueryOptions: structs.QueryOptions{Token: token},
  1011  	}
  1012  	var resp structs.IndexedCheckServiceNodes
  1013  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &resp))
  1014  	assert.Len(resp.Nodes, 0)
  1015  
  1016  	// List w/ token. This should work since we're requesting "foo", but should
  1017  	// also only contain the proxies with names that adhere to our ACL.
  1018  	req = structs.ServiceSpecificRequest{
  1019  		Connect:      true,
  1020  		Datacenter:   "dc1",
  1021  		ServiceName:  "foo",
  1022  		QueryOptions: structs.QueryOptions{Token: token},
  1023  	}
  1024  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &resp))
  1025  	assert.Len(resp.Nodes, 1)
  1026  }
  1027  
  1028  func TestHealth_NodeChecks_FilterACL(t *testing.T) {
  1029  	t.Parallel()
  1030  	dir, token, srv, codec := testACLFilterServer(t)
  1031  	defer os.RemoveAll(dir)
  1032  	defer srv.Shutdown()
  1033  	defer codec.Close()
  1034  
  1035  	opt := structs.NodeSpecificRequest{
  1036  		Datacenter:   "dc1",
  1037  		Node:         srv.config.NodeName,
  1038  		QueryOptions: structs.QueryOptions{Token: token},
  1039  	}
  1040  	reply := structs.IndexedHealthChecks{}
  1041  	if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &opt, &reply); err != nil {
  1042  		t.Fatalf("err: %s", err)
  1043  	}
  1044  	found := false
  1045  	for _, chk := range reply.HealthChecks {
  1046  		switch chk.ServiceName {
  1047  		case "foo":
  1048  			found = true
  1049  		case "bar":
  1050  			t.Fatalf("bad: %#v", reply.HealthChecks)
  1051  		}
  1052  	}
  1053  	if !found {
  1054  		t.Fatalf("bad: %#v", reply.HealthChecks)
  1055  	}
  1056  
  1057  	// We've already proven that we call the ACL filtering function so we
  1058  	// test node filtering down in acl.go for node cases. This also proves
  1059  	// that we respect the version 8 ACL flag, since the test server sets
  1060  	// that to false (the regression value of *not* changing this is better
  1061  	// for now until we change the sense of the version 8 ACL flag).
  1062  }
  1063  
  1064  func TestHealth_ServiceChecks_FilterACL(t *testing.T) {
  1065  	t.Parallel()
  1066  	dir, token, srv, codec := testACLFilterServer(t)
  1067  	defer os.RemoveAll(dir)
  1068  	defer srv.Shutdown()
  1069  	defer codec.Close()
  1070  
  1071  	opt := structs.ServiceSpecificRequest{
  1072  		Datacenter:   "dc1",
  1073  		ServiceName:  "foo",
  1074  		QueryOptions: structs.QueryOptions{Token: token},
  1075  	}
  1076  	reply := structs.IndexedHealthChecks{}
  1077  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &opt, &reply); err != nil {
  1078  		t.Fatalf("err: %s", err)
  1079  	}
  1080  	found := false
  1081  	for _, chk := range reply.HealthChecks {
  1082  		if chk.ServiceName == "foo" {
  1083  			found = true
  1084  			break
  1085  		}
  1086  	}
  1087  	if !found {
  1088  		t.Fatalf("bad: %#v", reply.HealthChecks)
  1089  	}
  1090  
  1091  	opt.ServiceName = "bar"
  1092  	reply = structs.IndexedHealthChecks{}
  1093  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &opt, &reply); err != nil {
  1094  		t.Fatalf("err: %s", err)
  1095  	}
  1096  	if len(reply.HealthChecks) != 0 {
  1097  		t.Fatalf("bad: %#v", reply.HealthChecks)
  1098  	}
  1099  
  1100  	// We've already proven that we call the ACL filtering function so we
  1101  	// test node filtering down in acl.go for node cases. This also proves
  1102  	// that we respect the version 8 ACL flag, since the test server sets
  1103  	// that to false (the regression value of *not* changing this is better
  1104  	// for now until we change the sense of the version 8 ACL flag).
  1105  }
  1106  
  1107  func TestHealth_ServiceNodes_FilterACL(t *testing.T) {
  1108  	t.Parallel()
  1109  	dir, token, srv, codec := testACLFilterServer(t)
  1110  	defer os.RemoveAll(dir)
  1111  	defer srv.Shutdown()
  1112  	defer codec.Close()
  1113  
  1114  	opt := structs.ServiceSpecificRequest{
  1115  		Datacenter:   "dc1",
  1116  		ServiceName:  "foo",
  1117  		QueryOptions: structs.QueryOptions{Token: token},
  1118  	}
  1119  	reply := structs.IndexedCheckServiceNodes{}
  1120  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &opt, &reply); err != nil {
  1121  		t.Fatalf("err: %s", err)
  1122  	}
  1123  	if len(reply.Nodes) != 1 {
  1124  		t.Fatalf("bad: %#v", reply.Nodes)
  1125  	}
  1126  
  1127  	opt.ServiceName = "bar"
  1128  	reply = structs.IndexedCheckServiceNodes{}
  1129  	if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &opt, &reply); err != nil {
  1130  		t.Fatalf("err: %s", err)
  1131  	}
  1132  	if len(reply.Nodes) != 0 {
  1133  		t.Fatalf("bad: %#v", reply.Nodes)
  1134  	}
  1135  
  1136  	// We've already proven that we call the ACL filtering function so we
  1137  	// test node filtering down in acl.go for node cases. This also proves
  1138  	// that we respect the version 8 ACL flag, since the test server sets
  1139  	// that to false (the regression value of *not* changing this is better
  1140  	// for now until we change the sense of the version 8 ACL flag).
  1141  }
  1142  
  1143  func TestHealth_ChecksInState_FilterACL(t *testing.T) {
  1144  	t.Parallel()
  1145  	dir, token, srv, codec := testACLFilterServer(t)
  1146  	defer os.RemoveAll(dir)
  1147  	defer srv.Shutdown()
  1148  	defer codec.Close()
  1149  
  1150  	opt := structs.ChecksInStateRequest{
  1151  		Datacenter:   "dc1",
  1152  		State:        api.HealthPassing,
  1153  		QueryOptions: structs.QueryOptions{Token: token},
  1154  	}
  1155  	reply := structs.IndexedHealthChecks{}
  1156  	if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &opt, &reply); err != nil {
  1157  		t.Fatalf("err: %s", err)
  1158  	}
  1159  
  1160  	found := false
  1161  	for _, chk := range reply.HealthChecks {
  1162  		switch chk.ServiceName {
  1163  		case "foo":
  1164  			found = true
  1165  		case "bar":
  1166  			t.Fatalf("bad service 'bar': %#v", reply.HealthChecks)
  1167  		}
  1168  	}
  1169  	if !found {
  1170  		t.Fatalf("missing service 'foo': %#v", reply.HealthChecks)
  1171  	}
  1172  
  1173  	// We've already proven that we call the ACL filtering function so we
  1174  	// test node filtering down in acl.go for node cases. This also proves
  1175  	// that we respect the version 8 ACL flag, since the test server sets
  1176  	// that to false (the regression value of *not* changing this is better
  1177  	// for now until we change the sense of the version 8 ACL flag).
  1178  }