github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/state/state_database.go (about)

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