github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/nomad/node_endpoint_test.go (about)

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