github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/nomad/job_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	memdb "github.com/hashicorp/go-memdb"
    11  	"github.com/hashicorp/net-rpc-msgpackrpc"
    12  	"github.com/hashicorp/nomad/nomad/mock"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/hashicorp/nomad/testutil"
    15  )
    16  
    17  func TestJobEndpoint_Register(t *testing.T) {
    18  	s1 := testServer(t, func(c *Config) {
    19  		c.NumSchedulers = 0 // Prevent automatic dequeue
    20  	})
    21  	defer s1.Shutdown()
    22  	codec := rpcClient(t, s1)
    23  	testutil.WaitForLeader(t, s1.RPC)
    24  
    25  	// Create the register request
    26  	job := mock.Job()
    27  	req := &structs.JobRegisterRequest{
    28  		Job:          job,
    29  		WriteRequest: structs.WriteRequest{Region: "global"},
    30  	}
    31  
    32  	// Fetch the response
    33  	var resp structs.JobRegisterResponse
    34  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
    35  		t.Fatalf("err: %v", err)
    36  	}
    37  	if resp.Index == 0 {
    38  		t.Fatalf("bad index: %d", resp.Index)
    39  	}
    40  
    41  	// Check for the node in the FSM
    42  	state := s1.fsm.State()
    43  	ws := memdb.NewWatchSet()
    44  	out, err := state.JobByID(ws, job.ID)
    45  	if err != nil {
    46  		t.Fatalf("err: %v", err)
    47  	}
    48  	if out == nil {
    49  		t.Fatalf("expected job")
    50  	}
    51  	if out.CreateIndex != resp.JobModifyIndex {
    52  		t.Fatalf("index mis-match")
    53  	}
    54  	serviceName := out.TaskGroups[0].Tasks[0].Services[0].Name
    55  	expectedServiceName := "web-frontend"
    56  	if serviceName != expectedServiceName {
    57  		t.Fatalf("Expected Service Name: %s, Actual: %s", expectedServiceName, serviceName)
    58  	}
    59  
    60  	// Lookup the evaluation
    61  	eval, err := state.EvalByID(ws, resp.EvalID)
    62  	if err != nil {
    63  		t.Fatalf("err: %v", err)
    64  	}
    65  	if eval == nil {
    66  		t.Fatalf("expected eval")
    67  	}
    68  	if eval.CreateIndex != resp.EvalCreateIndex {
    69  		t.Fatalf("index mis-match")
    70  	}
    71  
    72  	if eval.Priority != job.Priority {
    73  		t.Fatalf("bad: %#v", eval)
    74  	}
    75  	if eval.Type != job.Type {
    76  		t.Fatalf("bad: %#v", eval)
    77  	}
    78  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
    79  		t.Fatalf("bad: %#v", eval)
    80  	}
    81  	if eval.JobID != job.ID {
    82  		t.Fatalf("bad: %#v", eval)
    83  	}
    84  	if eval.JobModifyIndex != resp.JobModifyIndex {
    85  		t.Fatalf("bad: %#v", eval)
    86  	}
    87  	if eval.Status != structs.EvalStatusPending {
    88  		t.Fatalf("bad: %#v", eval)
    89  	}
    90  }
    91  
    92  func TestJobEndpoint_Register_InvalidDriverConfig(t *testing.T) {
    93  	s1 := testServer(t, func(c *Config) {
    94  		c.NumSchedulers = 0 // Prevent automatic dequeue
    95  	})
    96  	defer s1.Shutdown()
    97  	codec := rpcClient(t, s1)
    98  	testutil.WaitForLeader(t, s1.RPC)
    99  
   100  	// Create the register request with a job containing an invalid driver
   101  	// config
   102  	job := mock.Job()
   103  	job.TaskGroups[0].Tasks[0].Config["foo"] = 1
   104  	req := &structs.JobRegisterRequest{
   105  		Job:          job,
   106  		WriteRequest: structs.WriteRequest{Region: "global"},
   107  	}
   108  
   109  	// Fetch the response
   110  	var resp structs.JobRegisterResponse
   111  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   112  	if err == nil {
   113  		t.Fatalf("expected a validation error")
   114  	}
   115  
   116  	if !strings.Contains(err.Error(), "-> config:") {
   117  		t.Fatalf("expected a driver config validation error but got: %v", err)
   118  	}
   119  }
   120  
   121  func TestJobEndpoint_Register_Payload(t *testing.T) {
   122  	s1 := testServer(t, func(c *Config) {
   123  		c.NumSchedulers = 0 // Prevent automatic dequeue
   124  	})
   125  	defer s1.Shutdown()
   126  	codec := rpcClient(t, s1)
   127  	testutil.WaitForLeader(t, s1.RPC)
   128  
   129  	// Create the register request with a job containing an invalid driver
   130  	// config
   131  	job := mock.Job()
   132  	job.Payload = []byte{0x1}
   133  	req := &structs.JobRegisterRequest{
   134  		Job:          job,
   135  		WriteRequest: structs.WriteRequest{Region: "global"},
   136  	}
   137  
   138  	// Fetch the response
   139  	var resp structs.JobRegisterResponse
   140  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   141  	if err == nil {
   142  		t.Fatalf("expected a validation error")
   143  	}
   144  
   145  	if !strings.Contains(err.Error(), "payload") {
   146  		t.Fatalf("expected a payload error but got: %v", err)
   147  	}
   148  }
   149  
   150  func TestJobEndpoint_Register_Existing(t *testing.T) {
   151  	s1 := testServer(t, func(c *Config) {
   152  		c.NumSchedulers = 0 // Prevent automatic dequeue
   153  	})
   154  	defer s1.Shutdown()
   155  	codec := rpcClient(t, s1)
   156  	testutil.WaitForLeader(t, s1.RPC)
   157  
   158  	// Create the register request
   159  	job := mock.Job()
   160  	req := &structs.JobRegisterRequest{
   161  		Job:          job,
   162  		WriteRequest: structs.WriteRequest{Region: "global"},
   163  	}
   164  
   165  	// Fetch the response
   166  	var resp structs.JobRegisterResponse
   167  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   168  		t.Fatalf("err: %v", err)
   169  	}
   170  	if resp.Index == 0 {
   171  		t.Fatalf("bad index: %d", resp.Index)
   172  	}
   173  
   174  	// Update the job definition
   175  	job2 := mock.Job()
   176  	job2.Priority = 100
   177  	job2.ID = job.ID
   178  	req.Job = job2
   179  
   180  	// Attempt update
   181  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   182  		t.Fatalf("err: %v", err)
   183  	}
   184  	if resp.Index == 0 {
   185  		t.Fatalf("bad index: %d", resp.Index)
   186  	}
   187  
   188  	// Check for the node in the FSM
   189  	state := s1.fsm.State()
   190  	ws := memdb.NewWatchSet()
   191  	out, err := state.JobByID(ws, job.ID)
   192  	if err != nil {
   193  		t.Fatalf("err: %v", err)
   194  	}
   195  	if out == nil {
   196  		t.Fatalf("expected job")
   197  	}
   198  	if out.ModifyIndex != resp.JobModifyIndex {
   199  		t.Fatalf("index mis-match")
   200  	}
   201  	if out.Priority != 100 {
   202  		t.Fatalf("expected update")
   203  	}
   204  
   205  	// Lookup the evaluation
   206  	eval, err := state.EvalByID(ws, resp.EvalID)
   207  	if err != nil {
   208  		t.Fatalf("err: %v", err)
   209  	}
   210  	if eval == nil {
   211  		t.Fatalf("expected eval")
   212  	}
   213  	if eval.CreateIndex != resp.EvalCreateIndex {
   214  		t.Fatalf("index mis-match")
   215  	}
   216  
   217  	if eval.Priority != job2.Priority {
   218  		t.Fatalf("bad: %#v", eval)
   219  	}
   220  	if eval.Type != job2.Type {
   221  		t.Fatalf("bad: %#v", eval)
   222  	}
   223  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
   224  		t.Fatalf("bad: %#v", eval)
   225  	}
   226  	if eval.JobID != job2.ID {
   227  		t.Fatalf("bad: %#v", eval)
   228  	}
   229  	if eval.JobModifyIndex != resp.JobModifyIndex {
   230  		t.Fatalf("bad: %#v", eval)
   231  	}
   232  	if eval.Status != structs.EvalStatusPending {
   233  		t.Fatalf("bad: %#v", eval)
   234  	}
   235  }
   236  
   237  func TestJobEndpoint_Register_Periodic(t *testing.T) {
   238  	s1 := testServer(t, func(c *Config) {
   239  		c.NumSchedulers = 0 // Prevent automatic dequeue
   240  	})
   241  	defer s1.Shutdown()
   242  	codec := rpcClient(t, s1)
   243  	testutil.WaitForLeader(t, s1.RPC)
   244  
   245  	// Create the register request for a periodic job.
   246  	job := mock.PeriodicJob()
   247  	req := &structs.JobRegisterRequest{
   248  		Job:          job,
   249  		WriteRequest: structs.WriteRequest{Region: "global"},
   250  	}
   251  
   252  	// Fetch the response
   253  	var resp structs.JobRegisterResponse
   254  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   255  		t.Fatalf("err: %v", err)
   256  	}
   257  	if resp.JobModifyIndex == 0 {
   258  		t.Fatalf("bad index: %d", resp.Index)
   259  	}
   260  
   261  	// Check for the node in the FSM
   262  	state := s1.fsm.State()
   263  	ws := memdb.NewWatchSet()
   264  	out, err := state.JobByID(ws, job.ID)
   265  	if err != nil {
   266  		t.Fatalf("err: %v", err)
   267  	}
   268  	if out == nil {
   269  		t.Fatalf("expected job")
   270  	}
   271  	if out.CreateIndex != resp.JobModifyIndex {
   272  		t.Fatalf("index mis-match")
   273  	}
   274  	serviceName := out.TaskGroups[0].Tasks[0].Services[0].Name
   275  	expectedServiceName := "web-frontend"
   276  	if serviceName != expectedServiceName {
   277  		t.Fatalf("Expected Service Name: %s, Actual: %s", expectedServiceName, serviceName)
   278  	}
   279  
   280  	if resp.EvalID != "" {
   281  		t.Fatalf("Register created an eval for a periodic job")
   282  	}
   283  }
   284  
   285  func TestJobEndpoint_Register_ParameterizedJob(t *testing.T) {
   286  	s1 := testServer(t, func(c *Config) {
   287  		c.NumSchedulers = 0 // Prevent automatic dequeue
   288  	})
   289  	defer s1.Shutdown()
   290  	codec := rpcClient(t, s1)
   291  	testutil.WaitForLeader(t, s1.RPC)
   292  
   293  	// Create the register request for a parameterized job.
   294  	job := mock.Job()
   295  	job.Type = structs.JobTypeBatch
   296  	job.ParameterizedJob = &structs.ParameterizedJobConfig{}
   297  	req := &structs.JobRegisterRequest{
   298  		Job:          job,
   299  		WriteRequest: structs.WriteRequest{Region: "global"},
   300  	}
   301  
   302  	// Fetch the response
   303  	var resp structs.JobRegisterResponse
   304  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   305  		t.Fatalf("err: %v", err)
   306  	}
   307  	if resp.JobModifyIndex == 0 {
   308  		t.Fatalf("bad index: %d", resp.Index)
   309  	}
   310  
   311  	// Check for the job in the FSM
   312  	state := s1.fsm.State()
   313  	ws := memdb.NewWatchSet()
   314  	out, err := state.JobByID(ws, job.ID)
   315  	if err != nil {
   316  		t.Fatalf("err: %v", err)
   317  	}
   318  	if out == nil {
   319  		t.Fatalf("expected job")
   320  	}
   321  	if out.CreateIndex != resp.JobModifyIndex {
   322  		t.Fatalf("index mis-match")
   323  	}
   324  	if resp.EvalID != "" {
   325  		t.Fatalf("Register created an eval for a parameterized job")
   326  	}
   327  }
   328  
   329  func TestJobEndpoint_Register_EnforceIndex(t *testing.T) {
   330  	s1 := testServer(t, func(c *Config) {
   331  		c.NumSchedulers = 0 // Prevent automatic dequeue
   332  	})
   333  	defer s1.Shutdown()
   334  	codec := rpcClient(t, s1)
   335  	testutil.WaitForLeader(t, s1.RPC)
   336  
   337  	// Create the register request and enforcing an incorrect index
   338  	job := mock.Job()
   339  	req := &structs.JobRegisterRequest{
   340  		Job:            job,
   341  		EnforceIndex:   true,
   342  		JobModifyIndex: 100, // Not registered yet so not possible
   343  		WriteRequest:   structs.WriteRequest{Region: "global"},
   344  	}
   345  
   346  	// Fetch the response
   347  	var resp structs.JobRegisterResponse
   348  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   349  	if err == nil || !strings.Contains(err.Error(), RegisterEnforceIndexErrPrefix) {
   350  		t.Fatalf("expected enforcement error")
   351  	}
   352  
   353  	// Create the register request and enforcing it is new
   354  	req = &structs.JobRegisterRequest{
   355  		Job:            job,
   356  		EnforceIndex:   true,
   357  		JobModifyIndex: 0,
   358  		WriteRequest:   structs.WriteRequest{Region: "global"},
   359  	}
   360  
   361  	// Fetch the response
   362  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   363  		t.Fatalf("err: %v", err)
   364  	}
   365  	if resp.Index == 0 {
   366  		t.Fatalf("bad index: %d", resp.Index)
   367  	}
   368  
   369  	curIndex := resp.JobModifyIndex
   370  
   371  	// Check for the node in the FSM
   372  	state := s1.fsm.State()
   373  	ws := memdb.NewWatchSet()
   374  	out, err := state.JobByID(ws, job.ID)
   375  	if err != nil {
   376  		t.Fatalf("err: %v", err)
   377  	}
   378  	if out == nil {
   379  		t.Fatalf("expected job")
   380  	}
   381  	if out.CreateIndex != resp.JobModifyIndex {
   382  		t.Fatalf("index mis-match")
   383  	}
   384  
   385  	// Reregister request and enforcing it be a new job
   386  	req = &structs.JobRegisterRequest{
   387  		Job:            job,
   388  		EnforceIndex:   true,
   389  		JobModifyIndex: 0,
   390  		WriteRequest:   structs.WriteRequest{Region: "global"},
   391  	}
   392  
   393  	// Fetch the response
   394  	err = msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   395  	if err == nil || !strings.Contains(err.Error(), RegisterEnforceIndexErrPrefix) {
   396  		t.Fatalf("expected enforcement error")
   397  	}
   398  
   399  	// Reregister request and enforcing it be at an incorrect index
   400  	req = &structs.JobRegisterRequest{
   401  		Job:            job,
   402  		EnforceIndex:   true,
   403  		JobModifyIndex: curIndex - 1,
   404  		WriteRequest:   structs.WriteRequest{Region: "global"},
   405  	}
   406  
   407  	// Fetch the response
   408  	err = msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   409  	if err == nil || !strings.Contains(err.Error(), RegisterEnforceIndexErrPrefix) {
   410  		t.Fatalf("expected enforcement error")
   411  	}
   412  
   413  	// Reregister request and enforcing it be at the correct index
   414  	job.Priority = job.Priority + 1
   415  	req = &structs.JobRegisterRequest{
   416  		Job:            job,
   417  		EnforceIndex:   true,
   418  		JobModifyIndex: curIndex,
   419  		WriteRequest:   structs.WriteRequest{Region: "global"},
   420  	}
   421  
   422  	// Fetch the response
   423  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   424  		t.Fatalf("err: %v", err)
   425  	}
   426  	if resp.Index == 0 {
   427  		t.Fatalf("bad index: %d", resp.Index)
   428  	}
   429  
   430  	out, err = state.JobByID(ws, job.ID)
   431  	if err != nil {
   432  		t.Fatalf("err: %v", err)
   433  	}
   434  	if out == nil {
   435  		t.Fatalf("expected job")
   436  	}
   437  	if out.Priority != job.Priority {
   438  		t.Fatalf("priority mis-match")
   439  	}
   440  }
   441  
   442  func TestJobEndpoint_Register_Vault_Disabled(t *testing.T) {
   443  	s1 := testServer(t, func(c *Config) {
   444  		c.NumSchedulers = 0 // Prevent automatic dequeue
   445  		f := false
   446  		c.VaultConfig.Enabled = &f
   447  	})
   448  	defer s1.Shutdown()
   449  	codec := rpcClient(t, s1)
   450  	testutil.WaitForLeader(t, s1.RPC)
   451  
   452  	// Create the register request with a job asking for a vault policy
   453  	job := mock.Job()
   454  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
   455  		Policies:   []string{"foo"},
   456  		ChangeMode: structs.VaultChangeModeRestart,
   457  	}
   458  	req := &structs.JobRegisterRequest{
   459  		Job:          job,
   460  		WriteRequest: structs.WriteRequest{Region: "global"},
   461  	}
   462  
   463  	// Fetch the response
   464  	var resp structs.JobRegisterResponse
   465  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   466  	if err == nil || !strings.Contains(err.Error(), "Vault not enabled") {
   467  		t.Fatalf("expected Vault not enabled error: %v", err)
   468  	}
   469  }
   470  
   471  func TestJobEndpoint_Register_Vault_AllowUnauthenticated(t *testing.T) {
   472  	s1 := testServer(t, func(c *Config) {
   473  		c.NumSchedulers = 0 // Prevent automatic dequeue
   474  	})
   475  	defer s1.Shutdown()
   476  	codec := rpcClient(t, s1)
   477  	testutil.WaitForLeader(t, s1.RPC)
   478  
   479  	// Enable vault and allow authenticated
   480  	tr := true
   481  	s1.config.VaultConfig.Enabled = &tr
   482  	s1.config.VaultConfig.AllowUnauthenticated = &tr
   483  
   484  	// Replace the Vault Client on the server
   485  	s1.vault = &TestVaultClient{}
   486  
   487  	// Create the register request with a job asking for a vault policy
   488  	job := mock.Job()
   489  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
   490  		Policies:   []string{"foo"},
   491  		ChangeMode: structs.VaultChangeModeRestart,
   492  	}
   493  	req := &structs.JobRegisterRequest{
   494  		Job:          job,
   495  		WriteRequest: structs.WriteRequest{Region: "global"},
   496  	}
   497  
   498  	// Fetch the response
   499  	var resp structs.JobRegisterResponse
   500  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   501  	if err != nil {
   502  		t.Fatalf("bad: %v", err)
   503  	}
   504  
   505  	// Check for the job in the FSM
   506  	state := s1.fsm.State()
   507  	ws := memdb.NewWatchSet()
   508  	out, err := state.JobByID(ws, job.ID)
   509  	if err != nil {
   510  		t.Fatalf("err: %v", err)
   511  	}
   512  	if out == nil {
   513  		t.Fatalf("expected job")
   514  	}
   515  	if out.CreateIndex != resp.JobModifyIndex {
   516  		t.Fatalf("index mis-match")
   517  	}
   518  }
   519  
   520  func TestJobEndpoint_Register_Vault_NoToken(t *testing.T) {
   521  	s1 := testServer(t, func(c *Config) {
   522  		c.NumSchedulers = 0 // Prevent automatic dequeue
   523  	})
   524  	defer s1.Shutdown()
   525  	codec := rpcClient(t, s1)
   526  	testutil.WaitForLeader(t, s1.RPC)
   527  
   528  	// Enable vault
   529  	tr, f := true, false
   530  	s1.config.VaultConfig.Enabled = &tr
   531  	s1.config.VaultConfig.AllowUnauthenticated = &f
   532  
   533  	// Replace the Vault Client on the server
   534  	s1.vault = &TestVaultClient{}
   535  
   536  	// Create the register request with a job asking for a vault policy but
   537  	// don't send a Vault token
   538  	job := mock.Job()
   539  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
   540  		Policies:   []string{"foo"},
   541  		ChangeMode: structs.VaultChangeModeRestart,
   542  	}
   543  	req := &structs.JobRegisterRequest{
   544  		Job:          job,
   545  		WriteRequest: structs.WriteRequest{Region: "global"},
   546  	}
   547  
   548  	// Fetch the response
   549  	var resp structs.JobRegisterResponse
   550  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   551  	if err == nil || !strings.Contains(err.Error(), "missing Vault Token") {
   552  		t.Fatalf("expected Vault not enabled error: %v", err)
   553  	}
   554  }
   555  
   556  func TestJobEndpoint_Register_Vault_Policies(t *testing.T) {
   557  	s1 := testServer(t, func(c *Config) {
   558  		c.NumSchedulers = 0 // Prevent automatic dequeue
   559  	})
   560  	defer s1.Shutdown()
   561  	codec := rpcClient(t, s1)
   562  	testutil.WaitForLeader(t, s1.RPC)
   563  
   564  	// Enable vault
   565  	tr, f := true, false
   566  	s1.config.VaultConfig.Enabled = &tr
   567  	s1.config.VaultConfig.AllowUnauthenticated = &f
   568  
   569  	// Replace the Vault Client on the server
   570  	tvc := &TestVaultClient{}
   571  	s1.vault = tvc
   572  
   573  	// Add three tokens: one that allows the requesting policy, one that does
   574  	// not and one that returns an error
   575  	policy := "foo"
   576  
   577  	badToken := structs.GenerateUUID()
   578  	badPolicies := []string{"a", "b", "c"}
   579  	tvc.SetLookupTokenAllowedPolicies(badToken, badPolicies)
   580  
   581  	goodToken := structs.GenerateUUID()
   582  	goodPolicies := []string{"foo", "bar", "baz"}
   583  	tvc.SetLookupTokenAllowedPolicies(goodToken, goodPolicies)
   584  
   585  	rootToken := structs.GenerateUUID()
   586  	rootPolicies := []string{"root"}
   587  	tvc.SetLookupTokenAllowedPolicies(rootToken, rootPolicies)
   588  
   589  	errToken := structs.GenerateUUID()
   590  	expectedErr := fmt.Errorf("return errors from vault")
   591  	tvc.SetLookupTokenError(errToken, expectedErr)
   592  
   593  	// Create the register request with a job asking for a vault policy but
   594  	// send the bad Vault token
   595  	job := mock.Job()
   596  	job.VaultToken = badToken
   597  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
   598  		Policies:   []string{policy},
   599  		ChangeMode: structs.VaultChangeModeRestart,
   600  	}
   601  	req := &structs.JobRegisterRequest{
   602  		Job:          job,
   603  		WriteRequest: structs.WriteRequest{Region: "global"},
   604  	}
   605  
   606  	// Fetch the response
   607  	var resp structs.JobRegisterResponse
   608  	err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   609  	if err == nil || !strings.Contains(err.Error(),
   610  		"doesn't allow access to the following policies: "+policy) {
   611  		t.Fatalf("expected permission denied error: %v", err)
   612  	}
   613  
   614  	// Use the err token
   615  	job.VaultToken = errToken
   616  	err = msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp)
   617  	if err == nil || !strings.Contains(err.Error(), expectedErr.Error()) {
   618  		t.Fatalf("expected permission denied error: %v", err)
   619  	}
   620  
   621  	// Use the good token
   622  	job.VaultToken = goodToken
   623  
   624  	// Fetch the response
   625  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   626  		t.Fatalf("bad: %v", err)
   627  	}
   628  
   629  	// Check for the job in the FSM
   630  	state := s1.fsm.State()
   631  	ws := memdb.NewWatchSet()
   632  	out, err := state.JobByID(ws, job.ID)
   633  	if err != nil {
   634  		t.Fatalf("err: %v", err)
   635  	}
   636  	if out == nil {
   637  		t.Fatalf("expected job")
   638  	}
   639  	if out.CreateIndex != resp.JobModifyIndex {
   640  		t.Fatalf("index mis-match")
   641  	}
   642  	if out.VaultToken != "" {
   643  		t.Fatalf("vault token not cleared")
   644  	}
   645  
   646  	// Check that an implicit constraint was created
   647  	constraints := out.TaskGroups[0].Constraints
   648  	if l := len(constraints); l != 1 {
   649  		t.Fatalf("Unexpected number of tests: %v", l)
   650  	}
   651  
   652  	if !constraints[0].Equal(vaultConstraint) {
   653  		t.Fatalf("bad constraint; got %#v; want %#v", constraints[0], vaultConstraint)
   654  	}
   655  
   656  	// Create the register request with another job asking for a vault policy but
   657  	// send the root Vault token
   658  	job2 := mock.Job()
   659  	job2.VaultToken = rootToken
   660  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
   661  		Policies:   []string{policy},
   662  		ChangeMode: structs.VaultChangeModeRestart,
   663  	}
   664  	req = &structs.JobRegisterRequest{
   665  		Job:          job2,
   666  		WriteRequest: structs.WriteRequest{Region: "global"},
   667  	}
   668  
   669  	// Fetch the response
   670  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   671  		t.Fatalf("bad: %v", err)
   672  	}
   673  
   674  	// Check for the job in the FSM
   675  	out, err = state.JobByID(ws, job2.ID)
   676  	if err != nil {
   677  		t.Fatalf("err: %v", err)
   678  	}
   679  	if out == nil {
   680  		t.Fatalf("expected job")
   681  	}
   682  	if out.CreateIndex != resp.JobModifyIndex {
   683  		t.Fatalf("index mis-match")
   684  	}
   685  	if out.VaultToken != "" {
   686  		t.Fatalf("vault token not cleared")
   687  	}
   688  }
   689  
   690  func TestJobEndpoint_Evaluate(t *testing.T) {
   691  	s1 := testServer(t, func(c *Config) {
   692  		c.NumSchedulers = 0 // Prevent automatic dequeue
   693  	})
   694  	defer s1.Shutdown()
   695  	codec := rpcClient(t, s1)
   696  	testutil.WaitForLeader(t, s1.RPC)
   697  
   698  	// Create the register request
   699  	job := mock.Job()
   700  	req := &structs.JobRegisterRequest{
   701  		Job:          job,
   702  		WriteRequest: structs.WriteRequest{Region: "global"},
   703  	}
   704  
   705  	// Fetch the response
   706  	var resp structs.JobRegisterResponse
   707  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   708  		t.Fatalf("err: %v", err)
   709  	}
   710  	if resp.Index == 0 {
   711  		t.Fatalf("bad index: %d", resp.Index)
   712  	}
   713  
   714  	// Force a re-evaluation
   715  	reEval := &structs.JobEvaluateRequest{
   716  		JobID:        job.ID,
   717  		WriteRequest: structs.WriteRequest{Region: "global"},
   718  	}
   719  
   720  	// Fetch the response
   721  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluate", reEval, &resp); err != nil {
   722  		t.Fatalf("err: %v", err)
   723  	}
   724  	if resp.Index == 0 {
   725  		t.Fatalf("bad index: %d", resp.Index)
   726  	}
   727  
   728  	// Lookup the evaluation
   729  	state := s1.fsm.State()
   730  	ws := memdb.NewWatchSet()
   731  	eval, err := state.EvalByID(ws, resp.EvalID)
   732  	if err != nil {
   733  		t.Fatalf("err: %v", err)
   734  	}
   735  	if eval == nil {
   736  		t.Fatalf("expected eval")
   737  	}
   738  	if eval.CreateIndex != resp.EvalCreateIndex {
   739  		t.Fatalf("index mis-match")
   740  	}
   741  
   742  	if eval.Priority != job.Priority {
   743  		t.Fatalf("bad: %#v", eval)
   744  	}
   745  	if eval.Type != job.Type {
   746  		t.Fatalf("bad: %#v", eval)
   747  	}
   748  	if eval.TriggeredBy != structs.EvalTriggerJobRegister {
   749  		t.Fatalf("bad: %#v", eval)
   750  	}
   751  	if eval.JobID != job.ID {
   752  		t.Fatalf("bad: %#v", eval)
   753  	}
   754  	if eval.JobModifyIndex != resp.JobModifyIndex {
   755  		t.Fatalf("bad: %#v", eval)
   756  	}
   757  	if eval.Status != structs.EvalStatusPending {
   758  		t.Fatalf("bad: %#v", eval)
   759  	}
   760  }
   761  
   762  func TestJobEndpoint_Evaluate_Periodic(t *testing.T) {
   763  	s1 := testServer(t, func(c *Config) {
   764  		c.NumSchedulers = 0 // Prevent automatic dequeue
   765  	})
   766  	defer s1.Shutdown()
   767  	codec := rpcClient(t, s1)
   768  	testutil.WaitForLeader(t, s1.RPC)
   769  
   770  	// Create the register request
   771  	job := mock.PeriodicJob()
   772  	req := &structs.JobRegisterRequest{
   773  		Job:          job,
   774  		WriteRequest: structs.WriteRequest{Region: "global"},
   775  	}
   776  
   777  	// Fetch the response
   778  	var resp structs.JobRegisterResponse
   779  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   780  		t.Fatalf("err: %v", err)
   781  	}
   782  	if resp.JobModifyIndex == 0 {
   783  		t.Fatalf("bad index: %d", resp.Index)
   784  	}
   785  
   786  	// Force a re-evaluation
   787  	reEval := &structs.JobEvaluateRequest{
   788  		JobID:        job.ID,
   789  		WriteRequest: structs.WriteRequest{Region: "global"},
   790  	}
   791  
   792  	// Fetch the response
   793  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluate", reEval, &resp); err == nil {
   794  		t.Fatal("expect an err")
   795  	}
   796  }
   797  
   798  func TestJobEndpoint_Evaluate_ParameterizedJob(t *testing.T) {
   799  	s1 := testServer(t, func(c *Config) {
   800  		c.NumSchedulers = 0 // Prevent automatic dequeue
   801  	})
   802  	defer s1.Shutdown()
   803  	codec := rpcClient(t, s1)
   804  	testutil.WaitForLeader(t, s1.RPC)
   805  
   806  	// Create the register request
   807  	job := mock.Job()
   808  	job.Type = structs.JobTypeBatch
   809  	job.ParameterizedJob = &structs.ParameterizedJobConfig{}
   810  	req := &structs.JobRegisterRequest{
   811  		Job:          job,
   812  		WriteRequest: structs.WriteRequest{Region: "global"},
   813  	}
   814  
   815  	// Fetch the response
   816  	var resp structs.JobRegisterResponse
   817  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
   818  		t.Fatalf("err: %v", err)
   819  	}
   820  	if resp.JobModifyIndex == 0 {
   821  		t.Fatalf("bad index: %d", resp.Index)
   822  	}
   823  
   824  	// Force a re-evaluation
   825  	reEval := &structs.JobEvaluateRequest{
   826  		JobID:        job.ID,
   827  		WriteRequest: structs.WriteRequest{Region: "global"},
   828  	}
   829  
   830  	// Fetch the response
   831  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluate", reEval, &resp); err == nil {
   832  		t.Fatal("expect an err")
   833  	}
   834  }
   835  
   836  func TestJobEndpoint_Deregister(t *testing.T) {
   837  	s1 := testServer(t, func(c *Config) {
   838  		c.NumSchedulers = 0 // Prevent automatic dequeue
   839  	})
   840  	defer s1.Shutdown()
   841  	codec := rpcClient(t, s1)
   842  	testutil.WaitForLeader(t, s1.RPC)
   843  
   844  	// Create the register request
   845  	job := mock.Job()
   846  	reg := &structs.JobRegisterRequest{
   847  		Job:          job,
   848  		WriteRequest: structs.WriteRequest{Region: "global"},
   849  	}
   850  
   851  	// Fetch the response
   852  	var resp structs.JobRegisterResponse
   853  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
   854  		t.Fatalf("err: %v", err)
   855  	}
   856  
   857  	// Deregister
   858  	dereg := &structs.JobDeregisterRequest{
   859  		JobID:        job.ID,
   860  		WriteRequest: structs.WriteRequest{Region: "global"},
   861  	}
   862  	var resp2 structs.JobDeregisterResponse
   863  	if err := msgpackrpc.CallWithCodec(codec, "Job.Deregister", dereg, &resp2); err != nil {
   864  		t.Fatalf("err: %v", err)
   865  	}
   866  	if resp2.Index == 0 {
   867  		t.Fatalf("bad index: %d", resp2.Index)
   868  	}
   869  
   870  	// Check for the node in the FSM
   871  	ws := memdb.NewWatchSet()
   872  	state := s1.fsm.State()
   873  	out, err := state.JobByID(ws, job.ID)
   874  	if err != nil {
   875  		t.Fatalf("err: %v", err)
   876  	}
   877  	if out != nil {
   878  		t.Fatalf("unexpected job")
   879  	}
   880  
   881  	// Lookup the evaluation
   882  	eval, err := state.EvalByID(ws, resp2.EvalID)
   883  	if err != nil {
   884  		t.Fatalf("err: %v", err)
   885  	}
   886  	if eval == nil {
   887  		t.Fatalf("expected eval")
   888  	}
   889  	if eval.CreateIndex != resp2.EvalCreateIndex {
   890  		t.Fatalf("index mis-match")
   891  	}
   892  
   893  	if eval.Priority != structs.JobDefaultPriority {
   894  		t.Fatalf("bad: %#v", eval)
   895  	}
   896  	if eval.Type != structs.JobTypeService {
   897  		t.Fatalf("bad: %#v", eval)
   898  	}
   899  	if eval.TriggeredBy != structs.EvalTriggerJobDeregister {
   900  		t.Fatalf("bad: %#v", eval)
   901  	}
   902  	if eval.JobID != job.ID {
   903  		t.Fatalf("bad: %#v", eval)
   904  	}
   905  	if eval.JobModifyIndex != resp2.JobModifyIndex {
   906  		t.Fatalf("bad: %#v", eval)
   907  	}
   908  	if eval.Status != structs.EvalStatusPending {
   909  		t.Fatalf("bad: %#v", eval)
   910  	}
   911  }
   912  
   913  func TestJobEndpoint_Deregister_NonExistent(t *testing.T) {
   914  	s1 := testServer(t, func(c *Config) {
   915  		c.NumSchedulers = 0 // Prevent automatic dequeue
   916  	})
   917  	defer s1.Shutdown()
   918  	codec := rpcClient(t, s1)
   919  	testutil.WaitForLeader(t, s1.RPC)
   920  
   921  	// Deregister
   922  	jobID := "foo"
   923  	dereg := &structs.JobDeregisterRequest{
   924  		JobID:        jobID,
   925  		WriteRequest: structs.WriteRequest{Region: "global"},
   926  	}
   927  	var resp2 structs.JobDeregisterResponse
   928  	if err := msgpackrpc.CallWithCodec(codec, "Job.Deregister", dereg, &resp2); err != nil {
   929  		t.Fatalf("err: %v", err)
   930  	}
   931  	if resp2.JobModifyIndex == 0 {
   932  		t.Fatalf("bad index: %d", resp2.Index)
   933  	}
   934  
   935  	// Lookup the evaluation
   936  	state := s1.fsm.State()
   937  	ws := memdb.NewWatchSet()
   938  	eval, err := state.EvalByID(ws, resp2.EvalID)
   939  	if err != nil {
   940  		t.Fatalf("err: %v", err)
   941  	}
   942  	if eval == nil {
   943  		t.Fatalf("expected eval")
   944  	}
   945  	if eval.CreateIndex != resp2.EvalCreateIndex {
   946  		t.Fatalf("index mis-match")
   947  	}
   948  
   949  	if eval.Priority != structs.JobDefaultPriority {
   950  		t.Fatalf("bad: %#v", eval)
   951  	}
   952  	if eval.Type != structs.JobTypeService {
   953  		t.Fatalf("bad: %#v", eval)
   954  	}
   955  	if eval.TriggeredBy != structs.EvalTriggerJobDeregister {
   956  		t.Fatalf("bad: %#v", eval)
   957  	}
   958  	if eval.JobID != jobID {
   959  		t.Fatalf("bad: %#v", eval)
   960  	}
   961  	if eval.JobModifyIndex != resp2.JobModifyIndex {
   962  		t.Fatalf("bad: %#v", eval)
   963  	}
   964  	if eval.Status != structs.EvalStatusPending {
   965  		t.Fatalf("bad: %#v", eval)
   966  	}
   967  }
   968  
   969  func TestJobEndpoint_Deregister_Periodic(t *testing.T) {
   970  	s1 := testServer(t, func(c *Config) {
   971  		c.NumSchedulers = 0 // Prevent automatic dequeue
   972  	})
   973  	defer s1.Shutdown()
   974  	codec := rpcClient(t, s1)
   975  	testutil.WaitForLeader(t, s1.RPC)
   976  
   977  	// Create the register request
   978  	job := mock.PeriodicJob()
   979  	reg := &structs.JobRegisterRequest{
   980  		Job:          job,
   981  		WriteRequest: structs.WriteRequest{Region: "global"},
   982  	}
   983  
   984  	// Fetch the response
   985  	var resp structs.JobRegisterResponse
   986  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
   987  		t.Fatalf("err: %v", err)
   988  	}
   989  
   990  	// Deregister
   991  	dereg := &structs.JobDeregisterRequest{
   992  		JobID:        job.ID,
   993  		WriteRequest: structs.WriteRequest{Region: "global"},
   994  	}
   995  	var resp2 structs.JobDeregisterResponse
   996  	if err := msgpackrpc.CallWithCodec(codec, "Job.Deregister", dereg, &resp2); err != nil {
   997  		t.Fatalf("err: %v", err)
   998  	}
   999  	if resp2.JobModifyIndex == 0 {
  1000  		t.Fatalf("bad index: %d", resp2.Index)
  1001  	}
  1002  
  1003  	// Check for the node in the FSM
  1004  	state := s1.fsm.State()
  1005  	ws := memdb.NewWatchSet()
  1006  	out, err := state.JobByID(ws, job.ID)
  1007  	if err != nil {
  1008  		t.Fatalf("err: %v", err)
  1009  	}
  1010  	if out != nil {
  1011  		t.Fatalf("unexpected job")
  1012  	}
  1013  
  1014  	if resp.EvalID != "" {
  1015  		t.Fatalf("Deregister created an eval for a periodic job")
  1016  	}
  1017  }
  1018  
  1019  func TestJobEndpoint_Deregister_ParameterizedJob(t *testing.T) {
  1020  	s1 := testServer(t, func(c *Config) {
  1021  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1022  	})
  1023  	defer s1.Shutdown()
  1024  	codec := rpcClient(t, s1)
  1025  	testutil.WaitForLeader(t, s1.RPC)
  1026  
  1027  	// Create the register request
  1028  	job := mock.Job()
  1029  	job.Type = structs.JobTypeBatch
  1030  	job.ParameterizedJob = &structs.ParameterizedJobConfig{}
  1031  	reg := &structs.JobRegisterRequest{
  1032  		Job:          job,
  1033  		WriteRequest: structs.WriteRequest{Region: "global"},
  1034  	}
  1035  
  1036  	// Fetch the response
  1037  	var resp structs.JobRegisterResponse
  1038  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
  1039  		t.Fatalf("err: %v", err)
  1040  	}
  1041  
  1042  	// Deregister
  1043  	dereg := &structs.JobDeregisterRequest{
  1044  		JobID:        job.ID,
  1045  		WriteRequest: structs.WriteRequest{Region: "global"},
  1046  	}
  1047  	var resp2 structs.JobDeregisterResponse
  1048  	if err := msgpackrpc.CallWithCodec(codec, "Job.Deregister", dereg, &resp2); err != nil {
  1049  		t.Fatalf("err: %v", err)
  1050  	}
  1051  	if resp2.JobModifyIndex == 0 {
  1052  		t.Fatalf("bad index: %d", resp2.Index)
  1053  	}
  1054  
  1055  	// Check for the node in the FSM
  1056  	state := s1.fsm.State()
  1057  	ws := memdb.NewWatchSet()
  1058  	out, err := state.JobByID(ws, job.ID)
  1059  	if err != nil {
  1060  		t.Fatalf("err: %v", err)
  1061  	}
  1062  	if out != nil {
  1063  		t.Fatalf("unexpected job")
  1064  	}
  1065  
  1066  	if resp.EvalID != "" {
  1067  		t.Fatalf("Deregister created an eval for a parameterized job")
  1068  	}
  1069  }
  1070  
  1071  func TestJobEndpoint_GetJob(t *testing.T) {
  1072  	s1 := testServer(t, nil)
  1073  	defer s1.Shutdown()
  1074  	codec := rpcClient(t, s1)
  1075  	testutil.WaitForLeader(t, s1.RPC)
  1076  
  1077  	// Create the register request
  1078  	job := mock.Job()
  1079  	reg := &structs.JobRegisterRequest{
  1080  		Job:          job,
  1081  		WriteRequest: structs.WriteRequest{Region: "global"},
  1082  	}
  1083  
  1084  	// Fetch the response
  1085  	var resp structs.JobRegisterResponse
  1086  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
  1087  		t.Fatalf("err: %v", err)
  1088  	}
  1089  	job.CreateIndex = resp.JobModifyIndex
  1090  	job.ModifyIndex = resp.JobModifyIndex
  1091  	job.JobModifyIndex = resp.JobModifyIndex
  1092  
  1093  	// Lookup the job
  1094  	get := &structs.JobSpecificRequest{
  1095  		JobID:        job.ID,
  1096  		QueryOptions: structs.QueryOptions{Region: "global"},
  1097  	}
  1098  	var resp2 structs.SingleJobResponse
  1099  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", get, &resp2); err != nil {
  1100  		t.Fatalf("err: %v", err)
  1101  	}
  1102  	if resp2.Index != resp.JobModifyIndex {
  1103  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
  1104  	}
  1105  
  1106  	// Make a copy of the origin job and change the service name so that we can
  1107  	// do a deep equal with the response from the GET JOB Api
  1108  	j := job
  1109  	j.TaskGroups[0].Tasks[0].Services[0].Name = "web-frontend"
  1110  	for tgix, tg := range j.TaskGroups {
  1111  		for tidx, t := range tg.Tasks {
  1112  			for sidx, service := range t.Services {
  1113  				for cidx, check := range service.Checks {
  1114  					check.Name = resp2.Job.TaskGroups[tgix].Tasks[tidx].Services[sidx].Checks[cidx].Name
  1115  				}
  1116  			}
  1117  		}
  1118  	}
  1119  
  1120  	if !reflect.DeepEqual(j, resp2.Job) {
  1121  		t.Fatalf("bad: %#v %#v", job, resp2.Job)
  1122  	}
  1123  
  1124  	// Lookup non-existing job
  1125  	get.JobID = "foobarbaz"
  1126  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", get, &resp2); err != nil {
  1127  		t.Fatalf("err: %v", err)
  1128  	}
  1129  	if resp2.Index != resp.JobModifyIndex {
  1130  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
  1131  	}
  1132  	if resp2.Job != nil {
  1133  		t.Fatalf("unexpected job")
  1134  	}
  1135  }
  1136  
  1137  func TestJobEndpoint_GetJobSummary(t *testing.T) {
  1138  	s1 := testServer(t, func(c *Config) {
  1139  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1140  	})
  1141  
  1142  	defer s1.Shutdown()
  1143  	codec := rpcClient(t, s1)
  1144  	testutil.WaitForLeader(t, s1.RPC)
  1145  
  1146  	// Create the register request
  1147  	job := mock.Job()
  1148  	reg := &structs.JobRegisterRequest{
  1149  		Job:          job,
  1150  		WriteRequest: structs.WriteRequest{Region: "global"},
  1151  	}
  1152  
  1153  	// Fetch the response
  1154  	var resp structs.JobRegisterResponse
  1155  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", reg, &resp); err != nil {
  1156  		t.Fatalf("err: %v", err)
  1157  	}
  1158  	job.CreateIndex = resp.JobModifyIndex
  1159  	job.ModifyIndex = resp.JobModifyIndex
  1160  	job.JobModifyIndex = resp.JobModifyIndex
  1161  
  1162  	// Lookup the job summary
  1163  	get := &structs.JobSummaryRequest{
  1164  		JobID:        job.ID,
  1165  		QueryOptions: structs.QueryOptions{Region: "global"},
  1166  	}
  1167  	var resp2 structs.JobSummaryResponse
  1168  	if err := msgpackrpc.CallWithCodec(codec, "Job.Summary", get, &resp2); err != nil {
  1169  		t.Fatalf("err: %v", err)
  1170  	}
  1171  	if resp2.Index != resp.JobModifyIndex {
  1172  		t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index)
  1173  	}
  1174  
  1175  	expectedJobSummary := structs.JobSummary{
  1176  		JobID: job.ID,
  1177  		Summary: map[string]structs.TaskGroupSummary{
  1178  			"web": structs.TaskGroupSummary{},
  1179  		},
  1180  		Children:    new(structs.JobChildrenSummary),
  1181  		CreateIndex: job.CreateIndex,
  1182  		ModifyIndex: job.CreateIndex,
  1183  	}
  1184  
  1185  	if !reflect.DeepEqual(resp2.JobSummary, &expectedJobSummary) {
  1186  		t.Fatalf("exptected: %v, actual: %v", expectedJobSummary, resp2.JobSummary)
  1187  	}
  1188  }
  1189  
  1190  func TestJobEndpoint_GetJobSummary_Blocking(t *testing.T) {
  1191  	s1 := testServer(t, nil)
  1192  	defer s1.Shutdown()
  1193  	state := s1.fsm.State()
  1194  	codec := rpcClient(t, s1)
  1195  	testutil.WaitForLeader(t, s1.RPC)
  1196  
  1197  	// Create a job and insert it
  1198  	job1 := mock.Job()
  1199  	time.AfterFunc(200*time.Millisecond, func() {
  1200  		if err := state.UpsertJob(100, job1); err != nil {
  1201  			t.Fatalf("err: %v", err)
  1202  		}
  1203  	})
  1204  
  1205  	// Ensure the job summary request gets fired
  1206  	req := &structs.JobSummaryRequest{
  1207  		JobID: job1.ID,
  1208  		QueryOptions: structs.QueryOptions{
  1209  			Region:        "global",
  1210  			MinQueryIndex: 50,
  1211  		},
  1212  	}
  1213  	var resp structs.JobSummaryResponse
  1214  	start := time.Now()
  1215  	if err := msgpackrpc.CallWithCodec(codec, "Job.Summary", req, &resp); err != nil {
  1216  		t.Fatalf("err: %v", err)
  1217  	}
  1218  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1219  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1220  	}
  1221  
  1222  	// Upsert an allocation for the job which should trigger the watch.
  1223  	time.AfterFunc(200*time.Millisecond, func() {
  1224  		alloc := mock.Alloc()
  1225  		alloc.JobID = job1.ID
  1226  		alloc.Job = job1
  1227  		if err := state.UpsertAllocs(200, []*structs.Allocation{alloc}); err != nil {
  1228  			t.Fatalf("err: %v", err)
  1229  		}
  1230  	})
  1231  	req = &structs.JobSummaryRequest{
  1232  		JobID: job1.ID,
  1233  		QueryOptions: structs.QueryOptions{
  1234  			Region:        "global",
  1235  			MinQueryIndex: 199,
  1236  		},
  1237  	}
  1238  	start = time.Now()
  1239  	var resp1 structs.JobSummaryResponse
  1240  	start = time.Now()
  1241  	if err := msgpackrpc.CallWithCodec(codec, "Job.Summary", req, &resp1); err != nil {
  1242  		t.Fatalf("err: %v", err)
  1243  	}
  1244  
  1245  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1246  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1247  	}
  1248  	if resp1.Index != 200 {
  1249  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
  1250  	}
  1251  	if resp1.JobSummary == nil {
  1252  		t.Fatalf("bad: %#v", resp)
  1253  	}
  1254  
  1255  	// Job delete fires watches
  1256  	time.AfterFunc(100*time.Millisecond, func() {
  1257  		if err := state.DeleteJob(300, job1.ID); err != nil {
  1258  			t.Fatalf("err: %v", err)
  1259  		}
  1260  	})
  1261  
  1262  	req.QueryOptions.MinQueryIndex = 250
  1263  	start = time.Now()
  1264  
  1265  	var resp2 structs.SingleJobResponse
  1266  	if err := msgpackrpc.CallWithCodec(codec, "Job.Summary", req, &resp2); err != nil {
  1267  		t.Fatalf("err: %v", err)
  1268  	}
  1269  
  1270  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1271  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
  1272  	}
  1273  	if resp2.Index != 300 {
  1274  		t.Fatalf("Bad index: %d %d", resp2.Index, 300)
  1275  	}
  1276  	if resp2.Job != nil {
  1277  		t.Fatalf("bad: %#v", resp2.Job)
  1278  	}
  1279  }
  1280  
  1281  func TestJobEndpoint_GetJob_Blocking(t *testing.T) {
  1282  	s1 := testServer(t, nil)
  1283  	defer s1.Shutdown()
  1284  	state := s1.fsm.State()
  1285  	codec := rpcClient(t, s1)
  1286  	testutil.WaitForLeader(t, s1.RPC)
  1287  
  1288  	// Create the jobs
  1289  	job1 := mock.Job()
  1290  	job2 := mock.Job()
  1291  
  1292  	// Upsert a job we are not interested in first.
  1293  	time.AfterFunc(100*time.Millisecond, func() {
  1294  		if err := state.UpsertJob(100, job1); err != nil {
  1295  			t.Fatalf("err: %v", err)
  1296  		}
  1297  	})
  1298  
  1299  	// Upsert another job later which should trigger the watch.
  1300  	time.AfterFunc(200*time.Millisecond, func() {
  1301  		if err := state.UpsertJob(200, job2); err != nil {
  1302  			t.Fatalf("err: %v", err)
  1303  		}
  1304  	})
  1305  
  1306  	req := &structs.JobSpecificRequest{
  1307  		JobID: job2.ID,
  1308  		QueryOptions: structs.QueryOptions{
  1309  			Region:        "global",
  1310  			MinQueryIndex: 150,
  1311  		},
  1312  	}
  1313  	start := time.Now()
  1314  	var resp structs.SingleJobResponse
  1315  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", req, &resp); err != nil {
  1316  		t.Fatalf("err: %v", err)
  1317  	}
  1318  
  1319  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1320  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1321  	}
  1322  	if resp.Index != 200 {
  1323  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
  1324  	}
  1325  	if resp.Job == nil || resp.Job.ID != job2.ID {
  1326  		t.Fatalf("bad: %#v", resp.Job)
  1327  	}
  1328  
  1329  	// Job delete fires watches
  1330  	time.AfterFunc(100*time.Millisecond, func() {
  1331  		if err := state.DeleteJob(300, job2.ID); err != nil {
  1332  			t.Fatalf("err: %v", err)
  1333  		}
  1334  	})
  1335  
  1336  	req.QueryOptions.MinQueryIndex = 250
  1337  	start = time.Now()
  1338  
  1339  	var resp2 structs.SingleJobResponse
  1340  	if err := msgpackrpc.CallWithCodec(codec, "Job.GetJob", req, &resp2); err != nil {
  1341  		t.Fatalf("err: %v", err)
  1342  	}
  1343  
  1344  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1345  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
  1346  	}
  1347  	if resp2.Index != 300 {
  1348  		t.Fatalf("Bad index: %d %d", resp2.Index, 300)
  1349  	}
  1350  	if resp2.Job != nil {
  1351  		t.Fatalf("bad: %#v", resp2.Job)
  1352  	}
  1353  }
  1354  
  1355  func TestJobEndpoint_ListJobs(t *testing.T) {
  1356  	s1 := testServer(t, nil)
  1357  	defer s1.Shutdown()
  1358  	codec := rpcClient(t, s1)
  1359  	testutil.WaitForLeader(t, s1.RPC)
  1360  
  1361  	// Create the register request
  1362  	job := mock.Job()
  1363  	state := s1.fsm.State()
  1364  	err := state.UpsertJob(1000, job)
  1365  	if err != nil {
  1366  		t.Fatalf("err: %v", err)
  1367  	}
  1368  
  1369  	// Lookup the jobs
  1370  	get := &structs.JobListRequest{
  1371  		QueryOptions: structs.QueryOptions{Region: "global"},
  1372  	}
  1373  	var resp2 structs.JobListResponse
  1374  	if err := msgpackrpc.CallWithCodec(codec, "Job.List", get, &resp2); err != nil {
  1375  		t.Fatalf("err: %v", err)
  1376  	}
  1377  	if resp2.Index != 1000 {
  1378  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
  1379  	}
  1380  
  1381  	if len(resp2.Jobs) != 1 {
  1382  		t.Fatalf("bad: %#v", resp2.Jobs)
  1383  	}
  1384  	if resp2.Jobs[0].ID != job.ID {
  1385  		t.Fatalf("bad: %#v", resp2.Jobs[0])
  1386  	}
  1387  
  1388  	// Lookup the jobs by prefix
  1389  	get = &structs.JobListRequest{
  1390  		QueryOptions: structs.QueryOptions{Region: "global", Prefix: resp2.Jobs[0].ID[:4]},
  1391  	}
  1392  	var resp3 structs.JobListResponse
  1393  	if err := msgpackrpc.CallWithCodec(codec, "Job.List", get, &resp3); err != nil {
  1394  		t.Fatalf("err: %v", err)
  1395  	}
  1396  	if resp3.Index != 1000 {
  1397  		t.Fatalf("Bad index: %d %d", resp3.Index, 1000)
  1398  	}
  1399  
  1400  	if len(resp3.Jobs) != 1 {
  1401  		t.Fatalf("bad: %#v", resp3.Jobs)
  1402  	}
  1403  	if resp3.Jobs[0].ID != job.ID {
  1404  		t.Fatalf("bad: %#v", resp3.Jobs[0])
  1405  	}
  1406  }
  1407  
  1408  func TestJobEndpoint_ListJobs_Blocking(t *testing.T) {
  1409  	s1 := testServer(t, nil)
  1410  	defer s1.Shutdown()
  1411  	state := s1.fsm.State()
  1412  	codec := rpcClient(t, s1)
  1413  	testutil.WaitForLeader(t, s1.RPC)
  1414  
  1415  	// Create the job
  1416  	job := mock.Job()
  1417  
  1418  	// Upsert job triggers watches
  1419  	time.AfterFunc(100*time.Millisecond, func() {
  1420  		if err := state.UpsertJob(100, job); err != nil {
  1421  			t.Fatalf("err: %v", err)
  1422  		}
  1423  	})
  1424  
  1425  	req := &structs.JobListRequest{
  1426  		QueryOptions: structs.QueryOptions{
  1427  			Region:        "global",
  1428  			MinQueryIndex: 50,
  1429  		},
  1430  	}
  1431  	start := time.Now()
  1432  	var resp structs.JobListResponse
  1433  	if err := msgpackrpc.CallWithCodec(codec, "Job.List", req, &resp); err != nil {
  1434  		t.Fatalf("err: %v", err)
  1435  	}
  1436  
  1437  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1438  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1439  	}
  1440  	if resp.Index != 100 {
  1441  		t.Fatalf("Bad index: %d %d", resp.Index, 100)
  1442  	}
  1443  	if len(resp.Jobs) != 1 || resp.Jobs[0].ID != job.ID {
  1444  		t.Fatalf("bad: %#v", resp)
  1445  	}
  1446  
  1447  	// Job deletion triggers watches
  1448  	time.AfterFunc(100*time.Millisecond, func() {
  1449  		if err := state.DeleteJob(200, job.ID); err != nil {
  1450  			t.Fatalf("err: %v", err)
  1451  		}
  1452  	})
  1453  
  1454  	req.MinQueryIndex = 150
  1455  	start = time.Now()
  1456  	var resp2 structs.JobListResponse
  1457  	if err := msgpackrpc.CallWithCodec(codec, "Job.List", req, &resp2); err != nil {
  1458  		t.Fatalf("err: %v", err)
  1459  	}
  1460  
  1461  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1462  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
  1463  	}
  1464  	if resp2.Index != 200 {
  1465  		t.Fatalf("Bad index: %d %d", resp2.Index, 200)
  1466  	}
  1467  	if len(resp2.Jobs) != 0 {
  1468  		t.Fatalf("bad: %#v", resp2)
  1469  	}
  1470  }
  1471  
  1472  func TestJobEndpoint_Allocations(t *testing.T) {
  1473  	s1 := testServer(t, nil)
  1474  	defer s1.Shutdown()
  1475  	codec := rpcClient(t, s1)
  1476  	testutil.WaitForLeader(t, s1.RPC)
  1477  
  1478  	// Create the register request
  1479  	alloc1 := mock.Alloc()
  1480  	alloc2 := mock.Alloc()
  1481  	alloc2.JobID = alloc1.JobID
  1482  	state := s1.fsm.State()
  1483  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1484  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1485  	err := state.UpsertAllocs(1000,
  1486  		[]*structs.Allocation{alloc1, alloc2})
  1487  	if err != nil {
  1488  		t.Fatalf("err: %v", err)
  1489  	}
  1490  
  1491  	// Lookup the jobs
  1492  	get := &structs.JobSpecificRequest{
  1493  		JobID:        alloc1.JobID,
  1494  		QueryOptions: structs.QueryOptions{Region: "global"},
  1495  	}
  1496  	var resp2 structs.JobAllocationsResponse
  1497  	if err := msgpackrpc.CallWithCodec(codec, "Job.Allocations", get, &resp2); err != nil {
  1498  		t.Fatalf("err: %v", err)
  1499  	}
  1500  	if resp2.Index != 1000 {
  1501  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
  1502  	}
  1503  
  1504  	if len(resp2.Allocations) != 2 {
  1505  		t.Fatalf("bad: %#v", resp2.Allocations)
  1506  	}
  1507  }
  1508  
  1509  func TestJobEndpoint_Allocations_Blocking(t *testing.T) {
  1510  	s1 := testServer(t, nil)
  1511  	defer s1.Shutdown()
  1512  	codec := rpcClient(t, s1)
  1513  	testutil.WaitForLeader(t, s1.RPC)
  1514  
  1515  	// Create the register request
  1516  	alloc1 := mock.Alloc()
  1517  	alloc2 := mock.Alloc()
  1518  	alloc2.JobID = "job1"
  1519  	state := s1.fsm.State()
  1520  
  1521  	// First upsert an unrelated alloc
  1522  	time.AfterFunc(100*time.Millisecond, func() {
  1523  		state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID))
  1524  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc1})
  1525  		if err != nil {
  1526  			t.Fatalf("err: %v", err)
  1527  		}
  1528  	})
  1529  
  1530  	// Upsert an alloc for the job we are interested in later
  1531  	time.AfterFunc(200*time.Millisecond, func() {
  1532  		state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID))
  1533  		err := state.UpsertAllocs(200, []*structs.Allocation{alloc2})
  1534  		if err != nil {
  1535  			t.Fatalf("err: %v", err)
  1536  		}
  1537  	})
  1538  
  1539  	// Lookup the jobs
  1540  	get := &structs.JobSpecificRequest{
  1541  		JobID: "job1",
  1542  		QueryOptions: structs.QueryOptions{
  1543  			Region:        "global",
  1544  			MinQueryIndex: 150,
  1545  		},
  1546  	}
  1547  	var resp structs.JobAllocationsResponse
  1548  	start := time.Now()
  1549  	if err := msgpackrpc.CallWithCodec(codec, "Job.Allocations", get, &resp); err != nil {
  1550  		t.Fatalf("err: %v", err)
  1551  	}
  1552  
  1553  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1554  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1555  	}
  1556  	if resp.Index != 200 {
  1557  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
  1558  	}
  1559  	if len(resp.Allocations) != 1 || resp.Allocations[0].JobID != "job1" {
  1560  		t.Fatalf("bad: %#v", resp.Allocations)
  1561  	}
  1562  }
  1563  
  1564  func TestJobEndpoint_Evaluations(t *testing.T) {
  1565  	s1 := testServer(t, nil)
  1566  	defer s1.Shutdown()
  1567  	codec := rpcClient(t, s1)
  1568  	testutil.WaitForLeader(t, s1.RPC)
  1569  
  1570  	// Create the register request
  1571  	eval1 := mock.Eval()
  1572  	eval2 := mock.Eval()
  1573  	eval2.JobID = eval1.JobID
  1574  	state := s1.fsm.State()
  1575  	err := state.UpsertEvals(1000,
  1576  		[]*structs.Evaluation{eval1, eval2})
  1577  	if err != nil {
  1578  		t.Fatalf("err: %v", err)
  1579  	}
  1580  
  1581  	// Lookup the jobs
  1582  	get := &structs.JobSpecificRequest{
  1583  		JobID:        eval1.JobID,
  1584  		QueryOptions: structs.QueryOptions{Region: "global"},
  1585  	}
  1586  	var resp2 structs.JobEvaluationsResponse
  1587  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluations", get, &resp2); err != nil {
  1588  		t.Fatalf("err: %v", err)
  1589  	}
  1590  	if resp2.Index != 1000 {
  1591  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
  1592  	}
  1593  
  1594  	if len(resp2.Evaluations) != 2 {
  1595  		t.Fatalf("bad: %#v", resp2.Evaluations)
  1596  	}
  1597  }
  1598  
  1599  func TestJobEndpoint_Evaluations_Blocking(t *testing.T) {
  1600  	s1 := testServer(t, nil)
  1601  	defer s1.Shutdown()
  1602  	codec := rpcClient(t, s1)
  1603  	testutil.WaitForLeader(t, s1.RPC)
  1604  
  1605  	// Create the register request
  1606  	eval1 := mock.Eval()
  1607  	eval2 := mock.Eval()
  1608  	eval2.JobID = "job1"
  1609  	state := s1.fsm.State()
  1610  
  1611  	// First upsert an unrelated eval
  1612  	time.AfterFunc(100*time.Millisecond, func() {
  1613  		err := state.UpsertEvals(100, []*structs.Evaluation{eval1})
  1614  		if err != nil {
  1615  			t.Fatalf("err: %v", err)
  1616  		}
  1617  	})
  1618  
  1619  	// Upsert an eval for the job we are interested in later
  1620  	time.AfterFunc(200*time.Millisecond, func() {
  1621  		err := state.UpsertEvals(200, []*structs.Evaluation{eval2})
  1622  		if err != nil {
  1623  			t.Fatalf("err: %v", err)
  1624  		}
  1625  	})
  1626  
  1627  	// Lookup the jobs
  1628  	get := &structs.JobSpecificRequest{
  1629  		JobID: "job1",
  1630  		QueryOptions: structs.QueryOptions{
  1631  			Region:        "global",
  1632  			MinQueryIndex: 150,
  1633  		},
  1634  	}
  1635  	var resp structs.JobEvaluationsResponse
  1636  	start := time.Now()
  1637  	if err := msgpackrpc.CallWithCodec(codec, "Job.Evaluations", get, &resp); err != nil {
  1638  		t.Fatalf("err: %v", err)
  1639  	}
  1640  
  1641  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1642  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1643  	}
  1644  	if resp.Index != 200 {
  1645  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
  1646  	}
  1647  	if len(resp.Evaluations) != 1 || resp.Evaluations[0].JobID != "job1" {
  1648  		t.Fatalf("bad: %#v", resp.Evaluations)
  1649  	}
  1650  }
  1651  
  1652  func TestJobEndpoint_Plan_WithDiff(t *testing.T) {
  1653  	s1 := testServer(t, func(c *Config) {
  1654  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1655  	})
  1656  	defer s1.Shutdown()
  1657  	codec := rpcClient(t, s1)
  1658  	testutil.WaitForLeader(t, s1.RPC)
  1659  
  1660  	// Create the register request
  1661  	job := mock.Job()
  1662  	req := &structs.JobRegisterRequest{
  1663  		Job:          job,
  1664  		WriteRequest: structs.WriteRequest{Region: "global"},
  1665  	}
  1666  
  1667  	// Fetch the response
  1668  	var resp structs.JobRegisterResponse
  1669  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
  1670  		t.Fatalf("err: %v", err)
  1671  	}
  1672  	if resp.Index == 0 {
  1673  		t.Fatalf("bad index: %d", resp.Index)
  1674  	}
  1675  
  1676  	// Create a plan request
  1677  	planReq := &structs.JobPlanRequest{
  1678  		Job:          job,
  1679  		Diff:         true,
  1680  		WriteRequest: structs.WriteRequest{Region: "global"},
  1681  	}
  1682  
  1683  	// Fetch the response
  1684  	var planResp structs.JobPlanResponse
  1685  	if err := msgpackrpc.CallWithCodec(codec, "Job.Plan", planReq, &planResp); err != nil {
  1686  		t.Fatalf("err: %v", err)
  1687  	}
  1688  
  1689  	// Check the response
  1690  	if planResp.JobModifyIndex == 0 {
  1691  		t.Fatalf("bad cas: %d", planResp.JobModifyIndex)
  1692  	}
  1693  	if planResp.Annotations == nil {
  1694  		t.Fatalf("no annotations")
  1695  	}
  1696  	if planResp.Diff == nil {
  1697  		t.Fatalf("no diff")
  1698  	}
  1699  	if len(planResp.FailedTGAllocs) == 0 {
  1700  		t.Fatalf("no failed task group alloc metrics")
  1701  	}
  1702  }
  1703  
  1704  func TestJobEndpoint_Plan_NoDiff(t *testing.T) {
  1705  	s1 := testServer(t, func(c *Config) {
  1706  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1707  	})
  1708  	defer s1.Shutdown()
  1709  	codec := rpcClient(t, s1)
  1710  	testutil.WaitForLeader(t, s1.RPC)
  1711  
  1712  	// Create the register request
  1713  	job := mock.Job()
  1714  	req := &structs.JobRegisterRequest{
  1715  		Job:          job,
  1716  		WriteRequest: structs.WriteRequest{Region: "global"},
  1717  	}
  1718  
  1719  	// Fetch the response
  1720  	var resp structs.JobRegisterResponse
  1721  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
  1722  		t.Fatalf("err: %v", err)
  1723  	}
  1724  	if resp.Index == 0 {
  1725  		t.Fatalf("bad index: %d", resp.Index)
  1726  	}
  1727  
  1728  	// Create a plan request
  1729  	planReq := &structs.JobPlanRequest{
  1730  		Job:          job,
  1731  		Diff:         false,
  1732  		WriteRequest: structs.WriteRequest{Region: "global"},
  1733  	}
  1734  
  1735  	// Fetch the response
  1736  	var planResp structs.JobPlanResponse
  1737  	if err := msgpackrpc.CallWithCodec(codec, "Job.Plan", planReq, &planResp); err != nil {
  1738  		t.Fatalf("err: %v", err)
  1739  	}
  1740  
  1741  	// Check the response
  1742  	if planResp.JobModifyIndex == 0 {
  1743  		t.Fatalf("bad cas: %d", planResp.JobModifyIndex)
  1744  	}
  1745  	if planResp.Annotations == nil {
  1746  		t.Fatalf("no annotations")
  1747  	}
  1748  	if planResp.Diff != nil {
  1749  		t.Fatalf("got diff")
  1750  	}
  1751  	if len(planResp.FailedTGAllocs) == 0 {
  1752  		t.Fatalf("no failed task group alloc metrics")
  1753  	}
  1754  }
  1755  
  1756  func TestJobEndpoint_ImplicitConstraints_Vault(t *testing.T) {
  1757  	s1 := testServer(t, func(c *Config) {
  1758  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1759  	})
  1760  	defer s1.Shutdown()
  1761  	codec := rpcClient(t, s1)
  1762  	testutil.WaitForLeader(t, s1.RPC)
  1763  
  1764  	// Enable vault
  1765  	tr, f := true, false
  1766  	s1.config.VaultConfig.Enabled = &tr
  1767  	s1.config.VaultConfig.AllowUnauthenticated = &f
  1768  
  1769  	// Replace the Vault Client on the server
  1770  	tvc := &TestVaultClient{}
  1771  	s1.vault = tvc
  1772  
  1773  	policy := "foo"
  1774  	goodToken := structs.GenerateUUID()
  1775  	goodPolicies := []string{"foo", "bar", "baz"}
  1776  	tvc.SetLookupTokenAllowedPolicies(goodToken, goodPolicies)
  1777  
  1778  	// Create the register request with a job asking for a vault policy
  1779  	job := mock.Job()
  1780  	job.VaultToken = goodToken
  1781  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
  1782  		Policies:   []string{policy},
  1783  		ChangeMode: structs.VaultChangeModeRestart,
  1784  	}
  1785  	req := &structs.JobRegisterRequest{
  1786  		Job:          job,
  1787  		WriteRequest: structs.WriteRequest{Region: "global"},
  1788  	}
  1789  
  1790  	// Fetch the response
  1791  	var resp structs.JobRegisterResponse
  1792  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
  1793  		t.Fatalf("bad: %v", err)
  1794  	}
  1795  
  1796  	// Check for the job in the FSM
  1797  	state := s1.fsm.State()
  1798  	ws := memdb.NewWatchSet()
  1799  	out, err := state.JobByID(ws, job.ID)
  1800  	if err != nil {
  1801  		t.Fatalf("err: %v", err)
  1802  	}
  1803  	if out == nil {
  1804  		t.Fatalf("expected job")
  1805  	}
  1806  	if out.CreateIndex != resp.JobModifyIndex {
  1807  		t.Fatalf("index mis-match")
  1808  	}
  1809  
  1810  	// Check that there is an implicit vault constraint
  1811  	constraints := out.TaskGroups[0].Constraints
  1812  	if len(constraints) != 1 {
  1813  		t.Fatalf("Expected an implicit constraint")
  1814  	}
  1815  
  1816  	if !constraints[0].Equal(vaultConstraint) {
  1817  		t.Fatalf("Expected implicit vault constraint")
  1818  	}
  1819  }
  1820  
  1821  func TestJobEndpoint_ImplicitConstraints_Signals(t *testing.T) {
  1822  	s1 := testServer(t, func(c *Config) {
  1823  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1824  	})
  1825  	defer s1.Shutdown()
  1826  	codec := rpcClient(t, s1)
  1827  	testutil.WaitForLeader(t, s1.RPC)
  1828  
  1829  	// Create the register request with a job asking for a template that sends a
  1830  	// signal
  1831  	job := mock.Job()
  1832  	signal := "SIGUSR1"
  1833  	job.TaskGroups[0].Tasks[0].Templates = []*structs.Template{
  1834  		&structs.Template{
  1835  			SourcePath:   "foo",
  1836  			DestPath:     "bar",
  1837  			ChangeMode:   structs.TemplateChangeModeSignal,
  1838  			ChangeSignal: signal,
  1839  		},
  1840  	}
  1841  	req := &structs.JobRegisterRequest{
  1842  		Job:          job,
  1843  		WriteRequest: structs.WriteRequest{Region: "global"},
  1844  	}
  1845  
  1846  	// Fetch the response
  1847  	var resp structs.JobRegisterResponse
  1848  	if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
  1849  		t.Fatalf("bad: %v", err)
  1850  	}
  1851  
  1852  	// Check for the job in the FSM
  1853  	state := s1.fsm.State()
  1854  	ws := memdb.NewWatchSet()
  1855  	out, err := state.JobByID(ws, job.ID)
  1856  	if err != nil {
  1857  		t.Fatalf("err: %v", err)
  1858  	}
  1859  	if out == nil {
  1860  		t.Fatalf("expected job")
  1861  	}
  1862  	if out.CreateIndex != resp.JobModifyIndex {
  1863  		t.Fatalf("index mis-match")
  1864  	}
  1865  
  1866  	// Check that there is an implicit signal constraint
  1867  	constraints := out.TaskGroups[0].Constraints
  1868  	if len(constraints) != 1 {
  1869  		t.Fatalf("Expected an implicit constraint")
  1870  	}
  1871  
  1872  	sigConstraint := getSignalConstraint([]string{signal})
  1873  
  1874  	if !constraints[0].Equal(sigConstraint) {
  1875  		t.Fatalf("Expected implicit vault constraint")
  1876  	}
  1877  }
  1878  
  1879  func TestJobEndpoint_ValidateJob_InvalidDriverConf(t *testing.T) {
  1880  	// Create a mock job with an invalid config
  1881  	job := mock.Job()
  1882  	job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{
  1883  		"foo": "bar",
  1884  	}
  1885  
  1886  	if err := validateJob(job); err == nil || !strings.Contains(err.Error(), "-> config") {
  1887  		t.Fatalf("Expected config error; got %v", err)
  1888  	}
  1889  }
  1890  
  1891  func TestJobEndpoint_ValidateJob_InvalidSignals(t *testing.T) {
  1892  	// Create a mock job that wants to send a signal to a driver that can't
  1893  	job := mock.Job()
  1894  	job.TaskGroups[0].Tasks[0].Driver = "qemu"
  1895  	job.TaskGroups[0].Tasks[0].Vault = &structs.Vault{
  1896  		Policies:     []string{"foo"},
  1897  		ChangeMode:   structs.VaultChangeModeSignal,
  1898  		ChangeSignal: "SIGUSR1",
  1899  	}
  1900  
  1901  	if err := validateJob(job); err == nil || !strings.Contains(err.Error(), "support sending signals") {
  1902  		t.Fatalf("Expected signal feasibility error; got %v", err)
  1903  	}
  1904  }
  1905  
  1906  func TestJobEndpoint_Dispatch(t *testing.T) {
  1907  
  1908  	// No requirements
  1909  	d1 := mock.Job()
  1910  	d1.Type = structs.JobTypeBatch
  1911  	d1.ParameterizedJob = &structs.ParameterizedJobConfig{}
  1912  
  1913  	// Require input data
  1914  	d2 := mock.Job()
  1915  	d2.Type = structs.JobTypeBatch
  1916  	d2.ParameterizedJob = &structs.ParameterizedJobConfig{
  1917  		Payload: structs.DispatchPayloadRequired,
  1918  	}
  1919  
  1920  	// Disallow input data
  1921  	d3 := mock.Job()
  1922  	d3.Type = structs.JobTypeBatch
  1923  	d3.ParameterizedJob = &structs.ParameterizedJobConfig{
  1924  		Payload: structs.DispatchPayloadForbidden,
  1925  	}
  1926  
  1927  	// Require meta
  1928  	d4 := mock.Job()
  1929  	d4.Type = structs.JobTypeBatch
  1930  	d4.ParameterizedJob = &structs.ParameterizedJobConfig{
  1931  		MetaRequired: []string{"foo", "bar"},
  1932  	}
  1933  
  1934  	// Optional meta
  1935  	d5 := mock.Job()
  1936  	d5.Type = structs.JobTypeBatch
  1937  	d5.ParameterizedJob = &structs.ParameterizedJobConfig{
  1938  		MetaOptional: []string{"foo", "bar"},
  1939  	}
  1940  
  1941  	// Periodic dispatch job
  1942  	d6 := mock.PeriodicJob()
  1943  	d6.ParameterizedJob = &structs.ParameterizedJobConfig{}
  1944  
  1945  	reqNoInputNoMeta := &structs.JobDispatchRequest{}
  1946  	reqInputDataNoMeta := &structs.JobDispatchRequest{
  1947  		Payload: []byte("hello world"),
  1948  	}
  1949  	reqNoInputDataMeta := &structs.JobDispatchRequest{
  1950  		Meta: map[string]string{
  1951  			"foo": "f1",
  1952  			"bar": "f2",
  1953  		},
  1954  	}
  1955  	reqInputDataMeta := &structs.JobDispatchRequest{
  1956  		Payload: []byte("hello world"),
  1957  		Meta: map[string]string{
  1958  			"foo": "f1",
  1959  			"bar": "f2",
  1960  		},
  1961  	}
  1962  	reqBadMeta := &structs.JobDispatchRequest{
  1963  		Payload: []byte("hello world"),
  1964  		Meta: map[string]string{
  1965  			"foo": "f1",
  1966  			"bar": "f2",
  1967  			"baz": "f3",
  1968  		},
  1969  	}
  1970  	reqInputDataTooLarge := &structs.JobDispatchRequest{
  1971  		Payload: make([]byte, DispatchPayloadSizeLimit+100),
  1972  	}
  1973  
  1974  	type testCase struct {
  1975  		name             string
  1976  		parameterizedJob *structs.Job
  1977  		dispatchReq      *structs.JobDispatchRequest
  1978  		noEval           bool
  1979  		err              bool
  1980  		errStr           string
  1981  	}
  1982  	cases := []testCase{
  1983  		{
  1984  			name:             "optional input data w/ data",
  1985  			parameterizedJob: d1,
  1986  			dispatchReq:      reqInputDataNoMeta,
  1987  			err:              false,
  1988  		},
  1989  		{
  1990  			name:             "optional input data w/o data",
  1991  			parameterizedJob: d1,
  1992  			dispatchReq:      reqNoInputNoMeta,
  1993  			err:              false,
  1994  		},
  1995  		{
  1996  			name:             "require input data w/ data",
  1997  			parameterizedJob: d2,
  1998  			dispatchReq:      reqInputDataNoMeta,
  1999  			err:              false,
  2000  		},
  2001  		{
  2002  			name:             "require input data w/o data",
  2003  			parameterizedJob: d2,
  2004  			dispatchReq:      reqNoInputNoMeta,
  2005  			err:              true,
  2006  			errStr:           "not provided but required",
  2007  		},
  2008  		{
  2009  			name:             "disallow input data w/o data",
  2010  			parameterizedJob: d3,
  2011  			dispatchReq:      reqNoInputNoMeta,
  2012  			err:              false,
  2013  		},
  2014  		{
  2015  			name:             "disallow input data w/ data",
  2016  			parameterizedJob: d3,
  2017  			dispatchReq:      reqInputDataNoMeta,
  2018  			err:              true,
  2019  			errStr:           "provided but forbidden",
  2020  		},
  2021  		{
  2022  			name:             "require meta w/ meta",
  2023  			parameterizedJob: d4,
  2024  			dispatchReq:      reqInputDataMeta,
  2025  			err:              false,
  2026  		},
  2027  		{
  2028  			name:             "require meta w/o meta",
  2029  			parameterizedJob: d4,
  2030  			dispatchReq:      reqNoInputNoMeta,
  2031  			err:              true,
  2032  			errStr:           "did not provide required meta keys",
  2033  		},
  2034  		{
  2035  			name:             "optional meta w/ meta",
  2036  			parameterizedJob: d5,
  2037  			dispatchReq:      reqNoInputDataMeta,
  2038  			err:              false,
  2039  		},
  2040  		{
  2041  			name:             "optional meta w/o meta",
  2042  			parameterizedJob: d5,
  2043  			dispatchReq:      reqNoInputNoMeta,
  2044  			err:              false,
  2045  		},
  2046  		{
  2047  			name:             "optional meta w/ bad meta",
  2048  			parameterizedJob: d5,
  2049  			dispatchReq:      reqBadMeta,
  2050  			err:              true,
  2051  			errStr:           "unpermitted metadata keys",
  2052  		},
  2053  		{
  2054  			name:             "optional input w/ too big of input",
  2055  			parameterizedJob: d1,
  2056  			dispatchReq:      reqInputDataTooLarge,
  2057  			err:              true,
  2058  			errStr:           "Payload exceeds maximum size",
  2059  		},
  2060  		{
  2061  			name:             "periodic job dispatched, ensure no eval",
  2062  			parameterizedJob: d6,
  2063  			dispatchReq:      reqNoInputNoMeta,
  2064  			noEval:           true,
  2065  		},
  2066  	}
  2067  
  2068  	for _, tc := range cases {
  2069  		t.Run(tc.name, func(t *testing.T) {
  2070  			s1 := testServer(t, func(c *Config) {
  2071  				c.NumSchedulers = 0 // Prevent automatic dequeue
  2072  			})
  2073  			defer s1.Shutdown()
  2074  			codec := rpcClient(t, s1)
  2075  			testutil.WaitForLeader(t, s1.RPC)
  2076  
  2077  			// Create the register request
  2078  			regReq := &structs.JobRegisterRequest{
  2079  				Job:          tc.parameterizedJob,
  2080  				WriteRequest: structs.WriteRequest{Region: "global"},
  2081  			}
  2082  
  2083  			// Fetch the response
  2084  			var regResp structs.JobRegisterResponse
  2085  			if err := msgpackrpc.CallWithCodec(codec, "Job.Register", regReq, &regResp); err != nil {
  2086  				t.Fatalf("err: %v", err)
  2087  			}
  2088  
  2089  			// Now try to dispatch
  2090  			tc.dispatchReq.JobID = tc.parameterizedJob.ID
  2091  			tc.dispatchReq.WriteRequest = structs.WriteRequest{Region: "global"}
  2092  
  2093  			var dispatchResp structs.JobDispatchResponse
  2094  			dispatchErr := msgpackrpc.CallWithCodec(codec, "Job.Dispatch", tc.dispatchReq, &dispatchResp)
  2095  
  2096  			if dispatchErr == nil {
  2097  				if tc.err {
  2098  					t.Fatalf("Expected error: %v", dispatchErr)
  2099  				}
  2100  
  2101  				// Check that we got an eval and job id back
  2102  				switch dispatchResp.EvalID {
  2103  				case "":
  2104  					if !tc.noEval {
  2105  						t.Fatalf("Bad response")
  2106  					}
  2107  				default:
  2108  					if tc.noEval {
  2109  						t.Fatalf("Got eval %q", dispatchResp.EvalID)
  2110  					}
  2111  				}
  2112  
  2113  				if dispatchResp.DispatchedJobID == "" {
  2114  					t.Fatalf("Bad response")
  2115  				}
  2116  
  2117  				state := s1.fsm.State()
  2118  				ws := memdb.NewWatchSet()
  2119  				out, err := state.JobByID(ws, dispatchResp.DispatchedJobID)
  2120  				if err != nil {
  2121  					t.Fatalf("err: %v", err)
  2122  				}
  2123  				if out == nil {
  2124  					t.Fatalf("expected job")
  2125  				}
  2126  				if out.CreateIndex != dispatchResp.JobCreateIndex {
  2127  					t.Fatalf("index mis-match")
  2128  				}
  2129  				if out.ParentID != tc.parameterizedJob.ID {
  2130  					t.Fatalf("bad parent ID")
  2131  				}
  2132  
  2133  				if tc.noEval {
  2134  					return
  2135  				}
  2136  
  2137  				// Lookup the evaluation
  2138  				eval, err := state.EvalByID(ws, dispatchResp.EvalID)
  2139  				if err != nil {
  2140  					t.Fatalf("err: %v", err)
  2141  				}
  2142  
  2143  				if eval == nil {
  2144  					t.Fatalf("expected eval")
  2145  				}
  2146  				if eval.CreateIndex != dispatchResp.EvalCreateIndex {
  2147  					t.Fatalf("index mis-match")
  2148  				}
  2149  			} else {
  2150  				if !tc.err {
  2151  					t.Fatalf("Got unexpected error: %v", dispatchErr)
  2152  				} else if !strings.Contains(dispatchErr.Error(), tc.errStr) {
  2153  					t.Fatalf("Expected err to include %q; got %v", tc.errStr, dispatchErr)
  2154  				}
  2155  			}
  2156  		})
  2157  	}
  2158  }