github.com/karlem/nomad@v0.10.2-rc1/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 TestPrettyPrint(t *testing.T) {
   222  	t.Parallel()
   223  	testPrettyPrint("pretty=1", true, t)
   224  }
   225  
   226  func TestPrettyPrintOff(t *testing.T) {
   227  	t.Parallel()
   228  	testPrettyPrint("pretty=0", false, t)
   229  }
   230  
   231  func TestPrettyPrintBare(t *testing.T) {
   232  	t.Parallel()
   233  	testPrettyPrint("pretty", true, t)
   234  }
   235  
   236  func testPrettyPrint(pretty string, prettyFmt bool, t *testing.T) {
   237  	s := makeHTTPServer(t, nil)
   238  	defer s.Shutdown()
   239  
   240  	r := &structs.Job{Name: "foo"}
   241  
   242  	resp := httptest.NewRecorder()
   243  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   244  		return r, nil
   245  	}
   246  
   247  	urlStr := "/v1/job/foo?" + pretty
   248  	req, _ := http.NewRequest("GET", urlStr, nil)
   249  	s.Server.wrap(handler)(resp, req)
   250  
   251  	var expected bytes.Buffer
   252  	var err error
   253  	if prettyFmt {
   254  		enc := codec.NewEncoder(&expected, structs.JsonHandlePretty)
   255  		err = enc.Encode(r)
   256  		expected.WriteByte('\n')
   257  	} else {
   258  		enc := codec.NewEncoder(&expected, structs.JsonHandle)
   259  		err = enc.Encode(r)
   260  	}
   261  	if err != nil {
   262  		t.Fatalf("failed to encode: %v", err)
   263  	}
   264  	actual, err := ioutil.ReadAll(resp.Body)
   265  	if err != nil {
   266  		t.Fatalf("err: %s", err)
   267  	}
   268  
   269  	if !bytes.Equal(expected.Bytes(), actual) {
   270  		t.Fatalf("bad:\nexpected:\t%q\nactual:\t\t%q", expected.String(), string(actual))
   271  	}
   272  }
   273  
   274  func TestPermissionDenied(t *testing.T) {
   275  	s := makeHTTPServer(t, func(c *Config) {
   276  		c.ACL.Enabled = true
   277  	})
   278  	defer s.Shutdown()
   279  
   280  	{
   281  		resp := httptest.NewRecorder()
   282  		handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   283  			return nil, structs.ErrPermissionDenied
   284  		}
   285  
   286  		req, _ := http.NewRequest("GET", "/v1/job/foo", nil)
   287  		s.Server.wrap(handler)(resp, req)
   288  		assert.Equal(t, resp.Code, 403)
   289  	}
   290  
   291  	// When remote RPC is used the errors have "rpc error: " prependend
   292  	{
   293  		resp := httptest.NewRecorder()
   294  		handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   295  			return nil, fmt.Errorf("rpc error: %v", structs.ErrPermissionDenied)
   296  		}
   297  
   298  		req, _ := http.NewRequest("GET", "/v1/job/foo", nil)
   299  		s.Server.wrap(handler)(resp, req)
   300  		assert.Equal(t, resp.Code, 403)
   301  	}
   302  }
   303  
   304  func TestTokenNotFound(t *testing.T) {
   305  	s := makeHTTPServer(t, func(c *Config) {
   306  		c.ACL.Enabled = true
   307  	})
   308  	defer s.Shutdown()
   309  
   310  	resp := httptest.NewRecorder()
   311  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   312  		return nil, structs.ErrTokenNotFound
   313  	}
   314  
   315  	urlStr := "/v1/job/foo"
   316  	req, _ := http.NewRequest("GET", urlStr, nil)
   317  	s.Server.wrap(handler)(resp, req)
   318  	assert.Equal(t, resp.Code, 403)
   319  }
   320  
   321  func TestParseWait(t *testing.T) {
   322  	t.Parallel()
   323  	resp := httptest.NewRecorder()
   324  	var b structs.QueryOptions
   325  
   326  	req, err := http.NewRequest("GET",
   327  		"/v1/catalog/nodes?wait=60s&index=1000", nil)
   328  	if err != nil {
   329  		t.Fatalf("err: %v", err)
   330  	}
   331  
   332  	if d := parseWait(resp, req, &b); d {
   333  		t.Fatalf("unexpected done")
   334  	}
   335  
   336  	if b.MinQueryIndex != 1000 {
   337  		t.Fatalf("Bad: %v", b)
   338  	}
   339  	if b.MaxQueryTime != 60*time.Second {
   340  		t.Fatalf("Bad: %v", b)
   341  	}
   342  }
   343  
   344  func TestParseWait_InvalidTime(t *testing.T) {
   345  	t.Parallel()
   346  	resp := httptest.NewRecorder()
   347  	var b structs.QueryOptions
   348  
   349  	req, err := http.NewRequest("GET",
   350  		"/v1/catalog/nodes?wait=60foo&index=1000", nil)
   351  	if err != nil {
   352  		t.Fatalf("err: %v", err)
   353  	}
   354  
   355  	if d := parseWait(resp, req, &b); !d {
   356  		t.Fatalf("expected done")
   357  	}
   358  
   359  	if resp.Code != 400 {
   360  		t.Fatalf("bad code: %v", resp.Code)
   361  	}
   362  }
   363  
   364  func TestParseWait_InvalidIndex(t *testing.T) {
   365  	t.Parallel()
   366  	resp := httptest.NewRecorder()
   367  	var b structs.QueryOptions
   368  
   369  	req, err := http.NewRequest("GET",
   370  		"/v1/catalog/nodes?wait=60s&index=foo", nil)
   371  	if err != nil {
   372  		t.Fatalf("err: %v", err)
   373  	}
   374  
   375  	if d := parseWait(resp, req, &b); !d {
   376  		t.Fatalf("expected done")
   377  	}
   378  
   379  	if resp.Code != 400 {
   380  		t.Fatalf("bad code: %v", resp.Code)
   381  	}
   382  }
   383  
   384  func TestParseConsistency(t *testing.T) {
   385  	t.Parallel()
   386  	var b structs.QueryOptions
   387  
   388  	req, err := http.NewRequest("GET",
   389  		"/v1/catalog/nodes?stale", nil)
   390  	if err != nil {
   391  		t.Fatalf("err: %v", err)
   392  	}
   393  
   394  	parseConsistency(req, &b)
   395  	if !b.AllowStale {
   396  		t.Fatalf("Bad: %v", b)
   397  	}
   398  
   399  	b = structs.QueryOptions{}
   400  	req, err = http.NewRequest("GET",
   401  		"/v1/catalog/nodes?consistent", nil)
   402  	if err != nil {
   403  		t.Fatalf("err: %v", err)
   404  	}
   405  
   406  	parseConsistency(req, &b)
   407  	if b.AllowStale {
   408  		t.Fatalf("Bad: %v", b)
   409  	}
   410  }
   411  
   412  func TestParseRegion(t *testing.T) {
   413  	t.Parallel()
   414  	s := makeHTTPServer(t, nil)
   415  	defer s.Shutdown()
   416  
   417  	req, err := http.NewRequest("GET",
   418  		"/v1/jobs?region=foo", nil)
   419  	if err != nil {
   420  		t.Fatalf("err: %v", err)
   421  	}
   422  
   423  	var region string
   424  	s.Server.parseRegion(req, &region)
   425  	if region != "foo" {
   426  		t.Fatalf("bad %s", region)
   427  	}
   428  
   429  	region = ""
   430  	req, err = http.NewRequest("GET", "/v1/jobs", nil)
   431  	if err != nil {
   432  		t.Fatalf("err: %v", err)
   433  	}
   434  
   435  	s.Server.parseRegion(req, &region)
   436  	if region != "global" {
   437  		t.Fatalf("bad %s", region)
   438  	}
   439  }
   440  
   441  func TestParseToken(t *testing.T) {
   442  	t.Parallel()
   443  	s := makeHTTPServer(t, nil)
   444  	defer s.Shutdown()
   445  
   446  	req, err := http.NewRequest("GET", "/v1/jobs", nil)
   447  	req.Header.Add("X-Nomad-Token", "foobar")
   448  	if err != nil {
   449  		t.Fatalf("err: %v", err)
   450  	}
   451  
   452  	var token string
   453  	s.Server.parseToken(req, &token)
   454  	if token != "foobar" {
   455  		t.Fatalf("bad %s", token)
   456  	}
   457  }
   458  
   459  // TestHTTP_VerifyHTTPSClient asserts that a client certificate signed by the
   460  // appropriate CA is required when VerifyHTTPSClient=true.
   461  func TestHTTP_VerifyHTTPSClient(t *testing.T) {
   462  	t.Parallel()
   463  	const (
   464  		cafile  = "../../helper/tlsutil/testdata/ca.pem"
   465  		foocert = "../../helper/tlsutil/testdata/nomad-foo.pem"
   466  		fookey  = "../../helper/tlsutil/testdata/nomad-foo-key.pem"
   467  	)
   468  	s := makeHTTPServer(t, func(c *Config) {
   469  		c.Region = "foo" // match the region on foocert
   470  		c.TLSConfig = &config.TLSConfig{
   471  			EnableHTTP:        true,
   472  			VerifyHTTPSClient: true,
   473  			CAFile:            cafile,
   474  			CertFile:          foocert,
   475  			KeyFile:           fookey,
   476  		}
   477  	})
   478  	defer s.Shutdown()
   479  
   480  	reqURL := fmt.Sprintf("https://%s/v1/agent/self", s.Agent.config.AdvertiseAddrs.HTTP)
   481  
   482  	// FAIL: Requests that expect 127.0.0.1 as the name should fail
   483  	resp, err := http.Get(reqURL)
   484  	if err == nil {
   485  		resp.Body.Close()
   486  		t.Fatalf("expected non-nil error but received: %v", resp.StatusCode)
   487  	}
   488  	urlErr, ok := err.(*url.Error)
   489  	if !ok {
   490  		t.Fatalf("expected a *url.Error but received: %T -> %v", err, err)
   491  	}
   492  	hostErr, ok := urlErr.Err.(x509.HostnameError)
   493  	if !ok {
   494  		t.Fatalf("expected a x509.HostnameError but received: %T -> %v", urlErr.Err, urlErr.Err)
   495  	}
   496  	if expected := "127.0.0.1"; hostErr.Host != expected {
   497  		t.Fatalf("expected hostname on error to be %q but found %q", expected, hostErr.Host)
   498  	}
   499  
   500  	// FAIL: Requests that specify a valid hostname but not the CA should
   501  	// fail
   502  	tlsConf := &tls.Config{
   503  		ServerName: "client.regionFoo.nomad",
   504  	}
   505  	transport := &http.Transport{TLSClientConfig: tlsConf}
   506  	client := &http.Client{Transport: transport}
   507  	req, err := http.NewRequest("GET", reqURL, nil)
   508  	if err != nil {
   509  		t.Fatalf("error creating request: %v", err)
   510  	}
   511  	resp, err = client.Do(req)
   512  	if err == nil {
   513  		resp.Body.Close()
   514  		t.Fatalf("expected non-nil error but received: %v", resp.StatusCode)
   515  	}
   516  	urlErr, ok = err.(*url.Error)
   517  	if !ok {
   518  		t.Fatalf("expected a *url.Error but received: %T -> %v", err, err)
   519  	}
   520  	_, ok = urlErr.Err.(x509.UnknownAuthorityError)
   521  	if !ok {
   522  		t.Fatalf("expected a x509.UnknownAuthorityError but received: %T -> %v", urlErr.Err, urlErr.Err)
   523  	}
   524  
   525  	// FAIL: Requests that specify a valid hostname and CA cert but lack a
   526  	// client certificate should fail
   527  	cacertBytes, err := ioutil.ReadFile(cafile)
   528  	if err != nil {
   529  		t.Fatalf("error reading cacert: %v", err)
   530  	}
   531  	tlsConf.RootCAs = x509.NewCertPool()
   532  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   533  	req, err = http.NewRequest("GET", reqURL, nil)
   534  	if err != nil {
   535  		t.Fatalf("error creating request: %v", err)
   536  	}
   537  	resp, err = client.Do(req)
   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  	opErr, ok := urlErr.Err.(*net.OpError)
   547  	if !ok {
   548  		t.Fatalf("expected a *net.OpErr but received: %T -> %v", urlErr.Err, urlErr.Err)
   549  	}
   550  	const badCertificate = "tls: bad certificate" // from crypto/tls/alert.go:52 and RFC 5246 ยง A.3
   551  	if opErr.Err.Error() != badCertificate {
   552  		t.Fatalf("expected tls.alert bad_certificate but received: %q", opErr.Err.Error())
   553  	}
   554  
   555  	// PASS: Requests that specify a valid hostname, CA cert, and client
   556  	// certificate succeed.
   557  	tlsConf.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   558  		c, err := tls.LoadX509KeyPair(foocert, fookey)
   559  		if err != nil {
   560  			return nil, err
   561  		}
   562  		return &c, nil
   563  	}
   564  	transport = &http.Transport{TLSClientConfig: tlsConf}
   565  	client = &http.Client{Transport: transport}
   566  	req, err = http.NewRequest("GET", reqURL, nil)
   567  	if err != nil {
   568  		t.Fatalf("error creating request: %v", err)
   569  	}
   570  	resp, err = client.Do(req)
   571  	if err != nil {
   572  		t.Fatalf("unexpected error: %v", err)
   573  	}
   574  	resp.Body.Close()
   575  	if resp.StatusCode != 200 {
   576  		t.Fatalf("expected 200 status code but got: %d", resp.StatusCode)
   577  	}
   578  }
   579  
   580  func TestHTTP_VerifyHTTPSClient_AfterConfigReload(t *testing.T) {
   581  	t.Parallel()
   582  	assert := assert.New(t)
   583  
   584  	const (
   585  		cafile   = "../../helper/tlsutil/testdata/ca.pem"
   586  		foocert  = "../../helper/tlsutil/testdata/nomad-bad.pem"
   587  		fookey   = "../../helper/tlsutil/testdata/nomad-bad-key.pem"
   588  		foocert2 = "../../helper/tlsutil/testdata/nomad-foo.pem"
   589  		fookey2  = "../../helper/tlsutil/testdata/nomad-foo-key.pem"
   590  	)
   591  
   592  	agentConfig := &Config{
   593  		TLSConfig: &config.TLSConfig{
   594  			EnableHTTP:        true,
   595  			VerifyHTTPSClient: true,
   596  			CAFile:            cafile,
   597  			CertFile:          foocert,
   598  			KeyFile:           fookey,
   599  		},
   600  	}
   601  
   602  	newConfig := &Config{
   603  		TLSConfig: &config.TLSConfig{
   604  			EnableHTTP:        true,
   605  			VerifyHTTPSClient: true,
   606  			CAFile:            cafile,
   607  			CertFile:          foocert2,
   608  			KeyFile:           fookey2,
   609  		},
   610  	}
   611  
   612  	s := makeHTTPServer(t, func(c *Config) {
   613  		c.TLSConfig = agentConfig.TLSConfig
   614  	})
   615  	defer s.Shutdown()
   616  
   617  	// Make an initial request that should fail.
   618  	// Requests that specify a valid hostname, CA cert, and client
   619  	// certificate succeed.
   620  	tlsConf := &tls.Config{
   621  		ServerName: "client.regionFoo.nomad",
   622  		RootCAs:    x509.NewCertPool(),
   623  		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   624  			c, err := tls.LoadX509KeyPair(foocert, fookey)
   625  			if err != nil {
   626  				return nil, err
   627  			}
   628  			return &c, nil
   629  		},
   630  	}
   631  
   632  	// HTTPS request should succeed
   633  	httpsReqURL := fmt.Sprintf("https://%s/v1/agent/self", s.Agent.config.AdvertiseAddrs.HTTP)
   634  
   635  	cacertBytes, err := ioutil.ReadFile(cafile)
   636  	assert.Nil(err)
   637  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   638  
   639  	transport := &http.Transport{TLSClientConfig: tlsConf}
   640  	client := &http.Client{Transport: transport}
   641  	req, err := http.NewRequest("GET", httpsReqURL, nil)
   642  	assert.Nil(err)
   643  
   644  	// Check that we get an error that the certificate isn't valid for the
   645  	// region we are contacting.
   646  	_, err = client.Do(req)
   647  	assert.Contains(err.Error(), "certificate is valid for")
   648  
   649  	// Reload the TLS configuration==
   650  	assert.Nil(s.Agent.Reload(newConfig))
   651  
   652  	// Requests that specify a valid hostname, CA cert, and client
   653  	// certificate succeed.
   654  	tlsConf = &tls.Config{
   655  		ServerName: "client.regionFoo.nomad",
   656  		RootCAs:    x509.NewCertPool(),
   657  		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   658  			c, err := tls.LoadX509KeyPair(foocert2, fookey2)
   659  			if err != nil {
   660  				return nil, err
   661  			}
   662  			return &c, nil
   663  		},
   664  	}
   665  
   666  	cacertBytes, err = ioutil.ReadFile(cafile)
   667  	assert.Nil(err)
   668  	tlsConf.RootCAs.AppendCertsFromPEM(cacertBytes)
   669  
   670  	transport = &http.Transport{TLSClientConfig: tlsConf}
   671  	client = &http.Client{Transport: transport}
   672  	req, err = http.NewRequest("GET", httpsReqURL, nil)
   673  	assert.Nil(err)
   674  
   675  	resp, err := client.Do(req)
   676  	if assert.Nil(err) {
   677  		resp.Body.Close()
   678  		assert.Equal(resp.StatusCode, 200)
   679  	}
   680  }
   681  
   682  func httpTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
   683  	s := makeHTTPServer(t, cb)
   684  	defer s.Shutdown()
   685  	testutil.WaitForLeader(t, s.Agent.RPC)
   686  	f(s)
   687  }
   688  
   689  func httpACLTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
   690  	s := makeHTTPServer(t, func(c *Config) {
   691  		c.ACL.Enabled = true
   692  		if cb != nil {
   693  			cb(c)
   694  		}
   695  	})
   696  	defer s.Shutdown()
   697  	testutil.WaitForLeader(t, s.Agent.RPC)
   698  	f(s)
   699  }
   700  
   701  func setToken(req *http.Request, token *structs.ACLToken) {
   702  	req.Header.Set("X-Nomad-Token", token.SecretID)
   703  }
   704  
   705  func encodeReq(obj interface{}) io.ReadCloser {
   706  	buf := bytes.NewBuffer(nil)
   707  	enc := json.NewEncoder(buf)
   708  	enc.Encode(obj)
   709  	return ioutil.NopCloser(buf)
   710  }