765 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			765 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
| package libpod
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"runtime"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/boltdb/bolt"
 | |
| 	"github.com/containers/storage"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	idRegistryName    = "id-registry"
 | |
| 	nameRegistryName  = "name-registry"
 | |
| 	nsRegistryName    = "ns-registry"
 | |
| 	ctrName           = "ctr"
 | |
| 	allCtrsName       = "all-ctrs"
 | |
| 	podName           = "pod"
 | |
| 	allPodsName       = "allPods"
 | |
| 	volName           = "vol"
 | |
| 	allVolsName       = "allVolumes"
 | |
| 	runtimeConfigName = "runtime-config"
 | |
| 
 | |
| 	configName         = "config"
 | |
| 	stateName          = "state"
 | |
| 	dependenciesName   = "dependencies"
 | |
| 	volCtrDependencies = "vol-dependencies"
 | |
| 	netNSName          = "netns"
 | |
| 	containersName     = "containers"
 | |
| 	podIDName          = "pod-id"
 | |
| 	namespaceName      = "namespace"
 | |
| 
 | |
| 	staticDirName   = "static-dir"
 | |
| 	tmpDirName      = "tmp-dir"
 | |
| 	runRootName     = "run-root"
 | |
| 	graphRootName   = "graph-root"
 | |
| 	graphDriverName = "graph-driver-name"
 | |
| 	osName          = "os"
 | |
| 	volPathName     = "volume-path"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	idRegistryBkt    = []byte(idRegistryName)
 | |
| 	nameRegistryBkt  = []byte(nameRegistryName)
 | |
| 	nsRegistryBkt    = []byte(nsRegistryName)
 | |
| 	ctrBkt           = []byte(ctrName)
 | |
| 	allCtrsBkt       = []byte(allCtrsName)
 | |
| 	podBkt           = []byte(podName)
 | |
| 	allPodsBkt       = []byte(allPodsName)
 | |
| 	volBkt           = []byte(volName)
 | |
| 	allVolsBkt       = []byte(allVolsName)
 | |
| 	runtimeConfigBkt = []byte(runtimeConfigName)
 | |
| 
 | |
| 	configKey          = []byte(configName)
 | |
| 	stateKey           = []byte(stateName)
 | |
| 	dependenciesBkt    = []byte(dependenciesName)
 | |
| 	volDependenciesBkt = []byte(volCtrDependencies)
 | |
| 	netNSKey           = []byte(netNSName)
 | |
| 	containersBkt      = []byte(containersName)
 | |
| 	podIDKey           = []byte(podIDName)
 | |
| 	namespaceKey       = []byte(namespaceName)
 | |
| 
 | |
| 	staticDirKey   = []byte(staticDirName)
 | |
| 	tmpDirKey      = []byte(tmpDirName)
 | |
| 	runRootKey     = []byte(runRootName)
 | |
| 	graphRootKey   = []byte(graphRootName)
 | |
| 	graphDriverKey = []byte(graphDriverName)
 | |
| 	osKey          = []byte(osName)
 | |
| 	volPathKey     = []byte(volPathName)
 | |
| )
 | |
| 
 | |
| // Check if the configuration of the database is compatible with the
 | |
| // configuration of the runtime opening it
 | |
| // If there is no runtime configuration loaded, load our own
 | |
| func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
 | |
| 	err := db.Update(func(tx *bolt.Tx) error {
 | |
| 		configBkt, err := getRuntimeConfigBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "OS", runtime.GOOS, osKey, runtime.GOOS); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "libpod root directory (staticdir)",
 | |
| 			rt.config.StaticDir, staticDirKey, ""); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "libpod temporary files directory (tmpdir)",
 | |
| 			rt.config.TmpDir, tmpDirKey, ""); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "storage temporary directory (runroot)",
 | |
| 			rt.config.StorageConfig.RunRoot, runRootKey,
 | |
| 			storage.DefaultStoreOptions.RunRoot); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "storage graph root directory (graphroot)",
 | |
| 			rt.config.StorageConfig.GraphRoot, graphRootKey,
 | |
