github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/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/nomad/mock"
    13  	"github.com/hashicorp/nomad/nomad/state"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/hashicorp/nomad/testutil"
    16  	"github.com/hashicorp/raft"
    17  )
    18  
    19  type MockSink struct {
    20  	*bytes.Buffer
    21  	cancel bool
    22  }
    23  
    24  func (m *MockSink) ID() string {
    25  	return "Mock"
    26  }
    27  
    28  func (m *MockSink) Cancel() error {
    29  	m.cancel = true
    30  	return nil
    31  }
    32  
    33  func (m *MockSink) Close() error {
    34  	return nil
    35  }
    36  
    37  func testStateStore(t *testing.T) *state.StateStore {
    38  	state, err := state.NewStateStore(os.Stderr)
    39  	if err != nil {
    40  		t.Fatalf("err: %v", err)
    41  	}
    42  	if state == nil {
    43  		t.Fatalf("missing state")
    44  	}
    45  	return state
    46  }
    47  
    48  func testFSM(t *testing.T) *nomadFSM {
    49  	p, _ := testPeriodicDispatcher()
    50  	broker := testBroker(t, 0)
    51  	blocked := NewBlockedEvals(broker)
    52  	fsm, err := NewFSM(broker, p, blocked, os.Stderr)
    53  	if err != nil {
    54  		t.Fatalf("err: %v", err)
    55  	}
    56  	if fsm == nil {
    57  		t.Fatalf("missing fsm")
    58  	}
    59  	return fsm
    60  }
    61  
    62  func makeLog(buf []byte) *raft.Log {
    63  	return &raft.Log{
    64  		Index: 1,
    65  		Term:  1,
    66  		Type:  raft.LogCommand,
    67  		Data:  buf,
    68  	}
    69  }
    70  
    71  func TestFSM_UpsertNode(t *testing.T) {
    72  	fsm := testFSM(t)
    73  	fsm.blockedEvals.SetEnabled(true)
    74  
    75  	node := mock.Node()
    76  
    77  	// Mark an eval as blocked.
    78  	eval := mock.Eval()
    79  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
    80  	fsm.blockedEvals.Block(eval)
    81  
    82  	req := structs.NodeRegisterRequest{
    83  		Node: node,
    84  	}
    85  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
    86  	if err != nil {
    87  		t.Fatalf("err: %v", err)
    88  	}
    89  
    90  	resp := fsm.Apply(makeLog(buf))
    91  	if resp != nil {
    92  		t.Fatalf("resp: %v", resp)
    93  	}
    94  
    95  	// Verify we are registered
    96  	ws := memdb.NewWatchSet()
    97  	n, err := fsm.State().NodeByID(ws, req.Node.ID)
    98  	if err != nil {
    99  		t.Fatalf("err: %v", err)
   100  	}
   101  	if n == nil {
   102  		t.Fatalf("not found!")
   103  	}
   104  	if n.CreateIndex != 1 {
   105  		t.Fatalf("bad index: %d", node.CreateIndex)
   106  	}
   107  
   108  	tt := fsm.TimeTable()
   109  	index := tt.NearestIndex(time.Now().UTC())
   110  	if index != 1 {
   111  		t.Fatalf("bad: %d", index)
   112  	}
   113  
   114  	// Verify the eval was unblocked.
   115  	testutil.WaitForResult(func() (bool, error) {
   116  		bStats := fsm.blockedEvals.Stats()
   117  		if bStats.TotalBlocked != 0 {
   118  			return false, fmt.Errorf("bad: %#v", bStats)
   119  		}
   120  		return true, nil
   121  	}, func(err error) {
   122  		t.Fatalf("err: %s", err)
   123  	})
   124  
   125  }
   126  
   127  func TestFSM_DeregisterNode(t *testing.T) {
   128  	fsm := testFSM(t)
   129  
   130  	node := mock.Node()
   131  	req := structs.NodeRegisterRequest{
   132  		Node: node,
   133  	}
   134  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   135  	if err != nil {
   136  		t.Fatalf("err: %v", err)
   137  	}
   138  
   139  	resp := fsm.Apply(makeLog(buf))
   140  	if resp != nil {
   141  		t.Fatalf("resp: %v", resp)
   142  	}
   143  
   144  	req2 := structs.NodeDeregisterRequest{
   145  		NodeID: node.ID,
   146  	}
   147  	buf, err = structs.Encode(structs.NodeDeregisterRequestType, req2)
   148  	if err != nil {
   149  		t.Fatalf("err: %v", err)
   150  	}
   151  
   152  	resp = fsm.Apply(makeLog(buf))
   153  	if resp != nil {
   154  		t.Fatalf("resp: %v", resp)
   155  	}
   156  
   157  	// Verify we are NOT registered
   158  	ws := memdb.NewWatchSet()
   159  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   160  	if err != nil {
   161  		t.Fatalf("err: %v", err)
   162  	}
   163  	if node != nil {
   164  		t.Fatalf("node found!")
   165  	}
   166  }
   167  
   168  func TestFSM_UpdateNodeStatus(t *testing.T) {
   169  	fsm := testFSM(t)
   170  	fsm.blockedEvals.SetEnabled(true)
   171  
   172  	node := mock.Node()
   173  	req := structs.NodeRegisterRequest{
   174  		Node: node,
   175  	}
   176  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   177  	if err != nil {
   178  		t.Fatalf("err: %v", err)
   179  	}
   180  
   181  	resp := fsm.Apply(makeLog(buf))
   182  	if resp != nil {
   183  		t.Fatalf("resp: %v", resp)
   184  	}
   185  
   186  	// Mark an eval as blocked.
   187  	eval := mock.Eval()
   188  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   189  	fsm.blockedEvals.Block(eval)
   190  
   191  	req2 := structs.NodeUpdateStatusRequest{
   192  		NodeID: node.ID,
   193  		Status: structs.NodeStatusReady,
   194  	}
   195  	buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2)
   196  	if err != nil {
   197  		t.Fatalf("err: %v", err)
   198  	}
   199  
   200  	resp = fsm.Apply(makeLog(buf))
   201  	if resp != nil {
   202  		t.Fatalf("resp: %v", resp)
   203  	}
   204  
   205  	// Verify the status is ready.
   206  	ws := memdb.NewWatchSet()
   207  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   208  	if err != nil {
   209  		t.Fatalf("err: %v", err)
   210  	}
   211  	if node.Status != structs.NodeStatusReady {
   212  		t.Fatalf("bad node: %#v", node)
   213  	}
   214  
   215  	// Verify the eval was unblocked.
   216  	testutil.WaitForResult(func() (bool, error) {
   217  		bStats := fsm.blockedEvals.Stats()
   218  		if bStats.TotalBlocked != 0 {
   219  			return false, fmt.Errorf("bad: %#v", bStats)
   220  		}
   221  		return true, nil
   222  	}, func(err error) {
   223  		t.Fatalf("err: %s", err)
   224  	})
   225  }
   226  
   227  func TestFSM_UpdateNodeDrain(t *testing.T) {
   228  	fsm := testFSM(t)
   229  
   230  	node := mock.Node()
   231  	req := structs.NodeRegisterRequest{
   232  		Node: node,
   233  	}
   234  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   235  	if err != nil {
   236  		t.Fatalf("err: %v", err)
   237  	}
   238  
   239  	resp := fsm.Apply(makeLog(buf))
   240  	if resp != nil {
   241  		t.Fatalf("resp: %v", resp)
   242  	}
   243  
   244  	req2 := structs.NodeUpdateDrainRequest{
   245  		NodeID: node.ID,
   246  		Drain:  true,
   247  	}
   248  	buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2)
   249  	if err != nil {
   250  		t.Fatalf("err: %v", err)
   251  	}
   252  
   253  	resp = fsm.Apply(makeLog(buf))
   254  	if resp != nil {
   255  		t.Fatalf("resp: %v", resp)
   256  	}
   257  
   258  	// Verify we are NOT registered
   259  	ws := memdb.NewWatchSet()
   260  	node, err = fsm.State().NodeByID(ws, req.Node.ID)
   261  	if err != nil {
   262  		t.Fatalf("err: %v", err)
   263  	}
   264  	if !node.Drain {
   265  		t.Fatalf("bad node: %#v", node)
   266  	}
   267  }
   268  
   269  func TestFSM_RegisterJob(t *testing.T) {
   270  	fsm := testFSM(t)
   271  
   272  	job := mock.PeriodicJob()
   273  	req := structs.JobRegisterRequest{
   274  		Job: job,
   275  	}
   276  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   277  	if err != nil {
   278  		t.Fatalf("err: %v", err)
   279  	}
   280  
   281  	resp := fsm.Apply(makeLog(buf))
   282  	if resp != nil {
   283  		t.Fatalf("resp: %v", resp)
   284  	}
   285  
   286  	// Verify we are registered
   287  	ws := memdb.NewWatchSet()
   288  	jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
   289  	if err != nil {
   290  		t.Fatalf("err: %v", err)
   291  	}
   292  	if jobOut == nil {
   293  		t.Fatalf("not found!")
   294  	}
   295  	if jobOut.CreateIndex != 1 {
   296  		t.Fatalf("bad index: %d", jobOut.CreateIndex)
   297  	}
   298  
   299  	// Verify it was added to the periodic runner.
   300  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok {
   301  		t.Fatal("job not added to periodic runner")
   302  	}
   303  
   304  	// Verify the launch time was tracked.
   305  	launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
   306  	if err != nil {
   307  		t.Fatalf("err: %v", err)
   308  	}
   309  	if launchOut == nil {
   310  		t.Fatalf("not found!")
   311  	}
   312  	if launchOut.Launch.IsZero() {
   313  		t.Fatalf("bad launch time: %v", launchOut.Launch)
   314  	}
   315  }
   316  
   317  func TestFSM_DeregisterJob(t *testing.T) {
   318  	fsm := testFSM(t)
   319  
   320  	job := mock.PeriodicJob()
   321  	req := structs.JobRegisterRequest{
   322  		Job: job,
   323  	}
   324  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   325  	if err != nil {
   326  		t.Fatalf("err: %v", err)
   327  	}
   328  
   329  	resp := fsm.Apply(makeLog(buf))
   330  	if resp != nil {
   331  		t.Fatalf("resp: %v", resp)
   332  	}
   333  
   334  	req2 := structs.JobDeregisterRequest{
   335  		JobID: job.ID,
   336  	}
   337  	buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
   338  	if err != nil {
   339  		t.Fatalf("err: %v", err)
   340  	}
   341  
   342  	resp = fsm.Apply(makeLog(buf))
   343  	if resp != nil {
   344  		t.Fatalf("resp: %v", resp)
   345  	}
   346  
   347  	// Verify we are NOT registered
   348  	ws := memdb.NewWatchSet()
   349  	jobOut, err := fsm.State().JobByID(ws, req.Job.ID)
   350  	if err != nil {
   351  		t.Fatalf("err: %v", err)
   352  	}
   353  	if jobOut != nil {
   354  		t.Fatalf("job found!")
   355  	}
   356  
   357  	// Verify it was removed from the periodic runner.
   358  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
   359  		t.Fatal("job not removed from periodic runner")
   360  	}
   361  
   362  	// Verify it was removed from the periodic launch table.
   363  	launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID)
   364  	if err != nil {
   365  		t.Fatalf("err: %v", err)
   366  	}
   367  	if launchOut != nil {
   368  		t.Fatalf("launch found!")
   369  	}
   370  }
   371  
   372  func TestFSM_UpdateEval(t *testing.T) {
   373  	fsm := testFSM(t)
   374  	fsm.evalBroker.SetEnabled(true)
   375  
   376  	req := structs.EvalUpdateRequest{
   377  		Evals: []*structs.Evaluation{mock.Eval()},
   378  	}
   379  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   380  	if err != nil {
   381  		t.Fatalf("err: %v", err)
   382  	}
   383  
   384  	resp := fsm.Apply(makeLog(buf))
   385  	if resp != nil {
   386  		t.Fatalf("resp: %v", resp)
   387  	}
   388  
   389  	// Verify we are registered
   390  	ws := memdb.NewWatchSet()
   391  	eval, err := fsm.State().EvalByID(ws, req.Evals[0].ID)
   392  	if err != nil {
   393  		t.Fatalf("err: %v", err)
   394  	}
   395  	if eval == nil {
   396  		t.Fatalf("not found!")
   397  	}
   398  	if eval.CreateIndex != 1 {
   399  		t.Fatalf("bad index: %d", eval.CreateIndex)
   400  	}
   401  
   402  	// Verify enqueued
   403  	stats := fsm.evalBroker.Stats()
   404  	if stats.TotalReady != 1 {
   405  		t.Fatalf("bad: %#v %#v", stats, eval)
   406  	}
   407  }
   408  
   409  func TestFSM_UpdateEval_Blocked(t *testing.T) {
   410  	fsm := testFSM(t)
   411  	fsm.evalBroker.SetEnabled(true)
   412  	fsm.blockedEvals.SetEnabled(true)
   413  
   414  	// Create a blocked eval.
   415  	eval := mock.Eval()
   416  	eval.Status = structs.EvalStatusBlocked
   417  
   418  	req := structs.EvalUpdateRequest{
   419  		Evals: []*structs.Evaluation{eval},
   420  	}
   421  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   422  	if err != nil {
   423  		t.Fatalf("err: %v", err)
   424  	}
   425  
   426  	resp := fsm.Apply(makeLog(buf))
   427  	if resp != nil {
   428  		t.Fatalf("resp: %v", resp)
   429  	}
   430  
   431  	// Verify we are registered
   432  	ws := memdb.NewWatchSet()
   433  	out, err := fsm.State().EvalByID(ws, eval.ID)
   434  	if err != nil {
   435  		t.Fatalf("err: %v", err)
   436  	}
   437  	if out == nil {
   438  		t.Fatalf("not found!")
   439  	}
   440  	if out.CreateIndex != 1 {
   441  		t.Fatalf("bad index: %d", out.CreateIndex)
   442  	}
   443  
   444  	// Verify the eval wasn't enqueued
   445  	stats := fsm.evalBroker.Stats()
   446  	if stats.TotalReady != 0 {
   447  		t.Fatalf("bad: %#v %#v", stats, out)
   448  	}
   449  
   450  	// Verify the eval was added to the blocked tracker.
   451  	bStats := fsm.blockedEvals.Stats()
   452  	if bStats.TotalBlocked != 1 {
   453  		t.Fatalf("bad: %#v %#v", bStats, out)
   454  	}
   455  }
   456  
   457  func TestFSM_UpdateEval_Untrack(t *testing.T) {
   458  	fsm := testFSM(t)
   459  	fsm.evalBroker.SetEnabled(true)
   460  	fsm.blockedEvals.SetEnabled(true)
   461  
   462  	// Mark an eval as blocked.
   463  	bEval := mock.Eval()
   464  	bEval.ClassEligibility = map[string]bool{"v1:123": true}
   465  	fsm.blockedEvals.Block(bEval)
   466  
   467  	// Create a successful eval for the same job
   468  	eval := mock.Eval()
   469  	eval.JobID = bEval.JobID
   470  	eval.Status = structs.EvalStatusComplete
   471  
   472  	req := structs.EvalUpdateRequest{
   473  		Evals: []*structs.Evaluation{eval},
   474  	}
   475  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   476  	if err != nil {
   477  		t.Fatalf("err: %v", err)
   478  	}
   479  
   480  	resp := fsm.Apply(makeLog(buf))
   481  	if resp != nil {
   482  		t.Fatalf("resp: %v", resp)
   483  	}
   484  
   485  	// Verify we are registered
   486  	ws := memdb.NewWatchSet()
   487  	out, err := fsm.State().EvalByID(ws, eval.ID)
   488  	if err != nil {
   489  		t.Fatalf("err: %v", err)
   490  	}
   491  	if out == nil {
   492  		t.Fatalf("not found!")
   493  	}
   494  	if out.CreateIndex != 1 {
   495  		t.Fatalf("bad index: %d", out.CreateIndex)
   496  	}
   497  
   498  	// Verify the eval wasn't enqueued
   499  	stats := fsm.evalBroker.Stats()
   500  	if stats.TotalReady != 0 {
   501  		t.Fatalf("bad: %#v %#v", stats, out)
   502  	}
   503  
   504  	// Verify the eval was untracked in the blocked tracker.
   505  	bStats := fsm.blockedEvals.Stats()
   506  	if bStats.TotalBlocked != 0 {
   507  		t.Fatalf("bad: %#v %#v", bStats, out)
   508  	}
   509  }
   510  
   511  func TestFSM_UpdateEval_NoUntrack(t *testing.T) {
   512  	fsm := testFSM(t)
   513  	fsm.evalBroker.SetEnabled(true)
   514  	fsm.blockedEvals.SetEnabled(true)
   515  
   516  	// Mark an eval as blocked.
   517  	bEval := mock.Eval()
   518  	bEval.ClassEligibility = map[string]bool{"v1:123": true}
   519  	fsm.blockedEvals.Block(bEval)
   520  
   521  	// Create a successful eval for the same job but with placement failures
   522  	eval := mock.Eval()
   523  	eval.JobID = bEval.JobID
   524  	eval.Status = structs.EvalStatusComplete
   525  	eval.FailedTGAllocs = make(map[string]*structs.AllocMetric)
   526  	eval.FailedTGAllocs["test"] = new(structs.AllocMetric)
   527  
   528  	req := structs.EvalUpdateRequest{
   529  		Evals: []*structs.Evaluation{eval},
   530  	}
   531  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   532  	if err != nil {
   533  		t.Fatalf("err: %v", err)
   534  	}
   535  
   536  	resp := fsm.Apply(makeLog(buf))
   537  	if resp != nil {
   538  		t.Fatalf("resp: %v", resp)
   539  	}
   540  
   541  	// Verify we are registered
   542  	ws := memdb.NewWatchSet()
   543  	out, err := fsm.State().EvalByID(ws, eval.ID)
   544  	if err != nil {
   545  		t.Fatalf("err: %v", err)
   546  	}
   547  	if out == nil {
   548  		t.Fatalf("not found!")
   549  	}
   550  	if out.CreateIndex != 1 {
   551  		t.Fatalf("bad index: %d", out.CreateIndex)
   552  	}
   553  
   554  	// Verify the eval wasn't enqueued
   555  	stats := fsm.evalBroker.Stats()
   556  	if stats.TotalReady != 0 {
   557  		t.Fatalf("bad: %#v %#v", stats, out)
   558  	}
   559  
   560  	// Verify the eval was not untracked in the blocked tracker.
   561  	bStats := fsm.blockedEvals.Stats()
   562  	if bStats.TotalBlocked != 1 {
   563  		t.Fatalf("bad: %#v %#v", bStats, out)
   564  	}
   565  }
   566  
   567  func TestFSM_DeleteEval(t *testing.T) {
   568  	fsm := testFSM(t)
   569  
   570  	eval := mock.Eval()
   571  	req := structs.EvalUpdateRequest{
   572  		Evals: []*structs.Evaluation{eval},
   573  	}
   574  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   575  	if err != nil {
   576  		t.Fatalf("err: %v", err)
   577  	}
   578  
   579  	resp := fsm.Apply(makeLog(buf))
   580  	if resp != nil {
   581  		t.Fatalf("resp: %v", resp)
   582  	}
   583  
   584  	req2 := structs.EvalDeleteRequest{
   585  		Evals: []string{eval.ID},
   586  	}
   587  	buf, err = structs.Encode(structs.EvalDeleteRequestType, req2)
   588  	if err != nil {
   589  		t.Fatalf("err: %v", err)
   590  	}
   591  
   592  	resp = fsm.Apply(makeLog(buf))
   593  	if resp != nil {
   594  		t.Fatalf("resp: %v", resp)
   595  	}
   596  
   597  	// Verify we are NOT registered
   598  	ws := memdb.NewWatchSet()
   599  	eval, err = fsm.State().EvalByID(ws, req.Evals[0].ID)
   600  	if err != nil {
   601  		t.Fatalf("err: %v", err)
   602  	}
   603  	if eval != nil {
   604  		t.Fatalf("eval found!")
   605  	}
   606  }
   607  
   608  func TestFSM_UpsertAllocs(t *testing.T) {
   609  	fsm := testFSM(t)
   610  
   611  	alloc := mock.Alloc()
   612  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   613  	req := structs.AllocUpdateRequest{
   614  		Alloc: []*structs.Allocation{alloc},
   615  	}
   616  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   617  	if err != nil {
   618  		t.Fatalf("err: %v", err)
   619  	}
   620  
   621  	resp := fsm.Apply(makeLog(buf))
   622  	if resp != nil {
   623  		t.Fatalf("resp: %v", resp)
   624  	}
   625  
   626  	// Verify we are registered
   627  	ws := memdb.NewWatchSet()
   628  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   629  	if err != nil {
   630  		t.Fatalf("err: %v", err)
   631  	}
   632  	alloc.CreateIndex = out.CreateIndex
   633  	alloc.ModifyIndex = out.ModifyIndex
   634  	alloc.AllocModifyIndex = out.AllocModifyIndex
   635  	if !reflect.DeepEqual(alloc, out) {
   636  		t.Fatalf("bad: %#v %#v", alloc, out)
   637  	}
   638  
   639  	evictAlloc := new(structs.Allocation)
   640  	*evictAlloc = *alloc
   641  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   642  	req2 := structs.AllocUpdateRequest{
   643  		Alloc: []*structs.Allocation{evictAlloc},
   644  	}
   645  	buf, err = structs.Encode(structs.AllocUpdateRequestType, req2)
   646  	if err != nil {
   647  		t.Fatalf("err: %v", err)
   648  	}
   649  
   650  	resp = fsm.Apply(makeLog(buf))
   651  	if resp != nil {
   652  		t.Fatalf("resp: %v", resp)
   653  	}
   654  
   655  	// Verify we are evicted
   656  	out, err = fsm.State().AllocByID(ws, alloc.ID)
   657  	if err != nil {
   658  		t.Fatalf("err: %v", err)
   659  	}
   660  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   661  		t.Fatalf("alloc found!")
   662  	}
   663  }
   664  
   665  func TestFSM_UpsertAllocs_SharedJob(t *testing.T) {
   666  	fsm := testFSM(t)
   667  
   668  	alloc := mock.Alloc()
   669  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   670  	job := alloc.Job
   671  	alloc.Job = nil
   672  	req := structs.AllocUpdateRequest{
   673  		Job:   job,
   674  		Alloc: []*structs.Allocation{alloc},
   675  	}
   676  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   677  	if err != nil {
   678  		t.Fatalf("err: %v", err)
   679  	}
   680  
   681  	resp := fsm.Apply(makeLog(buf))
   682  	if resp != nil {
   683  		t.Fatalf("resp: %v", resp)
   684  	}
   685  
   686  	// Verify we are registered
   687  	ws := memdb.NewWatchSet()
   688  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   689  	if err != nil {
   690  		t.Fatalf("err: %v", err)
   691  	}
   692  	alloc.CreateIndex = out.CreateIndex
   693  	alloc.ModifyIndex = out.ModifyIndex
   694  	alloc.AllocModifyIndex = out.AllocModifyIndex
   695  
   696  	// Job should be re-attached
   697  	alloc.Job = job
   698  	if !reflect.DeepEqual(alloc, out) {
   699  		t.Fatalf("bad: %#v %#v", alloc, out)
   700  	}
   701  
   702  	// Ensure that the original job is used
   703  	evictAlloc := new(structs.Allocation)
   704  	*evictAlloc = *alloc
   705  	job = mock.Job()
   706  	job.Priority = 123
   707  
   708  	evictAlloc.Job = nil
   709  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   710  	req2 := structs.AllocUpdateRequest{
   711  		Job:   job,
   712  		Alloc: []*structs.Allocation{evictAlloc},
   713  	}
   714  	buf, err = structs.Encode(structs.AllocUpdateRequestType, req2)
   715  	if err != nil {
   716  		t.Fatalf("err: %v", err)
   717  	}
   718  
   719  	resp = fsm.Apply(makeLog(buf))
   720  	if resp != nil {
   721  		t.Fatalf("resp: %v", resp)
   722  	}
   723  
   724  	// Verify we are evicted
   725  	out, err = fsm.State().AllocByID(ws, alloc.ID)
   726  	if err != nil {
   727  		t.Fatalf("err: %v", err)
   728  	}
   729  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   730  		t.Fatalf("alloc found!")
   731  	}
   732  	if out.Job == nil || out.Job.Priority == 123 {
   733  		t.Fatalf("bad job")
   734  	}
   735  }
   736  
   737  func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) {
   738  	fsm := testFSM(t)
   739  
   740  	alloc := mock.Alloc()
   741  	fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID))
   742  	job := alloc.Job
   743  	resources := alloc.Resources
   744  	alloc.Resources = nil
   745  	req := structs.AllocUpdateRequest{
   746  		Job:   job,
   747  		Alloc: []*structs.Allocation{alloc},
   748  	}
   749  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   750  	if err != nil {
   751  		t.Fatalf("err: %v", err)
   752  	}
   753  
   754  	resp := fsm.Apply(makeLog(buf))
   755  	if resp != nil {
   756  		t.Fatalf("resp: %v", resp)
   757  	}
   758  
   759  	// Verify we are registered
   760  	ws := memdb.NewWatchSet()
   761  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   762  	if err != nil {
   763  		t.Fatalf("err: %v", err)
   764  	}
   765  	alloc.CreateIndex = out.CreateIndex
   766  	alloc.ModifyIndex = out.ModifyIndex
   767  	alloc.AllocModifyIndex = out.AllocModifyIndex
   768  
   769  	// Resources should be recomputed
   770  	resources.DiskMB = alloc.Job.TaskGroups[0].EphemeralDisk.SizeMB
   771  	alloc.Resources = resources
   772  	if !reflect.DeepEqual(alloc, out) {
   773  		t.Fatalf("bad: %#v %#v", alloc, out)
   774  	}
   775  }
   776  
   777  func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) {
   778  	fsm := testFSM(t)
   779  	fsm.blockedEvals.SetEnabled(true)
   780  	state := fsm.State()
   781  
   782  	node := mock.Node()
   783  	state.UpsertNode(1, node)
   784  
   785  	// Mark an eval as blocked.
   786  	eval := mock.Eval()
   787  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   788  	fsm.blockedEvals.Block(eval)
   789  
   790  	bStats := fsm.blockedEvals.Stats()
   791  	if bStats.TotalBlocked != 1 {
   792  		t.Fatalf("bad: %#v", bStats)
   793  	}
   794  
   795  	// Create a completed eval
   796  	alloc := mock.Alloc()
   797  	alloc.NodeID = node.ID
   798  	alloc2 := mock.Alloc()
   799  	alloc2.NodeID = node.ID
   800  	state.UpsertJobSummary(8, mock.JobSummary(alloc.JobID))
   801  	state.UpsertJobSummary(9, mock.JobSummary(alloc2.JobID))
   802  	state.UpsertAllocs(10, []*structs.Allocation{alloc, alloc2})
   803  
   804  	clientAlloc := new(structs.Allocation)
   805  	*clientAlloc = *alloc
   806  	clientAlloc.ClientStatus = structs.AllocClientStatusComplete
   807  	update2 := &structs.Allocation{
   808  		ID:           alloc2.ID,
   809  		ClientStatus: structs.AllocClientStatusRunning,
   810  	}
   811  
   812  	req := structs.AllocUpdateRequest{
   813  		Alloc: []*structs.Allocation{clientAlloc, update2},
   814  	}
   815  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   816  	if err != nil {
   817  		t.Fatalf("err: %v", err)
   818  	}
   819  
   820  	resp := fsm.Apply(makeLog(buf))
   821  	if resp != nil {
   822  		t.Fatalf("resp: %v", resp)
   823  	}
   824  
   825  	// Verify we are updated
   826  	ws := memdb.NewWatchSet()
   827  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   828  	if err != nil {
   829  		t.Fatalf("err: %v", err)
   830  	}
   831  	clientAlloc.CreateIndex = out.CreateIndex
   832  	clientAlloc.ModifyIndex = out.ModifyIndex
   833  	if !reflect.DeepEqual(clientAlloc, out) {
   834  		t.Fatalf("bad: %#v %#v", clientAlloc, out)
   835  	}
   836  
   837  	out, err = fsm.State().AllocByID(ws, alloc2.ID)
   838  	if err != nil {
   839  		t.Fatalf("err: %v", err)
   840  	}
   841  	alloc2.CreateIndex = out.CreateIndex
   842  	alloc2.ModifyIndex = out.ModifyIndex
   843  	alloc2.ClientStatus = structs.AllocClientStatusRunning
   844  	alloc2.TaskStates = nil
   845  	if !reflect.DeepEqual(alloc2, out) {
   846  		t.Fatalf("bad: %#v %#v", alloc2, out)
   847  	}
   848  
   849  	// Verify the eval was unblocked.
   850  	testutil.WaitForResult(func() (bool, error) {
   851  		bStats = fsm.blockedEvals.Stats()
   852  		if bStats.TotalBlocked != 0 {
   853  			return false, fmt.Errorf("bad: %#v %#v", bStats, out)
   854  		}
   855  		return true, nil
   856  	}, func(err error) {
   857  		t.Fatalf("err: %s", err)
   858  	})
   859  }
   860  
   861  func TestFSM_UpdateAllocFromClient(t *testing.T) {
   862  	fsm := testFSM(t)
   863  	state := fsm.State()
   864  
   865  	alloc := mock.Alloc()
   866  	state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID))
   867  	state.UpsertAllocs(10, []*structs.Allocation{alloc})
   868  
   869  	clientAlloc := new(structs.Allocation)
   870  	*clientAlloc = *alloc
   871  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   872  
   873  	req := structs.AllocUpdateRequest{
   874  		Alloc: []*structs.Allocation{clientAlloc},
   875  	}
   876  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   877  	if err != nil {
   878  		t.Fatalf("err: %v", err)
   879  	}
   880  
   881  	resp := fsm.Apply(makeLog(buf))
   882  	if resp != nil {
   883  		t.Fatalf("resp: %v", resp)
   884  	}
   885  
   886  	// Verify we are registered
   887  	ws := memdb.NewWatchSet()
   888  	out, err := fsm.State().AllocByID(ws, alloc.ID)
   889  	if err != nil {
   890  		t.Fatalf("err: %v", err)
   891  	}
   892  	clientAlloc.CreateIndex = out.CreateIndex
   893  	clientAlloc.ModifyIndex = out.ModifyIndex
   894  	if !reflect.DeepEqual(clientAlloc, out) {
   895  		t.Fatalf("err: %#v,%#v", clientAlloc, out)
   896  	}
   897  }
   898  
   899  func TestFSM_UpsertVaultAccessor(t *testing.T) {
   900  	fsm := testFSM(t)
   901  	fsm.blockedEvals.SetEnabled(true)
   902  
   903  	va := mock.VaultAccessor()
   904  	va2 := mock.VaultAccessor()
   905  	req := structs.VaultAccessorsRequest{
   906  		Accessors: []*structs.VaultAccessor{va, va2},
   907  	}
   908  	buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req)
   909  	if err != nil {
   910  		t.Fatalf("err: %v", err)
   911  	}
   912  
   913  	resp := fsm.Apply(makeLog(buf))
   914  	if resp != nil {
   915  		t.Fatalf("resp: %v", resp)
   916  	}
   917  
   918  	// Verify we are registered
   919  	ws := memdb.NewWatchSet()
   920  	out1, err := fsm.State().VaultAccessor(ws, va.Accessor)
   921  	if err != nil {
   922  		t.Fatalf("err: %v", err)
   923  	}
   924  	if out1 == nil {
   925  		t.Fatalf("not found!")
   926  	}
   927  	if out1.CreateIndex != 1 {
   928  		t.Fatalf("bad index: %d", out1.CreateIndex)
   929  	}
   930  	out2, err := fsm.State().VaultAccessor(ws, va2.Accessor)
   931  	if err != nil {
   932  		t.Fatalf("err: %v", err)
   933  	}
   934  	if out2 == nil {
   935  		t.Fatalf("not found!")
   936  	}
   937  	if out1.CreateIndex != 1 {
   938  		t.Fatalf("bad index: %d", out2.CreateIndex)
   939  	}
   940  
   941  	tt := fsm.TimeTable()
   942  	index := tt.NearestIndex(time.Now().UTC())
   943  	if index != 1 {
   944  		t.Fatalf("bad: %d", index)
   945  	}
   946  }
   947  
   948  func TestFSM_DeregisterVaultAccessor(t *testing.T) {
   949  	fsm := testFSM(t)
   950  	fsm.blockedEvals.SetEnabled(true)
   951  
   952  	va := mock.VaultAccessor()
   953  	va2 := mock.VaultAccessor()
   954  	accessors := []*structs.VaultAccessor{va, va2}
   955  
   956  	// Insert the accessors
   957  	if err := fsm.State().UpsertVaultAccessor(1000, accessors); err != nil {
   958  		t.Fatalf("bad: %v", err)
   959  	}
   960  
   961  	req := structs.VaultAccessorsRequest{
   962  		Accessors: accessors,
   963  	}
   964  	buf, err := structs.Encode(structs.VaultAccessorDegisterRequestType, req)
   965  	if err != nil {
   966  		t.Fatalf("err: %v", err)
   967  	}
   968  
   969  	resp := fsm.Apply(makeLog(buf))
   970  	if resp != nil {
   971  		t.Fatalf("resp: %v", resp)
   972  	}
   973  
   974  	ws := memdb.NewWatchSet()
   975  	out1, err := fsm.State().VaultAccessor(ws, va.Accessor)
   976  	if err != nil {
   977  		t.Fatalf("err: %v", err)
   978  	}
   979  	if out1 != nil {
   980  		t.Fatalf("not deleted!")
   981  	}
   982  
   983  	tt := fsm.TimeTable()
   984  	index := tt.NearestIndex(time.Now().UTC())
   985  	if index != 1 {
   986  		t.Fatalf("bad: %d", index)
   987  	}
   988  }
   989  
   990  func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM {
   991  	// Snapshot
   992  	snap, err := fsm.Snapshot()
   993  	if err != nil {
   994  		t.Fatalf("err: %v", err)
   995  	}
   996  	defer snap.Release()
   997  
   998  	// Persist
   999  	buf := bytes.NewBuffer(nil)
  1000  	sink := &MockSink{buf, false}
  1001  	if err := snap.Persist(sink); err != nil {
  1002  		t.Fatalf("err: %v", err)
  1003  	}
  1004  
  1005  	// Try to restore on a new FSM
  1006  	fsm2 := testFSM(t)
  1007  	snap, err = fsm2.Snapshot()
  1008  	if err != nil {
  1009  		t.Fatalf("err: %v", err)
  1010  	}
  1011  	defer snap.Release()
  1012  
  1013  	abandonCh := fsm2.State().AbandonCh()
  1014  
  1015  	// Do a restore
  1016  	if err := fsm2.Restore(sink); err != nil {
  1017  		t.Fatalf("err: %v", err)
  1018  	}
  1019  
  1020  	select {
  1021  	case <-abandonCh:
  1022  	default:
  1023  		t.Fatalf("bad")
  1024  	}
  1025  
  1026  	return fsm2
  1027  }
  1028  
  1029  func TestFSM_SnapshotRestore_Nodes(t *testing.T) {
  1030  	// Add some state
  1031  	fsm := testFSM(t)
  1032  	state := fsm.State()
  1033  	node1 := mock.Node()
  1034  	state.UpsertNode(1000, node1)
  1035  	node2 := mock.Node()
  1036  	state.UpsertNode(1001, node2)
  1037  
  1038  	// Verify the contents
  1039  	fsm2 := testSnapshotRestore(t, fsm)
  1040  	state2 := fsm2.State()
  1041  	ws := memdb.NewWatchSet()
  1042  	out1, _ := state2.NodeByID(ws, node1.ID)
  1043  	out2, _ := state2.NodeByID(ws, node2.ID)
  1044  	if !reflect.DeepEqual(node1, out1) {
  1045  		t.Fatalf("bad: \n%#v\n%#v", out1, node1)
  1046  	}
  1047  	if !reflect.DeepEqual(node2, out2) {
  1048  		t.Fatalf("bad: \n%#v\n%#v", out2, node2)
  1049  	}
  1050  }
  1051  
  1052  func TestFSM_SnapshotRestore_Jobs(t *testing.T) {
  1053  	// Add some state
  1054  	fsm := testFSM(t)
  1055  	state := fsm.State()
  1056  	job1 := mock.Job()
  1057  	state.UpsertJob(1000, job1)
  1058  	job2 := mock.Job()
  1059  	state.UpsertJob(1001, job2)
  1060  
  1061  	// Verify the contents
  1062  	ws := memdb.NewWatchSet()
  1063  	fsm2 := testSnapshotRestore(t, fsm)
  1064  	state2 := fsm2.State()
  1065  	out1, _ := state2.JobByID(ws, job1.ID)
  1066  	out2, _ := state2.JobByID(ws, job2.ID)
  1067  	if !reflect.DeepEqual(job1, out1) {
  1068  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
  1069  	}
  1070  	if !reflect.DeepEqual(job2, out2) {
  1071  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
  1072  	}
  1073  }
  1074  
  1075  func TestFSM_SnapshotRestore_Evals(t *testing.T) {
  1076  	// Add some state
  1077  	fsm := testFSM(t)
  1078  	state := fsm.State()
  1079  	eval1 := mock.Eval()
  1080  	state.UpsertEvals(1000, []*structs.Evaluation{eval1})
  1081  	eval2 := mock.Eval()
  1082  	state.UpsertEvals(1001, []*structs.Evaluation{eval2})
  1083  
  1084  	// Verify the contents
  1085  	fsm2 := testSnapshotRestore(t, fsm)
  1086  	state2 := fsm2.State()
  1087  	ws := memdb.NewWatchSet()
  1088  	out1, _ := state2.EvalByID(ws, eval1.ID)
  1089  	out2, _ := state2.EvalByID(ws, eval2.ID)
  1090  	if !reflect.DeepEqual(eval1, out1) {
  1091  		t.Fatalf("bad: \n%#v\n%#v", out1, eval1)
  1092  	}
  1093  	if !reflect.DeepEqual(eval2, out2) {
  1094  		t.Fatalf("bad: \n%#v\n%#v", out2, eval2)
  1095  	}
  1096  }
  1097  
  1098  func TestFSM_SnapshotRestore_Allocs(t *testing.T) {
  1099  	// Add some state
  1100  	fsm := testFSM(t)
  1101  	state := fsm.State()
  1102  	alloc1 := mock.Alloc()
  1103  	alloc2 := mock.Alloc()
  1104  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1105  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1106  	state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
  1107  	state.UpsertAllocs(1001, []*structs.Allocation{alloc2})
  1108  
  1109  	// Verify the contents
  1110  	fsm2 := testSnapshotRestore(t, fsm)
  1111  	state2 := fsm2.State()
  1112  	ws := memdb.NewWatchSet()
  1113  	out1, _ := state2.AllocByID(ws, alloc1.ID)
  1114  	out2, _ := state2.AllocByID(ws, alloc2.ID)
  1115  	if !reflect.DeepEqual(alloc1, out1) {
  1116  		t.Fatalf("bad: \n%#v\n%#v", out1, alloc1)
  1117  	}
  1118  	if !reflect.DeepEqual(alloc2, out2) {
  1119  		t.Fatalf("bad: \n%#v\n%#v", out2, alloc2)
  1120  	}
  1121  }
  1122  
  1123  func TestFSM_SnapshotRestore_Allocs_NoSharedResources(t *testing.T) {
  1124  	// Add some state
  1125  	fsm := testFSM(t)
  1126  	state := fsm.State()
  1127  	alloc1 := mock.Alloc()
  1128  	alloc2 := mock.Alloc()
  1129  	alloc1.SharedResources = nil
  1130  	alloc2.SharedResources = nil
  1131  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1132  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1133  	state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
  1134  	state.UpsertAllocs(1001, []*structs.Allocation{alloc2})
  1135  
  1136  	// Verify the contents
  1137  	fsm2 := testSnapshotRestore(t, fsm)
  1138  	state2 := fsm2.State()
  1139  	ws := memdb.NewWatchSet()
  1140  	out1, _ := state2.AllocByID(ws, alloc1.ID)
  1141  	out2, _ := state2.AllocByID(ws, alloc2.ID)
  1142  	alloc1.SharedResources = &structs.Resources{DiskMB: 150}
  1143  	alloc2.SharedResources = &structs.Resources{DiskMB: 150}
  1144  
  1145  	if !reflect.DeepEqual(alloc1, out1) {
  1146  		t.Fatalf("bad: \n%#v\n%#v", out1, alloc1)
  1147  	}
  1148  	if !reflect.DeepEqual(alloc2, out2) {
  1149  		t.Fatalf("bad: \n%#v\n%#v", out2, alloc2)
  1150  	}
  1151  }
  1152  
  1153  func TestFSM_SnapshotRestore_Indexes(t *testing.T) {
  1154  	// Add some state
  1155  	fsm := testFSM(t)
  1156  	state := fsm.State()
  1157  	node1 := mock.Node()
  1158  	state.UpsertNode(1000, node1)
  1159  
  1160  	// Verify the contents
  1161  	fsm2 := testSnapshotRestore(t, fsm)
  1162  	state2 := fsm2.State()
  1163  
  1164  	index, err := state2.Index("nodes")
  1165  	if err != nil {
  1166  		t.Fatalf("err: %v", err)
  1167  	}
  1168  	if index != 1000 {
  1169  		t.Fatalf("bad: %d", index)
  1170  	}
  1171  }
  1172  
  1173  func TestFSM_SnapshotRestore_TimeTable(t *testing.T) {
  1174  	// Add some state
  1175  	fsm := testFSM(t)
  1176  
  1177  	tt := fsm.TimeTable()
  1178  	start := time.Now().UTC()
  1179  	tt.Witness(1000, start)
  1180  	tt.Witness(2000, start.Add(10*time.Minute))
  1181  
  1182  	// Verify the contents
  1183  	fsm2 := testSnapshotRestore(t, fsm)
  1184  
  1185  	tt2 := fsm2.TimeTable()
  1186  	if tt2.NearestTime(1500) != start {
  1187  		t.Fatalf("bad")
  1188  	}
  1189  	if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 {
  1190  		t.Fatalf("bad")
  1191  	}
  1192  }
  1193  
  1194  func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) {
  1195  	// Add some state
  1196  	fsm := testFSM(t)
  1197  	state := fsm.State()
  1198  	job1 := mock.Job()
  1199  	launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()}
  1200  	state.UpsertPeriodicLaunch(1000, launch1)
  1201  	job2 := mock.Job()
  1202  	launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()}
  1203  	state.UpsertPeriodicLaunch(1001, launch2)
  1204  
  1205  	// Verify the contents
  1206  	fsm2 := testSnapshotRestore(t, fsm)
  1207  	state2 := fsm2.State()
  1208  	ws := memdb.NewWatchSet()
  1209  	out1, _ := state2.PeriodicLaunchByID(ws, launch1.ID)
  1210  	out2, _ := state2.PeriodicLaunchByID(ws, launch2.ID)
  1211  	if !reflect.DeepEqual(launch1, out1) {
  1212  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
  1213  	}
  1214  	if !reflect.DeepEqual(launch2, out2) {
  1215  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
  1216  	}
  1217  }
  1218  
  1219  func TestFSM_SnapshotRestore_JobSummary(t *testing.T) {
  1220  	// Add some state
  1221  	fsm := testFSM(t)
  1222  	state := fsm.State()
  1223  
  1224  	job1 := mock.Job()
  1225  	state.UpsertJob(1000, job1)
  1226  	ws := memdb.NewWatchSet()
  1227  	js1, _ := state.JobSummaryByID(ws, job1.ID)
  1228  
  1229  	job2 := mock.Job()
  1230  	state.UpsertJob(1001, job2)
  1231  	js2, _ := state.JobSummaryByID(ws, job2.ID)
  1232  
  1233  	// Verify the contents
  1234  	fsm2 := testSnapshotRestore(t, fsm)
  1235  	state2 := fsm2.State()
  1236  	out1, _ := state2.JobSummaryByID(ws, job1.ID)
  1237  	out2, _ := state2.JobSummaryByID(ws, job2.ID)
  1238  	if !reflect.DeepEqual(js1, out1) {
  1239  		t.Fatalf("bad: \n%#v\n%#v", js1, out1)
  1240  	}
  1241  	if !reflect.DeepEqual(js2, out2) {
  1242  		t.Fatalf("bad: \n%#v\n%#v", js2, out2)
  1243  	}
  1244  }
  1245  
  1246  func TestFSM_SnapshotRestore_VaultAccessors(t *testing.T) {
  1247  	// Add some state
  1248  	fsm := testFSM(t)
  1249  	state := fsm.State()
  1250  	a1 := mock.VaultAccessor()
  1251  	a2 := mock.VaultAccessor()
  1252  	state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a1, a2})
  1253  
  1254  	// Verify the contents
  1255  	fsm2 := testSnapshotRestore(t, fsm)
  1256  	state2 := fsm2.State()
  1257  	ws := memdb.NewWatchSet()
  1258  	out1, _ := state2.VaultAccessor(ws, a1.Accessor)
  1259  	out2, _ := state2.VaultAccessor(ws, a2.Accessor)
  1260  	if !reflect.DeepEqual(a1, out1) {
  1261  		t.Fatalf("bad: \n%#v\n%#v", out1, a1)
  1262  	}
  1263  	if !reflect.DeepEqual(a2, out2) {
  1264  		t.Fatalf("bad: \n%#v\n%#v", out2, a2)
  1265  	}
  1266  }
  1267  
  1268  func TestFSM_SnapshotRestore_AddMissingSummary(t *testing.T) {
  1269  	// Add some state
  1270  	fsm := testFSM(t)
  1271  	state := fsm.State()
  1272  
  1273  	// make an allocation
  1274  	alloc := mock.Alloc()
  1275  	state.UpsertJob(1010, alloc.Job)
  1276  	state.UpsertAllocs(1011, []*structs.Allocation{alloc})
  1277  
  1278  	// Delete the summary
  1279  	state.DeleteJobSummary(1040, alloc.Job.ID)
  1280  
  1281  	// Delete the index
  1282  	if err := state.RemoveIndex("job_summary"); err != nil {
  1283  		t.Fatalf("err: %v", err)
  1284  	}
  1285  
  1286  	fsm2 := testSnapshotRestore(t, fsm)
  1287  	state2 := fsm2.State()
  1288  	latestIndex, _ := state.LatestIndex()
  1289  
  1290  	ws := memdb.NewWatchSet()
  1291  	out, _ := state2.JobSummaryByID(ws, alloc.Job.ID)
  1292  	expected := structs.JobSummary{
  1293  		JobID: alloc.Job.ID,
  1294  		Summary: map[string]structs.TaskGroupSummary{
  1295  			"web": structs.TaskGroupSummary{
  1296  				Starting: 1,
  1297  			},
  1298  		},
  1299  		CreateIndex: 1010,
  1300  		ModifyIndex: latestIndex,
  1301  	}
  1302  	if !reflect.DeepEqual(&expected, out) {
  1303  		t.Fatalf("expected: %#v, actual: %#v", &expected, out)
  1304  	}
  1305  }
  1306  
  1307  func TestFSM_ReconcileSummaries(t *testing.T) {
  1308  	// Add some state
  1309  	fsm := testFSM(t)
  1310  	state := fsm.State()
  1311  
  1312  	// Add a node
  1313  	node := mock.Node()
  1314  	state.UpsertNode(800, node)
  1315  
  1316  	// Make a job so that none of the tasks can be placed
  1317  	job1 := mock.Job()
  1318  	job1.TaskGroups[0].Tasks[0].Resources.CPU = 5000
  1319  	state.UpsertJob(1000, job1)
  1320  
  1321  	// make a job which can make partial progress
  1322  	alloc := mock.Alloc()
  1323  	alloc.NodeID = node.ID
  1324  	state.UpsertJob(1010, alloc.Job)
  1325  	state.UpsertAllocs(1011, []*structs.Allocation{alloc})
  1326  
  1327  	// Delete the summaries
  1328  	state.DeleteJobSummary(1030, job1.ID)
  1329  	state.DeleteJobSummary(1040, alloc.Job.ID)
  1330  
  1331  	req := structs.GenericRequest{}
  1332  	buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req)
  1333  	if err != nil {
  1334  		t.Fatalf("err: %v", err)
  1335  	}
  1336  
  1337  	resp := fsm.Apply(makeLog(buf))
  1338  	if resp != nil {
  1339  		t.Fatalf("resp: %v", resp)
  1340  	}
  1341  
  1342  	ws := memdb.NewWatchSet()
  1343  	out1, _ := state.JobSummaryByID(ws, job1.ID)
  1344  	expected := structs.JobSummary{
  1345  		JobID: job1.ID,
  1346  		Summary: map[string]structs.TaskGroupSummary{
  1347  			"web": structs.TaskGroupSummary{
  1348  				Queued: 10,
  1349  			},
  1350  		},
  1351  		CreateIndex: 1000,
  1352  		ModifyIndex: out1.ModifyIndex,
  1353  	}
  1354  	if !reflect.DeepEqual(&expected, out1) {
  1355  		t.Fatalf("expected: %#v, actual: %#v", &expected, out1)
  1356  	}
  1357  
  1358  	// This exercises the code path which adds the allocations made by the
  1359  	// planner and the number of unplaced allocations in the reconcile summaries
  1360  	// codepath
  1361  	out2, _ := state.JobSummaryByID(ws, alloc.Job.ID)
  1362  	expected = structs.JobSummary{
  1363  		JobID: alloc.Job.ID,
  1364  		Summary: map[string]structs.TaskGroupSummary{
  1365  			"web": structs.TaskGroupSummary{
  1366  				Queued:   10,
  1367  				Starting: 1,
  1368  			},
  1369  		},
  1370  		CreateIndex: 1010,
  1371  		ModifyIndex: out2.ModifyIndex,
  1372  	}
  1373  	if !reflect.DeepEqual(&expected, out2) {
  1374  		t.Fatalf("expected: %#v, actual: %#v", &expected, out2)
  1375  	}
  1376  }