Add constraint that dependencies must be in the same ns

Dependency containers must be in the same namespace, to ensure
there are never problems resolving a dependency.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
This commit is contained in:
Matthew Heon
2018-06-25 23:39:11 -04:00
parent ab9bc21877
commit e838dcb4bf
8 changed files with 340 additions and 17 deletions

View File

@ -1,6 +1,7 @@
package libpod package libpod
import ( import (
"bytes"
"encoding/json" "encoding/json"
"os" "os"
"strings" "strings"
@ -11,10 +12,12 @@ import (
// BoltState is a state implementation backed by a Bolt DB // BoltState is a state implementation backed by a Bolt DB
type BoltState struct { type BoltState struct {
valid bool valid bool
dbPath string dbPath string
lockDir string namespace string
runtime *Runtime namespaceBytes []byte
lockDir string
runtime *Runtime
} }
// NewBoltState creates a new bolt-backed state database // NewBoltState creates a new bolt-backed state database
@ -23,6 +26,8 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
state.dbPath = path state.dbPath = path
state.lockDir = lockDir state.lockDir = lockDir
state.runtime = runtime state.runtime = runtime
state.namespace = ""
state.namespaceBytes = nil
// Make the directory that will hold container lockfiles // Make the directory that will hold container lockfiles
if err := os.MkdirAll(lockDir, 0750); err != nil { if err := os.MkdirAll(lockDir, 0750); err != nil {
@ -47,6 +52,9 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
if _, err := tx.CreateBucketIfNotExists(nameRegistryBkt); err != nil { if _, err := tx.CreateBucketIfNotExists(nameRegistryBkt); err != nil {
return errors.Wrapf(err, "error creating name-registry bucket") return errors.Wrapf(err, "error creating name-registry bucket")
} }
if _, err := tx.CreateBucketIfNotExists(nsRegistryBkt); err != nil {
return errors.Wrapf(err, "error creating ns-registry bucket")
}
if _, err := tx.CreateBucketIfNotExists(ctrBkt); err != nil { if _, err := tx.CreateBucketIfNotExists(ctrBkt); err != nil {
return errors.Wrapf(err, "error creating containers bucket") return errors.Wrapf(err, "error creating containers bucket")
} }
@ -193,6 +201,20 @@ func (s *BoltState) Refresh() error {
return err return err
} }
// 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
}
// Container retrieves a single container from the state by its full ID // Container retrieves a single container from the state by its full ID
func (s *BoltState) Container(id string) (*Container, error) { func (s *BoltState) Container(id string) (*Container, error) {
if id == "" { if id == "" {
@ -262,6 +284,11 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
return err return err
} }
nsBucket, err := getNSBucket(tx)
if err != nil {
return err
}
// First, check if the ID given was the actual container ID // First, check if the ID given was the actual container ID
var id []byte var id []byte
ctrExists := ctrBucket.Bucket([]byte(idOrName)) ctrExists := ctrBucket.Bucket([]byte(idOrName))
@ -274,6 +301,14 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
// Use else-if in case the name is set to a partial ID // Use else-if in case the name is set to a partial ID
exists := false exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error { err = idBucket.ForEach(func(checkID, checkName []byte) error {
// If the container isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBucket.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if string(checkName) == idOrName { if string(checkName) == idOrName {
if exists { if exists {
return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName) return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
@ -334,7 +369,14 @@ func (s *BoltState) HasContainer(id string) (bool, error) {
ctrExists := ctrBucket.Bucket(ctrID) ctrExists := ctrBucket.Bucket(ctrID)
if ctrExists != nil { if ctrExists != nil {
exists = true if s.namespaceBytes != nil {
nsBytes := ctrBucket.Get(namespaceKey)
if bytes.Equal(nsBytes, nsBytes) {
exists = true
}
} else {
exists = true
}
} }
return nil return nil
@ -383,7 +425,7 @@ func (s *BoltState) RemoveContainer(ctr *Container) error {
defer db.Close() defer db.Close()
err = db.Update(func(tx *bolt.Tx) error { err = db.Update(func(tx *bolt.Tx) error {
return removeContainer(ctr, nil, tx) return removeContainer(ctr, nil, tx, s.namespace)
}) })
return err return err
} }
@ -398,6 +440,12 @@ func (s *BoltState) UpdateContainer(ctr *Container) error {
return ErrCtrRemoved return ErrCtrRemoved
} }
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)
}
}
newState := new(containerState) newState := new(containerState)
netNSPath := "" netNSPath := ""
@ -460,6 +508,12 @@ func (s *BoltState) SaveContainer(ctr *Container) error {
return ErrCtrRemoved return ErrCtrRemoved
} }
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)
}
}
stateJSON, err := json.Marshal(ctr.state) stateJSON, err := json.Marshal(ctr.state)
if err != nil { if err != nil {
return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID()) return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID())
@ -519,6 +573,12 @@ func (s *BoltState) ContainerInUse(ctr *Container) ([]string, error) {
return nil, ErrCtrRemoved return nil, ErrCtrRemoved
} }
if s.namespace != "" {
if s.namespace != ctr.config.Namespace {
return nil, errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
}
}
depCtrs := []string{} depCtrs := []string{}
db, err := s.getDBCon() db, err := s.getDBCon()
@ -602,9 +662,20 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
ctr.config = new(ContainerConfig) ctr.config = new(ContainerConfig)
ctr.state = new(containerState) ctr.state = new(containerState)
ctrs = append(ctrs, ctr) 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.
if errors.Cause(err) != ErrNSMismatch {
return err
}
} else {
ctrs = append(ctrs, ctr)
}
return nil
return s.getContainerFromDB(id, ctr, ctrBucket)
}) })
}) })
if err != nil { if err != nil {
@ -682,6 +753,11 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
return err return err
} }
nsBkt, err := getNSBucket(tx)
if err != nil {
return err
}
// First, check if the ID given was the actual pod ID // First, check if the ID given was the actual pod ID
var id []byte var id []byte
podExists := podBkt.Bucket([]byte(idOrName)) podExists := podBkt.Bucket([]byte(idOrName))
@ -694,6 +770,14 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
// Use else-if in case the name is set to a partial ID // Use else-if in case the name is set to a partial ID
exists := false exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error { err = idBucket.ForEach(func(checkID, checkName []byte) error {
// 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
}
}
if string(checkName) == idOrName { if string(checkName) == idOrName {
if exists { if exists {
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName) return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
@ -719,7 +803,6 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
// We might have found a container ID, but it's OK // We might have found a container ID, but it's OK
// We'll just fail in getPodFromDB with ErrNoSuchPod // We'll just fail in getPodFromDB with ErrNoSuchPod
return s.getPodFromDB(id, pod, podBkt) return s.getPodFromDB(id, pod, podBkt)
}) })
if err != nil { if err != nil {
@ -757,7 +840,14 @@ func (s *BoltState) HasPod(id string) (bool, error) {
podDB := podBkt.Bucket(podID) podDB := podBkt.Bucket(podID)
if podDB != nil { if podDB != nil {
exists = true if s.namespaceBytes != nil {
podNS := podDB.Get(namespaceKey)
if bytes.Equal(s.namespaceBytes, podNS) {
exists = true
}
} else {
exists = true
}
} }
return nil return nil
@ -1318,7 +1408,7 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
defer db.Close() defer db.Close()
err = db.Update(func(tx *bolt.Tx) error { err = db.Update(func(tx *bolt.Tx) error {
return removeContainer(ctr, pod, tx) return removeContainer(ctr, pod, tx, s.namespace)
}) })
return err return err
} }

View File

@ -16,6 +16,7 @@ import (
const ( const (
idRegistryName = "id-registry" idRegistryName = "id-registry"
nameRegistryName = "name-registry" nameRegistryName = "name-registry"
nsRegistryName = "ns-registry"
ctrName = "ctr" ctrName = "ctr"
allCtrsName = "all-ctrs" allCtrsName = "all-ctrs"
podName = "pod" podName = "pod"
@ -34,6 +35,7 @@ const (
var ( var (
idRegistryBkt = []byte(idRegistryName) idRegistryBkt = []byte(idRegistryName)
nameRegistryBkt = []byte(nameRegistryName) nameRegistryBkt = []byte(nameRegistryName)
nsRegistryBkt = []byte(nsRegistryName)
ctrBkt = []byte(ctrName) ctrBkt = []byte(ctrName)
allCtrsBkt = []byte(allCtrsName) allCtrsBkt = []byte(allCtrsName)
podBkt = []byte(podName) podBkt = []byte(podName)
@ -168,6 +170,14 @@ func getNamesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil 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) { func getCtrBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
bkt := tx.Bucket(ctrBkt) bkt := tx.Bucket(ctrBkt)
if bkt == nil { if bkt == nil {
@ -214,6 +224,13 @@ func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found", string(id)) 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) podConfigBytes := podDB.Get(configKey)
if podConfigBytes == nil { if podConfigBytes == nil {
return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id)) return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
@ -287,6 +304,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return err return err
} }
nsBucket, err := getNSBucket(tx)
if err != nil {
return err
}
ctrBucket, err := getCtrBucket(tx) ctrBucket, err := getCtrBucket(tx)
if err != nil { if err != nil {
return err return err
@ -343,6 +365,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
if err := namesBucket.Put(ctrName, ctrID); err != nil { if err := namesBucket.Put(ctrName, ctrID); err != nil {
return errors.Wrapf(err, "error adding container %s name (%s) to DB", ctr.ID(), ctr.Name()) 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 { if err := allCtrsBucket.Put(ctrID, ctrName); err != nil {
return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID()) return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
} }
@ -403,6 +430,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
} }
} }
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) depCtrDependsBkt := depCtrBkt.Bucket(dependenciesBkt)
if depCtrDependsBkt == nil { if depCtrDependsBkt == nil {
return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", dependsCtr) return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", dependsCtr)
@ -427,7 +459,7 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
// Remove a container from the DB // Remove a container from the DB
// If pod is not nil, the container is treated as belonging to a pod, and // If pod is not nil, the container is treated as belonging to a pod, and
// will be removed from the pod as well // will be removed from the pod as well
func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error { func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx, namespace string) error {
ctrID := []byte(ctr.ID()) ctrID := []byte(ctr.ID())
ctrName := []byte(ctr.Name()) ctrName := []byte(ctr.Name())
@ -446,6 +478,11 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
return err return err
} }
nsBucket, err := getNSBucket(tx)
if err != nil {
return err
}
allCtrsBucket, err := getAllCtrsBucket(tx) allCtrsBucket, err := getAllCtrsBucket(tx)
if err != nil { if err != nil {
return err return err
@ -475,6 +512,14 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID()) 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 namespace != "" {
if 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, namespace)
}
}
if podDB != nil { if podDB != nil {
// Check if the container is in the pod, remove it if it is // Check if the container is in the pod, remove it if it is
podCtrs := podDB.Bucket(containersBkt) podCtrs := podDB.Bucket(containersBkt)
@ -521,6 +566,9 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
if err := namesBucket.Delete(ctrName); err != nil { if err := namesBucket.Delete(ctrName); err != nil {
return errors.Wrapf(err, "error deleting container %s name in DB", ctr.ID()) 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 { if err := allCtrsBucket.Delete(ctrID); err != nil {
return errors.Wrapf(err, "error deleting container %s from all containers bucket in DB", ctr.ID()) return errors.Wrapf(err, "error deleting container %s from all containers bucket in DB", ctr.ID())
} }

View File

@ -3,6 +3,7 @@
package libpod package libpod
import ( import (
"bytes"
"encoding/json" "encoding/json"
"path/filepath" "path/filepath"
@ -19,6 +20,13 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.
return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id)) 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) configBytes := ctrBkt.Get(configKey)
if configBytes == nil { if configBytes == nil {
return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id)) return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id))

View File

@ -20,6 +20,7 @@ type InMemoryState struct {
podContainers map[string]map[string]*Container podContainers map[string]map[string]*Container
nameIndex *registrar.Registrar nameIndex *registrar.Registrar
idIndex *truncindex.TruncIndex idIndex *truncindex.TruncIndex
namespace string
} }
// NewInMemoryState initializes a new, empty in-memory state // NewInMemoryState initializes a new, empty in-memory state
@ -36,6 +37,8 @@ func NewInMemoryState() (State, error) {
state.nameIndex = registrar.NewRegistrar() state.nameIndex = registrar.NewRegistrar()
state.idIndex = truncindex.NewTruncIndex([]string{}) state.idIndex = truncindex.NewTruncIndex([]string{})
state.namespace = ""
return state, nil return state, nil
} }
@ -51,6 +54,13 @@ func (s *InMemoryState) Refresh() error {
return nil return nil
} }
// SetNamespace sets the namespace for container and pod retrieval.
func (s *InMemoryState) SetNamespace(ns string) error {
s.namespace = ns
return nil
}
// Container retrieves a container from its full ID // Container retrieves a container from its full ID
func (s *InMemoryState) Container(id string) (*Container, error) { func (s *InMemoryState) Container(id string) (*Container, error) {
if id == "" { if id == "" {
@ -133,6 +143,9 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
} else if depCtr.config.Pod != "" { } else if depCtr.config.Pod != "" {
return errors.Wrapf(ErrInvalidArg, "cannot depend on container in a pod if not part of same pod") return errors.Wrapf(ErrInvalidArg, "cannot depend on container in a pod if not part of same pod")
} }
if depCtr.config.Namespace != ctr.config.Namespace {
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depID, depCtr.config.Namespace)
}
} }
if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil {
@ -519,9 +532,13 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
if _, ok = s.containers[depCtr]; !ok { if _, ok = s.containers[depCtr]; !ok {
return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr) return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr)
} }
if _, ok = podCtrs[depCtr]; !ok { depCtrStruct, ok := podCtrs[depCtr]
if !ok {
return errors.Wrapf(ErrInvalidArg, "cannot depend on container %s as it is not in pod %s", depCtr, pod.ID()) return errors.Wrapf(ErrInvalidArg, "cannot depend on container %s as it is not in pod %s", depCtr, pod.ID())
} }
if depCtrStruct.config.Namespace != ctr.config.Namespace {
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depCtr, depCtrStruct.config.Namespace)
}
} }
// Add container to state // Add container to state