| 			storage.DefaultStoreOptions.GraphRoot); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if err := validateDBAgainstConfig(configBkt, "storage graph driver",
 | |
| 			rt.config.StorageConfig.GraphDriverName,
 | |
| 			graphDriverKey,
 | |
| 			storage.DefaultStoreOptions.GraphDriverName); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		return validateDBAgainstConfig(configBkt, "volume path",
 | |
| 			rt.config.VolumePath, volPathKey, "")
 | |
| 	})
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Validate a configuration entry in the DB against current runtime config
 | |
| // If the given configuration key does not exist it will be created
 | |
| // If the given runtimeValue or value retrieved from the database are the empty
 | |
| // string and defaultValue is not, defaultValue will be checked instead. This
 | |
| // ensures that we will not fail on configuration changes in configured c/storage.
 | |
| func validateDBAgainstConfig(bucket *bolt.Bucket, fieldName, runtimeValue string, keyName []byte, defaultValue string) error {
 | |
| 	keyBytes := bucket.Get(keyName)
 | |
| 	if keyBytes == nil {
 | |
| 		dbValue := []byte(runtimeValue)
 | |
| 		if runtimeValue == "" && defaultValue != "" {
 | |
| 			dbValue = []byte(defaultValue)
 | |
| 		}
 | |
| 
 | |
| 		if err := bucket.Put(keyName, dbValue); err != nil {
 | |
| 			return errors.Wrapf(err, "error updating %s in DB runtime config", fieldName)
 | |
| 		}
 | |
| 	} else {
 | |
| 		if runtimeValue != string(keyBytes) {
 | |
| 			// If runtimeValue is the empty string, check against
 | |
| 			// the default
 | |
| 			if runtimeValue == "" && defaultValue != "" &&
 | |
| 				string(keyBytes) == defaultValue {
 | |
| 				return nil
 | |
| 			}
 | |
| 
 | |
| 			// If DB value is the empty string, check that the
 | |
| 			// runtime value is the default
 | |
| 			if string(keyBytes) == "" && defaultValue != "" &&
 | |
| 				runtimeValue == defaultValue {
 | |
| 				return nil
 | |
| 			}
 | |
| 
 | |
| 			return errors.Wrapf(ErrDBBadConfig, "database %s %s does not match our %s %s",
 | |
| 				fieldName, string(keyBytes), fieldName, runtimeValue)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Open a connection to the database.
 | |
| // Must be paired with a `defer closeDBCon()` on the returned database, to
 | |
| // ensure the state is properly unlocked
 | |
| func (s *BoltState) getDBCon() (*bolt.DB, error) {
 | |
| 	// We need an in-memory lock to avoid issues around POSIX file advisory
 | |
| 	// locks as described in the link below:
 | |
| 	// https://www.sqlite.org/src/artifact/c230a7a24?ln=994-1081
 | |
| 	s.dbLock.Lock()
 | |
| 
 | |
| 	db, err := bolt.Open(s.dbPath, 0600, nil)
 | |
| 	if err != nil {
 | |
| 		return nil, errors.Wrapf(err, "error opening database %s", s.dbPath)
 | |
| 	}
 | |
| 
 | |
| 	return db, nil
 | |
| }
 | |
| 
 | |
| // Close a connection to the database.
 | |
| // MUST be used in place of `db.Close()` to ensure proper unlocking of the
 | |
| // state.
 | |
| func (s *BoltState) closeDBCon(db *bolt.DB) error {
 | |
| 	err := db.Close()
 | |
| 
 | |
| 	s.dbLock.Unlock()
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func getIDBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(idRegistryBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "id registry bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getNamesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(nameRegistryBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "name registry bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getNSBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(nsRegistryBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "namespace registry bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getCtrBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(ctrBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "containers bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getAllCtrsBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(allCtrsBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "all containers bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getPodBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(podBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "pods bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getAllPodsBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(allPodsBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "all pods bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getVolBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(volBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "volumes bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getAllVolsBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(allVolsBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "all volumes bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
 | |
| 	bkt := tx.Bucket(runtimeConfigBkt)
 | |
| 	if bkt == nil {
 | |
| 		return nil, errors.Wrapf(ErrDBBadConfig, "runtime configuration bucket not found in DB")
 | |
| 	}
 | |
| 	return bkt, nil
 | |
| }
 | |
| 
 | |
| func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error {
 | |
| 	valid := true
 | |
| 	ctrBkt := ctrsBkt.Bucket(id)
 | |
| 	if ctrBkt == nil {
 | |
| 		return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id))
 | |
| 	}
 | |
| 
 | |
| 	if s.namespaceBytes != nil {
 | |
| 		ctrNamespaceBytes := ctrBkt.Get(namespaceKey)
 | |
| 		if !bytes.Equal(s.namespaceBytes, ctrNamespaceBytes) {
 | |
| 			return errors.Wrapf(ErrNSMismatch, "cannot retrieve container %s as it is part of namespace %q and we are in namespace %q", string(id), string(ctrNamespaceBytes), s.namespace)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	configBytes := ctrBkt.Get(configKey)
 | |
| 	if configBytes == nil {
 | |
| 		return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id))
 | |
| 	}
 | |
| 
 | |
| 	if err := json.Unmarshal(configBytes, ctr.config); err != nil {
 | |
| 		return errors.Wrapf(err, "error unmarshalling container %s config", string(id))
 | |
| 	}
 | |
| 
 | |
| 	// Get the lock
 | |
| 	lock, err := s.runtime.lockManager.RetrieveLock(ctr.config.LockID)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrapf(err, "error retrieving lock for container %s", string(id))
 | |
| 	}
 | |
| 	ctr.lock = lock
 | |
| 
 | |
| 	ctr.runtime = s.runtime
 | |
| 	ctr.valid = valid
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error {
 | |
| 	podDB := podBkt.Bucket(id)
 | |
| 	if podDB == nil {
 | |
| 		return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found", string(id))
 | |
| 	}
 | |
| 
 | |
| 	if s.namespaceBytes != nil {
 | |
| 		podNamespaceBytes := podDB.Get(namespaceKey)
 | |
| 		if !bytes.Equal(s.namespaceBytes, podNamespaceBytes) {
 | |
| 			return errors.Wrapf(ErrNSMismatch, "cannot retrieve pod %s as it is part of namespace %q and we are in namespace %q", string(id), string(podNamespaceBytes), s.namespace)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	podConfigBytes := podDB.Get(configKey)
 | |
| 	if podConfigBytes == nil {
 | |
| 		return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
 | |
| 	}
 | |
| 
 | |
| 	if err := json.Unmarshal(podConfigBytes, pod.config); err != nil {
 | |
| 		return errors.Wrapf(err, "error unmarshalling pod %s config from DB", string(id))
 | |
| 	}
 | |
| 
 | |
| 	// Get the lock
 | |
| 	lock, err := s.runtime.lockManager.RetrieveLock(pod.config.LockID)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrapf(err, "error retrieving lock for pod %s", string(id))
 | |
| 	}
 | |
| 	pod.lock = lock
 | |
| 
 | |
| 	pod.runtime = s.runtime
 | |
| 	pod.valid = true
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s *BoltState) getVolumeFromDB(name []byte, volume *Volume, volBkt *bolt.Bucket) error {
 | |
| 	volDB := volBkt.Bucket(name)
 | |
| 	if volDB == nil {
 | |
| 		return errors.Wrapf(ErrNoSuchVolume, "volume with name %s not found", string(name))
 | |
| 	}
 | |
| 
 | |
| 	volConfigBytes := volDB.Get(configKey)
 | |
| 	if volConfigBytes == nil {
 | |
| 		return errors.Wrapf(ErrInternal, "volume %s is missing configuration key in DB", string(name))
 | |
| 	}
 | |
| 
 | |
| 	if err := json.Unmarshal(volConfigBytes, volume.config); err != nil {
 | |
| 		return errors.Wrapf(err, "error unmarshalling volume %s config from DB", string(name))
 | |
| 	}
 | |
| 
 | |
| 	volume.runtime = s.runtime
 | |
| 	volume.valid = true
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Add a container to the DB
 | |
| // If pod is not nil, the container is added to the pod as well
 | |
| func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
 | |
| 	if s.namespace != "" && s.namespace != ctr.config.Namespace {
 | |
| 		return errors.Wrapf(ErrNSMismatch, "cannot add container %s as it is in namespace %q and we are in namespace %q",
 | |
| 			ctr.ID(), s.namespace, ctr.config.Namespace)
 | |
| 	}
 | |
| 
 | |
| 	// JSON container structs to insert into DB
 | |
| 	// TODO use a higher-performance struct encoding than JSON
 | |
| 	configJSON, err := json.Marshal(ctr.config)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrapf(err, "error marshalling container %s config to JSON", ctr.ID())
 | |
| 	}
 | |
| 	stateJSON, err := json.Marshal(ctr.state)
 | |
| 	if err != nil {
 | |
| 		return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID())
 | |
| 	}
 | |
| 	netNSPath := getNetNSPath(ctr)
 | |
| 	dependsCtrs := ctr.Dependencies()
 | |
| 
 | |
| 	ctrID := []byte(ctr.ID())
 | |
| 	ctrName := []byte(ctr.Name())
 | |
| 
 | |
| 	var ctrNamespace []byte
 | |
| 	if ctr.config.Namespace != "" {
 | |
| 		ctrNamespace = []byte(ctr.config.Namespace)
 | |
| 	}
 | |
| 
 | |
| 	db, err := s.getDBCon()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.closeDBCon(db)
 | |
| 
 | |
| 	err = db.Update(func(tx *bolt.Tx) error {
 | |
| 		idsBucket, err := getIDBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		namesBucket, err := getNamesBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		nsBucket, err := getNSBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		ctrBucket, err := getCtrBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		allCtrsBucket, err := getAllCtrsBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		volBkt, err := getVolBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		// If a pod was given, check if it exists
 | |
| 		var podDB *bolt.Bucket
 | |
| 		var podCtrs *bolt.Bucket
 | |
| 		if pod != nil {
 | |
| 			podBucket, err := getPodBucket(tx)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			podID := []byte(pod.ID())
 | |
| 
 | |
| 			podDB = podBucket.Bucket(podID)
 | |
| 			if podDB == nil {
 | |
| 				pod.valid = false
 | |
| 				return errors.Wrapf(ErrNoSuchPod, "pod %s does not exist in database", pod.ID())
 | |
| 			}
 | |
| 			podCtrs = podDB.Bucket(containersBkt)
 | |
| 			if podCtrs == nil {
 | |
| 				return errors.Wrapf(ErrInternal, "pod %s does not have a containers bucket", pod.ID())
 | |
| 			}
 | |
| 
 | |
| 			podNS := podDB.Get(namespaceKey)
 | |
| 			if !bytes.Equal(podNS, ctrNamespace) {
 | |
| 				return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and pod %s is in namespace %s",
 | |
| 					ctr.ID(), ctr.config.Namespace, pod.ID(), pod.config.Namespace)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Check if we already have a container with the given ID and name
 | |
| 		idExist := idsBucket.Get(ctrID)
 | |
| 		if idExist != nil {
 | |
| 			return errors.Wrapf(ErrCtrExists, "ID %s is in use", ctr.ID())
 | |
| 		}
 | |
| 		nameExist := namesBucket.Get(ctrName)
 | |
| 		if nameExist != nil {
 | |
| 			return errors.Wrapf(ErrCtrExists, "name %s is in use", ctr.Name())
 | |
| 		}
 | |
| 
 | |
| 		// No overlapping containers
 | |
| 		// Add the new container to the DB
 | |
| 		if err := idsBucket.Put(ctrID, ctrName); err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s ID to DB", ctr.ID())
 | |
| 		}
 | |
| 		if err := namesBucket.Put(ctrName, ctrID); err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s name (%s) to DB", ctr.ID(), ctr.Name())
 | |
| 		}
 | |
| 		if ctrNamespace != nil {
 | |
| 			if err := nsBucket.Put(ctrID, ctrNamespace); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding container %s namespace (%q) to DB", ctr.ID(), ctr.Namespace())
 | |
| 			}
 | |
| 		}
 | |
| 		if err := allCtrsBucket.Put(ctrID, ctrName); err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
 | |
| 		}
 | |
| 
 | |
| 		newCtrBkt, err := ctrBucket.CreateBucket(ctrID)
 | |
| 		if err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s bucket to DB", ctr.ID())
 | |
| 		}
 | |
| 
 | |
| 		if err := newCtrBkt.Put(configKey, configJSON); err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s config to DB", ctr.ID())
 | |
| 		}
 | |
| 		if err := newCtrBkt.Put(stateKey, stateJSON); err != nil {
 | |
| 			return errors.Wrapf(err, "error adding container %s state to DB", ctr.ID())
 | |
| 		}
 | |
| 		if ctrNamespace != nil {
 | |
| 			if err := newCtrBkt.Put(namespaceKey, ctrNamespace); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding container %s namespace to DB", ctr.ID())
 | |
| 			}
 | |
| 		}
 | |
| 		if pod != nil {
 | |
| 			if err := newCtrBkt.Put(podIDKey, []byte(pod.ID())); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding container %s pod to DB", ctr.ID())
 | |
| 			}
 | |
| 		}
 | |
| 		if netNSPath != "" {
 | |
| 			if err := newCtrBkt.Put(netNSKey, []byte(netNSPath)); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding container %s netns path to DB", ctr.ID())
 | |
| 			}
 | |
| 		}
 | |
