github.com/manicqin/nomad@v0.9.5/command/agent/http_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"net/url"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/hashicorp/nomad/nomad/mock"
    19  	"github.com/hashicorp/nomad/nomad/structs"
    20  	"github.com/hashicorp/nomad/nomad/structs/config"
    21  	"github.com/hashicorp/nomad/testutil"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	"github.com/ugorji/go/codec"
    25  )
    26  
    27  // makeHTTPServer returns a test server whose logs will be written to
    28  // the passed writer. If the writer is nil, the logs are written to stderr.
    29  func makeHTTPServer(t testing.TB, cb func(c *Config)) *TestAgent {
    30  	return NewTestAgent(t, t.Name(), cb)
    31  }
    32  
    33  func BenchmarkHTTPRequests(b *testing.B) {
    34  	s := makeHTTPServer(b, func(c *Config) {
    35  		c.Client.Enabled = false
    36  	})
    37  	defer s.Shutdown()
    38  
    39  	job := mock.Job()
    40  	var allocs []*structs.Allocation
    41  	count := 1000
    42  	for i := 0; i < count; i++ {
    43  		alloc := mock.Alloc()
    44  		alloc.Job = job
    45  		alloc.JobID = job.ID
    46  		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
    47  		allocs = append(allocs, alloc)
    48  	}
    49  
    50  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
    51  		return allocs[:count], nil
    52  	}
    53  	b.ResetTimer()
    54  
    55  	b.RunParallel(func(pb *testing.PB) {
    56  		for pb.Next() {
    57  			resp := httptest.NewRecorder()
    58  			req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
    59  			s.Server.wrap(handler)(resp, req)
    60  		}
    61  	})
    62  }
    63  
    64  // TestRootFallthrough tests rootFallthrough handler to
    65  // verify redirect and 404 behavior
    66  func TestRootFallthrough(t *testing.T) {
    67  	t.Parallel()
    68  
    69  	cases := []struct {
    70  		desc         string
    71  		path         string
    72  		expectedPath string
    73  		expectedCode int
    74  	}{
    75  		{
    76  			desc:         "unknown endpoint 404s",
    77  			path:         "/v1/unknown/endpoint",
    78  			expectedCode: 404,
    79  		},
    80  		{
    81  			desc:         "root path redirects to ui",
    82  			path:         "/",
    83  			expectedPath: "/ui/",
    84  			expectedCode: 307,
    85  		},
    86  	}
    87  
    88  	s := makeHTTPServer(t, nil)
    89  	defer s.Shutdown()
    90  
    91  	// setup a client that doesn't follow redirects
    92  	client := &http.Client{
    93  		CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
    94  			return http.ErrUseLastResponse
    95  		},
    96  	}
    97  
    98  	for _, tc := range cases {
    99  		t.Run(tc.desc, func(t *testing.T) {
   100  
   101  			reqURL := fmt.Sprintf("http://%s%s", s.Agent.config.AdvertiseAddrs.HTTP, tc.path)
   102  
   103  			resp, err := client.Get(reqURL)
   104  			require.NoError(t, err)
   105  			require.Equal(t, tc.expectedCode, resp.StatusCode)
   106  
   107  			if tc.expectedPath != "" {
   108  				loc, err := resp.Location()
   109  				require.NoError(t, err)
   110  				require.Equal(t, tc.expectedPath, loc.Path)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func TestSetIndex(t *testing.T) {
   117  	t.Parallel()
   118  	resp := httptest.NewRecorder()
   119  	setIndex(resp, 1000)
   120  	header := resp.Header().Get("X-Nomad-Index")
   121  	if header != "1000" {
   122  		t.Fatalf("Bad: %v", header)
   123  	}
   124  	setIndex(resp, 2000)
   125  	if v := resp.Header()["X-Nomad-Index"]; len(v) != 1 {
   126  		t.Fatalf("bad: %#v", v)
   127  	}
   128  }
   129  
   130  func TestSetKnownLeader(t *testing.T) {
   131  	t.Parallel()
   132  	resp := httptest.NewRecorder()
   133  	setKnownLeader(resp, true)
   134  	header := resp.Header().Get("X-Nomad-KnownLeader")
   135  	if header != "true" {
   136  		t.Fatalf("Bad: %v", header)
   137  	}
   138  	resp = httptest.NewRecorder()
   139  	setKnownLeader(resp, false)
   140  	header = resp.Header().Get("X-Nomad-KnownLeader")
   141  	if header != "false" {
   142  		t.Fatalf("Bad: %v", header)
   143  	}
   144  }
   145  
   146  func TestSetLastContact(t *testing.T) {
   147  	t.Parallel()
   148  	resp := httptest.NewRecorder()
   149  	setLastContact(resp, 123456*time.Microsecond)
   150  	header := resp.Header().Get("X-Nomad-LastContact")
   151  	if header != "123" {
   152  		t.Fatalf("Bad: %v", header)
   153  	}
   154  }
   155  
   156  func TestSetMeta(t *testing.T) {
   157  	t.Parallel()
   158  	meta := structs.QueryMeta{
   159  		Index:       1000,
   160  		KnownLeader: true,
   161  		LastContact: 123456 * time.Microsecond,
   162  	}
   163  	resp := httptest.NewRecorder()
   164  	setMeta(resp, &meta)
   165  	header := resp.Header().Get("X-Nomad-Index")
   166  	if header != "1000" {
   167  		t.Fatalf("Bad: %v", header)
   168  	}
   169  	header = resp.Header().Get("X-Nomad-KnownLeader")
   170  	if header != "true" {
   171  		t.Fatalf("Bad: %v", header)
   172  	}
   173  	header = resp.Header().Get("X-Nomad-LastContact")
   174  	if header != "123" {
   175  		t.Fatalf("Bad: %v", header)
   176  	}
   177  }
   178  
   179  func TestSetHeaders(t *testing.T) {
   180  	t.Parallel()
   181  	s := makeHTTPServer(t, nil)
   182  	s.Agent.config.HTTPAPIResponseHeaders = map[string]string{"foo": "bar"}
   183  	defer s.Shutdown()
   184  
   185  	resp := httptest.NewRecorder()
   186  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   187  		return &structs.Job{Name: "foo"}, nil
   188  	}
   189  
   190  	req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   191  	s.Server.wrap(handler)(resp, req)
   192  	header := resp.Header().Get("foo")
   193  
   194  	if header != "bar" {
   195  		t.Fatalf("expected header: %v, actual: %v", "bar", header)
   196  	}
   197  
   198  }
   199  
   200  func TestContentTypeIsJSON(t *testing.T) {
   201  	t.Parallel()
   202  	s := makeHTTPServer(t, nil)
   203  	defer s.Shutdown()
   204  
   205  	resp := httptest.NewRecorder()
   206  
   207  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   208  		return &structs.Job{Name: "foo"}, nil
   209  	}
   210  
   211  	req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   212  	s.Server.wrap(handler)(resp, req)
   213  
   214  	contentType := resp.Header().Get("Content-Type")
   215  
   216  	if contentType != "application/json" {
   217  		t.Fatalf("Content-Type header was not 'application/json'")
   218  	}
   219  }
   220  
   221  func TestWrapNonJSON(t *testing.T) {
   222  	t.Parallel()
   223  	s := makeHTTPServer(t, nil)
   224  	defer s.Shutdown()
   225  
   226  	resp := httptest.NewRecorder()
   227  
   228  	handler := func(resp http.ResponseWriter, req *http.Request) ([]byte, error) {
   229  		return []byte("test response"), nil
   230  	}
   231  
   232  	req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   233  	s.Server.wrapNonJSON(handler)(resp, req)
   234  
   235  	respBody, _ := ioutil.ReadAll(resp.Body)
   236  	require.Equal(t, respBody, []byte("test response"))
   237  
   238  }
   239  
   240  func TestWrapNonJSON_Error(t *testing.T) {
   241  	t.Parallel()
   242  	s := makeHTTPServer(t, nil)
   243  	defer s.Shutdown()
   244  
   245  	handlerRPCErr := func(resp http.ResponseWriter, req *http.Request) ([]byte, error) {
   246  		return nil, structs.NewErrRPCCoded(404, "not found")
   247  	}
   248  
   249  	handlerCodedErr := func(resp http.ResponseWriter, req *http.Request) ([]byte, error) {
   250  		return nil, CodedError(422, "unprocessable")
   251  	}
   252  
   253  	// RPC coded error
   254  	{
   255  		resp := httptest.NewRecorder()
   256  		req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   257  		s.Server.wrapNonJSON(handlerRPCErr)(resp, req)
   258  		respBody, _ := ioutil.ReadAll(resp.Body)
   259  		require.Equal(t, []byte("not found"), respBody)
   260  		require.Equal(t, 404, resp.Code)
   261  	}
   262  
   263  	// CodedError
   264  	{
   265  		resp := httptest.NewRecorder()
   266  		req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   267  		s.Server.wrapNonJSON(handlerCodedErr)(resp, req)
   268  		respBody, _ := ioutil.ReadAll(resp.Body)
   269  		require.Equal(t, []byte("unprocessable"), respBody)
   270  		require.Equal(t, 422, resp.Code)
   271  	}
   272  
   273  }
   274  
   275  func TestPrettyPrint(t *testing.T) {
   276  	t.Parallel()
   277  	testPrettyPrint("pretty=1", true, t)
   278  }
   279  
   280  func TestPrettyPrintOff(t *testing.T) {
   281  	t.Parallel()
   282  	testPrettyPrint("pretty=0", false, t)
   283  }
   284  
   285  func TestPrettyPrintBare(t *testing.T) {
   286  	t.Parallel()
   287  	testPrettyPrint("pretty", true, t)
   288  }
   289  
   290  func testPrettyPrint(pretty string, prettyFmt bool, t *testing.T) {
   291  	s := makeHTTPServer(t, nil)
   292  	defer s.Shutdown()
   293  
   294  	r := &structs.Job{Name: "foo"}
   295  
   296  	resp := httptest.NewRecorder()
   297  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   298  		return r, nil
   299  	}
   300  
   301  	urlStr := "/v1/job/foo?" + pretty
   302  	req, _ := http.NewRequest("GET", urlStr, nil)
   303  	s.Server.wrap(handler)(resp, req)
   304  
   305  	var expected bytes.Buffer
   306  	var err error
   307  	if prettyFmt {
   308  		enc := codec.NewEncoder(&expected, structs.JsonHandlePretty)
   309  		err = enc.Encode(r)
   310  		expected.WriteByte('\n')
   311  	} else {
   312  		enc := codec.NewEncoder(&expected, structs.JsonHandle)
   313  		err = enc.Encode(r)
   314  	}
   315  	if err != nil {
   316  		t.Fatalf("failed to encode: %v", err)
   317  	}
   318  	actual, err := ioutil.ReadAll(resp.Body)
   319  	if err != nil {
   320  		t.Fatalf("err: %s", err)
   321  	}
   322  
   323  	if !bytes.Equal(expected.Bytes(), actual) {
   324  		t.Fatalf("bad:\nexpected:\t%q\nactual:\t\t%q", expected.String(), string(actual))
   325  	}
   326  }
   327  
   328  func TestPermissionDenied(t *testing.T) {
   329  	s := makeHTTPServer(t, func(c *Config) {
   330  		c.ACL.Enabled = true
   331  	})
   332  	defer s.Shutdown()
   333  
   334  	{
   335  		resp := httptest.NewRecorder()
   336  		handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   337  			return nil, structs.ErrPermissionDenied
   338  		}
   339  
   340  		req, _ := http.NewRequest("GET", "/v1/job/foo", nil)
   341  		s.Server.wrap(handler)(resp, req)
   342  		assert.Equal(t, resp.Code, 403)
   343  	}
   344  
   345  	// When remote RPC is used the errors have "rpc error: " prependend
   346  	{
   347  		resp := httptest.NewRecorder()
   348  		handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   349  			return nil, fmt.Errorf("rpc error: %v", structs.ErrPermissionDenied)
   350  		}
   351  
   352  		req, _ := http.NewRequest("GET", "/v1/job/foo", nil)
   353  		s.Server.wrap(handler)(resp, req)
   354  		assert.Equal(t, resp.Code, 403)
   355  	}
   356  }
   357  
   358  func TestTokenNotFound(t *testing.T) {
   359  	s := makeHTTPServer(t, func(c *Config) {
   360  		c.ACL.Enabled = true
   361  	})
   362  	defer s.Shutdown()
   363  
   364  	resp := httptest.NewRecorder()
   365  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   366  		return nil, structs.ErrTokenNotFound
   367  	}
   368  
   369  	urlStr := "/v1/job/foo"
   370  	req, _ := http.NewRequest("GET", urlStr, nil)
   371  	s.Server.wrap(handler)(resp, req)
   372  	assert.Equal(t, resp.Code, 403)
   373  }
   374  
   375  func TestParseWait(t *testing.T) {
   376  	t.Parallel()
   377  	resp := httptest.NewRecorder()
   378  	var b structs.QueryOptions
   379  
   380  	req, err := http.NewRequest("GET",
   381  		"/v1/catalog/nodes?wait=60s&index=1000", nil)
   382  	if err != nil {
   383  		t.Fatalf("err: %v", err)
   384  	}
   385  
   386  	if d := parseWait(resp, req, &b); d {
   387  		t.Fatalf("unexpected done")
   388  	}
   389  
   390  	if b.MinQueryIndex != 1000 {
   391  		t.Fatalf("Bad: %v", b)
   392  	}
   393  	if b.MaxQueryTime != 60*time.Second {
   394  		t.Fatalf("Bad: %v", b)
   395  	}
   396  }
   397  
   398  func TestParseWait_InvalidTime(t *testing.T) {
   399  	t.Parallel()
   400  	resp := httptest.NewRecorder()
   401  	var b structs.QueryOptions
   402  
   403  	req, err := http.NewRequest("GET",
   404  		"/v1/catalog/nodes?wait=60foo&index=1000", nil)
   405  	if err != nil {
   406  		t.Fatalf("err: %v", err)
   407  	}
   408  
   409  	if d := parseWait(resp, req, &b); !d {
   410  		t.Fatalf("expected done")
   411  	}
   412  
   413  	if resp.Code != 400 {
   414  		t.Fatalf("bad code: %v", resp.Code)
   415  	}
   416  }
   417  
   418  func TestParseWait_InvalidIndex(t *testing.T) {
   419  	t.Parallel()
   420  	resp := httptest.NewRecorder()
   421  	var b structs.QueryOptions
   422  
   423  	req, err := http.NewRequest("GET",
   424  		"/v1/catalog/nodes?wait=60s&index=foo", nil)
   425  	if err != nil {
   426  		t.Fatalf("err: %v", err)
   427  	}
   428  
   429  	if d := parseWait(resp, req, &b); !d {
   430  		t.Fatalf("expected done")
   431  	}
   432  
   433  	if resp.Code != 400 {
   434  		t.Fatalf("bad code: %v", resp.Code)
   435  	}
   436  }
   437  
   438  func TestParseConsistency(t *testing.T) {
   439  	t.Parallel()
   440  	var b structs.QueryOptions
   441  
   442  	req, err := http.NewRequest("GET",
   443  		"/v1/catalog/nodes?stale", nil)
   444  	if err != nil {
   445  		t.Fatalf("err: %v", err)
   446  	}
   447  
   448  	parseConsistency(req, &b)
   449  	if !b.AllowStale {
   450  		t.Fatalf("Bad: %v", b)
   451  	}
   452  
   453  	b = structs.QueryOptions{}
   454  	req, err = http.NewRequest("GET",
   455  		"/v1/catalog/nodes?consistent", nil)
   456  	if err != nil {
   457  		t.Fatalf("err: %v", err)
   458  	}
   459  
   460  	parseConsistency(req, &b)
   461  	if b.AllowStale {
   462  		t.Fatalf("Bad: %v", b)
   463  	}
   464  }
   465  
   466  func TestParseRegion(t *testing.T) {
   467  	t.Parallel()
   468  	s := makeHTTPServer(t, nil)
   469  	defer s.Shutdown()
   470  
   471  	req, err := http.NewRequest("GET",
   472  		"/v1/jobs?region=foo", nil)
   473  	if err != nil {
   474  		t.Fatalf("err: %v", err)
   475  	}
   476  
   477  	var region string
   478  	s.Server.parseRegion(req, &region)
   479  	if region != "foo" {
   480  		t.Fatalf("bad %s", region)
   481  	}
   482  
   483  	region = ""
   484  	req, err = http.NewRequest("GET", "/v1/jobs", nil)
   485  	if err != nil {
   486  		t.Fatalf("err: %v", err)
   487  	}
   488  
   489  	s.Server.parseRegion(req, &region)
   490  	if region != "global" {
   491  		t.Fatalf("bad %s", region)
   492  	}
   493  }
   494  
   495  func TestParseToken(t *testing.T) {
   496  	t.Parallel()
   497  	s := makeHTTPServer(t, nil)
   498  	defer s.Shutdown()
   499  
   500  	req, err := http.NewRequest("GET", "/v1/jobs", nil)
   501  	req.Header.Add("X-Nomad-Token", "foobar")
   502  	if err != nil {
   503  		t.Fatalf("err: %v", err)
   504  	}
   505  
   506  	var token string
   507  	s.Server.parseToken(req, &token)
   508  	if token != "foobar" {
   509  		t.Fatalf("bad %s", token)
   510  	}
   511  }
   512  
   513  // TestHTTP_VerifyHTTPSClient asserts that a client certificate signed by the
   514  // appropriate CA is required when VerifyHTTPSClient=true.
   515  func TestHTTP_VerifyHTTPSClient(t *testing.T) {
   516  	t.Parallel()
   517  	const (
   518  		cafile  = "../../helper/tlsutil/testdata/ca.pem"
   519  		foocert = "../../helper/tlsutil/testdata/nomad-foo.pem"
   520  		fookey  = "../../helper/tlsutil/testdata/nomad-foo-key.pem"
   521  	)
   522  	s := makeHTTPServer(t, func(c *Config) {
   523  		c.Region = "foo" // match the region on foocert
   524  		c.TLSConfig = &config.TLSConfig{
   525  			EnableHTTP:        true,
   526  			VerifyHTTPSClient: true,
   527  			CAFile:            cafile,
   528  			CertFile:          foocert,
   529  			KeyFile:           fookey,
   530  		}
   531  	})
   532  	defer s.Shutdown()
   533  
   534  	reqURL := fmt.Sprintf("https://%s/v1/agent/self", s.Agent.config.AdvertiseAddrs.HTTP)
   535  
   536  	// FAIL: Requests that expect 127.0.0.1 as the name should fail
   537  	resp, err := http.Get(reqURL)
   538  	if err == nil {
   539  		resp.Body.Close()
   540  		t.Fatalf("expected non-nil error but received: %v", resp.StatusCode)
   541  	}
   542  	urlErr, ok := err.(*url.Error)
   543  	if !ok {
   544  		t.Fatalf("expected a *url.Error but received: %T -> %v", err, err)
   545  	}
   546  	hostErr, ok := urlErr.Err.(x509.HostnameError)
   547  	if !ok {
   548  		t.Fatalf("expected a x509.HostnameError but received: %T -> %v", urlErr.Err, urlErr.Err)
   549  	}
   550  	if expected := "127.0.0.1"; hostErr.Host != expected {
   551  		t.Fatalf("expected hostname on error to be %q but found %q", expected, hostErr.Host)
   552  	}
   553  
   554  	// FAIL: Requests that specify a valid hostname but not the CA should
   555  	// fail
   556  	tlsConf := &tls.Config{
   557  		ServerName: "client.regionFoo.nomad",
   558  	}
   559  	transport := &http.Transport{TLSClientConfig: tlsConf}
   560  	client := &http.Client{Transport: transport}
   561  	req, err := http.NewRequest("GET", reqURL, nil)
   562  	if err != nil {
   563  		t.Fatalf("error creating request: %v", err)
   564  	}
   565  	resp, err = client.Do(req)
   566  	if err == nil {
   567  		resp.Body.Close()
   568  		t.Fatalf("expected non-nil error but received: %v", resp.StatusCode)
   569  	}
   570  	urlErr, ok = err.(*url.Error)
   571  	if !ok {
   572  		t.Fatalf("expected a *url.Error but received: %T -> %v", err, err)
   573  	}
   574  	_, ok = urlErr.Err.(x509.UnknownAuthorityError)
   575  	if !ok {
   576  		t.Fatalf("expected a x509.UnknownAuthorityError but received: %T -> %v", urlErr.Err, urlErr.Err)
   577  	}
   578  
   579  	// FAIL: Requests that specify a valid hostname and CA cert but lack a
   580  	// client certificate should fail
   581  	cacertBytes, err := ioutil.ReadFile(cafile)
   582  	if err != nil {
   583  		t.Fatalf("error reading cacert: %v", err)
   584  	}
   585  	tlsConf.RootCAs = x509.NewCertPool()
   586  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   587  	req, err = http.NewRequest("GET", reqURL, nil)
   588  	if err != nil {
   589  		t.Fatalf("error creating request: %v", err)
   590  	}
   591  	resp, err = client.Do(req)
   592  	if err == nil {
   593  		resp.Body.Close()
   594  		t.Fatalf("expected non-nil error but received: %v", resp.StatusCode)
   595  	}
   596  	urlErr, ok = err.(*url.Error)
   597  	if !ok {
   598  		t.Fatalf("expected a *url.Error but received: %T -> %v", err, err)
   599  	}
   600  	opErr, ok := urlErr.Err.(*net.OpError)
   601  	if !ok {
   602  		t.Fatalf("expected a *net.OpErr but received: %T -> %v", urlErr.Err, urlErr.Err)
   603  	}
   604  	const badCertificate = "tls: bad certificate" // from crypto/tls/alert.go:52 and RFC 5246 ยง A.3
   605  	if opErr.Err.Error() != badCertificate {
   606  		t.Fatalf("expected tls.alert bad_certificate but received: %q", opErr.Err.Error())
   607  	}
   608  
   609  	// PASS: Requests that specify a valid hostname, CA cert, and client
   610  	// certificate succeed.
   611  	tlsConf.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   612  		c, err := tls.LoadX509KeyPair(foocert, fookey)
   613  		if err != nil {
   614  			return nil, err
   615  		}
   616  		return &c, nil
   617  	}
   618  	transport = &http.Transport{TLSClientConfig: tlsConf}
   619  	client = &http.Client{Transport: transport}
   620  	req, err = http.NewRequest("GET", reqURL, nil)
   621  	if err != nil {
   622  		t.Fatalf("error creating request: %v", err)
   623  	}
   624  	resp, err = client.Do(req)
   625  	if err != nil {
   626  		t.Fatalf("unexpected error: %v", err)
   627  	}
   628  	resp.Body.Close()
   629  	if resp.StatusCode != 200 {
   630  		t.Fatalf("expected 200 status code but got: %d", resp.StatusCode)
   631  	}
   632  }
   633  
   634  func TestHTTP_VerifyHTTPSClient_AfterConfigReload(t *testing.T) {
   635  	t.Parallel()
   636  	assert := assert.New(t)
   637  
   638  	const (
   639  		cafile   = "../../helper/tlsutil/testdata/ca.pem"
   640  		foocert  = "../../helper/tlsutil/testdata/nomad-bad.pem"
   641  		fookey   = "../../helper/tlsutil/testdata/nomad-bad-key.pem"
   642  		foocert2 = "../../helper/tlsutil/testdata/nomad-foo.pem"
   643  		fookey2  = "../../helper/tlsutil/testdata/nomad-foo-key.pem"
   644  	)
   645  
   646  	agentConfig := &Config{
   647  		TLSConfig: &config.TLSConfig{
   648  			EnableHTTP:        true,
   649  			VerifyHTTPSClient: true,
   650  			CAFile:            cafile,
   651  			CertFile:          foocert,
   652  			KeyFile:           fookey,
   653  		},
   654  	}
   655  
   656  	newConfig := &Config{
   657  		TLSConfig: &config.TLSConfig{
   658  			EnableHTTP:        true,
   659  			VerifyHTTPSClient: true,
   660  			CAFile:            cafile,
   661  			CertFile:          foocert2,
   662  			KeyFile:           fookey2,
   663  		},
   664  	}
   665  
   666  	s := makeHTTPServer(t, func(c *Config) {
   667  		c.TLSConfig = agentConfig.TLSConfig
   668  	})
   669  	defer s.Shutdown()
   670  
   671  	// Make an initial request that should fail.
   672  	// Requests that specify a valid hostname, CA cert, and client
   673  	// certificate succeed.
   674  	tlsConf := &tls.Config{
   675  		ServerName: "client.regionFoo.nomad",
   676  		RootCAs:    x509.NewCertPool(),
   677  		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   678  			c, err := tls.LoadX509KeyPair(foocert, fookey)
   679  			if err != nil {
   680  				return nil, err
   681  			}
   682  			return &c, nil
   683  		},
   684  	}
   685  
   686  	// HTTPS request should succeed
   687  	httpsReqURL := fmt.Sprintf("https://%s/v1/agent/self", s.Agent.config.AdvertiseAddrs.HTTP)
   688  
   689  	cacertBytes, err := ioutil.ReadFile(cafile)
   690  	assert.Nil(err)
   691  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   692  
   693  	transport := &http.Transport{TLSClientConfig: tlsConf}
   694  	client := &http.Client{Transport: transport}
   695  	req, err := http.NewRequest("GET", httpsReqURL, nil)
   696  	assert.Nil(err)
   697  
   698  	// Check that we get an error that the certificate isn't valid for the
   699  	// region we are contacting.
   700  	_, err = client.Do(req)
   701  	assert.Contains(err.Error(), "certificate is valid for")
   702  
   703  	// Reload the TLS configuration==
   704  	assert.Nil(s.Agent.Reload(newConfig))
   705  
   706  	// Requests that specify a valid hostname, CA cert, and client
   707  	// certificate succeed.
   708  	tlsConf = &tls.Config{
   709  		ServerName: "client.regionFoo.nomad",
   710  		RootCAs:    x509.NewCertPool(),
   711  		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   712  			c, err := tls.LoadX509KeyPair(foocert2, fookey2)
   713  			if err != nil {
   714  				return nil, err
   715  			}
   716  			return &c, nil
   717  		},
   718  	}
   719  
   720  	cacertBytes, err = ioutil.ReadFile(cafile)
   721  	assert.Nil(err)
   722  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   723  
   724  	transport = &http.Transport{TLSClientConfig: tlsConf}
   725  	client = &http.Client{Transport: transport}
   726  	req, err = http.NewRequest("GET", httpsReqURL, nil)
   727  	assert.Nil(err)
   728  
   729  	resp, err := client.Do(req)
   730  	if assert.Nil(err) {
   731  		resp.Body.Close()
   732  		assert.Equal(resp.StatusCode, 200)
   733  	}
   734  }
   735  
   736  func httpTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
   737  	s := makeHTTPServer(t, cb)
   738  	defer s.Shutdown()
   739  	testutil.WaitForLeader(t, s.Agent.RPC)
   740  	f(s)
   741  }
   742  
   743  func httpACLTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
   744  	s := makeHTTPServer(t, func(c *Config) {
   745  		c.ACL.Enabled = true
   746  		if cb != nil {
   747  			cb(c)
   748  		}
   749  	})
   750  	defer s.Shutdown()
   751  	testutil.WaitForLeader(t, s.Agent.RPC)
   752  	f(s)
   753  }
   754  
   755  func setToken(req *http.Request, token *structs.ACLToken) {
   756  	req.Header.Set("X-Nomad-Token", token.SecretID)
   757  }
   758  
   759  func encodeReq(obj interface{}) io.ReadCloser {
   760  	buf := bytes.NewBuffer(nil)
   761  	enc := json.NewEncoder(buf)
   762  	enc.Encode(obj)
   763  	return ioutil.NopCloser(buf)
   764  }