View File

@ -284,6 +284,27 @@ func WithCNIPluginDir(dir string) RuntimeOption {
} }
} }
// WithNamespace sets the namespace for libpod.
// Namespace is the libpod namespace to use.
// Namespaces are used to create scopes to separate containers and pods
// in the state.
// When namespace is set, libpod will only view containers and pods in
// the same namespace. All containers and pods created will default to
// the namespace set here.
// A namespace of "", the empty string, is equivalent to no namespace,
// and all containers and pods will be visible.
func WithNamespace(ns string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.Namespace = ns
return nil
}
}
// Container Creation Options // Container Creation Options
// WithShmDir sets the directory that should be mounted on /dev/shm. // WithShmDir sets the directory that should be mounted on /dev/shm.
@ -963,11 +984,11 @@ func WithRootFS(rootfs string) CtrCreateOption {
} }
} }
// WithNamespace sets the namespace the container will be created in. // WithCtrNamespace sets the namespace the container will be created in.
// Namespaces are used to create separate views of Podman's state - runtimes can // Namespaces are used to create separate views of Podman's state - runtimes can
// join a specific namespace and see only containers and pods in that namespace. // join a specific namespace and see only containers and pods in that namespace.
// Empty string namespaces are allowed, and correspond to a lack of namespace. // Empty string namespaces are allowed, and correspond to a lack of namespace.
func WithNamespace(ns string) CtrCreateOption { func WithCtrNamespace(ns string) CtrCreateOption {
return func(ctr *Container) error { return func(ctr *Container) error {
if ctr.valid { if ctr.valid {
return ErrCtrFinalized return ErrCtrFinalized

View File

@ -136,10 +136,22 @@ type RuntimeConfig struct {
// CNIDefaultNetwork is the network name of the default CNI network // CNIDefaultNetwork is the network name of the default CNI network
// to attach pods to // to attach pods to
CNIDefaultNetwork string `toml:"cni_default_network,omitempty"` CNIDefaultNetwork string `toml:"cni_default_network,omitempty"`
// HooksDirNotExistFatal switches between fatal errors and non-fatal warnings if the configured HooksDir does not exist. // HooksDirNotExistFatal switches between fatal errors and non-fatal
// warnings if the configured HooksDir does not exist.
HooksDirNotExistFatal bool `toml:"hooks_dir_not_exist_fatal"` HooksDirNotExistFatal bool `toml:"hooks_dir_not_exist_fatal"`
// DefaultMountsFile is the path to the default mounts file for testing purposes only // DefaultMountsFile is the path to the default mounts file for testing
// purposes only
DefaultMountsFile string `toml:"-"` DefaultMountsFile string `toml:"-"`
// Namespace is the libpod namespace to use.
// Namespaces are used to create scopes to separate containers and pods
// in the state.
// When namespace is set, libpod will only view containers and pods in
// the same namespace. All containers and pods created will default to
// the namespace set here.
// A namespace of "", the empty string, is equivalent to no namespace,
// and all containers and pods will be visible.
// The default namespace is "".
Namespace string `toml:"namespace,omitempty"`
} }
var ( var (

View File

@ -9,6 +9,10 @@ type State interface {
// Refresh clears container and pod states after a reboot // Refresh clears container and pod states after a reboot
Refresh() error Refresh() error
// SetNamespace() sets the namespace for the store, and will determine
// what containers are retrieved with container and pod retrieval calls
SetNamespace(ns string) error
// Return a container from the database from its full ID // Return a container from the database from its full ID
Container(id string) (*Container, error) Container(id string) (*Container, error)
// Return a container from the database by full or partial ID or full // Return a container from the database by full or partial ID or full

View File

@ -281,6 +281,56 @@ func TestAddCtrDepInPodFails(t *testing.T) {
}) })
} }
func TestAddCtrDepInSameNamespaceSucceeds(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
testCtr1.config.Namespace = "test1"
testCtr2.config.Namespace = "test1"
err = state.AddContainer(testCtr1)
assert.NoError(t, err)
err = state.AddContainer(testCtr2)
assert.NoError(t, err)
ctrs, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 2, len(ctrs))
})
}
func TestAddCtrDepInDifferentNamespaceFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
testCtr1.config.Namespace = "test1"
testCtr2.config.Namespace = "test2"
err = state.AddContainer(testCtr1)
assert.NoError(t, err)
err = state.AddContainer(testCtr2)
assert.Error(t, err)
ctrs, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 1, len(ctrs))
testContainersEqual(t, testCtr1, ctrs[0])
})
}
func TestGetNonexistentContainerFails(t *testing.T) { func TestGetNonexistentContainerFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) { runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Container("does not exist") _, err := state.Container("does not exist")
@ -2224,6 +2274,79 @@ func TestAddContainerToPodDependencyOutsidePodFails(t *testing.T) {
}) })
} }
func TestAddContainerToPodDependencyInSameNamespaceSucceeds(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
testPod.config.Namespace = "test1"
testCtr1, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr1.config.Pod = testPod.ID()
testCtr1.config.Namespace = "test1"
testCtr2, err := getTestCtrN("3", lockPath)
assert.NoError(t, err)
testCtr2.config.Pod = testPod.ID()
testCtr2.config.IPCNsCtr = testCtr1.ID()
testCtr2.config.Namespace = "test1"
err = state.AddPod(testPod)
assert.NoError(t, err)
err = state.AddContainerToPod(testPod, testCtr1)
assert.NoError(t, err)
err = state.AddContainerToPod(testPod, testCtr2)
assert.NoError(t, err)
deps, err := state.ContainerInUse(testCtr1)
assert.NoError(t, err)
assert.Equal(t, 1, len(deps))
assert.Equal(t, testCtr2.ID(), deps[0])
})
}
func TestAddContainerToPodDependencyInSeparateNamespaceFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
testPod.config.Namespace = "test1"
testCtr1, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr1.config.Pod = testPod.ID()
testCtr1.config.Namespace = "test1"
testCtr2, err := getTestCtrN("3", lockPath)
assert.NoError(t, err)
testCtr2.config.Pod = testPod.ID()
testCtr2.config.IPCNsCtr = testCtr1.ID()
testCtr2.config.Namespace = "test2"
err = state.AddPod(testPod)
assert.NoError(t, err)
err = state.AddContainerToPod(testPod, testCtr1)
assert.NoError(t, err)
err = state.AddContainerToPod(testPod, testCtr2)
assert.Error(t, err)
ctrs, err := state.PodContainers(testPod)
assert.NoError(t, err)
assert.Equal(t, 1, len(ctrs))
allCtrs, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 1, len(allCtrs))
deps, err := state.ContainerInUse(testCtr1)
assert.NoError(t, err)
assert.Equal(t, 0, len(deps))
})
}
func TestAddContainerToPodSameNamespaceSucceeds(t *testing.T) { func TestAddContainerToPodSameNamespaceSucceeds(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) { runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath) testPod, err := getTestPod1(lockPath)