2017-12-14 03:02:15 +00:00
package libpod
import (
2018-06-26 03:39:11 +00:00
"bytes"
2017-12-14 03:02:15 +00:00
"strings"
2018-07-25 14:43:28 +00:00
"sync"
2017-12-14 03:02:15 +00:00
2019-06-24 20:48:34 +00:00
"github.com/containers/libpod/libpod/define"
2019-05-20 14:56:00 +00:00
bolt "github.com/etcd-io/bbolt"
2019-01-08 19:05:56 +00:00
jsoniter "github.com/json-iterator/go"
2017-12-14 03:02:15 +00:00
"github.com/pkg/errors"
2018-07-18 17:47:08 +00:00
"github.com/sirupsen/logrus"
2017-12-14 03:02:15 +00:00
)
2019-01-08 19:05:56 +00:00
var json = jsoniter . ConfigCompatibleWithStandardLibrary
2017-12-14 03:02:15 +00:00
// BoltState is a state implementation backed by a Bolt DB
type BoltState struct {
2018-06-26 03:39:11 +00:00
valid bool
dbPath string
2018-07-25 14:43:28 +00:00
dbLock sync . Mutex
2018-06-26 03:39:11 +00:00
namespace string
namespaceBytes [ ] byte
runtime * Runtime
2017-12-14 03:02:15 +00:00
}
2018-07-24 20:11:26 +00:00
// A brief description of the format of the BoltDB state:
// At the top level, the following buckets are created:
// - idRegistryBkt: Maps ID to Name for containers and pods.
// Used to ensure container and pod IDs are globally unique.
// - nameRegistryBkt: Maps Name to ID for containers and pods.
// Used to ensure container and pod names are globally unique.
// - nsRegistryBkt: Maps ID to namespace for all containers and pods.
// Used during lookup operations to determine if a given ID is in the same
// namespace as the state.
// - ctrBkt: Contains a sub-bucket for each container in the state.
// Each sub-bucket has config and state keys holding the container's JSON
// encoded configuration and state (respectively), an optional netNS key
// containing the path to the container's network namespace, a dependencies
// bucket containing the container's dependencies, and an optional pod key
// containing the ID of the pod the container is joined to.
// - allCtrsBkt: Map of ID to name containing only containers. Used for
// container lookup operations.
// - podBkt: Contains a sub-bucket for each pod in the state.
// Each sub-bucket has config and state keys holding the pod's JSON encoded
// configuration and state, plus a containers sub bucket holding the IDs of
// containers in the pod.
// - allPodsBkt: Map of ID to name containing only pods. Used for pod lookup
// operations.
// - runtimeConfigBkt: Contains configuration of the libpod instance that
// initially created the database. This must match for any further instances
// that access the database, to ensure that state mismatches with
// containers/storage do not occur.
2017-12-14 03:02:15 +00:00
// NewBoltState creates a new bolt-backed state database
2018-12-04 18:50:38 +00:00
func NewBoltState ( path string , runtime * Runtime ) ( State , error ) {
2017-12-14 03:02:15 +00:00
state := new ( BoltState )
state . dbPath = path
state . runtime = runtime
2018-06-26 03:39:11 +00:00
state . namespace = ""
state . namespaceBytes = nil
2017-12-14 03:02:15 +00:00
2018-07-18 17:47:08 +00:00
logrus . Debugf ( "Initializing boltdb state at %s" , path )
2017-12-14 03:02:15 +00:00
db , err := bolt . Open ( path , 0600 , nil )
if err != nil {
return nil , errors . Wrapf ( err , "error opening database %s" , path )
}
2019-07-08 18:20:17 +00:00
// Everywhere else, we use s.deferredCloseDBCon(db) to ensure the state's DB
2018-07-25 14:43:28 +00:00
// mutex is also unlocked.
// However, here, the mutex has not been locked, since we just created
// the DB connection, and it hasn't left this function yet - no risk of
// concurrent access.
// As such, just a db.Close() is fine here.
2017-12-14 03:02:15 +00:00
defer db . Close ( )
2019-06-20 14:17:38 +00:00
createBuckets := [ ] [ ] byte {
idRegistryBkt ,
nameRegistryBkt ,
nsRegistryBkt ,
ctrBkt ,
allCtrsBkt ,
podBkt ,
allPodsBkt ,
volBkt ,
allVolsBkt ,
runtimeConfigBkt ,
}
// Does the DB need an update?
needsUpdate := false
err = db . View ( func ( tx * bolt . Tx ) error {
for _ , bkt := range createBuckets {
if test := tx . Bucket ( bkt ) ; test == nil {
needsUpdate = true
break
}
2018-08-08 13:50:15 +00:00
}
2019-06-20 14:17:38 +00:00
return nil
} )
if err != nil {
return nil , errors . Wrapf ( err , "error checking DB schema" )
}
if ! needsUpdate {
state . valid = true
return state , nil
}
// Ensure schema is properly created in DB
err = db . Update ( func ( tx * bolt . Tx ) error {
for _ , bkt := range createBuckets {
if _ , err := tx . CreateBucketIfNotExists ( bkt ) ; err != nil {
return errors . Wrapf ( err , "error creating bucket %s" , string ( bkt ) )
}
2017-12-14 03:02:15 +00:00
}
return nil
} )
if err != nil {
2019-06-20 14:17:38 +00:00
return nil , errors . Wrapf ( err , "error creating buckets for DB" )
2017-12-14 03:02:15 +00:00
}
state . valid = true
return state , nil
}
// Close closes the state and prevents further use
func ( s * BoltState ) Close ( ) error {
s . valid = false
return nil
}
// Refresh clears container and pod states after a reboot
func ( s * BoltState ) Refresh ( ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
idBucket , err := getIDBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
ctrsBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-05-16 16:45:09 +00:00
podsBucket , err := getPodBucket ( tx )
if err != nil {
return err
}
2019-09-03 19:03:44 +00:00
allVolsBucket , err := getAllVolsBucket ( tx )
if err != nil {
return err
}
volBucket , err := getVolBucket ( tx )
if err != nil {
return err
}
2017-12-14 03:02:15 +00:00
// Iterate through all IDs. Check if they are containers.
// If they are, unmarshal their state, and then clear
// PID, mountpoint, and state for all of them
// Then save the modified state
// Also clear all network namespaces
err = idBucket . ForEach ( func ( id , name [ ] byte ) error {
2018-02-09 21:02:45 +00:00
ctrBkt := ctrsBucket . Bucket ( id )
if ctrBkt == nil {
2018-05-16 16:45:09 +00:00
// It's a pod
podBkt := podsBucket . Bucket ( id )
if podBkt == nil {
// This is neither a pod nor a container
// Error out on the dangling ID
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "id %s is not a pod or a container" , string ( id ) )
2018-05-16 16:45:09 +00:00
}
// Get the state
stateBytes := podBkt . Get ( stateKey )
if stateBytes == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s missing state key" , string ( id ) )
2018-05-16 16:45:09 +00:00
}
state := new ( podState )
if err := json . Unmarshal ( stateBytes , state ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling state for pod %s" , string ( id ) )
}
// Clear the CGroup path
state . CgroupPath = ""
newStateBytes , err := json . Marshal ( state )
if err != nil {
return errors . Wrapf ( err , "error marshalling modified state for pod %s" , string ( id ) )
}
if err := podBkt . Put ( stateKey , newStateBytes ) ; err != nil {
return errors . Wrapf ( err , "error updating state for pod %s in DB" , string ( id ) )
}
2018-02-09 21:02:45 +00:00
// It's not a container, nothing to do
return nil
}
// First, delete the network namespace
if err := ctrBkt . Delete ( netNSKey ) ; err != nil {
return errors . Wrapf ( err , "error removing network namespace for container %s" , string ( id ) )
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
stateBytes := ctrBkt . Get ( stateKey )
2017-12-14 03:02:15 +00:00
if stateBytes == nil {
2018-02-09 21:02:45 +00:00
// Badly formatted container bucket
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "container %s missing state in DB" , string ( id ) )
2017-12-14 03:02:15 +00:00
}
2019-01-17 14:43:34 +00:00
state := new ( ContainerState )
2017-12-14 03:02:15 +00:00
if err := json . Unmarshal ( stateBytes , state ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling state for container %s" , string ( id ) )
}
2018-06-21 13:45:03 +00:00
if err := resetState ( state ) ; err != nil {
return errors . Wrapf ( err , "error resetting state for container %s" , string ( id ) )
}
2017-12-14 03:02:15 +00:00
newStateBytes , err := json . Marshal ( state )
if err != nil {
return errors . Wrapf ( err , "error marshalling modified state for container %s" , string ( id ) )
}
2018-02-09 21:02:45 +00:00
if err := ctrBkt . Put ( stateKey , newStateBytes ) ; err != nil {
2017-12-14 03:02:15 +00:00
return errors . Wrapf ( err , "error updating state for container %s in DB" , string ( id ) )
}
return nil
} )
2019-09-03 19:03:44 +00:00
if err != nil {
return err
}
// Now refresh volumes
err = allVolsBucket . ForEach ( func ( id , name [ ] byte ) error {
dbVol := volBucket . Bucket ( id )
if dbVol == nil {
return errors . Wrapf ( define . ErrInternal , "inconsistency in state - volume %s is in all volumes bucket but volume not found" , string ( id ) )
}
// Get the state
volStateBytes := dbVol . Get ( stateKey )
if volStateBytes == nil {
// If the volume doesn't have a state, nothing to do
return nil
}
oldState := new ( VolumeState )
if err := json . Unmarshal ( volStateBytes , oldState ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling state for volume %s" , string ( id ) )
}
// Reset mount count to 0
oldState . MountCount = 0
newState , err := json . Marshal ( oldState )
if err != nil {
return errors . Wrapf ( err , "error marshalling state for volume %s" , string ( id ) )
}
if err := dbVol . Put ( stateKey , newState ) ; err != nil {
return errors . Wrapf ( err , "error storing new state for volume %s" , string ( id ) )
}
return nil
} )
2017-12-14 03:02:15 +00:00
return err
} )
return err
}
2018-11-28 20:27:09 +00:00
// GetDBConfig retrieves runtime configuration fields that were created when
// the database was first initialized
func ( s * BoltState ) GetDBConfig ( ) ( * DBConfig , error ) {
2018-12-02 18:36:55 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-12-02 18:36:55 +00:00
}
2018-11-28 20:27:09 +00:00
cfg := new ( DBConfig )
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-11-28 20:27:09 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
configBucket , err := getRuntimeConfigBucket ( tx )
if err != nil {
return nil
}
// Some of these may be nil
// When we convert to string, Go will coerce them to ""
// That's probably fine - we could raise an error if the key is
// missing, but just not including it is also OK.
libpodRoot := configBucket . Get ( staticDirKey )
libpodTmp := configBucket . Get ( tmpDirKey )
storageRoot := configBucket . Get ( graphRootKey )
storageTmp := configBucket . Get ( runRootKey )
graphDriver := configBucket . Get ( graphDriverKey )
2019-02-21 14:42:22 +00:00
volumePath := configBucket . Get ( volPathKey )
2018-11-28 20:27:09 +00:00
cfg . LibpodRoot = string ( libpodRoot )
cfg . LibpodTmp = string ( libpodTmp )
cfg . StorageRoot = string ( storageRoot )
cfg . StorageTmp = string ( storageTmp )
cfg . GraphDriver = string ( graphDriver )
2019-02-21 14:42:22 +00:00
cfg . VolumePath = string ( volumePath )
2018-11-28 20:27:09 +00:00
return nil
} )
if err != nil {
return nil , err
}
return cfg , nil
}
2018-12-02 18:36:55 +00:00
// ValidateDBConfig validates paths in the given runtime against the database
func ( s * BoltState ) ValidateDBConfig ( runtime * Runtime ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-12-02 18:36:55 +00:00
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-12-02 18:36:55 +00:00
// Check runtime configuration
if err := checkRuntimeConfig ( db , runtime ) ; err != nil {
return err
}
return nil
}
2018-06-26 03:39:11 +00:00
// SetNamespace sets the namespace that will be used for container and pod
// retrieval
func ( s * BoltState ) SetNamespace ( ns string ) error {
s . namespace = ns
if ns != "" {
s . namespaceBytes = [ ] byte ( ns )
} else {
s . namespaceBytes = nil
}
return nil
}
2017-12-14 03:02:15 +00:00
// Container retrieves a single container from the state by its full ID
func ( s * BoltState ) Container ( id string ) ( * Container , error ) {
if id == "" {
2019-06-24 20:48:34 +00:00
return nil , define . ErrEmptyID
2017-12-14 03:02:15 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
ctrID := [ ] byte ( id )
ctr := new ( Container )
2019-01-07 19:20:04 +00:00
ctr . config = new ( ContainerConfig )
2019-01-17 14:43:34 +00:00
ctr . state = new ( ContainerState )
2017-12-14 03:02:15 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
return s . getContainerFromDB ( ctrID , ctr , ctrBucket )
2017-12-14 03:02:15 +00:00
} )
if err != nil {
return nil , err
}
return ctr , nil
}
2019-07-05 10:54:07 +00:00
// LookupContainerID retrieves a container ID from the state by full or unique
// partial ID or name
func ( s * BoltState ) LookupContainerID ( idOrName string ) ( string , error ) {
if idOrName == "" {
return "" , define . ErrEmptyID
}
if ! s . valid {
return "" , define . ErrDBClosed
}
db , err := s . getDBCon ( )
if err != nil {
return "" , err
}
defer s . deferredCloseDBCon ( db )
var id [ ] byte
err = db . View ( func ( tx * bolt . Tx ) error {
ctrBucket , err := getCtrBucket ( tx )
if err != nil {
return err
}
namesBucket , err := getNamesBucket ( tx )
if err != nil {
return err
}
nsBucket , err := getNSBucket ( tx )
if err != nil {
return err
}
fullID , err := s . lookupContainerID ( idOrName , ctrBucket , namesBucket , nsBucket )
// Check if it is in our namespace
if s . namespaceBytes != nil {
ns := nsBucket . Get ( fullID )
if ! bytes . Equal ( ns , s . namespaceBytes ) {
return errors . Wrapf ( define . ErrNoSuchCtr , "no container found with name or ID %s" , idOrName )
}
}
id = fullID
return err
} )
if err != nil {
return "" , err
}
retID := string ( id )
return retID , nil
}
2017-12-14 03:02:15 +00:00
// LookupContainer retrieves a container from the state by full or unique
// partial ID or name
func ( s * BoltState ) LookupContainer ( idOrName string ) ( * Container , error ) {
if idOrName == "" {
2019-06-24 20:48:34 +00:00
return nil , define . ErrEmptyID
2017-12-14 03:02:15 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
ctr := new ( Container )
2019-01-07 19:20:04 +00:00
ctr . config = new ( ContainerConfig )
2019-01-17 14:43:34 +00:00
ctr . state = new ( ContainerState )
2017-12-14 03:02:15 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2019-03-06 19:26:57 +00:00
namesBucket , err := getNamesBucket ( tx )
if err != nil {
return err
}
2018-06-26 03:39:11 +00:00
nsBucket , err := getNSBucket ( tx )
if err != nil {
return err
}
2019-07-05 10:54:07 +00:00
id , err := s . lookupContainerID ( idOrName , ctrBucket , namesBucket , nsBucket )
2019-03-06 19:26:57 +00:00
if err != nil {
return err
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
return s . getContainerFromDB ( id , ctr , ctrBucket )
2017-12-14 03:02:15 +00:00
} )
if err != nil {
return nil , err
}
return ctr , nil
}
// HasContainer checks if a container is present in the state
func ( s * BoltState ) HasContainer ( id string ) ( bool , error ) {
if id == "" {
2019-06-24 20:48:34 +00:00
return false , define . ErrEmptyID
2017-12-14 03:02:15 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return false , define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
ctrID := [ ] byte ( id )
db , err := s . getDBCon ( )
if err != nil {
return false , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
exists := false
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-07-18 17:47:08 +00:00
ctrDB := ctrBucket . Bucket ( ctrID )
if ctrDB != nil {
2018-06-26 03:39:11 +00:00
if s . namespaceBytes != nil {
2018-07-18 17:47:08 +00:00
nsBytes := ctrDB . Get ( namespaceKey )
2018-07-03 15:12:00 +00:00
if bytes . Equal ( nsBytes , s . namespaceBytes ) {
2018-06-26 03:39:11 +00:00
exists = true
}
} else {
exists = true
}
2017-12-14 03:02:15 +00:00
}
return nil
} )
if err != nil {
return false , err
}
return exists , nil
}
// AddContainer adds a container to the state
// The container being added cannot belong to a pod
func ( s * BoltState ) AddContainer ( ctr * Container ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return define . ErrCtrRemoved
2017-12-14 03:02:15 +00:00
}
if ctr . config . Pod != "" {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInvalidArg , "cannot add a container that belongs to a pod with AddContainer - use AddContainerToPod" )
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
return s . addContainer ( ctr , nil )
2017-12-14 03:02:15 +00:00
}
// RemoveContainer removes a container from the state
2018-02-09 21:02:45 +00:00
// Only removes containers not in pods - for containers that are a member of a
// pod, use RemoveContainerFromPod
2017-12-14 03:02:15 +00:00
func ( s * BoltState ) RemoveContainer ( ctr * Container ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
if ctr . config . Pod != "" {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrPodExists , "container %s is part of a pod, use RemoveContainerFromPod instead" , ctr . ID ( ) )
2018-02-09 21:02:45 +00:00
}
2017-12-14 03:02:15 +00:00
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
2018-07-03 15:12:00 +00:00
return s . removeContainer ( ctr , nil , tx )
2017-12-14 03:02:15 +00:00
} )
return err
}
// UpdateContainer updates a container's state from the database
func ( s * BoltState ) UpdateContainer ( ctr * Container ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return define . ErrCtrRemoved
2017-12-14 03:02:15 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != ctr . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "container %s is in namespace %q, does not match our namespace %q" , ctr . ID ( ) , ctr . config . Namespace , s . namespace )
2018-06-26 03:39:11 +00:00
}
2019-01-17 14:43:34 +00:00
newState := new ( ContainerState )
2017-12-14 03:02:15 +00:00
netNSPath := ""
ctrID := [ ] byte ( ctr . ID ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
ctrToUpdate := ctrBucket . Bucket ( ctrID )
if ctrToUpdate == nil {
ctr . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "container %s does not exist in database" , ctr . ID ( ) )
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
newStateBytes := ctrToUpdate . Get ( stateKey )
2017-12-14 03:02:15 +00:00
if newStateBytes == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "container %s does not have a state key in DB" , ctr . ID ( ) )
2017-12-14 03:02:15 +00:00
}
if err := json . Unmarshal ( newStateBytes , newState ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling container %s state" , ctr . ID ( ) )
}
2018-02-09 21:02:45 +00:00
netNSBytes := ctrToUpdate . Get ( netNSKey )
2017-12-14 03:02:15 +00:00
if netNSBytes != nil {
netNSPath = string ( netNSBytes )
}
return nil
} )
if err != nil {
return err
}
2018-07-30 13:42:35 +00:00
// Handle network namespace
if err := replaceNetNS ( netNSPath , ctr , newState ) ; err != nil {
return err
}
2017-12-14 03:02:15 +00:00
// New state compiled successfully, swap it into the current state
ctr . state = newState
return nil
}
// SaveContainer saves a container's current state in the database
func ( s * BoltState ) SaveContainer ( ctr * Container ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return define . ErrCtrRemoved
2017-12-14 03:02:15 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != ctr . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "container %s is in namespace %q, does not match our namespace %q" , ctr . ID ( ) , ctr . config . Namespace , s . namespace )
2018-06-26 03:39:11 +00:00
}
2017-12-14 03:02:15 +00:00
stateJSON , err := json . Marshal ( ctr . state )
if err != nil {
return errors . Wrapf ( err , "error marshalling container %s state to JSON" , ctr . ID ( ) )
}
2018-07-30 13:42:35 +00:00
netNSPath := getNetNSPath ( ctr )
2017-12-14 03:02:15 +00:00
ctrID := [ ] byte ( ctr . ID ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
ctrToSave := ctrBucket . Bucket ( ctrID )
if ctrToSave == nil {
2018-02-09 22:13:07 +00:00
ctr . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "container %s does not exist in DB" , ctr . ID ( ) )
2017-12-14 03:02:15 +00:00
}
// Update the state
2018-02-09 21:02:45 +00:00
if err := ctrToSave . Put ( stateKey , stateJSON ) ; err != nil {
2017-12-14 03:02:15 +00:00
return errors . Wrapf ( err , "error updating container %s state in DB" , ctr . ID ( ) )
}
if netNSPath != "" {
2018-02-09 21:02:45 +00:00
if err := ctrToSave . Put ( netNSKey , [ ] byte ( netNSPath ) ) ; err != nil {
2017-12-14 03:02:15 +00:00
return errors . Wrapf ( err , "error updating network namespace path for container %s in DB" , ctr . ID ( ) )
}
2018-02-09 21:02:45 +00:00
} else {
// Delete the existing network namespace
if err := ctrToSave . Delete ( netNSKey ) ; err != nil {
return errors . Wrapf ( err , "error removing network namespace path for container %s in DB" , ctr . ID ( ) )
}
2017-12-14 03:02:15 +00:00
}
return nil
} )
return err
}
// ContainerInUse checks if other containers depend on the given container
// It returns a slice of the IDs of the containers depending on the given
// container. If the slice is empty, no containers depend on the given container
func ( s * BoltState ) ContainerInUse ( ctr * Container ) ( [ ] string , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrCtrRemoved
2017-12-14 03:02:15 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != ctr . config . Namespace {
2019-06-24 20:48:34 +00:00
return nil , errors . Wrapf ( define . ErrNSMismatch , "container %s is in namespace %q, does not match our namespace %q" , ctr . ID ( ) , ctr . config . Namespace , s . namespace )
2018-06-26 03:39:11 +00:00
}
2017-12-14 03:02:15 +00:00
depCtrs := [ ] string { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
ctrDB := ctrBucket . Bucket ( [ ] byte ( ctr . ID ( ) ) )
if ctrDB == nil {
ctr . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "no container with ID %s found in DB" , ctr . ID ( ) )
2018-02-09 21:02:45 +00:00
}
dependsBkt := ctrDB . Bucket ( dependenciesBkt )
if dependsBkt == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "container %s has no dependencies bucket" , ctr . ID ( ) )
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
// Iterate through and add dependencies
err = dependsBkt . ForEach ( func ( id , value [ ] byte ) error {
depCtrs = append ( depCtrs , string ( id ) )
return nil
} )
if err != nil {
return err
2017-12-14 03:02:15 +00:00
}
return nil
} )
if err != nil {
return nil , err
}
return depCtrs , nil
}
// AllContainers retrieves all the containers in the database
func ( s * BoltState ) AllContainers ( ) ( [ ] * Container , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2017-12-14 03:02:15 +00:00
}
ctrs := [ ] * Container { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-10 21:44:38 +00:00
allCtrsBucket , err := getAllCtrsBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
ctrBucket , err := getCtrBucket ( tx )
2017-12-14 03:02:15 +00:00
if err != nil {
return err
}
2018-06-14 16:41:38 +00:00
return allCtrsBucket . ForEach ( func ( id , name [ ] byte ) error {
2018-02-10 21:44:38 +00:00
// If performance becomes an issue, this check can be
// removed. But the error messages that come back will
// be much less helpful.
2018-02-09 21:02:45 +00:00
ctrExists := ctrBucket . Bucket ( id )
if ctrExists == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "state is inconsistent - container ID %s in all containers, but container not found" , string ( id ) )
2018-02-09 21:02:45 +00:00
}
2017-12-14 03:02:15 +00:00
ctr := new ( Container )
2019-01-07 19:20:04 +00:00
ctr . config = new ( ContainerConfig )
2019-01-17 14:43:34 +00:00
ctr . state = new ( ContainerState )
2017-12-14 03:02:15 +00:00
2018-06-26 03:39:11 +00:00
if err := s . getContainerFromDB ( id , ctr , ctrBucket ) ; err != nil {
// If the error is a namespace mismatch, we can
// ignore it safely.
// We just won't include the container in the
// results.
2019-06-24 20:48:34 +00:00
if errors . Cause ( err ) != define . ErrNSMismatch {
2018-08-17 18:21:21 +00:00
// Even if it's not an NS mismatch, it's
// not worth erroring over.
// If we do, a single bad container JSON
// could render libpod unusable.
logrus . Errorf ( "Error retrieving container %s from the database: %v" , string ( id ) , err )
2018-06-26 03:39:11 +00:00
}
} else {
ctrs = append ( ctrs , ctr )
}
return nil
2017-12-14 03:02:15 +00:00
} )
} )
if err != nil {
return nil , err
}
return ctrs , nil
}
2019-07-05 10:54:07 +00:00
// GetContainerConfig returns a container config from the database by full ID
func ( s * BoltState ) GetContainerConfig ( id string ) ( * ContainerConfig , error ) {
if len ( id ) == 0 {
return nil , define . ErrEmptyID
}
if ! s . valid {
return nil , define . ErrDBClosed
}
config := new ( ContainerConfig )
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
defer s . deferredCloseDBCon ( db )
err = db . View ( func ( tx * bolt . Tx ) error {
ctrBucket , err := getCtrBucket ( tx )
if err != nil {
return err
}
return s . getContainerConfigFromDB ( [ ] byte ( id ) , config , ctrBucket )
} )
if err != nil {
return nil , err
}
return config , nil
}
2019-02-14 20:53:24 +00:00
// RewriteContainerConfig rewrites a container's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
func ( s * BoltState ) RewriteContainerConfig ( ctr * Container , newCfg * ContainerConfig ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2019-02-14 20:53:24 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return define . ErrCtrRemoved
2019-02-14 20:53:24 +00:00
}
newCfgJSON , err := json . Marshal ( newCfg )
if err != nil {
return errors . Wrapf ( err , "error marshalling new configuration JSON for container %s" , ctr . ID ( ) )
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2019-02-14 20:53:24 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
ctrBkt , err := getCtrBucket ( tx )
if err != nil {
return err
}
ctrDB := ctrBkt . Bucket ( [ ] byte ( ctr . ID ( ) ) )
if ctrDB == nil {
ctr . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "no container with ID %s found in DB" , ctr . ID ( ) )
2019-02-14 20:53:24 +00:00
}
if err := ctrDB . Put ( configKey , newCfgJSON ) ; err != nil {
return errors . Wrapf ( err , "error updating container %s config JSON" , ctr . ID ( ) )
2019-02-14 22:52:49 +00:00
}
return nil
} )
return err
}
// RewritePodConfig rewrites a pod's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
func ( s * BoltState ) RewritePodConfig ( pod * Pod , newCfg * PodConfig ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2019-02-14 22:52:49 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2019-02-14 22:52:49 +00:00
}
newCfgJSON , err := json . Marshal ( newCfg )
if err != nil {
2019-08-27 17:45:11 +00:00
return errors . Wrapf ( err , "error marshalling new configuration JSON for pod %s" , pod . ID ( ) )
2019-02-14 22:52:49 +00:00
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2019-02-14 22:52:49 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
podDB := podBkt . Bucket ( [ ] byte ( pod . ID ( ) ) )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "no pod with ID %s found in DB" , pod . ID ( ) )
2019-02-14 22:52:49 +00:00
}
if err := podDB . Put ( configKey , newCfgJSON ) ; err != nil {
return errors . Wrapf ( err , "error updating pod %s config JSON" , pod . ID ( ) )
2019-02-14 20:53:24 +00:00
}
return nil
} )
return err
}
2019-08-27 17:45:11 +00:00
// RewriteVolumeConfig rewrites a volume's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
func ( s * BoltState ) RewriteVolumeConfig ( volume * Volume , newCfg * VolumeConfig ) error {
if ! s . valid {
return define . ErrDBClosed
}
if ! volume . valid {
return define . ErrVolumeRemoved
}
newCfgJSON , err := json . Marshal ( newCfg )
if err != nil {
return errors . Wrapf ( err , "error marshalling new configuration JSON for volume %q" , volume . Name ( ) )
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
defer s . deferredCloseDBCon ( db )
err = db . Update ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
volDB := volBkt . Bucket ( [ ] byte ( volume . Name ( ) ) )
if volDB == nil {
volume . valid = false
return errors . Wrapf ( define . ErrNoSuchVolume , "no volume with name %q found in DB" , volume . Name ( ) )
}
if err := volDB . Put ( configKey , newCfgJSON ) ; err != nil {
return errors . Wrapf ( err , "error updating volume %q config JSON" , volume . Name ( ) )
}
return nil
} )
return err
}
2017-12-14 03:02:15 +00:00
// Pod retrieves a pod given its full ID
func ( s * BoltState ) Pod ( id string ) ( * Pod , error ) {
2018-02-09 21:02:45 +00:00
if id == "" {
2019-06-24 20:48:34 +00:00
return nil , define . ErrEmptyID
2018-02-09 21:02:45 +00:00
}
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( id )
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
pod := new ( Pod )
2018-02-09 22:13:07 +00:00
pod . config = new ( PodConfig )
2018-05-14 23:30:11 +00:00
pod . state = new ( podState )
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
2017-12-14 03:02:15 +00:00
2018-02-09 21:02:45 +00:00
return s . getPodFromDB ( podID , pod , podBkt )
} )
if err != nil {
return nil , err
}
return pod , nil
2017-12-14 03:02:15 +00:00
}
2018-02-09 21:02:45 +00:00
// LookupPod retrieves a pod from full or unique partial ID or name
func ( s * BoltState ) LookupPod ( idOrName string ) ( * Pod , error ) {
if idOrName == "" {
2019-06-24 20:48:34 +00:00
return nil , define . ErrEmptyID
2018-02-09 21:02:45 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
pod := new ( Pod )
2018-02-09 22:13:07 +00:00
pod . config = new ( PodConfig )
2018-05-14 23:30:11 +00:00
pod . state = new ( podState )
2018-02-09 21:02:45 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
2019-03-06 19:26:57 +00:00
namesBkt , err := getNamesBucket ( tx )
if err != nil {
return err
}
2018-06-26 03:39:11 +00:00
nsBkt , err := getNSBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
// First, check if the ID given was the actual pod ID
var id [ ] byte
podExists := podBkt . Bucket ( [ ] byte ( idOrName ) )
if podExists != nil {
2018-07-24 20:11:26 +00:00
// A full pod ID was given.
// It might not be in our namespace, but getPodFromDB()
// will handle that case.
2018-02-09 21:02:45 +00:00
id = [ ] byte ( idOrName )
2019-03-06 19:26:57 +00:00
return s . getPodFromDB ( id , pod , podBkt )
}
// Next, check if the full name was given
isCtr := false
fullID := namesBkt . Get ( [ ] byte ( idOrName ) )
if fullID != nil {
// The name exists and maps to an ID.
// However, we aren't yet sure if the ID is a pod.
podExists = podBkt . Bucket ( fullID )
if podExists != nil {
// A pod bucket matching the full ID was found.
return s . getPodFromDB ( fullID , pod , podBkt )
}
// Don't error if we have a name match but it's not a
// pod - there's a chance we have a pod with an ID
// starting with those characters.
// However, so we can return a good error, note whether
// this is a container.
isCtr = true
}
// They did not give us a full pod name or ID.
// Search for partial ID matches.
exists := false
2019-07-03 09:53:29 +00:00
err = podBkt . ForEach ( func ( checkID , checkName [ ] byte ) error {
2019-03-06 19:26:57 +00:00
// If the pod isn't in our namespace, we
// can't match it
if s . namespaceBytes != nil {
ns := nsBkt . Get ( checkID )
if ! bytes . Equal ( ns , s . namespaceBytes ) {
return nil
2018-06-26 03:39:11 +00:00
}
2019-03-06 19:26:57 +00:00
}
if strings . HasPrefix ( string ( checkID ) , idOrName ) {
if exists {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrPodExists , "more than one result for ID or name %s" , idOrName )
2018-02-09 21:02:45 +00:00
}
2019-03-06 19:26:57 +00:00
id = checkID
exists = true
}
2018-02-09 21:02:45 +00:00
2019-03-06 19:26:57 +00:00
return nil
} )
if err != nil {
return err
} else if ! exists {
if isCtr {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "%s is a container, not a pod" , idOrName )
2018-02-09 21:02:45 +00:00
}
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "no pod with name or ID %s found" , idOrName )
2018-02-09 21:02:45 +00:00
}
// We might have found a container ID, but it's OK
// We'll just fail in getPodFromDB with ErrNoSuchPod
return s . getPodFromDB ( id , pod , podBkt )
} )
if err != nil {
return nil , err
}
return pod , nil
}
// HasPod checks if a pod with the given ID exists in the state
func ( s * BoltState ) HasPod ( id string ) ( bool , error ) {
if id == "" {
2019-06-24 20:48:34 +00:00
return false , define . ErrEmptyID
2018-02-09 21:02:45 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return false , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
podID := [ ] byte ( id )
exists := false
db , err := s . getDBCon ( )
if err != nil {
return false , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
2018-02-09 22:13:07 +00:00
podDB := podBkt . Bucket ( podID )
if podDB != nil {
2018-06-26 03:39:11 +00:00
if s . namespaceBytes != nil {
podNS := podDB . Get ( namespaceKey )
if bytes . Equal ( s . namespaceBytes , podNS ) {
exists = true
}
} else {
exists = true
}
2018-02-09 21:02:45 +00:00
}
return nil
} )
if err != nil {
return false , err
}
return exists , nil
}
// PodHasContainer checks if the given pod has a container with the given ID
func ( s * BoltState ) PodHasContainer ( pod * Pod , id string ) ( bool , error ) {
if id == "" {
2019-06-24 20:48:34 +00:00
return false , define . ErrEmptyID
2018-02-09 21:02:45 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return false , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return false , define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return false , errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
ctrID := [ ] byte ( id )
podID := [ ] byte ( pod . ID ( ) )
exists := false
db , err := s . getDBCon ( )
if err != nil {
return false , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
// Get pod itself
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "pod %s not found in database" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Get pod containers bucket
podCtrs := podDB . Bucket ( containersBkt )
if podCtrs == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s missing containers bucket in DB" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
2018-07-03 15:12:00 +00:00
// Don't bother with a namespace check on the container -
// We maintain the invariant that container namespaces must
// match the namespace of the pod they join.
// We already checked the pod namespace, so we should be fine.
2018-02-09 21:02:45 +00:00
ctr := podCtrs . Get ( ctrID )
if ctr != nil {
exists = true
}
return nil
} )
if err != nil {
return false , err
}
return exists , nil
}
// PodContainersByID returns the IDs of all containers present in the given pod
func ( s * BoltState ) PodContainersByID ( pod * Pod ) ( [ ] string , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return nil , errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( pod . ID ( ) )
ctrs := [ ] string { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
// Get pod itself
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "pod %s not found in database" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Get pod containers bucket
podCtrs := podDB . Bucket ( containersBkt )
if podCtrs == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s missing containers bucket in DB" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Iterate through all containers in the pod
err = podCtrs . ForEach ( func ( id , val [ ] byte ) error {
ctrs = append ( ctrs , string ( id ) )
return nil
} )
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
return ctrs , nil
}
// PodContainers returns all the containers present in the given pod
func ( s * BoltState ) PodContainers ( pod * Pod ) ( [ ] * Container , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return nil , errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( pod . ID ( ) )
ctrs := [ ] * Container { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
ctrBkt , err := getCtrBucket ( tx )
if err != nil {
return err
}
// Get pod itself
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "pod %s not found in database" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Get pod containers bucket
podCtrs := podDB . Bucket ( containersBkt )
if podCtrs == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s missing containers bucket in DB" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Iterate through all containers in the pod
err = podCtrs . ForEach ( func ( id , val [ ] byte ) error {
newCtr := new ( Container )
2019-01-07 19:20:04 +00:00
newCtr . config = new ( ContainerConfig )
2019-01-17 14:43:34 +00:00
newCtr . state = new ( ContainerState )
2018-02-09 21:02:45 +00:00
ctrs = append ( ctrs , newCtr )
return s . getContainerFromDB ( id , newCtr , ctrBkt )
} )
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
return ctrs , nil
}
2018-08-08 13:50:15 +00:00
// AddVolume adds the given volume to the state. It also adds ctrDepID to
// the sub bucket holding the container dependencies that this volume has
func ( s * BoltState ) AddVolume ( volume * Volume ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
if ! volume . valid {
2019-06-24 20:48:34 +00:00
return define . ErrVolumeRemoved
2018-08-08 13:50:15 +00:00
}
volName := [ ] byte ( volume . Name ( ) )
volConfigJSON , err := json . Marshal ( volume . config )
if err != nil {
return errors . Wrapf ( err , "error marshalling volume %s config to JSON" , volume . Name ( ) )
}
2019-08-30 20:09:17 +00:00
// Volume state is allowed to not exist
var volStateJSON [ ] byte
if volume . state != nil {
2019-09-05 14:00:50 +00:00
volStateJSON , err = json . Marshal ( volume . state )
2019-08-30 20:09:17 +00:00
if err != nil {
return errors . Wrapf ( err , "error marshalling volume %s state to JSON" , volume . Name ( ) )
}
}
2018-08-08 13:50:15 +00:00
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
allVolsBkt , err := getAllVolsBucket ( tx )
if err != nil {
return err
}
// Check if we already have a volume with the given name
volExists := allVolsBkt . Get ( volName )
if volExists != nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrVolumeExists , "name %s is in use" , volume . Name ( ) )
2018-08-08 13:50:15 +00:00
}
// We are good to add the volume
// Make a bucket for it
newVol , err := volBkt . CreateBucket ( volName )
if err != nil {
return errors . Wrapf ( err , "error creating bucket for volume %s" , volume . Name ( ) )
}
// Make a subbucket for the containers using the volume. Dependent container IDs will be addedremoved to
// this bucket in addcontainer/removeContainer
if _ , err := newVol . CreateBucket ( volDependenciesBkt ) ; err != nil {
return errors . Wrapf ( err , "error creating bucket for containers using volume %s" , volume . Name ( ) )
}
if err := newVol . Put ( configKey , volConfigJSON ) ; err != nil {
return errors . Wrapf ( err , "error storing volume %s configuration in DB" , volume . Name ( ) )
}
2019-08-30 20:09:17 +00:00
if volStateJSON != nil {
if err := newVol . Put ( stateKey , volStateJSON ) ; err != nil {
return errors . Wrapf ( err , "error storing volume %s state in DB" , volume . Name ( ) )
}
}
2018-08-08 13:50:15 +00:00
if err := allVolsBkt . Put ( volName , volName ) ; err != nil {
return errors . Wrapf ( err , "error storing volume %s in all volumes bucket in DB" , volume . Name ( ) )
}
return nil
} )
return err
}
// RemoveVolume removes the given volume from the state
func ( s * BoltState ) RemoveVolume ( volume * Volume ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
volName := [ ] byte ( volume . Name ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
allVolsBkt , err := getAllVolsBucket ( tx )
if err != nil {
return err
}
2019-03-15 14:01:23 +00:00
ctrBkt , err := getCtrBucket ( tx )
if err != nil {
return err
}
2018-08-08 13:50:15 +00:00
// Check if the volume exists
volDB := volBkt . Bucket ( volName )
if volDB == nil {
volume . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchVolume , "volume %s does not exist in DB" , volume . Name ( ) )
2018-08-08 13:50:15 +00:00
}
// Check if volume is not being used by any container
// This should never be nil
// But if it is, we can assume that no containers are using
// the volume.
volCtrsBkt := volDB . Bucket ( volDependenciesBkt )
if volCtrsBkt != nil {
var deps [ ] string
err = volCtrsBkt . ForEach ( func ( id , value [ ] byte ) error {
2019-03-15 14:01:23 +00:00
// Alright, this is ugly.
// But we need it to work around the change in
// volume dependency handling, to make sure that
// older Podman versions don't cause DB
// corruption.
// Look up all dependencies and see that they
// still exist before appending.
ctrExists := ctrBkt . Bucket ( id )
if ctrExists == nil {
return nil
}
2018-08-08 13:50:15 +00:00
deps = append ( deps , string ( id ) )
return nil
} )
if err != nil {
return errors . Wrapf ( err , "error getting list of dependencies from dependencies bucket for volumes %q" , volume . Name ( ) )
}
if len ( deps ) > 0 {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrVolumeBeingUsed , "volume %s is being used by container(s) %s" , volume . Name ( ) , strings . Join ( deps , "," ) )
2018-08-08 13:50:15 +00:00
}
}
// volume is ready for removal
// Let's kick it out
if err := allVolsBkt . Delete ( volName ) ; err != nil {
return errors . Wrapf ( err , "error removing volume %s from all volumes bucket in DB" , volume . Name ( ) )
}
if err := volBkt . DeleteBucket ( volName ) ; err != nil {
return errors . Wrapf ( err , "error removing volume %s from DB" , volume . Name ( ) )
}
return nil
} )
return err
}
2019-08-30 20:09:17 +00:00
// UpdateVolume updates the volume's state from the database.
func ( s * BoltState ) UpdateVolume ( volume * Volume ) error {
if ! s . valid {
return define . ErrDBClosed
}
if ! volume . valid {
return define . ErrVolumeRemoved
}
newState := new ( VolumeState )
volumeName := [ ] byte ( volume . Name ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
defer s . deferredCloseDBCon ( db )
err = db . View ( func ( tx * bolt . Tx ) error {
volBucket , err := getVolBucket ( tx )
if err != nil {
return err
}
volToUpdate := volBucket . Bucket ( volumeName )
if volToUpdate == nil {
volume . valid = false
return errors . Wrapf ( define . ErrNoSuchVolume , "no volume with name %s found in database" , volume . Name ( ) )
}
stateBytes := volToUpdate . Get ( stateKey )
if stateBytes == nil {
// Having no state is valid.
// Return nil, use the empty state.
return nil
}
if err := json . Unmarshal ( stateBytes , newState ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling volume %s state" , volume . Name ( ) )
}
return nil
} )
if err != nil {
return err
}
volume . state = newState
return nil
}
// SaveVolume saves the volume's state to the database.
func ( s * BoltState ) SaveVolume ( volume * Volume ) error {
if ! s . valid {
return define . ErrDBClosed
}
if ! volume . valid {
return define . ErrVolumeRemoved
}
volumeName := [ ] byte ( volume . Name ( ) )
var newStateJSON [ ] byte
if volume . state != nil {
stateJSON , err := json . Marshal ( volume . state )
if err != nil {
return errors . Wrapf ( err , "error marshalling volume %s state to JSON" , volume . Name ( ) )
}
newStateJSON = stateJSON
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
defer s . deferredCloseDBCon ( db )
err = db . Update ( func ( tx * bolt . Tx ) error {
volBucket , err := getVolBucket ( tx )
if err != nil {
return err
}
volToUpdate := volBucket . Bucket ( volumeName )
if volToUpdate == nil {
volume . valid = false
return errors . Wrapf ( define . ErrNoSuchVolume , "no volume with name %s found in database" , volume . Name ( ) )
}
return volToUpdate . Put ( stateKey , newStateJSON )
} )
return err
}
2018-08-08 13:50:15 +00:00
// AllVolumes returns all volumes present in the state
func ( s * BoltState ) AllVolumes ( ) ( [ ] * Volume , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
volumes := [ ] * Volume { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
allVolsBucket , err := getAllVolsBucket ( tx )
if err != nil {
return err
}
volBucket , err := getVolBucket ( tx )
if err != nil {
return err
}
err = allVolsBucket . ForEach ( func ( id , name [ ] byte ) error {
volExists := volBucket . Bucket ( id )
// This check can be removed if performance becomes an
// issue, but much less helpful errors will be produced
if volExists == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "inconsistency in state - volume %s is in all volumes bucket but volume not found" , string ( id ) )
2018-08-08 13:50:15 +00:00
}
volume := new ( Volume )
volume . config = new ( VolumeConfig )
2019-09-03 19:03:44 +00:00
volume . state = new ( VolumeState )
2018-08-08 13:50:15 +00:00
if err := s . getVolumeFromDB ( id , volume , volBucket ) ; err != nil {
2019-06-24 20:48:34 +00:00
if errors . Cause ( err ) != define . ErrNSMismatch {
2018-08-08 13:50:15 +00:00
logrus . Errorf ( "Error retrieving volume %s from the database: %v" , string ( id ) , err )
}
} else {
volumes = append ( volumes , volume )
}
return nil
} )
return err
} )
if err != nil {
return nil , err
}
return volumes , nil
}
// Volume retrieves a volume from full name
func ( s * BoltState ) Volume ( name string ) ( * Volume , error ) {
if name == "" {
2019-06-24 20:48:34 +00:00
return nil , define . ErrEmptyID
2018-08-08 13:50:15 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
volName := [ ] byte ( name )
volume := new ( Volume )
volume . config = new ( VolumeConfig )
2019-08-30 20:09:17 +00:00
volume . state = new ( VolumeState )
2018-08-08 13:50:15 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
return s . getVolumeFromDB ( volName , volume , volBkt )
} )
if err != nil {
return nil , err
}
return volume , nil
}
2019-08-27 19:25:54 +00:00
// LookupVolume locates a volume from a partial name.
func ( s * BoltState ) LookupVolume ( name string ) ( * Volume , error ) {
if name == "" {
return nil , define . ErrEmptyID
}
if ! s . valid {
return nil , define . ErrDBClosed
}
volName := [ ] byte ( name )
volume := new ( Volume )
volume . config = new ( VolumeConfig )
2019-09-11 14:17:28 +00:00
volume . state = new ( VolumeState )
2019-08-27 19:25:54 +00:00
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
defer s . deferredCloseDBCon ( db )
err = db . View ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
allVolsBkt , err := getAllVolsBucket ( tx )
if err != nil {
return err
}
// Check for exact match on name
volDB := volBkt . Bucket ( volName )
if volDB != nil {
return s . getVolumeFromDB ( volName , volume , volBkt )
}
// No exact match. Search all names.
foundMatch := false
err = allVolsBkt . ForEach ( func ( checkName , checkName2 [ ] byte ) error {
if strings . HasPrefix ( string ( checkName ) , name ) {
if foundMatch {
return errors . Wrapf ( define . ErrVolumeExists , "more than one result for volume name %q" , name )
}
foundMatch = true
volName = checkName
}
return nil
} )
if err != nil {
return err
}
if ! foundMatch {
return errors . Wrapf ( define . ErrNoSuchVolume , "no volume with name %q found" , name )
}
return s . getVolumeFromDB ( volName , volume , volBkt )
} )
if err != nil {
return nil , err
}
return volume , nil
}
2018-08-08 13:50:15 +00:00
// HasVolume returns true if the given volume exists in the state, otherwise it returns false
func ( s * BoltState ) HasVolume ( name string ) ( bool , error ) {
if name == "" {
2019-06-24 20:48:34 +00:00
return false , define . ErrEmptyID
2018-08-08 13:50:15 +00:00
}
if ! s . valid {
2019-06-24 20:48:34 +00:00
return false , define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
volName := [ ] byte ( name )
exists := false
db , err := s . getDBCon ( )
if err != nil {
return false , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
volBkt , err := getVolBucket ( tx )
if err != nil {
return err
}
volDB := volBkt . Bucket ( volName )
if volDB != nil {
exists = true
}
return nil
} )
if err != nil {
return false , err
}
return exists , nil
}
// VolumeInUse checks if any container is using the volume
// It returns a slice of the IDs of the containers using the given
// volume. If the slice is empty, no containers use the given volume
func ( s * BoltState ) VolumeInUse ( volume * Volume ) ( [ ] string , error ) {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-08-08 13:50:15 +00:00
}
if ! volume . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrVolumeRemoved
2018-08-08 13:50:15 +00:00
}
depCtrs := [ ] string { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-08-08 13:50:15 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
volBucket , err := getVolBucket ( tx )
if err != nil {
return err
}
2019-03-15 14:01:23 +00:00
ctrBucket , err := getCtrBucket ( tx )
if err != nil {
return err
}
2018-08-08 13:50:15 +00:00
volDB := volBucket . Bucket ( [ ] byte ( volume . Name ( ) ) )
if volDB == nil {
volume . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchVolume , "no volume with name %s found in DB" , volume . Name ( ) )
2018-08-08 13:50:15 +00:00
}
dependsBkt := volDB . Bucket ( volDependenciesBkt )
if dependsBkt == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "volume %s has no dependencies bucket" , volume . Name ( ) )
2018-08-08 13:50:15 +00:00
}
// Iterate through and add dependencies
err = dependsBkt . ForEach ( func ( id , value [ ] byte ) error {
2019-03-15 14:01:23 +00:00
// Look up all dependencies and see that they
// still exist before appending.
ctrExists := ctrBucket . Bucket ( id )
if ctrExists == nil {
return nil
}
2018-08-08 13:50:15 +00:00
depCtrs = append ( depCtrs , string ( id ) )
return nil
} )
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
return depCtrs , nil
}
2018-02-09 21:02:45 +00:00
// AddPod adds the given pod to the state.
func ( s * BoltState ) AddPod ( pod * Pod ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( pod . ID ( ) )
podName := [ ] byte ( pod . Name ( ) )
2018-06-25 17:27:57 +00:00
var podNamespace [ ] byte
if pod . config . Namespace != "" {
podNamespace = [ ] byte ( pod . config . Namespace )
}
2018-05-14 23:30:11 +00:00
podConfigJSON , err := json . Marshal ( pod . config )
2018-02-09 21:02:45 +00:00
if err != nil {
2018-05-14 23:30:11 +00:00
return errors . Wrapf ( err , "error marshalling pod %s config to JSON" , pod . ID ( ) )
}
podStateJSON , err := json . Marshal ( pod . state )
if err != nil {
return errors . Wrapf ( err , "error marshalling pod %s state to JSON" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
2018-02-10 21:44:38 +00:00
allPodsBkt , err := getAllPodsBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
idsBkt , err := getIDBucket ( tx )
if err != nil {
return err
}
namesBkt , err := getNamesBucket ( tx )
if err != nil {
return err
}
2018-07-03 15:12:00 +00:00
nsBkt , err := getNSBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
// Check if we already have something with the given ID and name
idExist := idsBkt . Get ( podID )
if idExist != nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrPodExists , "ID %s is in use" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
nameExist := namesBkt . Get ( podName )
if nameExist != nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrPodExists , "name %s is in use" , pod . Name ( ) )
2018-02-09 21:02:45 +00:00
}
// We are good to add the pod
// Make a bucket for it
newPod , err := podBkt . CreateBucket ( podID )
if err != nil {
return errors . Wrapf ( err , "error creating bucket for pod %s" , pod . ID ( ) )
}
// Make a subbucket for pod containers
if _ , err := newPod . CreateBucket ( containersBkt ) ; err != nil {
return errors . Wrapf ( err , "error creating bucket for pod %s containers" , pod . ID ( ) )
}
2018-05-14 23:30:11 +00:00
if err := newPod . Put ( configKey , podConfigJSON ) ; err != nil {
2018-02-09 21:02:45 +00:00
return errors . Wrapf ( err , "error storing pod %s configuration in DB" , pod . ID ( ) )
}
2018-05-14 23:30:11 +00:00
if err := newPod . Put ( stateKey , podStateJSON ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s state JSON in DB" , pod . ID ( ) )
}
2018-06-25 17:27:57 +00:00
if podNamespace != nil {
if err := newPod . Put ( namespaceKey , podNamespace ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s namespace in DB" , pod . ID ( ) )
}
2018-07-03 15:12:00 +00:00
if err := nsBkt . Put ( podID , podNamespace ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s namespace in DB" , pod . ID ( ) )
}
2018-06-25 17:27:57 +00:00
}
2018-02-09 21:02:45 +00:00
// Add us to the ID and names buckets
if err := idsBkt . Put ( podID , podName ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s ID in DB" , pod . ID ( ) )
}
if err := namesBkt . Put ( podName , podID ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s name in DB" , pod . Name ( ) )
}
2018-02-10 21:44:38 +00:00
if err := allPodsBkt . Put ( podID , podName ) ; err != nil {
return errors . Wrapf ( err , "error storing pod %s in all pods bucket in DB" , pod . ID ( ) )
}
2018-02-09 21:02:45 +00:00
return nil
} )
if err != nil {
return err
}
return nil
}
// RemovePod removes the given pod from the state
// Only empty pods can be removed
2017-12-14 03:02:15 +00:00
func ( s * BoltState ) RemovePod ( pod * Pod ) error {
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( pod . ID ( ) )
podName := [ ] byte ( pod . Name ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
2018-02-10 21:44:38 +00:00
allPodsBkt , err := getAllPodsBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
idsBkt , err := getIDBucket ( tx )
if err != nil {
return err
}
namesBkt , err := getNamesBucket ( tx )
if err != nil {
return err
}
2018-07-03 15:12:00 +00:00
nsBkt , err := getNSBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
// Check if the pod exists
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "pod %s does not exist in DB" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Check if pod is empty
// This should never be nil
// But if it is, we can assume there are no containers in the
// pod.
// So let's eject the malformed pod without error.
podCtrsBkt := podDB . Bucket ( containersBkt )
if podCtrsBkt != nil {
cursor := podCtrsBkt . Cursor ( )
if id , _ := cursor . First ( ) ; id != nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrCtrExists , "pod %s is not empty" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
}
// Pod is empty, and ready for removal
// Let's kick it out
if err := idsBkt . Delete ( podID ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s ID from DB" , pod . ID ( ) )
}
if err := namesBkt . Delete ( podName ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s name (%s) from DB" , pod . ID ( ) , pod . Name ( ) )
}
2018-07-03 15:12:00 +00:00
if err := nsBkt . Delete ( podID ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s namespace from DB" , pod . ID ( ) )
}
2018-02-10 21:44:38 +00:00
if err := allPodsBkt . Delete ( podID ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s ID from all pods bucket in DB" , pod . ID ( ) )
}
2018-02-09 21:02:45 +00:00
if err := podBkt . DeleteBucket ( podID ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s from DB" , pod . ID ( ) )
}
return nil
} )
if err != nil {
return err
}
return nil
2017-12-14 03:02:15 +00:00
}
// RemovePodContainers removes all containers in a pod
func ( s * BoltState ) RemovePodContainers ( pod * Pod ) error {
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-02-09 21:02:45 +00:00
podID := [ ] byte ( pod . ID ( ) )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
ctrBkt , err := getCtrBucket ( tx )
if err != nil {
return err
}
2018-02-10 21:44:38 +00:00
allCtrsBkt , err := getAllCtrsBucket ( tx )
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
idsBkt , err := getIDBucket ( tx )
if err != nil {
return err
}
namesBkt , err := getNamesBucket ( tx )
if err != nil {
return err
}
// Check if the pod exists
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "pod %s does not exist in DB" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
podCtrsBkt := podDB . Bucket ( containersBkt )
if podCtrsBkt == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s does not have a containers bucket" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
// Traverse all containers in the pod with a cursor
// for-each has issues with data mutation
2018-02-09 22:13:07 +00:00
err = podCtrsBkt . ForEach ( func ( id , name [ ] byte ) error {
2018-02-09 21:02:45 +00:00
// Get the container so we can check dependencies
ctr := ctrBkt . Bucket ( id )
if ctr == nil {
// This should never happen
// State is inconsistent
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "pod %s referenced nonexistant container %s" , pod . ID ( ) , string ( id ) )
2018-02-09 21:02:45 +00:00
}
ctrDeps := ctr . Bucket ( dependenciesBkt )
// This should never be nil, but if it is, we're
// removing it anyways, so continue if it is
if ctrDeps != nil {
err = ctrDeps . ForEach ( func ( depID , name [ ] byte ) error {
exists := podCtrsBkt . Get ( depID )
if exists == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrCtrExists , "container %s has dependency %s outside of pod %s" , string ( id ) , string ( depID ) , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
return nil
} )
2018-02-09 22:43:05 +00:00
if err != nil {
return err
}
2018-02-09 21:02:45 +00:00
}
// Dependencies are set, we're clear to remove
if err := ctrBkt . DeleteBucket ( id ) ; err != nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "error deleting container %s from DB" , string ( id ) )
2018-02-09 21:02:45 +00:00
}
if err := idsBkt . Delete ( id ) ; err != nil {
return errors . Wrapf ( err , "error deleting container %s ID in DB" , string ( id ) )
}
if err := namesBkt . Delete ( name ) ; err != nil {
return errors . Wrapf ( err , "error deleting container %s name in DB" , string ( id ) )
}
2018-02-10 21:44:38 +00:00
if err := allCtrsBkt . Delete ( id ) ; err != nil {
return errors . Wrapf ( err , "error deleting container %s ID from all containers bucket in DB" , string ( id ) )
}
2018-02-09 22:13:07 +00:00
return nil
} )
if err != nil {
return err
}
// Delete and recreate the bucket to empty it
if err := podDB . DeleteBucket ( containersBkt ) ; err != nil {
return errors . Wrapf ( err , "error removing pod %s containers bucket" , pod . ID ( ) )
}
if _ , err := podDB . CreateBucket ( containersBkt ) ; err != nil {
return errors . Wrapf ( err , "error recreating pod %s containers bucket" , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
return nil
} )
if err != nil {
return err
}
return nil
2017-12-14 03:02:15 +00:00
}
// AddContainerToPod adds the given container to an existing pod
// The container will be added to the state and the pod
func ( s * BoltState ) AddContainerToPod ( pod * Pod , ctr * Container ) error {
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
if ! ctr . valid {
2019-06-24 20:48:34 +00:00
return define . ErrCtrRemoved
2018-02-09 21:02:45 +00:00
}
if ctr . config . Pod != pod . ID ( ) {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchCtr , "container %s is not part of pod %s" , ctr . ID ( ) , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
return s . addContainer ( ctr , pod )
2017-12-14 03:02:15 +00:00
}
// RemoveContainerFromPod removes a container from an existing pod
// The container will also be removed from the state
func ( s * BoltState ) RemoveContainerFromPod ( pod * Pod , ctr * Container ) error {
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-02-09 21:02:45 +00:00
}
2018-07-03 15:12:00 +00:00
if s . namespace != "" {
if s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
if s . namespace != ctr . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "container %s in in namespace %q but we are in namespace %q" , ctr . ID ( ) , ctr . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
}
2018-02-09 21:02:45 +00:00
if ctr . config . Pod == "" {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "container %s is not part of a pod, use RemoveContainer instead" , ctr . ID ( ) )
2018-02-09 21:02:45 +00:00
}
if ctr . config . Pod != pod . ID ( ) {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInvalidArg , "container %s is not part of pod %s" , ctr . ID ( ) , pod . ID ( ) )
2018-02-09 21:02:45 +00:00
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . Update ( func ( tx * bolt . Tx ) error {
2018-07-03 15:12:00 +00:00
return s . removeContainer ( ctr , pod , tx )
2018-02-09 21:02:45 +00:00
} )
return err
2017-12-14 03:02:15 +00:00
}
2018-05-14 23:30:11 +00:00
// UpdatePod updates a pod's state from the database
func ( s * BoltState ) UpdatePod ( pod * Pod ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-05-14 23:30:11 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-05-14 23:30:11 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-05-14 23:30:11 +00:00
newState := new ( podState )
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-05-14 23:30:11 +00:00
podID := [ ] byte ( pod . ID ( ) )
err = db . View ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "no pod with ID %s found in database" , pod . ID ( ) )
2018-05-14 23:30:11 +00:00
}
// Get the pod state JSON
podStateBytes := podDB . Get ( stateKey )
if podStateBytes == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "pod %s is missing state key in DB" , pod . ID ( ) )
2018-05-14 23:30:11 +00:00
}
if err := json . Unmarshal ( podStateBytes , newState ) ; err != nil {
return errors . Wrapf ( err , "error unmarshalling pod %s state JSON" , pod . ID ( ) )
}
return nil
} )
if err != nil {
return err
}
pod . state = newState
return nil
}
// SavePod saves a pod's state to the database
func ( s * BoltState ) SavePod ( pod * Pod ) error {
if ! s . valid {
2019-06-24 20:48:34 +00:00
return define . ErrDBClosed
2018-05-14 23:30:11 +00:00
}
if ! pod . valid {
2019-06-24 20:48:34 +00:00
return define . ErrPodRemoved
2018-05-14 23:30:11 +00:00
}
2018-07-19 21:21:27 +00:00
if s . namespace != "" && s . namespace != pod . config . Namespace {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNSMismatch , "pod %s is in namespace %q but we are in namespace %q" , pod . ID ( ) , pod . config . Namespace , s . namespace )
2018-07-03 15:12:00 +00:00
}
2018-05-14 23:30:11 +00:00
stateJSON , err := json . Marshal ( pod . state )
if err != nil {
return errors . Wrapf ( err , "error marshalling pod %s state to JSON" , pod . ID ( ) )
}
db , err := s . getDBCon ( )
if err != nil {
return err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-05-14 23:30:11 +00:00
podID := [ ] byte ( pod . ID ( ) )
err = db . Update ( func ( tx * bolt . Tx ) error {
podBkt , err := getPodBucket ( tx )
if err != nil {
return err
}
podDB := podBkt . Bucket ( podID )
if podDB == nil {
pod . valid = false
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrNoSuchPod , "no pod with ID %s found in database" , pod . ID ( ) )
2018-05-14 23:30:11 +00:00
}
// Set the pod state JSON
if err := podDB . Put ( stateKey , stateJSON ) ; err != nil {
return errors . Wrapf ( err , "error updating pod %s state in database" , pod . ID ( ) )
}
return nil
} )
if err != nil {
return err
}
return nil
}
2017-12-14 03:02:15 +00:00
// AllPods returns all pods present in the state
func ( s * BoltState ) AllPods ( ) ( [ ] * Pod , error ) {
2018-02-09 21:02:45 +00:00
if ! s . valid {
2019-06-24 20:48:34 +00:00
return nil , define . ErrDBClosed
2018-02-09 21:02:45 +00:00
}
pods := [ ] * Pod { }
db , err := s . getDBCon ( )
if err != nil {
return nil , err
}
2019-07-08 18:20:17 +00:00
defer s . deferredCloseDBCon ( db )
2018-02-09 21:02:45 +00:00
err = db . View ( func ( tx * bolt . Tx ) error {
2018-02-10 21:44:38 +00:00
allPodsBucket , err := getAllPodsBucket ( tx )
2018-02-09 21:02:45 +00:00
if err != nil {
return err
}
podBucket , err := getPodBucket ( tx )
if err != nil {
return err
}
2018-02-10 21:44:38 +00:00
err = allPodsBucket . ForEach ( func ( id , name [ ] byte ) error {
2018-02-09 21:02:45 +00:00
podExists := podBucket . Bucket ( id )
2018-02-10 21:44:38 +00:00
// This check can be removed if performance becomes an
// issue, but much less helpful errors will be produced
2018-02-09 21:02:45 +00:00
if podExists == nil {
2019-06-24 20:48:34 +00:00
return errors . Wrapf ( define . ErrInternal , "inconsistency in state - pod %s is in all pods bucket but pod not found" , string ( id ) )
2018-02-09 21:02:45 +00:00
}
pod := new ( Pod )
2018-02-09 22:13:07 +00:00
pod . config = new ( PodConfig )
2018-05-14 23:30:11 +00:00
pod . state = new ( podState )
2018-02-09 21:02:45 +00:00
2018-07-03 15:12:00 +00:00
if err := s . getPodFromDB ( id , pod , podBucket ) ; err != nil {
2019-06-24 20:48:34 +00:00
if errors . Cause ( err ) != define . ErrNSMismatch {
2018-08-17 18:21:21 +00:00
logrus . Errorf ( "Error retrieving pod %s from the database: %v" , string ( id ) , err )
2018-07-03 15:12:00 +00:00
}
} else {
pods = append ( pods , pod )
}
2018-02-09 21:02:45 +00:00
2018-07-03 15:12:00 +00:00
return nil
2018-02-09 21:02:45 +00:00
} )
return err
} )
if err != nil {
return nil , err
}
return pods , nil
2017-12-14 03:02:15 +00:00
}