github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/nomad/node_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/net-rpc-msgpackrpc"
    10  	"github.com/hashicorp/nomad/nomad/mock"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/nomad/testutil"
    13  )
    14  
    15  func TestClientEndpoint_Register(t *testing.T) {
    16  	s1 := testServer(t, nil)
    17  	defer s1.Shutdown()
    18  	codec := rpcClient(t, s1)
    19  	testutil.WaitForLeader(t, s1.RPC)
    20  
    21  	// Create the register request
    22  	node := mock.Node()
    23  	req := &structs.NodeRegisterRequest{
    24  		Node:         node,
    25  		WriteRequest: structs.WriteRequest{Region: "global"},
    26  	}
    27  
    28  	// Fetch the response
    29  	var resp structs.GenericResponse
    30  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", req, &resp); err != nil {
    31  		t.Fatalf("err: %v", err)
    32  	}
    33  	if resp.Index == 0 {
    34  		t.Fatalf("bad index: %d", resp.Index)
    35  	}
    36  
    37  	// Check for the node in the FSM
    38  	state := s1.fsm.State()
    39  	out, err := state.NodeByID(node.ID)
    40  	if err != nil {
    41  		t.Fatalf("err: %v", err)
    42  	}
    43  	if out == nil {
    44  		t.Fatalf("expected node")
    45  	}
    46  	if out.CreateIndex != resp.Index {
    47  		t.Fatalf("index mis-match")
    48  	}
    49  	if out.ComputedClass == "" {
    50  		t.Fatal("ComputedClass not set")
    51  	}
    52  }
    53  
    54  func TestClientEndpoint_Deregister(t *testing.T) {
    55  	s1 := testServer(t, nil)
    56  	defer s1.Shutdown()
    57  	codec := rpcClient(t, s1)
    58  	testutil.WaitForLeader(t, s1.RPC)
    59  
    60  	// Create the register request
    61  	node := mock.Node()
    62  	reg := &structs.NodeRegisterRequest{
    63  		Node:         node,
    64  		WriteRequest: structs.WriteRequest{Region: "global"},
    65  	}
    66  
    67  	// Fetch the response
    68  	var resp structs.GenericResponse
    69  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
    70  		t.Fatalf("err: %v", err)
    71  	}
    72  
    73  	// Deregister
    74  	dereg := &structs.NodeDeregisterRequest{
    75  		NodeID:       node.ID,
    76  		WriteRequest: structs.WriteRequest{Region: "global"},
    77  	}
    78  	var resp2 structs.GenericResponse
    79  	if err := msgpackrpc.CallWithCodec(codec, "Node.Deregister", dereg, &resp2); err != nil {
    80  		t.Fatalf("err: %v", err)
    81  	}
    82  	if resp2.Index == 0 {
    83  		t.Fatalf("bad index: %d", resp2.Index)
    84  	}
    85  
    86  	// Check for the node in the FSM
    87  	state := s1.fsm.State()
    88  	out, err := state.NodeByID(node.ID)
    89  	if err != nil {
    90  		t.Fatalf("err: %v", err)
    91  	}
    92  	if out != nil {
    93  		t.Fatalf("unexpected node")
    94  	}
    95  }
    96  
    97  func TestClientEndpoint_UpdateStatus(t *testing.T) {
    98  	s1 := testServer(t, nil)
    99  	defer s1.Shutdown()
   100  	codec := rpcClient(t, s1)
   101  	testutil.WaitForLeader(t, s1.RPC)
   102  
   103  	// Create the register request
   104  	node := mock.Node()
   105  	reg := &structs.NodeRegisterRequest{
   106  		Node:         node,
   107  		WriteRequest: structs.WriteRequest{Region: "global"},
   108  	}
   109  
   110  	// Fetch the response
   111  	var resp structs.NodeUpdateResponse
   112  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   113  		t.Fatalf("err: %v", err)
   114  	}
   115  
   116  	// Check for heartbeat interval
   117  	ttl := resp.HeartbeatTTL
   118  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   119  		t.Fatalf("bad: %#v", ttl)
   120  	}
   121  
   122  	// Update the status
   123  	dereg := &structs.NodeUpdateStatusRequest{
   124  		NodeID:       node.ID,
   125  		Status:       structs.NodeStatusInit,
   126  		WriteRequest: structs.WriteRequest{Region: "global"},
   127  	}
   128  	var resp2 structs.NodeUpdateResponse
   129  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil {
   130  		t.Fatalf("err: %v", err)
   131  	}
   132  	if resp2.Index == 0 {
   133  		t.Fatalf("bad index: %d", resp2.Index)
   134  	}
   135  
   136  	// Check for heartbeat interval
   137  	ttl = resp2.HeartbeatTTL
   138  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   139  		t.Fatalf("bad: %#v", ttl)
   140  	}
   141  
   142  	// Check for the node in the FSM
   143  	state := s1.fsm.State()
   144  	out, err := state.NodeByID(node.ID)
   145  	if err != nil {
   146  		t.Fatalf("err: %v", err)
   147  	}
   148  	if out == nil {
   149  		t.Fatalf("expected node")
   150  	}
   151  	if out.ModifyIndex != resp2.Index {
   152  		t.Fatalf("index mis-match")
   153  	}
   154  }
   155  
   156  func TestClientEndpoint_UpdateStatus_GetEvals(t *testing.T) {
   157  	s1 := testServer(t, nil)
   158  	defer s1.Shutdown()
   159  	codec := rpcClient(t, s1)
   160  	testutil.WaitForLeader(t, s1.RPC)
   161  
   162  	// Register a system job.
   163  	job := mock.SystemJob()
   164  	state := s1.fsm.State()
   165  	if err := state.UpsertJob(1, job); err != nil {
   166  		t.Fatalf("err: %v", err)
   167  	}
   168  
   169  	// Create the register request
   170  	node := mock.Node()
   171  	node.Status = structs.NodeStatusInit
   172  	reg := &structs.NodeRegisterRequest{
   173  		Node:         node,
   174  		WriteRequest: structs.WriteRequest{Region: "global"},
   175  	}
   176  
   177  	// Fetch the response
   178  	var resp structs.NodeUpdateResponse
   179  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   180  		t.Fatalf("err: %v", err)
   181  	}
   182  
   183  	// Check for heartbeat interval
   184  	ttl := resp.HeartbeatTTL
   185  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   186  		t.Fatalf("bad: %#v", ttl)
   187  	}
   188  
   189  	// Update the status
   190  	update := &structs.NodeUpdateStatusRequest{
   191  		NodeID:       node.ID,
   192  		Status:       structs.NodeStatusReady,
   193  		WriteRequest: structs.WriteRequest{Region: "global"},
   194  	}
   195  	var resp2 structs.NodeUpdateResponse
   196  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", update, &resp2); err != nil {
   197  		t.Fatalf("err: %v", err)
   198  	}
   199  	if resp2.Index == 0 {
   200  		t.Fatalf("bad index: %d", resp2.Index)
   201  	}
   202  
   203  	// Check for an eval caused by the system job.
   204  	if len(resp2.EvalIDs) != 1 {
   205  		t.Fatalf("expected one eval; got %#v", resp2.EvalIDs)
   206  	}
   207  
   208  	evalID := resp2.EvalIDs[0]
   209  	eval, err := state.EvalByID(evalID)
   210  	if err != nil {
   211  		t.Fatalf("could not get eval %v", evalID)
   212  	}
   213  
   214  	if eval.Type != "system" {
   215  		t.Fatalf("unexpected eval type; got %v; want %q", eval.Type, "system")
   216  	}
   217  
   218  	// Check for heartbeat interval
   219  	ttl = resp2.HeartbeatTTL
   220  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   221  		t.Fatalf("bad: %#v", ttl)
   222  	}
   223  
   224  	// Check for the node in the FSM
   225  	out, err := state.NodeByID(node.ID)
   226  	if err != nil {
   227  		t.Fatalf("err: %v", err)
   228  	}
   229  	if out == nil {
   230  		t.Fatalf("expected node")
   231  	}
   232  	if out.ModifyIndex != resp2.Index {
   233  		t.Fatalf("index mis-match")
   234  	}
   235  }
   236  
   237  func TestClientEndpoint_UpdateStatus_HeartbeatOnly(t *testing.T) {
   238  	s1 := testServer(t, nil)
   239  	defer s1.Shutdown()
   240  	codec := rpcClient(t, s1)
   241  	testutil.WaitForLeader(t, s1.RPC)
   242  
   243  	// Create the register request
   244  	node := mock.Node()
   245  	reg := &structs.NodeRegisterRequest{
   246  		Node:         node,
   247  		WriteRequest: structs.WriteRequest{Region: "global"},
   248  	}
   249  
   250  	// Fetch the response
   251  	var resp structs.NodeUpdateResponse
   252  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   253  		t.Fatalf("err: %v", err)
   254  	}
   255  
   256  	// Check for heartbeat interval
   257  	ttl := resp.HeartbeatTTL
   258  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   259  		t.Fatalf("bad: %#v", ttl)
   260  	}
   261  
   262  	// Update the status, static state
   263  	dereg := &structs.NodeUpdateStatusRequest{
   264  		NodeID:       node.ID,
   265  		Status:       node.Status,
   266  		WriteRequest: structs.WriteRequest{Region: "global"},
   267  	}
   268  	var resp2 structs.NodeUpdateResponse
   269  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil {
   270  		t.Fatalf("err: %v", err)
   271  	}
   272  	if resp2.Index != 0 {
   273  		t.Fatalf("bad index: %d", resp2.Index)
   274  	}
   275  
   276  	// Check for heartbeat interval
   277  	ttl = resp2.HeartbeatTTL
   278  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   279  		t.Fatalf("bad: %#v", ttl)
   280  	}
   281  }
   282  
   283  func TestClientEndpoint_UpdateDrain(t *testing.T) {
   284  	s1 := testServer(t, nil)
   285  	defer s1.Shutdown()
   286  	codec := rpcClient(t, s1)
   287  	testutil.WaitForLeader(t, s1.RPC)
   288  
   289  	// Create the register request
   290  	node := mock.Node()
   291  	reg := &structs.NodeRegisterRequest{
   292  		Node:         node,
   293  		WriteRequest: structs.WriteRequest{Region: "global"},
   294  	}
   295  
   296  	// Fetch the response
   297  	var resp structs.NodeUpdateResponse
   298  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   299  		t.Fatalf("err: %v", err)
   300  	}
   301  
   302  	// Update the status
   303  	dereg := &structs.NodeUpdateDrainRequest{
   304  		NodeID:       node.ID,
   305  		Drain:        true,
   306  		WriteRequest: structs.WriteRequest{Region: "global"},
   307  	}
   308  	var resp2 structs.NodeDrainUpdateResponse
   309  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateDrain", dereg, &resp2); err != nil {
   310  		t.Fatalf("err: %v", err)
   311  	}
   312  	if resp2.Index == 0 {
   313  		t.Fatalf("bad index: %d", resp2.Index)
   314  	}
   315  
   316  	// Check for the node in the FSM
   317  	state := s1.fsm.State()
   318  	out, err := state.NodeByID(node.ID)
   319  	if err != nil {
   320  		t.Fatalf("err: %v", err)
   321  	}
   322  	if !out.Drain {
   323  		t.Fatalf("bad: %#v", out)
   324  	}
   325  }
   326  
   327  func TestClientEndpoint_GetNode(t *testing.T) {
   328  	s1 := testServer(t, nil)
   329  	defer s1.Shutdown()
   330  	codec := rpcClient(t, s1)
   331  	testutil.WaitForLeader(t, s1.RPC)
   332  
   333  	// Create the register request
   334  	node := mock.Node()
   335  	reg := &structs.NodeRegisterRequest{
   336  		Node:         node,
   337  		WriteRequest: structs.WriteRequest{Region: "global"},
   338  	}
   339  
   340  	// Fetch the response
   341  	var resp structs.GenericResponse
   342  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   343  		t.Fatalf("err: %v", err)
   344  	}
   345  	node.CreateIndex = resp.Index
   346  	node.ModifyIndex = resp.Index
   347  
   348  	// Lookup the node
   349  	get := &structs.NodeSpecificRequest{
   350  		NodeID:       node.ID,
   351  		QueryOptions: structs.QueryOptions{Region: "global"},
   352  	}
   353  	var resp2 structs.SingleNodeResponse
   354  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil {
   355  		t.Fatalf("err: %v", err)
   356  	}
   357  	if resp2.Index != resp.Index {
   358  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   359  	}
   360  
   361  	if resp2.Node.ComputedClass == "" {
   362  		t.Fatalf("bad ComputedClass: %#v", resp2.Node)
   363  	}
   364  
   365  	if !reflect.DeepEqual(node, resp2.Node) {
   366  		t.Fatalf("bad: %#v %#v", node, resp2.Node)
   367  	}
   368  
   369  	// Lookup non-existing node
   370  	get.NodeID = "12345678-abcd-efab-cdef-123456789abc"
   371  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil {
   372  		t.Fatalf("err: %v", err)
   373  	}
   374  	if resp2.Index != resp.Index {
   375  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   376  	}
   377  	if resp2.Node != nil {
   378  		t.Fatalf("unexpected node")
   379  	}
   380  }
   381  
   382  func TestClientEndpoint_GetNode_Blocking(t *testing.T) {
   383  	s1 := testServer(t, nil)
   384  	defer s1.Shutdown()
   385  	state := s1.fsm.State()
   386  	codec := rpcClient(t, s1)
   387  	testutil.WaitForLeader(t, s1.RPC)
   388  
   389  	// Create the node
   390  	node1 := mock.Node()
   391  	node2 := mock.Node()
   392  
   393  	// First create an unrelated node.
   394  	time.AfterFunc(100*time.Millisecond, func() {
   395  		if err := state.UpsertNode(100, node1); err != nil {
   396  			t.Fatalf("err: %v", err)
   397  		}
   398  	})
   399  
   400  	// Upsert the node we are watching later
   401  	time.AfterFunc(200*time.Millisecond, func() {
   402  		if err := state.UpsertNode(200, node2); err != nil {
   403  			t.Fatalf("err: %v", err)
   404  		}
   405  	})
   406  
   407  	// Lookup the node
   408  	req := &structs.NodeSpecificRequest{
   409  		NodeID: node2.ID,
   410  		QueryOptions: structs.QueryOptions{
   411  			Region:        "global",
   412  			MinQueryIndex: 50,
   413  		},
   414  	}
   415  	var resp structs.SingleNodeResponse
   416  	start := time.Now()
   417  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp); err != nil {
   418  		t.Fatalf("err: %v", err)
   419  	}
   420  
   421  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
   422  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   423  	}
   424  	if resp.Index != 200 {
   425  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   426  	}
   427  	if resp.Node == nil || resp.Node.ID != node2.ID {
   428  		t.Fatalf("bad: %#v", resp.Node)
   429  	}
   430  
   431  	// Node update triggers watches
   432  	time.AfterFunc(100*time.Millisecond, func() {
   433  		nodeUpdate := mock.Node()
   434  		nodeUpdate.ID = node2.ID
   435  		nodeUpdate.Status = structs.NodeStatusDown
   436  		if err := state.UpsertNode(300, nodeUpdate); err != nil {
   437  			t.Fatalf("err: %v", err)
   438  		}
   439  	})
   440  
   441  	req.QueryOptions.MinQueryIndex = 250
   442  	var resp2 structs.SingleNodeResponse
   443  	start = time.Now()
   444  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp2); err != nil {
   445  		t.Fatalf("err: %v", err)
   446  	}
   447  
   448  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   449  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   450  	}
   451  	if resp2.Index != 300 {
   452  		t.Fatalf("Bad index: %d %d", resp2.Index, 300)
   453  	}
   454  	if resp2.Node == nil || resp2.Node.Status != structs.NodeStatusDown {
   455  		t.Fatalf("bad: %#v", resp2.Node)
   456  	}
   457  
   458  	// Node delete triggers watches
   459  	time.AfterFunc(100*time.Millisecond, func() {
   460  		if err := state.DeleteNode(400, node2.ID); err != nil {
   461  			t.Fatalf("err: %v", err)
   462  		}
   463  	})
   464  
   465  	req.QueryOptions.MinQueryIndex = 350
   466  	var resp3 structs.SingleNodeResponse
   467  	start = time.Now()
   468  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp3); err != nil {
   469  		t.Fatalf("err: %v", err)
   470  	}
   471  
   472  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   473  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   474  	}
   475  	if resp3.Index != 400 {
   476  		t.Fatalf("Bad index: %d %d", resp2.Index, 400)
   477  	}
   478  	if resp3.Node != nil {
   479  		t.Fatalf("bad: %#v", resp3.Node)
   480  	}
   481  }
   482  
   483  func TestClientEndpoint_GetAllocs(t *testing.T) {
   484  	s1 := testServer(t, nil)
   485  	defer s1.Shutdown()
   486  	codec := rpcClient(t, s1)
   487  	testutil.WaitForLeader(t, s1.RPC)
   488  
   489  	// Create the register request
   490  	node := mock.Node()
   491  	reg := &structs.NodeRegisterRequest{
   492  		Node:         node,
   493  		WriteRequest: structs.WriteRequest{Region: "global"},
   494  	}
   495  
   496  	// Fetch the response
   497  	var resp structs.GenericResponse
   498  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   499  		t.Fatalf("err: %v", err)
   500  	}
   501  	node.CreateIndex = resp.Index
   502  	node.ModifyIndex = resp.Index
   503  
   504  	// Inject fake evaluations
   505  	alloc := mock.Alloc()
   506  	alloc.NodeID = node.ID
   507  	state := s1.fsm.State()
   508  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   509  	if err != nil {
   510  		t.Fatalf("err: %v", err)
   511  	}
   512  
   513  	// Lookup the allocs
   514  	get := &structs.NodeSpecificRequest{
   515  		NodeID:       node.ID,
   516  		QueryOptions: structs.QueryOptions{Region: "global"},
   517  	}
   518  	var resp2 structs.NodeAllocsResponse
   519  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil {
   520  		t.Fatalf("err: %v", err)
   521  	}
   522  	if resp2.Index != 100 {
   523  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   524  	}
   525  
   526  	if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID {
   527  		t.Fatalf("bad: %#v", resp2.Allocs)
   528  	}
   529  
   530  	// Lookup non-existing node
   531  	get.NodeID = "foobarbaz"
   532  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil {
   533  		t.Fatalf("err: %v", err)
   534  	}
   535  	if resp2.Index != 100 {
   536  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   537  	}
   538  	if len(resp2.Allocs) != 0 {
   539  		t.Fatalf("unexpected node")
   540  	}
   541  }
   542  
   543  func TestClientEndpoint_GetClientAllocs(t *testing.T) {
   544  	s1 := testServer(t, nil)
   545  	defer s1.Shutdown()
   546  	codec := rpcClient(t, s1)
   547  	testutil.WaitForLeader(t, s1.RPC)
   548  
   549  	// Create the register request
   550  	node := mock.Node()
   551  	reg := &structs.NodeRegisterRequest{
   552  		Node:         node,
   553  		WriteRequest: structs.WriteRequest{Region: "global"},
   554  	}
   555  
   556  	// Fetch the response
   557  	var resp structs.GenericResponse
   558  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   559  		t.Fatalf("err: %v", err)
   560  	}
   561  	node.CreateIndex = resp.Index
   562  	node.ModifyIndex = resp.Index
   563  
   564  	// Inject fake evaluations
   565  	alloc := mock.Alloc()
   566  	alloc.NodeID = node.ID
   567  	state := s1.fsm.State()
   568  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   569  	if err != nil {
   570  		t.Fatalf("err: %v", err)
   571  	}
   572  
   573  	// Lookup the allocs
   574  	get := &structs.NodeSpecificRequest{
   575  		NodeID:       node.ID,
   576  		QueryOptions: structs.QueryOptions{Region: "global"},
   577  	}
   578  	var resp2 structs.NodeClientAllocsResponse
   579  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetClientAllocs", get, &resp2); err != nil {
   580  		t.Fatalf("err: %v", err)
   581  	}
   582  	if resp2.Index != 100 {
   583  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   584  	}
   585  
   586  	if len(resp2.Allocs) != 1 || resp2.Allocs[alloc.ID] != 100 {
   587  		t.Fatalf("bad: %#v", resp2.Allocs)
   588  	}
   589  
   590  	// Lookup non-existing node
   591  	get.NodeID = "foobarbaz"
   592  	var resp3 structs.NodeClientAllocsResponse
   593  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetClientAllocs", get, &resp3); err != nil {
   594  		t.Fatalf("err: %v", err)
   595  	}
   596  	if resp3.Index != 100 {
   597  		t.Fatalf("Bad index: %d %d", resp3.Index, 100)
   598  	}
   599  	if len(resp3.Allocs) != 0 {
   600  		t.Fatalf("unexpected node %#v", resp3.Allocs)
   601  	}
   602  }
   603  
   604  func TestClientEndpoint_GetClientAllocs_Blocking(t *testing.T) {
   605  	s1 := testServer(t, nil)
   606  	defer s1.Shutdown()
   607  	codec := rpcClient(t, s1)
   608  	testutil.WaitForLeader(t, s1.RPC)
   609  
   610  	// Create the register request
   611  	node := mock.Node()
   612  	reg := &structs.NodeRegisterRequest{
   613  		Node:         node,
   614  		WriteRequest: structs.WriteRequest{Region: "global"},
   615  	}
   616  
   617  	// Fetch the response
   618  	var resp structs.GenericResponse
   619  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   620  		t.Fatalf("err: %v", err)
   621  	}
   622  	node.CreateIndex = resp.Index
   623  	node.ModifyIndex = resp.Index
   624  
   625  	// Inject fake evaluations async
   626  	alloc := mock.Alloc()
   627  	alloc.NodeID = node.ID
   628  	state := s1.fsm.State()
   629  	start := time.Now()
   630  	time.AfterFunc(100*time.Millisecond, func() {
   631  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   632  		if err != nil {
   633  			t.Fatalf("err: %v", err)
   634  		}
   635  	})
   636  
   637  	// Lookup the allocs in a blocking query
   638  	req := &structs.NodeSpecificRequest{
   639  		NodeID: node.ID,
   640  		QueryOptions: structs.QueryOptions{
   641  			Region:        "global",
   642  			MinQueryIndex: 50,
   643  			MaxQueryTime:  time.Second,
   644  		},
   645  	}
   646  	var resp2 structs.NodeClientAllocsResponse
   647  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetClientAllocs", req, &resp2); err != nil {
   648  		t.Fatalf("err: %v", err)
   649  	}
   650  
   651  	// Should block at least 100ms
   652  	if time.Since(start) < 100*time.Millisecond {
   653  		t.Fatalf("too fast")
   654  	}
   655  
   656  	if resp2.Index != 100 {
   657  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   658  	}
   659  
   660  	if len(resp2.Allocs) != 1 || resp2.Allocs[alloc.ID] != 100 {
   661  		t.Fatalf("bad: %#v", resp2.Allocs)
   662  	}
   663  
   664  	// Alloc updates fire watches
   665  	time.AfterFunc(100*time.Millisecond, func() {
   666  		allocUpdate := mock.Alloc()
   667  		allocUpdate.NodeID = alloc.NodeID
   668  		allocUpdate.ID = alloc.ID
   669  		allocUpdate.ClientStatus = structs.AllocClientStatusRunning
   670  		err := state.UpsertAllocs(200, []*structs.Allocation{allocUpdate})
   671  		if err != nil {
   672  			t.Fatalf("err: %v", err)
   673  		}
   674  	})
   675  
   676  	req.QueryOptions.MinQueryIndex = 150
   677  	var resp3 structs.NodeClientAllocsResponse
   678  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetClientAllocs", req, &resp3); err != nil {
   679  		t.Fatalf("err: %v", err)
   680  	}
   681  
   682  	if time.Since(start) < 100*time.Millisecond {
   683  		t.Fatalf("too fast")
   684  	}
   685  	if resp3.Index != 200 {
   686  		t.Fatalf("Bad index: %d %d", resp3.Index, 200)
   687  	}
   688  	if len(resp3.Allocs) != 1 || resp3.Allocs[alloc.ID] != 200 {
   689  		t.Fatalf("bad: %#v", resp3.Allocs)
   690  	}
   691  }
   692  
   693  func TestClientEndpoint_GetAllocs_Blocking(t *testing.T) {
   694  	s1 := testServer(t, nil)
   695  	defer s1.Shutdown()
   696  	codec := rpcClient(t, s1)
   697  	testutil.WaitForLeader(t, s1.RPC)
   698  
   699  	// Create the register request
   700  	node := mock.Node()
   701  	reg := &structs.NodeRegisterRequest{
   702  		Node:         node,
   703  		WriteRequest: structs.WriteRequest{Region: "global"},
   704  	}
   705  
   706  	// Fetch the response
   707  	var resp structs.GenericResponse
   708  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   709  		t.Fatalf("err: %v", err)
   710  	}
   711  	node.CreateIndex = resp.Index
   712  	node.ModifyIndex = resp.Index
   713  
   714  	// Inject fake evaluations async
   715  	alloc := mock.Alloc()
   716  	alloc.NodeID = node.ID
   717  	state := s1.fsm.State()
   718  	start := time.Now()
   719  	time.AfterFunc(100*time.Millisecond, func() {
   720  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   721  		if err != nil {
   722  			t.Fatalf("err: %v", err)
   723  		}
   724  	})
   725  
   726  	// Lookup the allocs in a blocking query
   727  	req := &structs.NodeSpecificRequest{
   728  		NodeID: node.ID,
   729  		QueryOptions: structs.QueryOptions{
   730  			Region:        "global",
   731  			MinQueryIndex: 50,
   732  			MaxQueryTime:  time.Second,
   733  		},
   734  	}
   735  	var resp2 structs.NodeAllocsResponse
   736  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", req, &resp2); err != nil {
   737  		t.Fatalf("err: %v", err)
   738  	}
   739  
   740  	// Should block at least 100ms
   741  	if time.Since(start) < 100*time.Millisecond {
   742  		t.Fatalf("too fast")
   743  	}
   744  
   745  	if resp2.Index != 100 {
   746  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   747  	}
   748  
   749  	if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID {
   750  		t.Fatalf("bad: %#v", resp2.Allocs)
   751  	}
   752  
   753  	// Alloc updates fire watches
   754  	time.AfterFunc(100*time.Millisecond, func() {
   755  		allocUpdate := mock.Alloc()
   756  		allocUpdate.NodeID = alloc.NodeID
   757  		allocUpdate.ID = alloc.ID
   758  		allocUpdate.ClientStatus = structs.AllocClientStatusRunning
   759  		err := state.UpdateAllocsFromClient(200, []*structs.Allocation{allocUpdate})
   760  		if err != nil {
   761  			t.Fatalf("err: %v", err)
   762  		}
   763  	})
   764  
   765  	req.QueryOptions.MinQueryIndex = 150
   766  	var resp3 structs.NodeAllocsResponse
   767  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", req, &resp3); err != nil {
   768  		t.Fatalf("err: %v", err)
   769  	}
   770  
   771  	if time.Since(start) < 100*time.Millisecond {
   772  		t.Fatalf("too fast")
   773  	}
   774  	if resp3.Index != 200 {
   775  		t.Fatalf("Bad index: %d %d", resp3.Index, 200)
   776  	}
   777  	if len(resp3.Allocs) != 1 || resp3.Allocs[0].ClientStatus != structs.AllocClientStatusRunning {
   778  		t.Fatalf("bad: %#v", resp3.Allocs[0])
   779  	}
   780  }
   781  
   782  func TestClientEndpoint_UpdateAlloc(t *testing.T) {
   783  	s1 := testServer(t, nil)
   784  	defer s1.Shutdown()
   785  	codec := rpcClient(t, s1)
   786  	testutil.WaitForLeader(t, s1.RPC)
   787  
   788  	// Create the register request
   789  	node := mock.Node()
   790  	reg := &structs.NodeRegisterRequest{
   791  		Node:         node,
   792  		WriteRequest: structs.WriteRequest{Region: "global"},
   793  	}
   794  
   795  	// Fetch the response
   796  	var resp structs.GenericResponse
   797  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   798  		t.Fatalf("err: %v", err)
   799  	}
   800  
   801  	// Inject fake evaluations
   802  	alloc := mock.Alloc()
   803  	alloc.NodeID = node.ID
   804  	state := s1.fsm.State()
   805  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   806  	if err != nil {
   807  		t.Fatalf("err: %v", err)
   808  	}
   809  
   810  	// Attempt update
   811  	clientAlloc := new(structs.Allocation)
   812  	*clientAlloc = *alloc
   813  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   814  
   815  	// Update the alloc
   816  	update := &structs.AllocUpdateRequest{
   817  		Alloc:        []*structs.Allocation{clientAlloc},
   818  		WriteRequest: structs.WriteRequest{Region: "global"},
   819  	}
   820  	var resp2 structs.NodeAllocsResponse
   821  	start := time.Now()
   822  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateAlloc", update, &resp2); err != nil {
   823  		t.Fatalf("err: %v", err)
   824  	}
   825  	if resp2.Index == 0 {
   826  		t.Fatalf("Bad index: %d", resp2.Index)
   827  	}
   828  	if diff := time.Since(start); diff < batchUpdateInterval {
   829  		t.Fatalf("too fast: %v", diff)
   830  	}
   831  
   832  	// Lookup the alloc
   833  	out, err := state.AllocByID(alloc.ID)
   834  	if err != nil {
   835  		t.Fatalf("err: %v", err)
   836  	}
   837  	if out.ClientStatus != structs.AllocClientStatusFailed {
   838  		t.Fatalf("Bad: %#v", out)
   839  	}
   840  }
   841  
   842  func TestClientEndpoint_BatchUpdate(t *testing.T) {
   843  	s1 := testServer(t, nil)
   844  	defer s1.Shutdown()
   845  	codec := rpcClient(t, s1)
   846  	testutil.WaitForLeader(t, s1.RPC)
   847  
   848  	// Create the register request
   849  	node := mock.Node()
   850  	reg := &structs.NodeRegisterRequest{
   851  		Node:         node,
   852  		WriteRequest: structs.WriteRequest{Region: "global"},
   853  	}
   854  
   855  	// Fetch the response
   856  	var resp structs.GenericResponse
   857  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   858  		t.Fatalf("err: %v", err)
   859  	}
   860  
   861  	// Inject fake evaluations
   862  	alloc := mock.Alloc()
   863  	alloc.NodeID = node.ID
   864  	state := s1.fsm.State()
   865  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   866  	if err != nil {
   867  		t.Fatalf("err: %v", err)
   868  	}
   869  
   870  	// Attempt update
   871  	clientAlloc := new(structs.Allocation)
   872  	*clientAlloc = *alloc
   873  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   874  
   875  	// Call to do the batch update
   876  	bf := NewBatchFuture()
   877  	endpoint := s1.endpoints.Node
   878  	endpoint.batchUpdate(bf, []*structs.Allocation{clientAlloc})
   879  	if err := bf.Wait(); err != nil {
   880  		t.Fatalf("err: %v", err)
   881  	}
   882  	if bf.Index() == 0 {
   883  		t.Fatalf("Bad index: %d", bf.Index())
   884  	}
   885  
   886  	// Lookup the alloc
   887  	out, err := state.AllocByID(alloc.ID)
   888  	if err != nil {
   889  		t.Fatalf("err: %v", err)
   890  	}
   891  	if out.ClientStatus != structs.AllocClientStatusFailed {
   892  		t.Fatalf("Bad: %#v", out)
   893  	}
   894  }
   895  
   896  func TestClientEndpoint_CreateNodeEvals(t *testing.T) {
   897  	s1 := testServer(t, nil)
   898  	defer s1.Shutdown()
   899  	testutil.WaitForLeader(t, s1.RPC)
   900  
   901  	// Inject fake evaluations
   902  	alloc := mock.Alloc()
   903  	state := s1.fsm.State()
   904  	if err := state.UpsertAllocs(1, []*structs.Allocation{alloc}); err != nil {
   905  		t.Fatalf("err: %v", err)
   906  	}
   907  
   908  	// Inject a fake system job.
   909  	job := mock.SystemJob()
   910  	if err := state.UpsertJob(1, job); err != nil {
   911  		t.Fatalf("err: %v", err)
   912  	}
   913  
   914  	// Create some evaluations
   915  	ids, index, err := s1.endpoints.Node.createNodeEvals(alloc.NodeID, 1)
   916  	if err != nil {
   917  		t.Fatalf("err: %v", err)
   918  	}
   919  	if index == 0 {
   920  		t.Fatalf("bad: %d", index)
   921  	}
   922  	if len(ids) != 2 {
   923  		t.Fatalf("bad: %s", ids)
   924  	}
   925  
   926  	// Lookup the evaluations
   927  	evalByType := make(map[string]*structs.Evaluation, 2)
   928  	for _, id := range ids {
   929  		eval, err := state.EvalByID(id)
   930  		if err != nil {
   931  			t.Fatalf("err: %v", err)
   932  		}
   933  		if eval == nil {
   934  			t.Fatalf("expected eval")
   935  		}
   936  
   937  		if old, ok := evalByType[eval.Type]; ok {
   938  			t.Fatalf("multiple evals of the same type: %v and %v", old, eval)
   939  		}
   940  
   941  		evalByType[eval.Type] = eval
   942  	}
   943  
   944  	if len(evalByType) != 2 {
   945  		t.Fatalf("Expected a service and system job; got %#v", evalByType)
   946  	}
   947  
   948  	// Ensure the evals are correct.
   949  	for schedType, eval := range evalByType {
   950  		expPriority := alloc.Job.Priority
   951  		expJobID := alloc.JobID
   952  		if schedType == "system" {
   953  			expPriority = job.Priority
   954  			expJobID = job.ID
   955  		}
   956  
   957  		if eval.CreateIndex != index {
   958  			t.Fatalf("CreateIndex mis-match on type %v: %#v", schedType, eval)
   959  		}
   960  		if eval.TriggeredBy != structs.EvalTriggerNodeUpdate {
   961  			t.Fatalf("TriggeredBy incorrect on type %v: %#v", schedType, eval)
   962  		}
   963  		if eval.NodeID != alloc.NodeID {
   964  			t.Fatalf("NodeID incorrect on type %v: %#v", schedType, eval)
   965  		}
   966  		if eval.NodeModifyIndex != 1 {
   967  			t.Fatalf("NodeModifyIndex incorrect on type %v: %#v", schedType, eval)
   968  		}
   969  		if eval.Status != structs.EvalStatusPending {
   970  			t.Fatalf("Status incorrect on type %v: %#v", schedType, eval)
   971  		}
   972  		if eval.Priority != expPriority {
   973  			t.Fatalf("Priority incorrect on type %v: %#v", schedType, eval)
   974  		}
   975  		if eval.JobID != expJobID {
   976  			t.Fatalf("JobID incorrect on type %v: %#v", schedType, eval)
   977  		}
   978  	}
   979  }
   980  
   981  func TestClientEndpoint_Evaluate(t *testing.T) {
   982  	s1 := testServer(t, func(c *Config) {
   983  		c.NumSchedulers = 0 // Prevent automatic dequeue
   984  	})
   985  	defer s1.Shutdown()
   986  	codec := rpcClient(t, s1)
   987  	testutil.WaitForLeader(t, s1.RPC)
   988  
   989  	// Inject fake evaluations
   990  	alloc := mock.Alloc()
   991  	node := mock.Node()
   992  	node.ID = alloc.NodeID
   993  	state := s1.fsm.State()
   994  	err := state.UpsertNode(1, node)
   995  	if err != nil {
   996  		t.Fatalf("err: %v", err)
   997  	}
   998  	err = state.UpsertAllocs(2, []*structs.Allocation{alloc})
   999  	if err != nil {
  1000  		t.Fatalf("err: %v", err)
  1001  	}
  1002  
  1003  	// Re-evaluate
  1004  	req := &structs.NodeEvaluateRequest{
  1005  		NodeID:       alloc.NodeID,
  1006  		WriteRequest: structs.WriteRequest{Region: "global"},
  1007  	}
  1008  
  1009  	// Fetch the response
  1010  	var resp structs.NodeUpdateResponse
  1011  	if err := msgpackrpc.CallWithCodec(codec, "Node.Evaluate", req, &resp); err != nil {
  1012  		t.Fatalf("err: %v", err)
  1013  	}
  1014  	if resp.Index == 0 {
  1015  		t.Fatalf("bad index: %d", resp.Index)
  1016  	}
  1017  
  1018  	// Create some evaluations
  1019  	ids := resp.EvalIDs
  1020  	if len(ids) != 1 {
  1021  		t.Fatalf("bad: %s", ids)
  1022  	}
  1023  
  1024  	// Lookup the evaluation
  1025  	eval, err := state.EvalByID(ids[0])
  1026  	if err != nil {
  1027  		t.Fatalf("err: %v", err)
  1028  	}
  1029  	if eval == nil {
  1030  		t.Fatalf("expected eval")
  1031  	}
  1032  	if eval.CreateIndex != resp.Index {
  1033  		t.Fatalf("index mis-match")
  1034  	}
  1035  
  1036  	if eval.Priority != alloc.Job.Priority {
  1037  		t.Fatalf("bad: %#v", eval)
  1038  	}
  1039  	if eval.Type != alloc.Job.Type {
  1040  		t.Fatalf("bad: %#v", eval)
  1041  	}
  1042  	if eval.TriggeredBy != structs.EvalTriggerNodeUpdate {
  1043  		t.Fatalf("bad: %#v", eval)
  1044  	}
  1045  	if eval.JobID != alloc.JobID {
  1046  		t.Fatalf("bad: %#v", eval)
  1047  	}
  1048  	if eval.NodeID != alloc.NodeID {
  1049  		t.Fatalf("bad: %#v", eval)
  1050  	}
  1051  	if eval.NodeModifyIndex != 1 {
  1052  		t.Fatalf("bad: %#v", eval)
  1053  	}
  1054  	if eval.Status != structs.EvalStatusPending {
  1055  		t.Fatalf("bad: %#v", eval)
  1056  	}
  1057  }
  1058  
  1059  func TestClientEndpoint_ListNodes(t *testing.T) {
  1060  	s1 := testServer(t, nil)
  1061  	defer s1.Shutdown()
  1062  	codec := rpcClient(t, s1)
  1063  	testutil.WaitForLeader(t, s1.RPC)
  1064  
  1065  	// Create the register request
  1066  	node := mock.Node()
  1067  	reg := &structs.NodeRegisterRequest{
  1068  		Node:         node,
  1069  		WriteRequest: structs.WriteRequest{Region: "global"},
  1070  	}
  1071  
  1072  	// Fetch the response
  1073  	var resp structs.GenericResponse
  1074  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
  1075  		t.Fatalf("err: %v", err)
  1076  	}
  1077  	node.CreateIndex = resp.Index
  1078  	node.ModifyIndex = resp.Index
  1079  
  1080  	// Lookup the node
  1081  	get := &structs.NodeListRequest{
  1082  		QueryOptions: structs.QueryOptions{Region: "global"},
  1083  	}
  1084  	var resp2 structs.NodeListResponse
  1085  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp2); err != nil {
  1086  		t.Fatalf("err: %v", err)
  1087  	}
  1088  	if resp2.Index != resp.Index {
  1089  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
  1090  	}
  1091  
  1092  	if len(resp2.Nodes) != 1 {
  1093  		t.Fatalf("bad: %#v", resp2.Nodes)
  1094  	}
  1095  	if resp2.Nodes[0].ID != node.ID {
  1096  		t.Fatalf("bad: %#v", resp2.Nodes[0])
  1097  	}
  1098  
  1099  	// Lookup the node with prefix
  1100  	get = &structs.NodeListRequest{
  1101  		QueryOptions: structs.QueryOptions{Region: "global", Prefix: node.ID[:4]},
  1102  	}
  1103  	var resp3 structs.NodeListResponse
  1104  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp3); err != nil {
  1105  		t.Fatalf("err: %v", err)
  1106  	}
  1107  	if resp3.Index != resp.Index {
  1108  		t.Fatalf("Bad index: %d %d", resp3.Index, resp2.Index)
  1109  	}
  1110  
  1111  	if len(resp3.Nodes) != 1 {
  1112  		t.Fatalf("bad: %#v", resp3.Nodes)
  1113  	}
  1114  	if resp3.Nodes[0].ID != node.ID {
  1115  		t.Fatalf("bad: %#v", resp3.Nodes[0])
  1116  	}
  1117  }
  1118  
  1119  func TestClientEndpoint_ListNodes_Blocking(t *testing.T) {
  1120  	s1 := testServer(t, nil)
  1121  	defer s1.Shutdown()
  1122  	state := s1.fsm.State()
  1123  	codec := rpcClient(t, s1)
  1124  	testutil.WaitForLeader(t, s1.RPC)
  1125  
  1126  	// Create the node
  1127  	node := mock.Node()
  1128  
  1129  	// Node upsert triggers watches
  1130  	time.AfterFunc(100*time.Millisecond, func() {
  1131  		if err := state.UpsertNode(2, node); err != nil {
  1132  			t.Fatalf("err: %v", err)
  1133  		}
  1134  	})
  1135  
  1136  	req := &structs.NodeListRequest{
  1137  		QueryOptions: structs.QueryOptions{
  1138  			Region:        "global",
  1139  			MinQueryIndex: 1,
  1140  		},
  1141  	}
  1142  	start := time.Now()
  1143  	var resp structs.NodeListResponse
  1144  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp); err != nil {
  1145  		t.Fatalf("err: %v", err)
  1146  	}
  1147  
  1148  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1149  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1150  	}
  1151  	if resp.Index != 2 {
  1152  		t.Fatalf("Bad index: %d %d", resp.Index, 2)
  1153  	}
  1154  	if len(resp.Nodes) != 1 || resp.Nodes[0].ID != node.ID {
  1155  		t.Fatalf("bad: %#v", resp.Nodes)
  1156  	}
  1157  
  1158  	// Node drain updates trigger watches.
  1159  	time.AfterFunc(100*time.Millisecond, func() {
  1160  		if err := state.UpdateNodeDrain(3, node.ID, true); err != nil {
  1161  			t.Fatalf("err: %v", err)
  1162  		}
  1163  	})
  1164  
  1165  	req.MinQueryIndex = 2
  1166  	var resp2 structs.NodeListResponse
  1167  	start = time.Now()
  1168  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp2); err != nil {
  1169  		t.Fatalf("err: %v", err)
  1170  	}
  1171  
  1172  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1173  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
  1174  	}
  1175  	if resp2.Index != 3 {
  1176  		t.Fatalf("Bad index: %d %d", resp2.Index, 3)
  1177  	}
  1178  	if len(resp2.Nodes) != 1 || !resp2.Nodes[0].Drain {
  1179  		t.Fatalf("bad: %#v", resp2.Nodes)
  1180  	}
  1181  
  1182  	// Node status update triggers watches
  1183  	time.AfterFunc(100*time.Millisecond, func() {
  1184  		if err := state.UpdateNodeStatus(4, node.ID, structs.NodeStatusDown); err != nil {
  1185  			t.Fatalf("err: %v", err)
  1186  		}
  1187  	})
  1188  
  1189  	req.MinQueryIndex = 3
  1190  	var resp3 structs.NodeListResponse
  1191  	start = time.Now()
  1192  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp3); err != nil {
  1193  		t.Fatalf("err: %v", err)
  1194  	}
  1195  
  1196  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1197  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp3)
  1198  	}
  1199  	if resp3.Index != 4 {
  1200  		t.Fatalf("Bad index: %d %d", resp3.Index, 4)
  1201  	}
  1202  	if len(resp3.Nodes) != 1 || resp3.Nodes[0].Status != structs.NodeStatusDown {
  1203  		t.Fatalf("bad: %#v", resp3.Nodes)
  1204  	}
  1205  
  1206  	// Node delete triggers watches.
  1207  	time.AfterFunc(100*time.Millisecond, func() {
  1208  		if err := state.DeleteNode(5, node.ID); err != nil {
  1209  			t.Fatalf("err: %v", err)
  1210  		}
  1211  	})
  1212  
  1213  	req.MinQueryIndex = 4
  1214  	var resp4 structs.NodeListResponse
  1215  	start = time.Now()
  1216  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp4); err != nil {
  1217  		t.Fatalf("err: %v", err)
  1218  	}
  1219  
  1220  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1221  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp4)
  1222  	}
  1223  	if resp4.Index != 5 {
  1224  		t.Fatalf("Bad index: %d %d", resp4.Index, 5)
  1225  	}
  1226  	if len(resp4.Nodes) != 0 {
  1227  		t.Fatalf("bad: %#v", resp4.Nodes)
  1228  	}
  1229  }
  1230  
  1231  func TestBatchFuture(t *testing.T) {
  1232  	bf := NewBatchFuture()
  1233  
  1234  	// Async respond to the future
  1235  	expect := fmt.Errorf("testing")
  1236  	go func() {
  1237  		time.Sleep(10 * time.Millisecond)
  1238  		bf.Respond(1000, expect)
  1239  	}()
  1240  
  1241  	// Block for the result
  1242  	start := time.Now()
  1243  	err := bf.Wait()
  1244  	diff := time.Since(start)
  1245  	if diff < 5*time.Millisecond {
  1246  		t.Fatalf("too fast")
  1247  	}
  1248  
  1249  	// Check the results
  1250  	if err != expect {
  1251  		t.Fatalf("bad: %s", err)
  1252  	}
  1253  	if bf.Index() != 1000 {
  1254  		t.Fatalf("bad: %d", bf.Index())
  1255  	}
  1256  }