github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/api/api_test.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hashicorp/nomad/helper/uuid"
    14  	"github.com/hashicorp/nomad/testutil"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  type configCallback func(c *Config)
    19  
    20  // seen is used to track which tests we have already marked as parallel
    21  var seen map[*testing.T]struct{}
    22  
    23  func init() {
    24  	seen = make(map[*testing.T]struct{})
    25  }
    26  
    27  func makeACLClient(t *testing.T, cb1 configCallback,
    28  	cb2 testutil.ServerConfigCallback) (*Client, *testutil.TestServer, *ACLToken) {
    29  	client, server := makeClient(t, cb1, func(c *testutil.TestServerConfig) {
    30  		c.ACL.Enabled = true
    31  		if cb2 != nil {
    32  			cb2(c)
    33  		}
    34  	})
    35  
    36  	// Get the root token
    37  	root, _, err := client.ACLTokens().Bootstrap(nil)
    38  	if err != nil {
    39  		t.Fatalf("failed to bootstrap ACLs: %v", err)
    40  	}
    41  	client.SetSecretID(root.SecretID)
    42  	return client, server, root
    43  }
    44  
    45  func makeClient(t *testing.T, cb1 configCallback,
    46  	cb2 testutil.ServerConfigCallback) (*Client, *testutil.TestServer) {
    47  	// Make client config
    48  	conf := DefaultConfig()
    49  	if cb1 != nil {
    50  		cb1(conf)
    51  	}
    52  
    53  	// Create server
    54  	server := testutil.NewTestServer(t, cb2)
    55  	conf.Address = "http://" + server.HTTPAddr
    56  
    57  	// Create client
    58  	client, err := NewClient(conf)
    59  	if err != nil {
    60  		t.Fatalf("err: %v", err)
    61  	}
    62  
    63  	return client, server
    64  }
    65  
    66  func TestRequestTime(t *testing.T) {
    67  	t.Parallel()
    68  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    69  		time.Sleep(100 * time.Millisecond)
    70  		d, err := json.Marshal(struct{ Done bool }{true})
    71  		if err != nil {
    72  			http.Error(w, err.Error(), http.StatusInternalServerError)
    73  			return
    74  		}
    75  		w.Write(d)
    76  	}))
    77  	defer srv.Close()
    78  
    79  	conf := DefaultConfig()
    80  	conf.Address = srv.URL
    81  
    82  	client, err := NewClient(conf)
    83  	if err != nil {
    84  		t.Fatalf("err: %v", err)
    85  	}
    86  
    87  	var out interface{}
    88  
    89  	qm, err := client.query("/", &out, nil)
    90  	if err != nil {
    91  		t.Fatalf("query err: %v", err)
    92  	}
    93  	if qm.RequestTime == 0 {
    94  		t.Errorf("bad request time: %d", qm.RequestTime)
    95  	}
    96  
    97  	wm, err := client.write("/", struct{ S string }{"input"}, &out, nil)
    98  	if err != nil {
    99  		t.Fatalf("write err: %v", err)
   100  	}
   101  	if wm.RequestTime == 0 {
   102  		t.Errorf("bad request time: %d", wm.RequestTime)
   103  	}
   104  
   105  	wm, err = client.delete("/", &out, nil)
   106  	if err != nil {
   107  		t.Fatalf("delete err: %v", err)
   108  	}
   109  	if wm.RequestTime == 0 {
   110  		t.Errorf("bad request time: %d", wm.RequestTime)
   111  	}
   112  }
   113  
   114  func TestDefaultConfig_env(t *testing.T) {
   115  	t.Parallel()
   116  	url := "http://1.2.3.4:5678"
   117  	auth := []string{"nomaduser", "12345"}
   118  	region := "test"
   119  	namespace := "dev"
   120  	token := "foobar"
   121  
   122  	os.Setenv("NOMAD_ADDR", url)
   123  	defer os.Setenv("NOMAD_ADDR", "")
   124  
   125  	os.Setenv("NOMAD_REGION", region)
   126  	defer os.Setenv("NOMAD_REGION", "")
   127  
   128  	os.Setenv("NOMAD_NAMESPACE", namespace)
   129  	defer os.Setenv("NOMAD_NAMESPACE", "")
   130  
   131  	os.Setenv("NOMAD_HTTP_AUTH", strings.Join(auth, ":"))
   132  	defer os.Setenv("NOMAD_HTTP_AUTH", "")
   133  
   134  	os.Setenv("NOMAD_TOKEN", token)
   135  	defer os.Setenv("NOMAD_TOKEN", "")
   136  
   137  	config := DefaultConfig()
   138  
   139  	if config.Address != url {
   140  		t.Errorf("expected %q to be %q", config.Address, url)
   141  	}
   142  
   143  	if config.Region != region {
   144  		t.Errorf("expected %q to be %q", config.Region, region)
   145  	}
   146  
   147  	if config.Namespace != namespace {
   148  		t.Errorf("expected %q to be %q", config.Namespace, namespace)
   149  	}
   150  
   151  	if config.HttpAuth.Username != auth[0] {
   152  		t.Errorf("expected %q to be %q", config.HttpAuth.Username, auth[0])
   153  	}
   154  
   155  	if config.HttpAuth.Password != auth[1] {
   156  		t.Errorf("expected %q to be %q", config.HttpAuth.Password, auth[1])
   157  	}
   158  
   159  	if config.SecretID != token {
   160  		t.Errorf("Expected %q to be %q", config.SecretID, token)
   161  	}
   162  }
   163  
   164  func TestSetQueryOptions(t *testing.T) {
   165  	t.Parallel()
   166  	c, s := makeClient(t, nil, nil)
   167  	defer s.Stop()
   168  
   169  	r, _ := c.newRequest("GET", "/v1/jobs")
   170  	q := &QueryOptions{
   171  		Region:     "foo",
   172  		Namespace:  "bar",
   173  		AllowStale: true,
   174  		WaitIndex:  1000,
   175  		WaitTime:   100 * time.Second,
   176  		AuthToken:  "foobar",
   177  	}
   178  	r.setQueryOptions(q)
   179  
   180  	if r.params.Get("region") != "foo" {
   181  		t.Fatalf("bad: %v", r.params)
   182  	}
   183  	if r.params.Get("namespace") != "bar" {
   184  		t.Fatalf("bad: %v", r.params)
   185  	}
   186  	if _, ok := r.params["stale"]; !ok {
   187  		t.Fatalf("bad: %v", r.params)
   188  	}
   189  	if r.params.Get("index") != "1000" {
   190  		t.Fatalf("bad: %v", r.params)
   191  	}
   192  	if r.params.Get("wait") != "100000ms" {
   193  		t.Fatalf("bad: %v", r.params)
   194  	}
   195  	if r.token != "foobar" {
   196  		t.Fatalf("bad: %v", r.token)
   197  	}
   198  }
   199  
   200  func TestSetWriteOptions(t *testing.T) {
   201  	t.Parallel()
   202  	c, s := makeClient(t, nil, nil)
   203  	defer s.Stop()
   204  
   205  	r, _ := c.newRequest("GET", "/v1/jobs")
   206  	q := &WriteOptions{
   207  		Region:    "foo",
   208  		Namespace: "bar",
   209  		AuthToken: "foobar",
   210  	}
   211  	r.setWriteOptions(q)
   212  
   213  	if r.params.Get("region") != "foo" {
   214  		t.Fatalf("bad: %v", r.params)
   215  	}
   216  	if r.params.Get("namespace") != "bar" {
   217  		t.Fatalf("bad: %v", r.params)
   218  	}
   219  	if r.token != "foobar" {
   220  		t.Fatalf("bad: %v", r.token)
   221  	}
   222  }
   223  
   224  func TestRequestToHTTP(t *testing.T) {
   225  	t.Parallel()
   226  	c, s := makeClient(t, nil, nil)
   227  	defer s.Stop()
   228  
   229  	r, _ := c.newRequest("DELETE", "/v1/jobs/foo")
   230  	q := &QueryOptions{
   231  		Region:    "foo",
   232  		Namespace: "bar",
   233  		AuthToken: "foobar",
   234  	}
   235  	r.setQueryOptions(q)
   236  	req, err := r.toHTTP()
   237  	if err != nil {
   238  		t.Fatalf("err: %v", err)
   239  	}
   240  
   241  	if req.Method != "DELETE" {
   242  		t.Fatalf("bad: %v", req)
   243  	}
   244  	if req.URL.RequestURI() != "/v1/jobs/foo?namespace=bar&region=foo" {
   245  		t.Fatalf("bad: %v", req)
   246  	}
   247  	if req.Header.Get("X-Nomad-Token") != "foobar" {
   248  		t.Fatalf("bad: %v", req)
   249  	}
   250  }
   251  
   252  func TestParseQueryMeta(t *testing.T) {
   253  	t.Parallel()
   254  	resp := &http.Response{
   255  		Header: make(map[string][]string),
   256  	}
   257  	resp.Header.Set("X-Nomad-Index", "12345")
   258  	resp.Header.Set("X-Nomad-LastContact", "80")
   259  	resp.Header.Set("X-Nomad-KnownLeader", "true")
   260  
   261  	qm := &QueryMeta{}
   262  	if err := parseQueryMeta(resp, qm); err != nil {
   263  		t.Fatalf("err: %v", err)
   264  	}
   265  
   266  	if qm.LastIndex != 12345 {
   267  		t.Fatalf("Bad: %v", qm)
   268  	}
   269  	if qm.LastContact != 80*time.Millisecond {
   270  		t.Fatalf("Bad: %v", qm)
   271  	}
   272  	if !qm.KnownLeader {
   273  		t.Fatalf("Bad: %v", qm)
   274  	}
   275  }
   276  
   277  func TestParseWriteMeta(t *testing.T) {
   278  	t.Parallel()
   279  	resp := &http.Response{
   280  		Header: make(map[string][]string),
   281  	}
   282  	resp.Header.Set("X-Nomad-Index", "12345")
   283  
   284  	wm := &WriteMeta{}
   285  	if err := parseWriteMeta(resp, wm); err != nil {
   286  		t.Fatalf("err: %v", err)
   287  	}
   288  
   289  	if wm.LastIndex != 12345 {
   290  		t.Fatalf("Bad: %v", wm)
   291  	}
   292  }
   293  
   294  func TestQueryString(t *testing.T) {
   295  	t.Parallel()
   296  	c, s := makeClient(t, nil, nil)
   297  	defer s.Stop()
   298  
   299  	r, _ := c.newRequest("PUT", "/v1/abc?foo=bar&baz=zip")
   300  	q := &WriteOptions{
   301  		Region:    "foo",
   302  		Namespace: "bar",
   303  	}
   304  	r.setWriteOptions(q)
   305  
   306  	req, err := r.toHTTP()
   307  	if err != nil {
   308  		t.Fatalf("err: %s", err)
   309  	}
   310  
   311  	if uri := req.URL.RequestURI(); uri != "/v1/abc?baz=zip&foo=bar&namespace=bar&region=foo" {
   312  		t.Fatalf("bad uri: %q", uri)
   313  	}
   314  }
   315  
   316  func TestClient_NodeClient(t *testing.T) {
   317  	http := "testdomain:4646"
   318  	tlsNode := func(string, *QueryOptions) (*Node, *QueryMeta, error) {
   319  		return &Node{
   320  			ID:         uuid.Generate(),
   321  			Status:     "ready",
   322  			HTTPAddr:   http,
   323  			TLSEnabled: true,
   324  		}, nil, nil
   325  	}
   326  	noTlsNode := func(string, *QueryOptions) (*Node, *QueryMeta, error) {
   327  		return &Node{
   328  			ID:         uuid.Generate(),
   329  			Status:     "ready",
   330  			HTTPAddr:   http,
   331  			TLSEnabled: false,
   332  		}, nil, nil
   333  	}
   334  
   335  	optionNoRegion := &QueryOptions{}
   336  	optionRegion := &QueryOptions{
   337  		Region: "foo",
   338  	}
   339  
   340  	clientNoRegion, err := NewClient(DefaultConfig())
   341  	assert.Nil(t, err)
   342  
   343  	regionConfig := DefaultConfig()
   344  	regionConfig.Region = "bar"
   345  	clientRegion, err := NewClient(regionConfig)
   346  	assert.Nil(t, err)
   347  
   348  	expectedTLSAddr := fmt.Sprintf("https://%s", http)
   349  	expectedNoTLSAddr := fmt.Sprintf("http://%s", http)
   350  
   351  	cases := []struct {
   352  		Node                  nodeLookup
   353  		QueryOptions          *QueryOptions
   354  		Client                *Client
   355  		ExpectedAddr          string
   356  		ExpectedRegion        string
   357  		ExpectedTLSServerName string
   358  	}{
   359  		{
   360  			Node:                  tlsNode,
   361  			QueryOptions:          optionNoRegion,
   362  			Client:                clientNoRegion,
   363  			ExpectedAddr:          expectedTLSAddr,
   364  			ExpectedRegion:        "global",
   365  			ExpectedTLSServerName: "client.global.nomad",
   366  		},
   367  		{
   368  			Node:                  tlsNode,
   369  			QueryOptions:          optionRegion,
   370  			Client:                clientNoRegion,
   371  			ExpectedAddr:          expectedTLSAddr,
   372  			ExpectedRegion:        "foo",
   373  			ExpectedTLSServerName: "client.foo.nomad",
   374  		},
   375  		{
   376  			Node:                  tlsNode,
   377  			QueryOptions:          optionRegion,
   378  			Client:                clientRegion,
   379  			ExpectedAddr:          expectedTLSAddr,
   380  			ExpectedRegion:        "foo",
   381  			ExpectedTLSServerName: "client.foo.nomad",
   382  		},
   383  		{
   384  			Node:                  tlsNode,
   385  			QueryOptions:          optionNoRegion,
   386  			Client:                clientRegion,
   387  			ExpectedAddr:          expectedTLSAddr,
   388  			ExpectedRegion:        "bar",
   389  			ExpectedTLSServerName: "client.bar.nomad",
   390  		},
   391  		{
   392  			Node:                  noTlsNode,
   393  			QueryOptions:          optionNoRegion,
   394  			Client:                clientNoRegion,
   395  			ExpectedAddr:          expectedNoTLSAddr,
   396  			ExpectedRegion:        "global",
   397  			ExpectedTLSServerName: "",
   398  		},
   399  		{
   400  			Node:                  noTlsNode,
   401  			QueryOptions:          optionRegion,
   402  			Client:                clientNoRegion,
   403  			ExpectedAddr:          expectedNoTLSAddr,
   404  			ExpectedRegion:        "foo",
   405  			ExpectedTLSServerName: "",
   406  		},
   407  		{
   408  			Node:                  noTlsNode,
   409  			QueryOptions:          optionRegion,
   410  			Client:                clientRegion,
   411  			ExpectedAddr:          expectedNoTLSAddr,
   412  			ExpectedRegion:        "foo",
   413  			ExpectedTLSServerName: "",
   414  		},
   415  		{
   416  			Node:                  noTlsNode,
   417  			QueryOptions:          optionNoRegion,
   418  			Client:                clientRegion,
   419  			ExpectedAddr:          expectedNoTLSAddr,
   420  			ExpectedRegion:        "bar",
   421  			ExpectedTLSServerName: "",
   422  		},
   423  	}
   424  
   425  	for _, c := range cases {
   426  		name := fmt.Sprintf("%s__%s__%s", c.ExpectedAddr, c.ExpectedRegion, c.ExpectedTLSServerName)
   427  		t.Run(name, func(t *testing.T) {
   428  			assert := assert.New(t)
   429  			nodeClient, err := c.Client.getNodeClientImpl("testID", c.QueryOptions, c.Node)
   430  			assert.Nil(err)
   431  			assert.Equal(c.ExpectedRegion, nodeClient.config.Region)
   432  			assert.Equal(c.ExpectedAddr, nodeClient.config.Address)
   433  			assert.NotNil(nodeClient.config.TLSConfig)
   434  			assert.Equal(c.ExpectedTLSServerName, nodeClient.config.TLSConfig.TLSServerName)
   435  		})
   436  	}
   437  }