From ab9bc2187795b61a41dfa825ddf173ff92d531d1 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 25 Jun 2018 13:27:57 -0400 Subject: Add namespaces and initial constraints to database Add basic awareness of namespaces to the database. As part of this, add constraints so containers can only be added to pods in the same namespace. Signed-off-by: Matthew Heon --- libpod/state_test.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) (limited to 'libpod/state_test.go') diff --git a/libpod/state_test.go b/libpod/state_test.go index 4d5eb9713..7174bbf2a 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -2224,6 +2224,97 @@ func TestAddContainerToPodDependencyOutsidePodFails(t *testing.T) { }) } +func TestAddContainerToPodSameNamespaceSucceeds(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" + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Namespace = "test1" + testCtr.config.Pod = testPod.ID() + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.NoError(t, err) + + allCtrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(allCtrs)) + testContainersEqual(t, testCtr, allCtrs[0]) + }) +} + +func TestAddContainerToPodDifferentNamespaceFails(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" + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Namespace = "test2" + testCtr.config.Pod = testPod.ID() + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.Error(t, err) + + allCtrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(allCtrs)) + }) +} + +func TestAddContainerToPodNamespaceOnCtrFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testPod, err := getTestPod1(lockPath) + assert.NoError(t, err) + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Namespace = "test1" + testCtr.config.Pod = testPod.ID() + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.Error(t, err) + + allCtrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(allCtrs)) + }) +} + +func TestAddContainerToPodNamespaceOnPodFails(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" + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Pod = testPod.ID() + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.Error(t, err) + + allCtrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(allCtrs)) + }) +} + func TestRemoveContainerFromPodBadPodFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) -- cgit v1.2.3-54-g00ecf From e838dcb4bf7dc35b1bcf21edad6a1f6c59d969ab Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 25 Jun 2018 23:39:11 -0400 Subject: 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 --- libpod/boltdb_state.go | 112 ++++++++++++++++++++++++++++++++---- libpod/boltdb_state_internal.go | 50 +++++++++++++++- libpod/boltdb_state_linux.go | 8 +++ libpod/in_memory_state.go | 19 ++++++- libpod/options.go | 25 +++++++- libpod/runtime.go | 16 +++++- libpod/state.go | 4 ++ libpod/state_test.go | 123 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 17 deletions(-) (limited to 'libpod/state_test.go') 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) -- cgit v1.2.3-54-g00ecf From 92e6bd01a8623a61e37217155508856eb2be3316 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 12 Jul 2018 10:25:49 -0400 Subject: Add namespaces to in memory state Signed-off-by: Matthew Heon --- libpod/in_memory_state.go | 272 +++++++++++++++++++++++++++++++++++++++++++--- libpod/state_test.go | 9 ++ 2 files changed, 264 insertions(+), 17 deletions(-) (limited to 'libpod/state_test.go') diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 4ea8740fb..265170284 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -14,13 +14,19 @@ import ( // An InMemoryState is a purely in-memory state store type InMemoryState struct { - pods map[string]*Pod - containers map[string]*Container - ctrDepends map[string][]string - podContainers map[string]map[string]*Container - nameIndex *registrar.Registrar - idIndex *truncindex.TruncIndex - namespace string + pods map[string]*Pod + containers map[string]*Container + ctrDepends map[string][]string + podContainers map[string]map[string]*Container + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex + namespace string + namespaceIndexes map[string]*namespaceIndex +} + +type namespaceIndex struct { + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex } // NewInMemoryState initializes a new, empty in-memory state @@ -39,6 +45,8 @@ func NewInMemoryState() (State, error) { state.namespace = "" + state.namespaceIndexes = make(map[string]*namespaceIndex) + return state, nil } @@ -72,20 +80,43 @@ func (s *InMemoryState) Container(id string) (*Container, error) { return nil, errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found", id) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return nil, err + } + return ctr, nil } // LookupContainer retrieves a container by full ID, unique partial ID, or name func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) { + var ( + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex + ) + if idOrName == "" { return nil, ErrEmptyID } - fullID, err := s.nameIndex.Get(idOrName) + if s.namespace != "" { + nsIndex, ok := s.namespaceIndexes[s.namespace] + if !ok { + // We have no containers in the namespace + // Return false + return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) + } + nameIndex = nsIndex.nameIndex + idIndex = nsIndex.idIndex + } else { + nameIndex = s.nameIndex + idIndex = s.idIndex + } + + fullID, err := nameIndex.Get(idOrName) if err != nil { if err == registrar.ErrNameNotReserved { // What was passed is not a name, assume it's an ID - fullID, err = s.idIndex.Get(idOrName) + fullID, err = idIndex.Get(idOrName) if err != nil { if err == truncindex.ErrNotExist { return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) @@ -112,9 +143,18 @@ func (s *InMemoryState) HasContainer(id string) (bool, error) { return false, ErrEmptyID } - _, ok := s.containers[id] + ctr, ok := s.containers[id] + if ok { + if s.namespace != "" { + if s.namespace != ctr.config.Namespace { + return false, nil + } + return true, nil + } + return true, nil + } - return ok, nil + return false, nil } // AddContainer adds a container to the state @@ -159,6 +199,25 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { s.containers[ctr.ID()] = ctr + // If we're in a namespace, add us to that namespace's indexes + if ctr.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[ctr.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", ctr.Name()) + } + if err := nsIndex.idIndex.Add(ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", ctr.ID()) + } + } + // Add containers this container depends on for _, depCtr := range depCtrs { s.addCtrToDependsMap(ctr.ID(), depCtr) @@ -173,6 +232,10 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { // Almost no validity checks are performed, to ensure we can kick // misbehaving containers out of the state + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Ensure we don't remove a container which other containers depend on deps, ok := s.ctrDepends[ctr.ID()] if ok && len(deps) != 0 { @@ -193,6 +256,17 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { delete(s.ctrDepends, ctr.ID()) + if ctr.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", ctr.config.Namespace) + } + if err := nsIndex.idIndex.Delete(ctr.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", ctr.ID()) + } + nsIndex.nameIndex.Release(ctr.Name()) + } + // Remove us from container dependencies depCtrs := ctr.Dependencies() for _, depCtr := range depCtrs { @@ -217,6 +291,10 @@ func (s *InMemoryState) UpdateContainer(ctr *Container) error { return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + return nil } @@ -236,6 +314,10 @@ func (s *InMemoryState) SaveContainer(ctr *Container) error { return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + return nil } @@ -245,6 +327,16 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) { return nil, ErrCtrRemoved } + // If the container does not exist, return error + if _, ok := s.containers[ctr.ID()]; !ok { + ctr.valid = false + return nil, errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) + } + + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return nil, err + } + arr, ok := s.ctrDepends[ctr.ID()] if !ok { return []string{}, nil @@ -257,7 +349,13 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) { func (s *InMemoryState) AllContainers() ([]*Container, error) { ctrs := make([]*Container, 0, len(s.containers)) for _, ctr := range s.containers { - ctrs = append(ctrs, ctr) + if s.namespace != "" { + if ctr.config.Namespace == s.namespace { + ctrs = append(ctrs, ctr) + } + } else { + ctrs = append(ctrs, ctr) + } } return ctrs, nil @@ -274,21 +372,44 @@ func (s *InMemoryState) Pod(id string) (*Pod, error) { return nil, errors.Wrapf(ErrNoSuchPod, "no pod with id %s found", id) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + return pod, nil } // LookupPod retrieves a pod from the state from a full or unique partial ID or // a full name func (s *InMemoryState) LookupPod(idOrName string) (*Pod, error) { + var ( + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex + ) + if idOrName == "" { return nil, ErrEmptyID } - fullID, err := s.nameIndex.Get(idOrName) + if s.namespace != "" { + nsIndex, ok := s.namespaceIndexes[s.namespace] + if !ok { + // We have no containers in the namespace + // Return false + return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) + } + nameIndex = nsIndex.nameIndex + idIndex = nsIndex.idIndex + } else { + nameIndex = s.nameIndex + idIndex = s.idIndex + } + + fullID, err := nameIndex.Get(idOrName) if err != nil { if err == registrar.ErrNameNotReserved { // What was passed is not a name, assume it's an ID - fullID, err = s.idIndex.Get(idOrName) + fullID, err = idIndex.Get(idOrName) if err != nil { if err == truncindex.ErrNotExist { return nil, errors.Wrapf(ErrNoSuchPod, "no pod found with name or ID %s", idOrName) @@ -315,9 +436,18 @@ func (s *InMemoryState) HasPod(id string) (bool, error) { return false, ErrEmptyID } - _, ok := s.pods[id] + pod, ok := s.pods[id] + if ok { + if s.namespace != "" { + if s.namespace != pod.config.Namespace { + return false, nil + } + return true, nil + } + return true, nil + } - return ok, nil + return false, nil } // PodHasContainer checks if the given pod has a container with the given ID @@ -330,6 +460,10 @@ func (s *InMemoryState) PodHasContainer(pod *Pod, ctrID string) (bool, error) { return false, ErrEmptyID } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return false, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -346,6 +480,10 @@ func (s *InMemoryState) PodContainersByID(pod *Pod) ([]string, error) { return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -371,6 +509,10 @@ func (s *InMemoryState) PodContainers(pod *Pod) ([]*Container, error) { return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -417,6 +559,25 @@ func (s *InMemoryState) AddPod(pod *Pod) error { s.podContainers[pod.ID()] = make(map[string]*Container) + // If we're in a namespace, add us to that namespace's indexes + if pod.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[pod.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[pod.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(pod.Name(), pod.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", pod.Name()) + } + if err := nsIndex.idIndex.Add(pod.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", pod.ID()) + } + } + return nil } @@ -426,6 +587,10 @@ func (s *InMemoryState) RemovePod(pod *Pod) error { // Don't make many validity checks to ensure we can kick badly formed // pods out of the state + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -446,6 +611,17 @@ func (s *InMemoryState) RemovePod(pod *Pod) error { delete(s.podContainers, pod.ID()) s.nameIndex.Release(pod.Name()) + if pod.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[pod.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", pod.config.Namespace) + } + if err := nsIndex.idIndex.Delete(pod.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", pod.ID()) + } + nsIndex.nameIndex.Release(pod.Name()) + } + return nil } @@ -458,6 +634,10 @@ func (s *InMemoryState) RemovePodContainers(pod *Pod) error { return errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + // Get pod containers podCtrs, ok := s.podContainers[pod.ID()] if !ok { @@ -560,6 +740,25 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { // Add container to pod containers podCtrs[ctr.ID()] = ctr + // If we're in a namespace, add us to that namespace's indexes + if ctr.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[ctr.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", ctr.Name()) + } + if err := nsIndex.idIndex.Add(ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", ctr.ID()) + } + } + // Add containers this container depends on for _, depCtr := range depCtrs { s.addCtrToDependsMap(ctr.ID(), depCtr) @@ -578,6 +777,10 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { return errors.Wrapf(ErrCtrRemoved, "container %s is not valid and cannot be removed from the pod", ctr.ID()) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Ensure we don't remove a container which other containers depend on deps, ok := s.ctrDepends[ctr.ID()] if ok && len(deps) != 0 { @@ -617,6 +820,17 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { // Remove the container from the pod delete(podCtrs, ctr.ID()) + if ctr.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", ctr.config.Namespace) + } + if err := nsIndex.idIndex.Delete(ctr.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", ctr.ID()) + } + nsIndex.nameIndex.Release(ctr.Name()) + } + // Remove us from container dependencies depCtrs := ctr.Dependencies() for _, depCtr := range depCtrs { @@ -633,6 +847,10 @@ func (s *InMemoryState) UpdatePod(pod *Pod) error { return ErrPodRemoved } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -648,6 +866,10 @@ func (s *InMemoryState) SavePod(pod *Pod) error { return ErrPodRemoved } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -660,7 +882,13 @@ func (s *InMemoryState) SavePod(pod *Pod) error { func (s *InMemoryState) AllPods() ([]*Pod, error) { pods := make([]*Pod, 0, len(s.pods)) for _, pod := range s.pods { - pods = append(pods, pod) + if s.namespace != "" { + if s.namespace == pod.config.Namespace { + pods = append(pods, pod) + } + } else { + pods = append(pods, pod) + } } return pods, nil @@ -705,3 +933,13 @@ func (s *InMemoryState) removeCtrFromDependsMap(ctrID, dependsID string) { s.ctrDepends[dependsID] = newArr } } + +// Check if we can access a pod or container, or if that is blocked by +// namespaces. +func (s *InMemoryState) checkNSMatch(id, ns string) error { + if s.namespace != "" && s.namespace != ns { + return errors.Wrapf(ErrNSMismatch, "cannot access %s as it is in namespace %q and we are in namespace %q", + id, ns, s.namespace) + } + return nil +} diff --git a/libpod/state_test.go b/libpod/state_test.go index e5c5a1de0..8a678ba82 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -634,6 +634,15 @@ func TestContainerInUseInvalidContainer(t *testing.T) { }) } +func TestContainerInUseCtrNotInState(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + _, err := state.ContainerInUse(testCtr) + assert.Error(t, err) + }) +} + func TestContainerInUseOneContainer(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr1, err := getTestCtr1(lockPath) -- cgit v1.2.3-54-g00ecf From 572fd75d226550ac1576bf38812e5417a9eddeee Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 18 Jul 2018 13:47:08 -0400 Subject: Add tests for state namespacing Signed-off-by: Matthew Heon --- libpod/boltdb_state.go | 9 +- libpod/state_test.go | 723 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 720 insertions(+), 12 deletions(-) (limited to 'libpod/state_test.go') diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 470201348..b2a246ca8 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -8,6 +8,7 @@ import ( "github.com/boltdb/bolt" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // BoltState is a state implementation backed by a Bolt DB @@ -29,6 +30,8 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) { state.namespace = "" state.namespaceBytes = nil + logrus.Debugf("Initializing boltdb state at %s", path) + // Make the directory that will hold container lockfiles if err := os.MkdirAll(lockDir, 0750); err != nil { // The directory is allowed to exist @@ -367,10 +370,10 @@ func (s *BoltState) HasContainer(id string) (bool, error) { return err } - ctrExists := ctrBucket.Bucket(ctrID) - if ctrExists != nil { + ctrDB := ctrBucket.Bucket(ctrID) + if ctrDB != nil { if s.namespaceBytes != nil { - nsBytes := ctrBucket.Get(namespaceKey) + nsBytes := ctrDB.Get(namespaceKey) if bytes.Equal(nsBytes, s.namespaceBytes) { exists = true } diff --git a/libpod/state_test.go b/libpod/state_test.go index 8a678ba82..0c924a1f1 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -345,6 +345,59 @@ func TestGetContainerWithEmptyIDFails(t *testing.T) { }) } +func TestGetContainerInDifferentNamespaceFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test2" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + _, err = state.Container(testCtr.ID()) + assert.Error(t, err) + }) +} + +func TestGetContainerInSameNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + ctr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, ctr) + }) +} + +func TestGetContainerInNamespaceWhileNotInNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + ctr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, ctr) + }) +} + func TestLookupContainerWithEmptyIDFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.LookupContainer("") @@ -448,6 +501,66 @@ func TestLookupCtrByPodIDFails(t *testing.T) { }) } +func TestLookupCtrInSameNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + ctr, err := state.LookupContainer(testCtr.ID()) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, ctr) + }) +} + +func TestLookupCtrInDifferentNamespaceFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.LookupContainer(testCtr.ID()) + assert.Error(t, err) + }) +} + +func TestLookupContainerMatchInDifferentNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr1, err := getTestContainer(strings.Repeat("0", 32), "test1", lockPath) + assert.NoError(t, err) + testCtr1.config.Namespace = "test2" + testCtr2, err := getTestContainer(strings.Repeat("0", 31)+"1", "test2", lockPath) + assert.NoError(t, err) + testCtr2.config.Namespace = "test1" + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.NoError(t, err) + + state.SetNamespace("test1") + + ctr, err := state.LookupContainer("000") + assert.NoError(t, err) + + testContainersEqual(t, testCtr2, ctr) + }) +} + func TestHasContainerEmptyIDFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.HasContainer("") @@ -491,6 +604,42 @@ func TestHasContainerPodIDIsFalse(t *testing.T) { }) } +func TestHasContainerSameNamespaceIsTrue(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + exists, err := state.HasContainer(testCtr.ID()) + assert.NoError(t, err) + assert.True(t, exists) + }) +} + +func TestHasContainerDifferentNamespaceIsFalse(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + exists, err := state.HasContainer(testCtr.ID()) + assert.NoError(t, err) + assert.False(t, exists) + }) +} + func TestSaveAndUpdateContainer(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) @@ -516,6 +665,35 @@ func TestSaveAndUpdateContainer(t *testing.T) { }) } +func TestSaveAndUpdateContainerSameNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + retrievedCtr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + retrievedCtr.state.State = ContainerStateStopped + retrievedCtr.state.ExitCode = 127 + retrievedCtr.state.FinishedTime = time.Now() + + err = state.SaveContainer(retrievedCtr) + assert.NoError(t, err) + + err = state.UpdateContainer(testCtr) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, retrievedCtr) + }) +} + func TestUpdateContainerNotInDatabaseReturnsError(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) @@ -534,6 +712,23 @@ func TestUpdateInvalidContainerReturnsError(t *testing.T) { }) } +func TestUpdateContainerNotInNamespaceReturnsError(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err = state.UpdateContainer(testCtr) + assert.Error(t, err) + }) +} + func TestSaveInvalidContainerReturnsError(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { err := state.SaveContainer(&Container{config: &ContainerConfig{ID: "1234"}}) @@ -552,6 +747,23 @@ func TestSaveContainerNotInStateReturnsError(t *testing.T) { }) } +func TestSaveContainerNotInNamespaceReturnsError(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err = state.SaveContainer(testCtr) + assert.Error(t, err) + }) +} + func TestRemoveContainer(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) @@ -584,6 +796,33 @@ func TestRemoveNonexistantContainerFails(t *testing.T) { }) } +func TestRemoveContainerNotInNamespaceFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(ctrs)) + + state.SetNamespace("test2") + + err = state.RemoveContainer(testCtr) + assert.Error(t, err) + + state.SetNamespace("") + + ctrs2, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(ctrs2)) + }) +} + func TestGetAllContainersOnNewStateIsEmpty(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { ctrs, err := state.AllContainers() @@ -627,6 +866,50 @@ func TestGetAllContainersTwoContainers(t *testing.T) { }) } +func TestGetAllContainersNoContainerInNamespace(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs)) + }) +} + +func TestGetContainerOneContainerInNamespace(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr1, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr1.config.Namespace = "test1" + + testCtr2, err := getTestCtr2(lockPath) + assert.NoError(t, err) + + err = state.AddContainer(testCtr1) + assert.NoError(t, err) + + err = state.AddContainer(testCtr2) + assert.NoError(t, err) + + state.SetNamespace("test1") + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 1, len(ctrs)) + + testContainersEqual(t, testCtr1, ctrs[0]) + }) +} + func TestContainerInUseInvalidContainer(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.ContainerInUse(&Container{}) @@ -638,7 +921,24 @@ func TestContainerInUseCtrNotInState(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) assert.NoError(t, err) - _, err := state.ContainerInUse(testCtr) + _, err = state.ContainerInUse(testCtr) + assert.Error(t, err) + }) +} + +func TestContainerInUseCtrNotInNamespace(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.ContainerInUse(testCtr) assert.Error(t, err) }) } @@ -1031,6 +1331,42 @@ func TestGetPodByCtrID(t *testing.T) { }) } +func TestGetPodInNamespaceSucceeds(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test1") + + statePod, err := state.Pod(testPod.ID()) + assert.NoError(t, err) + + testPodsEqual(t, testPod, statePod) + }) +} + +func TestGetPodPodNotInNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.Pod(testPod.ID()) + assert.Error(t, err) + }) +} + func TestLookupPodEmptyID(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.LookupPod("") @@ -1135,6 +1471,69 @@ func TestLookupPodByCtrName(t *testing.T) { }) } +func TestLookupPodInSameNamespaceSucceeds(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test1") + + statePod, err := state.LookupPod(testPod.ID()) + assert.NoError(t, err) + + testPodsEqual(t, testPod, statePod) + }) +} + +func TestLookupPodInDifferentNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.LookupPod(testPod.ID()) + assert.Error(t, err) + }) +} + +func TestLookupPodOneInDifferentNamespaceFindsRightPod(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testPod1, err := getTestPod(strings.Repeat("1", 32), "test1", lockPath) + assert.NoError(t, err) + + testPod1.config.Namespace = "test1" + + testPod2, err := getTestPod(strings.Repeat("1", 31)+"2", "test2", lockPath) + assert.NoError(t, err) + + testPod2.config.Namespace = "test2" + + err = state.AddPod(testPod1) + assert.NoError(t, err) + + err = state.AddPod(testPod2) + assert.NoError(t, err) + + state.SetNamespace("test1") + + pod, err := state.LookupPod(strings.Repeat("1", 5)) + assert.NoError(t, err) + + testPodsEqual(t, testPod1, pod) + }) +} + func TestHasPodEmptyIDErrors(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.HasPod("") @@ -1155,38 +1554,74 @@ func TestHasPodWrongIDFalse(t *testing.T) { testPod, err := getTestPod1(lockPath) assert.NoError(t, err) - err = state.AddPod(testPod) + err = state.AddPod(testPod) + assert.NoError(t, err) + + exist, err := state.HasPod(strings.Repeat("a", 32)) + assert.NoError(t, err) + assert.False(t, exist) + }) +} + +func TestHasPodRightIDTrue(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testPod, err := getTestPod1(lockPath) + assert.NoError(t, err) + + err = state.AddPod(testPod) + assert.NoError(t, err) + + exist, err := state.HasPod(testPod.ID()) + assert.NoError(t, err) + assert.True(t, exist) + }) +} + +func TestHasPodCtrIDFalse(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + err = state.AddContainer(testCtr) assert.NoError(t, err) - exist, err := state.HasPod(strings.Repeat("a", 32)) + exist, err := state.HasPod(testCtr.ID()) assert.NoError(t, err) assert.False(t, exist) }) } -func TestHasPodRightIDTrue(t *testing.T) { +func TestHasPodSameNamespaceSucceeds(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" + err = state.AddPod(testPod) assert.NoError(t, err) + state.SetNamespace("test1") + exist, err := state.HasPod(testPod.ID()) assert.NoError(t, err) assert.True(t, exist) }) } -func TestHasPodCtrIDFalse(t *testing.T) { +func TestHasPodDifferentNamespaceFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { - testCtr, err := getTestCtr1(lockPath) + testPod, err := getTestPod1(lockPath) assert.NoError(t, err) - err = state.AddContainer(testCtr) + testPod.config.Namespace = "test1" + + err = state.AddPod(testPod) assert.NoError(t, err) - exist, err := state.HasPod(testCtr.ID()) + state.SetNamespace("test2") + + exist, err := state.HasPod(testPod.ID()) assert.NoError(t, err) assert.False(t, exist) }) @@ -1427,6 +1862,29 @@ func TestRemovePodAfterEmptySucceeds(t *testing.T) { }) } +func TestRemovePodNotInNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err = state.RemovePod(testPod) + assert.Error(t, err) + + state.SetNamespace("") + + allPods, err := state.AllPods() + assert.NoError(t, err) + assert.Equal(t, 1, len(allPods)) + }) +} + func TestAllPodsEmptyOnEmptyState(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { allPods, err := state.AllPods() @@ -1489,6 +1947,52 @@ func TestAllPodsMultiplePods(t *testing.T) { }) } +func TestAllPodsPodInDifferentNamespaces(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + allPods, err := state.AllPods() + assert.NoError(t, err) + assert.Equal(t, 0, len(allPods)) + }) +} + +func TestAllPodsOnePodInDifferentNamespace(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testPod1, err := getTestPod1(lockPath) + assert.NoError(t, err) + + testPod1.config.Namespace = "test1" + + testPod2, err := getTestPod2(lockPath) + assert.NoError(t, err) + + testPod2.config.Namespace = "test2" + + err = state.AddPod(testPod1) + assert.NoError(t, err) + + err = state.AddPod(testPod2) + assert.NoError(t, err) + + state.SetNamespace("test1") + + allPods, err := state.AllPods() + assert.NoError(t, err) + assert.Equal(t, 1, len(allPods)) + + testPodsEqual(t, testPod1, allPods[0]) + }) +} + func TestPodHasContainerNoSuchPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.PodHasContainer(&Pod{config: &PodConfig{}}, strings.Repeat("0", 32)) @@ -1565,6 +2069,23 @@ func TestPodHasContainerSucceeds(t *testing.T) { }) } +func TestPodHasContainerPodNotInNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.PodHasContainer(testPod, strings.Repeat("2", 32)) + assert.Error(t, err) + }) +} + func TestPodContainersByIDInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.PodContainersByID(&Pod{config: &PodConfig{}}) @@ -1667,6 +2188,23 @@ func TestPodContainersByIDMultipleContainers(t *testing.T) { }) } +func TestPodContainerByIDPodNotInNamespace(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.PodContainersByID(testPod) + assert.Error(t, err) + }) +} + func TestPodContainersInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.PodContainers(&Pod{config: &PodConfig{}}) @@ -1674,7 +2212,7 @@ func TestPodContainersInvalidPod(t *testing.T) { }) } -func TestPodContainerdPodNotInState(t *testing.T) { +func TestPodContainersPodNotInState(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testPod, err := getTestPod1(lockPath) assert.NoError(t, err) @@ -1770,6 +2308,23 @@ func TestPodContainersMultipleContainers(t *testing.T) { }) } +func TestPodContainersPodNotInNamespace(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.PodContainers(testPod) + assert.Error(t, err) + }) +} + func TestRemovePodContainersInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { err := state.RemovePodContainers(&Pod{config: &PodConfig{}}) @@ -1926,6 +2481,23 @@ func TestRemovePodContainerDependencyInPod(t *testing.T) { }) } +func TestRemoveContainersNotInNamespace(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err := state.RemovePodContainers(testPod) + assert.Error(t, err) + }) +} + func TestAddContainerToPodInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) @@ -2620,6 +3192,76 @@ func TestRemoveContainerFromPodWithDependencySucceedsAfterDepRemoved(t *testing. }) } +func TestRemoveContainerFromPodSameNamespaceSucceeds(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" + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Pod = testPod.ID() + + testCtr.config.Namespace = "test1" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.NoError(t, err) + + state.SetNamespace("test1") + + err = state.RemoveContainerFromPod(testPod, testCtr) + assert.NoError(t, err) + + ctrs, err := state.PodContainers(testPod) + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs)) + + allCtrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(allCtrs)) + }) +} + +func TestRemoveContainerFromPodDifferentNamespaceFails(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" + + testCtr, err := getTestCtr2(lockPath) + assert.NoError(t, err) + testCtr.config.Pod = testPod.ID() + + testCtr.config.Namespace = "test1" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + err = state.AddContainerToPod(testPod, testCtr) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err = state.RemoveContainerFromPod(testPod, testCtr) + assert.Error(t, err) + + state.SetNamespace("") + + 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)) + }) +} + func TestUpdatePodInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { err := state.UpdatePod(&Pod{config: &PodConfig{}}) @@ -2637,6 +3279,23 @@ func TestUpdatePodPodNotInStateFails(t *testing.T) { }) } +func TestUpdatePodNotInNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.UpdatePod(testPod) + assert.Error(t, err) + }) +} + func TestSavePodInvalidPod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { err := state.SavePod(&Pod{config: &PodConfig{}}) @@ -2654,6 +3313,23 @@ func TestSavePodPodNotInStateFails(t *testing.T) { }) } +func TestSavePodNotInNamespaceFails(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + _, err = state.SavePod(testPod) + assert.Error(t, err) + }) +} + func TestSaveAndUpdatePod(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testPod, err := getTestPod1(lockPath) @@ -2678,3 +3354,32 @@ func TestSaveAndUpdatePod(t *testing.T) { testPodsEqual(t, testPod, statePod) }) } + +func TestSaveAndUpdatePodSameNamespace(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" + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test1") + + statePod, err := state.Pod(testPod.ID()) + assert.NoError(t, err) + + testPodsEqual(t, testPod, statePod) + + testPod.state.CgroupPath = "/new/path/for/test" + + err = state.SavePod(testPod) + assert.NoError(t, err) + + err = state.UpdatePod(statePod) + assert.NoError(t, err) + + testPodsEqual(t, testPod, statePod) + }) +} -- cgit v1.2.3-54-g00ecf From 7b30659629deaddafc7fc925d869324ae754c216 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 19 Jul 2018 11:24:42 -0400 Subject: Enforce namespace checks on container add Signed-off-by: Matthew Heon --- libpod/boltdb_state_internal.go | 5 ++ libpod/in_memory_state.go | 8 ++++ libpod/state_test.go | 100 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 3 deletions(-) (limited to 'libpod/state_test.go') diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 81c9f49f5..b03c11531 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -266,6 +266,11 @@ func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error // 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) diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 265170284..55be89d4c 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -172,6 +172,10 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { return errors.Wrapf(ErrInvalidArg, "cannot add a container that is in a pod with AddContainer, use AddContainerToPod") } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // There are potential race conditions with this // But in-memory state is intended purely for testing and not production // use, so this should be fine. @@ -692,6 +696,10 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { ctr.ID(), ctr.config.Namespace, pod.ID(), pod.config.Namespace) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Retrieve pod containers list podCtrs, ok := s.podContainers[pod.ID()] if !ok { diff --git a/libpod/state_test.go b/libpod/state_test.go index 0c924a1f1..4e9ba8850 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -331,6 +331,45 @@ func TestAddCtrDepInDifferentNamespaceFails(t *testing.T) { }) } +func TestAddCtrSameNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + state.SetNamespace("test1") + + err = state.AddContainer(testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, retrievedCtr) + }) +} + +func TestAddCtrDifferentNamespaceFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + + state.SetNamespace("test2") + + err = state.AddContainer(testCtr) + assert.Error(t, err) + + state.SetNamespace("") + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs)) + }) +} + func TestGetNonexistentContainerFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { _, err := state.Container("does not exist") @@ -2493,7 +2532,7 @@ func TestRemoveContainersNotInNamespace(t *testing.T) { state.SetNamespace("test2") - err := state.RemovePodContainers(testPod) + err = state.RemovePodContainers(testPod) assert.Error(t, err) }) } @@ -3019,6 +3058,61 @@ func TestAddContainerToPodNamespaceOnPodFails(t *testing.T) { }) } +func TestAddCtrToPodSameNamespaceSucceeds(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testPod, err := getTestPod2(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + testPod.config.Namespace = "test1" + testCtr.config.Pod = testPod.ID() + + err = state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test1") + + err = state.AddContainerToPod(testPod, testCtr) + assert.NoError(t, err) + + retrievedCtr, err := state.Container(testCtr.ID()) + assert.NoError(t, err) + + testContainersEqual(t, testCtr, retrievedCtr) + }) +} + +func TestAddCtrToPodDifferentNamespaceFails(t *testing.T) { + runForAllStates(t, func(t *testing.T, state State, lockPath string) { + testCtr, err := getTestCtr1(lockPath) + assert.NoError(t, err) + + testPod, err := getTestPod2(lockPath) + assert.NoError(t, err) + + testCtr.config.Namespace = "test1" + testPod.config.Namespace = "test1" + testCtr.config.Pod = testPod.ID() + + state.AddPod(testPod) + assert.NoError(t, err) + + state.SetNamespace("test2") + + err = state.AddContainerToPod(testPod, testCtr) + assert.Error(t, err) + + state.SetNamespace("") + + ctrs, err := state.AllContainers() + assert.NoError(t, err) + assert.Equal(t, 0, len(ctrs)) + }) +} + func TestRemoveContainerFromPodBadPodFails(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { testCtr, err := getTestCtr1(lockPath) @@ -3291,7 +3385,7 @@ func TestUpdatePodNotInNamespaceFails(t *testing.T) { state.SetNamespace("test2") - _, err = state.UpdatePod(testPod) + err = state.UpdatePod(testPod) assert.Error(t, err) }) } @@ -3325,7 +3419,7 @@ func TestSavePodNotInNamespaceFails(t *testing.T) { state.SetNamespace("test2") - _, err = state.SavePod(testPod) + err = state.SavePod(testPod) assert.Error(t, err) }) } -- cgit v1.2.3-54-g00ecf From 84afa32493335e74d6094c3723c53cf38ac9e6bb Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 19 Jul 2018 12:04:44 -0400 Subject: Ensure pods are part of the set namespace when added Signed-off-by: Matthew Heon --- libpod/in_memory_state.go | 4 ++++ libpod/state_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'libpod/state_test.go') diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 55be89d4c..e323b069c 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -542,6 +542,10 @@ func (s *InMemoryState) AddPod(pod *Pod) error { return errors.Wrapf(ErrPodRemoved, "pod %s is not valid and cannot be added", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; ok { return errors.Wrapf(ErrPodExists, "pod with ID %s already exists in state", pod.ID()) } diff --git a/libpod/state_test.go b/libpod/state_test.go index 4e9ba8850..30638024c 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -1790,6 +1790,47 @@ func TestAddPodCtrNameConflictFails(t *testing.T) { }) } +func TestAddPodSameNamespaceSucceeds(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" + + state.SetNamespace("test1") + + err = state.AddPod(testPod) + assert.NoError(t, err) + + allPods, err := state.AllPods() + assert.NoError(t, err) + assert.Equal(t, 1, len(allPods)) + + testPodsEqual(t, testPod, allPods[0]) + assert.Equal(t, testPod.valid, allPods[0].valid) + }) +} + +func TestAddPodDifferentNamespaceFails(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" + + state.SetNamespace("test2") + + err = state.AddPod(testPod) + assert.Error(t, err) + + state.SetNamespace("") + + allPods, err := state.AllPods() + assert.NoError(t, err) + assert.Equal(t, 0, len(allPods)) + }) +} + func TestRemovePodInvalidPodErrors(t *testing.T) { runForAllStates(t, func(t *testing.T, state State, lockPath string) { err := state.RemovePod(&Pod{config: &PodConfig{}}) -- cgit v1.2.3-54-g00ecf