github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/nomad/state/state_store.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  
     8  	"github.com/hashicorp/go-memdb"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  )
    11  
    12  // IndexEntry is used with the "index" table
    13  // for managing the latest Raft index affecting a table.
    14  type IndexEntry struct {
    15  	Key   string
    16  	Value uint64
    17  }
    18  
    19  // The StateStore is responsible for maintaining all the Nomad
    20  // state. It is manipulated by the FSM which maintains consistency
    21  // through the use of Raft. The goals of the StateStore are to provide
    22  // high concurrency for read operations without blocking writes, and
    23  // to provide write availability in the face of reads. EVERY object
    24  // returned as a result of a read against the state store should be
    25  // considered a constant and NEVER modified in place.
    26  type StateStore struct {
    27  	logger *log.Logger
    28  	db     *memdb.MemDB
    29  
    30  	// abandonCh is used to signal watchers that this state store has been
    31  	// abandoned (usually during a restore). This is only ever closed.
    32  	abandonCh chan struct{}
    33  }
    34  
    35  // NewStateStore is used to create a new state store
    36  func NewStateStore(logOutput io.Writer) (*StateStore, error) {
    37  	// Create the MemDB
    38  	db, err := memdb.NewMemDB(stateStoreSchema())
    39  	if err != nil {
    40  		return nil, fmt.Errorf("state store setup failed: %v", err)
    41  	}
    42  
    43  	// Create the state store
    44  	s := &StateStore{
    45  		logger:    log.New(logOutput, "", log.LstdFlags),
    46  		db:        db,
    47  		abandonCh: make(chan struct{}),
    48  	}
    49  	return s, nil
    50  }
    51  
    52  // Snapshot is used to create a point in time snapshot. Because
    53  // we use MemDB, we just need to snapshot the state of the underlying
    54  // database.
    55  func (s *StateStore) Snapshot() (*StateSnapshot, error) {
    56  	snap := &StateSnapshot{
    57  		StateStore: StateStore{
    58  			logger: s.logger,
    59  			db:     s.db.Snapshot(),
    60  		},
    61  	}
    62  	return snap, nil
    63  }
    64  
    65  // Restore is used to optimize the efficiency of rebuilding
    66  // state by minimizing the number of transactions and checking
    67  // overhead.
    68  func (s *StateStore) Restore() (*StateRestore, error) {
    69  	txn := s.db.Txn(true)
    70  	r := &StateRestore{
    71  		txn: txn,
    72  	}
    73  	return r, nil
    74  }
    75  
    76  // AbandonCh returns a channel you can wait on to know if the state store was
    77  // abandoned.
    78  func (s *StateStore) AbandonCh() <-chan struct{} {
    79  	return s.abandonCh
    80  }
    81  
    82  // Abandon is used to signal that the given state store has been abandoned.
    83  // Calling this more than one time will panic.
    84  func (s *StateStore) Abandon() {
    85  	close(s.abandonCh)
    86  }
    87  
    88  // UpsertJobSummary upserts a job summary into the state store.
    89  func (s *StateStore) UpsertJobSummary(index uint64, jobSummary *structs.JobSummary) error {
    90  	txn := s.db.Txn(true)
    91  	defer txn.Abort()
    92  
    93  	// Update the index
    94  	if err := txn.Insert("job_summary", jobSummary); err != nil {
    95  		return err
    96  	}
    97  
    98  	// Update the indexes table for job summary
    99  	if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
   100  		return fmt.Errorf("index update failed: %v", err)
   101  	}
   102  
   103  	txn.Commit()
   104  	return nil
   105  }
   106  
   107  // DeleteJobSummary deletes the job summary with the given ID. This is for
   108  // testing purposes only.
   109  func (s *StateStore) DeleteJobSummary(index uint64, id string) error {
   110  	txn := s.db.Txn(true)
   111  	defer txn.Abort()
   112  
   113  	// Delete the job summary
   114  	if _, err := txn.DeleteAll("job_summary", "id", id); err != nil {
   115  		return fmt.Errorf("deleting job summary failed: %v", err)
   116  	}
   117  	if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
   118  		return fmt.Errorf("index update failed: %v", err)
   119  	}
   120  	txn.Commit()
   121  	return nil
   122  }
   123  
   124  // UpsertNode is used to register a node or update a node definition
   125  // This is assumed to be triggered by the client, so we retain the value
   126  // of drain which is set by the scheduler.
   127  func (s *StateStore) UpsertNode(index uint64, node *structs.Node) error {
   128  	txn := s.db.Txn(true)
   129  	defer txn.Abort()
   130  
   131  	// Check if the node already exists
   132  	existing, err := txn.First("nodes", "id", node.ID)
   133  	if err != nil {
   134  		return fmt.Errorf("node lookup failed: %v", err)
   135  	}
   136  
   137  	// Setup the indexes correctly
   138  	if existing != nil {
   139  		exist := existing.(*structs.Node)
   140  		node.CreateIndex = exist.CreateIndex
   141  		node.ModifyIndex = index
   142  		node.Drain = exist.Drain // Retain the drain mode
   143  	} else {
   144  		node.CreateIndex = index
   145  		node.ModifyIndex = index
   146  	}
   147  
   148  	// Insert the node
   149  	if err := txn.Insert("nodes", node); err != nil {
   150  		return fmt.Errorf("node insert failed: %v", err)
   151  	}
   152  	if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil {
   153  		return fmt.Errorf("index update failed: %v", err)
   154  	}
   155  
   156  	txn.Commit()
   157  	return nil
   158  }
   159  
   160  // DeleteNode is used to deregister a node
   161  func (s *StateStore) DeleteNode(index uint64, nodeID string) error {
   162  	txn := s.db.Txn(true)
   163  	defer txn.Abort()
   164  
   165  	// Lookup the node
   166  	existing, err := txn.First("nodes", "id", nodeID)
   167  	if err != nil {
   168  		return fmt.Errorf("node lookup failed: %v", err)
   169  	}
   170  	if existing == nil {
   171  		return fmt.Errorf("node not found")
   172  	}
   173  
   174  	// Delete the node
   175  	if err := txn.Delete("nodes", existing); err != nil {
   176  		return fmt.Errorf("node delete failed: %v", err)
   177  	}
   178  	if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil {
   179  		return fmt.Errorf("index update failed: %v", err)
   180  	}
   181  
   182  	txn.Commit()
   183  	return nil
   184  }
   185  
   186  // UpdateNodeStatus is used to update the status of a node
   187  func (s *StateStore) UpdateNodeStatus(index uint64, nodeID, status string) error {
   188  	txn := s.db.Txn(true)
   189  	defer txn.Abort()
   190  
   191  	// Lookup the node
   192  	existing, err := txn.First("nodes", "id", nodeID)
   193  	if err != nil {
   194  		return fmt.Errorf("node lookup failed: %v", err)
   195  	}
   196  	if existing == nil {
   197  		return fmt.Errorf("node not found")
   198  	}
   199  
   200  	// Copy the existing node
   201  	existingNode := existing.(*structs.Node)
   202  	copyNode := new(structs.Node)
   203  	*copyNode = *existingNode
   204  
   205  	// Update the status in the copy
   206  	copyNode.Status = status
   207  	copyNode.ModifyIndex = index
   208  
   209  	// Insert the node
   210  	if err := txn.Insert("nodes", copyNode); err != nil {
   211  		return fmt.Errorf("node update failed: %v", err)
   212  	}
   213  	if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil {
   214  		return fmt.Errorf("index update failed: %v", err)
   215  	}
   216  
   217  	txn.Commit()
   218  	return nil
   219  }
   220  
   221  // UpdateNodeDrain is used to update the drain of a node
   222  func (s *StateStore) UpdateNodeDrain(index uint64, nodeID string, drain bool) error {
   223  	txn := s.db.Txn(true)
   224  	defer txn.Abort()
   225  
   226  	// Lookup the node
   227  	existing, err := txn.First("nodes", "id", nodeID)
   228  	if err != nil {
   229  		return fmt.Errorf("node lookup failed: %v", err)
   230  	}
   231  	if existing == nil {
   232  		return fmt.Errorf("node not found")
   233  	}
   234  
   235  	// Copy the existing node
   236  	existingNode := existing.(*structs.Node)
   237  	copyNode := new(structs.Node)
   238  	*copyNode = *existingNode
   239  
   240  	// Update the drain in the copy
   241  	copyNode.Drain = drain
   242  	copyNode.ModifyIndex = index
   243  
   244  	// Insert the node
   245  	if err := txn.Insert("nodes", copyNode); err != nil {
   246  		return fmt.Errorf("node update failed: %v", err)
   247  	}
   248  	if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil {
   249  		return fmt.Errorf("index update failed: %v", err)
   250  	}
   251  
   252  	txn.Commit()
   253  	return nil
   254  }
   255  
   256  // NodeByID is used to lookup a node by ID
   257  func (s *StateStore) NodeByID(ws memdb.WatchSet, nodeID string) (*structs.Node, error) {
   258  	txn := s.db.Txn(false)
   259  
   260  	watchCh, existing, err := txn.FirstWatch("nodes", "id", nodeID)
   261  	if err != nil {
   262  		return nil, fmt.Errorf("node lookup failed: %v", err)
   263  	}
   264  	ws.Add(watchCh)
   265  
   266  	if existing != nil {
   267  		return existing.(*structs.Node), nil
   268  	}
   269  	return nil, nil
   270  }
   271  
   272  // NodesByIDPrefix is used to lookup nodes by prefix
   273  func (s *StateStore) NodesByIDPrefix(ws memdb.WatchSet, nodeID string) (memdb.ResultIterator, error) {
   274  	txn := s.db.Txn(false)
   275  
   276  	iter, err := txn.Get("nodes", "id_prefix", nodeID)
   277  	if err != nil {
   278  		return nil, fmt.Errorf("node lookup failed: %v", err)
   279  	}
   280  	ws.Add(iter.WatchCh())
   281  
   282  	return iter, nil
   283  }
   284  
   285  // Nodes returns an iterator over all the nodes
   286  func (s *StateStore) Nodes(ws memdb.WatchSet) (memdb.ResultIterator, error) {
   287  	txn := s.db.Txn(false)
   288  
   289  	// Walk the entire nodes table
   290  	iter, err := txn.Get("nodes", "id")
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	ws.Add(iter.WatchCh())
   295  	return iter, nil
   296  }
   297  
   298  // UpsertJob is used to register a job or update a job definition
   299  func (s *StateStore) UpsertJob(index uint64, job *structs.Job) error {
   300  	txn := s.db.Txn(true)
   301  	defer txn.Abort()
   302  
   303  	// Check if the job already exists
   304  	existing, err := txn.First("jobs", "id", job.ID)
   305  	if err != nil {
   306  		return fmt.Errorf("job lookup failed: %v", err)
   307  	}
   308  
   309  	// Setup the indexes correctly
   310  	if existing != nil {
   311  		job.CreateIndex = existing.(*structs.Job).CreateIndex
   312  		job.ModifyIndex = index
   313  		job.JobModifyIndex = index
   314  
   315  		// Compute the job status
   316  		var err error
   317  		job.Status, err = s.getJobStatus(txn, job, false)
   318  		if err != nil {
   319  			return fmt.Errorf("setting job status for %q failed: %v", job.ID, err)
   320  		}
   321  	} else {
   322  		job.CreateIndex = index
   323  		job.ModifyIndex = index
   324  		job.JobModifyIndex = index
   325  
   326  		if err := s.setJobStatus(index, txn, job, false, ""); err != nil {
   327  			return fmt.Errorf("setting job status for %q failed: %v", job.ID, err)
   328  		}
   329  
   330  		// Have to get the job again since it could have been updated
   331  		updated, err := txn.First("jobs", "id", job.ID)
   332  		if err != nil {
   333  			return fmt.Errorf("job lookup failed: %v", err)
   334  		}
   335  		if updated != nil {
   336  			job = updated.(*structs.Job)
   337  		}
   338  	}
   339  
   340  	if err := s.updateSummaryWithJob(index, job, txn); err != nil {
   341  		return fmt.Errorf("unable to create job summary: %v", err)
   342  	}
   343  
   344  	// Create the EphemeralDisk if it's nil by adding up DiskMB from task resources.
   345  	// COMPAT 0.4.1 -> 0.5
   346  	s.addEphemeralDiskToTaskGroups(job)
   347  
   348  	// Insert the job
   349  	if err := txn.Insert("jobs", job); err != nil {
   350  		return fmt.Errorf("job insert failed: %v", err)
   351  	}
   352  	if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil {
   353  		return fmt.Errorf("index update failed: %v", err)
   354  	}
   355  
   356  	txn.Commit()
   357  	return nil
   358  }
   359  
   360  // DeleteJob is used to deregister a job
   361  func (s *StateStore) DeleteJob(index uint64, jobID string) error {
   362  	txn := s.db.Txn(true)
   363  	defer txn.Abort()
   364  
   365  	// Lookup the node
   366  	existing, err := txn.First("jobs", "id", jobID)
   367  	if err != nil {
   368  		return fmt.Errorf("job lookup failed: %v", err)
   369  	}
   370  	if existing == nil {
   371  		return fmt.Errorf("job not found")
   372  	}
   373  
   374  	// Check if we should update a parent job summary
   375  	job := existing.(*structs.Job)
   376  	if job.ParentID != "" {
   377  		summaryRaw, err := txn.First("job_summary", "id", job.ParentID)
   378  		if err != nil {
   379  			return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
   380  		}
   381  
   382  		// Only continue if the summary exists. It could not exist if the parent
   383  		// job was removed
   384  		if summaryRaw != nil {
   385  			existing := summaryRaw.(*structs.JobSummary)
   386  			pSummary := existing.Copy()
   387  			if pSummary.Children != nil {
   388  
   389  				modified := false
   390  				switch job.Status {
   391  				case structs.JobStatusPending:
   392  					pSummary.Children.Pending--
   393  					pSummary.Children.Dead++
   394  					modified = true
   395  				case structs.JobStatusRunning:
   396  					pSummary.Children.Running--
   397  					pSummary.Children.Dead++
   398  					modified = true
   399  				case structs.JobStatusDead:
   400  				default:
   401  					return fmt.Errorf("unknown old job status %q", job.Status)
   402  				}
   403  
   404  				if modified {
   405  					// Update the modify index
   406  					pSummary.ModifyIndex = index
   407  
   408  					// Insert the summary
   409  					if err := txn.Insert("job_summary", pSummary); err != nil {
   410  						return fmt.Errorf("job summary insert failed: %v", err)
   411  					}
   412  					if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
   413  						return fmt.Errorf("index update failed: %v", err)
   414  					}
   415  				}
   416  			}
   417  		}
   418  	}
   419  
   420  	// Delete the job
   421  	if err := txn.Delete("jobs", existing); err != nil {
   422  		return fmt.Errorf("job delete failed: %v", err)
   423  	}
   424  	if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil {
   425  		return fmt.Errorf("index update failed: %v", err)
   426  	}
   427  
   428  	// Delete the job summary
   429  	if _, err = txn.DeleteAll("job_summary", "id", jobID); err != nil {
   430  		return fmt.Errorf("deleing job summary failed: %v", err)
   431  	}
   432  	if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
   433  		return fmt.Errorf("index update failed: %v", err)
   434  	}
   435  
   436  	txn.Commit()
   437  	return nil
   438  }
   439  
   440  // JobByID is used to lookup a job by its ID
   441  func (s *StateStore) JobByID(ws memdb.WatchSet, id string) (*structs.Job, error) {
   442  	txn := s.db.Txn(false)
   443  
   444  	watchCh, existing, err := txn.FirstWatch("jobs", "id", id)
   445  	if err != nil {
   446  		return nil, fmt.Errorf("job lookup failed: %v", err)
   447  	}
   448  	ws.Add(watchCh)
   449  
   450  	if existing != nil {
   451  		return existing.(*structs.Job), nil
   452  	}
   453  	return nil, nil
   454  }
   455  
   456  // JobsByIDPrefix is used to lookup a job by prefix
   457  func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
   458  	txn := s.db.Txn(false)
   459  
   460  	iter, err := txn.Get("jobs", "id_prefix", id)
   461  	if err != nil {
   462  		return nil, fmt.Errorf("job lookup failed: %v", err)
   463  	}
   464  
   465  	ws.Add(iter.WatchCh())
   466  
   467  	return iter, nil
   468  }
   469  
   470  // Jobs returns an iterator over all the jobs
   471  func (s *StateStore) Jobs(ws memdb.WatchSet) (memdb.ResultIterator, error) {
   472  	txn := s.db.Txn(false)
   473  
   474  	// Walk the entire jobs table
   475  	iter, err := txn.Get("jobs", "id")
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  
   480  	ws.Add(iter.WatchCh())
   481  
   482  	return iter, nil
   483  }
   484  
   485  // JobsByPeriodic returns an iterator over all the periodic or non-periodic jobs.
   486  func (s *StateStore) JobsByPeriodic(ws memdb.WatchSet, periodic bool) (memdb.ResultIterator, error) {
   487  	txn := s.db.Txn(false)
   488  
   489  	iter, err := txn.Get("jobs", "periodic", periodic)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	ws.Add(iter.WatchCh())
   495  
   496  	return iter, nil
   497  }
   498  
   499  // JobsByScheduler returns an iterator over all the jobs with the specific
   500  // scheduler type.
   501  func (s *StateStore) JobsByScheduler(ws memdb.WatchSet, schedulerType string) (memdb.ResultIterator, error) {
   502  	txn := s.db.Txn(false)
   503  
   504  	// Return an iterator for jobs with the specific type.
   505  	iter, err := txn.Get("jobs", "type", schedulerType)
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  
   510  	ws.Add(iter.WatchCh())
   511  
   512  	return iter, nil
   513  }
   514  
   515  // JobsByGC returns an iterator over all jobs eligible or uneligible for garbage
   516  // collection.
   517  func (s *StateStore) JobsByGC(ws memdb.WatchSet, gc bool) (memdb.ResultIterator, error) {
   518  	txn := s.db.Txn(false)
   519  
   520  	iter, err := txn.Get("jobs", "gc", gc)
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  
   525  	ws.Add(iter.WatchCh())
   526  
   527  	return iter, nil
   528  }
   529  
   530  // JobSummary returns a job summary object which matches a specific id.
   531  func (s *StateStore) JobSummaryByID(ws memdb.WatchSet, jobID string) (*structs.JobSummary, error) {
   532  	txn := s.db.Txn(false)
   533  
   534  	watchCh, existing, err := txn.FirstWatch("job_summary", "id", jobID)
   535  	if err != nil {
   536  		return nil, err
   537  	}
   538  
   539  	ws.Add(watchCh)
   540  
   541  	if existing != nil {
   542  		summary := existing.(*structs.JobSummary)
   543  		return summary, nil
   544  	}
   545  
   546  	return nil, nil
   547  }
   548  
   549  // JobSummaries walks the entire job summary table and returns all the job
   550  // summary objects
   551  func (s *StateStore) JobSummaries(ws memdb.WatchSet) (memdb.ResultIterator, error) {
   552  	txn := s.db.Txn(false)
   553  
   554  	iter, err := txn.Get("job_summary", "id")
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  
   559  	ws.Add(iter.WatchCh())
   560  
   561  	return iter, nil
   562  }
   563  
   564  // JobSummaryByPrefix is used to look up Job Summary by id prefix
   565  func (s *StateStore) JobSummaryByPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
   566  	txn := s.db.Txn(false)
   567  
   568  	iter, err := txn.Get("job_summary", "id_prefix", id)
   569  	if err != nil {
   570  		return nil, fmt.Errorf("eval lookup failed: %v", err)
   571  	}
   572  
   573  	ws.Add(iter.WatchCh())
   574  
   575  	return iter, nil
   576  }
   577  
   578  // UpsertPeriodicLaunch is used to register a launch or update it.
   579  func (s *StateStore) UpsertPeriodicLaunch(index uint64, launch *structs.PeriodicLaunch) error {
   580  	txn := s.db.Txn(true)
   581  	defer txn.Abort()
   582  
   583  	// Check if the job already exists
   584  	existing, err := txn.First("periodic_launch", "id", launch.ID)
   585  	if err != nil {
   586  		return fmt.Errorf("periodic launch lookup failed: %v", err)
   587  	}
   588  
   589  	// Setup the indexes correctly
   590  	if existing != nil {
   591  		launch.CreateIndex = existing.(*structs.PeriodicLaunch).CreateIndex
   592  		launch.ModifyIndex = index
   593  	} else {
   594  		launch.CreateIndex = index
   595  		launch.ModifyIndex = index
   596  	}
   597  
   598  	// Insert the job
   599  	if err := txn.Insert("periodic_launch", launch); err != nil {
   600  		return fmt.Errorf("launch insert failed: %v", err)
   601  	}
   602  	if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil {
   603  		return fmt.Errorf("index update failed: %v", err)
   604  	}
   605  
   606  	txn.Commit()
   607  	return nil
   608  }
   609  
   610  // DeletePeriodicLaunch is used to delete the periodic launch
   611  func (s *StateStore) DeletePeriodicLaunch(index uint64, jobID string) error {
   612  	txn := s.db.Txn(true)
   613  	defer txn.Abort()
   614  
   615  	// Lookup the launch
   616  	existing, err := txn.First("periodic_launch", "id", jobID)
   617  	if err != nil {
   618  		return fmt.Errorf("launch lookup failed: %v", err)
   619  	}
   620  	if existing == nil {
   621  		return fmt.Errorf("launch not found")
   622  	}
   623  
   624  	// Delete the launch
   625  	if err := txn.Delete("periodic_launch", existing); err != nil {
   626  		return fmt.Errorf("launch delete failed: %v", err)
   627  	}
   628  	if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil {
   629  		return fmt.Errorf("index update failed: %v", err)
   630  	}
   631  
   632  	txn.Commit()
   633  	return nil
   634  }
   635  
   636  // PeriodicLaunchByID is used to lookup a periodic launch by the periodic job
   637  // ID.
   638  func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, id string) (*structs.PeriodicLaunch, error) {
   639  	txn := s.db.Txn(false)
   640  
   641  	watchCh, existing, err := txn.FirstWatch("periodic_launch", "id", id)
   642  	if err != nil {
   643  		return nil, fmt.Errorf("periodic launch lookup failed: %v", err)
   644  	}
   645  
   646  	ws.Add(watchCh)
   647  
   648  	if existing != nil {
   649  		return existing.(*structs.PeriodicLaunch), nil
   650  	}
   651  	return nil, nil
   652  }
   653  
   654  // PeriodicLaunches returns an iterator over all the periodic launches
   655  func (s *StateStore) PeriodicLaunches(ws memdb.WatchSet) (memdb.ResultIterator, error) {
   656  	txn := s.db.Txn(false)
   657  
   658  	// Walk the entire table
   659  	iter, err := txn.Get("periodic_launch", "id")
   660  	if err != nil {
   661  		return nil, err
   662  	}
   663  
   664  	ws.Add(iter.WatchCh())
   665  
   666  	return iter, nil
   667  }
   668  
   669  // UpsertEvals is used to upsert a set of evaluations
   670  func (s *StateStore) UpsertEvals(index uint64, evals []*structs.Evaluation) error {
   671  	txn := s.db.Txn(true)
   672  	defer txn.Abort()
   673  
   674  	// Do a nested upsert
   675  	jobs := make(map[string]string, len(evals))
   676  	for _, eval := range evals {
   677  		if err := s.nestedUpsertEval(txn, index, eval); err != nil {
   678  			return err
   679  		}
   680  
   681  		jobs[eval.JobID] = ""
   682  	}
   683  
   684  	// Set the job's status
   685  	if err := s.setJobStatuses(index, txn, jobs, false); err != nil {
   686  		return fmt.Errorf("setting job status failed: %v", err)
   687  	}
   688  
   689  	txn.Commit()
   690  	return nil
   691  }
   692  
   693  // nestedUpsertEvaluation is used to nest an evaluation upsert within a transaction
   694  func (s *StateStore) nestedUpsertEval(txn *memdb.Txn, index uint64, eval *structs.Evaluation) error {
   695  	// Lookup the evaluation
   696  	existing, err := txn.First("evals", "id", eval.ID)
   697  	if err != nil {
   698  		return fmt.Errorf("eval lookup failed: %v", err)
   699  	}
   700  
   701  	// Update the indexes
   702  	if existing != nil {
   703  		eval.CreateIndex = existing.(*structs.Evaluation).CreateIndex
   704  		eval.ModifyIndex = index
   705  	} else {
   706  		eval.CreateIndex = index
   707  		eval.ModifyIndex = index
   708  	}
   709  
   710  	// Update the job summary
   711  	summaryRaw, err := txn.First("job_summary", "id", eval.JobID)
   712  	if err != nil {
   713  		return fmt.Errorf("job summary lookup failed: %v", err)
   714  	}
   715  	if summaryRaw != nil {
   716  		js := summaryRaw.(*structs.JobSummary).Copy()
   717  		hasSummaryChanged := false
   718  		for tg, num := range eval.QueuedAllocations {
   719  			if summary, ok := js.Summary[tg]; ok {
   720  				if summary.Queued != num {
   721  					summary.Queued = num
   722  					js.Summary[tg] = summary
   723  					hasSummaryChanged = true
   724  				}
   725  			} else {
   726  				s.logger.Printf("[ERR] state_store: unable to update queued for job %q and task group %q", eval.JobID, tg)
   727  			}
   728  		}
   729  
   730  		// Insert the job summary
   731  		if hasSummaryChanged {
   732  			js.ModifyIndex = index
   733  			if err := txn.Insert("job_summary", js); err != nil {
   734  				return fmt.Errorf("job summary insert failed: %v", err)
   735  			}
   736  			if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
   737  				return fmt.Errorf("index update failed: %v", err)
   738  			}
   739  		}
   740  	}
   741  
   742  	// Check if the job has any blocked evaluations and cancel them
   743  	if eval.Status == structs.EvalStatusComplete && len(eval.FailedTGAllocs) == 0 {
   744  		// Get the blocked evaluation for a job if it exists
   745  		iter, err := txn.Get("evals", "job", eval.JobID, structs.EvalStatusBlocked)
   746  		if err != nil {
   747  			return fmt.Errorf("failed to get blocked evals for job %q: %v", eval.JobID, err)
   748  		}
   749  
   750  		var blocked []*structs.Evaluation
   751  		for {
   752  			raw := iter.Next()
   753  			if raw == nil {
   754  				break
   755  			}
   756  			blocked = append(blocked, raw.(*structs.Evaluation))
   757  		}
   758  
   759  		// Go through and update the evals
   760  		for _, eval := range blocked {
   761  			newEval := eval.Copy()
   762  			newEval.Status = structs.EvalStatusCancelled
   763  			newEval.StatusDescription = fmt.Sprintf("evaluation %q successful", newEval.ID)
   764  			newEval.ModifyIndex = index
   765  			if err := txn.Insert("evals", newEval); err != nil {
   766  				return fmt.Errorf("eval insert failed: %v", err)
   767  			}
   768  		}
   769  	}
   770  
   771  	// Insert the eval
   772  	if err := txn.Insert("evals", eval); err != nil {
   773  		return fmt.Errorf("eval insert failed: %v", err)
   774  	}
   775  	if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil {
   776  		return fmt.Errorf("index update failed: %v", err)
   777  	}
   778  	return nil
   779  }
   780  
   781  // DeleteEval is used to delete an evaluation
   782  func (s *StateStore) DeleteEval(index uint64, evals []string, allocs []string) error {
   783  	txn := s.db.Txn(true)
   784  	defer txn.Abort()
   785  
   786  	jobs := make(map[string]string, len(evals))
   787  	for _, eval := range evals {
   788  		existing, err := txn.First("evals", "id", eval)
   789  		if err != nil {
   790  			return fmt.Errorf("eval lookup failed: %v", err)
   791  		}
   792  		if existing == nil {
   793  			continue
   794  		}
   795  		if err := txn.Delete("evals", existing); err != nil {
   796  			return fmt.Errorf("eval delete failed: %v", err)
   797  		}
   798  		jobID := existing.(*structs.Evaluation).JobID
   799  		jobs[jobID] = ""
   800  	}
   801  
   802  	for _, alloc := range allocs {
   803  		existing, err := txn.First("allocs", "id", alloc)
   804  		if err != nil {
   805  			return fmt.Errorf("alloc lookup failed: %v", err)
   806  		}
   807  		if existing == nil {
   808  			continue
   809  		}
   810  		if err := txn.Delete("allocs", existing); err != nil {
   811  			return fmt.Errorf("alloc delete failed: %v", err)
   812  		}
   813  	}
   814  
   815  	// Update the indexes
   816  	if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil {
   817  		return fmt.Errorf("index update failed: %v", err)
   818  	}
   819  	if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil {
   820  		return fmt.Errorf("index update failed: %v", err)
   821  	}
   822  
   823  	// Set the job's status
   824  	if err := s.setJobStatuses(index, txn, jobs, true); err != nil {
   825  		return fmt.Errorf("setting job status failed: %v", err)
   826  	}
   827  
   828  	txn.Commit()
   829  	return nil
   830  }
   831  
   832  // EvalByID is used to lookup an eval by its ID
   833  func (s *StateStore) EvalByID(ws memdb.WatchSet, id string) (*structs.Evaluation, error) {
   834  	txn := s.db.Txn(false)
   835  
   836  	watchCh, existing, err := txn.FirstWatch("evals", "id", id)
   837  	if err != nil {
   838  		return nil, fmt.Errorf("eval lookup failed: %v", err)
   839  	}
   840  
   841  	ws.Add(watchCh)
   842  
   843  	if existing != nil {
   844  		return existing.(*structs.Evaluation), nil
   845  	}
   846  	return nil, nil
   847  }
   848  
   849  // EvalsByIDPrefix is used to lookup evaluations by prefix
   850  func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
   851  	txn := s.db.Txn(false)
   852  
   853  	iter, err := txn.Get("evals", "id_prefix", id)
   854  	if err != nil {
   855  		return nil, fmt.Errorf("eval lookup failed: %v", err)
   856  	}
   857  
   858  	ws.Add(iter.WatchCh())
   859  
   860  	return iter, nil
   861  }
   862  
   863  // EvalsByJob returns all the evaluations by job id
   864  func (s *StateStore) EvalsByJob(ws memdb.WatchSet, jobID string) ([]*structs.Evaluation, error) {
   865  	txn := s.db.Txn(false)
   866  
   867  	// Get an iterator over the node allocations
   868  	iter, err := txn.Get("evals", "job_prefix", jobID)
   869  	if err != nil {
   870  		return nil, err
   871  	}
   872  
   873  	ws.Add(iter.WatchCh())
   874  
   875  	var out []*structs.Evaluation
   876  	for {
   877  		raw := iter.Next()
   878  		if raw == nil {
   879  			break
   880  		}
   881  
   882  		e := raw.(*structs.Evaluation)
   883  
   884  		// Filter non-exact matches
   885  		if e.JobID != jobID {
   886  			continue
   887  		}
   888  
   889  		out = append(out, e)
   890  	}
   891  	return out, nil
   892  }
   893  
   894  // Evals returns an iterator over all the evaluations
   895  func (s *StateStore) Evals(ws memdb.WatchSet) (memdb.ResultIterator, error) {
   896  	txn := s.db.Txn(false)
   897  
   898  	// Walk the entire table
   899  	iter, err := txn.Get("evals", "id")
   900  	if err != nil {
   901  		return nil, err
   902  	}
   903  
   904  	ws.Add(iter.WatchCh())
   905  
   906  	return iter, nil
   907  }
   908  
   909  // UpdateAllocsFromClient is used to update an allocation based on input
   910  // from a client. While the schedulers are the authority on the allocation for
   911  // most things, some updates are authoritative from the client. Specifically,
   912  // the desired state comes from the schedulers, while the actual state comes
   913  // from clients.
   914  func (s *StateStore) UpdateAllocsFromClient(index uint64, allocs []*structs.Allocation) error {
   915  	txn := s.db.Txn(true)
   916  	defer txn.Abort()
   917  
   918  	// Handle each of the updated allocations
   919  	for _, alloc := range allocs {
   920  		if err := s.nestedUpdateAllocFromClient(txn, index, alloc); err != nil {
   921  			return err
   922  		}
   923  	}
   924  
   925  	// Update the indexes
   926  	if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil {
   927  		return fmt.Errorf("index update failed: %v", err)
   928  	}
   929  
   930  	txn.Commit()
   931  	return nil
   932  }
   933  
   934  // nestedUpdateAllocFromClient is used to nest an update of an allocation with client status
   935  func (s *StateStore) nestedUpdateAllocFromClient(txn *memdb.Txn, index uint64, alloc *structs.Allocation) error {
   936  	// Look for existing alloc
   937  	existing, err := txn.First("allocs", "id", alloc.ID)
   938  	if err != nil {
   939  		return fmt.Errorf("alloc lookup failed: %v", err)
   940  	}
   941  
   942  	// Nothing to do if this does not exist
   943  	if existing == nil {
   944  		return nil
   945  	}
   946  	exist := existing.(*structs.Allocation)
   947  
   948  	// Copy everything from the existing allocation
   949  	copyAlloc := exist.Copy()
   950  
   951  	// Pull in anything the client is the authority on
   952  	copyAlloc.ClientStatus = alloc.ClientStatus
   953  	copyAlloc.ClientDescription = alloc.ClientDescription
   954  	copyAlloc.TaskStates = alloc.TaskStates
   955  
   956  	// Update the modify index
   957  	copyAlloc.ModifyIndex = index
   958  
   959  	if err := s.updateSummaryWithAlloc(index, copyAlloc, exist, txn); err != nil {
   960  		return fmt.Errorf("error updating job summary: %v", err)
   961  	}
   962  
   963  	// Update the allocation
   964  	if err := txn.Insert("allocs", copyAlloc); err != nil {
   965  		return fmt.Errorf("alloc insert failed: %v", err)
   966  	}
   967  
   968  	// Set the job's status
   969  	forceStatus := ""
   970  	if !copyAlloc.TerminalStatus() {
   971  		forceStatus = structs.JobStatusRunning
   972  	}
   973  	jobs := map[string]string{exist.JobID: forceStatus}
   974  	if err := s.setJobStatuses(index, txn, jobs, false); err != nil {
   975  		return fmt.Errorf("setting job status failed: %v", err)
   976  	}
   977  	return nil
   978  }
   979  
   980  // UpsertAllocs is used to evict a set of allocations
   981  // and allocate new ones at the same time.
   982  func (s *StateStore) UpsertAllocs(index uint64, allocs []*structs.Allocation) error {
   983  	txn := s.db.Txn(true)
   984  	defer txn.Abort()
   985  
   986  	// Handle the allocations
   987  	jobs := make(map[string]string, 1)
   988  	for _, alloc := range allocs {
   989  		existing, err := txn.First("allocs", "id", alloc.ID)
   990  		if err != nil {
   991  			return fmt.Errorf("alloc lookup failed: %v", err)
   992  		}
   993  		exist, _ := existing.(*structs.Allocation)
   994  
   995  		if exist == nil {
   996  			alloc.CreateIndex = index
   997  			alloc.ModifyIndex = index
   998  			alloc.AllocModifyIndex = index
   999  		} else {
  1000  			alloc.CreateIndex = exist.CreateIndex
  1001  			alloc.ModifyIndex = index
  1002  			alloc.AllocModifyIndex = index
  1003  
  1004  			// If the scheduler is marking this allocation as lost we do not
  1005  			// want to reuse the status of the existing allocation.
  1006  			if alloc.ClientStatus != structs.AllocClientStatusLost {
  1007  				alloc.ClientStatus = exist.ClientStatus
  1008  				alloc.ClientDescription = exist.ClientDescription
  1009  			}
  1010  
  1011  			// The job has been denormalized so re-attach the original job
  1012  			if alloc.Job == nil {
  1013  				alloc.Job = exist.Job
  1014  			}
  1015  		}
  1016  
  1017  		if err := s.updateSummaryWithAlloc(index, alloc, exist, txn); err != nil {
  1018  			return fmt.Errorf("error updating job summary: %v", err)
  1019  		}
  1020  
  1021  		// Create the EphemeralDisk if it's nil by adding up DiskMB from task resources.
  1022  		// COMPAT 0.4.1 -> 0.5
  1023  		if alloc.Job != nil {
  1024  			s.addEphemeralDiskToTaskGroups(alloc.Job)
  1025  		}
  1026  
  1027  		if err := txn.Insert("allocs", alloc); err != nil {
  1028  			return fmt.Errorf("alloc insert failed: %v", err)
  1029  		}
  1030  
  1031  		// If the allocation is running, force the job to running status.
  1032  		forceStatus := ""
  1033  		if !alloc.TerminalStatus() {
  1034  			forceStatus = structs.JobStatusRunning
  1035  		}
  1036  		jobs[alloc.JobID] = forceStatus
  1037  	}
  1038  
  1039  	// Update the indexes
  1040  	if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil {
  1041  		return fmt.Errorf("index update failed: %v", err)
  1042  	}
  1043  
  1044  	// Set the job's status
  1045  	if err := s.setJobStatuses(index, txn, jobs, false); err != nil {
  1046  		return fmt.Errorf("setting job status failed: %v", err)
  1047  	}
  1048  
  1049  	txn.Commit()
  1050  	return nil
  1051  }
  1052  
  1053  // AllocByID is used to lookup an allocation by its ID
  1054  func (s *StateStore) AllocByID(ws memdb.WatchSet, id string) (*structs.Allocation, error) {
  1055  	txn := s.db.Txn(false)
  1056  
  1057  	watchCh, existing, err := txn.FirstWatch("allocs", "id", id)
  1058  	if err != nil {
  1059  		return nil, fmt.Errorf("alloc lookup failed: %v", err)
  1060  	}
  1061  
  1062  	ws.Add(watchCh)
  1063  
  1064  	if existing != nil {
  1065  		return existing.(*structs.Allocation), nil
  1066  	}
  1067  	return nil, nil
  1068  }
  1069  
  1070  // AllocsByIDPrefix is used to lookup allocs by prefix
  1071  func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) {
  1072  	txn := s.db.Txn(false)
  1073  
  1074  	iter, err := txn.Get("allocs", "id_prefix", id)
  1075  	if err != nil {
  1076  		return nil, fmt.Errorf("alloc lookup failed: %v", err)
  1077  	}
  1078  
  1079  	ws.Add(iter.WatchCh())
  1080  
  1081  	return iter, nil
  1082  }
  1083  
  1084  // AllocsByNode returns all the allocations by node
  1085  func (s *StateStore) AllocsByNode(ws memdb.WatchSet, node string) ([]*structs.Allocation, error) {
  1086  	txn := s.db.Txn(false)
  1087  
  1088  	// Get an iterator over the node allocations, using only the
  1089  	// node prefix which ignores the terminal status
  1090  	iter, err := txn.Get("allocs", "node_prefix", node)
  1091  	if err != nil {
  1092  		return nil, err
  1093  	}
  1094  
  1095  	ws.Add(iter.WatchCh())
  1096  
  1097  	var out []*structs.Allocation
  1098  	for {
  1099  		raw := iter.Next()
  1100  		if raw == nil {
  1101  			break
  1102  		}
  1103  		out = append(out, raw.(*structs.Allocation))
  1104  	}
  1105  	return out, nil
  1106  }
  1107  
  1108  // AllocsByNode returns all the allocations by node and terminal status
  1109  func (s *StateStore) AllocsByNodeTerminal(ws memdb.WatchSet, node string, terminal bool) ([]*structs.Allocation, error) {
  1110  	txn := s.db.Txn(false)
  1111  
  1112  	// Get an iterator over the node allocations
  1113  	iter, err := txn.Get("allocs", "node", node, terminal)
  1114  	if err != nil {
  1115  		return nil, err
  1116  	}
  1117  
  1118  	ws.Add(iter.WatchCh())
  1119  
  1120  	var out []*structs.Allocation
  1121  	for {
  1122  		raw := iter.Next()
  1123  		if raw == nil {
  1124  			break
  1125  		}
  1126  		out = append(out, raw.(*structs.Allocation))
  1127  	}
  1128  	return out, nil
  1129  }
  1130  
  1131  // AllocsByJob returns all the allocations by job id
  1132  func (s *StateStore) AllocsByJob(ws memdb.WatchSet, jobID string, all bool) ([]*structs.Allocation, error) {
  1133  	txn := s.db.Txn(false)
  1134  
  1135  	// Get the job
  1136  	var job *structs.Job
  1137  	rawJob, err := txn.First("jobs", "id", jobID)
  1138  	if err != nil {
  1139  		return nil, err
  1140  	}
  1141  	if rawJob != nil {
  1142  		job = rawJob.(*structs.Job)
  1143  	}
  1144  
  1145  	// Get an iterator over the node allocations
  1146  	iter, err := txn.Get("allocs", "job", jobID)
  1147  	if err != nil {
  1148  		return nil, err
  1149  	}
  1150  
  1151  	ws.Add(iter.WatchCh())
  1152  
  1153  	var out []*structs.Allocation
  1154  	for {
  1155  		raw := iter.Next()
  1156  		if raw == nil {
  1157  			break
  1158  		}
  1159  
  1160  		alloc := raw.(*structs.Allocation)
  1161  		// If the allocation belongs to a job with the same ID but a different
  1162  		// create index and we are not getting all the allocations whose Jobs
  1163  		// matches the same Job ID then we skip it
  1164  		if !all && job != nil && alloc.Job.CreateIndex != job.CreateIndex {
  1165  			continue
  1166  		}
  1167  		out = append(out, raw.(*structs.Allocation))
  1168  	}
  1169  	return out, nil
  1170  }
  1171  
  1172  // AllocsByEval returns all the allocations by eval id
  1173  func (s *StateStore) AllocsByEval(ws memdb.WatchSet, evalID string) ([]*structs.Allocation, error) {
  1174  	txn := s.db.Txn(false)
  1175  
  1176  	// Get an iterator over the eval allocations
  1177  	iter, err := txn.Get("allocs", "eval", evalID)
  1178  	if err != nil {
  1179  		return nil, err
  1180  	}
  1181  
  1182  	ws.Add(iter.WatchCh())
  1183  
  1184  	var out []*structs.Allocation
  1185  	for {
  1186  		raw := iter.Next()
  1187  		if raw == nil {
  1188  			break
  1189  		}
  1190  		out = append(out, raw.(*structs.Allocation))
  1191  	}
  1192  	return out, nil
  1193  }
  1194  
  1195  // Allocs returns an iterator over all the evaluations
  1196  func (s *StateStore) Allocs(ws memdb.WatchSet) (memdb.ResultIterator, error) {
  1197  	txn := s.db.Txn(false)
  1198  
  1199  	// Walk the entire table
  1200  	iter, err := txn.Get("allocs", "id")
  1201  	if err != nil {
  1202  		return nil, err
  1203  	}
  1204  
  1205  	ws.Add(iter.WatchCh())
  1206  
  1207  	return iter, nil
  1208  }
  1209  
  1210  // UpsertVaultAccessors is used to register a set of Vault Accessors
  1211  func (s *StateStore) UpsertVaultAccessor(index uint64, accessors []*structs.VaultAccessor) error {
  1212  	txn := s.db.Txn(true)
  1213  	defer txn.Abort()
  1214  
  1215  	for _, accessor := range accessors {
  1216  		// Set the create index
  1217  		accessor.CreateIndex = index
  1218  
  1219  		// Insert the accessor
  1220  		if err := txn.Insert("vault_accessors", accessor); err != nil {
  1221  			return fmt.Errorf("accessor insert failed: %v", err)
  1222  		}
  1223  	}
  1224  
  1225  	if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil {
  1226  		return fmt.Errorf("index update failed: %v", err)
  1227  	}
  1228  
  1229  	txn.Commit()
  1230  	return nil
  1231  }
  1232  
  1233  // DeleteVaultAccessors is used to delete a set of Vault Accessors
  1234  func (s *StateStore) DeleteVaultAccessors(index uint64, accessors []*structs.VaultAccessor) error {
  1235  	txn := s.db.Txn(true)
  1236  	defer txn.Abort()
  1237  
  1238  	// Lookup the accessor
  1239  	for _, accessor := range accessors {
  1240  		// Delete the accessor
  1241  		if err := txn.Delete("vault_accessors", accessor); err != nil {
  1242  			return fmt.Errorf("accessor delete failed: %v", err)
  1243  		}
  1244  	}
  1245  
  1246  	if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil {
  1247  		return fmt.Errorf("index update failed: %v", err)
  1248  	}
  1249  
  1250  	txn.Commit()
  1251  	return nil
  1252  }
  1253  
  1254  // VaultAccessor returns the given Vault accessor
  1255  func (s *StateStore) VaultAccessor(ws memdb.WatchSet, accessor string) (*structs.VaultAccessor, error) {
  1256  	txn := s.db.Txn(false)
  1257  
  1258  	watchCh, existing, err := txn.FirstWatch("vault_accessors", "id", accessor)
  1259  	if err != nil {
  1260  		return nil, fmt.Errorf("accessor lookup failed: %v", err)
  1261  	}
  1262  
  1263  	ws.Add(watchCh)
  1264  
  1265  	if existing != nil {
  1266  		return existing.(*structs.VaultAccessor), nil
  1267  	}
  1268  
  1269  	return nil, nil
  1270  }
  1271  
  1272  // VaultAccessors returns an iterator of Vault accessors.
  1273  func (s *StateStore) VaultAccessors(ws memdb.WatchSet) (memdb.ResultIterator, error) {
  1274  	txn := s.db.Txn(false)
  1275  
  1276  	iter, err := txn.Get("vault_accessors", "id")
  1277  	if err != nil {
  1278  		return nil, err
  1279  	}
  1280  
  1281  	ws.Add(iter.WatchCh())
  1282  
  1283  	return iter, nil
  1284  }
  1285  
  1286  // VaultAccessorsByAlloc returns all the Vault accessors by alloc id
  1287  func (s *StateStore) VaultAccessorsByAlloc(ws memdb.WatchSet, allocID string) ([]*structs.VaultAccessor, error) {
  1288  	txn := s.db.Txn(false)
  1289  
  1290  	// Get an iterator over the accessors
  1291  	iter, err := txn.Get("vault_accessors", "alloc_id", allocID)
  1292  	if err != nil {
  1293  		return nil, err
  1294  	}
  1295  
  1296  	ws.Add(iter.WatchCh())
  1297  
  1298  	var out []*structs.VaultAccessor
  1299  	for {
  1300  		raw := iter.Next()
  1301  		if raw == nil {
  1302  			break
  1303  		}
  1304  		out = append(out, raw.(*structs.VaultAccessor))
  1305  	}
  1306  	return out, nil
  1307  }
  1308  
  1309  // VaultAccessorsByNode returns all the Vault accessors by node id
  1310  func (s *StateStore) VaultAccessorsByNode(ws memdb.WatchSet, nodeID string) ([]*structs.VaultAccessor, error) {
  1311  	txn := s.db.Txn(false)
  1312  
  1313  	// Get an iterator over the accessors
  1314  	iter, err := txn.Get("vault_accessors", "node_id", nodeID)
  1315  	if err != nil {
  1316  		return nil, err
  1317  	}
  1318  
  1319  	ws.Add(iter.WatchCh())
  1320  
  1321  	var out []*structs.VaultAccessor
  1322  	for {
  1323  		raw := iter.Next()
  1324  		if raw == nil {
  1325  			break
  1326  		}
  1327  		out = append(out, raw.(*structs.VaultAccessor))
  1328  	}
  1329  	return out, nil
  1330  }
  1331  
  1332  // LastIndex returns the greatest index value for all indexes
  1333  func (s *StateStore) LatestIndex() (uint64, error) {
  1334  	indexes, err := s.Indexes()
  1335  	if err != nil {
  1336  		return 0, err
  1337  	}
  1338  
  1339  	var max uint64 = 0
  1340  	for {
  1341  		raw := indexes.Next()
  1342  		if raw == nil {
  1343  			break
  1344  		}
  1345  
  1346  		// Prepare the request struct
  1347  		idx := raw.(*IndexEntry)
  1348  
  1349  		// Determine the max
  1350  		if idx.Value > max {
  1351  			max = idx.Value
  1352  		}
  1353  	}
  1354  
  1355  	return max, nil
  1356  }
  1357  
  1358  // Index finds the matching index value
  1359  func (s *StateStore) Index(name string) (uint64, error) {
  1360  	txn := s.db.Txn(false)
  1361  
  1362  	// Lookup the first matching index
  1363  	out, err := txn.First("index", "id", name)
  1364  	if err != nil {
  1365  		return 0, err
  1366  	}
  1367  	if out == nil {
  1368  		return 0, nil
  1369  	}
  1370  	return out.(*IndexEntry).Value, nil
  1371  }
  1372  
  1373  // RemoveIndex is a helper method to remove an index for testing purposes
  1374  func (s *StateStore) RemoveIndex(name string) error {
  1375  	txn := s.db.Txn(true)
  1376  	defer txn.Abort()
  1377  
  1378  	if _, err := txn.DeleteAll("index", "id", name); err != nil {
  1379  		return err
  1380  	}
  1381  
  1382  	txn.Commit()
  1383  	return nil
  1384  }
  1385  
  1386  // Indexes returns an iterator over all the indexes
  1387  func (s *StateStore) Indexes() (memdb.ResultIterator, error) {
  1388  	txn := s.db.Txn(false)
  1389  
  1390  	// Walk the entire nodes table
  1391  	iter, err := txn.Get("index", "id")
  1392  	if err != nil {
  1393  		return nil, err
  1394  	}
  1395  	return iter, nil
  1396  }
  1397  
  1398  // ReconcileJobSummaries re-creates summaries for all jobs present in the state
  1399  // store
  1400  func (s *StateStore) ReconcileJobSummaries(index uint64) error {
  1401  	txn := s.db.Txn(true)
  1402  	defer txn.Abort()
  1403  
  1404  	// Get all the jobs
  1405  	iter, err := txn.Get("jobs", "id")
  1406  	if err != nil {
  1407  		return err
  1408  	}
  1409  	for {
  1410  		rawJob := iter.Next()
  1411  		if rawJob == nil {
  1412  			break
  1413  		}
  1414  		job := rawJob.(*structs.Job)
  1415  
  1416  		// Create a job summary for the job
  1417  		summary := &structs.JobSummary{
  1418  			JobID:   job.ID,
  1419  			Summary: make(map[string]structs.TaskGroupSummary),
  1420  		}
  1421  		for _, tg := range job.TaskGroups {
  1422  			summary.Summary[tg.Name] = structs.TaskGroupSummary{}
  1423  		}
  1424  
  1425  		// Find all the allocations for the jobs
  1426  		iterAllocs, err := txn.Get("allocs", "job", job.ID)
  1427  		if err != nil {
  1428  			return err
  1429  		}
  1430  
  1431  		// Calculate the summary for the job
  1432  		for {
  1433  			rawAlloc := iterAllocs.Next()
  1434  			if rawAlloc == nil {
  1435  				break
  1436  			}
  1437  			alloc := rawAlloc.(*structs.Allocation)
  1438  
  1439  			// Ignore the allocation if it doesn't belong to the currently
  1440  			// registered job. The allocation is checked because of issue #2304
  1441  			if alloc.Job == nil || alloc.Job.CreateIndex != job.CreateIndex {
  1442  				continue
  1443  			}
  1444  
  1445  			tg := summary.Summary[alloc.TaskGroup]
  1446  			switch alloc.ClientStatus {
  1447  			case structs.AllocClientStatusFailed:
  1448  				tg.Failed += 1
  1449  			case structs.AllocClientStatusLost:
  1450  				tg.Lost += 1
  1451  			case structs.AllocClientStatusComplete:
  1452  				tg.Complete += 1
  1453  			case structs.AllocClientStatusRunning:
  1454  				tg.Running += 1
  1455  			case structs.AllocClientStatusPending:
  1456  				tg.Starting += 1
  1457  			default:
  1458  				s.logger.Printf("[ERR] state_store: invalid client status: %v in allocation %q", alloc.ClientStatus, alloc.ID)
  1459  			}
  1460  			summary.Summary[alloc.TaskGroup] = tg
  1461  		}
  1462  
  1463  		// Set the create index of the summary same as the job's create index
  1464  		// and the modify index to the current index
  1465  		summary.CreateIndex = job.CreateIndex
  1466  		summary.ModifyIndex = index
  1467  
  1468  		// Insert the job summary
  1469  		if err := txn.Insert("job_summary", summary); err != nil {
  1470  			return fmt.Errorf("error inserting job summary: %v", err)
  1471  		}
  1472  	}
  1473  
  1474  	// Update the indexes table for job summary
  1475  	if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
  1476  		return fmt.Errorf("index update failed: %v", err)
  1477  	}
  1478  	txn.Commit()
  1479  	return nil
  1480  }
  1481  
  1482  // setJobStatuses is a helper for calling setJobStatus on multiple jobs by ID.
  1483  // It takes a map of job IDs to an optional forceStatus string. It returns an
  1484  // error if the job doesn't exist or setJobStatus fails.
  1485  func (s *StateStore) setJobStatuses(index uint64, txn *memdb.Txn,
  1486  	jobs map[string]string, evalDelete bool) error {
  1487  	for job, forceStatus := range jobs {
  1488  		existing, err := txn.First("jobs", "id", job)
  1489  		if err != nil {
  1490  			return fmt.Errorf("job lookup failed: %v", err)
  1491  		}
  1492  
  1493  		if existing == nil {
  1494  			continue
  1495  		}
  1496  
  1497  		if err := s.setJobStatus(index, txn, existing.(*structs.Job), evalDelete, forceStatus); err != nil {
  1498  			return err
  1499  		}
  1500  	}
  1501  
  1502  	return nil
  1503  }
  1504  
  1505  // setJobStatus sets the status of the job by looking up associated evaluations
  1506  // and allocations. evalDelete should be set to true if setJobStatus is being
  1507  // called because an evaluation is being deleted (potentially because of garbage
  1508  // collection). If forceStatus is non-empty, the job's status will be set to the
  1509  // passed status.
  1510  func (s *StateStore) setJobStatus(index uint64, txn *memdb.Txn,
  1511  	job *structs.Job, evalDelete bool, forceStatus string) error {
  1512  
  1513  	// Capture the current status so we can check if there is a change
  1514  	oldStatus := job.Status
  1515  	if index == job.CreateIndex {
  1516  		oldStatus = ""
  1517  	}
  1518  	newStatus := forceStatus
  1519  
  1520  	// If forceStatus is not set, compute the jobs status.
  1521  	if forceStatus == "" {
  1522  		var err error
  1523  		newStatus, err = s.getJobStatus(txn, job, evalDelete)
  1524  		if err != nil {
  1525  			return err
  1526  		}
  1527  	}
  1528  
  1529  	// Fast-path if nothing has changed.
  1530  	if oldStatus == newStatus {
  1531  		return nil
  1532  	}
  1533  
  1534  	// Copy and update the existing job
  1535  	updated := job.Copy()
  1536  	updated.Status = newStatus
  1537  	updated.ModifyIndex = index
  1538  
  1539  	// Insert the job
  1540  	if err := txn.Insert("jobs", updated); err != nil {
  1541  		return fmt.Errorf("job insert failed: %v", err)
  1542  	}
  1543  	if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil {
  1544  		return fmt.Errorf("index update failed: %v", err)
  1545  	}
  1546  
  1547  	// Update the children summary
  1548  	if updated.ParentID != "" {
  1549  		// Try to update the summary of the parent job summary
  1550  		summaryRaw, err := txn.First("job_summary", "id", updated.ParentID)
  1551  		if err != nil {
  1552  			return fmt.Errorf("unable to retrieve summary for parent job: %v", err)
  1553  		}
  1554  
  1555  		// Only continue if the summary exists. It could not exist if the parent
  1556  		// job was removed
  1557  		if summaryRaw != nil {
  1558  			existing := summaryRaw.(*structs.JobSummary)
  1559  			pSummary := existing.Copy()
  1560  			if pSummary.Children == nil {
  1561  				pSummary.Children = new(structs.JobChildrenSummary)
  1562  			}
  1563  
  1564  			// Determine the transistion and update the correct fields
  1565  			children := pSummary.Children
  1566  
  1567  			// Decrement old status
  1568  			if oldStatus != "" {
  1569  				switch oldStatus {
  1570  				case structs.JobStatusPending:
  1571  					children.Pending--
  1572  				case structs.JobStatusRunning:
  1573  					children.Running--
  1574  				case structs.JobStatusDead:
  1575  					children.Dead--
  1576  				default:
  1577  					return fmt.Errorf("unknown old job status %q", oldStatus)
  1578  				}
  1579  			}
  1580  
  1581  			// Increment new status
  1582  			switch newStatus {
  1583  			case structs.JobStatusPending:
  1584  				children.Pending++
  1585  			case structs.JobStatusRunning:
  1586  				children.Running++
  1587  			case structs.JobStatusDead:
  1588  				children.Dead++
  1589  			default:
  1590  				return fmt.Errorf("unknown new job status %q", newStatus)
  1591  			}
  1592  
  1593  			// Update the index
  1594  			pSummary.ModifyIndex = index
  1595  
  1596  			// Insert the summary
  1597  			if err := txn.Insert("job_summary", pSummary); err != nil {
  1598  				return fmt.Errorf("job summary insert failed: %v", err)
  1599  			}
  1600  			if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
  1601  				return fmt.Errorf("index update failed: %v", err)
  1602  			}
  1603  		}
  1604  	}
  1605  
  1606  	return nil
  1607  }
  1608  
  1609  func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete bool) (string, error) {
  1610  	allocs, err := txn.Get("allocs", "job", job.ID)
  1611  	if err != nil {
  1612  		return "", err
  1613  	}
  1614  
  1615  	// If there is a non-terminal allocation, the job is running.
  1616  	hasAlloc := false
  1617  	for alloc := allocs.Next(); alloc != nil; alloc = allocs.Next() {
  1618  		hasAlloc = true
  1619  		if !alloc.(*structs.Allocation).TerminalStatus() {
  1620  			return structs.JobStatusRunning, nil
  1621  		}
  1622  	}
  1623  
  1624  	evals, err := txn.Get("evals", "job_prefix", job.ID)
  1625  	if err != nil {
  1626  		return "", err
  1627  	}
  1628  
  1629  	hasEval := false
  1630  	for raw := evals.Next(); raw != nil; raw = evals.Next() {
  1631  		e := raw.(*structs.Evaluation)
  1632  
  1633  		// Filter non-exact matches
  1634  		if e.JobID != job.ID {
  1635  			continue
  1636  		}
  1637  
  1638  		hasEval = true
  1639  		if !e.TerminalStatus() {
  1640  			return structs.JobStatusPending, nil
  1641  		}
  1642  	}
  1643  
  1644  	// The job is dead if all the allocations and evals are terminal or if there
  1645  	// are no evals because of garbage collection.
  1646  	if evalDelete || hasEval || hasAlloc {
  1647  		return structs.JobStatusDead, nil
  1648  	}
  1649  
  1650  	// If there are no allocations or evaluations it is a new job. If the
  1651  	// job is periodic or is a parameterized job, we mark it as running as
  1652  	// it will never have an allocation/evaluation against it.
  1653  	if job.IsPeriodic() || job.IsParameterized() {
  1654  		return structs.JobStatusRunning, nil
  1655  	}
  1656  	return structs.JobStatusPending, nil
  1657  }
  1658  
  1659  // updateSummaryWithJob creates or updates job summaries when new jobs are
  1660  // upserted or existing ones are updated
  1661  func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job,
  1662  	txn *memdb.Txn) error {
  1663  
  1664  	// Update the job summary
  1665  	summaryRaw, err := txn.First("job_summary", "id", job.ID)
  1666  	if err != nil {
  1667  		return fmt.Errorf("job summary lookup failed: %v", err)
  1668  	}
  1669  
  1670  	// Get the summary or create if necessary
  1671  	var summary *structs.JobSummary
  1672  	hasSummaryChanged := false
  1673  	if summaryRaw != nil {
  1674  		summary = summaryRaw.(*structs.JobSummary).Copy()
  1675  	} else {
  1676  		summary = &structs.JobSummary{
  1677  			JobID:       job.ID,
  1678  			Summary:     make(map[string]structs.TaskGroupSummary),
  1679  			Children:    new(structs.JobChildrenSummary),
  1680  			CreateIndex: index,
  1681  		}
  1682  		hasSummaryChanged = true
  1683  	}
  1684  
  1685  	for _, tg := range job.TaskGroups {
  1686  		if _, ok := summary.Summary[tg.Name]; !ok {
  1687  			newSummary := structs.TaskGroupSummary{
  1688  				Complete: 0,
  1689  				Failed:   0,
  1690  				Running:  0,
  1691  				Starting: 0,
  1692  			}
  1693  			summary.Summary[tg.Name] = newSummary
  1694  			hasSummaryChanged = true
  1695  		}
  1696  	}
  1697  
  1698  	// The job summary has changed, so update the modify index.
  1699  	if hasSummaryChanged {
  1700  		summary.ModifyIndex = index
  1701  
  1702  		// Update the indexes table for job summary
  1703  		if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
  1704  			return fmt.Errorf("index update failed: %v", err)
  1705  		}
  1706  		if err := txn.Insert("job_summary", summary); err != nil {
  1707  			return err
  1708  		}
  1709  	}
  1710  
  1711  	return nil
  1712  }
  1713  
  1714  // updateSummaryWithAlloc updates the job summary when allocations are updated
  1715  // or inserted
  1716  func (s *StateStore) updateSummaryWithAlloc(index uint64, alloc *structs.Allocation,
  1717  	existingAlloc *structs.Allocation, txn *memdb.Txn) error {
  1718  
  1719  	// We don't have to update the summary if the job is missing
  1720  	if alloc.Job == nil {
  1721  		return nil
  1722  	}
  1723  
  1724  	summaryRaw, err := txn.First("job_summary", "id", alloc.JobID)
  1725  	if err != nil {
  1726  		return fmt.Errorf("unable to lookup job summary for job id %q: %v", alloc.JobID, err)
  1727  	}
  1728  
  1729  	if summaryRaw == nil {
  1730  		// Check if the job is de-registered
  1731  		rawJob, err := txn.First("jobs", "id", alloc.JobID)
  1732  		if err != nil {
  1733  			return fmt.Errorf("unable to query job: %v", err)
  1734  		}
  1735  
  1736  		// If the job is de-registered then we skip updating it's summary
  1737  		if rawJob == nil {
  1738  			return nil
  1739  		}
  1740  
  1741  		return fmt.Errorf("job summary for job %q is not present", alloc.JobID)
  1742  	}
  1743  
  1744  	// Get a copy of the existing summary
  1745  	jobSummary := summaryRaw.(*structs.JobSummary).Copy()
  1746  
  1747  	// Not updating the job summary because the allocation doesn't belong to the
  1748  	// currently registered job
  1749  	if jobSummary.CreateIndex != alloc.Job.CreateIndex {
  1750  		return nil
  1751  	}
  1752  
  1753  	tgSummary, ok := jobSummary.Summary[alloc.TaskGroup]
  1754  	if !ok {
  1755  		return fmt.Errorf("unable to find task group in the job summary: %v", alloc.TaskGroup)
  1756  	}
  1757  
  1758  	summaryChanged := false
  1759  	if existingAlloc == nil {
  1760  		switch alloc.DesiredStatus {
  1761  		case structs.AllocDesiredStatusStop, structs.AllocDesiredStatusEvict:
  1762  			s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v",
  1763  				alloc.ID, alloc.DesiredStatus)
  1764  		}
  1765  		switch alloc.ClientStatus {
  1766  		case structs.AllocClientStatusPending:
  1767  			tgSummary.Starting += 1
  1768  			if tgSummary.Queued > 0 {
  1769  				tgSummary.Queued -= 1
  1770  			}
  1771  			summaryChanged = true
  1772  		case structs.AllocClientStatusRunning, structs.AllocClientStatusFailed,
  1773  			structs.AllocClientStatusComplete:
  1774  			s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v",
  1775  				alloc.ID, alloc.ClientStatus)
  1776  		}
  1777  	} else if existingAlloc.ClientStatus != alloc.ClientStatus {
  1778  		// Incrementing the client of the bin of the current state
  1779  		switch alloc.ClientStatus {
  1780  		case structs.AllocClientStatusRunning:
  1781  			tgSummary.Running += 1
  1782  		case structs.AllocClientStatusFailed:
  1783  			tgSummary.Failed += 1
  1784  		case structs.AllocClientStatusPending:
  1785  			tgSummary.Starting += 1
  1786  		case structs.AllocClientStatusComplete:
  1787  			tgSummary.Complete += 1
  1788  		case structs.AllocClientStatusLost:
  1789  			tgSummary.Lost += 1
  1790  		}
  1791  
  1792  		// Decrementing the count of the bin of the last state
  1793  		switch existingAlloc.ClientStatus {
  1794  		case structs.AllocClientStatusRunning:
  1795  			tgSummary.Running -= 1
  1796  		case structs.AllocClientStatusPending:
  1797  			tgSummary.Starting -= 1
  1798  		case structs.AllocClientStatusLost:
  1799  			tgSummary.Lost -= 1
  1800  		case structs.AllocClientStatusFailed, structs.AllocClientStatusComplete:
  1801  		default:
  1802  			s.logger.Printf("[ERR] state_store: invalid old state of allocation with id: %v, and state: %v",
  1803  				existingAlloc.ID, existingAlloc.ClientStatus)
  1804  		}
  1805  		summaryChanged = true
  1806  	}
  1807  	jobSummary.Summary[alloc.TaskGroup] = tgSummary
  1808  
  1809  	if summaryChanged {
  1810  		jobSummary.ModifyIndex = index
  1811  
  1812  		// Update the indexes table for job summary
  1813  		if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil {
  1814  			return fmt.Errorf("index update failed: %v", err)
  1815  		}
  1816  
  1817  		if err := txn.Insert("job_summary", jobSummary); err != nil {
  1818  			return fmt.Errorf("updating job summary failed: %v", err)
  1819  		}
  1820  	}
  1821  
  1822  	return nil
  1823  }
  1824  
  1825  // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups
  1826  func (s *StateStore) addEphemeralDiskToTaskGroups(job *structs.Job) {
  1827  	for _, tg := range job.TaskGroups {
  1828  		var diskMB int
  1829  		for _, task := range tg.Tasks {
  1830  			if task.Resources != nil {
  1831  				diskMB += task.Resources.DiskMB
  1832  				task.Resources.DiskMB = 0
  1833  			}
  1834  		}
  1835  		if tg.EphemeralDisk != nil {
  1836  			continue
  1837  		}
  1838  		tg.EphemeralDisk = &structs.EphemeralDisk{
  1839  			SizeMB: diskMB,
  1840  		}
  1841  	}
  1842  }
  1843  
  1844  // StateSnapshot is used to provide a point-in-time snapshot
  1845  type StateSnapshot struct {
  1846  	StateStore
  1847  }
  1848  
  1849  // StateRestore is used to optimize the performance when
  1850  // restoring state by only using a single large transaction
  1851  // instead of thousands of sub transactions
  1852  type StateRestore struct {
  1853  	txn *memdb.Txn
  1854  }
  1855  
  1856  // Abort is used to abort the restore operation
  1857  func (s *StateRestore) Abort() {
  1858  	s.txn.Abort()
  1859  }
  1860  
  1861  // Commit is used to commit the restore operation
  1862  func (s *StateRestore) Commit() {
  1863  	s.txn.Commit()
  1864  }
  1865  
  1866  // NodeRestore is used to restore a node
  1867  func (r *StateRestore) NodeRestore(node *structs.Node) error {
  1868  	if err := r.txn.Insert("nodes", node); err != nil {
  1869  		return fmt.Errorf("node insert failed: %v", err)
  1870  	}
  1871  	return nil
  1872  }
  1873  
  1874  // JobRestore is used to restore a job
  1875  func (r *StateRestore) JobRestore(job *structs.Job) error {
  1876  	// Create the EphemeralDisk if it's nil by adding up DiskMB from task resources.
  1877  	// COMPAT 0.4.1 -> 0.5
  1878  	r.addEphemeralDiskToTaskGroups(job)
  1879  
  1880  	if err := r.txn.Insert("jobs", job); err != nil {
  1881  		return fmt.Errorf("job insert failed: %v", err)
  1882  	}
  1883  	return nil
  1884  }
  1885  
  1886  // EvalRestore is used to restore an evaluation
  1887  func (r *StateRestore) EvalRestore(eval *structs.Evaluation) error {
  1888  	if err := r.txn.Insert("evals", eval); err != nil {
  1889  		return fmt.Errorf("eval insert failed: %v", err)
  1890  	}
  1891  	return nil
  1892  }
  1893  
  1894  // AllocRestore is used to restore an allocation
  1895  func (r *StateRestore) AllocRestore(alloc *structs.Allocation) error {
  1896  	// Set the shared resources if it's not present
  1897  	// COMPAT 0.4.1 -> 0.5
  1898  	if alloc.SharedResources == nil {
  1899  		alloc.SharedResources = &structs.Resources{
  1900  			DiskMB: alloc.Resources.DiskMB,
  1901  		}
  1902  	}
  1903  
  1904  	// Create the EphemeralDisk if it's nil by adding up DiskMB from task resources.
  1905  	if alloc.Job != nil {
  1906  		r.addEphemeralDiskToTaskGroups(alloc.Job)
  1907  	}
  1908  
  1909  	if err := r.txn.Insert("allocs", alloc); err != nil {
  1910  		return fmt.Errorf("alloc insert failed: %v", err)
  1911  	}
  1912  	return nil
  1913  }
  1914  
  1915  // IndexRestore is used to restore an index
  1916  func (r *StateRestore) IndexRestore(idx *IndexEntry) error {
  1917  	if err := r.txn.Insert("index", idx); err != nil {
  1918  		return fmt.Errorf("index insert failed: %v", err)
  1919  	}
  1920  	return nil
  1921  }
  1922  
  1923  // PeriodicLaunchRestore is used to restore a periodic launch.
  1924  func (r *StateRestore) PeriodicLaunchRestore(launch *structs.PeriodicLaunch) error {
  1925  	if err := r.txn.Insert("periodic_launch", launch); err != nil {
  1926  		return fmt.Errorf("periodic launch insert failed: %v", err)
  1927  	}
  1928  	return nil
  1929  }
  1930  
  1931  // JobSummaryRestore is used to restore a job summary
  1932  func (r *StateRestore) JobSummaryRestore(jobSummary *structs.JobSummary) error {
  1933  	if err := r.txn.Insert("job_summary", jobSummary); err != nil {
  1934  		return fmt.Errorf("job summary insert failed: %v", err)
  1935  	}
  1936  	return nil
  1937  }
  1938  
  1939  // VaultAccessorRestore is used to restore a vault accessor
  1940  func (r *StateRestore) VaultAccessorRestore(accessor *structs.VaultAccessor) error {
  1941  	if err := r.txn.Insert("vault_accessors", accessor); err != nil {
  1942  		return fmt.Errorf("vault accessor insert failed: %v", err)
  1943  	}
  1944  	return nil
  1945  }
  1946  
  1947  // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups
  1948  func (r *StateRestore) addEphemeralDiskToTaskGroups(job *structs.Job) {
  1949  	for _, tg := range job.TaskGroups {
  1950  		if tg.EphemeralDisk != nil {
  1951  			continue
  1952  		}
  1953  		var sizeMB int
  1954  		for _, task := range tg.Tasks {
  1955  			if task.Resources != nil {
  1956  				sizeMB += task.Resources.DiskMB
  1957  				task.Resources.DiskMB = 0
  1958  			}
  1959  		}
  1960  		tg.EphemeralDisk = &structs.EphemeralDisk{
  1961  			SizeMB: sizeMB,
  1962  		}
  1963  	}
  1964  }