summaryrefslogtreecommitdiff
path: root/libpod/boltdb_state_internal.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/boltdb_state_internal.go')
-rw-r--r--libpod/boltdb_state_internal.go382
1 files changed, 327 insertions, 55 deletions
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index c8aa4adbf..89adac79d 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -3,34 +3,42 @@ package libpod
import (
"encoding/json"
"path/filepath"
+ "strings"
"github.com/boltdb/bolt"
"github.com/containers/storage"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
const (
idRegistryName = "id-registry"
nameRegistryName = "name-registry"
- ctrConfigName = "container-config"
- ctrStateName = "container-state"
- netNSName = "net-ns"
- runtimeConfigName = "runtime-config"
- ctrDependsName = "container-depends"
+ ctrName = "ctr"
podName = "pod"
- podContainersName = "pod-containers"
+ runtimeConfigName = "runtime-config"
+
+ configName = "config"
+ stateName = "state"
+ dependenciesName = "dependencies"
+ netNSName = "netns"
+ containersName = "containers"
+
+ dbExistName = "ok"
)
var (
idRegistryBkt = []byte(idRegistryName)
nameRegistryBkt = []byte(nameRegistryName)
- ctrConfigBkt = []byte(ctrConfigName)
- ctrStateBkt = []byte(ctrStateName)
- netNSBkt = []byte(netNSName)
- runtimeConfigBkt = []byte(runtimeConfigName)
- ctrDependsBkt = []byte(ctrDependsName)
+ ctrBkt = []byte(ctrName)
podBkt = []byte(podName)
- podContainersBkt = []byte(podContainersName)
+ runtimeConfigBkt = []byte(runtimeConfigName)
+
+ configKey = []byte(configName)
+ stateKey = []byte(stateName)
+ dependenciesBkt = []byte(dependenciesName)
+ netNSKey = []byte(netNSName)
+ containersBkt = []byte(containersName)
)
// Check if the configuration of the database is compatible with the
@@ -122,26 +130,18 @@ func getNamesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil
}
-func getCtrConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(ctrConfigBkt)
+func getCtrBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
+ bkt := tx.Bucket(ctrBkt)
if bkt == nil {
- return nil, errors.Wrapf(ErrDBBadConfig, "container config bucket not found in DB")
+ return nil, errors.Wrapf(ErrDBBadConfig, "containers bucket not found in DB")
}
return bkt, nil
}
-func getCtrStateBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(ctrStateBkt)
- if bkt == nil {
- return nil, errors.Wrapf(ErrDBBadConfig, "container state bucket not found in DB")
- }
- return bkt, nil
-}
-
-func getNetNSBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(netNSBkt)
+func getPodBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
+ bkt := tx.Bucket(podBkt)
if bkt == nil {
- return nil, errors.Wrapf(ErrDBBadConfig, "network namespace bucket not found in DB")
+ return nil, errors.Wrapf(ErrDBBadConfig, "pods bucket not found in DB")
}
return bkt, nil
}
@@ -154,52 +154,34 @@ func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil
}
-func getCtrDependsBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(ctrDependsBkt)
- if bkt == nil {
- return nil, errors.Wrapf(ErrDBBadConfig, "container dependencies bucket not found in DB")
+func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error {
+ ctrBkt := ctrsBkt.Bucket(id)
+ if ctrBkt == nil {
+ return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id))
}
- 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")
+ configBytes := ctrBkt.Get(configKey)
+ if configBytes == nil {
+ return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id))
}
- return bkt, nil
-}
-func getPodContainersBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(podContainersBkt)
- if bkt == nil {
- return nil, errors.Wrapf(ErrDBBadConfig, "pod containers bucket not found in DB")
+ stateBytes := ctrBkt.Get(stateKey)
+ if stateBytes == nil {
+ return errors.Wrapf(ErrInternal, "container %s missing state key in DB", string(id))
}
- return bkt, nil
-}
-func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, config, state, netNS *bolt.Bucket) error {
- configBytes := config.Get(id)
- if configBytes == nil {
- return errors.Wrapf(ErrNoSuchCtr, "error unmarshalling container %s config", string(id))
- }
+ netNSBytes := ctrBkt.Get(netNSKey)
if err := json.Unmarshal(configBytes, ctr.config); err != nil {
return errors.Wrapf(err, "error unmarshalling container %s config", string(id))
}
- stateBytes := state.Get(id)
- if stateBytes == nil {
- return errors.Wrapf(ErrInternal, "container %s has config but no state", string(id))
- }
-
if err := json.Unmarshal(stateBytes, ctr.state); err != nil {
return errors.Wrapf(err, "error unmarshalling container %s state", string(id))
}
// The container may not have a network namespace, so it's OK if this is
// nil
- netNSBytes := netNS.Get(id)
if netNSBytes != nil {
nsPath := string(netNSBytes)
netNS, err := joinNetNS(nsPath)
@@ -222,3 +204,293 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, config, state,
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))
+ }
+
+ podBytes := podDB.Get(configKey)
+ if podBytes == nil {
+ return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
+ }
+
+ if err := json.Unmarshal(podBytes, pod); err != nil {
+ return errors.Wrapf(err, "error unmarshalling pod %s from DB", string(id))
+ }
+
+ // Get the lock
+ lockPath := filepath.Join(s.lockDir, string(id))
+ lock, err := storage.GetLockfile(lockPath)
+ if err != nil {
+ return errors.Wrapf(err, "error retrieving lockfile for pod %s", string(id))
+ }
+ pod.lock = lock
+
+ pod.runtime = s.runtime
+ pod.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 {
+ // 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 := ""
+ if ctr.state.NetNS != nil {
+ netNSPath = ctr.state.NetNS.Path()
+ }
+
+ dependsCtrs := ctr.Dependencies()
+
+ ctrID := []byte(ctr.ID())
+ ctrName := []byte(ctr.Name())
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ 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
+ }
+
+ ctrBucket, err := getCtrBucket(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())
+ }
+ }
+
+ // 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())
+ }
+
+ 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 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)
+ }
+ 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())
+ }
+ }
+
+ 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 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
+ }
+
+ // 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())
+ }
+
+ 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())
+ }
+
+ 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)
+ }
+ }
+
+ return nil
+}