github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/nomad/deployment_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	memdb "github.com/hashicorp/go-memdb"
     8  	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
     9  	"github.com/hashicorp/nomad/helper"
    10  	"github.com/hashicorp/nomad/nomad/mock"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  func TestDeploymentEndpoint_GetDeployment(t *testing.T) {
    17  	t.Parallel()
    18  	s1 := testServer(t, nil)
    19  	defer s1.Shutdown()
    20  	codec := rpcClient(t, s1)
    21  	testutil.WaitForLeader(t, s1.RPC)
    22  	assert := assert.New(t)
    23  
    24  	// Create the deployment
    25  	j := mock.Job()
    26  	d := mock.Deployment()
    27  	d.JobID = j.ID
    28  	state := s1.fsm.State()
    29  
    30  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
    31  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
    32  
    33  	// Lookup the deployments
    34  	get := &structs.DeploymentSpecificRequest{
    35  		DeploymentID: d.ID,
    36  		QueryOptions: structs.QueryOptions{Region: "global"},
    37  	}
    38  	var resp structs.SingleDeploymentResponse
    39  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC")
    40  	assert.EqualValues(resp.Index, 1000, "resp.Index")
    41  	assert.Equal(d, resp.Deployment, "Returned deployment not equal")
    42  }
    43  
    44  func TestDeploymentEndpoint_GetDeployment_Blocking(t *testing.T) {
    45  	t.Parallel()
    46  	s1 := testServer(t, nil)
    47  	defer s1.Shutdown()
    48  	codec := rpcClient(t, s1)
    49  	testutil.WaitForLeader(t, s1.RPC)
    50  	state := s1.fsm.State()
    51  	assert := assert.New(t)
    52  
    53  	// Create the deployments
    54  	j1 := mock.Job()
    55  	j2 := mock.Job()
    56  	d1 := mock.Deployment()
    57  	d1.JobID = j1.ID
    58  	d2 := mock.Deployment()
    59  	d2.JobID = j2.ID
    60  
    61  	assert.Nil(state.UpsertJob(98, j1), "UpsertJob")
    62  	assert.Nil(state.UpsertJob(99, j2), "UpsertJob")
    63  
    64  	// Upsert a deployment we are not interested in first.
    65  	time.AfterFunc(100*time.Millisecond, func() {
    66  		assert.Nil(state.UpsertDeployment(100, d1), "UpsertDeployment")
    67  	})
    68  
    69  	// Upsert another deployment later which should trigger the watch.
    70  	time.AfterFunc(200*time.Millisecond, func() {
    71  		assert.Nil(state.UpsertDeployment(200, d2), "UpsertDeployment")
    72  	})
    73  
    74  	// Lookup the deployments
    75  	get := &structs.DeploymentSpecificRequest{
    76  		DeploymentID: d2.ID,
    77  		QueryOptions: structs.QueryOptions{
    78  			Region:        "global",
    79  			MinQueryIndex: 150,
    80  		},
    81  	}
    82  	start := time.Now()
    83  	var resp structs.SingleDeploymentResponse
    84  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.GetDeployment", get, &resp), "RPC")
    85  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
    86  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
    87  	}
    88  	assert.EqualValues(resp.Index, 200, "resp.Index")
    89  	assert.Equal(d2, resp.Deployment, "deployments equal")
    90  }
    91  
    92  func TestDeploymentEndpoint_Fail(t *testing.T) {
    93  	t.Parallel()
    94  	s1 := testServer(t, func(c *Config) {
    95  		c.NumSchedulers = 0 // Prevent automatic dequeue
    96  	})
    97  	defer s1.Shutdown()
    98  	codec := rpcClient(t, s1)
    99  	testutil.WaitForLeader(t, s1.RPC)
   100  	assert := assert.New(t)
   101  
   102  	// Create the deployment
   103  	j := mock.Job()
   104  	d := mock.Deployment()
   105  	d.JobID = j.ID
   106  	state := s1.fsm.State()
   107  
   108  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   109  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   110  
   111  	// Mark the deployment as failed
   112  	req := &structs.DeploymentFailRequest{
   113  		DeploymentID: d.ID,
   114  		WriteRequest: structs.WriteRequest{Region: "global"},
   115  	}
   116  
   117  	// Fetch the response
   118  	var resp structs.DeploymentUpdateResponse
   119  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Fail", req, &resp), "RPC")
   120  	assert.NotEqual(resp.Index, uint64(0), "bad response index")
   121  
   122  	// Lookup the evaluation
   123  	ws := memdb.NewWatchSet()
   124  	eval, err := state.EvalByID(ws, resp.EvalID)
   125  	assert.Nil(err, "EvalByID failed")
   126  	assert.NotNil(eval, "Expect eval")
   127  	assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch")
   128  	assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger")
   129  	assert.Equal(eval.JobID, d.JobID, "eval job id")
   130  	assert.Equal(eval.DeploymentID, d.ID, "eval deployment id")
   131  	assert.Equal(eval.Status, structs.EvalStatusPending, "eval status")
   132  
   133  	// Lookup the deployment
   134  	dout, err := state.DeploymentByID(ws, d.ID)
   135  	assert.Nil(err, "DeploymentByID failed")
   136  	assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status")
   137  	assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionFailedByUser, "wrong status description")
   138  	assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index")
   139  }
   140  
   141  func TestDeploymentEndpoint_Fail_Rollback(t *testing.T) {
   142  	t.Parallel()
   143  	s1 := testServer(t, func(c *Config) {
   144  		c.NumSchedulers = 0 // Prevent automatic dequeue
   145  	})
   146  	defer s1.Shutdown()
   147  	codec := rpcClient(t, s1)
   148  	testutil.WaitForLeader(t, s1.RPC)
   149  	assert := assert.New(t)
   150  	state := s1.fsm.State()
   151  
   152  	// Create the original job
   153  	j := mock.Job()
   154  	j.Stable = true
   155  	j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy()
   156  	j.TaskGroups[0].Update.MaxParallel = 2
   157  	j.TaskGroups[0].Update.AutoRevert = true
   158  	assert.Nil(state.UpsertJob(998, j), "UpsertJob")
   159  
   160  	// Create the second job, deployment and alloc
   161  	j2 := j.Copy()
   162  	j2.Stable = false
   163  
   164  	d := mock.Deployment()
   165  	d.TaskGroups["web"].AutoRevert = true
   166  	d.JobID = j2.ID
   167  	d.JobVersion = j2.Version
   168  
   169  	a := mock.Alloc()
   170  	a.JobID = j.ID
   171  	a.DeploymentID = d.ID
   172  
   173  	assert.Nil(state.UpsertJob(999, j2), "UpsertJob")
   174  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   175  	assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs")
   176  
   177  	// Mark the deployment as failed
   178  	req := &structs.DeploymentFailRequest{
   179  		DeploymentID: d.ID,
   180  		WriteRequest: structs.WriteRequest{Region: "global"},
   181  	}
   182  
   183  	// Fetch the response
   184  	var resp structs.DeploymentUpdateResponse
   185  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Fail", req, &resp), "RPC")
   186  	assert.NotEqual(resp.Index, uint64(0), "bad response index")
   187  	assert.NotNil(resp.RevertedJobVersion, "bad revert version")
   188  	assert.EqualValues(0, *resp.RevertedJobVersion, "bad revert version")
   189  
   190  	// Lookup the evaluation
   191  	ws := memdb.NewWatchSet()
   192  	eval, err := state.EvalByID(ws, resp.EvalID)
   193  	assert.Nil(err, "EvalByID failed")
   194  	assert.NotNil(eval, "Expect eval")
   195  	assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch")
   196  	assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger")
   197  	assert.Equal(eval.JobID, d.JobID, "eval job id")
   198  	assert.Equal(eval.DeploymentID, d.ID, "eval deployment id")
   199  	assert.Equal(eval.Status, structs.EvalStatusPending, "eval status")
   200  
   201  	// Lookup the deployment
   202  	expectedDesc := structs.DeploymentStatusDescriptionRollback(structs.DeploymentStatusDescriptionFailedByUser, 0)
   203  	dout, err := state.DeploymentByID(ws, d.ID)
   204  	assert.Nil(err, "DeploymentByID failed")
   205  	assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status")
   206  	assert.Equal(dout.StatusDescription, expectedDesc, "wrong status description")
   207  	assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index")
   208  
   209  	// Lookup the job
   210  	jout, err := state.JobByID(ws, j.ID)
   211  	assert.Nil(err, "JobByID")
   212  	assert.NotNil(jout, "job")
   213  	assert.EqualValues(2, jout.Version, "reverted job version")
   214  }
   215  
   216  func TestDeploymentEndpoint_Pause(t *testing.T) {
   217  	t.Parallel()
   218  	s1 := testServer(t, func(c *Config) {
   219  		c.NumSchedulers = 0 // Prevent automatic dequeue
   220  	})
   221  	defer s1.Shutdown()
   222  	codec := rpcClient(t, s1)
   223  	testutil.WaitForLeader(t, s1.RPC)
   224  	assert := assert.New(t)
   225  
   226  	// Create the deployment
   227  	j := mock.Job()
   228  	d := mock.Deployment()
   229  	d.JobID = j.ID
   230  	state := s1.fsm.State()
   231  
   232  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   233  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   234  
   235  	// Mark the deployment as failed
   236  	req := &structs.DeploymentPauseRequest{
   237  		DeploymentID: d.ID,
   238  		Pause:        true,
   239  		WriteRequest: structs.WriteRequest{Region: "global"},
   240  	}
   241  
   242  	// Fetch the response
   243  	var resp structs.DeploymentUpdateResponse
   244  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Pause", req, &resp), "RPC")
   245  	assert.NotEqual(resp.Index, uint64(0), "bad response index")
   246  	assert.Zero(resp.EvalCreateIndex, "Shouldn't create eval")
   247  	assert.Zero(resp.EvalID, "Shouldn't create eval")
   248  
   249  	// Lookup the deployment
   250  	ws := memdb.NewWatchSet()
   251  	dout, err := state.DeploymentByID(ws, d.ID)
   252  	assert.Nil(err, "DeploymentByID failed")
   253  	assert.Equal(dout.Status, structs.DeploymentStatusPaused, "wrong status")
   254  	assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionPaused, "wrong status description")
   255  	assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index")
   256  }
   257  
   258  func TestDeploymentEndpoint_Promote(t *testing.T) {
   259  	t.Parallel()
   260  	s1 := testServer(t, func(c *Config) {
   261  		c.NumSchedulers = 0 // Prevent automatic dequeue
   262  	})
   263  	defer s1.Shutdown()
   264  	codec := rpcClient(t, s1)
   265  	testutil.WaitForLeader(t, s1.RPC)
   266  	assert := assert.New(t)
   267  
   268  	// Create the deployment, job and canary
   269  	j := mock.Job()
   270  	j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy()
   271  	j.TaskGroups[0].Update.MaxParallel = 2
   272  	j.TaskGroups[0].Update.Canary = 2
   273  	d := mock.Deployment()
   274  	d.TaskGroups["web"].DesiredCanaries = 2
   275  	d.JobID = j.ID
   276  	a := mock.Alloc()
   277  	d.TaskGroups[a.TaskGroup].PlacedCanaries = []string{a.ID}
   278  	a.DeploymentID = d.ID
   279  	a.DeploymentStatus = &structs.AllocDeploymentStatus{
   280  		Healthy: helper.BoolToPtr(true),
   281  	}
   282  
   283  	state := s1.fsm.State()
   284  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   285  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   286  	assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs")
   287  
   288  	// Promote the deployment
   289  	req := &structs.DeploymentPromoteRequest{
   290  		DeploymentID: d.ID,
   291  		All:          true,
   292  		WriteRequest: structs.WriteRequest{Region: "global"},
   293  	}
   294  
   295  	// Fetch the response
   296  	var resp structs.DeploymentUpdateResponse
   297  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Promote", req, &resp), "RPC")
   298  	assert.NotEqual(resp.Index, uint64(0), "bad response index")
   299  
   300  	// Lookup the evaluation
   301  	ws := memdb.NewWatchSet()
   302  	eval, err := state.EvalByID(ws, resp.EvalID)
   303  	assert.Nil(err, "EvalByID failed")
   304  	assert.NotNil(eval, "Expect eval")
   305  	assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch")
   306  	assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger")
   307  	assert.Equal(eval.JobID, d.JobID, "eval job id")
   308  	assert.Equal(eval.DeploymentID, d.ID, "eval deployment id")
   309  	assert.Equal(eval.Status, structs.EvalStatusPending, "eval status")
   310  
   311  	// Lookup the deployment
   312  	dout, err := state.DeploymentByID(ws, d.ID)
   313  	assert.Nil(err, "DeploymentByID failed")
   314  	assert.Equal(dout.Status, structs.DeploymentStatusRunning, "wrong status")
   315  	assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionRunning, "wrong status description")
   316  	assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index")
   317  	assert.Len(dout.TaskGroups, 1, "should have one group")
   318  	assert.Contains(dout.TaskGroups, "web", "should have web group")
   319  	assert.True(dout.TaskGroups["web"].Promoted, "web group should be promoted")
   320  }
   321  
   322  func TestDeploymentEndpoint_SetAllocHealth(t *testing.T) {
   323  	t.Parallel()
   324  	s1 := testServer(t, func(c *Config) {
   325  		c.NumSchedulers = 0 // Prevent automatic dequeue
   326  	})
   327  	defer s1.Shutdown()
   328  	codec := rpcClient(t, s1)
   329  	testutil.WaitForLeader(t, s1.RPC)
   330  	assert := assert.New(t)
   331  
   332  	// Create the deployment, job and canary
   333  	j := mock.Job()
   334  	j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy()
   335  	j.TaskGroups[0].Update.MaxParallel = 2
   336  	d := mock.Deployment()
   337  	d.JobID = j.ID
   338  	a := mock.Alloc()
   339  	a.JobID = j.ID
   340  	a.DeploymentID = d.ID
   341  
   342  	state := s1.fsm.State()
   343  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   344  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   345  	assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs")
   346  
   347  	// Set the alloc as healthy
   348  	req := &structs.DeploymentAllocHealthRequest{
   349  		DeploymentID:         d.ID,
   350  		HealthyAllocationIDs: []string{a.ID},
   351  		WriteRequest:         structs.WriteRequest{Region: "global"},
   352  	}
   353  
   354  	// Fetch the response
   355  	var resp structs.DeploymentUpdateResponse
   356  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.SetAllocHealth", req, &resp), "RPC")
   357  	assert.NotZero(resp.Index, "bad response index")
   358  
   359  	// Lookup the evaluation
   360  	ws := memdb.NewWatchSet()
   361  	eval, err := state.EvalByID(ws, resp.EvalID)
   362  	assert.Nil(err, "EvalByID failed")
   363  	assert.NotNil(eval, "Expect eval")
   364  	assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch")
   365  	assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger")
   366  	assert.Equal(eval.JobID, d.JobID, "eval job id")
   367  	assert.Equal(eval.DeploymentID, d.ID, "eval deployment id")
   368  	assert.Equal(eval.Status, structs.EvalStatusPending, "eval status")
   369  
   370  	// Lookup the deployment
   371  	dout, err := state.DeploymentByID(ws, d.ID)
   372  	assert.Nil(err, "DeploymentByID failed")
   373  	assert.Equal(dout.Status, structs.DeploymentStatusRunning, "wrong status")
   374  	assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionRunning, "wrong status description")
   375  	assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index")
   376  	assert.Len(dout.TaskGroups, 1, "should have one group")
   377  	assert.Contains(dout.TaskGroups, "web", "should have web group")
   378  	assert.Equal(1, dout.TaskGroups["web"].HealthyAllocs, "should have one healthy")
   379  
   380  	// Lookup the allocation
   381  	aout, err := state.AllocByID(ws, a.ID)
   382  	assert.Nil(err, "AllocByID")
   383  	assert.NotNil(aout, "alloc")
   384  	assert.NotNil(aout.DeploymentStatus, "alloc deployment status")
   385  	assert.NotNil(aout.DeploymentStatus.Healthy, "alloc deployment healthy")
   386  	assert.True(*aout.DeploymentStatus.Healthy, "alloc deployment healthy")
   387  }
   388  
   389  func TestDeploymentEndpoint_SetAllocHealth_Rollback(t *testing.T) {
   390  	t.Parallel()
   391  	s1 := testServer(t, func(c *Config) {
   392  		c.NumSchedulers = 0 // Prevent automatic dequeue
   393  	})
   394  	defer s1.Shutdown()
   395  	codec := rpcClient(t, s1)
   396  	testutil.WaitForLeader(t, s1.RPC)
   397  	assert := assert.New(t)
   398  	state := s1.fsm.State()
   399  
   400  	// Create the original job
   401  	j := mock.Job()
   402  	j.Stable = true
   403  	j.TaskGroups[0].Update = structs.DefaultUpdateStrategy.Copy()
   404  	j.TaskGroups[0].Update.MaxParallel = 2
   405  	j.TaskGroups[0].Update.AutoRevert = true
   406  	assert.Nil(state.UpsertJob(998, j), "UpsertJob")
   407  
   408  	// Create the second job, deployment and alloc
   409  	j2 := j.Copy()
   410  	j2.Stable = false
   411  
   412  	d := mock.Deployment()
   413  	d.TaskGroups["web"].AutoRevert = true
   414  	d.JobID = j2.ID
   415  	d.JobVersion = j2.Version
   416  
   417  	a := mock.Alloc()
   418  	a.JobID = j.ID
   419  	a.DeploymentID = d.ID
   420  
   421  	assert.Nil(state.UpsertJob(999, j2), "UpsertJob")
   422  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   423  	assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs")
   424  
   425  	// Set the alloc as unhealthy
   426  	req := &structs.DeploymentAllocHealthRequest{
   427  		DeploymentID:           d.ID,
   428  		UnhealthyAllocationIDs: []string{a.ID},
   429  		WriteRequest:           structs.WriteRequest{Region: "global"},
   430  	}
   431  
   432  	// Fetch the response
   433  	var resp structs.DeploymentUpdateResponse
   434  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.SetAllocHealth", req, &resp), "RPC")
   435  	assert.NotZero(resp.Index, "bad response index")
   436  	assert.NotNil(resp.RevertedJobVersion, "bad revert version")
   437  	assert.EqualValues(0, *resp.RevertedJobVersion, "bad revert version")
   438  
   439  	// Lookup the evaluation
   440  	ws := memdb.NewWatchSet()
   441  	eval, err := state.EvalByID(ws, resp.EvalID)
   442  	assert.Nil(err, "EvalByID failed")
   443  	assert.NotNil(eval, "Expect eval")
   444  	assert.Equal(eval.CreateIndex, resp.EvalCreateIndex, "eval index mismatch")
   445  	assert.Equal(eval.TriggeredBy, structs.EvalTriggerDeploymentWatcher, "eval trigger")
   446  	assert.Equal(eval.JobID, d.JobID, "eval job id")
   447  	assert.Equal(eval.DeploymentID, d.ID, "eval deployment id")
   448  	assert.Equal(eval.Status, structs.EvalStatusPending, "eval status")
   449  
   450  	// Lookup the deployment
   451  	expectedDesc := structs.DeploymentStatusDescriptionRollback(structs.DeploymentStatusDescriptionFailedAllocations, 0)
   452  	dout, err := state.DeploymentByID(ws, d.ID)
   453  	assert.Nil(err, "DeploymentByID failed")
   454  	assert.Equal(dout.Status, structs.DeploymentStatusFailed, "wrong status")
   455  	assert.Equal(dout.StatusDescription, expectedDesc, "wrong status description")
   456  	assert.Equal(resp.DeploymentModifyIndex, dout.ModifyIndex, "wrong modify index")
   457  	assert.Len(dout.TaskGroups, 1, "should have one group")
   458  	assert.Contains(dout.TaskGroups, "web", "should have web group")
   459  	assert.Equal(1, dout.TaskGroups["web"].UnhealthyAllocs, "should have one healthy")
   460  
   461  	// Lookup the allocation
   462  	aout, err := state.AllocByID(ws, a.ID)
   463  	assert.Nil(err, "AllocByID")
   464  	assert.NotNil(aout, "alloc")
   465  	assert.NotNil(aout.DeploymentStatus, "alloc deployment status")
   466  	assert.NotNil(aout.DeploymentStatus.Healthy, "alloc deployment healthy")
   467  	assert.False(*aout.DeploymentStatus.Healthy, "alloc deployment healthy")
   468  
   469  	// Lookup the job
   470  	jout, err := state.JobByID(ws, j.ID)
   471  	assert.Nil(err, "JobByID")
   472  	assert.NotNil(jout, "job")
   473  	assert.EqualValues(2, jout.Version, "reverted job version")
   474  }
   475  
   476  func TestDeploymentEndpoint_List(t *testing.T) {
   477  	t.Parallel()
   478  	s1 := testServer(t, nil)
   479  	defer s1.Shutdown()
   480  	codec := rpcClient(t, s1)
   481  	testutil.WaitForLeader(t, s1.RPC)
   482  	assert := assert.New(t)
   483  
   484  	// Create the register request
   485  	j := mock.Job()
   486  	d := mock.Deployment()
   487  	d.JobID = j.ID
   488  	state := s1.fsm.State()
   489  
   490  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   491  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   492  
   493  	// Lookup the deployments
   494  	get := &structs.DeploymentListRequest{
   495  		QueryOptions: structs.QueryOptions{Region: "global"},
   496  	}
   497  	var resp structs.DeploymentListResponse
   498  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp), "RPC")
   499  	assert.EqualValues(resp.Index, 1000, "Wrong Index")
   500  	assert.Len(resp.Deployments, 1, "Deployments")
   501  	assert.Equal(resp.Deployments[0].ID, d.ID, "Deployment ID")
   502  
   503  	// Lookup the deploys by prefix
   504  	get = &structs.DeploymentListRequest{
   505  		QueryOptions: structs.QueryOptions{Region: "global", Prefix: d.ID[:4]},
   506  	}
   507  
   508  	var resp2 structs.DeploymentListResponse
   509  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp2), "RPC")
   510  	assert.EqualValues(resp.Index, 1000, "Wrong Index")
   511  	assert.Len(resp2.Deployments, 1, "Deployments")
   512  	assert.Equal(resp2.Deployments[0].ID, d.ID, "Deployment ID")
   513  }
   514  
   515  func TestDeploymentEndpoint_List_Blocking(t *testing.T) {
   516  	t.Parallel()
   517  	s1 := testServer(t, nil)
   518  	defer s1.Shutdown()
   519  	state := s1.fsm.State()
   520  	codec := rpcClient(t, s1)
   521  	testutil.WaitForLeader(t, s1.RPC)
   522  	assert := assert.New(t)
   523  
   524  	// Create the deployment
   525  	j := mock.Job()
   526  	d := mock.Deployment()
   527  	d.JobID = j.ID
   528  
   529  	assert.Nil(state.UpsertJob(999, j), "UpsertJob")
   530  
   531  	// Upsert alloc triggers watches
   532  	time.AfterFunc(100*time.Millisecond, func() {
   533  		assert.Nil(state.UpsertDeployment(3, d), "UpsertDeployment")
   534  	})
   535  
   536  	req := &structs.DeploymentListRequest{
   537  		QueryOptions: structs.QueryOptions{
   538  			Region:        "global",
   539  			MinQueryIndex: 1,
   540  		},
   541  	}
   542  	start := time.Now()
   543  	var resp structs.DeploymentListResponse
   544  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", req, &resp), "RPC")
   545  	assert.EqualValues(resp.Index, 3, "Wrong Index")
   546  	assert.Len(resp.Deployments, 1, "Deployments")
   547  	assert.Equal(resp.Deployments[0].ID, d.ID, "Deployment ID")
   548  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   549  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   550  	}
   551  
   552  	// Deployment updates trigger watches
   553  	d2 := d.Copy()
   554  	d2.Status = structs.DeploymentStatusPaused
   555  	time.AfterFunc(100*time.Millisecond, func() {
   556  		assert.Nil(state.UpsertDeployment(5, d2), "UpsertDeployment")
   557  	})
   558  
   559  	req.MinQueryIndex = 3
   560  	start = time.Now()
   561  	var resp2 structs.DeploymentListResponse
   562  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", req, &resp2), "RPC")
   563  	assert.EqualValues(5, resp2.Index, "Wrong Index")
   564  	assert.Len(resp2.Deployments, 1, "Deployments")
   565  	assert.Equal(d2.ID, resp2.Deployments[0].ID, "Deployment ID")
   566  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   567  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   568  	}
   569  }
   570  
   571  func TestDeploymentEndpoint_Allocations(t *testing.T) {
   572  	t.Parallel()
   573  	s1 := testServer(t, nil)
   574  	defer s1.Shutdown()
   575  	codec := rpcClient(t, s1)
   576  	testutil.WaitForLeader(t, s1.RPC)
   577  	assert := assert.New(t)
   578  
   579  	// Create the register request
   580  	j := mock.Job()
   581  	d := mock.Deployment()
   582  	d.JobID = j.ID
   583  	a := mock.Alloc()
   584  	a.DeploymentID = d.ID
   585  	summary := mock.JobSummary(a.JobID)
   586  	state := s1.fsm.State()
   587  
   588  	assert.Nil(state.UpsertJob(998, j), "UpsertJob")
   589  	assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary")
   590  	assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
   591  	assert.Nil(state.UpsertAllocs(1001, []*structs.Allocation{a}), "UpsertAllocs")
   592  
   593  	// Lookup the allocations
   594  	get := &structs.DeploymentSpecificRequest{
   595  		DeploymentID: d.ID,
   596  		QueryOptions: structs.QueryOptions{Region: "global"},
   597  	}
   598  	var resp structs.AllocListResponse
   599  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", get, &resp), "RPC")
   600  	assert.EqualValues(1001, resp.Index, "Wrong Index")
   601  	assert.Len(resp.Allocations, 1, "Allocations")
   602  	assert.Equal(a.ID, resp.Allocations[0].ID, "Allocation ID")
   603  }
   604  
   605  func TestDeploymentEndpoint_Allocations_Blocking(t *testing.T) {
   606  	t.Parallel()
   607  	s1 := testServer(t, nil)
   608  	defer s1.Shutdown()
   609  	state := s1.fsm.State()
   610  	codec := rpcClient(t, s1)
   611  	testutil.WaitForLeader(t, s1.RPC)
   612  	assert := assert.New(t)
   613  
   614  	// Create the alloc
   615  	j := mock.Job()
   616  	d := mock.Deployment()
   617  	d.JobID = j.ID
   618  	a := mock.Alloc()
   619  	a.DeploymentID = d.ID
   620  	summary := mock.JobSummary(a.JobID)
   621  
   622  	assert.Nil(state.UpsertJob(1, j), "UpsertJob")
   623  	assert.Nil(state.UpsertDeployment(2, d), "UpsertDeployment")
   624  	assert.Nil(state.UpsertJobSummary(3, summary), "UpsertJobSummary")
   625  
   626  	// Upsert alloc triggers watches
   627  	time.AfterFunc(100*time.Millisecond, func() {
   628  		assert.Nil(state.UpsertAllocs(4, []*structs.Allocation{a}), "UpsertAllocs")
   629  	})
   630  
   631  	req := &structs.DeploymentSpecificRequest{
   632  		DeploymentID: d.ID,
   633  		QueryOptions: structs.QueryOptions{
   634  			Region:        "global",
   635  			MinQueryIndex: 1,
   636  		},
   637  	}
   638  	start := time.Now()
   639  	var resp structs.AllocListResponse
   640  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", req, &resp), "RPC")
   641  	assert.EqualValues(4, resp.Index, "Wrong Index")
   642  	assert.Len(resp.Allocations, 1, "Allocations")
   643  	assert.Equal(a.ID, resp.Allocations[0].ID, "Allocation ID")
   644  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   645  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   646  	}
   647  
   648  	// Client updates trigger watches
   649  	a2 := mock.Alloc()
   650  	a2.ID = a.ID
   651  	a2.DeploymentID = a.DeploymentID
   652  	a2.ClientStatus = structs.AllocClientStatusRunning
   653  	time.AfterFunc(100*time.Millisecond, func() {
   654  		assert.Nil(state.UpsertJobSummary(5, mock.JobSummary(a2.JobID)), "UpsertJobSummary")
   655  		assert.Nil(state.UpdateAllocsFromClient(6, []*structs.Allocation{a2}), "bpdateAllocsFromClient")
   656  	})
   657  
   658  	req.MinQueryIndex = 4
   659  	start = time.Now()
   660  	var resp2 structs.AllocListResponse
   661  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Allocations", req, &resp2), "RPC")
   662  	assert.EqualValues(6, resp2.Index, "Wrong Index")
   663  	assert.Len(resp2.Allocations, 1, "Allocations")
   664  	assert.Equal(a.ID, resp2.Allocations[0].ID, "Allocation ID")
   665  	assert.Equal(structs.AllocClientStatusRunning, resp2.Allocations[0].ClientStatus, "Client Status")
   666  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   667  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   668  	}
   669  }
   670  
   671  func TestDeploymentEndpoint_Reap(t *testing.T) {
   672  	t.Parallel()
   673  	s1 := testServer(t, nil)
   674  	defer s1.Shutdown()
   675  	codec := rpcClient(t, s1)
   676  	testutil.WaitForLeader(t, s1.RPC)
   677  	assert := assert.New(t)
   678  
   679  	// Create the register request
   680  	d1 := mock.Deployment()
   681  	assert.Nil(s1.fsm.State().UpsertDeployment(1000, d1), "UpsertDeployment")
   682  
   683  	// Reap the eval
   684  	get := &structs.DeploymentDeleteRequest{
   685  		Deployments:  []string{d1.ID},
   686  		WriteRequest: structs.WriteRequest{Region: "global"},
   687  	}
   688  	var resp structs.GenericResponse
   689  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Reap", get, &resp), "RPC")
   690  	assert.NotZero(resp.Index, "bad response index")
   691  
   692  	// Ensure deleted
   693  	ws := memdb.NewWatchSet()
   694  	outD, err := s1.fsm.State().DeploymentByID(ws, d1.ID)
   695  	assert.Nil(err, "DeploymentByID")
   696  	assert.Nil(outD, "Deleted Deployment")
   697  }