| 		if _, err := newCtrBkt.CreateBucket(dependenciesBkt); err != nil {
 | |
| 			return errors.Wrapf(err, "error creating dependencies bucket for container %s", ctr.ID())
 | |
| 		}
 | |
| 
 | |
| 		// Add dependencies for the container
 | |
| 		for _, dependsCtr := range dependsCtrs {
 | |
| 			depCtrID := []byte(dependsCtr)
 | |
| 
 | |
| 			depCtrBkt := ctrBucket.Bucket(depCtrID)
 | |
| 			if depCtrBkt == nil {
 | |
| 				return errors.Wrapf(ErrNoSuchCtr, "container %s depends on container %s, but it does not exist in the DB", ctr.ID(), dependsCtr)
 | |
| 			}
 | |
| 
 | |
| 			depCtrPod := depCtrBkt.Get(podIDKey)
 | |
| 			if pod != nil {
 | |
| 				// If we're part of a pod, make sure the dependency is part of the same pod
 | |
| 				if depCtrPod == nil {
 | |
| 					return errors.Wrapf(ErrInvalidArg, "container %s depends on container %s which is not in pod %s", ctr.ID(), dependsCtr, pod.ID())
 | |
| 				}
 | |
| 
 | |
| 				if string(depCtrPod) != pod.ID() {
 | |
| 					return errors.Wrapf(ErrInvalidArg, "container %s depends on container %s which is in a different pod (%s)", ctr.ID(), dependsCtr, string(depCtrPod))
 | |
| 				}
 | |
| 			} else {
 | |
| 				// If we're not part of a pod, we cannot depend on containers in a pod
 | |
| 				if depCtrPod != nil {
 | |
| 					return errors.Wrapf(ErrInvalidArg, "container %s depends on container %s which is in a pod - containers not in pods cannot depend on containers in pods", ctr.ID(), dependsCtr)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			depNamespace := depCtrBkt.Get(namespaceKey)
 | |
| 			if !bytes.Equal(ctrNamespace, depNamespace) {
 | |
| 				return errors.Wrapf(ErrNSMismatch, "container %s in namespace %q depends on container %s in namespace %q - namespaces must match", ctr.ID(), ctr.config.Namespace, dependsCtr, string(depNamespace))
 | |
| 			}
 | |
| 
 | |
| 			depCtrDependsBkt := depCtrBkt.Bucket(dependenciesBkt)
 | |
| 			if depCtrDependsBkt == nil {
 | |
| 				return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", dependsCtr)
 | |
| 			}
 | |
| 			if err := depCtrDependsBkt.Put(ctrID, ctrName); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding ctr %s as dependency of container %s", ctr.ID(), dependsCtr)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Add ctr to pod
 | |
| 		if pod != nil {
 | |
| 			if err := podCtrs.Put(ctrID, ctrName); err != nil {
 | |
| 				return errors.Wrapf(err, "error adding container %s to pod %s", ctr.ID(), pod.ID())
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Add container to volume dependencies bucket if container is using a named volume
 | |
| 		if ctr.runtime.config.VolumePath == "" {
 | |
| 			return nil
 | |
| 		}
 | |
| 		for _, vol := range ctr.config.Spec.Mounts {
 | |
| 			if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
 | |
| 				volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
 | |
| 				volDB := volBkt.Bucket([]byte(volName))
 | |
| 				if volDB == nil {
 | |
| 					return errors.Wrapf(ErrNoSuchVolume, "no volume with name %s found in database", volName)
 | |
| 				}
 | |
| 
 | |
| 				ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
 | |
| 				if depExists := ctrDepsBkt.Get(ctrID); depExists == nil {
 | |
| 					if err := ctrDepsBkt.Put(ctrID, ctrID); err != nil {
 | |
| 						return errors.Wrapf(err, "error storing container dependencies %q for volume %s in ctrDependencies bucket in DB", ctr.ID(), volName)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return nil
 | |
| 	})
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Remove a container from the DB
 | |
| // If pod is not nil, the container is treated as belonging to a pod, and
 | |
| // will be removed from the pod as well
 | |
| func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
 | |
| 	ctrID := []byte(ctr.ID())
 | |
| 	ctrName := []byte(ctr.Name())
 | |
| 
 | |
| 	idsBucket, err := getIDBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	namesBucket, err := getNamesBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	ctrBucket, err := getCtrBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	nsBucket, err := getNSBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	allCtrsBucket, err := getAllCtrsBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	volBkt, err := getVolBucket(tx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Does the pod exist?
 | |
| 	var podDB *bolt.Bucket
 | |
| 	if pod != nil {
 | |
| 		podBucket, err := getPodBucket(tx)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		podID := []byte(pod.ID())
 | |
| 
 | |
| 		podDB = podBucket.Bucket(podID)
 | |
| 		if podDB == nil {
 | |
| 			pod.valid = false
 | |
| 			return errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in DB", pod.ID())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Does the container exist?
 | |
| 	ctrExists := ctrBucket.Bucket(ctrID)
 | |
| 	if ctrExists == nil {
 | |
| 		ctr.valid = false
 | |
| 		return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
 | |
| 	}
 | |
| 
 | |
| 	// Compare namespace
 | |
| 	// We can't remove containers not in our namespace
 | |
| 	if s.namespace != "" {
 | |
| 		if s.namespace != ctr.config.Namespace {
 | |
| 			return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
 | |
| 		}
 | |
| 		if pod != nil && s.namespace != pod.config.Namespace {
 | |
| 			return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q, does not match out namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if podDB != nil {
 | |
| 		// Check if the container is in the pod, remove it if it is
 | |
| 		podCtrs := podDB.Bucket(containersBkt)
 | |
| 		if podCtrs == nil {
 | |
| 			// Malformed pod
 | |
| 			logrus.Errorf("pod %s malformed in database, missing containers bucket!", pod.ID())
 | |
| 		} else {
 | |
| 			ctrInPod := podCtrs.Get(ctrID)
 | |
| 			if ctrInPod == nil {
 | |
| 				return errors.Wrapf(ErrNoSuchCtr, "container %s is not in pod %s", ctr.ID(), pod.ID())
 | |
| 			}
 | |
| 			if err := podCtrs.Delete(ctrID); err != nil {
 | |
| 				return errors.Wrapf(err, "error removing container %s from pod %s", ctr.ID(), pod.ID())
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Does the container have dependencies?
 | |
| 	ctrDepsBkt := ctrExists.Bucket(dependenciesBkt)
 | |
| 	if ctrDepsBkt == nil {
 | |
| 		return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", ctr.ID())
 | |
| 	}
 | |
| 	deps := []string{}
 | |
| 	err = ctrDepsBkt.ForEach(func(id, value []byte) error {
 | |
| 		deps = append(deps, string(id))
 | |
| 
 | |
| 		return nil
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if len(deps) != 0 {
 | |
| 		return errors.Wrapf(ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
 | |
| 	}
 | |
| 
 | |
| 	if err := ctrBucket.DeleteBucket(ctrID); err != nil {
 | |
| 		return errors.Wrapf(ErrInternal, "error deleting container %s from DB", ctr.ID())
 | |
| 	}
 | |
| 
 | |
| 	if err := idsBucket.Delete(ctrID); err != nil {
 | |
| 		return errors.Wrapf(err, "error deleting container %s ID in DB", ctr.ID())
 | |
| 	}
 | |
| 
 | |
| 	if err := namesBucket.Delete(ctrName); err != nil {
 | |
| 		return errors.Wrapf(err, "error deleting container %s name in DB", ctr.ID())
 | |
| 	}
 | |
| 	if err := nsBucket.Delete(ctrID); err != nil {
 | |
| 		return errors.Wrapf(err, "error deleting container %s namespace in DB", ctr.ID())
 | |
| 	}
 | |
| 	if err := allCtrsBucket.Delete(ctrID); err != nil {
 | |
| 		return errors.Wrapf(err, "error deleting container %s from all containers bucket in DB", ctr.ID())
 | |
| 	}
 | |
| 
 | |
| 	depCtrs := ctr.Dependencies()
 | |
| 
 | |
| 	// Remove us from other container's dependencies
 | |
| 	for _, depCtr := range depCtrs {
 | |
| 		depCtrID := []byte(depCtr)
 | |
| 
 | |
| 		depCtrBkt := ctrBucket.Bucket(depCtrID)
 | |
| 		if depCtrBkt == nil {
 | |
| 			// The dependent container has been removed
 | |
| 			// This should not be possible, and means the
 | |
| 			// state is inconsistent, but don't error
 | |
| 			// The container with inconsistent state is the
 | |
| 			// one being removed
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		depCtrDependsBkt := depCtrBkt.Bucket(dependenciesBkt)
 | |
| 		if depCtrDependsBkt == nil {
 | |
| 			// This is more serious - another container in
 | |
| 			// the state is inconsistent
 | |
| 			// Log it, continue removing
 | |
| 			logrus.Errorf("Container %s is missing dependencies bucket in DB", ctr.ID())
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if err := depCtrDependsBkt.Delete(ctrID); err != nil {
 | |
| 			return errors.Wrapf(err, "error removing container %s as a dependency of container %s", ctr.ID(), depCtr)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Remove container from volume dependencies bucket if container is using a named volume
 | |
| 	for _, vol := range ctr.config.Spec.Mounts {
 | |
| 		if strings.Contains(vol.Source, ctr.runtime.config.VolumePath) {
 | |
| 			volName := strings.Split(vol.Source[len(ctr.runtime.config.VolumePath)+1:], "/")[0]
 | |
| 
 | |
| 			volDB := volBkt.Bucket([]byte(volName))
 | |
| 			if volDB == nil {
 | |
| 				// Let's assume the volume was already deleted and continue to remove the container
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			ctrDepsBkt := volDB.Bucket(volDependenciesBkt)
 | |
| 			if depExists := ctrDepsBkt.Get(ctrID); depExists != nil {
 | |
| 				if err := ctrDepsBkt.Delete(ctrID); err != nil {
 | |
| 					return errors.Wrapf(err, "error deleting container dependencies %q for volume %s in ctrDependencies bucket in DB", ctr.ID(), volName)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 |