github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/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  }
    49  
    50  func TestClientEndpoint_Deregister(t *testing.T) {
    51  	s1 := testServer(t, nil)
    52  	defer s1.Shutdown()
    53  	codec := rpcClient(t, s1)
    54  	testutil.WaitForLeader(t, s1.RPC)
    55  
    56  	// Create the register request
    57  	node := mock.Node()
    58  	reg := &structs.NodeRegisterRequest{
    59  		Node:         node,
    60  		WriteRequest: structs.WriteRequest{Region: "global"},
    61  	}
    62  
    63  	// Fetch the response
    64  	var resp structs.GenericResponse
    65  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
    66  		t.Fatalf("err: %v", err)
    67  	}
    68  
    69  	// Deregister
    70  	dereg := &structs.NodeDeregisterRequest{
    71  		NodeID:       node.ID,
    72  		WriteRequest: structs.WriteRequest{Region: "global"},
    73  	}
    74  	var resp2 structs.GenericResponse
    75  	if err := msgpackrpc.CallWithCodec(codec, "Node.Deregister", dereg, &resp2); err != nil {
    76  		t.Fatalf("err: %v", err)
    77  	}
    78  	if resp2.Index == 0 {
    79  		t.Fatalf("bad index: %d", resp2.Index)
    80  	}
    81  
    82  	// Check for the node in the FSM
    83  	state := s1.fsm.State()
    84  	out, err := state.NodeByID(node.ID)
    85  	if err != nil {
    86  		t.Fatalf("err: %v", err)
    87  	}
    88  	if out != nil {
    89  		t.Fatalf("unexpected node")
    90  	}
    91  }
    92  
    93  func TestClientEndpoint_UpdateStatus(t *testing.T) {
    94  	s1 := testServer(t, nil)
    95  	defer s1.Shutdown()
    96  	codec := rpcClient(t, s1)
    97  	testutil.WaitForLeader(t, s1.RPC)
    98  
    99  	// Create the register request
   100  	node := mock.Node()
   101  	reg := &structs.NodeRegisterRequest{
   102  		Node:         node,
   103  		WriteRequest: structs.WriteRequest{Region: "global"},
   104  	}
   105  
   106  	// Fetch the response
   107  	var resp structs.NodeUpdateResponse
   108  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   109  		t.Fatalf("err: %v", err)
   110  	}
   111  
   112  	// Check for heartbeat interval
   113  	ttl := resp.HeartbeatTTL
   114  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   115  		t.Fatalf("bad: %#v", ttl)
   116  	}
   117  
   118  	// Update the status
   119  	dereg := &structs.NodeUpdateStatusRequest{
   120  		NodeID:       node.ID,
   121  		Status:       structs.NodeStatusInit,
   122  		WriteRequest: structs.WriteRequest{Region: "global"},
   123  	}
   124  	var resp2 structs.NodeUpdateResponse
   125  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil {
   126  		t.Fatalf("err: %v", err)
   127  	}
   128  	if resp2.Index == 0 {
   129  		t.Fatalf("bad index: %d", resp2.Index)
   130  	}
   131  
   132  	// Check for heartbeat interval
   133  	ttl = resp2.HeartbeatTTL
   134  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   135  		t.Fatalf("bad: %#v", ttl)
   136  	}
   137  
   138  	// Check for the node in the FSM
   139  	state := s1.fsm.State()
   140  	out, err := state.NodeByID(node.ID)
   141  	if err != nil {
   142  		t.Fatalf("err: %v", err)
   143  	}
   144  	if out == nil {
   145  		t.Fatalf("expected node")
   146  	}
   147  	if out.ModifyIndex != resp2.Index {
   148  		t.Fatalf("index mis-match")
   149  	}
   150  }
   151  
   152  func TestClientEndpoint_UpdateStatus_GetEvals(t *testing.T) {
   153  	s1 := testServer(t, nil)
   154  	defer s1.Shutdown()
   155  	codec := rpcClient(t, s1)
   156  	testutil.WaitForLeader(t, s1.RPC)
   157  
   158  	// Register a system job.
   159  	job := mock.SystemJob()
   160  	state := s1.fsm.State()
   161  	if err := state.UpsertJob(1, job); err != nil {
   162  		t.Fatalf("err: %v", err)
   163  	}
   164  
   165  	// Create the register request
   166  	node := mock.Node()
   167  	node.Status = structs.NodeStatusInit
   168  	reg := &structs.NodeRegisterRequest{
   169  		Node:         node,
   170  		WriteRequest: structs.WriteRequest{Region: "global"},
   171  	}
   172  
   173  	// Fetch the response
   174  	var resp structs.NodeUpdateResponse
   175  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   176  		t.Fatalf("err: %v", err)
   177  	}
   178  
   179  	// Check for heartbeat interval
   180  	ttl := resp.HeartbeatTTL
   181  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   182  		t.Fatalf("bad: %#v", ttl)
   183  	}
   184  
   185  	// Update the status
   186  	update := &structs.NodeUpdateStatusRequest{
   187  		NodeID:       node.ID,
   188  		Status:       structs.NodeStatusReady,
   189  		WriteRequest: structs.WriteRequest{Region: "global"},
   190  	}
   191  	var resp2 structs.NodeUpdateResponse
   192  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", update, &resp2); err != nil {
   193  		t.Fatalf("err: %v", err)
   194  	}
   195  	if resp2.Index == 0 {
   196  		t.Fatalf("bad index: %d", resp2.Index)
   197  	}
   198  
   199  	// Check for an eval caused by the system job.
   200  	if len(resp2.EvalIDs) != 1 {
   201  		t.Fatalf("expected one eval; got %#v", resp2.EvalIDs)
   202  	}
   203  
   204  	evalID := resp2.EvalIDs[0]
   205  	eval, err := state.EvalByID(evalID)
   206  	if err != nil {
   207  		t.Fatalf("could not get eval %v", evalID)
   208  	}
   209  
   210  	if eval.Type != "system" {
   211  		t.Fatalf("unexpected eval type; got %v; want %q", eval.Type, "system")
   212  	}
   213  
   214  	// Check for heartbeat interval
   215  	ttl = resp2.HeartbeatTTL
   216  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   217  		t.Fatalf("bad: %#v", ttl)
   218  	}
   219  
   220  	// Check for the node in the FSM
   221  	out, err := state.NodeByID(node.ID)
   222  	if err != nil {
   223  		t.Fatalf("err: %v", err)
   224  	}
   225  	if out == nil {
   226  		t.Fatalf("expected node")
   227  	}
   228  	if out.ModifyIndex != resp2.Index {
   229  		t.Fatalf("index mis-match")
   230  	}
   231  }
   232  
   233  func TestClientEndpoint_UpdateStatus_HeartbeatOnly(t *testing.T) {
   234  	s1 := testServer(t, nil)
   235  	defer s1.Shutdown()
   236  	codec := rpcClient(t, s1)
   237  	testutil.WaitForLeader(t, s1.RPC)
   238  
   239  	// Create the register request
   240  	node := mock.Node()
   241  	reg := &structs.NodeRegisterRequest{
   242  		Node:         node,
   243  		WriteRequest: structs.WriteRequest{Region: "global"},
   244  	}
   245  
   246  	// Fetch the response
   247  	var resp structs.NodeUpdateResponse
   248  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   249  		t.Fatalf("err: %v", err)
   250  	}
   251  
   252  	// Check for heartbeat interval
   253  	ttl := resp.HeartbeatTTL
   254  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   255  		t.Fatalf("bad: %#v", ttl)
   256  	}
   257  
   258  	// Update the status, static state
   259  	dereg := &structs.NodeUpdateStatusRequest{
   260  		NodeID:       node.ID,
   261  		Status:       node.Status,
   262  		WriteRequest: structs.WriteRequest{Region: "global"},
   263  	}
   264  	var resp2 structs.NodeUpdateResponse
   265  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil {
   266  		t.Fatalf("err: %v", err)
   267  	}
   268  	if resp2.Index != 0 {
   269  		t.Fatalf("bad index: %d", resp2.Index)
   270  	}
   271  
   272  	// Check for heartbeat interval
   273  	ttl = resp2.HeartbeatTTL
   274  	if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL {
   275  		t.Fatalf("bad: %#v", ttl)
   276  	}
   277  }
   278  
   279  func TestClientEndpoint_UpdateDrain(t *testing.T) {
   280  	s1 := testServer(t, nil)
   281  	defer s1.Shutdown()
   282  	codec := rpcClient(t, s1)
   283  	testutil.WaitForLeader(t, s1.RPC)
   284  
   285  	// Create the register request
   286  	node := mock.Node()
   287  	reg := &structs.NodeRegisterRequest{
   288  		Node:         node,
   289  		WriteRequest: structs.WriteRequest{Region: "global"},
   290  	}
   291  
   292  	// Fetch the response
   293  	var resp structs.NodeUpdateResponse
   294  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   295  		t.Fatalf("err: %v", err)
   296  	}
   297  
   298  	// Update the status
   299  	dereg := &structs.NodeUpdateDrainRequest{
   300  		NodeID:       node.ID,
   301  		Drain:        true,
   302  		WriteRequest: structs.WriteRequest{Region: "global"},
   303  	}
   304  	var resp2 structs.NodeDrainUpdateResponse
   305  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateDrain", dereg, &resp2); err != nil {
   306  		t.Fatalf("err: %v", err)
   307  	}
   308  	if resp2.Index == 0 {
   309  		t.Fatalf("bad index: %d", resp2.Index)
   310  	}
   311  
   312  	// Check for the node in the FSM
   313  	state := s1.fsm.State()
   314  	out, err := state.NodeByID(node.ID)
   315  	if err != nil {
   316  		t.Fatalf("err: %v", err)
   317  	}
   318  	if !out.Drain {
   319  		t.Fatalf("bad: %#v", out)
   320  	}
   321  }
   322  
   323  func TestClientEndpoint_GetNode(t *testing.T) {
   324  	s1 := testServer(t, nil)
   325  	defer s1.Shutdown()
   326  	codec := rpcClient(t, s1)
   327  	testutil.WaitForLeader(t, s1.RPC)
   328  
   329  	// Create the register request
   330  	node := mock.Node()
   331  	reg := &structs.NodeRegisterRequest{
   332  		Node:         node,
   333  		WriteRequest: structs.WriteRequest{Region: "global"},
   334  	}
   335  
   336  	// Fetch the response
   337  	var resp structs.GenericResponse
   338  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   339  		t.Fatalf("err: %v", err)
   340  	}
   341  	node.CreateIndex = resp.Index
   342  	node.ModifyIndex = resp.Index
   343  
   344  	// Lookup the node
   345  	get := &structs.NodeSpecificRequest{
   346  		NodeID:       node.ID,
   347  		QueryOptions: structs.QueryOptions{Region: "global"},
   348  	}
   349  	var resp2 structs.SingleNodeResponse
   350  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil {
   351  		t.Fatalf("err: %v", err)
   352  	}
   353  	if resp2.Index != resp.Index {
   354  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   355  	}
   356  
   357  	if !reflect.DeepEqual(node, resp2.Node) {
   358  		t.Fatalf("bad: %#v %#v", node, resp2.Node)
   359  	}
   360  
   361  	// Lookup non-existing node
   362  	get.NodeID = "foobarbaz"
   363  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil {
   364  		t.Fatalf("err: %v", err)
   365  	}
   366  	if resp2.Index != resp.Index {
   367  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   368  	}
   369  	if resp2.Node != nil {
   370  		t.Fatalf("unexpected node")
   371  	}
   372  }
   373  
   374  func TestClientEndpoint_GetAllocs(t *testing.T) {
   375  	s1 := testServer(t, nil)
   376  	defer s1.Shutdown()
   377  	codec := rpcClient(t, s1)
   378  	testutil.WaitForLeader(t, s1.RPC)
   379  
   380  	// Create the register request
   381  	node := mock.Node()
   382  	reg := &structs.NodeRegisterRequest{
   383  		Node:         node,
   384  		WriteRequest: structs.WriteRequest{Region: "global"},
   385  	}
   386  
   387  	// Fetch the response
   388  	var resp structs.GenericResponse
   389  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   390  		t.Fatalf("err: %v", err)
   391  	}
   392  	node.CreateIndex = resp.Index
   393  	node.ModifyIndex = resp.Index
   394  
   395  	// Inject fake evaluations
   396  	alloc := mock.Alloc()
   397  	alloc.NodeID = node.ID
   398  	state := s1.fsm.State()
   399  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   400  	if err != nil {
   401  		t.Fatalf("err: %v", err)
   402  	}
   403  
   404  	// Lookup the allocs
   405  	get := &structs.NodeSpecificRequest{
   406  		NodeID:       node.ID,
   407  		QueryOptions: structs.QueryOptions{Region: "global"},
   408  	}
   409  	var resp2 structs.NodeAllocsResponse
   410  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil {
   411  		t.Fatalf("err: %v", err)
   412  	}
   413  	if resp2.Index != 100 {
   414  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   415  	}
   416  
   417  	if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID {
   418  		t.Fatalf("bad: %#v", resp2.Allocs)
   419  	}
   420  
   421  	// Lookup non-existing node
   422  	get.NodeID = "foobarbaz"
   423  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil {
   424  		t.Fatalf("err: %v", err)
   425  	}
   426  	if resp2.Index != 100 {
   427  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   428  	}
   429  	if len(resp2.Allocs) != 0 {
   430  		t.Fatalf("unexpected node")
   431  	}
   432  }
   433  
   434  func TestClientEndpoint_GetAllocs_Blocking(t *testing.T) {
   435  	s1 := testServer(t, nil)
   436  	defer s1.Shutdown()
   437  	codec := rpcClient(t, s1)
   438  	testutil.WaitForLeader(t, s1.RPC)
   439  
   440  	// Create the register request
   441  	node := mock.Node()
   442  	reg := &structs.NodeRegisterRequest{
   443  		Node:         node,
   444  		WriteRequest: structs.WriteRequest{Region: "global"},
   445  	}
   446  
   447  	// Fetch the response
   448  	var resp structs.GenericResponse
   449  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   450  		t.Fatalf("err: %v", err)
   451  	}
   452  	node.CreateIndex = resp.Index
   453  	node.ModifyIndex = resp.Index
   454  
   455  	// Inject fake evaluations async
   456  	alloc := mock.Alloc()
   457  	alloc.NodeID = node.ID
   458  	state := s1.fsm.State()
   459  	start := time.Now()
   460  	go func() {
   461  		time.Sleep(100 * time.Millisecond)
   462  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   463  		if err != nil {
   464  			t.Fatalf("err: %v", err)
   465  		}
   466  	}()
   467  
   468  	// Lookup the allocs in a blocking query
   469  	get := &structs.NodeSpecificRequest{
   470  		NodeID: node.ID,
   471  		QueryOptions: structs.QueryOptions{
   472  			Region:        "global",
   473  			MinQueryIndex: 50,
   474  			MaxQueryTime:  time.Second,
   475  		},
   476  	}
   477  	var resp2 structs.NodeAllocsResponse
   478  	if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil {
   479  		t.Fatalf("err: %v", err)
   480  	}
   481  
   482  	// Should block at least 100ms
   483  	if time.Since(start) < 100*time.Millisecond {
   484  		t.Fatalf("too fast")
   485  	}
   486  
   487  	if resp2.Index != 100 {
   488  		t.Fatalf("Bad index: %d %d", resp2.Index, 100)
   489  	}
   490  
   491  	if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID {
   492  		t.Fatalf("bad: %#v", resp2.Allocs)
   493  	}
   494  }
   495  
   496  func TestClientEndpoint_UpdateAlloc(t *testing.T) {
   497  	s1 := testServer(t, nil)
   498  	defer s1.Shutdown()
   499  	codec := rpcClient(t, s1)
   500  	testutil.WaitForLeader(t, s1.RPC)
   501  
   502  	// Create the register request
   503  	node := mock.Node()
   504  	reg := &structs.NodeRegisterRequest{
   505  		Node:         node,
   506  		WriteRequest: structs.WriteRequest{Region: "global"},
   507  	}
   508  
   509  	// Fetch the response
   510  	var resp structs.GenericResponse
   511  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   512  		t.Fatalf("err: %v", err)
   513  	}
   514  
   515  	// Inject fake evaluations
   516  	alloc := mock.Alloc()
   517  	alloc.NodeID = node.ID
   518  	state := s1.fsm.State()
   519  	err := state.UpsertAllocs(100, []*structs.Allocation{alloc})
   520  	if err != nil {
   521  		t.Fatalf("err: %v", err)
   522  	}
   523  
   524  	// Attempt update
   525  	clientAlloc := new(structs.Allocation)
   526  	*clientAlloc = *alloc
   527  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   528  
   529  	// Update the alloc
   530  	update := &structs.AllocUpdateRequest{
   531  		Alloc:        []*structs.Allocation{clientAlloc},
   532  		WriteRequest: structs.WriteRequest{Region: "global"},
   533  	}
   534  	var resp2 structs.NodeAllocsResponse
   535  	if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateAlloc", update, &resp2); err != nil {
   536  		t.Fatalf("err: %v", err)
   537  	}
   538  	if resp2.Index == 0 {
   539  		t.Fatalf("Bad index: %d", resp2.Index)
   540  	}
   541  
   542  	// Lookup the alloc
   543  	out, err := state.AllocByID(alloc.ID)
   544  	if err != nil {
   545  		t.Fatalf("err: %v", err)
   546  	}
   547  	if out.ClientStatus != structs.AllocClientStatusFailed {
   548  		t.Fatalf("Bad: %#v", out)
   549  	}
   550  }
   551  
   552  func TestClientEndpoint_CreateNodeEvals(t *testing.T) {
   553  	s1 := testServer(t, nil)
   554  	defer s1.Shutdown()
   555  	testutil.WaitForLeader(t, s1.RPC)
   556  
   557  	// Inject fake evaluations
   558  	alloc := mock.Alloc()
   559  	state := s1.fsm.State()
   560  	if err := state.UpsertAllocs(1, []*structs.Allocation{alloc}); err != nil {
   561  		t.Fatalf("err: %v", err)
   562  	}
   563  
   564  	// Inject a fake system job.
   565  	job := mock.SystemJob()
   566  	if err := state.UpsertJob(1, job); err != nil {
   567  		t.Fatalf("err: %v", err)
   568  	}
   569  
   570  	// Create some evaluations
   571  	ids, index, err := s1.endpoints.Node.createNodeEvals(alloc.NodeID, 1)
   572  	if err != nil {
   573  		t.Fatalf("err: %v", err)
   574  	}
   575  	if index == 0 {
   576  		t.Fatalf("bad: %d", index)
   577  	}
   578  	if len(ids) != 2 {
   579  		t.Fatalf("bad: %s", ids)
   580  	}
   581  
   582  	// Lookup the evaluations
   583  	evalByType := make(map[string]*structs.Evaluation, 2)
   584  	for _, id := range ids {
   585  		eval, err := state.EvalByID(id)
   586  		if err != nil {
   587  			t.Fatalf("err: %v", err)
   588  		}
   589  		if eval == nil {
   590  			t.Fatalf("expected eval")
   591  		}
   592  
   593  		if old, ok := evalByType[eval.Type]; ok {
   594  			t.Fatalf("multiple evals of the same type: %v and %v", old, eval)
   595  		}
   596  
   597  		evalByType[eval.Type] = eval
   598  	}
   599  
   600  	if len(evalByType) != 2 {
   601  		t.Fatalf("Expected a service and system job; got %#v", evalByType)
   602  	}
   603  
   604  	// Ensure the evals are correct.
   605  	for schedType, eval := range evalByType {
   606  		expPriority := alloc.Job.Priority
   607  		expJobID := alloc.JobID
   608  		if schedType == "system" {
   609  			expPriority = job.Priority
   610  			expJobID = job.ID
   611  		}
   612  
   613  		if eval.CreateIndex != index {
   614  			t.Fatalf("CreateIndex mis-match on type %v: %#v", schedType, eval)
   615  		}
   616  		if eval.TriggeredBy != structs.EvalTriggerNodeUpdate {
   617  			t.Fatalf("TriggeredBy incorrect on type %v: %#v", schedType, eval)
   618  		}
   619  		if eval.NodeID != alloc.NodeID {
   620  			t.Fatalf("NodeID incorrect on type %v: %#v", schedType, eval)
   621  		}
   622  		if eval.NodeModifyIndex != 1 {
   623  			t.Fatalf("NodeModifyIndex incorrect on type %v: %#v", schedType, eval)
   624  		}
   625  		if eval.Status != structs.EvalStatusPending {
   626  			t.Fatalf("Status incorrect on type %v: %#v", schedType, eval)
   627  		}
   628  		if eval.Priority != expPriority {
   629  			t.Fatalf("Priority incorrect on type %v: %#v", schedType, eval)
   630  		}
   631  		if eval.JobID != expJobID {
   632  			t.Fatalf("JobID incorrect on type %v: %#v", schedType, eval)
   633  		}
   634  	}
   635  }
   636  
   637  func TestClientEndpoint_Evaluate(t *testing.T) {
   638  	s1 := testServer(t, func(c *Config) {
   639  		c.NumSchedulers = 0 // Prevent automatic dequeue
   640  	})
   641  	defer s1.Shutdown()
   642  	codec := rpcClient(t, s1)
   643  	testutil.WaitForLeader(t, s1.RPC)
   644  
   645  	// Inject fake evaluations
   646  	alloc := mock.Alloc()
   647  	node := mock.Node()
   648  	node.ID = alloc.NodeID
   649  	state := s1.fsm.State()
   650  	err := state.UpsertNode(1, node)
   651  	if err != nil {
   652  		t.Fatalf("err: %v", err)
   653  	}
   654  	err = state.UpsertAllocs(2, []*structs.Allocation{alloc})
   655  	if err != nil {
   656  		t.Fatalf("err: %v", err)
   657  	}
   658  
   659  	// Re-evaluate
   660  	req := &structs.NodeEvaluateRequest{
   661  		NodeID:       alloc.NodeID,
   662  		WriteRequest: structs.WriteRequest{Region: "global"},
   663  	}
   664  
   665  	// Fetch the response
   666  	var resp structs.NodeUpdateResponse
   667  	if err := msgpackrpc.CallWithCodec(codec, "Node.Evaluate", req, &resp); err != nil {
   668  		t.Fatalf("err: %v", err)
   669  	}
   670  	if resp.Index == 0 {
   671  		t.Fatalf("bad index: %d", resp.Index)
   672  	}
   673  
   674  	// Create some evaluations
   675  	ids := resp.EvalIDs
   676  	if len(ids) != 1 {
   677  		t.Fatalf("bad: %s", ids)
   678  	}
   679  
   680  	// Lookup the evaluation
   681  	eval, err := state.EvalByID(ids[0])
   682  	if err != nil {
   683  		t.Fatalf("err: %v", err)
   684  	}
   685  	if eval == nil {
   686  		t.Fatalf("expected eval")
   687  	}
   688  	if eval.CreateIndex != resp.Index {
   689  		t.Fatalf("index mis-match")
   690  	}
   691  
   692  	if eval.Priority != alloc.Job.Priority {
   693  		t.Fatalf("bad: %#v", eval)
   694  	}
   695  	if eval.Type != alloc.Job.Type {
   696  		t.Fatalf("bad: %#v", eval)
   697  	}
   698  	if eval.TriggeredBy != structs.EvalTriggerNodeUpdate {
   699  		t.Fatalf("bad: %#v", eval)
   700  	}
   701  	if eval.JobID != alloc.JobID {
   702  		t.Fatalf("bad: %#v", eval)
   703  	}
   704  	if eval.NodeID != alloc.NodeID {
   705  		t.Fatalf("bad: %#v", eval)
   706  	}
   707  	if eval.NodeModifyIndex != 1 {
   708  		t.Fatalf("bad: %#v", eval)
   709  	}
   710  	if eval.Status != structs.EvalStatusPending {
   711  		t.Fatalf("bad: %#v", eval)
   712  	}
   713  }
   714  
   715  func TestClientEndpoint_ListNodes(t *testing.T) {
   716  	s1 := testServer(t, nil)
   717  	defer s1.Shutdown()
   718  	codec := rpcClient(t, s1)
   719  	testutil.WaitForLeader(t, s1.RPC)
   720  
   721  	// Create the register request
   722  	node := mock.Node()
   723  	reg := &structs.NodeRegisterRequest{
   724  		Node:         node,
   725  		WriteRequest: structs.WriteRequest{Region: "global"},
   726  	}
   727  
   728  	// Fetch the response
   729  	var resp structs.GenericResponse
   730  	if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil {
   731  		t.Fatalf("err: %v", err)
   732  	}
   733  	node.CreateIndex = resp.Index
   734  	node.ModifyIndex = resp.Index
   735  
   736  	// Lookup the node
   737  	get := &structs.NodeListRequest{
   738  		QueryOptions: structs.QueryOptions{Region: "global"},
   739  	}
   740  	var resp2 structs.NodeListResponse
   741  	if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp2); err != nil {
   742  		t.Fatalf("err: %v", err)
   743  	}
   744  	if resp2.Index != resp.Index {
   745  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   746  	}
   747  
   748  	if len(resp2.Nodes) != 1 {
   749  		t.Fatalf("bad: %#v", resp2.Nodes)
   750  	}
   751  	if resp2.Nodes[0].ID != node.ID {
   752  		t.Fatalf("bad: %#v", resp2.Nodes[0])
   753  	}
   754  }