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

     1  package nomad
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/net-rpc-msgpackrpc"
     8  	"github.com/hashicorp/nomad/nomad/mock"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  	"github.com/hashicorp/nomad/testutil"
    11  )
    12  
    13  func TestJobEndpoint_Register(t *testing.T) {
    14  	s1 := testServer(t, func(c *Config) {
    15  		c.NumSchedulers = 0 // Prevent automatic dequeue
    16  	})
    17  	defer s1.Shutdown()
    18  	codec := rpcClient(t, s1)
    19  	testutil.WaitForLeader(t, s1.RPC)
    20  
    21  	// Create the register request
    22  	job := mock.Job()
    23  	req := &structs.JobRegisterRequest{
    24  		Job:          job,
    25  		WriteRequest: structs.WriteRequest{Region: "global"},
    26  	}
    27  
    28  	// Fetch the response
    29  	var resp structs.JobRegisterResponse
    30  	if err := msgpackrpc.CallWithCodec(codec, "Job.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.JobByID(job.ID)
    40  	if err != nil {
    41  		t.Fatalf("err: %v", err)
    42  	}
    43  	if out == nil {
    44  		t.Fatalf("expected job")
    45  	}
    46  	if out.CreateIndex != resp.JobModifyIndex {
    47  		t.Fatalf("index mis-match")
    48  	}
    49  
    50  	// Lookup the evaluation
    51  	eval, err := state.EvalByID(resp.EvalID)
    52  	if err != nil {
    53  		t.Fatalf("err: %v", err)
    54  	}
    55  	if eval == nil {
    56  		t.Fatalf("expected eval")
    57  	}
    58  	if eval.CreateIndex != resp.EvalCreateIndex {
    59  		t.Fatalf("index mis-match")
    60  	}
    61  
    62  	if eval.Priority != job.Priority {
    63  		t.Fatalf("bad: %#v", eval)
    64  	}
    65  	if eval.Type != job.Type {
    66  		t.Fatalf("bad: %#v", eval)
    67  	}
    68  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
    69  		t.Fatalf("bad: %#v", eval)
    70  	}
    71  	if eval.JobID != job.ID {
    72  		t.Fatalf("bad: %#v", eval)
    73  	}
    74  	if eval.JobModifyIndex != resp.JobModifyIndex {
    75  		t.Fatalf("bad: %#v", eval)
    76  	}
    77  	if eval.Status != structs.EvalStatusPending {
    78  		t.Fatalf("bad: %#v", eval)
    79  	}
    80  }
    81  
    82  func TestJobEndpoint_Register_Existing(t *testing.T) {
    83  	s1 := testServer(t, func(c *Config) {
    84  		c.NumSchedulers = 0 // Prevent automatic dequeue
    85  	})
    86  	defer s1.Shutdown()
    87  	codec := rpcClient(t, s1)
    88  	testutil.WaitForLeader(t, s1.RPC)
    89  
    90  	// Create the register request
    91  	job := mock.Job()
    92  	req := &structs.JobRegisterRequest{
    93  		Job:          job,
    94  		WriteRequest: structs.WriteRequest{Region: "global"},
    95  	}
    96  
    97  	// Fetch the response
    98  	var resp structs.JobRegisterResponse
    99  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   100  		t.Fatalf("err: %v", err)
   101  	}
   102  	if resp.Index == 0 {
   103  		t.Fatalf("bad index: %d", resp.Index)
   104  	}
   105  
   106  	// Update the job definition
   107  	job2 := mock.Job()
   108  	job2.Priority = 100
   109  	job2.ID = job.ID
   110  	req.Job = job2
   111  
   112  	// Attempt update
   113  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   114  		t.Fatalf("err: %v", err)
   115  	}
   116  	if resp.Index == 0 {
   117  		t.Fatalf("bad index: %d", resp.Index)
   118  	}
   119  
   120  	// Check for the node in the FSM
   121  	state := s1.fsm.State()
   122  	out, err := state.JobByID(job.ID)
   123  	if err != nil {
   124  		t.Fatalf("err: %v", err)
   125  	}
   126  	if out == nil {
   127  		t.Fatalf("expected job")
   128  	}
   129  	if out.ModifyIndex != resp.JobModifyIndex {
   130  		t.Fatalf("index mis-match")
   131  	}
   132  	if out.Priority != 100 {
   133  		t.Fatalf("expected update")
   134  	}
   135  
   136  	// Lookup the evaluation
   137  	eval, err := state.EvalByID(resp.EvalID)
   138  	if err != nil {
   139  		t.Fatalf("err: %v", err)
   140  	}
   141  	if eval == nil {
   142  		t.Fatalf("expected eval")
   143  	}
   144  	if eval.CreateIndex != resp.EvalCreateIndex {
   145  		t.Fatalf("index mis-match")
   146  	}
   147  
   148  	if eval.Priority != job2.Priority {
   149  		t.Fatalf("bad: %#v", eval)
   150  	}
   151  	if eval.Type != job2.Type {
   152  		t.Fatalf("bad: %#v", eval)
   153  	}
   154  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
   155  		t.Fatalf("bad: %#v", eval)
   156  	}
   157  	if eval.JobID != job2.ID {
   158  		t.Fatalf("bad: %#v", eval)
   159  	}
   160  	if eval.JobModifyIndex != resp.JobModifyIndex {
   161  		t.Fatalf("bad: %#v", eval)
   162  	}
   163  	if eval.Status != structs.EvalStatusPending {
   164  		t.Fatalf("bad: %#v", eval)
   165  	}
   166  }
   167  
   168  func TestJobEndpoint_Evaluate(t *testing.T) {
   169  	s1 := testServer(t, func(c *Config) {
   170  		c.NumSchedulers = 0 // Prevent automatic dequeue
   171  	})
   172  	defer s1.Shutdown()
   173  	codec := rpcClient(t, s1)
   174  	testutil.WaitForLeader(t, s1.RPC)
   175  
   176  	// Create the register request
   177  	job := mock.Job()
   178  	req := &structs.JobRegisterRequest{
   179  		Job:          job,
   180  		WriteRequest: structs.WriteRequest{Region: "global"},
   181  	}
   182  
   183  	// Fetch the response
   184  	var resp structs.JobRegisterResponse
   185  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   186  		t.Fatalf("err: %v", err)
   187  	}
   188  	if resp.Index == 0 {
   189  		t.Fatalf("bad index: %d", resp.Index)
   190  	}
   191  
   192  	// Force a re-evaluation
   193  	reEval := &structs.JobEvaluateRequest{
   194  		JobID:        job.ID,
   195  		WriteRequest: structs.WriteRequest{Region: "global"},
   196  	}
   197  
   198  	// Fetch the response
   199  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluate", reEval, &resp); err != nil {
   200  		t.Fatalf("err: %v", err)
   201  	}
   202  	if resp.Index == 0 {
   203  		t.Fatalf("bad index: %d", resp.Index)
   204  	}
   205  
   206  	// Lookup the evaluation
   207  	state := s1.fsm.State()
   208  	eval, err := state.EvalByID(resp.EvalID)
   209  	if err != nil {
   210  		t.Fatalf("err: %v", err)
   211  	}
   212  	if eval == nil {
   213  		t.Fatalf("expected eval")
   214  	}
   215  	if eval.CreateIndex != resp.EvalCreateIndex {
   216  		t.Fatalf("index mis-match")
   217  	}
   218  
   219  	if eval.Priority != job.Priority {
   220  		t.Fatalf("bad: %#v", eval)
   221  	}
   222  	if eval.Type != job.Type {
   223  		t.Fatalf("bad: %#v", eval)
   224  	}
   225  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
   226  		t.Fatalf("bad: %#v", eval)
   227  	}
   228  	if eval.JobID != job.ID {
   229  		t.Fatalf("bad: %#v", eval)
   230  	}
   231  	if eval.JobModifyIndex != resp.JobModifyIndex {
   232  		t.Fatalf("bad: %#v", eval)
   233  	}
   234  	if eval.Status != structs.EvalStatusPending {
   235  		t.Fatalf("bad: %#v", eval)
   236  	}
   237  }
   238  
   239  func TestJobEndpoint_Deregister(t *testing.T) {
   240  	s1 := testServer(t, func(c *Config) {
   241  		c.NumSchedulers = 0 // Prevent automatic dequeue
   242  	})
   243  	defer s1.Shutdown()
   244  	codec := rpcClient(t, s1)
   245  	testutil.WaitForLeader(t, s1.RPC)
   246  
   247  	// Create the register request
   248  	job := mock.Job()
   249  	reg := &structs.JobRegisterRequest{
   250  		Job:          job,
   251  		WriteRequest: structs.WriteRequest{Region: "global"},
   252  	}
   253  
   254  	// Fetch the response
   255  	var resp structs.JobRegisterResponse
   256  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
   257  		t.Fatalf("err: %v", err)
   258  	}
   259  
   260  	// Deregister
   261  	dereg := &structs.JobDeregisterRequest{
   262  		JobID:        job.ID,
   263  		WriteRequest: structs.WriteRequest{Region: "global"},
   264  	}
   265  	var resp2 structs.JobDeregisterResponse
   266  	if err := msgpackrpc.CallWithCodec(codec, "Job.Deregister", dereg, &resp2); err != nil {
   267  		t.Fatalf("err: %v", err)
   268  	}
   269  	if resp2.Index == 0 {
   270  		t.Fatalf("bad index: %d", resp2.Index)
   271  	}
   272  
   273  	// Check for the node in the FSM
   274  	state := s1.fsm.State()
   275  	out, err := state.JobByID(job.ID)
   276  	if err != nil {
   277  		t.Fatalf("err: %v", err)
   278  	}
   279  	if out != nil {
   280  		t.Fatalf("unexpected job")
   281  	}
   282  
   283  	// Lookup the evaluation
   284  	eval, err := state.EvalByID(resp2.EvalID)
   285  	if err != nil {
   286  		t.Fatalf("err: %v", err)
   287  	}
   288  	if eval == nil {
   289  		t.Fatalf("expected eval")
   290  	}
   291  	if eval.CreateIndex != resp2.EvalCreateIndex {
   292  		t.Fatalf("index mis-match")
   293  	}
   294  
   295  	if eval.Priority != structs.JobDefaultPriority {
   296  		t.Fatalf("bad: %#v", eval)
   297  	}
   298  	if eval.Type != structs.JobTypeService {
   299  		t.Fatalf("bad: %#v", eval)
   300  	}
   301  	if eval.TriggeredBy != structs.EvalTriggerJobDeregister {
   302  		t.Fatalf("bad: %#v", eval)
   303  	}
   304  	if eval.JobID != job.ID {
   305  		t.Fatalf("bad: %#v", eval)
   306  	}
   307  	if eval.JobModifyIndex != resp2.JobModifyIndex {
   308  		t.Fatalf("bad: %#v", eval)
   309  	}
   310  	if eval.Status != structs.EvalStatusPending {
   311  		t.Fatalf("bad: %#v", eval)
   312  	}
   313  }
   314  
   315  func TestJobEndpoint_GetJob(t *testing.T) {
   316  	s1 := testServer(t, nil)
   317  	defer s1.Shutdown()
   318  	codec := rpcClient(t, s1)
   319  	testutil.WaitForLeader(t, s1.RPC)
   320  
   321  	// Create the register request
   322  	job := mock.Job()
   323  	reg := &structs.JobRegisterRequest{
   324  		Job:          job,
   325  		WriteRequest: structs.WriteRequest{Region: "global"},
   326  	}
   327  
   328  	// Fetch the response
   329  	var resp structs.JobRegisterResponse
   330  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
   331  		t.Fatalf("err: %v", err)
   332  	}
   333  	job.CreateIndex = resp.JobModifyIndex
   334  	job.ModifyIndex = resp.JobModifyIndex
   335  
   336  	// Lookup the job
   337  	get := &structs.JobSpecificRequest{
   338  		JobID:        job.ID,
   339  		QueryOptions: structs.QueryOptions{Region: "global"},
   340  	}
   341  	var resp2 structs.SingleJobResponse
   342  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", get, &resp2); err != nil {
   343  		t.Fatalf("err: %v", err)
   344  	}
   345  	if resp2.Index != resp.JobModifyIndex {
   346  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   347  	}
   348  
   349  	if !reflect.DeepEqual(job, resp2.Job) {
   350  		t.Fatalf("bad: %#v %#v", job, resp2.Job)
   351  	}
   352  
   353  	// Lookup non-existing job
   354  	get.JobID = "foobarbaz"
   355  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", get, &resp2); err != nil {
   356  		t.Fatalf("err: %v", err)
   357  	}
   358  	if resp2.Index != resp.JobModifyIndex {
   359  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
   360  	}
   361  	if resp2.Job != nil {
   362  		t.Fatalf("unexpected job")
   363  	}
   364  }
   365  
   366  func TestJobEndpoint_ListJobs(t *testing.T) {
   367  	s1 := testServer(t, nil)
   368  	defer s1.Shutdown()
   369  	codec := rpcClient(t, s1)
   370  	testutil.WaitForLeader(t, s1.RPC)
   371  
   372  	// Create the register request
   373  	job := mock.Job()
   374  	state := s1.fsm.State()
   375  	err := state.UpsertJob(1000, job)
   376  	if err != nil {
   377  		t.Fatalf("err: %v", err)
   378  	}
   379  
   380  	// Lookup the jobs
   381  	get := &structs.JobListRequest{
   382  		QueryOptions: structs.QueryOptions{Region: "global"},
   383  	}
   384  	var resp2 structs.JobListResponse
   385  	if err := msgpackrpc.CallWithCodec(codec, "Job.List", get, &resp2); err != nil {
   386  		t.Fatalf("err: %v", err)
   387  	}
   388  	if resp2.Index != 1000 {
   389  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
   390  	}
   391  
   392  	if len(resp2.Jobs) != 1 {
   393  		t.Fatalf("bad: %#v", resp2.Jobs)
   394  	}
   395  	if resp2.Jobs[0].ID != job.ID {
   396  		t.Fatalf("bad: %#v", resp2.Jobs[0])
   397  	}
   398  }
   399  
   400  func TestJobEndpoint_Allocations(t *testing.T) {
   401  	s1 := testServer(t, nil)
   402  	defer s1.Shutdown()
   403  	codec := rpcClient(t, s1)
   404  	testutil.WaitForLeader(t, s1.RPC)
   405  
   406  	// Create the register request
   407  	alloc1 := mock.Alloc()
   408  	alloc2 := mock.Alloc()
   409  	alloc2.JobID = alloc1.JobID
   410  	state := s1.fsm.State()
   411  	err := state.UpsertAllocs(1000,
   412  		[]*structs.Allocation{alloc1, alloc2})
   413  	if err != nil {
   414  		t.Fatalf("err: %v", err)
   415  	}
   416  
   417  	// Lookup the jobs
   418  	get := &structs.JobSpecificRequest{
   419  		JobID:        alloc1.JobID,
   420  		QueryOptions: structs.QueryOptions{Region: "global"},
   421  	}
   422  	var resp2 structs.JobAllocationsResponse
   423  	if err := msgpackrpc.CallWithCodec(codec, "Job.Allocations", get, &resp2); err != nil {
   424  		t.Fatalf("err: %v", err)
   425  	}
   426  	if resp2.Index != 1000 {
   427  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
   428  	}
   429  
   430  	if len(resp2.Allocations) != 2 {
   431  		t.Fatalf("bad: %#v", resp2.Allocations)
   432  	}
   433  }
   434  
   435  func TestJobEndpoint_Evaluations(t *testing.T) {
   436  	s1 := testServer(t, nil)
   437  	defer s1.Shutdown()
   438  	codec := rpcClient(t, s1)
   439  	testutil.WaitForLeader(t, s1.RPC)
   440  
   441  	// Create the register request
   442  	eval1 := mock.Eval()
   443  	eval2 := mock.Eval()
   444  	eval2.JobID = eval1.JobID
   445  	state := s1.fsm.State()
   446  	err := state.UpsertEvals(1000,
   447  		[]*structs.Evaluation{eval1, eval2})
   448  	if err != nil {
   449  		t.Fatalf("err: %v", err)
   450  	}
   451  
   452  	// Lookup the jobs
   453  	get := &structs.JobSpecificRequest{
   454  		JobID:        eval1.JobID,
   455  		QueryOptions: structs.QueryOptions{Region: "global"},
   456  	}
   457  	var resp2 structs.JobEvaluationsResponse
   458  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluations", get, &resp2); err != nil {
   459  		t.Fatalf("err: %v", err)
   460  	}
   461  	if resp2.Index != 1000 {
   462  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
   463  	}
   464  
   465  	if len(resp2.Evaluations) != 2 {
   466  		t.Fatalf("bad: %#v", resp2.Evaluations)
   467  	}
   468  }