github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/nomad/eval_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 TestEvalEndpoint_GetEval(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  	eval1 := mock.Eval()
    22  	s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1})
    23  
    24  	// Lookup the eval
    25  	get := &structs.EvalSpecificRequest{
    26  		EvalID:       eval1.ID,
    27  		QueryOptions: structs.QueryOptions{Region: "global"},
    28  	}
    29  	var resp structs.SingleEvalResponse
    30  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil {
    31  		t.Fatalf("err: %v", err)
    32  	}
    33  	if resp.Index != 1000 {
    34  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
    35  	}
    36  
    37  	if !reflect.DeepEqual(eval1, resp.Eval) {
    38  		t.Fatalf("bad: %#v %#v", eval1, resp.Eval)
    39  	}
    40  
    41  	// Lookup non-existing node
    42  	get.EvalID = structs.GenerateUUID()
    43  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp); err != nil {
    44  		t.Fatalf("err: %v", err)
    45  	}
    46  	if resp.Index != 1000 {
    47  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
    48  	}
    49  	if resp.Eval != nil {
    50  		t.Fatalf("unexpected eval")
    51  	}
    52  }
    53  
    54  func TestEvalEndpoint_GetEval_Blocking(t *testing.T) {
    55  	s1 := testServer(t, nil)
    56  	defer s1.Shutdown()
    57  	state := s1.fsm.State()
    58  	codec := rpcClient(t, s1)
    59  	testutil.WaitForLeader(t, s1.RPC)
    60  
    61  	// Create the evals
    62  	eval1 := mock.Eval()
    63  	eval2 := mock.Eval()
    64  
    65  	// First create an unrelated eval
    66  	time.AfterFunc(100*time.Millisecond, func() {
    67  		err := state.UpsertEvals(100, []*structs.Evaluation{eval1})
    68  		if err != nil {
    69  			t.Fatalf("err: %v", err)
    70  		}
    71  	})
    72  
    73  	// Upsert the eval we are watching later
    74  	time.AfterFunc(200*time.Millisecond, func() {
    75  		err := state.UpsertEvals(200, []*structs.Evaluation{eval2})
    76  		if err != nil {
    77  			t.Fatalf("err: %v", err)
    78  		}
    79  	})
    80  
    81  	// Lookup the eval
    82  	req := &structs.EvalSpecificRequest{
    83  		EvalID: eval2.ID,
    84  		QueryOptions: structs.QueryOptions{
    85  			Region:        "global",
    86  			MinQueryIndex: 50,
    87  		},
    88  	}
    89  	var resp structs.SingleEvalResponse
    90  	start := time.Now()
    91  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp); err != nil {
    92  		t.Fatalf("err: %v", err)
    93  	}
    94  
    95  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
    96  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
    97  	}
    98  	if resp.Index != 200 {
    99  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   100  	}
   101  	if resp.Eval == nil || resp.Eval.ID != eval2.ID {
   102  		t.Fatalf("bad: %#v", resp.Eval)
   103  	}
   104  
   105  	// Eval delete triggers watches
   106  	time.AfterFunc(100*time.Millisecond, func() {
   107  		err := state.DeleteEval(300, []string{eval2.ID}, []string{})
   108  		if err != nil {
   109  			t.Fatalf("err: %v", err)
   110  		}
   111  	})
   112  
   113  	req.QueryOptions.MinQueryIndex = 250
   114  	var resp2 structs.SingleEvalResponse
   115  	start = time.Now()
   116  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp2); err != nil {
   117  		t.Fatalf("err: %v", err)
   118  	}
   119  
   120  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   121  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   122  	}
   123  	if resp2.Index != 300 {
   124  		t.Fatalf("Bad index: %d %d", resp2.Index, 300)
   125  	}
   126  	if resp2.Eval != nil {
   127  		t.Fatalf("bad: %#v", resp2.Eval)
   128  	}
   129  }
   130  
   131  func TestEvalEndpoint_Dequeue(t *testing.T) {
   132  	s1 := testServer(t, func(c *Config) {
   133  		c.NumSchedulers = 0 // Prevent automatic dequeue
   134  	})
   135  	defer s1.Shutdown()
   136  	codec := rpcClient(t, s1)
   137  	testutil.WaitForLeader(t, s1.RPC)
   138  
   139  	// Create the register request
   140  	eval1 := mock.Eval()
   141  	testutil.WaitForResult(func() (bool, error) {
   142  		err := s1.evalBroker.Enqueue(eval1)
   143  		return err == nil, err
   144  	}, func(err error) {
   145  		t.Fatalf("err: %v", err)
   146  	})
   147  
   148  	// Dequeue the eval
   149  	get := &structs.EvalDequeueRequest{
   150  		Schedulers:   defaultSched,
   151  		WriteRequest: structs.WriteRequest{Region: "global"},
   152  	}
   153  	var resp structs.EvalDequeueResponse
   154  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil {
   155  		t.Fatalf("err: %v", err)
   156  	}
   157  
   158  	if !reflect.DeepEqual(eval1, resp.Eval) {
   159  		t.Fatalf("bad: %v %v", eval1, resp.Eval)
   160  	}
   161  
   162  	// Ensure outstanding
   163  	token, ok := s1.evalBroker.Outstanding(eval1.ID)
   164  	if !ok {
   165  		t.Fatalf("should be outstanding")
   166  	}
   167  	if token != resp.Token {
   168  		t.Fatalf("bad token: %#v %#v", token, resp.Token)
   169  	}
   170  }
   171  
   172  func TestEvalEndpoint_Ack(t *testing.T) {
   173  	s1 := testServer(t, nil)
   174  	defer s1.Shutdown()
   175  	codec := rpcClient(t, s1)
   176  
   177  	testutil.WaitForResult(func() (bool, error) {
   178  		return s1.evalBroker.Enabled(), nil
   179  	}, func(err error) {
   180  		t.Fatalf("should enable eval broker")
   181  	})
   182  
   183  	// Create the register request
   184  	eval1 := mock.Eval()
   185  	s1.evalBroker.Enqueue(eval1)
   186  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   187  	if err != nil {
   188  		t.Fatalf("err: %v", err)
   189  	}
   190  	if out == nil {
   191  		t.Fatalf("missing eval")
   192  	}
   193  
   194  	// Ack the eval
   195  	get := &structs.EvalAckRequest{
   196  		EvalID:       out.ID,
   197  		Token:        token,
   198  		WriteRequest: structs.WriteRequest{Region: "global"},
   199  	}
   200  	var resp structs.GenericResponse
   201  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Ack", get, &resp); err != nil {
   202  		t.Fatalf("err: %v", err)
   203  	}
   204  
   205  	// Ensure outstanding
   206  	if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok {
   207  		t.Fatalf("should not be outstanding")
   208  	}
   209  }
   210  
   211  func TestEvalEndpoint_Nack(t *testing.T) {
   212  	s1 := testServer(t, func(c *Config) {
   213  		// Disable all of the schedulers so we can manually dequeue
   214  		// evals and check the queue status
   215  		c.NumSchedulers = 0
   216  	})
   217  	defer s1.Shutdown()
   218  	codec := rpcClient(t, s1)
   219  
   220  	testutil.WaitForResult(func() (bool, error) {
   221  		return s1.evalBroker.Enabled(), nil
   222  	}, func(err error) {
   223  		t.Fatalf("should enable eval broker")
   224  	})
   225  
   226  	// Create the register request
   227  	eval1 := mock.Eval()
   228  	s1.evalBroker.Enqueue(eval1)
   229  	out, token, _ := s1.evalBroker.Dequeue(defaultSched, time.Second)
   230  	if out == nil {
   231  		t.Fatalf("missing eval")
   232  	}
   233  
   234  	// Nack the eval
   235  	get := &structs.EvalAckRequest{
   236  		EvalID:       out.ID,
   237  		Token:        token,
   238  		WriteRequest: structs.WriteRequest{Region: "global"},
   239  	}
   240  	var resp structs.GenericResponse
   241  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Nack", get, &resp); err != nil {
   242  		t.Fatalf("err: %v", err)
   243  	}
   244  
   245  	// Ensure outstanding
   246  	if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok {
   247  		t.Fatalf("should not be outstanding")
   248  	}
   249  
   250  	// Should get it back
   251  	out2, _, _ := s1.evalBroker.Dequeue(defaultSched, time.Second)
   252  	if out2 != out {
   253  		t.Fatalf("nack failed")
   254  	}
   255  }
   256  
   257  func TestEvalEndpoint_Update(t *testing.T) {
   258  	s1 := testServer(t, nil)
   259  	defer s1.Shutdown()
   260  	codec := rpcClient(t, s1)
   261  
   262  	testutil.WaitForResult(func() (bool, error) {
   263  		return s1.evalBroker.Enabled(), nil
   264  	}, func(err error) {
   265  		t.Fatalf("should enable eval broker")
   266  	})
   267  
   268  	// Create the register request
   269  	eval1 := mock.Eval()
   270  	s1.evalBroker.Enqueue(eval1)
   271  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   272  	if err != nil {
   273  		t.Fatalf("err: %v", err)
   274  	}
   275  	if out == nil {
   276  		t.Fatalf("missing eval")
   277  	}
   278  
   279  	// Update the eval
   280  	eval2 := eval1.Copy()
   281  	eval2.Status = structs.EvalStatusComplete
   282  
   283  	get := &structs.EvalUpdateRequest{
   284  		Evals:        []*structs.Evaluation{eval2},
   285  		EvalToken:    token,
   286  		WriteRequest: structs.WriteRequest{Region: "global"},
   287  	}
   288  	var resp structs.GenericResponse
   289  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Update", get, &resp); err != nil {
   290  		t.Fatalf("err: %v", err)
   291  	}
   292  
   293  	// Ensure updated
   294  	outE, err := s1.fsm.State().EvalByID(eval2.ID)
   295  	if err != nil {
   296  		t.Fatalf("err: %v", err)
   297  	}
   298  	if outE.Status != structs.EvalStatusComplete {
   299  		t.Fatalf("Bad: %#v", out)
   300  	}
   301  }
   302  
   303  func TestEvalEndpoint_Create(t *testing.T) {
   304  	s1 := testServer(t, func(c *Config) {
   305  		c.NumSchedulers = 0 // Prevent automatic dequeue
   306  	})
   307  	defer s1.Shutdown()
   308  	codec := rpcClient(t, s1)
   309  
   310  	testutil.WaitForResult(func() (bool, error) {
   311  		return s1.evalBroker.Enabled(), nil
   312  	}, func(err error) {
   313  		t.Fatalf("should enable eval broker")
   314  	})
   315  
   316  	// Create the register request
   317  	prev := mock.Eval()
   318  	s1.evalBroker.Enqueue(prev)
   319  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   320  	if err != nil {
   321  		t.Fatalf("err: %v", err)
   322  	}
   323  	if out == nil {
   324  		t.Fatalf("missing eval")
   325  	}
   326  
   327  	// Create the register request
   328  	eval1 := mock.Eval()
   329  	eval1.PreviousEval = prev.ID
   330  	get := &structs.EvalUpdateRequest{
   331  		Evals:        []*structs.Evaluation{eval1},
   332  		EvalToken:    token,
   333  		WriteRequest: structs.WriteRequest{Region: "global"},
   334  	}
   335  	var resp structs.GenericResponse
   336  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Create", get, &resp); err != nil {
   337  		t.Fatalf("err: %v", err)
   338  	}
   339  
   340  	// Ensure created
   341  	outE, err := s1.fsm.State().EvalByID(eval1.ID)
   342  	if err != nil {
   343  		t.Fatalf("err: %v", err)
   344  	}
   345  
   346  	eval1.CreateIndex = resp.Index
   347  	eval1.ModifyIndex = resp.Index
   348  	if !reflect.DeepEqual(eval1, outE) {
   349  		t.Fatalf("Bad: %#v %#v", outE, eval1)
   350  	}
   351  }
   352  
   353  func TestEvalEndpoint_Reap(t *testing.T) {
   354  	s1 := testServer(t, nil)
   355  	defer s1.Shutdown()
   356  	codec := rpcClient(t, s1)
   357  	testutil.WaitForLeader(t, s1.RPC)
   358  
   359  	// Create the register request
   360  	eval1 := mock.Eval()
   361  	s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1})
   362  
   363  	// Reap the eval
   364  	get := &structs.EvalDeleteRequest{
   365  		Evals:        []string{eval1.ID},
   366  		WriteRequest: structs.WriteRequest{Region: "global"},
   367  	}
   368  	var resp structs.GenericResponse
   369  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Reap", get, &resp); err != nil {
   370  		t.Fatalf("err: %v", err)
   371  	}
   372  	if resp.Index == 0 {
   373  		t.Fatalf("Bad index: %d", resp.Index)
   374  	}
   375  
   376  	// Ensure deleted
   377  	outE, err := s1.fsm.State().EvalByID(eval1.ID)
   378  	if err != nil {
   379  		t.Fatalf("err: %v", err)
   380  	}
   381  	if outE != nil {
   382  		t.Fatalf("Bad: %#v", outE)
   383  	}
   384  }
   385  
   386  func TestEvalEndpoint_List(t *testing.T) {
   387  	s1 := testServer(t, nil)
   388  	defer s1.Shutdown()
   389  	codec := rpcClient(t, s1)
   390  	testutil.WaitForLeader(t, s1.RPC)
   391  
   392  	// Create the register request
   393  	eval1 := mock.Eval()
   394  	eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
   395  	eval2 := mock.Eval()
   396  	eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
   397  	s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1, eval2})
   398  
   399  	// Lookup the eval
   400  	get := &structs.EvalListRequest{
   401  		QueryOptions: structs.QueryOptions{Region: "global"},
   402  	}
   403  	var resp structs.EvalListResponse
   404  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil {
   405  		t.Fatalf("err: %v", err)
   406  	}
   407  	if resp.Index != 1000 {
   408  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
   409  	}
   410  
   411  	if len(resp.Evaluations) != 2 {
   412  		t.Fatalf("bad: %#v", resp.Evaluations)
   413  	}
   414  
   415  	// Lookup the eval by prefix
   416  	get = &structs.EvalListRequest{
   417  		QueryOptions: structs.QueryOptions{Region: "global", Prefix: "aaaabb"},
   418  	}
   419  	var resp2 structs.EvalListResponse
   420  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil {
   421  		t.Fatalf("err: %v", err)
   422  	}
   423  	if resp2.Index != 1000 {
   424  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
   425  	}
   426  
   427  	if len(resp2.Evaluations) != 1 {
   428  		t.Fatalf("bad: %#v", resp2.Evaluations)
   429  	}
   430  
   431  }
   432  
   433  func TestEvalEndpoint_List_Blocking(t *testing.T) {
   434  	s1 := testServer(t, nil)
   435  	defer s1.Shutdown()
   436  	state := s1.fsm.State()
   437  	codec := rpcClient(t, s1)
   438  	testutil.WaitForLeader(t, s1.RPC)
   439  
   440  	// Create the ieval
   441  	eval := mock.Eval()
   442  
   443  	// Upsert eval triggers watches
   444  	time.AfterFunc(100*time.Millisecond, func() {
   445  		if err := state.UpsertEvals(2, []*structs.Evaluation{eval}); err != nil {
   446  			t.Fatalf("err: %v", err)
   447  		}
   448  	})
   449  
   450  	req := &structs.EvalListRequest{
   451  		QueryOptions: structs.QueryOptions{
   452  			Region:        "global",
   453  			MinQueryIndex: 1,
   454  		},
   455  	}
   456  	start := time.Now()
   457  	var resp structs.EvalListResponse
   458  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp); err != nil {
   459  		t.Fatalf("err: %v", err)
   460  	}
   461  
   462  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   463  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   464  	}
   465  	if resp.Index != 2 {
   466  		t.Fatalf("Bad index: %d %d", resp.Index, 2)
   467  	}
   468  	if len(resp.Evaluations) != 1 || resp.Evaluations[0].ID != eval.ID {
   469  		t.Fatalf("bad: %#v", resp.Evaluations)
   470  	}
   471  
   472  	// Eval deletion triggers watches
   473  	time.AfterFunc(100*time.Millisecond, func() {
   474  		if err := state.DeleteEval(3, []string{eval.ID}, nil); err != nil {
   475  			t.Fatalf("err: %v", err)
   476  		}
   477  	})
   478  
   479  	req.MinQueryIndex = 2
   480  	start = time.Now()
   481  	var resp2 structs.EvalListResponse
   482  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp2); err != nil {
   483  		t.Fatalf("err: %v", err)
   484  	}
   485  
   486  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   487  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   488  	}
   489  	if resp2.Index != 3 {
   490  		t.Fatalf("Bad index: %d %d", resp2.Index, 3)
   491  	}
   492  	if len(resp2.Evaluations) != 0 {
   493  		t.Fatalf("bad: %#v", resp2.Evaluations)
   494  	}
   495  }
   496  
   497  func TestEvalEndpoint_Allocations(t *testing.T) {
   498  	s1 := testServer(t, nil)
   499  	defer s1.Shutdown()
   500  	codec := rpcClient(t, s1)
   501  	testutil.WaitForLeader(t, s1.RPC)
   502  
   503  	// Create the register request
   504  	alloc1 := mock.Alloc()
   505  	alloc2 := mock.Alloc()
   506  	alloc2.EvalID = alloc1.EvalID
   507  	state := s1.fsm.State()
   508  	err := state.UpsertAllocs(1000,
   509  		[]*structs.Allocation{alloc1, alloc2})
   510  	if err != nil {
   511  		t.Fatalf("err: %v", err)
   512  	}
   513  
   514  	// Lookup the eval
   515  	get := &structs.EvalSpecificRequest{
   516  		EvalID:       alloc1.EvalID,
   517  		QueryOptions: structs.QueryOptions{Region: "global"},
   518  	}
   519  	var resp structs.EvalAllocationsResponse
   520  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil {
   521  		t.Fatalf("err: %v", err)
   522  	}
   523  	if resp.Index != 1000 {
   524  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
   525  	}
   526  
   527  	if len(resp.Allocations) != 2 {
   528  		t.Fatalf("bad: %#v", resp.Allocations)
   529  	}
   530  }
   531  
   532  func TestEvalEndpoint_Allocations_Blocking(t *testing.T) {
   533  	s1 := testServer(t, nil)
   534  	defer s1.Shutdown()
   535  	state := s1.fsm.State()
   536  	codec := rpcClient(t, s1)
   537  	testutil.WaitForLeader(t, s1.RPC)
   538  
   539  	// Create the allocs
   540  	alloc1 := mock.Alloc()
   541  	alloc2 := mock.Alloc()
   542  
   543  	// Upsert an unrelated alloc first
   544  	time.AfterFunc(100*time.Millisecond, func() {
   545  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc1})
   546  		if err != nil {
   547  			t.Fatalf("err: %v", err)
   548  		}
   549  	})
   550  
   551  	// Upsert an alloc which will trigger the watch later
   552  	time.AfterFunc(200*time.Millisecond, func() {
   553  		err := state.UpsertAllocs(200, []*structs.Allocation{alloc2})
   554  		if err != nil {
   555  			t.Fatalf("err: %v", err)
   556  		}
   557  	})
   558  
   559  	// Lookup the eval
   560  	get := &structs.EvalSpecificRequest{
   561  		EvalID: alloc2.EvalID,
   562  		QueryOptions: structs.QueryOptions{
   563  			Region:        "global",
   564  			MinQueryIndex: 50,
   565  		},
   566  	}
   567  	var resp structs.EvalAllocationsResponse
   568  	start := time.Now()
   569  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil {
   570  		t.Fatalf("err: %v", err)
   571  	}
   572  
   573  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
   574  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   575  	}
   576  	if resp.Index != 200 {
   577  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   578  	}
   579  	if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc2.ID {
   580  		t.Fatalf("bad: %#v", resp.Allocations)
   581  	}
   582  }