github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/state/db_bolt.go (about)

     1  package state
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	hclog "github.com/hashicorp/go-hclog"
    11  	trstate "github.com/hashicorp/nomad/client/allocrunner/taskrunner/state"
    12  	dmstate "github.com/hashicorp/nomad/client/devicemanager/state"
    13  	"github.com/hashicorp/nomad/client/dynamicplugins"
    14  	driverstate "github.com/hashicorp/nomad/client/pluginmanager/drivermanager/state"
    15  	"github.com/hashicorp/nomad/client/serviceregistration/checks"
    16  	"github.com/hashicorp/nomad/helper/boltdd"
    17  	"github.com/hashicorp/nomad/nomad/structs"
    18  	"go.etcd.io/bbolt"
    19  )
    20  
    21  /*
    22  The client has a boltDB backed state store. The schema as of 0.9 looks as follows:
    23  
    24  meta/
    25  |--> version -> '2' (not msgpack encoded)
    26  |--> upgraded -> time.Now().Format(timeRFC3339)
    27  allocations/
    28  |--> <alloc-id>/
    29     |--> alloc          -> allocEntry{*structs.Allocation}
    30  	 |--> deploy_status  -> deployStatusEntry{*structs.AllocDeploymentStatus}
    31  	 |--> network_status -> networkStatusEntry{*structs.AllocNetworkStatus}
    32     |--> task-<name>/
    33        |--> local_state -> *trstate.LocalState # Local-only state
    34        |--> task_state  -> *structs.TaskState  # Syncs to servers
    35     |--> checks/
    36        |--> check-<id> -> *structs.CheckState # Syncs to servers
    37  
    38  devicemanager/
    39  |--> plugin_state -> *dmstate.PluginState
    40  
    41  drivermanager/
    42  |--> plugin_state -> *driverstate.PluginState
    43  
    44  dynamicplugins/
    45  |--> registry_state -> *dynamicplugins.RegistryState
    46  */
    47  
    48  var (
    49  	// metaBucketName is the name of the metadata bucket
    50  	metaBucketName = []byte("meta")
    51  
    52  	// metaVersionKey is the key the state schema version is stored under.
    53  	metaVersionKey = []byte("version")
    54  
    55  	// metaVersion is the value of the state schema version to detect when
    56  	// an upgrade is needed. It skips the usual boltdd/msgpack backend to
    57  	// be as portable and futureproof as possible.
    58  	metaVersion = []byte{'3'}
    59  
    60  	// metaUpgradedKey is the key that stores the timestamp of the last
    61  	// time the schema was upgraded.
    62  	metaUpgradedKey = []byte("upgraded")
    63  
    64  	// allocationsBucketName is the bucket name containing all allocation related
    65  	// data
    66  	allocationsBucketName = []byte("allocations")
    67  
    68  	// allocKey is the key Allocations are stored under encapsulated in
    69  	// allocEntry structs.
    70  	allocKey = []byte("alloc")
    71  
    72  	// allocDeployStatusKey is the key *structs.AllocDeploymentStatus is
    73  	// stored under.
    74  	allocDeployStatusKey = []byte("deploy_status")
    75  
    76  	// allocNetworkStatusKey is the key *structs.AllocNetworkStatus is
    77  	// stored under
    78  	allocNetworkStatusKey = []byte("network_status")
    79  
    80  	// checkResultsBucket is the bucket name in which check query results are stored
    81  	checkResultsBucket = []byte("check_results")
    82  
    83  	// allocations -> $allocid -> task-$taskname -> the keys below
    84  	taskLocalStateKey = []byte("local_state")
    85  	taskStateKey      = []byte("task_state")
    86  
    87  	// devManagerBucket is the bucket name containing all device manager related
    88  	// data
    89  	devManagerBucket = []byte("devicemanager")
    90  
    91  	// driverManagerBucket is the bucket name containing all driver manager
    92  	// related data
    93  	driverManagerBucket = []byte("drivermanager")
    94  
    95  	// managerPluginStateKey is the key by which plugin manager plugin state is
    96  	// stored at
    97  	managerPluginStateKey = []byte("plugin_state")
    98  
    99  	// dynamicPluginBucketName is the bucket name containing all dynamic plugin
   100  	// registry data. each dynamic plugin registry will have its own subbucket.
   101  	dynamicPluginBucketName = []byte("dynamicplugins")
   102  
   103  	// registryStateKey is the key at which dynamic plugin registry state is stored
   104  	registryStateKey = []byte("registry_state")
   105  )
   106  
   107  // taskBucketName returns the bucket name for the given task name.
   108  func taskBucketName(taskName string) []byte {
   109  	return []byte("task-" + taskName)
   110  }
   111  
   112  // NewStateDBFunc creates a StateDB given a state directory.
   113  type NewStateDBFunc func(logger hclog.Logger, stateDir string) (StateDB, error)
   114  
   115  // GetStateDBFactory returns a func for creating a StateDB
   116  func GetStateDBFactory(devMode bool) NewStateDBFunc {
   117  	// Return a noop state db implementation when in debug mode
   118  	if devMode {
   119  		return func(hclog.Logger, string) (StateDB, error) {
   120  			return NoopDB{}, nil
   121  		}
   122  	}
   123  
   124  	return NewBoltStateDB
   125  }
   126  
   127  // BoltStateDB persists and restores Nomad client state in a boltdb. All
   128  // methods are safe for concurrent access.
   129  type BoltStateDB struct {
   130  	stateDir string
   131  	db       *boltdd.DB
   132  	logger   hclog.Logger
   133  }
   134  
   135  // NewBoltStateDB creates or opens an existing boltdb state file or returns an
   136  // error.
   137  func NewBoltStateDB(logger hclog.Logger, stateDir string) (StateDB, error) {
   138  	fn := filepath.Join(stateDir, "state.db")
   139  
   140  	// Check to see if the DB already exists
   141  	fi, err := os.Stat(fn)
   142  	if err != nil && !os.IsNotExist(err) {
   143  		return nil, err
   144  	}
   145  	firstRun := fi == nil
   146  
   147  	// Timeout to force failure when accessing a data dir that is already in use
   148  	timeout := &bbolt.Options{Timeout: 5 * time.Second}
   149  
   150  	// Create or open the boltdb state database
   151  	db, err := boltdd.Open(fn, 0600, timeout)
   152  	if err == bbolt.ErrTimeout {
   153  		return nil, fmt.Errorf("timed out while opening database, is another Nomad process accessing data_dir %s?", stateDir)
   154  	} else if err != nil {
   155  		return nil, fmt.Errorf("failed to create state database: %v", err)
   156  	}
   157  
   158  	sdb := &BoltStateDB{
   159  		stateDir: stateDir,
   160  		db:       db,
   161  		logger:   logger,
   162  	}
   163  
   164  	// If db did not already exist, initialize metadata fields
   165  	if firstRun {
   166  		if err := sdb.init(); err != nil {
   167  			return nil, err
   168  		}
   169  	}
   170  
   171  	return sdb, nil
   172  }
   173  
   174  func (s *BoltStateDB) Name() string {
   175  	return "boltdb"
   176  }
   177  
   178  // GetAllAllocations gets all allocations persisted by this client and returns
   179  // a map of alloc ids to errors for any allocations that could not be restored.
   180  //
   181  // If a fatal error was encountered it will be returned and the other two
   182  // values will be nil.
   183  func (s *BoltStateDB) GetAllAllocations() ([]*structs.Allocation, map[string]error, error) {
   184  	var allocs []*structs.Allocation
   185  	var errs map[string]error
   186  
   187  	err := s.db.View(func(tx *boltdd.Tx) error {
   188  		allocs, errs = s.getAllAllocations(tx)
   189  		return nil
   190  	})
   191  
   192  	// db.View itself may return an error, so still check
   193  	if err != nil {
   194  		return nil, nil, err
   195  	}
   196  
   197  	return allocs, errs, nil
   198  }
   199  
   200  // allocEntry wraps values in the Allocations buckets
   201  type allocEntry struct {
   202  	Alloc *structs.Allocation
   203  }
   204  
   205  func (s *BoltStateDB) getAllAllocations(tx *boltdd.Tx) ([]*structs.Allocation, map[string]error) {
   206  	allocs := make([]*structs.Allocation, 0, 10)
   207  	errs := map[string]error{}
   208  
   209  	allocationsBkt := tx.Bucket(allocationsBucketName)
   210  	if allocationsBkt == nil {
   211  		// No allocs
   212  		return allocs, errs
   213  	}
   214  
   215  	// Create a cursor for iteration.
   216  	c := allocationsBkt.BoltBucket().Cursor()
   217  
   218  	// Iterate over all the allocation buckets
   219  	for k, _ := c.First(); k != nil; k, _ = c.Next() {
   220  		allocID := string(k)
   221  		allocBkt := allocationsBkt.Bucket(k)
   222  		if allocBkt == nil {
   223  			errs[allocID] = fmt.Errorf("missing alloc bucket")
   224  			continue
   225  		}
   226  
   227  		var ae allocEntry
   228  		if err := allocBkt.Get(allocKey, &ae); err != nil {
   229  			errs[allocID] = fmt.Errorf("failed to decode alloc: %v", err)
   230  			continue
   231  		}
   232  
   233  		// Handle upgrade path
   234  		ae.Alloc.Canonicalize()
   235  		ae.Alloc.Job.Canonicalize()
   236  
   237  		allocs = append(allocs, ae.Alloc)
   238  	}
   239  
   240  	return allocs, errs
   241  }
   242  
   243  // PutAllocation stores an allocation or returns an error.
   244  func (s *BoltStateDB) PutAllocation(alloc *structs.Allocation, opts ...WriteOption) error {
   245  	return s.updateWithOptions(opts, func(tx *boltdd.Tx) error {
   246  		// Retrieve the root allocations bucket
   247  		allocsBkt, err := tx.CreateBucketIfNotExists(allocationsBucketName)
   248  		if err != nil {
   249  			return err
   250  		}
   251  
   252  		// Retrieve the specific allocations bucket
   253  		key := []byte(alloc.ID)
   254  		allocBkt, err := allocsBkt.CreateBucketIfNotExists(key)
   255  		if err != nil {
   256  			return err
   257  		}
   258  
   259  		allocState := allocEntry{
   260  			Alloc: alloc,
   261  		}
   262  		return allocBkt.Put(allocKey, &allocState)
   263  	})
   264  }
   265  
   266  // deployStatusEntry wraps values for DeploymentStatus keys.
   267  type deployStatusEntry struct {
   268  	DeploymentStatus *structs.AllocDeploymentStatus
   269  }
   270  
   271  // PutDeploymentStatus stores an allocation's DeploymentStatus or returns an
   272  // error.
   273  func (s *BoltStateDB) PutDeploymentStatus(allocID string, ds *structs.AllocDeploymentStatus) error {
   274  	return s.db.Update(func(tx *boltdd.Tx) error {
   275  		return putDeploymentStatusImpl(tx, allocID, ds)
   276  	})
   277  }
   278  
   279  func putDeploymentStatusImpl(tx *boltdd.Tx, allocID string, ds *structs.AllocDeploymentStatus) error {
   280  	allocBkt, err := getAllocationBucket(tx, allocID)
   281  	if err != nil {
   282  		return err
   283  	}
   284  
   285  	entry := deployStatusEntry{
   286  		DeploymentStatus: ds,
   287  	}
   288  	return allocBkt.Put(allocDeployStatusKey, &entry)
   289  }
   290  
   291  // GetDeploymentStatus retrieves an allocation's DeploymentStatus or returns an
   292  // error.
   293  func (s *BoltStateDB) GetDeploymentStatus(allocID string) (*structs.AllocDeploymentStatus, error) {
   294  	var entry deployStatusEntry
   295  
   296  	err := s.db.View(func(tx *boltdd.Tx) error {
   297  		allAllocsBkt := tx.Bucket(allocationsBucketName)
   298  		if allAllocsBkt == nil {
   299  			// No state, return
   300  			return nil
   301  		}
   302  
   303  		allocBkt := allAllocsBkt.Bucket([]byte(allocID))
   304  		if allocBkt == nil {
   305  			// No state for alloc, return
   306  			return nil
   307  		}
   308  
   309  		return allocBkt.Get(allocDeployStatusKey, &entry)
   310  	})
   311  
   312  	// It's valid for this field to be nil/missing
   313  	if boltdd.IsErrNotFound(err) {
   314  		return nil, nil
   315  	}
   316  
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  
   321  	return entry.DeploymentStatus, nil
   322  }
   323  
   324  // networkStatusEntry wraps values for NetworkStatus keys.
   325  type networkStatusEntry struct {
   326  	NetworkStatus *structs.AllocNetworkStatus
   327  }
   328  
   329  // PutNetworkStatus stores an allocation's DeploymentStatus or returns an
   330  // error.
   331  func (s *BoltStateDB) PutNetworkStatus(allocID string, ds *structs.AllocNetworkStatus, opts ...WriteOption) error {
   332  	return s.updateWithOptions(opts, func(tx *boltdd.Tx) error {
   333  		return putNetworkStatusImpl(tx, allocID, ds)
   334  	})
   335  }
   336  
   337  func putNetworkStatusImpl(tx *boltdd.Tx, allocID string, ds *structs.AllocNetworkStatus) error {
   338  	allocBkt, err := getAllocationBucket(tx, allocID)
   339  	if err != nil {
   340  		return err
   341  	}
   342  
   343  	entry := networkStatusEntry{
   344  		NetworkStatus: ds,
   345  	}
   346  	return allocBkt.Put(allocNetworkStatusKey, &entry)
   347  }
   348  
   349  // GetNetworkStatus retrieves an allocation's NetworkStatus or returns an
   350  // error.
   351  func (s *BoltStateDB) GetNetworkStatus(allocID string) (*structs.AllocNetworkStatus, error) {
   352  	var entry networkStatusEntry
   353  
   354  	err := s.db.View(func(tx *boltdd.Tx) error {
   355  		allAllocsBkt := tx.Bucket(allocationsBucketName)
   356  		if allAllocsBkt == nil {
   357  			// No state, return
   358  			return nil
   359  		}
   360  
   361  		allocBkt := allAllocsBkt.Bucket([]byte(allocID))
   362  		if allocBkt == nil {
   363  			// No state for alloc, return
   364  			return nil
   365  		}
   366  
   367  		return allocBkt.Get(allocNetworkStatusKey, &entry)
   368  	})
   369  
   370  	// It's valid for this field to be nil/missing
   371  	if boltdd.IsErrNotFound(err) {
   372  		return nil, nil
   373  	}
   374  
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	return entry.NetworkStatus, nil
   380  }
   381  
   382  // GetTaskRunnerState returns the LocalState and TaskState for a
   383  // TaskRunner. LocalState or TaskState will be nil if they do not exist.
   384  //
   385  // If an error is encountered both LocalState and TaskState will be nil.
   386  func (s *BoltStateDB) GetTaskRunnerState(allocID, taskName string) (*trstate.LocalState, *structs.TaskState, error) {
   387  	var ls *trstate.LocalState
   388  	var ts *structs.TaskState
   389  
   390  	err := s.db.View(func(tx *boltdd.Tx) error {
   391  		allAllocsBkt := tx.Bucket(allocationsBucketName)
   392  		if allAllocsBkt == nil {
   393  			// No state, return
   394  			return nil
   395  		}
   396  
   397  		allocBkt := allAllocsBkt.Bucket([]byte(allocID))
   398  		if allocBkt == nil {
   399  			// No state for alloc, return
   400  			return nil
   401  		}
   402  
   403  		taskBkt := allocBkt.Bucket(taskBucketName(taskName))
   404  		if taskBkt == nil {
   405  			// No state for task, return
   406  			return nil
   407  		}
   408  
   409  		// Restore Local State if it exists
   410  		ls = &trstate.LocalState{}
   411  		if err := taskBkt.Get(taskLocalStateKey, ls); err != nil {
   412  			if !boltdd.IsErrNotFound(err) {
   413  				return fmt.Errorf("failed to read local task runner state: %v", err)
   414  			}
   415  
   416  			// Key not found, reset ls to nil
   417  			ls = nil
   418  		}
   419  
   420  		// Restore Task State if it exists
   421  		ts = &structs.TaskState{}
   422  		if err := taskBkt.Get(taskStateKey, ts); err != nil {
   423  			if !boltdd.IsErrNotFound(err) {
   424  				return fmt.Errorf("failed to read task state: %v", err)
   425  			}
   426  
   427  			// Key not found, reset ts to nil
   428  			ts = nil
   429  		}
   430  
   431  		return nil
   432  	})
   433  
   434  	if err != nil {
   435  		return nil, nil, err
   436  	}
   437  
   438  	return ls, ts, nil
   439  }
   440  
   441  // PutTaskRunnerLocalState stores TaskRunner's LocalState or returns an error.
   442  func (s *BoltStateDB) PutTaskRunnerLocalState(allocID, taskName string, val *trstate.LocalState) error {
   443  	return s.db.Update(func(tx *boltdd.Tx) error {
   444  		return putTaskRunnerLocalStateImpl(tx, allocID, taskName, val)
   445  	})
   446  }
   447  
   448  // putTaskRunnerLocalStateImpl stores TaskRunner's LocalState in an ongoing
   449  // transaction or returns an error.
   450  func putTaskRunnerLocalStateImpl(tx *boltdd.Tx, allocID, taskName string, val *trstate.LocalState) error {
   451  	taskBkt, err := getTaskBucket(tx, allocID, taskName)
   452  	if err != nil {
   453  		return fmt.Errorf("failed to retrieve allocation bucket: %v", err)
   454  	}
   455  
   456  	if err := taskBkt.Put(taskLocalStateKey, val); err != nil {
   457  		return fmt.Errorf("failed to write task_runner state: %v", err)
   458  	}
   459  
   460  	return nil
   461  }
   462  
   463  // PutTaskState stores a task's state or returns an error.
   464  func (s *BoltStateDB) PutTaskState(allocID, taskName string, state *structs.TaskState) error {
   465  	return s.db.Update(func(tx *boltdd.Tx) error {
   466  		return putTaskStateImpl(tx, allocID, taskName, state)
   467  	})
   468  }
   469  
   470  // putTaskStateImpl stores a task's state in an ongoing transaction or returns
   471  // an error.
   472  func putTaskStateImpl(tx *boltdd.Tx, allocID, taskName string, state *structs.TaskState) error {
   473  	taskBkt, err := getTaskBucket(tx, allocID, taskName)
   474  	if err != nil {
   475  		return fmt.Errorf("failed to retrieve allocation bucket: %v", err)
   476  	}
   477  
   478  	return taskBkt.Put(taskStateKey, state)
   479  }
   480  
   481  // DeleteTaskBucket is used to delete a task bucket if it exists.
   482  func (s *BoltStateDB) DeleteTaskBucket(allocID, taskName string) error {
   483  	return s.db.Update(func(tx *boltdd.Tx) error {
   484  		// Retrieve the root allocations bucket
   485  		allocations := tx.Bucket(allocationsBucketName)
   486  		if allocations == nil {
   487  			return nil
   488  		}
   489  
   490  		// Retrieve the specific allocations bucket
   491  		alloc := allocations.Bucket([]byte(allocID))
   492  		if alloc == nil {
   493  			return nil
   494  		}
   495  
   496  		// Check if the bucket exists
   497  		key := taskBucketName(taskName)
   498  		return alloc.DeleteBucket(key)
   499  	})
   500  }
   501  
   502  // DeleteAllocationBucket is used to delete an allocation bucket if it exists.
   503  func (s *BoltStateDB) DeleteAllocationBucket(allocID string, opts ...WriteOption) error {
   504  	return s.updateWithOptions(opts, func(tx *boltdd.Tx) error {
   505  		// Retrieve the root allocations bucket
   506  		allocations := tx.Bucket(allocationsBucketName)
   507  		if allocations == nil {
   508  			return nil
   509  		}
   510  
   511  		key := []byte(allocID)
   512  		return allocations.DeleteBucket(key)
   513  	})
   514  }
   515  
   516  // Close releases all database resources and unlocks the database file on disk.
   517  // All transactions must be closed before closing the database.
   518  func (s *BoltStateDB) Close() error {
   519  	return s.db.Close()
   520  }
   521  
   522  // getAllocationBucket returns the bucket used to persist state about a
   523  // particular allocation. If the root allocation bucket or the specific
   524  // allocation bucket doesn't exist, it will be created as long as the
   525  // transaction is writable.
   526  func getAllocationBucket(tx *boltdd.Tx, allocID string) (*boltdd.Bucket, error) {
   527  	var err error
   528  	w := tx.Writable()
   529  
   530  	// Retrieve the root allocations bucket
   531  	allocations := tx.Bucket(allocationsBucketName)
   532  	if allocations == nil {
   533  		if !w {
   534  			return nil, fmt.Errorf("Allocations bucket doesn't exist and transaction is not writable")
   535  		}
   536  
   537  		allocations, err = tx.CreateBucketIfNotExists(allocationsBucketName)
   538  		if err != nil {
   539  			return nil, err
   540  		}
   541  	}
   542  
   543  	// Retrieve the specific allocations bucket
   544  	key := []byte(allocID)
   545  	alloc := allocations.Bucket(key)
   546  	if alloc == nil {
   547  		if !w {
   548  			return nil, fmt.Errorf("Allocation bucket doesn't exist and transaction is not writable")
   549  		}
   550  
   551  		alloc, err = allocations.CreateBucket(key)
   552  		if err != nil {
   553  			return nil, err
   554  		}
   555  	}
   556  
   557  	return alloc, nil
   558  }
   559  
   560  // getTaskBucket returns the bucket used to persist state about a
   561  // particular task. If the root allocation bucket, the specific
   562  // allocation or task bucket doesn't exist, they will be created as long as the
   563  // transaction is writable.
   564  func getTaskBucket(tx *boltdd.Tx, allocID, taskName string) (*boltdd.Bucket, error) {
   565  	alloc, err := getAllocationBucket(tx, allocID)
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  
   570  	// Retrieve the specific task bucket
   571  	w := tx.Writable()
   572  	key := taskBucketName(taskName)
   573  	task := alloc.Bucket(key)
   574  	if task == nil {
   575  		if !w {
   576  			return nil, fmt.Errorf("Task bucket doesn't exist and transaction is not writable")
   577  		}
   578  
   579  		task, err = alloc.CreateBucket(key)
   580  		if err != nil {
   581  			return nil, err
   582  		}
   583  	}
   584  
   585  	return task, nil
   586  }
   587  
   588  // PutDevicePluginState stores the device manager's plugin state or returns an
   589  // error.
   590  func (s *BoltStateDB) PutDevicePluginState(ps *dmstate.PluginState) error {
   591  	return s.db.Update(func(tx *boltdd.Tx) error {
   592  		// Retrieve the root device manager bucket
   593  		devBkt, err := tx.CreateBucketIfNotExists(devManagerBucket)
   594  		if err != nil {
   595  			return err
   596  		}
   597  
   598  		return devBkt.Put(managerPluginStateKey, ps)
   599  	})
   600  }
   601  
   602  // GetDevicePluginState stores the device manager's plugin state or returns an
   603  // error.
   604  func (s *BoltStateDB) GetDevicePluginState() (*dmstate.PluginState, error) {
   605  	var ps *dmstate.PluginState
   606  
   607  	err := s.db.View(func(tx *boltdd.Tx) error {
   608  		devBkt := tx.Bucket(devManagerBucket)
   609  		if devBkt == nil {
   610  			// No state, return
   611  			return nil
   612  		}
   613  
   614  		// Restore Plugin State if it exists
   615  		ps = &dmstate.PluginState{}
   616  		if err := devBkt.Get(managerPluginStateKey, ps); err != nil {
   617  			if !boltdd.IsErrNotFound(err) {
   618  				return fmt.Errorf("failed to read device manager plugin state: %v", err)
   619  			}
   620  
   621  			// Key not found, reset ps to nil
   622  			ps = nil
   623  		}
   624  
   625  		return nil
   626  	})
   627  
   628  	if err != nil {
   629  		return nil, err
   630  	}
   631  
   632  	return ps, nil
   633  }
   634  
   635  // PutDriverPluginState stores the driver manager's plugin state or returns an
   636  // error.
   637  func (s *BoltStateDB) PutDriverPluginState(ps *driverstate.PluginState) error {
   638  	return s.db.Update(func(tx *boltdd.Tx) error {
   639  		// Retrieve the root driver manager bucket
   640  		driverBkt, err := tx.CreateBucketIfNotExists(driverManagerBucket)
   641  		if err != nil {
   642  			return err
   643  		}
   644  
   645  		return driverBkt.Put(managerPluginStateKey, ps)
   646  	})
   647  }
   648  
   649  // GetDriverPluginState stores the driver manager's plugin state or returns an
   650  // error.
   651  func (s *BoltStateDB) GetDriverPluginState() (*driverstate.PluginState, error) {
   652  	var ps *driverstate.PluginState
   653  
   654  	err := s.db.View(func(tx *boltdd.Tx) error {
   655  		driverBkt := tx.Bucket(driverManagerBucket)
   656  		if driverBkt == nil {
   657  			// No state, return
   658  			return nil
   659  		}
   660  
   661  		// Restore Plugin State if it exists
   662  		ps = &driverstate.PluginState{}
   663  		if err := driverBkt.Get(managerPluginStateKey, ps); err != nil {
   664  			if !boltdd.IsErrNotFound(err) {
   665  				return fmt.Errorf("failed to read driver manager plugin state: %v", err)
   666  			}
   667  
   668  			// Key not found, reset ps to nil
   669  			ps = nil
   670  		}
   671  
   672  		return nil
   673  	})
   674  
   675  	if err != nil {
   676  		return nil, err
   677  	}
   678  
   679  	return ps, nil
   680  }
   681  
   682  // PutDynamicPluginRegistryState stores the dynamic plugin registry's
   683  // state or returns an error.
   684  func (s *BoltStateDB) PutDynamicPluginRegistryState(ps *dynamicplugins.RegistryState) error {
   685  	return s.db.Update(func(tx *boltdd.Tx) error {
   686  		// Retrieve the root dynamic plugin manager bucket
   687  		dynamicBkt, err := tx.CreateBucketIfNotExists(dynamicPluginBucketName)
   688  		if err != nil {
   689  			return err
   690  		}
   691  		return dynamicBkt.Put(registryStateKey, ps)
   692  	})
   693  }
   694  
   695  // GetDynamicPluginRegistryState stores the dynamic plugin registry's
   696  // registry state or returns an error.
   697  func (s *BoltStateDB) GetDynamicPluginRegistryState() (*dynamicplugins.RegistryState, error) {
   698  	var ps *dynamicplugins.RegistryState
   699  
   700  	err := s.db.View(func(tx *boltdd.Tx) error {
   701  		dynamicBkt := tx.Bucket(dynamicPluginBucketName)
   702  		if dynamicBkt == nil {
   703  			// No state, return
   704  			return nil
   705  		}
   706  
   707  		// Restore Plugin State if it exists
   708  		ps = &dynamicplugins.RegistryState{}
   709  		if err := dynamicBkt.Get(registryStateKey, ps); err != nil {
   710  			if !boltdd.IsErrNotFound(err) {
   711  				return fmt.Errorf("failed to read dynamic plugin registry state: %v", err)
   712  			}
   713  
   714  			// Key not found, reset ps to nil
   715  			ps = nil
   716  		}
   717  
   718  		return nil
   719  	})
   720  
   721  	if err != nil {
   722  		return nil, err
   723  	}
   724  
   725  	return ps, nil
   726  }
   727  
   728  func keyForCheck(allocID string, checkID structs.CheckID) []byte {
   729  	return []byte(fmt.Sprintf("%s_%s", allocID, checkID))
   730  }
   731  
   732  // PutCheckResult puts qr into the state store.
   733  func (s *BoltStateDB) PutCheckResult(allocID string, qr *structs.CheckQueryResult) error {
   734  	return s.db.Update(func(tx *boltdd.Tx) error {
   735  		bkt, err := tx.CreateBucketIfNotExists(checkResultsBucket)
   736  		if err != nil {
   737  			return err
   738  		}
   739  		key := keyForCheck(allocID, qr.ID)
   740  		return bkt.Put(key, qr)
   741  	})
   742  }
   743  
   744  // GetCheckResults gets the check results associated with allocID from the state store.
   745  func (s *BoltStateDB) GetCheckResults() (checks.ClientResults, error) {
   746  	m := make(checks.ClientResults)
   747  
   748  	err := s.db.View(func(tx *boltdd.Tx) error {
   749  		bkt := tx.Bucket(checkResultsBucket)
   750  		if bkt == nil {
   751  			return nil // nothing set yet
   752  		}
   753  
   754  		if err := boltdd.Iterate(bkt, nil, func(key []byte, qr structs.CheckQueryResult) {
   755  			parts := bytes.SplitN(key, []byte("_"), 2)
   756  			allocID, _ := parts[0], parts[1]
   757  			m.Insert(string(allocID), &qr)
   758  		}); err != nil {
   759  			return err
   760  		}
   761  		return nil
   762  	})
   763  	return m, err
   764  }
   765  
   766  func (s *BoltStateDB) DeleteCheckResults(allocID string, checkIDs []structs.CheckID) error {
   767  	return s.db.Update(func(tx *boltdd.Tx) error {
   768  		bkt := tx.Bucket(checkResultsBucket)
   769  		if bkt == nil {
   770  			return nil // nothing set yet
   771  		}
   772  
   773  		for _, id := range checkIDs {
   774  			key := keyForCheck(allocID, id)
   775  			if err := bkt.Delete(key); err != nil {
   776  				return err
   777  			}
   778  		}
   779  		return nil
   780  	})
   781  }
   782  
   783  func (s *BoltStateDB) PurgeCheckResults(allocID string) error {
   784  	return s.db.Update(func(tx *boltdd.Tx) error {
   785  		bkt := tx.Bucket(checkResultsBucket)
   786  		if bkt == nil {
   787  			return nil // nothing set yet
   788  		}
   789  		return bkt.DeletePrefix([]byte(allocID + "_"))
   790  	})
   791  }
   792  
   793  // init initializes metadata entries in a newly created state database.
   794  func (s *BoltStateDB) init() error {
   795  	return s.db.Update(func(tx *boltdd.Tx) error {
   796  		return addMeta(tx.BoltTx())
   797  	})
   798  }
   799  
   800  // updateWithOptions enables adjustments to db.Update operation, including Batch mode.
   801  func (s *BoltStateDB) updateWithOptions(opts []WriteOption, updateFn func(tx *boltdd.Tx) error) error {
   802  	writeOpts := mergeWriteOptions(opts)
   803  
   804  	if writeOpts.BatchMode {
   805  		// In Batch mode, BoltDB opportunistically combines multiple concurrent writes into one or
   806  		// several transactions. See boltdb.Batch() documentation for details.
   807  		return s.db.Batch(updateFn)
   808  	} else {
   809  		return s.db.Update(updateFn)
   810  	}
   811  }
   812  
   813  // Upgrade bolt state db from 0.8 schema to 0.9 schema. Noop if already using
   814  // 0.9 schema. Creates a backup before upgrading.
   815  func (s *BoltStateDB) Upgrade() error {
   816  	// Check to see if the underlying DB needs upgrading.
   817  	upgrade09, upgrade13, err := NeedsUpgrade(s.db.BoltDB())
   818  	if err != nil {
   819  		return err
   820  	}
   821  	if !upgrade09 && !upgrade13 {
   822  		// No upgrade needed!
   823  		return nil
   824  	}
   825  
   826  	// Upgraded needed. Backup the boltdb first.
   827  	backupFileName := filepath.Join(s.stateDir, "state.db.backup")
   828  	if err := backupDB(s.db.BoltDB(), backupFileName); err != nil {
   829  		return fmt.Errorf("error backing up state db: %v", err)
   830  	}
   831  
   832  	// Perform the upgrade
   833  	if err := s.db.Update(func(tx *boltdd.Tx) error {
   834  
   835  		if upgrade09 {
   836  			if err := UpgradeAllocs(s.logger, tx); err != nil {
   837  				return err
   838  			}
   839  		}
   840  		if upgrade13 {
   841  			if err := UpgradeDynamicPluginRegistry(s.logger, tx); err != nil {
   842  				return err
   843  			}
   844  		}
   845  
   846  		// Add standard metadata
   847  		if err := addMeta(tx.BoltTx()); err != nil {
   848  			return err
   849  		}
   850  
   851  		// Write the time the upgrade was done
   852  		bkt, err := tx.CreateBucketIfNotExists(metaBucketName)
   853  		if err != nil {
   854  			return err
   855  		}
   856  
   857  		return bkt.Put(metaUpgradedKey, time.Now().Format(time.RFC3339))
   858  	}); err != nil {
   859  		return err
   860  	}
   861  
   862  	s.logger.Info("successfully upgraded state")
   863  	return nil
   864  }
   865  
   866  // DB allows access to the underlying BoltDB for testing purposes.
   867  func (s *BoltStateDB) DB() *boltdd.DB {
   868  	return s.db
   869  }