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

     1  package nomad
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  
    11  	memdb "github.com/hashicorp/go-memdb"
    12  	"github.com/hashicorp/nomad/helper"
    13  	"github.com/hashicorp/nomad/nomad/mock"
    14  	"github.com/hashicorp/nomad/nomad/state"
    15  	"github.com/hashicorp/nomad/nomad/structs"
    16  	"github.com/hashicorp/nomad/testutil"
    17  	"github.com/hashicorp/raft"
    18  	"github.com/kr/pretty"
    19  )
    20  
    21  type MockSink struct {
    22  	*bytes.Buffer
    23  	cancel bool
    24  }
    25  
    26  func (m *MockSink) ID() string {
    27  	return "Mock"
    28  }
    29  
    30  func (m *MockSink) Cancel() error {
    31  	m.cancel = true
    32  	return nil
    33  }
    34  
    35  func (m *MockSink) Close() error {
    36  	return nil
    37  }
    38  
    39  func testStateStore(t *testing.T) *state.StateStore {
    40  	state, err := state.NewStateStore(os.Stderr)
    41  	if err != nil {
    42  		t.Fatalf("err: %v", err)
    43  	}
    44  	if state == nil {
    45  		t.Fatalf("missing state")
    46  	}
    47  	return state
    48  }
    49  
    50  func testFSM(t *testing.T) *nomadFSM {
    51  	p, _ := testPeriodicDispatcher()
    52  	broker := testBroker(t, 0)
    53  	blocked := NewBlockedEvals(broker)
    54  	fsm, err := NewFSM(broker, p, blocked, os.Stderr)
    55  	if err != nil {
    56  		t.Fatalf("err: %v", err)
    57  	}
    58  	if fsm == nil {
    59  		t.Fatalf("missing fsm")
    60  	}
    61  	return fsm
    62  }
    63  
    64  func makeLog(buf []byte) *raft.Log {
    65  	return &raft.Log{
    66  		Index: 1,
    67  		Term:  1,
    68  		Type:  raft.LogCommand,
    69  		Data:  buf,
    70  	}
    71  }
    72  
    73  func TestFSM_UpsertNode(t *testing.T) {
    74  	t.Parallel()
    75  	fsm := testFSM(t)
    76  	fsm.blockedEvals.SetEnabled(true)
    77  
    78  	node := mock.Node()
    79  
    80  	// Mark an eval as blocked.
    81  	eval := mock.Eval()
    82  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
    83  	fsm.blockedEvals.Block(eval)
    84  
    85  	req := structs.NodeRegisterRequest{
    86  		Node: node,
    87  	}
    88  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
    89  	if err != nil {
    90  		t.Fatalf("err: %v", err)
    91  	}
    92  
    93  	resp := fsm.Apply(makeLog(buf))
    94  	if resp != nil {
    95  		t.Fatalf("resp: %v", resp)
    96  	}
    97  
    98  	// Verify we are registered
    99  	ws := memdb.NewWatchSet()
   100  	n, err := fsm.State().NodeByID(ws, req.Node.ID)
   101  	if err != nil {
   102  		t.Fatalf("err: %v", err)
   103  	}
   104  	if n == nil {
   105  		t.Fatalf("not found!")
   106  	}
   107  	if n.CreateIndex != 1 {
   108  		t.Fatalf("bad index: %d", node.CreateIndex)
   109  	}
   110  
   111  	tt := fsm.TimeTable()
   112  	index := tt.NearestIndex(time.Now().UTC())
   113  	if index != 1 {
   114  		t.Fatalf("bad: %d", index)
   115  	}
   116  
   117  	// Verify the eval was unblocked.
   118  	testutil.WaitForResult(func() (bool, error) {
   119  		bStats := fsm.blockedEvals.Stats()
   120  		if bStats.TotalBlocked != 0 {
   121  			return false, fmt.Errorf("bad: %#v", bStats)
   122  		}
   123  		return true, nil
   124  	}, func(err error) {
   125  		t.Fatalf("err: %s", err)
   126  	})
   127  
   128  }
   129  
   130  func TestFSM_DeregisterNode(t *testing.T) {
   131  	t.Parallel()
   132  	fsm := testFSM(t)
   133  
   134  	node := mock.Node()
   135  	req := structs.NodeRegisterRequest{
   136  		Node: node,
   137  	}
   138  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   139  	if err != nil {
   140  		t.Fatalf("err: %v", err)
   141  	}
   142  
   143  	resp := fsm.Apply(makeLog(buf))
   144  	if resp != nil {
   145  		t.Fatalf("resp: %v", resp)
   146  	}
   147  
   148  	req2 := structs.NodeDeregisterRequest{
   149  		NodeID: node.ID,
   150  	}
   151  	buf, err = structs.Encode(structs.NodeDeregisterRequestType, req2)
   152  	if err != nil {
   153  		t.Fatalf("err: %v", err)
   154  	}
   155  
   156  	resp = fsm.Apply(makeLog(buf))
   157  	if resp != nil {
   158  		t.Fatalf("resp: %v", resp)
   159  	}
   160  
   161  	// Verify we are NOT registered
   162  	ws := memdb.NewWatchSet()
   163  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   164  	if err != nil {
   165  		t.Fatalf("err: %v", err)
   166  	}
   167  	if node != nil {
   168  		t.Fatalf("node found!")
   169  	}
   170  }
   171  
   172  func TestFSM_UpdateNodeStatus(t *testing.T) {
   173  	t.Parallel()
   174  	fsm := testFSM(t)
   175  	fsm.blockedEvals.SetEnabled(true)
   176  
   177  	node := mock.Node()
   178  	req := structs.NodeRegisterRequest{
   179  		Node: node,
   180  	}
   181  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   182  	if err != nil {
   183  		t.Fatalf("err: %v", err)
   184  	}
   185  
   186  	resp := fsm.Apply(makeLog(buf))
   187  	if resp != nil {
   188  		t.Fatalf("resp: %v", resp)
   189  	}
   190  
   191  	// Mark an eval as blocked.
   192  	eval := mock.Eval()
   193  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   194  	fsm.blockedEvals.Block(eval)
   195  
   196  	req2 := structs.NodeUpdateStatusRequest{
   197  		NodeID: node.ID,
   198  		Status: structs.NodeStatusReady,
   199  	}
   200  	buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2)
   201  	if err != nil {
   202  		t.Fatalf("err: %v", err)
   203  	}
   204  
   205  	resp = fsm.Apply(makeLog(buf))
   206  	if resp != nil {
   207  		t.Fatalf("resp: %v", resp)
   208  	}
   209  
   210  	// Verify the status is ready.
   211  	ws := memdb.NewWatchSet()
   212  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   213  	if err != nil {
   214  		t.Fatalf("err: %v", err)
   215  	}
   216  	if node.Status != structs.NodeStatusReady {
   217  		t.Fatalf("bad node: %#v", node)
   218  	}
   219  
   220  	// Verify the eval was unblocked.
   221  	testutil.WaitForResult(func() (bool, error) {
   222  		bStats := fsm.blockedEvals.Stats()
   223  		if bStats.TotalBlocked != 0 {
   224  			return false, fmt.Errorf("bad: %#v", bStats)
   225  		}
   226  		return true, nil
   227  	}, func(err error) {
   228  		t.Fatalf("err: %s", err)
   229  	})
   230  }
   231  
   232  func TestFSM_UpdateNodeDrain(t *testing.T) {
   233  	t.Parallel()
   234  	fsm := testFSM(t)
   235  
   236  	node := mock.Node()
   237  	req := structs.NodeRegisterRequest{
   238  		Node: node,
   239  	}
   240  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   241  	if err != nil {
   242  		t.Fatalf("err: %v", err)
   243  	}
   244  
   245  	resp := fsm.Apply(makeLog(buf))
   246  	if resp != nil {
   247  		t.Fatalf("resp: %v", resp)
   248  	}
   249  
   250  	req2 := structs.NodeUpdateDrainRequest{
   251  		NodeID: node.ID,
   252  		Drain:  true,
   253  	}
   254  	buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2)
   255  	if err != nil {
   256  		t.Fatalf("err: %v", err)
   257  	}
   258  
   259  	resp = fsm.Apply(makeLog(buf))
   260  	if resp != nil {
   261  		t.Fatalf("resp: %v", resp)
   262  	}
   263  
   264  	// Verify we are NOT registered
   265  	ws := memdb.NewWatchSet()
   266  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   267  	if err != nil {
   268  		t.Fatalf("err: %v", err)
   269  	}
   270  	if !node.Drain {
   271  		t.Fatalf("bad node: %#v", node)
   272  	}
   273  }
   274  
   275  func TestFSM_RegisterJob(t *testing.T) {
   276  	t.Parallel()
   277  	fsm := testFSM(t)
   278  
   279  	job := mock.PeriodicJob()
   280  	req := structs.JobRegisterRequest{
   281  		Job: job,
   282  	}
   283  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   284  	if err != nil {
   285  		t.Fatalf("err: %v", err)
   286  	}
   287  
   288  	resp := fsm.Apply(makeLog(buf))
   289  	if resp != nil {
   290  		t.Fatalf("resp: %v", resp)
   291  	}
   292  
   293  	// Verify we are registered
   294  	ws := memdb.NewWatchSet()
   295  	jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
   296  	if err != nil {
   297  		t.Fatalf("err: %v", err)
   298  	}
   299  	if jobOut == nil {
   300  		t.Fatalf("not found!")
   301  	}
   302  	if jobOut.CreateIndex != 1 {
   303  		t.Fatalf("bad index: %d", jobOut.CreateIndex)
   304  	}
   305  
   306  	// Verify it was added to the periodic runner.
   307  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok {
   308  		t.Fatal("job not added to periodic runner")
   309  	}
   310  
   311  	// Verify the launch time was tracked.
   312  	launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
   313  	if err != nil {
   314  		t.Fatalf("err: %v", err)
   315  	}
   316  	if launchOut == nil {
   317  		t.Fatalf("not found!")
   318  	}
   319  	if launchOut.Launch.IsZero() {
   320  		t.Fatalf("bad launch time: %v", launchOut.Launch)
   321  	}
   322  }
   323  
   324  func TestFSM_DeregisterJob_Purge(t *testing.T) {
   325  	t.Parallel()
   326  	fsm := testFSM(t)
   327  
   328  	job := mock.PeriodicJob()
   329  	req := structs.JobRegisterRequest{
   330  		Job: job,
   331  	}
   332  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   333  	if err != nil {
   334  		t.Fatalf("err: %v", err)
   335  	}
   336  
   337  	resp := fsm.Apply(makeLog(buf))
   338  	if resp != nil {
   339  		t.Fatalf("resp: %v", resp)
   340  	}
   341  
   342  	req2 := structs.JobDeregisterRequest{
   343  		JobID: job.ID,
   344  		Purge: true,
   345  	}
   346  	buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
   347  	if err != nil {
   348  		t.Fatalf("err: %v", err)
   349  	}
   350  
   351  	resp = fsm.Apply(makeLog(buf))
   352  	if resp != nil {
   353  		t.Fatalf("resp: %v", resp)
   354  	}
   355  
   356  	// Verify we are NOT registered
   357  	ws := memdb.NewWatchSet()
   358  	jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
   359  	if err != nil {
   360  		t.Fatalf("err: %v", err)
   361  	}
   362  	if jobOut != nil {
   363  		t.Fatalf("job found!")
   364  	}
   365  
   366  	// Verify it was removed from the periodic runner.
   367  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
   368  		t.Fatal("job not removed from periodic runner")
   369  	}
   370  
   371  	// Verify it was removed from the periodic launch table.
   372  	launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
   373  	if err != nil {
   374  		t.Fatalf("err: %v", err)
   375  	}
   376  	if launchOut != nil {
   377  		t.Fatalf("launch found!")
   378  	}
   379  }
   380  
   381  func TestFSM_DeregisterJob_NoPurge(t *testing.T) {
   382  	t.Parallel()
   383  	fsm := testFSM(t)
   384  
   385  	job := mock.PeriodicJob()
   386  	req := structs.JobRegisterRequest{
   387  		Job: job,
   388  	}
   389  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   390  	if err != nil {
   391  		t.Fatalf("err: %v", err)
   392  	}
   393  
   394  	resp := fsm.Apply(makeLog(buf))
   395  	if resp != nil {
   396  		t.Fatalf("resp: %v", resp)
   397  	}
   398  
   399  	req2 := structs.JobDeregisterRequest{
   400  		JobID: job.ID,
   401  		Purge: false,
   402  	}
   403  	buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
   404  	if err != nil {
   405  		t.Fatalf("err: %v", err)
   406  	}
   407  
   408  	resp = fsm.Apply(makeLog(buf))
   409  	if resp != nil {
   410  		t.Fatalf("resp: %v", resp)
   411  	}
   412  
   413  	// Verify we are NOT registered
   414  	ws := memdb.NewWatchSet()
   415  	jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
   416  	if err != nil {
   417  		t.Fatalf("err: %v", err)
   418  	}
   419  	if jobOut == nil {
   420  		t.Fatalf("job not found!")
   421  	}
   422  	if !jobOut.Stop {
   423  		t.Fatalf("job not stopped found!")
   424  	}
   425  
   426  	// Verify it was removed from the periodic runner.
   427  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
   428  		t.Fatal("job not removed from periodic runner")
   429  	}
   430  
   431  	// Verify it was removed from the periodic launch table.
   432  	launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
   433  	if err != nil {
   434  		t.Fatalf("err: %v", err)
   435  	}
   436  	if launchOut == nil {
   437  		t.Fatalf("launch not found!")
   438  	}
   439  }
   440  
   441  func TestFSM_UpdateEval(t *testing.T) {
   442  	t.Parallel()
   443  	fsm := testFSM(t)
   444  	fsm.evalBroker.SetEnabled(true)
   445  
   446  	req := structs.EvalUpdateRequest{
   447  		Evals: []*structs.Evaluation{mock.Eval()},
   448  	}
   449  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   450  	if err != nil {
   451  		t.Fatalf("err: %v", err)
   452  	}
   453  
   454  	resp := fsm.Apply(makeLog(buf))
   455  	if resp != nil {
   456  		t.Fatalf("resp: %v", resp)
   457  	}
   458  
   459  	// Verify we are registered
   460  	ws := memdb.NewWatchSet()
   461  	eval, err := fsm.State().EvalByID(ws, req.Evals[0].ID)
   462  	if err != nil {
   463  		t.Fatalf("err: %v", err)
   464  	}
   465  	if eval == nil {
   466  		t.Fatalf("not found!")
   467  	}
   468  	if eval.CreateIndex != 1 {
   469  		t.Fatalf("bad index: %d", eval.CreateIndex)
   470  	}
   471  
   472  	// Verify enqueued
   473  	stats := fsm.evalBroker.Stats()
   474  	if stats.TotalReady != 1 {
   475  		t.Fatalf("bad: %#v %#v", stats, eval)
   476  	}
   477  }
   478  
   479  func TestFSM_UpdateEval_Blocked(t *testing.T) {
   480  	t.Parallel()
   481  	fsm := testFSM(t)
   482  	fsm.evalBroker.SetEnabled(true)
   483  	fsm.blockedEvals.SetEnabled(true)
   484  
   485  	// Create a blocked eval.
   486  	eval := mock.Eval()
   487  	eval.Status = structs.EvalStatusBlocked
   488  
   489  	req := structs.EvalUpdateRequest{
   490  		Evals: []*structs.Evaluation{eval},
   491  	}
   492  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   493  	if err != nil {
   494  		t.Fatalf("err: %v", err)
   495  	}
   496  
   497  	resp := fsm.Apply(makeLog(buf))
   498  	if resp != nil {
   499  		t.Fatalf("resp: %v", resp)
   500  	}
   501  
   502  	// Verify we are registered
   503  	ws := memdb.NewWatchSet()
   504  	out, err := fsm.State().EvalByID(ws, eval.ID)
   505  	if err != nil {
   506  		t.Fatalf("err: %v", err)
   507  	}
   508  	if out == nil {
   509  		t.Fatalf("not found!")
   510  	}
   511  	if out.CreateIndex != 1 {
   512  		t.Fatalf("bad index: %d", out.CreateIndex)
   513  	}
   514  
   515  	// Verify the eval wasn't enqueued
   516  	stats := fsm.evalBroker.Stats()
   517  	if stats.TotalReady != 0 {
   518  		t.Fatalf("bad: %#v %#v", stats, out)
   519  	}
   520  
   521  	// Verify the eval was added to the blocked tracker.
   522  	bStats := fsm.blockedEvals.Stats()
   523  	if bStats.TotalBlocked != 1 {
   524  		t.Fatalf("bad: %#v %#v", bStats, out)
   525  	}
   526  }
   527  
   528  func TestFSM_UpdateEval_Untrack(t *testing.T) {
   529  	t.Parallel()
   530  	fsm := testFSM(t)
   531  	fsm.evalBroker.SetEnabled(true)
   532  	fsm.blockedEvals.SetEnabled(true)
   533  
   534  	// Mark an eval as blocked.
   535  	bEval := mock.Eval()
   536  	bEval.ClassEligibility = map[string]bool{"v1:123": true}
   537  	fsm.blockedEvals.Block(bEval)
   538  
   539  	// Create a successful eval for the same job
   540  	eval := mock.Eval()
   541  	eval.JobID = bEval.JobID
   542  	eval.Status = structs.EvalStatusComplete
   543  
   544  	req := structs.EvalUpdateRequest{
   545  		Evals: []*structs.Evaluation{eval},
   546  	}
   547  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   548  	if err != nil {
   549  		t.Fatalf("err: %v", err)
   550  	}
   551  
   552  	resp := fsm.Apply(makeLog(buf))
   553  	if resp != nil {
   554  		t.Fatalf("resp: %v", resp)
   555  	}
   556  
   557  	// Verify we are registered
   558  	ws := memdb.NewWatchSet()
   559  	out, err := fsm.State().EvalByID(ws, eval.ID)
   560  	if err != nil {
   561  		t.Fatalf("err: %v", err)
   562  	}
   563  	if out == nil {
   564  		t.Fatalf("not found!")
   565  	}
   566  	if out.CreateIndex != 1 {
   567  		t.Fatalf("bad index: %d", out.CreateIndex)
   568  	}
   569  
   570  	// Verify the eval wasn't enqueued
   571  	stats := fsm.evalBroker.Stats()
   572  	if stats.TotalReady != 0 {
   573  		t.Fatalf("bad: %#v %#v", stats, out)
   574  	}
   575  
   576  	// Verify the eval was untracked in the blocked tracker.
   577  	bStats := fsm.blockedEvals.Stats()
   578  	if bStats.TotalBlocked != 0 {
   579  		t.Fatalf("bad: %#v %#v", bStats, out)
   580  	}
   581  }
   582  
   583  func TestFSM_UpdateEval_NoUntrack(t *testing.T) {
   584  	t.Parallel()
   585  	fsm := testFSM(t)
   586  	fsm.evalBroker.SetEnabled(true)
   587  	fsm.blockedEvals.SetEnabled(true)
   588  
   589  	// Mark an eval as blocked.
   590  	bEval := mock.Eval()
   591  	bEval.ClassEligibility = map[string]bool{"v1:123": true}
   592  	fsm.blockedEvals.Block(bEval)
   593  
   594  	// Create a successful eval for the same job but with placement failures
   595  	eval := mock.Eval()
   596  	eval.JobID = bEval.JobID
   597  	eval.Status = structs.EvalStatusComplete
   598  	eval.FailedTGAllocs = make(map[string]*structs.AllocMetric)
   599  	eval.FailedTGAllocs["test"] = new(structs.AllocMetric)
   600  
   601  	req := structs.EvalUpdateRequest{
   602  		Evals: []*structs.Evaluation{eval},
   603  	}
   604  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   605  	if err != nil {
   606  		t.Fatalf("err: %v", err)
   607  	}
   608  
   609  	resp := fsm.Apply(makeLog(buf))
   610  	if resp != nil {
   611  		t.Fatalf("resp: %v", resp)
   612  	}
   613  
   614  	// Verify we are registered
   615  	ws := memdb.NewWatchSet()
   616  	out, err := fsm.State().EvalByID(ws, eval.ID)
   617  	if err != nil {
   618  		t.Fatalf("err: %v", err)
   619  	}
   620  	if out == nil {
   621  		t.Fatalf("not found!")
   622  	}
   623  	if out.CreateIndex != 1 {
   624  		t.Fatalf("bad index: %d", out.CreateIndex)
   625  	}
   626  
   627  	// Verify the eval wasn't enqueued
   628  	stats := fsm.evalBroker.Stats()
   629  	if stats.TotalReady != 0 {
   630  		t.Fatalf("bad: %#v %#v", stats, out)
   631  	}
   632  
   633  	// Verify the eval was not untracked in the blocked tracker.
   634  	bStats := fsm.blockedEvals.Stats()
   635  	if bStats.TotalBlocked != 1 {
   636  		t.Fatalf("bad: %#v %#v", bStats, out)
   637  	}
   638  }
   639  
   640  func TestFSM_DeleteEval(t *testing.T) {
   641  	t.Parallel()
   642  	fsm := testFSM(t)
   643  
   644  	eval := mock.Eval()
   645  	req := structs.EvalUpdateRequest{
   646  		Evals: []*structs.Evaluation{eval},
   647  	}
   648  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   649  	if err != nil {
   650  		t.Fatalf("err: %v", err)
   651  	}
   652  
   653  	resp := fsm.Apply(makeLog(buf))
   654  	if resp != nil {
   655  		t.Fatalf("resp: %v", resp)
   656  	}
   657  
   658  	req2 := structs.EvalDeleteRequest{
   659  		Evals: []string{eval.ID},
   660  	}
   661  	buf, err = structs.Encode(structs.EvalDeleteRequestType, req2)
   662  	if err != nil {
   663  		t.Fatalf("err: %v", err)
   664  	}
   665  
   666  	resp = fsm.Apply(makeLog(buf))
   667  	if resp != nil {
   668  		t.Fatalf("resp: %v", resp)
   669  	}
   670  
   671  	// Verify we are NOT registered
   672  	ws := memdb.NewWatchSet()
   673  	eval, err = fsm.State().EvalByID(ws, req.Evals[0].ID)
   674  	if err != nil {
   675  		t.Fatalf("err: %v", err)
   676  	}
   677  	if eval != nil {
   678  		t.Fatalf("eval found!")
   679  	}
   680  }
   681  
   682  func TestFSM_UpsertAllocs(t *testing.T) {
   683  	t.Parallel()
   684  	fsm := testFSM(t)
   685  
   686  	alloc := mock.Alloc()
   687  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   688  	req := structs.AllocUpdateRequest{
   689  		Alloc: []*structs.Allocation{alloc},
   690  	}
   691  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   692  	if err != nil {
   693  		t.Fatalf("err: %v", err)
   694  	}
   695  
   696  	resp := fsm.Apply(makeLog(buf))
   697  	if resp != nil {
   698  		t.Fatalf("resp: %v", resp)
   699  	}
   700  
   701  	// Verify we are registered
   702  	ws := memdb.NewWatchSet()
   703  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   704  	if err != nil {
   705  		t.Fatalf("err: %v", err)
   706  	}
   707  	alloc.CreateIndex = out.CreateIndex
   708  	alloc.ModifyIndex = out.ModifyIndex
   709  	alloc.AllocModifyIndex = out.AllocModifyIndex
   710  	if !reflect.DeepEqual(alloc, out) {
   711  		t.Fatalf("bad: %#v %#v", alloc, out)
   712  	}
   713  
   714  	evictAlloc := new(structs.Allocation)
   715  	*evictAlloc = *alloc
   716  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   717  	req2 := structs.AllocUpdateRequest{
   718  		Alloc: []*structs.Allocation{evictAlloc},
   719  	}
   720  	buf, err = structs.Encode(structs.AllocUpdateRequestType, req2)
   721  	if err != nil {
   722  		t.Fatalf("err: %v", err)
   723  	}
   724  
   725  	resp = fsm.Apply(makeLog(buf))
   726  	if resp != nil {
   727  		t.Fatalf("resp: %v", resp)
   728  	}
   729  
   730  	// Verify we are evicted
   731  	out, err = fsm.State().AllocByID(ws, alloc.ID)
   732  	if err != nil {
   733  		t.Fatalf("err: %v", err)
   734  	}
   735  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   736  		t.Fatalf("alloc found!")
   737  	}
   738  }
   739  
   740  func TestFSM_UpsertAllocs_SharedJob(t *testing.T) {
   741  	t.Parallel()
   742  	fsm := testFSM(t)
   743  
   744  	alloc := mock.Alloc()
   745  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   746  	job := alloc.Job
   747  	alloc.Job = nil
   748  	req := structs.AllocUpdateRequest{
   749  		Job:   job,
   750  		Alloc: []*structs.Allocation{alloc},
   751  	}
   752  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   753  	if err != nil {
   754  		t.Fatalf("err: %v", err)
   755  	}
   756  
   757  	resp := fsm.Apply(makeLog(buf))
   758  	if resp != nil {
   759  		t.Fatalf("resp: %v", resp)
   760  	}
   761  
   762  	// Verify we are registered
   763  	ws := memdb.NewWatchSet()
   764  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   765  	if err != nil {
   766  		t.Fatalf("err: %v", err)
   767  	}
   768  	alloc.CreateIndex = out.CreateIndex
   769  	alloc.ModifyIndex = out.ModifyIndex
   770  	alloc.AllocModifyIndex = out.AllocModifyIndex
   771  
   772  	// Job should be re-attached
   773  	alloc.Job = job
   774  	if !reflect.DeepEqual(alloc, out) {
   775  		t.Fatalf("bad: %#v %#v", alloc, out)
   776  	}
   777  
   778  	// Ensure that the original job is used
   779  	evictAlloc := new(structs.Allocation)
   780  	*evictAlloc = *alloc
   781  	job = mock.Job()
   782  	job.Priority = 123
   783  
   784  	evictAlloc.Job = nil
   785  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   786  	req2 := structs.AllocUpdateRequest{
   787  		Job:   job,
   788  		Alloc: []*structs.Allocation{evictAlloc},
   789  	}
   790  	buf, err = structs.Encode(structs.AllocUpdateRequestType, req2)
   791  	if err != nil {
   792  		t.Fatalf("err: %v", err)
   793  	}
   794  
   795  	resp = fsm.Apply(makeLog(buf))
   796  	if resp != nil {
   797  		t.Fatalf("resp: %v", resp)
   798  	}
   799  
   800  	// Verify we are evicted
   801  	out, err = fsm.State().AllocByID(ws, alloc.ID)
   802  	if err != nil {
   803  		t.Fatalf("err: %v", err)
   804  	}
   805  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   806  		t.Fatalf("alloc found!")
   807  	}
   808  	if out.Job == nil || out.Job.Priority == 123 {
   809  		t.Fatalf("bad job")
   810  	}
   811  }
   812  
   813  func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) {
   814  	t.Parallel()
   815  	fsm := testFSM(t)
   816  
   817  	alloc := mock.Alloc()
   818  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   819  	job := alloc.Job
   820  	resources := alloc.Resources
   821  	alloc.Resources = nil
   822  	req := structs.AllocUpdateRequest{
   823  		Job:   job,
   824  		Alloc: []*structs.Allocation{alloc},
   825  	}
   826  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   827  	if err != nil {
   828  		t.Fatalf("err: %v", err)
   829  	}
   830  
   831  	resp := fsm.Apply(makeLog(buf))
   832  	if resp != nil {
   833  		t.Fatalf("resp: %v", resp)
   834  	}
   835  
   836  	// Verify we are registered
   837  	ws := memdb.NewWatchSet()
   838  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   839  	if err != nil {
   840  		t.Fatalf("err: %v", err)
   841  	}
   842  	alloc.CreateIndex = out.CreateIndex
   843  	alloc.ModifyIndex = out.ModifyIndex
   844  	alloc.AllocModifyIndex = out.AllocModifyIndex
   845  
   846  	// Resources should be recomputed
   847  	resources.DiskMB = alloc.Job.TaskGroups[0].EphemeralDisk.SizeMB
   848  	alloc.Resources = resources
   849  	if !reflect.DeepEqual(alloc, out) {
   850  		t.Fatalf("bad: %#v %#v", alloc, out)
   851  	}
   852  }
   853  
   854  func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) {
   855  	t.Parallel()
   856  	fsm := testFSM(t)
   857  	fsm.blockedEvals.SetEnabled(true)
   858  	state := fsm.State()
   859  
   860  	node := mock.Node()
   861  	state.UpsertNode(1, node)
   862  
   863  	// Mark an eval as blocked.
   864  	eval := mock.Eval()
   865  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   866  	fsm.blockedEvals.Block(eval)
   867  
   868  	bStats := fsm.blockedEvals.Stats()
   869  	if bStats.TotalBlocked != 1 {
   870  		t.Fatalf("bad: %#v", bStats)
   871  	}
   872  
   873  	// Create a completed eval
   874  	alloc := mock.Alloc()
   875  	alloc.NodeID = node.ID
   876  	alloc2 := mock.Alloc()
   877  	alloc2.NodeID = node.ID
   878  	state.UpsertJobSummary(8, mock.JobSummary(alloc.JobID))
   879  	state.UpsertJobSummary(9, mock.JobSummary(alloc2.JobID))
   880  	state.UpsertAllocs(10, []*structs.Allocation{alloc, alloc2})
   881  
   882  	clientAlloc := new(structs.Allocation)
   883  	*clientAlloc = *alloc
   884  	clientAlloc.ClientStatus = structs.AllocClientStatusComplete
   885  	update2 := &structs.Allocation{
   886  		ID:           alloc2.ID,
   887  		ClientStatus: structs.AllocClientStatusRunning,
   888  	}
   889  
   890  	req := structs.AllocUpdateRequest{
   891  		Alloc: []*structs.Allocation{clientAlloc, update2},
   892  	}
   893  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   894  	if err != nil {
   895  		t.Fatalf("err: %v", err)
   896  	}
   897  
   898  	resp := fsm.Apply(makeLog(buf))
   899  	if resp != nil {
   900  		t.Fatalf("resp: %v", resp)
   901  	}
   902  
   903  	// Verify we are updated
   904  	ws := memdb.NewWatchSet()
   905  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   906  	if err != nil {
   907  		t.Fatalf("err: %v", err)
   908  	}
   909  	clientAlloc.CreateIndex = out.CreateIndex
   910  	clientAlloc.ModifyIndex = out.ModifyIndex
   911  	if !reflect.DeepEqual(clientAlloc, out) {
   912  		t.Fatalf("bad: %#v %#v", clientAlloc, out)
   913  	}
   914  
   915  	out, err = fsm.State().AllocByID(ws, alloc2.ID)
   916  	if err != nil {
   917  		t.Fatalf("err: %v", err)
   918  	}
   919  	alloc2.CreateIndex = out.CreateIndex
   920  	alloc2.ModifyIndex = out.ModifyIndex
   921  	alloc2.ClientStatus = structs.AllocClientStatusRunning
   922  	alloc2.TaskStates = nil
   923  	if !reflect.DeepEqual(alloc2, out) {
   924  		t.Fatalf("bad: %#v %#v", alloc2, out)
   925  	}
   926  
   927  	// Verify the eval was unblocked.
   928  	testutil.WaitForResult(func() (bool, error) {
   929  		bStats = fsm.blockedEvals.Stats()
   930  		if bStats.TotalBlocked != 0 {
   931  			return false, fmt.Errorf("bad: %#v %#v", bStats, out)
   932  		}
   933  		return true, nil
   934  	}, func(err error) {
   935  		t.Fatalf("err: %s", err)
   936  	})
   937  }
   938  
   939  func TestFSM_UpdateAllocFromClient(t *testing.T) {
   940  	t.Parallel()
   941  	fsm := testFSM(t)
   942  	state := fsm.State()
   943  
   944  	alloc := mock.Alloc()
   945  	state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID))
   946  	state.UpsertAllocs(10, []*structs.Allocation{alloc})
   947  
   948  	clientAlloc := new(structs.Allocation)
   949  	*clientAlloc = *alloc
   950  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   951  
   952  	req := structs.AllocUpdateRequest{
   953  		Alloc: []*structs.Allocation{clientAlloc},
   954  	}
   955  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   956  	if err != nil {
   957  		t.Fatalf("err: %v", err)
   958  	}
   959  
   960  	resp := fsm.Apply(makeLog(buf))
   961  	if resp != nil {
   962  		t.Fatalf("resp: %v", resp)
   963  	}
   964  
   965  	// Verify we are registered
   966  	ws := memdb.NewWatchSet()
   967  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   968  	if err != nil {
   969  		t.Fatalf("err: %v", err)
   970  	}
   971  	clientAlloc.CreateIndex = out.CreateIndex
   972  	clientAlloc.ModifyIndex = out.ModifyIndex
   973  	if !reflect.DeepEqual(clientAlloc, out) {
   974  		t.Fatalf("err: %#v,%#v", clientAlloc, out)
   975  	}
   976  }
   977  
   978  func TestFSM_UpsertVaultAccessor(t *testing.T) {
   979  	t.Parallel()
   980  	fsm := testFSM(t)
   981  	fsm.blockedEvals.SetEnabled(true)
   982  
   983  	va := mock.VaultAccessor()
   984  	va2 := mock.VaultAccessor()
   985  	req := structs.VaultAccessorsRequest{
   986  		Accessors: []*structs.VaultAccessor{va, va2},
   987  	}
   988  	buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req)
   989  	if err != nil {
   990  		t.Fatalf("err: %v", err)
   991  	}
   992  
   993  	resp := fsm.Apply(makeLog(buf))
   994  	if resp != nil {
   995  		t.Fatalf("resp: %v", resp)
   996  	}
   997  
   998  	// Verify we are registered
   999  	ws := memdb.NewWatchSet()
  1000  	out1, err := fsm.State().VaultAccessor(ws, va.Accessor)
  1001  	if err != nil {
  1002  		t.Fatalf("err: %v", err)
  1003  	}
  1004  	if out1 == nil {
  1005  		t.Fatalf("not found!")
  1006  	}
  1007  	if out1.CreateIndex != 1 {
  1008  		t.Fatalf("bad index: %d", out1.CreateIndex)
  1009  	}
  1010  	out2, err := fsm.State().VaultAccessor(ws, va2.Accessor)
  1011  	if err != nil {
  1012  		t.Fatalf("err: %v", err)
  1013  	}
  1014  	if out2 == nil {
  1015  		t.Fatalf("not found!")
  1016  	}
  1017  	if out1.CreateIndex != 1 {
  1018  		t.Fatalf("bad index: %d", out2.CreateIndex)
  1019  	}
  1020  
  1021  	tt := fsm.TimeTable()
  1022  	index := tt.NearestIndex(time.Now().UTC())
  1023  	if index != 1 {
  1024  		t.Fatalf("bad: %d", index)
  1025  	}
  1026  }
  1027  
  1028  func TestFSM_DeregisterVaultAccessor(t *testing.T) {
  1029  	t.Parallel()
  1030  	fsm := testFSM(t)
  1031  	fsm.blockedEvals.SetEnabled(true)
  1032  
  1033  	va := mock.VaultAccessor()
  1034  	va2 := mock.VaultAccessor()
  1035  	accessors := []*structs.VaultAccessor{va, va2}
  1036  
  1037  	// Insert the accessors
  1038  	if err := fsm.State().UpsertVaultAccessor(1000, accessors); err != nil {
  1039  		t.Fatalf("bad: %v", err)
  1040  	}
  1041  
  1042  	req := structs.VaultAccessorsRequest{
  1043  		Accessors: accessors,
  1044  	}
  1045  	buf, err := structs.Encode(structs.VaultAccessorDegisterRequestType, req)
  1046  	if err != nil {
  1047  		t.Fatalf("err: %v", err)
  1048  	}
  1049  
  1050  	resp := fsm.Apply(makeLog(buf))
  1051  	if resp != nil {
  1052  		t.Fatalf("resp: %v", resp)
  1053  	}
  1054  
  1055  	ws := memdb.NewWatchSet()
  1056  	out1, err := fsm.State().VaultAccessor(ws, va.Accessor)
  1057  	if err != nil {
  1058  		t.Fatalf("err: %v", err)
  1059  	}
  1060  	if out1 != nil {
  1061  		t.Fatalf("not deleted!")
  1062  	}
  1063  
  1064  	tt := fsm.TimeTable()
  1065  	index := tt.NearestIndex(time.Now().UTC())
  1066  	if index != 1 {
  1067  		t.Fatalf("bad: %d", index)
  1068  	}
  1069  }
  1070  
  1071  func TestFSM_ApplyPlanResults(t *testing.T) {
  1072  	t.Parallel()
  1073  	fsm := testFSM(t)
  1074  
  1075  	// Create the request and create a deployment
  1076  	alloc := mock.Alloc()
  1077  	job := alloc.Job
  1078  	alloc.Job = nil
  1079  
  1080  	d := mock.Deployment()
  1081  	d.JobID = job.ID
  1082  	d.JobModifyIndex = job.ModifyIndex
  1083  	d.JobVersion = job.Version
  1084  
  1085  	alloc.DeploymentID = d.ID
  1086  
  1087  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
  1088  	req := structs.ApplyPlanResultsRequest{
  1089  		AllocUpdateRequest: structs.AllocUpdateRequest{
  1090  			Job:   job,
  1091  			Alloc: []*structs.Allocation{alloc},
  1092  		},
  1093  		Deployment: d,
  1094  	}
  1095  	buf, err := structs.Encode(structs.ApplyPlanResultsRequestType, req)
  1096  	if err != nil {
  1097  		t.Fatalf("err: %v", err)
  1098  	}
  1099  
  1100  	resp := fsm.Apply(makeLog(buf))
  1101  	if resp != nil {
  1102  		t.Fatalf("resp: %v", resp)
  1103  	}
  1104  
  1105  	// Verify the allocation is registered
  1106  	ws := memdb.NewWatchSet()
  1107  	out, err := fsm.State().AllocByID(ws, alloc.ID)
  1108  	if err != nil {
  1109  		t.Fatalf("err: %v", err)
  1110  	}
  1111  	alloc.CreateIndex = out.CreateIndex
  1112  	alloc.ModifyIndex = out.ModifyIndex
  1113  	alloc.AllocModifyIndex = out.AllocModifyIndex
  1114  
  1115  	// Job should be re-attached
  1116  	alloc.Job = job
  1117  	if !reflect.DeepEqual(alloc, out) {
  1118  		t.Fatalf("bad: %#v %#v", alloc, out)
  1119  	}
  1120  
  1121  	dout, err := fsm.State().DeploymentByID(ws, d.ID)
  1122  	if err != nil {
  1123  		t.Fatalf("err: %v", err)
  1124  	}
  1125  	if tg, ok := dout.TaskGroups[alloc.TaskGroup]; !ok || tg.PlacedAllocs != 1 {
  1126  		t.Fatalf("err: %v %v", tg, err)
  1127  	}
  1128  
  1129  	// Ensure that the original job is used
  1130  	evictAlloc := alloc.Copy()
  1131  	job = mock.Job()
  1132  	job.Priority = 123
  1133  
  1134  	evictAlloc.Job = nil
  1135  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
  1136  	req2 := structs.ApplyPlanResultsRequest{
  1137  		AllocUpdateRequest: structs.AllocUpdateRequest{
  1138  			Job:   job,
  1139  			Alloc: []*structs.Allocation{evictAlloc},
  1140  		},
  1141  	}
  1142  	buf, err = structs.Encode(structs.ApplyPlanResultsRequestType, req2)
  1143  	if err != nil {
  1144  		t.Fatalf("err: %v", err)
  1145  	}
  1146  
  1147  	resp = fsm.Apply(makeLog(buf))
  1148  	if resp != nil {
  1149  		t.Fatalf("resp: %v", resp)
  1150  	}
  1151  
  1152  	// Verify we are evicted
  1153  	out, err = fsm.State().AllocByID(ws, alloc.ID)
  1154  	if err != nil {
  1155  		t.Fatalf("err: %v", err)
  1156  	}
  1157  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
  1158  		t.Fatalf("alloc found!")
  1159  	}
  1160  	if out.Job == nil || out.Job.Priority == 123 {
  1161  		t.Fatalf("bad job")
  1162  	}
  1163  }
  1164  
  1165  func TestFSM_DeploymentStatusUpdate(t *testing.T) {
  1166  	t.Parallel()
  1167  	fsm := testFSM(t)
  1168  	fsm.evalBroker.SetEnabled(true)
  1169  	state := fsm.State()
  1170  
  1171  	// Upsert a deployment
  1172  	d := mock.Deployment()
  1173  	if err := state.UpsertDeployment(1, d); err != nil {
  1174  		t.Fatalf("bad: %v", err)
  1175  	}
  1176  
  1177  	// Create a request to update the deployment, create an eval and job
  1178  	e := mock.Eval()
  1179  	j := mock.Job()
  1180  	status, desc := structs.DeploymentStatusFailed, "foo"
  1181  	req := &structs.DeploymentStatusUpdateRequest{
  1182  		DeploymentUpdate: &structs.DeploymentStatusUpdate{
  1183  			DeploymentID:      d.ID,
  1184  			Status:            status,
  1185  			StatusDescription: desc,
  1186  		},
  1187  		Job:  j,
  1188  		Eval: e,
  1189  	}
  1190  	buf, err := structs.Encode(structs.DeploymentStatusUpdateRequestType, req)
  1191  	if err != nil {
  1192  		t.Fatalf("err: %v", err)
  1193  	}
  1194  	resp := fsm.Apply(makeLog(buf))
  1195  	if resp != nil {
  1196  		t.Fatalf("resp: %v", resp)
  1197  	}
  1198  
  1199  	// Check that the status was updated properly
  1200  	ws := memdb.NewWatchSet()
  1201  	dout, err := state.DeploymentByID(ws, d.ID)
  1202  	if err != nil {
  1203  		t.Fatalf("bad: %v", err)
  1204  	}
  1205  	if dout.Status != status || dout.StatusDescription != desc {
  1206  		t.Fatalf("bad: %#v", dout)
  1207  	}
  1208  
  1209  	// Check that the evaluation was created
  1210  	eout, _ := state.EvalByID(ws, e.ID)
  1211  	if err != nil {
  1212  		t.Fatalf("bad: %v", err)
  1213  	}
  1214  	if eout == nil {
  1215  		t.Fatalf("bad: %#v", eout)
  1216  	}
  1217  
  1218  	// Check that the job was created
  1219  	jout, _ := state.JobByID(ws, j.ID)
  1220  	if err != nil {
  1221  		t.Fatalf("bad: %v", err)
  1222  	}
  1223  	if jout == nil {
  1224  		t.Fatalf("bad: %#v", jout)
  1225  	}
  1226  
  1227  	// Assert the eval was enqueued
  1228  	stats := fsm.evalBroker.Stats()
  1229  	if stats.TotalReady != 1 {
  1230  		t.Fatalf("bad: %#v %#v", stats, e)
  1231  	}
  1232  }
  1233  
  1234  func TestFSM_JobStabilityUpdate(t *testing.T) {
  1235  	t.Parallel()
  1236  	fsm := testFSM(t)
  1237  	fsm.evalBroker.SetEnabled(true)
  1238  	state := fsm.State()
  1239  
  1240  	// Upsert a deployment
  1241  	job := mock.Job()
  1242  	if err := state.UpsertJob(1, job); err != nil {
  1243  		t.Fatalf("bad: %v", err)
  1244  	}
  1245  
  1246  	// Create a request to update the job to stable
  1247  	req := &structs.JobStabilityRequest{
  1248  		JobID:      job.ID,
  1249  		JobVersion: job.Version,
  1250  		Stable:     true,
  1251  	}
  1252  	buf, err := structs.Encode(structs.JobStabilityRequestType, req)
  1253  	if err != nil {
  1254  		t.Fatalf("err: %v", err)
  1255  	}
  1256  	resp := fsm.Apply(makeLog(buf))
  1257  	if resp != nil {
  1258  		t.Fatalf("resp: %v", resp)
  1259  	}
  1260  
  1261  	// Check that the stability was updated properly
  1262  	ws := memdb.NewWatchSet()
  1263  	jout, _ := state.JobByIDAndVersion(ws, job.ID, job.Version)
  1264  	if err != nil {
  1265  		t.Fatalf("bad: %v", err)
  1266  	}
  1267  	if jout == nil || !jout.Stable {
  1268  		t.Fatalf("bad: %#v", jout)
  1269  	}
  1270  }
  1271  
  1272  func TestFSM_DeploymentPromotion(t *testing.T) {
  1273  	t.Parallel()
  1274  	fsm := testFSM(t)
  1275  	fsm.evalBroker.SetEnabled(true)
  1276  	state := fsm.State()
  1277  
  1278  	// Create a job with two task groups
  1279  	j := mock.Job()
  1280  	tg1 := j.TaskGroups[0]
  1281  	tg2 := tg1.Copy()
  1282  	tg2.Name = "foo"
  1283  	j.TaskGroups = append(j.TaskGroups, tg2)
  1284  	if err := state.UpsertJob(1, j); err != nil {
  1285  		t.Fatalf("bad: %v", err)
  1286  	}
  1287  
  1288  	// Create a deployment
  1289  	d := mock.Deployment()
  1290  	d.JobID = j.ID
  1291  	d.TaskGroups = map[string]*structs.DeploymentState{
  1292  		"web": &structs.DeploymentState{
  1293  			DesiredTotal:    10,
  1294  			DesiredCanaries: 1,
  1295  		},
  1296  		"foo": &structs.DeploymentState{
  1297  			DesiredTotal:    10,
  1298  			DesiredCanaries: 1,
  1299  		},
  1300  	}
  1301  	if err := state.UpsertDeployment(2, d); err != nil {
  1302  		t.Fatalf("bad: %v", err)
  1303  	}
  1304  
  1305  	// Create a set of allocations
  1306  	c1 := mock.Alloc()
  1307  	c1.JobID = j.ID
  1308  	c1.DeploymentID = d.ID
  1309  	d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID)
  1310  	c1.DeploymentStatus = &structs.AllocDeploymentStatus{
  1311  		Healthy: helper.BoolToPtr(true),
  1312  	}
  1313  	c2 := mock.Alloc()
  1314  	c2.JobID = j.ID
  1315  	c2.DeploymentID = d.ID
  1316  	d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID)
  1317  	c2.TaskGroup = tg2.Name
  1318  	c2.DeploymentStatus = &structs.AllocDeploymentStatus{
  1319  		Healthy: helper.BoolToPtr(true),
  1320  	}
  1321  
  1322  	if err := state.UpsertAllocs(3, []*structs.Allocation{c1, c2}); err != nil {
  1323  		t.Fatalf("err: %v", err)
  1324  	}
  1325  
  1326  	// Create an eval
  1327  	e := mock.Eval()
  1328  
  1329  	// Promote the canaries
  1330  	req := &structs.ApplyDeploymentPromoteRequest{
  1331  		DeploymentPromoteRequest: structs.DeploymentPromoteRequest{
  1332  			DeploymentID: d.ID,
  1333  			All:          true,
  1334  		},
  1335  		Eval: e,
  1336  	}
  1337  	buf, err := structs.Encode(structs.DeploymentPromoteRequestType, req)
  1338  	if err != nil {
  1339  		t.Fatalf("err: %v", err)
  1340  	}
  1341  	resp := fsm.Apply(makeLog(buf))
  1342  	if resp != nil {
  1343  		t.Fatalf("resp: %v", resp)
  1344  	}
  1345  
  1346  	// Check that the status per task group was updated properly
  1347  	ws := memdb.NewWatchSet()
  1348  	dout, err := state.DeploymentByID(ws, d.ID)
  1349  	if err != nil {
  1350  		t.Fatalf("bad: %v", err)
  1351  	}
  1352  	if len(dout.TaskGroups) != 2 {
  1353  		t.Fatalf("bad: %#v", dout.TaskGroups)
  1354  	}
  1355  	for tg, state := range dout.TaskGroups {
  1356  		if !state.Promoted {
  1357  			t.Fatalf("bad: group %q not promoted %#v", tg, state)
  1358  		}
  1359  	}
  1360  
  1361  	// Check that the evaluation was created
  1362  	eout, _ := state.EvalByID(ws, e.ID)
  1363  	if err != nil {
  1364  		t.Fatalf("bad: %v", err)
  1365  	}
  1366  	if eout == nil {
  1367  		t.Fatalf("bad: %#v", eout)
  1368  	}
  1369  
  1370  	// Assert the eval was enqueued
  1371  	stats := fsm.evalBroker.Stats()
  1372  	if stats.TotalReady != 1 {
  1373  		t.Fatalf("bad: %#v %#v", stats, e)
  1374  	}
  1375  }
  1376  
  1377  func TestFSM_DeploymentAllocHealth(t *testing.T) {
  1378  	t.Parallel()
  1379  	fsm := testFSM(t)
  1380  	fsm.evalBroker.SetEnabled(true)
  1381  	state := fsm.State()
  1382  
  1383  	// Insert a deployment
  1384  	d := mock.Deployment()
  1385  	if err := state.UpsertDeployment(1, d); err != nil {
  1386  		t.Fatalf("bad: %v", err)
  1387  	}
  1388  
  1389  	// Insert two allocations
  1390  	a1 := mock.Alloc()
  1391  	a1.DeploymentID = d.ID
  1392  	a2 := mock.Alloc()
  1393  	a2.DeploymentID = d.ID
  1394  	if err := state.UpsertAllocs(2, []*structs.Allocation{a1, a2}); err != nil {
  1395  		t.Fatalf("bad: %v", err)
  1396  	}
  1397  
  1398  	// Create a job to roll back to
  1399  	j := mock.Job()
  1400  
  1401  	// Create an eval that should be upserted
  1402  	e := mock.Eval()
  1403  
  1404  	// Create a status update for the deployment
  1405  	status, desc := structs.DeploymentStatusFailed, "foo"
  1406  	u := &structs.DeploymentStatusUpdate{
  1407  		DeploymentID:      d.ID,
  1408  		Status:            status,
  1409  		StatusDescription: desc,
  1410  	}
  1411  
  1412  	// Set health against the deployment
  1413  	req := &structs.ApplyDeploymentAllocHealthRequest{
  1414  		DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{
  1415  			DeploymentID:           d.ID,
  1416  			HealthyAllocationIDs:   []string{a1.ID},
  1417  			UnhealthyAllocationIDs: []string{a2.ID},
  1418  		},
  1419  		Job:              j,
  1420  		Eval:             e,
  1421  		DeploymentUpdate: u,
  1422  	}
  1423  	buf, err := structs.Encode(structs.DeploymentAllocHealthRequestType, req)
  1424  	if err != nil {
  1425  		t.Fatalf("err: %v", err)
  1426  	}
  1427  	resp := fsm.Apply(makeLog(buf))
  1428  	if resp != nil {
  1429  		t.Fatalf("resp: %v", resp)
  1430  	}
  1431  
  1432  	// Check that the status was updated properly
  1433  	ws := memdb.NewWatchSet()
  1434  	dout, err := state.DeploymentByID(ws, d.ID)
  1435  	if err != nil {
  1436  		t.Fatalf("bad: %v", err)
  1437  	}
  1438  	if dout.Status != status || dout.StatusDescription != desc {
  1439  		t.Fatalf("bad: %#v", dout)
  1440  	}
  1441  
  1442  	// Check that the evaluation was created
  1443  	eout, _ := state.EvalByID(ws, e.ID)
  1444  	if err != nil {
  1445  		t.Fatalf("bad: %v", err)
  1446  	}
  1447  	if eout == nil {
  1448  		t.Fatalf("bad: %#v", eout)
  1449  	}
  1450  
  1451  	// Check that the job was created
  1452  	jout, _ := state.JobByID(ws, j.ID)
  1453  	if err != nil {
  1454  		t.Fatalf("bad: %v", err)
  1455  	}
  1456  	if jout == nil {
  1457  		t.Fatalf("bad: %#v", jout)
  1458  	}
  1459  
  1460  	// Check the status of the allocs
  1461  	out1, err := state.AllocByID(ws, a1.ID)
  1462  	if err != nil {
  1463  		t.Fatalf("err: %v", err)
  1464  	}
  1465  	out2, err := state.AllocByID(ws, a2.ID)
  1466  	if err != nil {
  1467  		t.Fatalf("err: %v", err)
  1468  	}
  1469  
  1470  	if !out1.DeploymentStatus.IsHealthy() {
  1471  		t.Fatalf("bad: alloc %q not healthy", out1.ID)
  1472  	}
  1473  	if !out2.DeploymentStatus.IsUnhealthy() {
  1474  		t.Fatalf("bad: alloc %q not unhealthy", out2.ID)
  1475  	}
  1476  
  1477  	// Assert the eval was enqueued
  1478  	stats := fsm.evalBroker.Stats()
  1479  	if stats.TotalReady != 1 {
  1480  		t.Fatalf("bad: %#v %#v", stats, e)
  1481  	}
  1482  }
  1483  
  1484  func TestFSM_DeleteDeployment(t *testing.T) {
  1485  	t.Parallel()
  1486  	fsm := testFSM(t)
  1487  	state := fsm.State()
  1488  
  1489  	// Upsert a deployments
  1490  	d := mock.Deployment()
  1491  	if err := state.UpsertDeployment(1, d); err != nil {
  1492  		t.Fatalf("bad: %v", err)
  1493  	}
  1494  
  1495  	req := structs.DeploymentDeleteRequest{
  1496  		Deployments: []string{d.ID},
  1497  	}
  1498  	buf, err := structs.Encode(structs.DeploymentDeleteRequestType, req)
  1499  	if err != nil {
  1500  		t.Fatalf("err: %v", err)
  1501  	}
  1502  
  1503  	resp := fsm.Apply(makeLog(buf))
  1504  	if resp != nil {
  1505  		t.Fatalf("resp: %v", resp)
  1506  	}
  1507  
  1508  	// Verify we are NOT registered
  1509  	ws := memdb.NewWatchSet()
  1510  	deployment, err := state.DeploymentByID(ws, d.ID)
  1511  	if err != nil {
  1512  		t.Fatalf("err: %v", err)
  1513  	}
  1514  	if deployment != nil {
  1515  		t.Fatalf("deployment found!")
  1516  	}
  1517  }
  1518  
  1519  func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM {
  1520  	// Snapshot
  1521  	snap, err := fsm.Snapshot()
  1522  	if err != nil {
  1523  		t.Fatalf("err: %v", err)
  1524  	}
  1525  	defer snap.Release()
  1526  
  1527  	// Persist
  1528  	buf := bytes.NewBuffer(nil)
  1529  	sink := &MockSink{buf, false}
  1530  	if err := snap.Persist(sink); err != nil {
  1531  		t.Fatalf("err: %v", err)
  1532  	}
  1533  
  1534  	// Try to restore on a new FSM
  1535  	fsm2 := testFSM(t)
  1536  	snap, err = fsm2.Snapshot()
  1537  	if err != nil {
  1538  		t.Fatalf("err: %v", err)
  1539  	}
  1540  	defer snap.Release()
  1541  
  1542  	abandonCh := fsm2.State().AbandonCh()
  1543  
  1544  	// Do a restore
  1545  	if err := fsm2.Restore(sink); err != nil {
  1546  		t.Fatalf("err: %v", err)
  1547  	}
  1548  
  1549  	select {
  1550  	case <-abandonCh:
  1551  	default:
  1552  		t.Fatalf("bad")
  1553  	}
  1554  
  1555  	return fsm2
  1556  }
  1557  
  1558  func TestFSM_SnapshotRestore_Nodes(t *testing.T) {
  1559  	t.Parallel()
  1560  	// Add some state
  1561  	fsm := testFSM(t)
  1562  	state := fsm.State()
  1563  	node1 := mock.Node()
  1564  	state.UpsertNode(1000, node1)
  1565  	node2 := mock.Node()
  1566  	state.UpsertNode(1001, node2)
  1567  
  1568  	// Verify the contents
  1569  	fsm2 := testSnapshotRestore(t, fsm)
  1570  	state2 := fsm2.State()
  1571  	ws := memdb.NewWatchSet()
  1572  	out1, _ := state2.NodeByID(ws, node1.ID)
  1573  	out2, _ := state2.NodeByID(ws, node2.ID)
  1574  	if !reflect.DeepEqual(node1, out1) {
  1575  		t.Fatalf("bad: \n%#v\n%#v", out1, node1)
  1576  	}
  1577  	if !reflect.DeepEqual(node2, out2) {
  1578  		t.Fatalf("bad: \n%#v\n%#v", out2, node2)
  1579  	}
  1580  }
  1581  
  1582  func TestFSM_SnapshotRestore_Jobs(t *testing.T) {
  1583  	t.Parallel()
  1584  	// Add some state
  1585  	fsm := testFSM(t)
  1586  	state := fsm.State()
  1587  	job1 := mock.Job()
  1588  	state.UpsertJob(1000, job1)
  1589  	job2 := mock.Job()
  1590  	state.UpsertJob(1001, job2)
  1591  
  1592  	// Verify the contents
  1593  	ws := memdb.NewWatchSet()
  1594  	fsm2 := testSnapshotRestore(t, fsm)
  1595  	state2 := fsm2.State()
  1596  	out1, _ := state2.JobByID(ws, job1.ID)
  1597  	out2, _ := state2.JobByID(ws, job2.ID)
  1598  	if !reflect.DeepEqual(job1, out1) {
  1599  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
  1600  	}
  1601  	if !reflect.DeepEqual(job2, out2) {
  1602  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
  1603  	}
  1604  }
  1605  
  1606  func TestFSM_SnapshotRestore_Evals(t *testing.T) {
  1607  	t.Parallel()
  1608  	// Add some state
  1609  	fsm := testFSM(t)
  1610  	state := fsm.State()
  1611  	eval1 := mock.Eval()
  1612  	state.UpsertEvals(1000, []*structs.Evaluation{eval1})
  1613  	eval2 := mock.Eval()
  1614  	state.UpsertEvals(1001, []*structs.Evaluation{eval2})
  1615  
  1616  	// Verify the contents
  1617  	fsm2 := testSnapshotRestore(t, fsm)
  1618  	state2 := fsm2.State()
  1619  	ws := memdb.NewWatchSet()
  1620  	out1, _ := state2.EvalByID(ws, eval1.ID)
  1621  	out2, _ := state2.EvalByID(ws, eval2.ID)
  1622  	if !reflect.DeepEqual(eval1, out1) {
  1623  		t.Fatalf("bad: \n%#v\n%#v", out1, eval1)
  1624  	}
  1625  	if !reflect.DeepEqual(eval2, out2) {
  1626  		t.Fatalf("bad: \n%#v\n%#v", out2, eval2)
  1627  	}
  1628  }
  1629  
  1630  func TestFSM_SnapshotRestore_Allocs(t *testing.T) {
  1631  	t.Parallel()
  1632  	// Add some state
  1633  	fsm := testFSM(t)
  1634  	state := fsm.State()
  1635  	alloc1 := mock.Alloc()
  1636  	alloc2 := mock.Alloc()
  1637  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1638  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1639  	state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
  1640  	state.UpsertAllocs(1001, []*structs.Allocation{alloc2})
  1641  
  1642  	// Verify the contents
  1643  	fsm2 := testSnapshotRestore(t, fsm)
  1644  	state2 := fsm2.State()
  1645  	ws := memdb.NewWatchSet()
  1646  	out1, _ := state2.AllocByID(ws, alloc1.ID)
  1647  	out2, _ := state2.AllocByID(ws, alloc2.ID)
  1648  	if !reflect.DeepEqual(alloc1, out1) {
  1649  		t.Fatalf("bad: \n%#v\n%#v", out1, alloc1)
  1650  	}
  1651  	if !reflect.DeepEqual(alloc2, out2) {
  1652  		t.Fatalf("bad: \n%#v\n%#v", out2, alloc2)
  1653  	}
  1654  }
  1655  
  1656  func TestFSM_SnapshotRestore_Allocs_NoSharedResources(t *testing.T) {
  1657  	t.Parallel()
  1658  	// Add some state
  1659  	fsm := testFSM(t)
  1660  	state := fsm.State()
  1661  	alloc1 := mock.Alloc()
  1662  	alloc2 := mock.Alloc()
  1663  	alloc1.SharedResources = nil
  1664  	alloc2.SharedResources = nil
  1665  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1666  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1667  	state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
  1668  	state.UpsertAllocs(1001, []*structs.Allocation{alloc2})
  1669  
  1670  	// Verify the contents
  1671  	fsm2 := testSnapshotRestore(t, fsm)
  1672  	state2 := fsm2.State()
  1673  	ws := memdb.NewWatchSet()
  1674  	out1, _ := state2.AllocByID(ws, alloc1.ID)
  1675  	out2, _ := state2.AllocByID(ws, alloc2.ID)
  1676  	alloc1.SharedResources = &structs.Resources{DiskMB: 150}
  1677  	alloc2.SharedResources = &structs.Resources{DiskMB: 150}
  1678  
  1679  	if !reflect.DeepEqual(alloc1, out1) {
  1680  		t.Fatalf("bad: \n%#v\n%#v", out1, alloc1)
  1681  	}
  1682  	if !reflect.DeepEqual(alloc2, out2) {
  1683  		t.Fatalf("bad: \n%#v\n%#v", out2, alloc2)
  1684  	}
  1685  }
  1686  
  1687  func TestFSM_SnapshotRestore_Indexes(t *testing.T) {
  1688  	t.Parallel()
  1689  	// Add some state
  1690  	fsm := testFSM(t)
  1691  	state := fsm.State()
  1692  	node1 := mock.Node()
  1693  	state.UpsertNode(1000, node1)
  1694  
  1695  	// Verify the contents
  1696  	fsm2 := testSnapshotRestore(t, fsm)
  1697  	state2 := fsm2.State()
  1698  
  1699  	index, err := state2.Index("nodes")
  1700  	if err != nil {
  1701  		t.Fatalf("err: %v", err)
  1702  	}
  1703  	if index != 1000 {
  1704  		t.Fatalf("bad: %d", index)
  1705  	}
  1706  }
  1707  
  1708  func TestFSM_SnapshotRestore_TimeTable(t *testing.T) {
  1709  	t.Parallel()
  1710  	// Add some state
  1711  	fsm := testFSM(t)
  1712  
  1713  	tt := fsm.TimeTable()
  1714  	start := time.Now().UTC()
  1715  	tt.Witness(1000, start)
  1716  	tt.Witness(2000, start.Add(10*time.Minute))
  1717  
  1718  	// Verify the contents
  1719  	fsm2 := testSnapshotRestore(t, fsm)
  1720  
  1721  	tt2 := fsm2.TimeTable()
  1722  	if tt2.NearestTime(1500) != start {
  1723  		t.Fatalf("bad")
  1724  	}
  1725  	if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 {
  1726  		t.Fatalf("bad")
  1727  	}
  1728  }
  1729  
  1730  func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) {
  1731  	t.Parallel()
  1732  	// Add some state
  1733  	fsm := testFSM(t)
  1734  	state := fsm.State()
  1735  	job1 := mock.Job()
  1736  	launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()}
  1737  	state.UpsertPeriodicLaunch(1000, launch1)
  1738  	job2 := mock.Job()
  1739  	launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()}
  1740  	state.UpsertPeriodicLaunch(1001, launch2)
  1741  
  1742  	// Verify the contents
  1743  	fsm2 := testSnapshotRestore(t, fsm)
  1744  	state2 := fsm2.State()
  1745  	ws := memdb.NewWatchSet()
  1746  	out1, _ := state2.PeriodicLaunchByID(ws, launch1.ID)
  1747  	out2, _ := state2.PeriodicLaunchByID(ws, launch2.ID)
  1748  	if !reflect.DeepEqual(launch1, out1) {
  1749  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
  1750  	}
  1751  	if !reflect.DeepEqual(launch2, out2) {
  1752  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
  1753  	}
  1754  }
  1755  
  1756  func TestFSM_SnapshotRestore_JobSummary(t *testing.T) {
  1757  	t.Parallel()
  1758  	// Add some state
  1759  	fsm := testFSM(t)
  1760  	state := fsm.State()
  1761  
  1762  	job1 := mock.Job()
  1763  	state.UpsertJob(1000, job1)
  1764  	ws := memdb.NewWatchSet()
  1765  	js1, _ := state.JobSummaryByID(ws, job1.ID)
  1766  
  1767  	job2 := mock.Job()
  1768  	state.UpsertJob(1001, job2)
  1769  	js2, _ := state.JobSummaryByID(ws, job2.ID)
  1770  
  1771  	// Verify the contents
  1772  	fsm2 := testSnapshotRestore(t, fsm)
  1773  	state2 := fsm2.State()
  1774  	out1, _ := state2.JobSummaryByID(ws, job1.ID)
  1775  	out2, _ := state2.JobSummaryByID(ws, job2.ID)
  1776  	if !reflect.DeepEqual(js1, out1) {
  1777  		t.Fatalf("bad: \n%#v\n%#v", js1, out1)
  1778  	}
  1779  	if !reflect.DeepEqual(js2, out2) {
  1780  		t.Fatalf("bad: \n%#v\n%#v", js2, out2)
  1781  	}
  1782  }
  1783  
  1784  func TestFSM_SnapshotRestore_VaultAccessors(t *testing.T) {
  1785  	t.Parallel()
  1786  	// Add some state
  1787  	fsm := testFSM(t)
  1788  	state := fsm.State()
  1789  	a1 := mock.VaultAccessor()
  1790  	a2 := mock.VaultAccessor()
  1791  	state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a1, a2})
  1792  
  1793  	// Verify the contents
  1794  	fsm2 := testSnapshotRestore(t, fsm)
  1795  	state2 := fsm2.State()
  1796  	ws := memdb.NewWatchSet()
  1797  	out1, _ := state2.VaultAccessor(ws, a1.Accessor)
  1798  	out2, _ := state2.VaultAccessor(ws, a2.Accessor)
  1799  	if !reflect.DeepEqual(a1, out1) {
  1800  		t.Fatalf("bad: \n%#v\n%#v", out1, a1)
  1801  	}
  1802  	if !reflect.DeepEqual(a2, out2) {
  1803  		t.Fatalf("bad: \n%#v\n%#v", out2, a2)
  1804  	}
  1805  }
  1806  
  1807  func TestFSM_SnapshotRestore_JobVersions(t *testing.T) {
  1808  	t.Parallel()
  1809  	// Add some state
  1810  	fsm := testFSM(t)
  1811  	state := fsm.State()
  1812  	job1 := mock.Job()
  1813  	state.UpsertJob(1000, job1)
  1814  	job2 := mock.Job()
  1815  	job2.ID = job1.ID
  1816  	state.UpsertJob(1001, job2)
  1817  
  1818  	// Verify the contents
  1819  	ws := memdb.NewWatchSet()
  1820  	fsm2 := testSnapshotRestore(t, fsm)
  1821  	state2 := fsm2.State()
  1822  	out1, _ := state2.JobByIDAndVersion(ws, job1.ID, job1.Version)
  1823  	out2, _ := state2.JobByIDAndVersion(ws, job2.ID, job2.Version)
  1824  	if !reflect.DeepEqual(job1, out1) {
  1825  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
  1826  	}
  1827  	if !reflect.DeepEqual(job2, out2) {
  1828  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
  1829  	}
  1830  	if job2.Version != 1 {
  1831  		t.Fatalf("bad: \n%#v\n%#v", 1, job2)
  1832  	}
  1833  }
  1834  
  1835  func TestFSM_SnapshotRestore_Deployments(t *testing.T) {
  1836  	t.Parallel()
  1837  	// Add some state
  1838  	fsm := testFSM(t)
  1839  	state := fsm.State()
  1840  	d1 := mock.Deployment()
  1841  	d2 := mock.Deployment()
  1842  	state.UpsertDeployment(1000, d1)
  1843  	state.UpsertDeployment(1001, d2)
  1844  
  1845  	// Verify the contents
  1846  	fsm2 := testSnapshotRestore(t, fsm)
  1847  	state2 := fsm2.State()
  1848  	ws := memdb.NewWatchSet()
  1849  	out1, _ := state2.DeploymentByID(ws, d1.ID)
  1850  	out2, _ := state2.DeploymentByID(ws, d2.ID)
  1851  	if !reflect.DeepEqual(d1, out1) {
  1852  		t.Fatalf("bad: \n%#v\n%#v", out1, d1)
  1853  	}
  1854  	if !reflect.DeepEqual(d2, out2) {
  1855  		t.Fatalf("bad: \n%#v\n%#v", out2, d2)
  1856  	}
  1857  }
  1858  
  1859  func TestFSM_SnapshotRestore_AddMissingSummary(t *testing.T) {
  1860  	t.Parallel()
  1861  	// Add some state
  1862  	fsm := testFSM(t)
  1863  	state := fsm.State()
  1864  
  1865  	// make an allocation
  1866  	alloc := mock.Alloc()
  1867  	state.UpsertJob(1010, alloc.Job)
  1868  	state.UpsertAllocs(1011, []*structs.Allocation{alloc})
  1869  
  1870  	// Delete the summary
  1871  	state.DeleteJobSummary(1040, alloc.Job.ID)
  1872  
  1873  	// Delete the index
  1874  	if err := state.RemoveIndex("job_summary"); err != nil {
  1875  		t.Fatalf("err: %v", err)
  1876  	}
  1877  
  1878  	fsm2 := testSnapshotRestore(t, fsm)
  1879  	state2 := fsm2.State()
  1880  	latestIndex, _ := state.LatestIndex()
  1881  
  1882  	ws := memdb.NewWatchSet()
  1883  	out, _ := state2.JobSummaryByID(ws, alloc.Job.ID)
  1884  	expected := structs.JobSummary{
  1885  		JobID: alloc.Job.ID,
  1886  		Summary: map[string]structs.TaskGroupSummary{
  1887  			"web": structs.TaskGroupSummary{
  1888  				Starting: 1,
  1889  			},
  1890  		},
  1891  		CreateIndex: 1010,
  1892  		ModifyIndex: latestIndex,
  1893  	}
  1894  	if !reflect.DeepEqual(&expected, out) {
  1895  		t.Fatalf("expected: %#v, actual: %#v", &expected, out)
  1896  	}
  1897  }
  1898  
  1899  func TestFSM_ReconcileSummaries(t *testing.T) {
  1900  	t.Parallel()
  1901  	// Add some state
  1902  	fsm := testFSM(t)
  1903  	state := fsm.State()
  1904  
  1905  	// Add a node
  1906  	node := mock.Node()
  1907  	state.UpsertNode(800, node)
  1908  
  1909  	// Make a job so that none of the tasks can be placed
  1910  	job1 := mock.Job()
  1911  	job1.TaskGroups[0].Tasks[0].Resources.CPU = 5000
  1912  	state.UpsertJob(1000, job1)
  1913  
  1914  	// make a job which can make partial progress
  1915  	alloc := mock.Alloc()
  1916  	alloc.NodeID = node.ID
  1917  	state.UpsertJob(1010, alloc.Job)
  1918  	state.UpsertAllocs(1011, []*structs.Allocation{alloc})
  1919  
  1920  	// Delete the summaries
  1921  	state.DeleteJobSummary(1030, job1.ID)
  1922  	state.DeleteJobSummary(1040, alloc.Job.ID)
  1923  
  1924  	req := structs.GenericRequest{}
  1925  	buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req)
  1926  	if err != nil {
  1927  		t.Fatalf("err: %v", err)
  1928  	}
  1929  
  1930  	resp := fsm.Apply(makeLog(buf))
  1931  	if resp != nil {
  1932  		t.Fatalf("resp: %v", resp)
  1933  	}
  1934  
  1935  	ws := memdb.NewWatchSet()
  1936  	out1, _ := state.JobSummaryByID(ws, job1.ID)
  1937  	expected := structs.JobSummary{
  1938  		JobID: job1.ID,
  1939  		Summary: map[string]structs.TaskGroupSummary{
  1940  			"web": structs.TaskGroupSummary{
  1941  				Queued: 10,
  1942  			},
  1943  		},
  1944  		CreateIndex: 1000,
  1945  		ModifyIndex: out1.ModifyIndex,
  1946  	}
  1947  	if !reflect.DeepEqual(&expected, out1) {
  1948  		t.Fatalf("expected: %#v, actual: %#v", &expected, out1)
  1949  	}
  1950  
  1951  	// This exercises the code path which adds the allocations made by the
  1952  	// planner and the number of unplaced allocations in the reconcile summaries
  1953  	// codepath
  1954  	out2, _ := state.JobSummaryByID(ws, alloc.Job.ID)
  1955  	expected = structs.JobSummary{
  1956  		JobID: alloc.Job.ID,
  1957  		Summary: map[string]structs.TaskGroupSummary{
  1958  			"web": structs.TaskGroupSummary{
  1959  				Queued:   9,
  1960  				Starting: 1,
  1961  			},
  1962  		},
  1963  		CreateIndex: 1010,
  1964  		ModifyIndex: out2.ModifyIndex,
  1965  	}
  1966  	if !reflect.DeepEqual(&expected, out2) {
  1967  		t.Fatalf("Diff % #v", pretty.Diff(&expected, out2))
  1968  	}
  1969  }