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

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/consul/agent/structs"
    13  	"github.com/hashicorp/consul/api"
    14  	"github.com/hashicorp/consul/testrpc"
    15  	"github.com/hashicorp/consul/testutil/retry"
    16  	"github.com/hashicorp/serf/coordinate"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestHealthChecksInState(t *testing.T) {
    22  	t.Parallel()
    23  	t.Run("warning", func(t *testing.T) {
    24  		a := NewTestAgent(t, t.Name(), "")
    25  		defer a.Shutdown()
    26  
    27  		req, _ := http.NewRequest("GET", "/v1/health/state/warning?dc=dc1", nil)
    28  		retry.Run(t, func(r *retry.R) {
    29  			resp := httptest.NewRecorder()
    30  			obj, err := a.srv.HealthChecksInState(resp, req)
    31  			if err != nil {
    32  				r.Fatal(err)
    33  			}
    34  			if err := checkIndex(resp); err != nil {
    35  				r.Fatal(err)
    36  			}
    37  
    38  			// Should be a non-nil empty list
    39  			nodes := obj.(structs.HealthChecks)
    40  			if nodes == nil || len(nodes) != 0 {
    41  				r.Fatalf("bad: %v", obj)
    42  			}
    43  		})
    44  	})
    45  
    46  	t.Run("passing", func(t *testing.T) {
    47  		a := NewTestAgent(t, t.Name(), "")
    48  		defer a.Shutdown()
    49  
    50  		req, _ := http.NewRequest("GET", "/v1/health/state/passing?dc=dc1", nil)
    51  		retry.Run(t, func(r *retry.R) {
    52  			resp := httptest.NewRecorder()
    53  			obj, err := a.srv.HealthChecksInState(resp, req)
    54  			if err != nil {
    55  				r.Fatal(err)
    56  			}
    57  			if err := checkIndex(resp); err != nil {
    58  				r.Fatal(err)
    59  			}
    60  
    61  			// Should be 1 health check for the server
    62  			nodes := obj.(structs.HealthChecks)
    63  			if len(nodes) != 1 {
    64  				r.Fatalf("bad: %v", obj)
    65  			}
    66  		})
    67  	})
    68  }
    69  
    70  func TestHealthChecksInState_NodeMetaFilter(t *testing.T) {
    71  	t.Parallel()
    72  	a := NewTestAgent(t, t.Name(), "")
    73  	defer a.Shutdown()
    74  
    75  	args := &structs.RegisterRequest{
    76  		Datacenter: "dc1",
    77  		Node:       "bar",
    78  		Address:    "127.0.0.1",
    79  		NodeMeta:   map[string]string{"somekey": "somevalue"},
    80  		Check: &structs.HealthCheck{
    81  			Node:   "bar",
    82  			Name:   "node check",
    83  			Status: api.HealthCritical,
    84  		},
    85  	}
    86  	var out struct{}
    87  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
    88  		t.Fatalf("err: %v", err)
    89  	}
    90  
    91  	req, _ := http.NewRequest("GET", "/v1/health/state/critical?node-meta=somekey:somevalue", nil)
    92  	retry.Run(t, func(r *retry.R) {
    93  		resp := httptest.NewRecorder()
    94  		obj, err := a.srv.HealthChecksInState(resp, req)
    95  		if err != nil {
    96  			r.Fatal(err)
    97  		}
    98  		if err := checkIndex(resp); err != nil {
    99  			r.Fatal(err)
   100  		}
   101  
   102  		// Should be 1 health check for the server
   103  		nodes := obj.(structs.HealthChecks)
   104  		if len(nodes) != 1 {
   105  			r.Fatalf("bad: %v", obj)
   106  		}
   107  	})
   108  }
   109  
   110  func TestHealthChecksInState_DistanceSort(t *testing.T) {
   111  	t.Parallel()
   112  	a := NewTestAgent(t, t.Name(), "")
   113  	defer a.Shutdown()
   114  
   115  	args := &structs.RegisterRequest{
   116  		Datacenter: "dc1",
   117  		Node:       "bar",
   118  		Address:    "127.0.0.1",
   119  		Check: &structs.HealthCheck{
   120  			Node:   "bar",
   121  			Name:   "node check",
   122  			Status: api.HealthCritical,
   123  		},
   124  	}
   125  
   126  	var out struct{}
   127  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   128  		t.Fatalf("err: %v", err)
   129  	}
   130  
   131  	args.Node, args.Check.Node = "foo", "foo"
   132  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   133  		t.Fatalf("err: %v", err)
   134  	}
   135  
   136  	req, _ := http.NewRequest("GET", "/v1/health/state/critical?dc=dc1&near=foo", nil)
   137  	resp := httptest.NewRecorder()
   138  	obj, err := a.srv.HealthChecksInState(resp, req)
   139  	if err != nil {
   140  		t.Fatalf("err: %v", err)
   141  	}
   142  	assertIndex(t, resp)
   143  	nodes := obj.(structs.HealthChecks)
   144  	if len(nodes) != 2 {
   145  		t.Fatalf("bad: %v", nodes)
   146  	}
   147  	if nodes[0].Node != "bar" {
   148  		t.Fatalf("bad: %v", nodes)
   149  	}
   150  	if nodes[1].Node != "foo" {
   151  		t.Fatalf("bad: %v", nodes)
   152  	}
   153  
   154  	// Send an update for the node and wait for it to get applied.
   155  	arg := structs.CoordinateUpdateRequest{
   156  		Datacenter: "dc1",
   157  		Node:       "foo",
   158  		Coord:      coordinate.NewCoordinate(coordinate.DefaultConfig()),
   159  	}
   160  	if err := a.RPC("Coordinate.Update", &arg, &out); err != nil {
   161  		t.Fatalf("err: %v", err)
   162  	}
   163  	// Retry until foo moves to the front of the line.
   164  	retry.Run(t, func(r *retry.R) {
   165  		resp = httptest.NewRecorder()
   166  		obj, err = a.srv.HealthChecksInState(resp, req)
   167  		if err != nil {
   168  			r.Fatalf("err: %v", err)
   169  		}
   170  		assertIndex(t, resp)
   171  		nodes = obj.(structs.HealthChecks)
   172  		if len(nodes) != 2 {
   173  			r.Fatalf("bad: %v", nodes)
   174  		}
   175  		if nodes[0].Node != "foo" {
   176  			r.Fatalf("bad: %v", nodes)
   177  		}
   178  		if nodes[1].Node != "bar" {
   179  			r.Fatalf("bad: %v", nodes)
   180  		}
   181  	})
   182  }
   183  
   184  func TestHealthNodeChecks(t *testing.T) {
   185  	t.Parallel()
   186  	a := NewTestAgent(t, t.Name(), "")
   187  	defer a.Shutdown()
   188  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   189  
   190  	req, _ := http.NewRequest("GET", "/v1/health/node/nope?dc=dc1", nil)
   191  	resp := httptest.NewRecorder()
   192  	obj, err := a.srv.HealthNodeChecks(resp, req)
   193  	if err != nil {
   194  		t.Fatalf("err: %v", err)
   195  	}
   196  	assertIndex(t, resp)
   197  
   198  	// Should be a non-nil empty list
   199  	nodes := obj.(structs.HealthChecks)
   200  	if nodes == nil || len(nodes) != 0 {
   201  		t.Fatalf("bad: %v", obj)
   202  	}
   203  
   204  	req, _ = http.NewRequest("GET", fmt.Sprintf("/v1/health/node/%s?dc=dc1", a.Config.NodeName), nil)
   205  	resp = httptest.NewRecorder()
   206  	obj, err = a.srv.HealthNodeChecks(resp, req)
   207  	if err != nil {
   208  		t.Fatalf("err: %v", err)
   209  	}
   210  	assertIndex(t, resp)
   211  
   212  	// Should be 1 health check for the server
   213  	nodes = obj.(structs.HealthChecks)
   214  	if len(nodes) != 1 {
   215  		t.Fatalf("bad: %v", obj)
   216  	}
   217  }
   218  
   219  func TestHealthServiceChecks(t *testing.T) {
   220  	t.Parallel()
   221  	a := NewTestAgent(t, t.Name(), "")
   222  	defer a.Shutdown()
   223  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   224  
   225  	req, _ := http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1", nil)
   226  	resp := httptest.NewRecorder()
   227  	obj, err := a.srv.HealthServiceChecks(resp, req)
   228  	if err != nil {
   229  		t.Fatalf("err: %v", err)
   230  	}
   231  	assertIndex(t, resp)
   232  
   233  	// Should be a non-nil empty list
   234  	nodes := obj.(structs.HealthChecks)
   235  	if nodes == nil || len(nodes) != 0 {
   236  		t.Fatalf("bad: %v", obj)
   237  	}
   238  
   239  	// Create a service check
   240  	args := &structs.RegisterRequest{
   241  		Datacenter: "dc1",
   242  		Node:       a.Config.NodeName,
   243  		Address:    "127.0.0.1",
   244  		Check: &structs.HealthCheck{
   245  			Node:      a.Config.NodeName,
   246  			Name:      "consul check",
   247  			ServiceID: "consul",
   248  		},
   249  	}
   250  
   251  	var out struct{}
   252  	if err = a.RPC("Catalog.Register", args, &out); err != nil {
   253  		t.Fatalf("err: %v", err)
   254  	}
   255  
   256  	req, _ = http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1", nil)
   257  	resp = httptest.NewRecorder()
   258  	obj, err = a.srv.HealthServiceChecks(resp, req)
   259  	if err != nil {
   260  		t.Fatalf("err: %v", err)
   261  	}
   262  	assertIndex(t, resp)
   263  
   264  	// Should be 1 health check for consul
   265  	nodes = obj.(structs.HealthChecks)
   266  	if len(nodes) != 1 {
   267  		t.Fatalf("bad: %v", obj)
   268  	}
   269  }
   270  
   271  func TestHealthServiceChecks_NodeMetaFilter(t *testing.T) {
   272  	t.Parallel()
   273  	a := NewTestAgent(t, t.Name(), "")
   274  	defer a.Shutdown()
   275  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   276  
   277  	req, _ := http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1&node-meta=somekey:somevalue", nil)
   278  	resp := httptest.NewRecorder()
   279  	obj, err := a.srv.HealthServiceChecks(resp, req)
   280  	if err != nil {
   281  		t.Fatalf("err: %v", err)
   282  	}
   283  	assertIndex(t, resp)
   284  
   285  	// Should be a non-nil empty list
   286  	nodes := obj.(structs.HealthChecks)
   287  	if nodes == nil || len(nodes) != 0 {
   288  		t.Fatalf("bad: %v", obj)
   289  	}
   290  
   291  	// Create a service check
   292  	args := &structs.RegisterRequest{
   293  		Datacenter: "dc1",
   294  		Node:       a.Config.NodeName,
   295  		Address:    "127.0.0.1",
   296  		NodeMeta:   map[string]string{"somekey": "somevalue"},
   297  		Check: &structs.HealthCheck{
   298  			Node:      a.Config.NodeName,
   299  			Name:      "consul check",
   300  			ServiceID: "consul",
   301  		},
   302  	}
   303  
   304  	var out struct{}
   305  	if err = a.RPC("Catalog.Register", args, &out); err != nil {
   306  		t.Fatalf("err: %v", err)
   307  	}
   308  
   309  	req, _ = http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1&node-meta=somekey:somevalue", nil)
   310  	resp = httptest.NewRecorder()
   311  	obj, err = a.srv.HealthServiceChecks(resp, req)
   312  	if err != nil {
   313  		t.Fatalf("err: %v", err)
   314  	}
   315  	assertIndex(t, resp)
   316  
   317  	// Should be 1 health check for consul
   318  	nodes = obj.(structs.HealthChecks)
   319  	if len(nodes) != 1 {
   320  		t.Fatalf("bad: %v", obj)
   321  	}
   322  }
   323  
   324  func TestHealthServiceChecks_DistanceSort(t *testing.T) {
   325  	t.Parallel()
   326  	a := NewTestAgent(t, t.Name(), "")
   327  	defer a.Shutdown()
   328  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   329  
   330  	// Create a service check
   331  	args := &structs.RegisterRequest{
   332  		Datacenter: "dc1",
   333  		Node:       "bar",
   334  		Address:    "127.0.0.1",
   335  		Service: &structs.NodeService{
   336  			ID:      "test",
   337  			Service: "test",
   338  		},
   339  		Check: &structs.HealthCheck{
   340  			Node:      "bar",
   341  			Name:      "test check",
   342  			ServiceID: "test",
   343  		},
   344  	}
   345  
   346  	var out struct{}
   347  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   348  		t.Fatalf("err: %v", err)
   349  	}
   350  
   351  	args.Node, args.Check.Node = "foo", "foo"
   352  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   353  		t.Fatalf("err: %v", err)
   354  	}
   355  
   356  	req, _ := http.NewRequest("GET", "/v1/health/checks/test?dc=dc1&near=foo", nil)
   357  	resp := httptest.NewRecorder()
   358  	obj, err := a.srv.HealthServiceChecks(resp, req)
   359  	if err != nil {
   360  		t.Fatalf("err: %v", err)
   361  	}
   362  	assertIndex(t, resp)
   363  	nodes := obj.(structs.HealthChecks)
   364  	if len(nodes) != 2 {
   365  		t.Fatalf("bad: %v", obj)
   366  	}
   367  	if nodes[0].Node != "bar" {
   368  		t.Fatalf("bad: %v", nodes)
   369  	}
   370  	if nodes[1].Node != "foo" {
   371  		t.Fatalf("bad: %v", nodes)
   372  	}
   373  
   374  	// Send an update for the node and wait for it to get applied.
   375  	arg := structs.CoordinateUpdateRequest{
   376  		Datacenter: "dc1",
   377  		Node:       "foo",
   378  		Coord:      coordinate.NewCoordinate(coordinate.DefaultConfig()),
   379  	}
   380  	if err := a.RPC("Coordinate.Update", &arg, &out); err != nil {
   381  		t.Fatalf("err: %v", err)
   382  	}
   383  	// Retry until foo has moved to the front of the line.
   384  	retry.Run(t, func(r *retry.R) {
   385  		resp = httptest.NewRecorder()
   386  		obj, err = a.srv.HealthServiceChecks(resp, req)
   387  		if err != nil {
   388  			r.Fatalf("err: %v", err)
   389  		}
   390  		assertIndex(t, resp)
   391  		nodes = obj.(structs.HealthChecks)
   392  		if len(nodes) != 2 {
   393  			r.Fatalf("bad: %v", obj)
   394  		}
   395  		if nodes[0].Node != "foo" {
   396  			r.Fatalf("bad: %v", nodes)
   397  		}
   398  		if nodes[1].Node != "bar" {
   399  			r.Fatalf("bad: %v", nodes)
   400  		}
   401  	})
   402  }
   403  
   404  func TestHealthServiceNodes(t *testing.T) {
   405  	t.Parallel()
   406  	a := NewTestAgent(t, t.Name(), "")
   407  	defer a.Shutdown()
   408  	testrpc.WaitForTestAgent(t, a.RPC, "dc1")
   409  
   410  	assert := assert.New(t)
   411  	require := require.New(t)
   412  
   413  	req, _ := http.NewRequest("GET", "/v1/health/service/consul?dc=dc1", nil)
   414  	resp := httptest.NewRecorder()
   415  	obj, err := a.srv.HealthServiceNodes(resp, req)
   416  	if err != nil {
   417  		t.Fatalf("err: %v", err)
   418  	}
   419  
   420  	assertIndex(t, resp)
   421  
   422  	// Should be 1 health check for consul
   423  	nodes := obj.(structs.CheckServiceNodes)
   424  	if len(nodes) != 1 {
   425  		t.Fatalf("bad: %v", obj)
   426  	}
   427  
   428  	req, _ = http.NewRequest("GET", "/v1/health/service/nope?dc=dc1", nil)
   429  	resp = httptest.NewRecorder()
   430  	obj, err = a.srv.HealthServiceNodes(resp, req)
   431  	if err != nil {
   432  		t.Fatalf("err: %v", err)
   433  	}
   434  
   435  	assertIndex(t, resp)
   436  
   437  	// Should be a non-nil empty list
   438  	nodes = obj.(structs.CheckServiceNodes)
   439  	if nodes == nil || len(nodes) != 0 {
   440  		t.Fatalf("bad: %v", obj)
   441  	}
   442  
   443  	args := &structs.RegisterRequest{
   444  		Datacenter: "dc1",
   445  		Node:       "bar",
   446  		Address:    "127.0.0.1",
   447  		Service: &structs.NodeService{
   448  			ID:      "test",
   449  			Service: "test",
   450  		},
   451  	}
   452  
   453  	var out struct{}
   454  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   455  		t.Fatalf("err: %v", err)
   456  	}
   457  
   458  	req, _ = http.NewRequest("GET", "/v1/health/service/test?dc=dc1", nil)
   459  	resp = httptest.NewRecorder()
   460  	obj, err = a.srv.HealthServiceNodes(resp, req)
   461  	if err != nil {
   462  		t.Fatalf("err: %v", err)
   463  	}
   464  
   465  	assertIndex(t, resp)
   466  
   467  	// Should be a non-nil empty list for checks
   468  	nodes = obj.(structs.CheckServiceNodes)
   469  	if len(nodes) != 1 || nodes[0].Checks == nil || len(nodes[0].Checks) != 0 {
   470  		t.Fatalf("bad: %v", obj)
   471  	}
   472  
   473  	// Test caching
   474  	{
   475  		// List instances with cache enabled
   476  		req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil)
   477  		resp := httptest.NewRecorder()
   478  		obj, err := a.srv.HealthServiceNodes(resp, req)
   479  		require.NoError(err)
   480  		nodes := obj.(structs.CheckServiceNodes)
   481  		assert.Len(nodes, 1)
   482  
   483  		// Should be a cache miss
   484  		assert.Equal("MISS", resp.Header().Get("X-Cache"))
   485  	}
   486  
   487  	{
   488  		// List instances with cache enabled
   489  		req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil)
   490  		resp := httptest.NewRecorder()
   491  		obj, err := a.srv.HealthServiceNodes(resp, req)
   492  		require.NoError(err)
   493  		nodes := obj.(structs.CheckServiceNodes)
   494  		assert.Len(nodes, 1)
   495  
   496  		// Should be a cache HIT now!
   497  		assert.Equal("HIT", resp.Header().Get("X-Cache"))
   498  	}
   499  
   500  	// Ensure background refresh works
   501  	{
   502  		// Register a new instance of the service
   503  		args2 := args
   504  		args2.Node = "baz"
   505  		args2.Address = "127.0.0.2"
   506  		require.NoError(a.RPC("Catalog.Register", args, &out))
   507  
   508  		retry.Run(t, func(r *retry.R) {
   509  			// List it again
   510  			req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil)
   511  			resp := httptest.NewRecorder()
   512  			obj, err := a.srv.HealthServiceNodes(resp, req)
   513  			r.Check(err)
   514  
   515  			nodes := obj.(structs.CheckServiceNodes)
   516  			if len(nodes) != 2 {
   517  				r.Fatalf("Want 2 nodes")
   518  			}
   519  
   520  			// Should be a cache hit! The data should've updated in the cache
   521  			// in the background so this should've been fetched directly from
   522  			// the cache.
   523  			if resp.Header().Get("X-Cache") != "HIT" {
   524  				r.Fatalf("should be a cache hit")
   525  			}
   526  		})
   527  	}
   528  }
   529  
   530  func TestHealthServiceNodes_NodeMetaFilter(t *testing.T) {
   531  	t.Parallel()
   532  	a := NewTestAgent(t, t.Name(), "")
   533  	defer a.Shutdown()
   534  	testrpc.WaitForLeader(t, a.RPC, "dc1")
   535  
   536  	req, _ := http.NewRequest("GET", "/v1/health/service/consul?dc=dc1&node-meta=somekey:somevalue", nil)
   537  	resp := httptest.NewRecorder()
   538  	obj, err := a.srv.HealthServiceNodes(resp, req)
   539  	if err != nil {
   540  		t.Fatalf("err: %v", err)
   541  	}
   542  
   543  	assertIndex(t, resp)
   544  
   545  	// Should be a non-nil empty list
   546  	nodes := obj.(structs.CheckServiceNodes)
   547  	if nodes == nil || len(nodes) != 0 {
   548  		t.Fatalf("bad: %v", obj)
   549  	}
   550  
   551  	args := &structs.RegisterRequest{
   552  		Datacenter: "dc1",
   553  		Node:       "bar",
   554  		Address:    "127.0.0.1",
   555  		NodeMeta:   map[string]string{"somekey": "somevalue"},
   556  		Service: &structs.NodeService{
   557  			ID:      "test",
   558  			Service: "test",
   559  		},
   560  	}
   561  
   562  	var out struct{}
   563  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   564  		t.Fatalf("err: %v", err)
   565  	}
   566  
   567  	req, _ = http.NewRequest("GET", "/v1/health/service/test?dc=dc1&node-meta=somekey:somevalue", nil)
   568  	resp = httptest.NewRecorder()
   569  	obj, err = a.srv.HealthServiceNodes(resp, req)
   570  	if err != nil {
   571  		t.Fatalf("err: %v", err)
   572  	}
   573  
   574  	assertIndex(t, resp)
   575  
   576  	// Should be a non-nil empty list for checks
   577  	nodes = obj.(structs.CheckServiceNodes)
   578  	if len(nodes) != 1 || nodes[0].Checks == nil || len(nodes[0].Checks) != 0 {
   579  		t.Fatalf("bad: %v", obj)
   580  	}
   581  }
   582  
   583  func TestHealthServiceNodes_DistanceSort(t *testing.T) {
   584  	t.Parallel()
   585  	a := NewTestAgent(t, t.Name(), "")
   586  	defer a.Shutdown()
   587  	dc := "dc1"
   588  	// Create a service check
   589  	args := &structs.RegisterRequest{
   590  		Datacenter: dc,
   591  		Node:       "bar",
   592  		Address:    "127.0.0.1",
   593  		Service: &structs.NodeService{
   594  			ID:      "test",
   595  			Service: "test",
   596  		},
   597  		Check: &structs.HealthCheck{
   598  			Node:      "bar",
   599  			Name:      "test check",
   600  			ServiceID: "test",
   601  		},
   602  	}
   603  	testrpc.WaitForLeader(t, a.RPC, dc)
   604  	var out struct{}
   605  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   606  		t.Fatalf("err: %v", err)
   607  	}
   608  
   609  	args.Node, args.Check.Node = "foo", "foo"
   610  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   611  		t.Fatalf("err: %v", err)
   612  	}
   613  
   614  	req, _ := http.NewRequest("GET", "/v1/health/service/test?dc=dc1&near=foo", nil)
   615  	resp := httptest.NewRecorder()
   616  	obj, err := a.srv.HealthServiceNodes(resp, req)
   617  	if err != nil {
   618  		t.Fatalf("err: %v", err)
   619  	}
   620  	assertIndex(t, resp)
   621  	nodes := obj.(structs.CheckServiceNodes)
   622  	if len(nodes) != 2 {
   623  		t.Fatalf("bad: %v", obj)
   624  	}
   625  	if nodes[0].Node.Node != "bar" {
   626  		t.Fatalf("bad: %v", nodes)
   627  	}
   628  	if nodes[1].Node.Node != "foo" {
   629  		t.Fatalf("bad: %v", nodes)
   630  	}
   631  
   632  	// Send an update for the node and wait for it to get applied.
   633  	arg := structs.CoordinateUpdateRequest{
   634  		Datacenter: "dc1",
   635  		Node:       "foo",
   636  		Coord:      coordinate.NewCoordinate(coordinate.DefaultConfig()),
   637  	}
   638  	if err := a.RPC("Coordinate.Update", &arg, &out); err != nil {
   639  		t.Fatalf("err: %v", err)
   640  	}
   641  	// Retry until foo has moved to the front of the line.
   642  	retry.Run(t, func(r *retry.R) {
   643  		resp = httptest.NewRecorder()
   644  		obj, err = a.srv.HealthServiceNodes(resp, req)
   645  		if err != nil {
   646  			r.Fatalf("err: %v", err)
   647  		}
   648  		assertIndex(t, resp)
   649  		nodes = obj.(structs.CheckServiceNodes)
   650  		if len(nodes) != 2 {
   651  			r.Fatalf("bad: %v", obj)
   652  		}
   653  		if nodes[0].Node.Node != "foo" {
   654  			r.Fatalf("bad: %v", nodes)
   655  		}
   656  		if nodes[1].Node.Node != "bar" {
   657  			r.Fatalf("bad: %v", nodes)
   658  		}
   659  	})
   660  }
   661  
   662  func TestHealthServiceNodes_PassingFilter(t *testing.T) {
   663  	t.Parallel()
   664  	a := NewTestAgent(t, t.Name(), "")
   665  	defer a.Shutdown()
   666  
   667  	dc := "dc1"
   668  	// Create a failing service check
   669  	args := &structs.RegisterRequest{
   670  		Datacenter: dc,
   671  		Node:       a.Config.NodeName,
   672  		Address:    "127.0.0.1",
   673  		Check: &structs.HealthCheck{
   674  			Node:      a.Config.NodeName,
   675  			Name:      "consul check",
   676  			ServiceID: "consul",
   677  			Status:    api.HealthCritical,
   678  		},
   679  	}
   680  
   681  	testrpc.WaitForLeader(t, a.RPC, dc)
   682  	var out struct{}
   683  	if err := a.RPC("Catalog.Register", args, &out); err != nil {
   684  		t.Fatalf("err: %v", err)
   685  	}
   686  
   687  	t.Run("bc_no_query_value", func(t *testing.T) {
   688  		req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing", nil)
   689  		resp := httptest.NewRecorder()
   690  		obj, err := a.srv.HealthServiceNodes(resp, req)
   691  		if err != nil {
   692  			t.Fatalf("err: %v", err)
   693  		}
   694  
   695  		assertIndex(t, resp)
   696  
   697  		// Should be 0 health check for consul
   698  		nodes := obj.(structs.CheckServiceNodes)
   699  		if len(nodes) != 0 {
   700  			t.Fatalf("bad: %v", obj)
   701  		}
   702  	})
   703  
   704  	t.Run("passing_true", func(t *testing.T) {
   705  		req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=true", nil)
   706  		resp := httptest.NewRecorder()
   707  		obj, err := a.srv.HealthServiceNodes(resp, req)
   708  		if err != nil {
   709  			t.Fatalf("err: %v", err)
   710  		}
   711  
   712  		assertIndex(t, resp)
   713  
   714  		// Should be 0 health check for consul
   715  		nodes := obj.(structs.CheckServiceNodes)
   716  		if len(nodes) != 0 {
   717  			t.Fatalf("bad: %v", obj)
   718  		}
   719  	})
   720  
   721  	t.Run("passing_false", func(t *testing.T) {
   722  		req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=false", nil)
   723  		resp := httptest.NewRecorder()
   724  		obj, err := a.srv.HealthServiceNodes(resp, req)
   725  		if err != nil {
   726  			t.Fatalf("err: %v", err)
   727  		}
   728  
   729  		assertIndex(t, resp)
   730  
   731  		// Should be 1 consul, it's unhealthy, but we specifically asked for
   732  		// everything.
   733  		nodes := obj.(structs.CheckServiceNodes)
   734  		if len(nodes) != 1 {
   735  			t.Fatalf("bad: %v", obj)
   736  		}
   737  	})
   738  
   739  	t.Run("passing_bad", func(t *testing.T) {
   740  		req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=nope-nope-nope", nil)
   741  		resp := httptest.NewRecorder()
   742  		a.srv.HealthServiceNodes(resp, req)
   743  
   744  		if code := resp.Code; code != 400 {
   745  			t.Errorf("bad response code %d, expected %d", code, 400)
   746  		}
   747  
   748  		body, err := ioutil.ReadAll(resp.Body)
   749  		if err != nil {
   750  			t.Fatal(err)
   751  		}
   752  		if !bytes.Contains(body, []byte("Invalid value for ?passing")) {
   753  			t.Errorf("bad %s", body)
   754  		}
   755  	})
   756  }
   757  
   758  func TestHealthServiceNodes_WanTranslation(t *testing.T) {
   759  	t.Parallel()
   760  	a1 := NewTestAgent(t, t.Name(), `
   761  		datacenter = "dc1"
   762  		translate_wan_addrs = true
   763  		acl_datacenter = ""
   764  	`)
   765  	defer a1.Shutdown()
   766  	testrpc.WaitForLeader(t, a1.RPC, "dc1")
   767  
   768  	a2 := NewTestAgent(t, t.Name(), `
   769  		datacenter = "dc2"
   770  		translate_wan_addrs = true
   771  		acl_datacenter = ""
   772  	`)
   773  	defer a2.Shutdown()
   774  	testrpc.WaitForLeader(t, a2.RPC, "dc2")
   775  
   776  	// Wait for the WAN join.
   777  	addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
   778  	if _, err := a2.JoinWAN([]string{addr}); err != nil {
   779  		t.Fatalf("err: %v", err)
   780  	}
   781  	retry.Run(t, func(r *retry.R) {
   782  		if got, want := len(a1.WANMembers()), 2; got < want {
   783  			r.Fatalf("got %d WAN members want at least %d", got, want)
   784  		}
   785  	})
   786  
   787  	// Register a node with DC2.
   788  	{
   789  		args := &structs.RegisterRequest{
   790  			Datacenter: "dc2",
   791  			Node:       "foo",
   792  			Address:    "127.0.0.1",
   793  			TaggedAddresses: map[string]string{
   794  				"wan": "127.0.0.2",
   795  			},
   796  			Service: &structs.NodeService{
   797  				Service: "http_wan_translation_test",
   798  			},
   799  		}
   800  
   801  		var out struct{}
   802  		if err := a2.RPC("Catalog.Register", args, &out); err != nil {
   803  			t.Fatalf("err: %v", err)
   804  		}
   805  	}
   806  
   807  	// Query for a service in DC2 from DC1.
   808  	req, _ := http.NewRequest("GET", "/v1/health/service/http_wan_translation_test?dc=dc2", nil)
   809  	resp1 := httptest.NewRecorder()
   810  	obj1, err1 := a1.srv.HealthServiceNodes(resp1, req)
   811  	if err1 != nil {
   812  		t.Fatalf("err: %v", err1)
   813  	}
   814  	assertIndex(t, resp1)
   815  
   816  	// Expect that DC1 gives us a WAN address (since the node is in DC2).
   817  	nodes1 := obj1.(structs.CheckServiceNodes)
   818  	if len(nodes1) != 1 {
   819  		t.Fatalf("bad: %v", obj1)
   820  	}
   821  	node1 := nodes1[0].Node
   822  	if node1.Address != "127.0.0.2" {
   823  		t.Fatalf("bad: %v", node1)
   824  	}
   825  
   826  	// Query DC2 from DC2.
   827  	resp2 := httptest.NewRecorder()
   828  	obj2, err2 := a2.srv.HealthServiceNodes(resp2, req)
   829  	if err2 != nil {
   830  		t.Fatalf("err: %v", err2)
   831  	}
   832  	assertIndex(t, resp2)
   833  
   834  	// Expect that DC2 gives us a private address (since the node is in DC2).
   835  	nodes2 := obj2.(structs.CheckServiceNodes)
   836  	if len(nodes2) != 1 {
   837  		t.Fatalf("bad: %v", obj2)
   838  	}
   839  	node2 := nodes2[0].Node
   840  	if node2.Address != "127.0.0.1" {
   841  		t.Fatalf("bad: %v", node2)
   842  	}
   843  }
   844  
   845  func TestHealthConnectServiceNodes(t *testing.T) {
   846  	t.Parallel()
   847  
   848  	assert := assert.New(t)
   849  	a := NewTestAgent(t, t.Name(), "")
   850  	defer a.Shutdown()
   851  
   852  	// Register
   853  	args := structs.TestRegisterRequestProxy(t)
   854  	var out struct{}
   855  	assert.Nil(a.RPC("Catalog.Register", args, &out))
   856  
   857  	// Request
   858  	req, _ := http.NewRequest("GET", fmt.Sprintf(
   859  		"/v1/health/connect/%s?dc=dc1", args.Service.Proxy.DestinationServiceName), nil)
   860  	resp := httptest.NewRecorder()
   861  	obj, err := a.srv.HealthConnectServiceNodes(resp, req)
   862  	assert.Nil(err)
   863  	assertIndex(t, resp)
   864  
   865  	// Should be a non-nil empty list for checks
   866  	nodes := obj.(structs.CheckServiceNodes)
   867  	assert.Len(nodes, 1)
   868  	assert.Len(nodes[0].Checks, 0)
   869  }
   870  
   871  func TestHealthConnectServiceNodes_PassingFilter(t *testing.T) {
   872  	t.Parallel()
   873  
   874  	a := NewTestAgent(t, t.Name(), "")
   875  	defer a.Shutdown()
   876  
   877  	// Register
   878  	args := structs.TestRegisterRequestProxy(t)
   879  	args.Check = &structs.HealthCheck{
   880  		Node:      args.Node,
   881  		Name:      "check",
   882  		ServiceID: args.Service.Service,
   883  		Status:    api.HealthCritical,
   884  	}
   885  	var out struct{}
   886  	assert.Nil(t, a.RPC("Catalog.Register", args, &out))
   887  
   888  	t.Run("bc_no_query_value", func(t *testing.T) {
   889  		assert := assert.New(t)
   890  		req, _ := http.NewRequest("GET", fmt.Sprintf(
   891  			"/v1/health/connect/%s?passing", args.Service.Proxy.DestinationServiceName), nil)
   892  		resp := httptest.NewRecorder()
   893  		obj, err := a.srv.HealthConnectServiceNodes(resp, req)
   894  		assert.Nil(err)
   895  		assertIndex(t, resp)
   896  
   897  		// Should be 0 health check for consul
   898  		nodes := obj.(structs.CheckServiceNodes)
   899  		assert.Len(nodes, 0)
   900  	})
   901  
   902  	t.Run("passing_true", func(t *testing.T) {
   903  		assert := assert.New(t)
   904  		req, _ := http.NewRequest("GET", fmt.Sprintf(
   905  			"/v1/health/connect/%s?passing=true", args.Service.Proxy.DestinationServiceName), nil)
   906  		resp := httptest.NewRecorder()
   907  		obj, err := a.srv.HealthConnectServiceNodes(resp, req)
   908  		assert.Nil(err)
   909  		assertIndex(t, resp)
   910  
   911  		// Should be 0 health check for consul
   912  		nodes := obj.(structs.CheckServiceNodes)
   913  		assert.Len(nodes, 0)
   914  	})
   915  
   916  	t.Run("passing_false", func(t *testing.T) {
   917  		assert := assert.New(t)
   918  		req, _ := http.NewRequest("GET", fmt.Sprintf(
   919  			"/v1/health/connect/%s?passing=false", args.Service.Proxy.DestinationServiceName), nil)
   920  		resp := httptest.NewRecorder()
   921  		obj, err := a.srv.HealthConnectServiceNodes(resp, req)
   922  		assert.Nil(err)
   923  		assertIndex(t, resp)
   924  
   925  		// Should be 1
   926  		nodes := obj.(structs.CheckServiceNodes)
   927  		assert.Len(nodes, 1)
   928  	})
   929  
   930  	t.Run("passing_bad", func(t *testing.T) {
   931  		assert := assert.New(t)
   932  		req, _ := http.NewRequest("GET", fmt.Sprintf(
   933  			"/v1/health/connect/%s?passing=nope-nope", args.Service.Proxy.DestinationServiceName), nil)
   934  		resp := httptest.NewRecorder()
   935  		a.srv.HealthConnectServiceNodes(resp, req)
   936  		assert.Equal(400, resp.Code)
   937  
   938  		body, err := ioutil.ReadAll(resp.Body)
   939  		assert.Nil(err)
   940  		assert.True(bytes.Contains(body, []byte("Invalid value for ?passing")))
   941  	})
   942  }
   943  
   944  func TestFilterNonPassing(t *testing.T) {
   945  	t.Parallel()
   946  	nodes := structs.CheckServiceNodes{
   947  		structs.CheckServiceNode{
   948  			Checks: structs.HealthChecks{
   949  				&structs.HealthCheck{
   950  					Status: api.HealthCritical,
   951  				},
   952  				&structs.HealthCheck{
   953  					Status: api.HealthCritical,
   954  				},
   955  			},
   956  		},
   957  		structs.CheckServiceNode{
   958  			Checks: structs.HealthChecks{
   959  				&structs.HealthCheck{
   960  					Status: api.HealthCritical,
   961  				},
   962  				&structs.HealthCheck{
   963  					Status: api.HealthCritical,
   964  				},
   965  			},
   966  		},
   967  		structs.CheckServiceNode{
   968  			Checks: structs.HealthChecks{
   969  				&structs.HealthCheck{
   970  					Status: api.HealthPassing,
   971  				},
   972  			},
   973  		},
   974  	}
   975  	out := filterNonPassing(nodes)
   976  	if len(out) != 1 && reflect.DeepEqual(out[0], nodes[2]) {
   977  		t.Fatalf("bad: %v", out)
   978  	}
   979  }