summaryrefslogtreecommitdiff
path: root/libpod/boltdb_state.go
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2017-12-13 22:02:15 -0500
committerAtomic Bot <atomic-devel@projectatomic.io>2018-02-12 14:28:07 +0000
commitb4cdc27b31a638322fb83b64aea869ce8600ea01 (patch)
tree5d248aeafa1f54a2c169d1d51deaaeb5de1e0e23 /libpod/boltdb_state.go
parent2e96acf3007cbd05a37a8af7156f0abda073cb5a (diff)
downloadpodman-b4cdc27b31a638322fb83b64aea869ce8600ea01.tar.gz
podman-b4cdc27b31a638322fb83b64aea869ce8600ea01.tar.bz2
podman-b4cdc27b31a638322fb83b64aea869ce8600ea01.zip
Add implementation for BoltDB-backed state
Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #184 Approved by: baude
Diffstat (limited to 'libpod/boltdb_state.go')
-rw-r--r--libpod/boltdb_state.go932
1 files changed, 932 insertions, 0 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
new file mode 100644
index 000000000..5502ec1b4
--- /dev/null
+++ b/libpod/boltdb_state.go
@@ -0,0 +1,932 @@
+package libpod
+
+import (
+ "encoding/json"
+ "os"
+ "strings"
+
+ "github.com/boltdb/bolt"
+ "github.com/pkg/errors"
+)
+
+// BoltState is a state implementation backed by a Bolt DB
+type BoltState struct {
+ valid bool
+ dbPath string
+ lockDir string
+ runtime *Runtime
+}
+
+// NewBoltState creates a new bolt-backed state database
+func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
+ state := new(BoltState)
+ state.dbPath = path
+ state.lockDir = lockDir
+ state.runtime = runtime
+
+ // Make the directory that will hold container lockfiles
+ if err := os.MkdirAll(lockDir, 0750); err != nil {
+ // The directory is allowed to exist
+ if !os.IsExist(err) {
+ return nil, errors.Wrapf(err, "error creating lockfiles dir %s", lockDir)
+ }
+ }
+ state.lockDir = lockDir
+
+ db, err := bolt.Open(path, 0600, nil)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error opening database %s", path)
+ }
+ defer db.Close()
+
+ // Perform initial database setup
+ err = db.Update(func(tx *bolt.Tx) error {
+ _, err = tx.CreateBucketIfNotExists(idRegistryBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating id-registry bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(nameRegistryBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating name-registry bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(ctrConfigBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating container-config bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(ctrStateBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating container-state bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(netNSBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating net-ns bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(runtimeConfigBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating runtime-config bucket")
+ }
+ _, err = tx.CreateBucketIfNotExists(ctrDependsBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating container-depends bucket")
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, errors.Wrapf(err, "error creating initial database layout")
+ }
+
+ // Check runtime configuration
+ if err := checkRuntimeConfig(db, runtime); err != nil {
+ return nil, err
+ }
+
+ 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 {
+ return ErrDBClosed
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ idBucket, err := getIDBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ // 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 {
+ if err := netNSBucket.Delete(id); err != nil {
+ return errors.Wrapf(err, "error removing network namespace ID %s", string(id))
+ }
+
+ stateBytes := ctrStateBucket.Get(id)
+ if stateBytes == nil {
+ // This is a pod, not a container
+ // Nothing to do
+ return nil
+ }
+
+ state := new(containerState)
+
+ if err := json.Unmarshal(stateBytes, state); err != nil {
+ return errors.Wrapf(err, "error unmarshalling state for container %s", string(id))
+ }
+
+ state.PID = 0
+ state.Mountpoint = ""
+ state.Mounted = false
+ state.State = ContainerStateConfigured
+
+ newStateBytes, err := json.Marshal(state)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling modified state for container %s", string(id))
+ }
+
+ if err := ctrStateBucket.Put(id, newStateBytes); err != nil {
+ return errors.Wrapf(err, "error updating state for container %s in DB", string(id))
+ }
+
+ return nil
+ })
+ return err
+ })
+ return err
+}
+
+// Container retrieves a single container from the state by its full ID
+func (s *BoltState) Container(id string) (*Container, error) {
+ if id == "" {
+ return nil, ErrEmptyID
+ }
+
+ if !s.valid {
+ return nil, ErrDBClosed
+ }
+
+ ctrID := []byte(id)
+
+ ctr := new(Container)
+ ctr.config = new(ContainerConfig)
+ ctr.state = new(containerState)
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrConfigBucket, err := getCtrConfigBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ err = s.getContainerFromDB(ctrID, ctr, ctrConfigBucket,
+ ctrStateBucket, netNSBucket)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return ctr, nil
+}
+
+// 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 == "" {
+ return nil, ErrEmptyID
+ }
+
+ if !s.valid {
+ return nil, ErrDBClosed
+ }
+
+ ctr := new(Container)
+ ctr.config = new(ContainerConfig)
+ ctr.state = new(containerState)
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bolt.Tx) error {
+ idBucket, err := getIDBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrConfigBucket, err := getCtrConfigBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ // First, check if the ID given was the actual container ID
+ // Query against state because it's a lot smaller than config
+ var id []byte
+ stateExists := ctrStateBucket.Get([]byte(idOrName))
+ if stateExists != nil {
+ // A full container ID was given
+ id = []byte(idOrName)
+ } else {
+ // They did not give us a full container ID.
+ // Search for partial ID or full name matches
+ // Use else-if in case the name is set to a partial ID
+ exists := false
+ err = idBucket.ForEach(func(checkID, checkName []byte) error {
+ if string(checkName) == idOrName {
+ if exists {
+ return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
+ }
+ id = checkID
+ exists = true
+ } else if strings.HasPrefix(string(checkID), idOrName) {
+ if exists {
+ return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
+ }
+ id = checkID
+ exists = true
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ } else if !exists {
+ return errors.Wrapf(ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
+ }
+ }
+
+ err = s.getContainerFromDB(id, ctr, ctrConfigBucket,
+ ctrStateBucket, netNSBucket)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ 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 == "" {
+ return false, ErrEmptyID
+ }
+
+ if !s.valid {
+ return false, ErrDBClosed
+ }
+
+ ctrID := []byte(id)
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return false, err
+ }
+ defer db.Close()
+
+ exists := false
+
+ err = db.View(func(tx *bolt.Tx) error {
+ idsBucket, err := getIDBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrExists := idsBucket.Get(ctrID)
+ if ctrExists != nil {
+ exists = true
+ }
+
+ 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 {
+ return ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ if ctr.config.Pod != "" {
+ return errors.Wrapf(ErrInvalidArg, "cannot add a container that belongs to a pod with AddContainer - use AddContainerToPod")
+ }
+
+ // 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()
+ }
+
+ // Collect dependencies for the container. Use a map to ensure no dupes.
+ 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
+ }
+
+ ctrConfigBucket, err := getCtrConfigBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrDependsBucket, err := getCtrDependsBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ // Check if we already have a container with the given ID and name
+ idExist := idsBucket.Get(ctrID)
+ if idExist != nil {
+ return errors.Wrapf(ErrCtrExists, "container with ID %s already exists", ctr.ID())
+ }
+ nameExist := namesBucket.Get(ctrName)
+ if nameExist != nil {
+ return errors.Wrapf(ErrCtrExists, "container with name %s already exists", 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 err := ctrConfigBucket.Put(ctrID, configJSON); err != nil {
+ return errors.Wrapf(err, "error adding container %s config to DB", ctr.ID())
+ }
+ if err := ctrStateBucket.Put(ctrID, stateJSON); err != nil {
+ return errors.Wrapf(err, "error adding container %s state to DB", ctr.ID())
+ }
+ if netNSPath != "" {
+ if err := netNSBucket.Put(ctrID, []byte(netNSPath)); err != nil {
+ return errors.Wrapf(err, "error adding container %s netns path to DB", ctr.ID())
+ }
+ }
+
+ // Add dependencies for the container
+ for _, dependsCtr := range dependsCtrs {
+ depCtrID := []byte(dependsCtr)
+ deps := ctrDependsBucket.Get(depCtrID)
+ depsArray := []string{}
+ if deps != nil {
+ if err := json.Unmarshal(deps, &depsArray); err != nil {
+ return errors.Wrapf(err, "error unmarshalling container %s deps JSON", dependsCtr)
+ }
+ }
+ depsArray = append(depsArray, ctr.ID())
+ depsJSON, err := json.Marshal(&depsArray)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling container %s deps JSON", dependsCtr)
+ }
+ if err := ctrDependsBucket.Put(depCtrID, depsJSON); err != nil {
+ return errors.Wrapf(err, "error adding container %s dependencies JSON to DB", dependsCtr)
+ }
+ }
+
+ return nil
+ })
+ return err
+}
+
+// RemoveContainer removes a container from the state
+// The container will only be removed from the state and not any pods it belongs
+// to
+func (s *BoltState) RemoveContainer(ctr *Container) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ ctrID := []byte(ctr.ID())
+ ctrName := []byte(ctr.Name())
+
+ depCtrs := ctr.Dependencies()
+
+ 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
+ }
+
+ ctrConfigBucket, err := getCtrConfigBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrDependsBucket, err := getCtrDependsBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ // Does the container exist?
+ ctrExists := idsBucket.Get(ctrID)
+ if ctrExists == nil {
+ return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
+ }
+
+ // Does the container have dependencies?
+ ctrDeps := ctrDependsBucket.Get(ctrID)
+ if ctrDeps != nil {
+ dependsCtrs := []string{}
+ if err := json.Unmarshal(ctrDeps, &dependsCtrs); err != nil {
+ return errors.Wrapf(err, "cannot unmarshal container %s dependencies JSON", ctr.ID())
+ }
+ if len(dependsCtrs) > 0 {
+ depsStr := strings.Join(dependsCtrs, ", ")
+
+ return errors.Wrapf(ErrCtrExists, "container %s is a dependency on the following containers: %s", ctr.ID(), depsStr)
+ }
+ }
+
+ 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 := ctrConfigBucket.Delete(ctrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s config in DB", ctr.ID())
+ }
+
+ if err := ctrStateBucket.Delete(ctrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s state in DB", ctr.ID())
+ }
+
+ // Can safely delete netNS even if it doesn't exist
+ // Delete on a non-existent key doesn't error
+ if err := netNSBucket.Delete(ctrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s network ns in DB", ctr.ID())
+ }
+
+ // As above, can safely delete even if it doesn't exist
+ if err := ctrDependsBucket.Delete(ctrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s dependencies in DB", ctr.ID())
+ }
+
+ // Remove us from other container's dependencies
+ for _, depCtr := range depCtrs {
+ depCtrID := []byte(depCtr)
+ dep := ctrDependsBucket.Get(depCtrID)
+ if dep == nil {
+ // Inconsistent state, but the dependency we were trying to remove doesn't exist...
+ // Just continue
+ continue
+ }
+ depStr := []string{}
+ if err := json.Unmarshal(dep, &depStr); err != nil {
+ return errors.Wrapf(err, "error unmarshaling ctr %s dependencies", ctr.ID(), depCtr)
+ }
+ newDeps := make([]string, 0, len(depStr))
+ for _, checkID := range depStr {
+ if checkID != ctr.ID() {
+ newDeps = append(newDeps, checkID)
+ }
+ }
+ if len(newDeps) == 0 {
+ // Just delete the container's deps
+ if err := ctrDependsBucket.Delete(depCtrID); err != nil {
+ return errors.Wrapf(err, "error deleting container %s dependencies in DB", depCtr)
+ }
+ } else {
+ // Store the new deps
+ depsJSON, err := json.Marshal(&newDeps)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling container %s dependencies", depCtr)
+ }
+ if err := ctrDependsBucket.Put(depCtrID, depsJSON); err != nil {
+ return errors.Wrapf(err, "error adding container %s dependencies to DB", depCtr)
+ }
+ }
+ }
+
+ return nil
+ })
+ return err
+}
+
+// UpdateContainer updates a container's state from the database
+func (s *BoltState) UpdateContainer(ctr *Container) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ newState := new(containerState)
+ netNSPath := ""
+
+ ctrID := []byte(ctr.ID())
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ newStateBytes := ctrStateBucket.Get(ctrID)
+ if newStateBytes == nil {
+ ctr.valid = false
+ return errors.Wrapf(ErrCtrRemoved, "container %s removed from database", ctr.ID())
+ }
+
+ if err := json.Unmarshal(newStateBytes, newState); err != nil {
+ return errors.Wrapf(err, "error unmarshalling container %s state", ctr.ID())
+ }
+
+ netNSBytes := netNSBucket.Get(ctrID)
+ if netNSBytes != nil {
+ netNSPath = string(netNSBytes)
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ // Do we need to replace the container's netns?
+ if netNSPath != "" {
+ // Check if the container's old state has a good netns
+ if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() {
+ newState.NetNS = ctr.state.NetNS
+ } else {
+ // Tear down the existing namespace
+ if err := s.runtime.teardownNetNS(ctr); err != nil {
+ return err
+ }
+
+ // Open the new network namespace
+ ns, err := joinNetNS(netNSPath)
+ if err != nil {
+ return errors.Wrapf(err, "error joining network namespace for container %s", ctr.ID())
+ }
+ newState.NetNS = ns
+ }
+ } else {
+ // The container no longer has a network namespace
+ // Tear down the old one
+ if err := s.runtime.teardownNetNS(ctr); err != nil {
+ return err
+ }
+ }
+
+ // 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 {
+ return ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ 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()
+ }
+
+ ctrID := []byte(ctr.ID())
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ oldJSON := ctrStateBucket.Get(ctrID)
+ if oldJSON == nil {
+ ctr.valid = false
+ return errors.Wrapf(ErrCtrRemoved, "container %s no longer present in DB", ctr.ID())
+ }
+
+ // Update the state
+ if err := ctrStateBucket.Put(ctrID, stateJSON); err != nil {
+ return errors.Wrapf(err, "error updating container %s state in DB", ctr.ID())
+ }
+
+ if netNSPath != "" {
+ if err := netNSBucket.Put(ctrID, []byte(netNSPath)); err != nil {
+ return errors.Wrapf(err, "error updating network namespace path for container %s in DB", ctr.ID())
+ }
+ }
+
+ 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 {
+ return nil, ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return nil, ErrCtrRemoved
+ }
+
+ depCtrs := []string{}
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrDependsBucket, err := getCtrDependsBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ depsJSON := ctrDependsBucket.Get([]byte(ctr.ID()))
+ if depsJSON == nil {
+ // No deps, just return
+ return nil
+ }
+
+ // We have deps, un-JSON them
+ if err := json.Unmarshal(depsJSON, &depCtrs); err != nil {
+ return errors.Wrapf(err, "error unmarshalling container %s dependencies", ctr.ID())
+ }
+
+ 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 {
+ return nil, ErrDBClosed
+ }
+
+ ctrs := []*Container{}
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrConfigBucket, err := getCtrConfigBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrStateBucket, err := getCtrStateBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ netNSBucket, err := getNetNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ // Iterate through all containers we know of in the state
+ // Build a full container struct for all of them and append
+ // it into the containers listing
+ err = ctrStateBucket.ForEach(func(id, data []byte) error {
+ ctr := new(Container)
+ ctr.config = new(ContainerConfig)
+ ctr.state = new(containerState)
+
+ err = s.getContainerFromDB(id, ctr, ctrConfigBucket,
+ ctrStateBucket, netNSBucket)
+ if err != nil {
+ return err
+ }
+
+ ctrs = append(ctrs, ctr)
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return ctrs, nil
+}
+
+// Pod retrieves a pod given its full ID
+func (s *BoltState) Pod(id string) (*Pod, error) {
+ return nil, ErrNotImplemented
+}
+
+// LookupPod retrieves a pod from full or unique partial ID or name
+func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
+ return nil, ErrNotImplemented
+}
+
+// HasPod checks if a pod with the given ID exists in the state
+func (s *BoltState) HasPod(id string) (bool, error) {
+ return false, ErrNotImplemented
+}
+
+// PodHasContainer checks if the given pod has a container with the given ID
+func (s *BoltState) PodHasContainer(pod *Pod, id string) (bool, error) {
+ return false, ErrNotImplemented
+}
+
+// PodContainersByID returns the IDs of all containers present in the given pod
+func (s *BoltState) PodContainersByID(pod *Pod) ([]string, error) {
+ return nil, ErrNotImplemented
+}
+
+// PodContainers returns all the containers present in the given pod
+func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) {
+ return nil, ErrNotImplemented
+}
+
+// AddPod adds the given pod to the state. Only empty pods can be added.
+func (s *BoltState) AddPod(pod *Pod) error {
+ return ErrNotImplemented
+}
+
+// RemovePod removes the given pod from the state
+// Only empty pods can be removed
+func (s *BoltState) RemovePod(pod *Pod) error {
+ return ErrNotImplemented
+}
+
+// RemovePodContainers removes all containers in a pod
+func (s *BoltState) RemovePodContainers(pod *Pod) error {
+ return ErrNotImplemented
+}
+
+// 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 {
+ return ErrNotImplemented
+}
+
+// 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 {
+ return ErrNotImplemented
+}
+
+// AllPods returns all pods present in the state
+func (s *BoltState) AllPods() ([]*Pod, error) {
+ return nil, ErrNotImplemented
+}