diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 112 | ||||
-rw-r--r-- | libpod/boltdb_state_internal.go | 50 | ||||
-rw-r--r-- | libpod/boltdb_state_linux.go | 8 | ||||
-rw-r--r-- | libpod/in_memory_state.go | 19 | ||||
-rw-r--r-- | libpod/options.go | 25 | ||||
-rw-r--r-- | libpod/runtime.go | 16 | ||||
-rw-r--r-- | libpod/state.go | 4 | ||||
-rw-r--r-- | libpod/state_test.go | 123 |
8 files changed, 340 insertions, 17 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 648c14267..73a906761 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -1,6 +1,7 @@ package libpod import ( + "bytes" "encoding/json" "os" "strings" @@ -11,10 +12,12 @@ import ( // BoltState is a state implementation backed by a Bolt DB type BoltState struct { - valid bool - dbPath string - lockDir string - runtime *Runtime + valid bool + dbPath string + namespace string + namespaceBytes []byte + lockDir string + runtime *Runtime } // 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.lockDir = lockDir state.runtime = runtime + state.namespace = "" + state.namespaceBytes = nil // Make the directory that will hold container lockfiles 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 { 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 { return errors.Wrapf(err, "error creating containers bucket") } @@ -193,6 +201,20 @@ func (s *BoltState) Refresh() error { 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 func (s *BoltState) Container(id string) (*Container, error) { if id == "" { @@ -262,6 +284,11 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) { return err } + nsBucket, err := getNSBucket(tx) + if err != nil { + return err + } + // First, check if the ID given was the actual container ID var id []byte 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 exists := false 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 exists { 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) 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 @@ -383,7 +425,7 @@ func (s *BoltState) RemoveContainer(ctr *Container) error { defer db.Close() err = db.Update(func(tx *bolt.Tx) error { - return removeContainer(ctr, nil, tx) + return removeContainer(ctr, nil, tx, s.namespace) }) return err } @@ -398,6 +440,12 @@ func (s *BoltState) UpdateContainer(ctr *Container) error { 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) netNSPath := "" @@ -460,6 +508,12 @@ func (s *BoltState) SaveContainer(ctr *Container) error { 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) if err != nil { 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 } + 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{} db, err := s.getDBCon() @@ -602,9 +662,20 @@ func (s *BoltState) AllContainers() ([]*Container, error) { ctr.config = new(ContainerConfig) 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 { @@ -682,6 +753,11 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) { return err } + nsBkt, err := getNSBucket(tx) + if err != nil { + return err + } + // First, check if the ID given was the actual pod ID var id []byte 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 exists := false 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 exists { 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'll just fail in getPodFromDB with ErrNoSuchPod - return s.getPodFromDB(id, pod, podBkt) }) if err != nil { @@ -757,7 +840,14 @@ func (s *BoltState) HasPod(id string) (bool, error) { podDB := podBkt.Bucket(podID) 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 @@ -1318,7 +1408,7 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { defer db.Close() err = db.Update(func(tx *bolt.Tx) error { - return removeContainer(ctr, pod, tx) + return removeContainer(ctr, pod, tx, s.namespace) }) return err } diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 5661c5b7f..718c43046 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -16,6 +16,7 @@ import ( const ( idRegistryName = "id-registry" nameRegistryName = "name-registry" + nsRegistryName = "ns-registry" ctrName = "ctr" allCtrsName = "all-ctrs" podName = "pod" @@ -34,6 +35,7 @@ const ( var ( idRegistryBkt = []byte(idRegistryName) nameRegistryBkt = []byte(nameRegistryName) + nsRegistryBkt = []byte(nsRegistryName) ctrBkt = []byte(ctrName) allCtrsBkt = []byte(allCtrsName) podBkt = []byte(podName) @@ -168,6 +170,14 @@ func getNamesBucket(tx *bolt.Tx) (*bolt.Bucket, error) { 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 { @@ -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)) } + 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)) @@ -287,6 +304,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { return err } + nsBucket, err := getNSBucket(tx) + if err != nil { + return err + } + ctrBucket, err := getCtrBucket(tx) if err != nil { return err @@ -343,6 +365,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { 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()) } @@ -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) if depCtrDependsBkt == nil { 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 // 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 { +func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx, namespace string) error { ctrID := []byte(ctr.ID()) ctrName := []byte(ctr.Name()) @@ -446,6 +478,11 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error { return err } + nsBucket, err := getNSBucket(tx) + if err != nil { + return err + } + allCtrsBucket, err := getAllCtrsBucket(tx) if err != nil { 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()) } + // 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 { // Check if the container is in the pod, remove it if it is 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 { 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()) } diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go index ceea955bd..7c0e08fb3 100644 --- a/libpod/boltdb_state_linux.go +++ b/libpod/boltdb_state_linux.go @@ -3,6 +3,7 @@ package libpod import ( + "bytes" "encoding/json" "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)) } + 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)) diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index cf2f43477..4ea8740fb 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -20,6 +20,7 @@ type InMemoryState struct { podContainers map[string]map[string]*Container nameIndex *registrar.Registrar idIndex *truncindex.TruncIndex + namespace string } // NewInMemoryState initializes a new, empty in-memory state @@ -36,6 +37,8 @@ func NewInMemoryState() (State, error) { state.nameIndex = registrar.NewRegistrar() state.idIndex = truncindex.NewTruncIndex([]string{}) + state.namespace = "" + return state, nil } @@ -51,6 +54,13 @@ func (s *InMemoryState) Refresh() error { 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 func (s *InMemoryState) Container(id string) (*Container, error) { if id == "" { @@ -133,6 +143,9 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { } else if depCtr.config.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 { @@ -519,9 +532,13 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { if _, ok = s.containers[depCtr]; !ok { 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()) } + 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 diff --git a/libpod/options.go b/libpod/options.go index fb07d1edf..155c15333 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -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 // 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 // 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. -func WithNamespace(ns string) CtrCreateOption { +func WithCtrNamespace(ns string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return ErrCtrFinalized diff --git a/libpod/runtime.go b/libpod/runtime.go index a551c9134..a0b673d81 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -136,10 +136,22 @@ type RuntimeConfig struct { // CNIDefaultNetwork is the network name of the default CNI network // to attach pods to 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"` - // 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:"-"` + // 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 ( diff --git a/libpod/state.go b/libpod/state.go index b71f811ea..6239e399a 100644 --- a/libpod/state.go +++ b/libpod/state.go @@ -9,6 +9,10 @@ type State interface { // Refresh clears container and pod states after a reboot 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 Container(id string) (*Container, error) // Return a container from the database by full or partial ID or full diff --git a/libpod/state_test.go b/libpod/state_test.go index 7174bbf2a..e5c5a1de0 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -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) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, 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) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testPod, err := getTestPod1(lockPath) |