github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/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  	"github.com/hashicorp/nomad/nomad/mock"
    12  	"github.com/hashicorp/nomad/nomad/state"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/hashicorp/nomad/testutil"
    15  	"github.com/hashicorp/raft"
    16  )
    17  
    18  type MockSink struct {
    19  	*bytes.Buffer
    20  	cancel bool
    21  }
    22  
    23  func (m *MockSink) ID() string {
    24  	return "Mock"
    25  }
    26  
    27  func (m *MockSink) Cancel() error {
    28  	m.cancel = true
    29  	return nil
    30  }
    31  
    32  func (m *MockSink) Close() error {
    33  	return nil
    34  }
    35  
    36  func testStateStore(t *testing.T) *state.StateStore {
    37  	state, err := state.NewStateStore(os.Stderr)
    38  	if err != nil {
    39  		t.Fatalf("err: %v", err)
    40  	}
    41  	if state == nil {
    42  		t.Fatalf("missing state")
    43  	}
    44  	return state
    45  }
    46  
    47  func testFSM(t *testing.T) *nomadFSM {
    48  	p, _ := testPeriodicDispatcher()
    49  	broker := testBroker(t, 0)
    50  	blocked := NewBlockedEvals(broker)
    51  	fsm, err := NewFSM(broker, p, blocked, os.Stderr)
    52  	if err != nil {
    53  		t.Fatalf("err: %v", err)
    54  	}
    55  	if fsm == nil {
    56  		t.Fatalf("missing fsm")
    57  	}
    58  	return fsm
    59  }
    60  
    61  func makeLog(buf []byte) *raft.Log {
    62  	return &raft.Log{
    63  		Index: 1,
    64  		Term:  1,
    65  		Type:  raft.LogCommand,
    66  		Data:  buf,
    67  	}
    68  }
    69  
    70  func TestFSM_UpsertNode(t *testing.T) {
    71  	fsm := testFSM(t)
    72  	fsm.blockedEvals.SetEnabled(true)
    73  
    74  	node := mock.Node()
    75  
    76  	// Mark an eval as blocked.
    77  	eval := mock.Eval()
    78  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
    79  	fsm.blockedEvals.Block(eval)
    80  
    81  	req := structs.NodeRegisterRequest{
    82  		Node: node,
    83  	}
    84  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
    85  	if err != nil {
    86  		t.Fatalf("err: %v", err)
    87  	}
    88  
    89  	resp := fsm.Apply(makeLog(buf))
    90  	if resp != nil {
    91  		t.Fatalf("resp: %v", resp)
    92  	}
    93  
    94  	// Verify we are registered
    95  	n, err := fsm.State().NodeByID(req.Node.ID)
    96  	if err != nil {
    97  		t.Fatalf("err: %v", err)
    98  	}
    99  	if n == nil {
   100  		t.Fatalf("not found!")
   101  	}
   102  	if n.CreateIndex != 1 {
   103  		t.Fatalf("bad index: %d", node.CreateIndex)
   104  	}
   105  
   106  	tt := fsm.TimeTable()
   107  	index := tt.NearestIndex(time.Now().UTC())
   108  	if index != 1 {
   109  		t.Fatalf("bad: %d", index)
   110  	}
   111  
   112  	// Verify the eval was unblocked.
   113  	testutil.WaitForResult(func() (bool, error) {
   114  		bStats := fsm.blockedEvals.Stats()
   115  		if bStats.TotalBlocked != 0 {
   116  			return false, fmt.Errorf("bad: %#v", bStats)
   117  		}
   118  		return true, nil
   119  	}, func(err error) {
   120  		t.Fatalf("err: %s", err)
   121  	})
   122  
   123  }
   124  
   125  func TestFSM_DeregisterNode(t *testing.T) {
   126  	fsm := testFSM(t)
   127  
   128  	node := mock.Node()
   129  	req := structs.NodeRegisterRequest{
   130  		Node: node,
   131  	}
   132  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   133  	if err != nil {
   134  		t.Fatalf("err: %v", err)
   135  	}
   136  
   137  	resp := fsm.Apply(makeLog(buf))
   138  	if resp != nil {
   139  		t.Fatalf("resp: %v", resp)
   140  	}
   141  
   142  	req2 := structs.NodeDeregisterRequest{
   143  		NodeID: node.ID,
   144  	}
   145  	buf, err = structs.Encode(structs.NodeDeregisterRequestType, req2)
   146  	if err != nil {
   147  		t.Fatalf("err: %v", err)
   148  	}
   149  
   150  	resp = fsm.Apply(makeLog(buf))
   151  	if resp != nil {
   152  		t.Fatalf("resp: %v", resp)
   153  	}
   154  
   155  	// Verify we are NOT registered
   156  	node, err = fsm.State().NodeByID(req.Node.ID)
   157  	if err != nil {
   158  		t.Fatalf("err: %v", err)
   159  	}
   160  	if node != nil {
   161  		t.Fatalf("node found!")
   162  	}
   163  }
   164  
   165  func TestFSM_UpdateNodeStatus(t *testing.T) {
   166  	fsm := testFSM(t)
   167  	fsm.blockedEvals.SetEnabled(true)
   168  
   169  	node := mock.Node()
   170  	req := structs.NodeRegisterRequest{
   171  		Node: node,
   172  	}
   173  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   174  	if err != nil {
   175  		t.Fatalf("err: %v", err)
   176  	}
   177  
   178  	resp := fsm.Apply(makeLog(buf))
   179  	if resp != nil {
   180  		t.Fatalf("resp: %v", resp)
   181  	}
   182  
   183  	// Mark an eval as blocked.
   184  	eval := mock.Eval()
   185  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   186  	fsm.blockedEvals.Block(eval)
   187  
   188  	req2 := structs.NodeUpdateStatusRequest{
   189  		NodeID: node.ID,
   190  		Status: structs.NodeStatusReady,
   191  	}
   192  	buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2)
   193  	if err != nil {
   194  		t.Fatalf("err: %v", err)
   195  	}
   196  
   197  	resp = fsm.Apply(makeLog(buf))
   198  	if resp != nil {
   199  		t.Fatalf("resp: %v", resp)
   200  	}
   201  
   202  	// Verify the status is ready.
   203  	node, err = fsm.State().NodeByID(req.Node.ID)
   204  	if err != nil {
   205  		t.Fatalf("err: %v", err)
   206  	}
   207  	if node.Status != structs.NodeStatusReady {
   208  		t.Fatalf("bad node: %#v", node)
   209  	}
   210  
   211  	// Verify the eval was unblocked.
   212  	testutil.WaitForResult(func() (bool, error) {
   213  		bStats := fsm.blockedEvals.Stats()
   214  		if bStats.TotalBlocked != 0 {
   215  			return false, fmt.Errorf("bad: %#v", bStats)
   216  		}
   217  		return true, nil
   218  	}, func(err error) {
   219  		t.Fatalf("err: %s", err)
   220  	})
   221  }
   222  
   223  func TestFSM_UpdateNodeDrain(t *testing.T) {
   224  	fsm := testFSM(t)
   225  
   226  	node := mock.Node()
   227  	req := structs.NodeRegisterRequest{
   228  		Node: node,
   229  	}
   230  	buf, err := structs.Encode(structs.NodeRegisterRequestType, req)
   231  	if err != nil {
   232  		t.Fatalf("err: %v", err)
   233  	}
   234  
   235  	resp := fsm.Apply(makeLog(buf))
   236  	if resp != nil {
   237  		t.Fatalf("resp: %v", resp)
   238  	}
   239  
   240  	req2 := structs.NodeUpdateDrainRequest{
   241  		NodeID: node.ID,
   242  		Drain:  true,
   243  	}
   244  	buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2)
   245  	if err != nil {
   246  		t.Fatalf("err: %v", err)
   247  	}
   248  
   249  	resp = fsm.Apply(makeLog(buf))
   250  	if resp != nil {
   251  		t.Fatalf("resp: %v", resp)
   252  	}
   253  
   254  	// Verify we are NOT registered
   255  	node, err = fsm.State().NodeByID(req.Node.ID)
   256  	if err != nil {
   257  		t.Fatalf("err: %v", err)
   258  	}
   259  	if !node.Drain {
   260  		t.Fatalf("bad node: %#v", node)
   261  	}
   262  }
   263  
   264  func TestFSM_RegisterJob(t *testing.T) {
   265  	fsm := testFSM(t)
   266  
   267  	job := mock.PeriodicJob()
   268  	req := structs.JobRegisterRequest{
   269  		Job: job,
   270  	}
   271  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   272  	if err != nil {
   273  		t.Fatalf("err: %v", err)
   274  	}
   275  
   276  	resp := fsm.Apply(makeLog(buf))
   277  	if resp != nil {
   278  		t.Fatalf("resp: %v", resp)
   279  	}
   280  
   281  	// Verify we are registered
   282  	jobOut, err := fsm.State().JobByID(req.Job.ID)
   283  	if err != nil {
   284  		t.Fatalf("err: %v", err)
   285  	}
   286  	if jobOut == nil {
   287  		t.Fatalf("not found!")
   288  	}
   289  	if jobOut.CreateIndex != 1 {
   290  		t.Fatalf("bad index: %d", jobOut.CreateIndex)
   291  	}
   292  
   293  	// Verify it was added to the periodic runner.
   294  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok {
   295  		t.Fatal("job not added to periodic runner")
   296  	}
   297  
   298  	// Verify the launch time was tracked.
   299  	launchOut, err := fsm.State().PeriodicLaunchByID(req.Job.ID)
   300  	if err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  	if launchOut == nil {
   304  		t.Fatalf("not found!")
   305  	}
   306  	if launchOut.Launch.IsZero() {
   307  		t.Fatalf("bad launch time: %v", launchOut.Launch)
   308  	}
   309  }
   310  
   311  func TestFSM_DeregisterJob(t *testing.T) {
   312  	fsm := testFSM(t)
   313  
   314  	job := mock.PeriodicJob()
   315  	req := structs.JobRegisterRequest{
   316  		Job: job,
   317  	}
   318  	buf, err := structs.Encode(structs.JobRegisterRequestType, req)
   319  	if err != nil {
   320  		t.Fatalf("err: %v", err)
   321  	}
   322  
   323  	resp := fsm.Apply(makeLog(buf))
   324  	if resp != nil {
   325  		t.Fatalf("resp: %v", resp)
   326  	}
   327  
   328  	req2 := structs.JobDeregisterRequest{
   329  		JobID: job.ID,
   330  	}
   331  	buf, err = structs.Encode(structs.JobDeregisterRequestType, req2)
   332  	if err != nil {
   333  		t.Fatalf("err: %v", err)
   334  	}
   335  
   336  	resp = fsm.Apply(makeLog(buf))
   337  	if resp != nil {
   338  		t.Fatalf("resp: %v", resp)
   339  	}
   340  
   341  	// Verify we are NOT registered
   342  	jobOut, err := fsm.State().JobByID(req.Job.ID)
   343  	if err != nil {
   344  		t.Fatalf("err: %v", err)
   345  	}
   346  	if jobOut != nil {
   347  		t.Fatalf("job found!")
   348  	}
   349  
   350  	// Verify it was removed from the periodic runner.
   351  	if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok {
   352  		t.Fatal("job not removed from periodic runner")
   353  	}
   354  
   355  	// Verify it was removed from the periodic launch table.
   356  	launchOut, err := fsm.State().PeriodicLaunchByID(req.Job.ID)
   357  	if err != nil {
   358  		t.Fatalf("err: %v", err)
   359  	}
   360  	if launchOut != nil {
   361  		t.Fatalf("launch found!")
   362  	}
   363  }
   364  
   365  func TestFSM_UpdateEval(t *testing.T) {
   366  	fsm := testFSM(t)
   367  	fsm.evalBroker.SetEnabled(true)
   368  
   369  	req := structs.EvalUpdateRequest{
   370  		Evals: []*structs.Evaluation{mock.Eval()},
   371  	}
   372  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   373  	if err != nil {
   374  		t.Fatalf("err: %v", err)
   375  	}
   376  
   377  	resp := fsm.Apply(makeLog(buf))
   378  	if resp != nil {
   379  		t.Fatalf("resp: %v", resp)
   380  	}
   381  
   382  	// Verify we are registered
   383  	eval, err := fsm.State().EvalByID(req.Evals[0].ID)
   384  	if err != nil {
   385  		t.Fatalf("err: %v", err)
   386  	}
   387  	if eval == nil {
   388  		t.Fatalf("not found!")
   389  	}
   390  	if eval.CreateIndex != 1 {
   391  		t.Fatalf("bad index: %d", eval.CreateIndex)
   392  	}
   393  
   394  	// Verify enqueued
   395  	stats := fsm.evalBroker.Stats()
   396  	if stats.TotalReady != 1 {
   397  		t.Fatalf("bad: %#v %#v", stats, eval)
   398  	}
   399  }
   400  
   401  func TestFSM_UpdateEval_Blocked(t *testing.T) {
   402  	fsm := testFSM(t)
   403  	fsm.evalBroker.SetEnabled(true)
   404  	fsm.blockedEvals.SetEnabled(true)
   405  
   406  	// Create a blocked eval.
   407  	eval := mock.Eval()
   408  	eval.Status = structs.EvalStatusBlocked
   409  
   410  	req := structs.EvalUpdateRequest{
   411  		Evals: []*structs.Evaluation{eval},
   412  	}
   413  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   414  	if err != nil {
   415  		t.Fatalf("err: %v", err)
   416  	}
   417  
   418  	resp := fsm.Apply(makeLog(buf))
   419  	if resp != nil {
   420  		t.Fatalf("resp: %v", resp)
   421  	}
   422  
   423  	// Verify we are registered
   424  	out, err := fsm.State().EvalByID(eval.ID)
   425  	if err != nil {
   426  		t.Fatalf("err: %v", err)
   427  	}
   428  	if out == nil {
   429  		t.Fatalf("not found!")
   430  	}
   431  	if out.CreateIndex != 1 {
   432  		t.Fatalf("bad index: %d", out.CreateIndex)
   433  	}
   434  
   435  	// Verify the eval wasn't enqueued
   436  	stats := fsm.evalBroker.Stats()
   437  	if stats.TotalReady != 0 {
   438  		t.Fatalf("bad: %#v %#v", stats, out)
   439  	}
   440  
   441  	// Verify the eval was added to the blocked tracker.
   442  	bStats := fsm.blockedEvals.Stats()
   443  	if bStats.TotalBlocked != 1 {
   444  		t.Fatalf("bad: %#v %#v", bStats, out)
   445  	}
   446  }
   447  
   448  func TestFSM_DeleteEval(t *testing.T) {
   449  	fsm := testFSM(t)
   450  
   451  	eval := mock.Eval()
   452  	req := structs.EvalUpdateRequest{
   453  		Evals: []*structs.Evaluation{eval},
   454  	}
   455  	buf, err := structs.Encode(structs.EvalUpdateRequestType, req)
   456  	if err != nil {
   457  		t.Fatalf("err: %v", err)
   458  	}
   459  
   460  	resp := fsm.Apply(makeLog(buf))
   461  	if resp != nil {
   462  		t.Fatalf("resp: %v", resp)
   463  	}
   464  
   465  	req2 := structs.EvalDeleteRequest{
   466  		Evals: []string{eval.ID},
   467  	}
   468  	buf, err = structs.Encode(structs.EvalDeleteRequestType, req2)
   469  	if err != nil {
   470  		t.Fatalf("err: %v", err)
   471  	}
   472  
   473  	resp = fsm.Apply(makeLog(buf))
   474  	if resp != nil {
   475  		t.Fatalf("resp: %v", resp)
   476  	}
   477  
   478  	// Verify we are NOT registered
   479  	eval, err = fsm.State().EvalByID(req.Evals[0].ID)
   480  	if err != nil {
   481  		t.Fatalf("err: %v", err)
   482  	}
   483  	if eval != nil {
   484  		t.Fatalf("eval found!")
   485  	}
   486  }
   487  
   488  func TestFSM_UpsertAllocs(t *testing.T) {
   489  	fsm := testFSM(t)
   490  
   491  	alloc := mock.Alloc()
   492  	req := structs.AllocUpdateRequest{
   493  		Alloc: []*structs.Allocation{alloc},
   494  	}
   495  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   496  	if err != nil {
   497  		t.Fatalf("err: %v", err)
   498  	}
   499  
   500  	resp := fsm.Apply(makeLog(buf))
   501  	if resp != nil {
   502  		t.Fatalf("resp: %v", resp)
   503  	}
   504  
   505  	// Verify we are registered
   506  	out, err := fsm.State().AllocByID(alloc.ID)
   507  	if err != nil {
   508  		t.Fatalf("err: %v", err)
   509  	}
   510  	alloc.CreateIndex = out.CreateIndex
   511  	alloc.ModifyIndex = out.ModifyIndex
   512  	alloc.AllocModifyIndex = out.AllocModifyIndex
   513  	if !reflect.DeepEqual(alloc, out) {
   514  		t.Fatalf("bad: %#v %#v", alloc, out)
   515  	}
   516  
   517  	evictAlloc := new(structs.Allocation)
   518  	*evictAlloc = *alloc
   519  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   520  	req2 := structs.AllocUpdateRequest{
   521  		Alloc: []*structs.Allocation{evictAlloc},
   522  	}
   523  	buf, err = structs.Encode(structs.AllocUpdateRequestType, req2)
   524  	if err != nil {
   525  		t.Fatalf("err: %v", err)
   526  	}
   527  
   528  	resp = fsm.Apply(makeLog(buf))
   529  	if resp != nil {
   530  		t.Fatalf("resp: %v", resp)
   531  	}
   532  
   533  	// Verify we are evicted
   534  	out, err = fsm.State().AllocByID(alloc.ID)
   535  	if err != nil {
   536  		t.Fatalf("err: %v", err)
   537  	}
   538  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   539  		t.Fatalf("alloc found!")
   540  	}
   541  }
   542  
   543  func TestFSM_UpsertAllocs_SharedJob(t *testing.T) {
   544  	fsm := testFSM(t)
   545  
   546  	alloc := mock.Alloc()
   547  	job := alloc.Job
   548  	alloc.Job = nil
   549  	req := structs.AllocUpdateRequest{
   550  		Job:   job,
   551  		Alloc: []*structs.Allocation{alloc},
   552  	}
   553  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   554  	if err != nil {
   555  		t.Fatalf("err: %v", err)
   556  	}
   557  
   558  	resp := fsm.Apply(makeLog(buf))
   559  	if resp != nil {
   560  		t.Fatalf("resp: %v", resp)
   561  	}
   562  
   563  	// Verify we are registered
   564  	out, err := fsm.State().AllocByID(alloc.ID)
   565  	if err != nil {
   566  		t.Fatalf("err: %v", err)
   567  	}
   568  	alloc.CreateIndex = out.CreateIndex
   569  	alloc.ModifyIndex = out.ModifyIndex
   570  	alloc.AllocModifyIndex = out.AllocModifyIndex
   571  
   572  	// Job should be re-attached
   573  	alloc.Job = job
   574  	if !reflect.DeepEqual(alloc, out) {
   575  		t.Fatalf("bad: %#v %#v", alloc, out)
   576  	}
   577  
   578  	evictAlloc := new(structs.Allocation)
   579  	*evictAlloc = *alloc
   580  	job = evictAlloc.Job
   581  	evictAlloc.Job = nil
   582  	evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict
   583  	req2 := structs.AllocUpdateRequest{
   584  		Job:   job,
   585  		Alloc: []*structs.Allocation{evictAlloc},
   586  	}
   587  	buf, err = structs.Encode(structs.AllocUpdateRequestType, 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 evicted
   598  	out, err = fsm.State().AllocByID(alloc.ID)
   599  	if err != nil {
   600  		t.Fatalf("err: %v", err)
   601  	}
   602  	if out.DesiredStatus != structs.AllocDesiredStatusEvict {
   603  		t.Fatalf("alloc found!")
   604  	}
   605  	if out.Job == nil {
   606  		t.Fatalf("missing job")
   607  	}
   608  }
   609  
   610  func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) {
   611  	fsm := testFSM(t)
   612  
   613  	alloc := mock.Alloc()
   614  	job := alloc.Job
   615  	resources := alloc.Resources
   616  	alloc.Resources = nil
   617  	req := structs.AllocUpdateRequest{
   618  		Job:   job,
   619  		Alloc: []*structs.Allocation{alloc},
   620  	}
   621  	buf, err := structs.Encode(structs.AllocUpdateRequestType, req)
   622  	if err != nil {
   623  		t.Fatalf("err: %v", err)
   624  	}
   625  
   626  	resp := fsm.Apply(makeLog(buf))
   627  	if resp != nil {
   628  		t.Fatalf("resp: %v", resp)
   629  	}
   630  
   631  	// Verify we are registered
   632  	out, err := fsm.State().AllocByID(alloc.ID)
   633  	if err != nil {
   634  		t.Fatalf("err: %v", err)
   635  	}
   636  	alloc.CreateIndex = out.CreateIndex
   637  	alloc.ModifyIndex = out.ModifyIndex
   638  	alloc.AllocModifyIndex = out.AllocModifyIndex
   639  
   640  	// Resources should be recomputed
   641  	alloc.Resources = resources
   642  	if !reflect.DeepEqual(alloc, out) {
   643  		t.Fatalf("bad: %#v %#v", alloc, out)
   644  	}
   645  }
   646  
   647  func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) {
   648  	fsm := testFSM(t)
   649  	fsm.blockedEvals.SetEnabled(true)
   650  	state := fsm.State()
   651  
   652  	node := mock.Node()
   653  	state.UpsertNode(1, node)
   654  
   655  	// Mark an eval as blocked.
   656  	eval := mock.Eval()
   657  	eval.ClassEligibility = map[string]bool{node.ComputedClass: true}
   658  	fsm.blockedEvals.Block(eval)
   659  
   660  	bStats := fsm.blockedEvals.Stats()
   661  	if bStats.TotalBlocked != 1 {
   662  		t.Fatalf("bad: %#v", bStats)
   663  	}
   664  
   665  	// Create a completed eval
   666  	alloc := mock.Alloc()
   667  	alloc.NodeID = node.ID
   668  	alloc2 := mock.Alloc()
   669  	alloc2.NodeID = node.ID
   670  	state.UpsertAllocs(1, []*structs.Allocation{alloc, alloc2})
   671  
   672  	clientAlloc := new(structs.Allocation)
   673  	*clientAlloc = *alloc
   674  	clientAlloc.ClientStatus = structs.AllocClientStatusComplete
   675  	update2 := &structs.Allocation{
   676  		ID:           alloc2.ID,
   677  		ClientStatus: structs.AllocClientStatusRunning,
   678  	}
   679  
   680  	req := structs.AllocUpdateRequest{
   681  		Alloc: []*structs.Allocation{clientAlloc, update2},
   682  	}
   683  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   684  	if err != nil {
   685  		t.Fatalf("err: %v", err)
   686  	}
   687  
   688  	resp := fsm.Apply(makeLog(buf))
   689  	if resp != nil {
   690  		t.Fatalf("resp: %v", resp)
   691  	}
   692  
   693  	// Verify we are updated
   694  	out, err := fsm.State().AllocByID(alloc.ID)
   695  	if err != nil {
   696  		t.Fatalf("err: %v", err)
   697  	}
   698  	clientAlloc.CreateIndex = out.CreateIndex
   699  	clientAlloc.ModifyIndex = out.ModifyIndex
   700  	if !reflect.DeepEqual(clientAlloc, out) {
   701  		t.Fatalf("bad: %#v %#v", clientAlloc, out)
   702  	}
   703  
   704  	out, err = fsm.State().AllocByID(alloc2.ID)
   705  	if err != nil {
   706  		t.Fatalf("err: %v", err)
   707  	}
   708  	alloc2.CreateIndex = out.CreateIndex
   709  	alloc2.ModifyIndex = out.ModifyIndex
   710  	alloc2.ClientStatus = structs.AllocClientStatusRunning
   711  	alloc2.TaskStates = nil
   712  	if !reflect.DeepEqual(alloc2, out) {
   713  		t.Fatalf("bad: %#v %#v", alloc2, out)
   714  	}
   715  
   716  	// Verify the eval was unblocked.
   717  	testutil.WaitForResult(func() (bool, error) {
   718  		bStats = fsm.blockedEvals.Stats()
   719  		if bStats.TotalBlocked != 0 {
   720  			return false, fmt.Errorf("bad: %#v %#v", bStats, out)
   721  		}
   722  		return true, nil
   723  	}, func(err error) {
   724  		t.Fatalf("err: %s", err)
   725  	})
   726  }
   727  
   728  func TestFSM_UpdateAllocFromClient(t *testing.T) {
   729  	fsm := testFSM(t)
   730  	state := fsm.State()
   731  
   732  	alloc := mock.Alloc()
   733  	state.UpsertAllocs(1, []*structs.Allocation{alloc})
   734  
   735  	clientAlloc := new(structs.Allocation)
   736  	*clientAlloc = *alloc
   737  	clientAlloc.ClientStatus = structs.AllocClientStatusFailed
   738  
   739  	req := structs.AllocUpdateRequest{
   740  		Alloc: []*structs.Allocation{clientAlloc},
   741  	}
   742  	buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req)
   743  	if err != nil {
   744  		t.Fatalf("err: %v", err)
   745  	}
   746  
   747  	resp := fsm.Apply(makeLog(buf))
   748  	if resp != nil {
   749  		t.Fatalf("resp: %v", resp)
   750  	}
   751  
   752  	// Verify we are registered
   753  	out, err := fsm.State().AllocByID(alloc.ID)
   754  	if err != nil {
   755  		t.Fatalf("err: %v", err)
   756  	}
   757  	clientAlloc.CreateIndex = out.CreateIndex
   758  	clientAlloc.ModifyIndex = out.ModifyIndex
   759  	if !reflect.DeepEqual(clientAlloc, out) {
   760  		t.Fatalf("bad: %#v %#v", clientAlloc, out)
   761  	}
   762  }
   763  
   764  func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM {
   765  	// Snapshot
   766  	snap, err := fsm.Snapshot()
   767  	if err != nil {
   768  		t.Fatalf("err: %v", err)
   769  	}
   770  	defer snap.Release()
   771  
   772  	// Persist
   773  	buf := bytes.NewBuffer(nil)
   774  	sink := &MockSink{buf, false}
   775  	if err := snap.Persist(sink); err != nil {
   776  		t.Fatalf("err: %v", err)
   777  	}
   778  
   779  	// Try to restore on a new FSM
   780  	fsm2 := testFSM(t)
   781  
   782  	// Do a restore
   783  	if err := fsm2.Restore(sink); err != nil {
   784  		t.Fatalf("err: %v", err)
   785  	}
   786  	return fsm2
   787  }
   788  
   789  func TestFSM_SnapshotRestore_Nodes(t *testing.T) {
   790  	// Add some state
   791  	fsm := testFSM(t)
   792  	state := fsm.State()
   793  	node1 := mock.Node()
   794  	state.UpsertNode(1000, node1)
   795  	node2 := mock.Node()
   796  	state.UpsertNode(1001, node2)
   797  
   798  	// Verify the contents
   799  	fsm2 := testSnapshotRestore(t, fsm)
   800  	state2 := fsm2.State()
   801  	out1, _ := state2.NodeByID(node1.ID)
   802  	out2, _ := state2.NodeByID(node2.ID)
   803  	if !reflect.DeepEqual(node1, out1) {
   804  		t.Fatalf("bad: \n%#v\n%#v", out1, node1)
   805  	}
   806  	if !reflect.DeepEqual(node2, out2) {
   807  		t.Fatalf("bad: \n%#v\n%#v", out2, node2)
   808  	}
   809  }
   810  
   811  func TestFSM_SnapshotRestore_Jobs(t *testing.T) {
   812  	// Add some state
   813  	fsm := testFSM(t)
   814  	state := fsm.State()
   815  	job1 := mock.Job()
   816  	state.UpsertJob(1000, job1)
   817  	job2 := mock.Job()
   818  	state.UpsertJob(1001, job2)
   819  
   820  	// Verify the contents
   821  	fsm2 := testSnapshotRestore(t, fsm)
   822  	state2 := fsm2.State()
   823  	out1, _ := state2.JobByID(job1.ID)
   824  	out2, _ := state2.JobByID(job2.ID)
   825  	if !reflect.DeepEqual(job1, out1) {
   826  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
   827  	}
   828  	if !reflect.DeepEqual(job2, out2) {
   829  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
   830  	}
   831  }
   832  
   833  func TestFSM_SnapshotRestore_Evals(t *testing.T) {
   834  	// Add some state
   835  	fsm := testFSM(t)
   836  	state := fsm.State()
   837  	eval1 := mock.Eval()
   838  	state.UpsertEvals(1000, []*structs.Evaluation{eval1})
   839  	eval2 := mock.Eval()
   840  	state.UpsertEvals(1001, []*structs.Evaluation{eval2})
   841  
   842  	// Verify the contents
   843  	fsm2 := testSnapshotRestore(t, fsm)
   844  	state2 := fsm2.State()
   845  	out1, _ := state2.EvalByID(eval1.ID)
   846  	out2, _ := state2.EvalByID(eval2.ID)
   847  	if !reflect.DeepEqual(eval1, out1) {
   848  		t.Fatalf("bad: \n%#v\n%#v", out1, eval1)
   849  	}
   850  	if !reflect.DeepEqual(eval2, out2) {
   851  		t.Fatalf("bad: \n%#v\n%#v", out2, eval2)
   852  	}
   853  }
   854  
   855  func TestFSM_SnapshotRestore_Allocs(t *testing.T) {
   856  	// Add some state
   857  	fsm := testFSM(t)
   858  	state := fsm.State()
   859  	alloc1 := mock.Alloc()
   860  	state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
   861  	alloc2 := mock.Alloc()
   862  	state.UpsertAllocs(1001, []*structs.Allocation{alloc2})
   863  
   864  	// Verify the contents
   865  	fsm2 := testSnapshotRestore(t, fsm)
   866  	state2 := fsm2.State()
   867  	out1, _ := state2.AllocByID(alloc1.ID)
   868  	out2, _ := state2.AllocByID(alloc2.ID)
   869  	if !reflect.DeepEqual(alloc1, out1) {
   870  		t.Fatalf("bad: \n%#v\n%#v", out1, alloc1)
   871  	}
   872  	if !reflect.DeepEqual(alloc2, out2) {
   873  		t.Fatalf("bad: \n%#v\n%#v", out2, alloc2)
   874  	}
   875  }
   876  
   877  func TestFSM_SnapshotRestore_Indexes(t *testing.T) {
   878  	// Add some state
   879  	fsm := testFSM(t)
   880  	state := fsm.State()
   881  	node1 := mock.Node()
   882  	state.UpsertNode(1000, node1)
   883  
   884  	// Verify the contents
   885  	fsm2 := testSnapshotRestore(t, fsm)
   886  	state2 := fsm2.State()
   887  
   888  	index, err := state2.Index("nodes")
   889  	if err != nil {
   890  		t.Fatalf("err: %v", err)
   891  	}
   892  	if index != 1000 {
   893  		t.Fatalf("bad: %d", index)
   894  	}
   895  }
   896  
   897  func TestFSM_SnapshotRestore_TimeTable(t *testing.T) {
   898  	// Add some state
   899  	fsm := testFSM(t)
   900  
   901  	tt := fsm.TimeTable()
   902  	start := time.Now().UTC()
   903  	tt.Witness(1000, start)
   904  	tt.Witness(2000, start.Add(10*time.Minute))
   905  
   906  	// Verify the contents
   907  	fsm2 := testSnapshotRestore(t, fsm)
   908  
   909  	tt2 := fsm2.TimeTable()
   910  	if tt2.NearestTime(1500) != start {
   911  		t.Fatalf("bad")
   912  	}
   913  	if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 {
   914  		t.Fatalf("bad")
   915  	}
   916  }
   917  
   918  func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) {
   919  	// Add some state
   920  	fsm := testFSM(t)
   921  	state := fsm.State()
   922  	job1 := mock.Job()
   923  	launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()}
   924  	state.UpsertPeriodicLaunch(1000, launch1)
   925  	job2 := mock.Job()
   926  	launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()}
   927  	state.UpsertPeriodicLaunch(1001, launch2)
   928  
   929  	// Verify the contents
   930  	fsm2 := testSnapshotRestore(t, fsm)
   931  	state2 := fsm2.State()
   932  	out1, _ := state2.PeriodicLaunchByID(launch1.ID)
   933  	out2, _ := state2.PeriodicLaunchByID(launch2.ID)
   934  	if !reflect.DeepEqual(launch1, out1) {
   935  		t.Fatalf("bad: \n%#v\n%#v", out1, job1)
   936  	}
   937  	if !reflect.DeepEqual(launch2, out2) {
   938  		t.Fatalf("bad: \n%#v\n%#v", out2, job2)
   939  	}
   940  }