github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/nomad/fsm.go (about)

     1  package nomad
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/armon/go-metrics"
    10  	"github.com/hashicorp/nomad/nomad/state"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/raft"
    13  	"github.com/ugorji/go/codec"
    14  )
    15  
    16  const (
    17  	// timeTableGranularity is the granularity of index to time tracking
    18  	timeTableGranularity = 5 * time.Minute
    19  
    20  	// timeTableLimit is the maximum limit of our tracking
    21  	timeTableLimit = 72 * time.Hour
    22  )
    23  
    24  // SnapshotType is prefixed to a record in the FSM snapshot
    25  // so that we can determine the type for restore
    26  type SnapshotType byte
    27  
    28  const (
    29  	NodeSnapshot SnapshotType = iota
    30  	JobSnapshot
    31  	IndexSnapshot
    32  	EvalSnapshot
    33  	AllocSnapshot
    34  	TimeTableSnapshot
    35  	PeriodicLaunchSnapshot
    36  )
    37  
    38  // nomadFSM implements a finite state machine that is used
    39  // along with Raft to provide strong consistency. We implement
    40  // this outside the Server to avoid exposing this outside the package.
    41  type nomadFSM struct {
    42  	evalBroker         *EvalBroker
    43  	blockedEvals       *BlockedEvals
    44  	periodicDispatcher *PeriodicDispatch
    45  	logOutput          io.Writer
    46  	logger             *log.Logger
    47  	state              *state.StateStore
    48  	timetable          *TimeTable
    49  }
    50  
    51  // nomadSnapshot is used to provide a snapshot of the current
    52  // state in a way that can be accessed concurrently with operations
    53  // that may modify the live state.
    54  type nomadSnapshot struct {
    55  	snap      *state.StateSnapshot
    56  	timetable *TimeTable
    57  }
    58  
    59  // snapshotHeader is the first entry in our snapshot
    60  type snapshotHeader struct {
    61  }
    62  
    63  // NewFSMPath is used to construct a new FSM with a blank state
    64  func NewFSM(evalBroker *EvalBroker, periodic *PeriodicDispatch,
    65  	blocked *BlockedEvals, logOutput io.Writer) (*nomadFSM, error) {
    66  	// Create a state store
    67  	state, err := state.NewStateStore(logOutput)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	fsm := &nomadFSM{
    73  		evalBroker:         evalBroker,
    74  		periodicDispatcher: periodic,
    75  		blockedEvals:       blocked,
    76  		logOutput:          logOutput,
    77  		logger:             log.New(logOutput, "", log.LstdFlags),
    78  		state:              state,
    79  		timetable:          NewTimeTable(timeTableGranularity, timeTableLimit),
    80  	}
    81  	return fsm, nil
    82  }
    83  
    84  // Close is used to cleanup resources associated with the FSM
    85  func (n *nomadFSM) Close() error {
    86  	return nil
    87  }
    88  
    89  // State is used to return a handle to the current state
    90  func (n *nomadFSM) State() *state.StateStore {
    91  	return n.state
    92  }
    93  
    94  // TimeTable returns the time table of transactions
    95  func (n *nomadFSM) TimeTable() *TimeTable {
    96  	return n.timetable
    97  }
    98  
    99  func (n *nomadFSM) Apply(log *raft.Log) interface{} {
   100  	buf := log.Data
   101  	msgType := structs.MessageType(buf[0])
   102  
   103  	// Witness this write
   104  	n.timetable.Witness(log.Index, time.Now().UTC())
   105  
   106  	// Check if this message type should be ignored when unknown. This is
   107  	// used so that new commands can be added with developer control if older
   108  	// versions can safely ignore the command, or if they should crash.
   109  	ignoreUnknown := false
   110  	if msgType&structs.IgnoreUnknownTypeFlag == structs.IgnoreUnknownTypeFlag {
   111  		msgType &= ^structs.IgnoreUnknownTypeFlag
   112  		ignoreUnknown = true
   113  	}
   114  
   115  	switch msgType {
   116  	case structs.NodeRegisterRequestType:
   117  		return n.applyUpsertNode(buf[1:], log.Index)
   118  	case structs.NodeDeregisterRequestType:
   119  		return n.applyDeregisterNode(buf[1:], log.Index)
   120  	case structs.NodeUpdateStatusRequestType:
   121  		return n.applyStatusUpdate(buf[1:], log.Index)
   122  	case structs.NodeUpdateDrainRequestType:
   123  		return n.applyDrainUpdate(buf[1:], log.Index)
   124  	case structs.JobRegisterRequestType:
   125  		return n.applyUpsertJob(buf[1:], log.Index)
   126  	case structs.JobDeregisterRequestType:
   127  		return n.applyDeregisterJob(buf[1:], log.Index)
   128  	case structs.EvalUpdateRequestType:
   129  		return n.applyUpdateEval(buf[1:], log.Index)
   130  	case structs.EvalDeleteRequestType:
   131  		return n.applyDeleteEval(buf[1:], log.Index)
   132  	case structs.AllocUpdateRequestType:
   133  		return n.applyAllocUpdate(buf[1:], log.Index)
   134  	case structs.AllocClientUpdateRequestType:
   135  		return n.applyAllocClientUpdate(buf[1:], log.Index)
   136  	default:
   137  		if ignoreUnknown {
   138  			n.logger.Printf("[WARN] nomad.fsm: ignoring unknown message type (%d), upgrade to newer version", msgType)
   139  			return nil
   140  		} else {
   141  			panic(fmt.Errorf("failed to apply request: %#v", buf))
   142  		}
   143  	}
   144  }
   145  
   146  func (n *nomadFSM) applyUpsertNode(buf []byte, index uint64) interface{} {
   147  	defer metrics.MeasureSince([]string{"nomad", "fsm", "register_node"}, time.Now())
   148  	var req structs.NodeRegisterRequest
   149  	if err := structs.Decode(buf, &req); err != nil {
   150  		panic(fmt.Errorf("failed to decode request: %v", err))
   151  	}
   152  
   153  	if err := n.state.UpsertNode(index, req.Node); err != nil {
   154  		n.logger.Printf("[ERR] nomad.fsm: UpsertNode failed: %v", err)
   155  		return err
   156  	}
   157  
   158  	// Unblock evals for the nodes computed node class if it is in a ready
   159  	// state.
   160  	if req.Node.Status == structs.NodeStatusReady {
   161  		n.blockedEvals.Unblock(req.Node.ComputedClass, index)
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func (n *nomadFSM) applyDeregisterNode(buf []byte, index uint64) interface{} {
   168  	defer metrics.MeasureSince([]string{"nomad", "fsm", "deregister_node"}, time.Now())
   169  	var req structs.NodeDeregisterRequest
   170  	if err := structs.Decode(buf, &req); err != nil {
   171  		panic(fmt.Errorf("failed to decode request: %v", err))
   172  	}
   173  
   174  	if err := n.state.DeleteNode(index, req.NodeID); err != nil {
   175  		n.logger.Printf("[ERR] nomad.fsm: DeleteNode failed: %v", err)
   176  		return err
   177  	}
   178  	return nil
   179  }
   180  
   181  func (n *nomadFSM) applyStatusUpdate(buf []byte, index uint64) interface{} {
   182  	defer metrics.MeasureSince([]string{"nomad", "fsm", "node_status_update"}, time.Now())
   183  	var req structs.NodeUpdateStatusRequest
   184  	if err := structs.Decode(buf, &req); err != nil {
   185  		panic(fmt.Errorf("failed to decode request: %v", err))
   186  	}
   187  
   188  	if err := n.state.UpdateNodeStatus(index, req.NodeID, req.Status); err != nil {
   189  		n.logger.Printf("[ERR] nomad.fsm: UpdateNodeStatus failed: %v", err)
   190  		return err
   191  	}
   192  
   193  	// Unblock evals for the nodes computed node class if it is in a ready
   194  	// state.
   195  	if req.Status == structs.NodeStatusReady {
   196  		node, err := n.state.NodeByID(req.NodeID)
   197  		if err != nil {
   198  			n.logger.Printf("[ERR] nomad.fsm: looking up node %q failed: %v", req.NodeID, err)
   199  			return err
   200  
   201  		}
   202  		n.blockedEvals.Unblock(node.ComputedClass, index)
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func (n *nomadFSM) applyDrainUpdate(buf []byte, index uint64) interface{} {
   209  	defer metrics.MeasureSince([]string{"nomad", "fsm", "node_drain_update"}, time.Now())
   210  	var req structs.NodeUpdateDrainRequest
   211  	if err := structs.Decode(buf, &req); err != nil {
   212  		panic(fmt.Errorf("failed to decode request: %v", err))
   213  	}
   214  
   215  	if err := n.state.UpdateNodeDrain(index, req.NodeID, req.Drain); err != nil {
   216  		n.logger.Printf("[ERR] nomad.fsm: UpdateNodeDrain failed: %v", err)
   217  		return err
   218  	}
   219  	return nil
   220  }
   221  
   222  func (n *nomadFSM) applyUpsertJob(buf []byte, index uint64) interface{} {
   223  	defer metrics.MeasureSince([]string{"nomad", "fsm", "register_job"}, time.Now())
   224  	var req structs.JobRegisterRequest
   225  	if err := structs.Decode(buf, &req); err != nil {
   226  		panic(fmt.Errorf("failed to decode request: %v", err))
   227  	}
   228  
   229  	if err := n.state.UpsertJob(index, req.Job); err != nil {
   230  		n.logger.Printf("[ERR] nomad.fsm: UpsertJob failed: %v", err)
   231  		return err
   232  	}
   233  
   234  	// We always add the job to the periodic dispatcher because there is the
   235  	// possibility that the periodic spec was removed and then we should stop
   236  	// tracking it.
   237  	if err := n.periodicDispatcher.Add(req.Job); err != nil {
   238  		n.logger.Printf("[ERR] nomad.fsm: periodicDispatcher.Add failed: %v", err)
   239  		return err
   240  	}
   241  
   242  	// If it is periodic, record the time it was inserted. This is necessary for
   243  	// recovering during leader election. It is possible that from the time it
   244  	// is added to when it was suppose to launch, leader election occurs and the
   245  	// job was not launched. In this case, we use the insertion time to
   246  	// determine if a launch was missed.
   247  	if req.Job.IsPeriodic() {
   248  		prevLaunch, err := n.state.PeriodicLaunchByID(req.Job.ID)
   249  		if err != nil {
   250  			n.logger.Printf("[ERR] nomad.fsm: PeriodicLaunchByID failed: %v", err)
   251  			return err
   252  		}
   253  
   254  		// Record the insertion time as a launch. We overload the launch table
   255  		// such that the first entry is the insertion time.
   256  		if prevLaunch == nil {
   257  			launch := &structs.PeriodicLaunch{ID: req.Job.ID, Launch: time.Now()}
   258  			if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
   259  				n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
   260  				return err
   261  			}
   262  		}
   263  	}
   264  
   265  	// Check if the parent job is periodic and mark the launch time.
   266  	parentID := req.Job.ParentID
   267  	if parentID != "" {
   268  		parent, err := n.state.JobByID(parentID)
   269  		if err != nil {
   270  			n.logger.Printf("[ERR] nomad.fsm: JobByID(%v) lookup for parent failed: %v", parentID, err)
   271  			return err
   272  		} else if parent == nil {
   273  			// The parent has been deregistered.
   274  			return nil
   275  		}
   276  
   277  		if parent.IsPeriodic() {
   278  			t, err := n.periodicDispatcher.LaunchTime(req.Job.ID)
   279  			if err != nil {
   280  				n.logger.Printf("[ERR] nomad.fsm: LaunchTime(%v) failed: %v", req.Job.ID, err)
   281  				return err
   282  			}
   283  
   284  			launch := &structs.PeriodicLaunch{ID: parentID, Launch: t}
   285  			if err := n.state.UpsertPeriodicLaunch(index, launch); err != nil {
   286  				n.logger.Printf("[ERR] nomad.fsm: UpsertPeriodicLaunch failed: %v", err)
   287  				return err
   288  			}
   289  		}
   290  	}
   291  
   292  	return nil
   293  }
   294  
   295  func (n *nomadFSM) applyDeregisterJob(buf []byte, index uint64) interface{} {
   296  	defer metrics.MeasureSince([]string{"nomad", "fsm", "deregister_job"}, time.Now())
   297  	var req structs.JobDeregisterRequest
   298  	if err := structs.Decode(buf, &req); err != nil {
   299  		panic(fmt.Errorf("failed to decode request: %v", err))
   300  	}
   301  
   302  	if err := n.state.DeleteJob(index, req.JobID); err != nil {
   303  		n.logger.Printf("[ERR] nomad.fsm: DeleteJob failed: %v", err)
   304  		return err
   305  	}
   306  
   307  	if err := n.periodicDispatcher.Remove(req.JobID); err != nil {
   308  		n.logger.Printf("[ERR] nomad.fsm: periodicDispatcher.Remove failed: %v", err)
   309  		return err
   310  	}
   311  
   312  	// We always delete from the periodic launch table because it is possible that
   313  	// the job was updated to be non-perioidic, thus checking if it is periodic
   314  	// doesn't ensure we clean it up properly.
   315  	n.state.DeletePeriodicLaunch(index, req.JobID)
   316  
   317  	return nil
   318  }
   319  
   320  func (n *nomadFSM) applyUpdateEval(buf []byte, index uint64) interface{} {
   321  	defer metrics.MeasureSince([]string{"nomad", "fsm", "update_eval"}, time.Now())
   322  	var req structs.EvalUpdateRequest
   323  	if err := structs.Decode(buf, &req); err != nil {
   324  		panic(fmt.Errorf("failed to decode request: %v", err))
   325  	}
   326  
   327  	if err := n.state.UpsertEvals(index, req.Evals); err != nil {
   328  		n.logger.Printf("[ERR] nomad.fsm: UpsertEvals failed: %v", err)
   329  		return err
   330  	}
   331  
   332  	for _, eval := range req.Evals {
   333  		if eval.ShouldEnqueue() {
   334  			n.evalBroker.Enqueue(eval)
   335  		} else if eval.ShouldBlock() {
   336  			n.blockedEvals.Block(eval)
   337  		}
   338  	}
   339  	return nil
   340  }
   341  
   342  func (n *nomadFSM) applyDeleteEval(buf []byte, index uint64) interface{} {
   343  	defer metrics.MeasureSince([]string{"nomad", "fsm", "delete_eval"}, time.Now())
   344  	var req structs.EvalDeleteRequest
   345  	if err := structs.Decode(buf, &req); err != nil {
   346  		panic(fmt.Errorf("failed to decode request: %v", err))
   347  	}
   348  
   349  	if err := n.state.DeleteEval(index, req.Evals, req.Allocs); err != nil {
   350  		n.logger.Printf("[ERR] nomad.fsm: DeleteEval failed: %v", err)
   351  		return err
   352  	}
   353  	return nil
   354  }
   355  
   356  func (n *nomadFSM) applyAllocUpdate(buf []byte, index uint64) interface{} {
   357  	defer metrics.MeasureSince([]string{"nomad", "fsm", "alloc_update"}, time.Now())
   358  	var req structs.AllocUpdateRequest
   359  	if err := structs.Decode(buf, &req); err != nil {
   360  		panic(fmt.Errorf("failed to decode request: %v", err))
   361  	}
   362  
   363  	// Attach the job to all the allocations. It is pulled out in the
   364  	// payload to avoid the redundancy of encoding, but should be denormalized
   365  	// prior to being inserted into MemDB.
   366  	if j := req.Job; j != nil {
   367  		for _, alloc := range req.Alloc {
   368  			if alloc.Job == nil {
   369  				alloc.Job = j
   370  			}
   371  		}
   372  	}
   373  
   374  	// Calculate the total resources of allocations. It is pulled out in the
   375  	// payload to avoid encoding something that can be computed, but should be
   376  	// denormalized prior to being inserted into MemDB.
   377  	for _, alloc := range req.Alloc {
   378  		if alloc.Resources != nil {
   379  			continue
   380  		}
   381  
   382  		alloc.Resources = new(structs.Resources)
   383  		for _, task := range alloc.TaskResources {
   384  			alloc.Resources.Add(task)
   385  		}
   386  	}
   387  
   388  	if err := n.state.UpsertAllocs(index, req.Alloc); err != nil {
   389  		n.logger.Printf("[ERR] nomad.fsm: UpsertAllocs failed: %v", err)
   390  		return err
   391  	}
   392  	return nil
   393  }
   394  
   395  func (n *nomadFSM) applyAllocClientUpdate(buf []byte, index uint64) interface{} {
   396  	defer metrics.MeasureSince([]string{"nomad", "fsm", "alloc_client_update"}, time.Now())
   397  	var req structs.AllocUpdateRequest
   398  	if err := structs.Decode(buf, &req); err != nil {
   399  		panic(fmt.Errorf("failed to decode request: %v", err))
   400  	}
   401  	if len(req.Alloc) == 0 {
   402  		return nil
   403  	}
   404  
   405  	// Update all the client allocations
   406  	if err := n.state.UpdateAllocsFromClient(index, req.Alloc); err != nil {
   407  		n.logger.Printf("[ERR] nomad.fsm: UpdateAllocFromClient failed: %v", err)
   408  		return err
   409  	}
   410  
   411  	// Unblock evals for the nodes computed node class if the client has
   412  	// finished running an allocation.
   413  	for _, alloc := range req.Alloc {
   414  		if alloc.ClientStatus == structs.AllocClientStatusComplete ||
   415  			alloc.ClientStatus == structs.AllocClientStatusFailed {
   416  			nodeID := alloc.NodeID
   417  			node, err := n.state.NodeByID(nodeID)
   418  			if err != nil || node == nil {
   419  				n.logger.Printf("[ERR] nomad.fsm: looking up node %q failed: %v", nodeID, err)
   420  				return err
   421  
   422  			}
   423  			n.blockedEvals.Unblock(node.ComputedClass, index)
   424  		}
   425  	}
   426  
   427  	return nil
   428  }
   429  
   430  func (n *nomadFSM) Snapshot() (raft.FSMSnapshot, error) {
   431  	// Create a new snapshot
   432  	snap, err := n.state.Snapshot()
   433  	if err != nil {
   434  		return nil, err
   435  	}
   436  
   437  	ns := &nomadSnapshot{
   438  		snap:      snap,
   439  		timetable: n.timetable,
   440  	}
   441  	return ns, nil
   442  }
   443  
   444  func (n *nomadFSM) Restore(old io.ReadCloser) error {
   445  	defer old.Close()
   446  
   447  	// Create a new state store
   448  	newState, err := state.NewStateStore(n.logOutput)
   449  	if err != nil {
   450  		return err
   451  	}
   452  	n.state = newState
   453  
   454  	// Start the state restore
   455  	restore, err := newState.Restore()
   456  	if err != nil {
   457  		return err
   458  	}
   459  	defer restore.Abort()
   460  
   461  	// Create a decoder
   462  	dec := codec.NewDecoder(old, structs.MsgpackHandle)
   463  
   464  	// Read in the header
   465  	var header snapshotHeader
   466  	if err := dec.Decode(&header); err != nil {
   467  		return err
   468  	}
   469  
   470  	// Populate the new state
   471  	msgType := make([]byte, 1)
   472  	for {
   473  		// Read the message type
   474  		_, err := old.Read(msgType)
   475  		if err == io.EOF {
   476  			break
   477  		} else if err != nil {
   478  			return err
   479  		}
   480  
   481  		// Decode
   482  		switch SnapshotType(msgType[0]) {
   483  		case TimeTableSnapshot:
   484  			if err := n.timetable.Deserialize(dec); err != nil {
   485  				return fmt.Errorf("time table deserialize failed: %v", err)
   486  			}
   487  
   488  		case NodeSnapshot:
   489  			node := new(structs.Node)
   490  			if err := dec.Decode(node); err != nil {
   491  				return err
   492  			}
   493  			if err := restore.NodeRestore(node); err != nil {
   494  				return err
   495  			}
   496  
   497  		case JobSnapshot:
   498  			job := new(structs.Job)
   499  			if err := dec.Decode(job); err != nil {
   500  				return err
   501  			}
   502  			if err := restore.JobRestore(job); err != nil {
   503  				return err
   504  			}
   505  
   506  		case EvalSnapshot:
   507  			eval := new(structs.Evaluation)
   508  			if err := dec.Decode(eval); err != nil {
   509  				return err
   510  			}
   511  			if err := restore.EvalRestore(eval); err != nil {
   512  				return err
   513  			}
   514  
   515  		case AllocSnapshot:
   516  			alloc := new(structs.Allocation)
   517  			if err := dec.Decode(alloc); err != nil {
   518  				return err
   519  			}
   520  			if err := restore.AllocRestore(alloc); err != nil {
   521  				return err
   522  			}
   523  
   524  		case IndexSnapshot:
   525  			idx := new(state.IndexEntry)
   526  			if err := dec.Decode(idx); err != nil {
   527  				return err
   528  			}
   529  			if err := restore.IndexRestore(idx); err != nil {
   530  				return err
   531  			}
   532  
   533  		case PeriodicLaunchSnapshot:
   534  			launch := new(structs.PeriodicLaunch)
   535  			if err := dec.Decode(launch); err != nil {
   536  				return err
   537  			}
   538  			if err := restore.PeriodicLaunchRestore(launch); err != nil {
   539  				return err
   540  			}
   541  
   542  		default:
   543  			return fmt.Errorf("Unrecognized snapshot type: %v", msgType)
   544  		}
   545  	}
   546  
   547  	// Commit the state restore
   548  	restore.Commit()
   549  	return nil
   550  }
   551  
   552  func (s *nomadSnapshot) Persist(sink raft.SnapshotSink) error {
   553  	defer metrics.MeasureSince([]string{"nomad", "fsm", "persist"}, time.Now())
   554  	// Register the nodes
   555  	encoder := codec.NewEncoder(sink, structs.MsgpackHandle)
   556  
   557  	// Write the header
   558  	header := snapshotHeader{}
   559  	if err := encoder.Encode(&header); err != nil {
   560  		sink.Cancel()
   561  		return err
   562  	}
   563  
   564  	// Write the time table
   565  	sink.Write([]byte{byte(TimeTableSnapshot)})
   566  	if err := s.timetable.Serialize(encoder); err != nil {
   567  		sink.Cancel()
   568  		return err
   569  	}
   570  
   571  	// Write all the data out
   572  	if err := s.persistIndexes(sink, encoder); err != nil {
   573  		sink.Cancel()
   574  		return err
   575  	}
   576  	if err := s.persistNodes(sink, encoder); err != nil {
   577  		sink.Cancel()
   578  		return err
   579  	}
   580  	if err := s.persistJobs(sink, encoder); err != nil {
   581  		sink.Cancel()
   582  		return err
   583  	}
   584  	if err := s.persistEvals(sink, encoder); err != nil {
   585  		sink.Cancel()
   586  		return err
   587  	}
   588  	if err := s.persistAllocs(sink, encoder); err != nil {
   589  		sink.Cancel()
   590  		return err
   591  	}
   592  	if err := s.persistPeriodicLaunches(sink, encoder); err != nil {
   593  		sink.Cancel()
   594  		return err
   595  	}
   596  	return nil
   597  }
   598  
   599  func (s *nomadSnapshot) persistIndexes(sink raft.SnapshotSink,
   600  	encoder *codec.Encoder) error {
   601  	// Get all the indexes
   602  	iter, err := s.snap.Indexes()
   603  	if err != nil {
   604  		return err
   605  	}
   606  
   607  	for {
   608  		// Get the next item
   609  		raw := iter.Next()
   610  		if raw == nil {
   611  			break
   612  		}
   613  
   614  		// Prepare the request struct
   615  		idx := raw.(*state.IndexEntry)
   616  
   617  		// Write out a node registration
   618  		sink.Write([]byte{byte(IndexSnapshot)})
   619  		if err := encoder.Encode(idx); err != nil {
   620  			return err
   621  		}
   622  	}
   623  	return nil
   624  }
   625  
   626  func (s *nomadSnapshot) persistNodes(sink raft.SnapshotSink,
   627  	encoder *codec.Encoder) error {
   628  	// Get all the nodes
   629  	nodes, err := s.snap.Nodes()
   630  	if err != nil {
   631  		return err
   632  	}
   633  
   634  	for {
   635  		// Get the next item
   636  		raw := nodes.Next()
   637  		if raw == nil {
   638  			break
   639  		}
   640  
   641  		// Prepare the request struct
   642  		node := raw.(*structs.Node)
   643  
   644  		// Write out a node registration
   645  		sink.Write([]byte{byte(NodeSnapshot)})
   646  		if err := encoder.Encode(node); err != nil {
   647  			return err
   648  		}
   649  	}
   650  	return nil
   651  }
   652  
   653  func (s *nomadSnapshot) persistJobs(sink raft.SnapshotSink,
   654  	encoder *codec.Encoder) error {
   655  	// Get all the jobs
   656  	jobs, err := s.snap.Jobs()
   657  	if err != nil {
   658  		return err
   659  	}
   660  
   661  	for {
   662  		// Get the next item
   663  		raw := jobs.Next()
   664  		if raw == nil {
   665  			break
   666  		}
   667  
   668  		// Prepare the request struct
   669  		job := raw.(*structs.Job)
   670  
   671  		// Write out a job registration
   672  		sink.Write([]byte{byte(JobSnapshot)})
   673  		if err := encoder.Encode(job); err != nil {
   674  			return err
   675  		}
   676  	}
   677  	return nil
   678  }
   679  
   680  func (s *nomadSnapshot) persistEvals(sink raft.SnapshotSink,
   681  	encoder *codec.Encoder) error {
   682  	// Get all the evaluations
   683  	evals, err := s.snap.Evals()
   684  	if err != nil {
   685  		return err
   686  	}
   687  
   688  	for {
   689  		// Get the next item
   690  		raw := evals.Next()
   691  		if raw == nil {
   692  			break
   693  		}
   694  
   695  		// Prepare the request struct
   696  		eval := raw.(*structs.Evaluation)
   697  
   698  		// Write out the evaluation
   699  		sink.Write([]byte{byte(EvalSnapshot)})
   700  		if err := encoder.Encode(eval); err != nil {
   701  			return err
   702  		}
   703  	}
   704  	return nil
   705  }
   706  
   707  func (s *nomadSnapshot) persistAllocs(sink raft.SnapshotSink,
   708  	encoder *codec.Encoder) error {
   709  	// Get all the allocations
   710  	allocs, err := s.snap.Allocs()
   711  	if err != nil {
   712  		return err
   713  	}
   714  
   715  	for {
   716  		// Get the next item
   717  		raw := allocs.Next()
   718  		if raw == nil {
   719  			break
   720  		}
   721  
   722  		// Prepare the request struct
   723  		alloc := raw.(*structs.Allocation)
   724  
   725  		// Write out the evaluation
   726  		sink.Write([]byte{byte(AllocSnapshot)})
   727  		if err := encoder.Encode(alloc); err != nil {
   728  			return err
   729  		}
   730  	}
   731  	return nil
   732  }
   733  
   734  func (s *nomadSnapshot) persistPeriodicLaunches(sink raft.SnapshotSink,
   735  	encoder *codec.Encoder) error {
   736  	// Get all the jobs
   737  	launches, err := s.snap.PeriodicLaunches()
   738  	if err != nil {
   739  		return err
   740  	}
   741  
   742  	for {
   743  		// Get the next item
   744  		raw := launches.Next()
   745  		if raw == nil {
   746  			break
   747  		}
   748  
   749  		// Prepare the request struct
   750  		launch := raw.(*structs.PeriodicLaunch)
   751  
   752  		// Write out a job registration
   753  		sink.Write([]byte{byte(PeriodicLaunchSnapshot)})
   754  		if err := encoder.Encode(launch); err != nil {
   755  			return err
   756  		}
   757  	}
   758  	return nil
   759  }
   760  
   761  // Release is a no-op, as we just need to GC the pointer
   762  // to the state store snapshot. There is nothing to explicitly
   763  // cleanup.
   764  func (s *nomadSnapshot) Release() {}