github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/command/agent/http_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"strconv"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/hashicorp/nomad/nomad/structs"
    17  	"github.com/hashicorp/nomad/testutil"
    18  )
    19  
    20  type TestServer struct {
    21  	T      *testing.T
    22  	Dir    string
    23  	Agent  *Agent
    24  	Server *HTTPServer
    25  }
    26  
    27  func (s *TestServer) Cleanup() {
    28  	s.Server.Shutdown()
    29  	s.Agent.Shutdown()
    30  	os.RemoveAll(s.Dir)
    31  }
    32  
    33  func makeHTTPServer(t *testing.T, cb func(c *Config)) *TestServer {
    34  	dir, agent := makeAgent(t, cb)
    35  	srv, err := NewHTTPServer(agent, agent.config, agent.logOutput)
    36  	if err != nil {
    37  		t.Fatalf("err: %v", err)
    38  	}
    39  	s := &TestServer{
    40  		T:      t,
    41  		Dir:    dir,
    42  		Agent:  agent,
    43  		Server: srv,
    44  	}
    45  	return s
    46  }
    47  
    48  func TestSetIndex(t *testing.T) {
    49  	resp := httptest.NewRecorder()
    50  	setIndex(resp, 1000)
    51  	header := resp.Header().Get("X-Nomad-Index")
    52  	if header != "1000" {
    53  		t.Fatalf("Bad: %v", header)
    54  	}
    55  	setIndex(resp, 2000)
    56  	if v := resp.Header()["X-Nomad-Index"]; len(v) != 1 {
    57  		t.Fatalf("bad: %#v", v)
    58  	}
    59  }
    60  
    61  func TestSetKnownLeader(t *testing.T) {
    62  	resp := httptest.NewRecorder()
    63  	setKnownLeader(resp, true)
    64  	header := resp.Header().Get("X-Nomad-KnownLeader")
    65  	if header != "true" {
    66  		t.Fatalf("Bad: %v", header)
    67  	}
    68  	resp = httptest.NewRecorder()
    69  	setKnownLeader(resp, false)
    70  	header = resp.Header().Get("X-Nomad-KnownLeader")
    71  	if header != "false" {
    72  		t.Fatalf("Bad: %v", header)
    73  	}
    74  }
    75  
    76  func TestSetLastContact(t *testing.T) {
    77  	resp := httptest.NewRecorder()
    78  	setLastContact(resp, 123456*time.Microsecond)
    79  	header := resp.Header().Get("X-Nomad-LastContact")
    80  	if header != "123" {
    81  		t.Fatalf("Bad: %v", header)
    82  	}
    83  }
    84  
    85  func TestSetMeta(t *testing.T) {
    86  	meta := structs.QueryMeta{
    87  		Index:       1000,
    88  		KnownLeader: true,
    89  		LastContact: 123456 * time.Microsecond,
    90  	}
    91  	resp := httptest.NewRecorder()
    92  	setMeta(resp, &meta)
    93  	header := resp.Header().Get("X-Nomad-Index")
    94  	if header != "1000" {
    95  		t.Fatalf("Bad: %v", header)
    96  	}
    97  	header = resp.Header().Get("X-Nomad-KnownLeader")
    98  	if header != "true" {
    99  		t.Fatalf("Bad: %v", header)
   100  	}
   101  	header = resp.Header().Get("X-Nomad-LastContact")
   102  	if header != "123" {
   103  		t.Fatalf("Bad: %v", header)
   104  	}
   105  }
   106  
   107  func TestContentTypeIsJSON(t *testing.T) {
   108  	s := makeHTTPServer(t, nil)
   109  	defer s.Cleanup()
   110  
   111  	resp := httptest.NewRecorder()
   112  
   113  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   114  		return &structs.Job{Name: "foo"}, nil
   115  	}
   116  
   117  	req, _ := http.NewRequest("GET", "/v1/kv/key", nil)
   118  	s.Server.wrap(handler)(resp, req)
   119  
   120  	contentType := resp.Header().Get("Content-Type")
   121  
   122  	if contentType != "application/json" {
   123  		t.Fatalf("Content-Type header was not 'application/json'")
   124  	}
   125  }
   126  
   127  func TestPrettyPrint(t *testing.T) {
   128  	testPrettyPrint("pretty=1", t)
   129  }
   130  
   131  func TestPrettyPrintBare(t *testing.T) {
   132  	testPrettyPrint("pretty", t)
   133  }
   134  
   135  func testPrettyPrint(pretty string, t *testing.T) {
   136  	s := makeHTTPServer(t, nil)
   137  	defer s.Cleanup()
   138  
   139  	r := &structs.Job{Name: "foo"}
   140  
   141  	resp := httptest.NewRecorder()
   142  	handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   143  		return r, nil
   144  	}
   145  
   146  	urlStr := "/v1/job/foo?" + pretty
   147  	req, _ := http.NewRequest("GET", urlStr, nil)
   148  	s.Server.wrap(handler)(resp, req)
   149  
   150  	expected, _ := json.MarshalIndent(r, "", "    ")
   151  	actual, err := ioutil.ReadAll(resp.Body)
   152  	if err != nil {
   153  		t.Fatalf("err: %s", err)
   154  	}
   155  
   156  	if !bytes.Equal(expected, actual) {
   157  		t.Fatalf("bad: %q", string(actual))
   158  	}
   159  }
   160  
   161  func TestParseWait(t *testing.T) {
   162  	resp := httptest.NewRecorder()
   163  	var b structs.QueryOptions
   164  
   165  	req, err := http.NewRequest("GET",
   166  		"/v1/catalog/nodes?wait=60s&index=1000", nil)
   167  	if err != nil {
   168  		t.Fatalf("err: %v", err)
   169  	}
   170  
   171  	if d := parseWait(resp, req, &b); d {
   172  		t.Fatalf("unexpected done")
   173  	}
   174  
   175  	if b.MinQueryIndex != 1000 {
   176  		t.Fatalf("Bad: %v", b)
   177  	}
   178  	if b.MaxQueryTime != 60*time.Second {
   179  		t.Fatalf("Bad: %v", b)
   180  	}
   181  }
   182  
   183  func TestParseWait_InvalidTime(t *testing.T) {
   184  	resp := httptest.NewRecorder()
   185  	var b structs.QueryOptions
   186  
   187  	req, err := http.NewRequest("GET",
   188  		"/v1/catalog/nodes?wait=60foo&index=1000", nil)
   189  	if err != nil {
   190  		t.Fatalf("err: %v", err)
   191  	}
   192  
   193  	if d := parseWait(resp, req, &b); !d {
   194  		t.Fatalf("expected done")
   195  	}
   196  
   197  	if resp.Code != 400 {
   198  		t.Fatalf("bad code: %v", resp.Code)
   199  	}
   200  }
   201  
   202  func TestParseWait_InvalidIndex(t *testing.T) {
   203  	resp := httptest.NewRecorder()
   204  	var b structs.QueryOptions
   205  
   206  	req, err := http.NewRequest("GET",
   207  		"/v1/catalog/nodes?wait=60s&index=foo", nil)
   208  	if err != nil {
   209  		t.Fatalf("err: %v", err)
   210  	}
   211  
   212  	if d := parseWait(resp, req, &b); !d {
   213  		t.Fatalf("expected done")
   214  	}
   215  
   216  	if resp.Code != 400 {
   217  		t.Fatalf("bad code: %v", resp.Code)
   218  	}
   219  }
   220  
   221  func TestParseConsistency(t *testing.T) {
   222  	var b structs.QueryOptions
   223  
   224  	req, err := http.NewRequest("GET",
   225  		"/v1/catalog/nodes?stale", nil)
   226  	if err != nil {
   227  		t.Fatalf("err: %v", err)
   228  	}
   229  
   230  	parseConsistency(req, &b)
   231  	if !b.AllowStale {
   232  		t.Fatalf("Bad: %v", b)
   233  	}
   234  
   235  	b = structs.QueryOptions{}
   236  	req, err = http.NewRequest("GET",
   237  		"/v1/catalog/nodes?consistent", nil)
   238  	if err != nil {
   239  		t.Fatalf("err: %v", err)
   240  	}
   241  
   242  	parseConsistency(req, &b)
   243  	if b.AllowStale {
   244  		t.Fatalf("Bad: %v", b)
   245  	}
   246  }
   247  
   248  func TestParseRegion(t *testing.T) {
   249  	s := makeHTTPServer(t, nil)
   250  	defer s.Cleanup()
   251  
   252  	req, err := http.NewRequest("GET",
   253  		"/v1/jobs?region=foo", nil)
   254  	if err != nil {
   255  		t.Fatalf("err: %v", err)
   256  	}
   257  
   258  	var region string
   259  	s.Server.parseRegion(req, &region)
   260  	if region != "foo" {
   261  		t.Fatalf("bad %s", region)
   262  	}
   263  
   264  	region = ""
   265  	req, err = http.NewRequest("GET", "/v1/jobs", nil)
   266  	if err != nil {
   267  		t.Fatalf("err: %v", err)
   268  	}
   269  
   270  	s.Server.parseRegion(req, &region)
   271  	if region != "global" {
   272  		t.Fatalf("bad %s", region)
   273  	}
   274  }
   275  
   276  // assertIndex tests that X-Nomad-Index is set and non-zero
   277  func assertIndex(t *testing.T, resp *httptest.ResponseRecorder) {
   278  	header := resp.Header().Get("X-Nomad-Index")
   279  	if header == "" || header == "0" {
   280  		t.Fatalf("Bad: %v", header)
   281  	}
   282  }
   283  
   284  // checkIndex is like assertIndex but returns an error
   285  func checkIndex(resp *httptest.ResponseRecorder) error {
   286  	header := resp.Header().Get("X-Nomad-Index")
   287  	if header == "" || header == "0" {
   288  		return fmt.Errorf("Bad: %v", header)
   289  	}
   290  	return nil
   291  }
   292  
   293  // getIndex parses X-Nomad-Index
   294  func getIndex(t *testing.T, resp *httptest.ResponseRecorder) uint64 {
   295  	header := resp.Header().Get("X-Nomad-Index")
   296  	if header == "" {
   297  		t.Fatalf("Bad: %v", header)
   298  	}
   299  	val, err := strconv.Atoi(header)
   300  	if err != nil {
   301  		t.Fatalf("Bad: %v", header)
   302  	}
   303  	return uint64(val)
   304  }
   305  
   306  func httpTest(t *testing.T, cb func(c *Config), f func(srv *TestServer)) {
   307  	s := makeHTTPServer(t, cb)
   308  	defer s.Cleanup()
   309  	testutil.WaitForLeader(t, s.Agent.RPC)
   310  	f(s)
   311  }
   312  
   313  func encodeReq(obj interface{}) io.ReadCloser {
   314  	buf := bytes.NewBuffer(nil)
   315  	enc := json.NewEncoder(buf)
   316  	enc.Encode(obj)
   317  	return ioutil.NopCloser(buf)
   318  }