aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/libpodruntime/runtime.go4
-rw-r--r--cmd/podman/main.go5
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman.1.md7
-rw-r--r--libpod.conf8
-rw-r--r--libpod/boltdb_state.go232
-rw-r--r--libpod/boltdb_state_internal.go77
-rw-r--r--libpod/boltdb_state_linux.go8
-rw-r--r--libpod/container.go8
-rw-r--r--libpod/container_inspect.go1
-rw-r--r--libpod/errors.go4
-rw-r--r--libpod/in_memory_state.go295
-rw-r--r--libpod/options.go61
-rw-r--r--libpod/pod.go8
-rw-r--r--libpod/runtime.go21
-rw-r--r--libpod/runtime_ctr.go6
-rw-r--r--libpod/runtime_pod_linux.go6
-rw-r--r--libpod/state.go107
-rw-r--r--libpod/state_test.go1065
-rw-r--r--pkg/inspect/inspect.go1
-rw-r--r--test/e2e/namespace_test.go51
21 files changed, 1901 insertions, 75 deletions
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go
index 3216d288b..9d1347cc5 100644
--- a/cmd/podman/libpodruntime/runtime.go
+++ b/cmd/podman/libpodruntime/runtime.go
@@ -88,6 +88,10 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions
// TODO CLI flags for image config?
// TODO CLI flag for signature policy?
+ if c.GlobalIsSet("namespace") {
+ options = append(options, libpod.WithNamespace(c.GlobalString("namespace")))
+ }
+
if c.GlobalIsSet("runtime") {
options = append(options, libpod.WithOCIRuntime(c.GlobalString("runtime")))
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 3dbf196c2..dbd7c1155 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -173,6 +173,11 @@ func main() {
Value: "error",
},
cli.StringFlag{
+ Name: "namespace",
+ Usage: "set the libpod namespace, used to create separate views of the containers and pods on the system",
+ Value: "",
+ },
+ cli.StringFlag{
Name: "root",
Usage: "path to the root directory in which data, including images, is stored",
},
diff --git a/completions/bash/podman b/completions/bash/podman
index 11203e52d..5d20bab3e 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2224,6 +2224,7 @@ _podman_podman() {
--storage-driver
--storage-opt
--log-level
+ --namespace
"
local boolean_options="
--help -h
diff --git a/docs/podman.1.md b/docs/podman.1.md
index 5581e0569..1ca420d12 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -37,7 +37,12 @@ Path to where the cpu performance results should be written
**--log-level**
-log messages above specified level: debug, info, warn, error (default), fatal or panic
+Log messages above specified level: debug, info, warn, error (default), fatal or panic
+
+**--namespace**
+
+Set libpod namespace. Namespaces are used to separate groups of containers and pods in libpod's state.
+When namespace is set, created containers and pods will join the given namespace, and only containers and pods in the given namespace will be visible to Podman.
**--root**=**value**
diff --git a/libpod.conf b/libpod.conf
index da5e010f2..3a363e256 100644
--- a/libpod.conf
+++ b/libpod.conf
@@ -57,3 +57,11 @@ cni_plugin_dir = [
"/usr/lib/cni",
"/opt/cni/bin"
]
+
+# Default libpod namespace
+# If libpod is joined to a namespace, it will see only containers and pods
+# that were created in the same namespace, and will create new containers and
+# pods in that namespace.
+# The default namespace is "", which corresponds to no namespace. When no
+# namespace is set, all containers and pods are visible.
+#namespace = ""
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 45d09348e..afbaecffb 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -1,28 +1,64 @@
package libpod
import (
+ "bytes"
"encoding/json"
"os"
"strings"
"github.com/boltdb/bolt"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// 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
}
+// A brief description of the format of the BoltDB state:
+// At the top level, the following buckets are created:
+// - idRegistryBkt: Maps ID to Name for containers and pods.
+// Used to ensure container and pod IDs are globally unique.
+// - nameRegistryBkt: Maps Name to ID for containers and pods.
+// Used to ensure container and pod names are globally unique.
+// - nsRegistryBkt: Maps ID to namespace for all containers and pods.
+// Used during lookup operations to determine if a given ID is in the same
+// namespace as the state.
+// - ctrBkt: Contains a sub-bucket for each container in the state.
+// Each sub-bucket has config and state keys holding the container's JSON
+// encoded configuration and state (respectively), an optional netNS key
+// containing the path to the container's network namespace, a dependencies
+// bucket containing the container's dependencies, and an optional pod key
+// containing the ID of the pod the container is joined to.
+// - allCtrsBkt: Map of ID to name containing only containers. Used for
+// container lookup operations.
+// - podBkt: Contains a sub-bucket for each pod in the state.
+// Each sub-bucket has config and state keys holding the pod's JSON encoded
+// configuration and state, plus a containers sub bucket holding the IDs of
+// containers in the pod.
+// - allPodsBkt: Map of ID to name containing only pods. Used for pod lookup
+// operations.
+// - runtimeConfigBkt: Contains configuration of the libpod instance that
+// initially created the database. This must match for any further instances
+// that access the database, to ensure that state mismatches with
+// containers/storage do not occur.
+
// NewBoltState creates a new bolt-backed state database
func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
state := new(BoltState)
state.dbPath = path
state.lockDir = lockDir
state.runtime = runtime
+ 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 {
@@ -47,6 +83,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 +232,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,11 +315,18 @@ 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))
if ctrExists != nil {
- // A full container ID was given
+ // A full container ID was given.
+ // It might not be in our namespace, but
+ // getContainerFromDB() will handle that case.
id = []byte(idOrName)
} else {
// They did not give us a full container ID.
@@ -274,6 +334,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)
@@ -332,9 +400,16 @@ func (s *BoltState) HasContainer(id string) (bool, error) {
return err
}
- ctrExists := ctrBucket.Bucket(ctrID)
- if ctrExists != nil {
- exists = true
+ ctrDB := ctrBucket.Bucket(ctrID)
+ if ctrDB != nil {
+ if s.namespaceBytes != nil {
+ nsBytes := ctrDB.Get(namespaceKey)
+ if bytes.Equal(nsBytes, s.namespaceBytes) {
+ exists = true
+ }
+ } else {
+ exists = true
+ }
}
return nil
@@ -383,7 +458,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 s.removeContainer(ctr, nil, tx)
})
return err
}
@@ -398,6 +473,10 @@ func (s *BoltState) UpdateContainer(ctr *Container) error {
return ErrCtrRemoved
}
+ if s.namespace != "" && 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 +539,10 @@ func (s *BoltState) SaveContainer(ctr *Container) error {
return ErrCtrRemoved
}
+ if s.namespace != "" && 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 +602,10 @@ func (s *BoltState) ContainerInUse(ctr *Container) ([]string, error) {
return nil, ErrCtrRemoved
}
+ if s.namespace != "" && 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 +689,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,11 +780,18 @@ 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))
if podExists != nil {
- // A full pod ID was given
+ // A full pod ID was given.
+ // It might not be in our namespace, but getPodFromDB()
+ // will handle that case.
id = []byte(idOrName)
} else {
// They did not give us a full pod ID.
@@ -694,6 +799,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 +832,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 +869,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
@@ -783,6 +902,10 @@ func (s *BoltState) PodHasContainer(pod *Pod, id string) (bool, error) {
return false, ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return false, errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
ctrID := []byte(id)
podID := []byte(pod.ID())
@@ -813,6 +936,11 @@ func (s *BoltState) PodHasContainer(pod *Pod, id string) (bool, error) {
return errors.Wrapf(ErrInternal, "pod %s missing containers bucket in DB", pod.ID())
}
+ // Don't bother with a namespace check on the container -
+ // We maintain the invariant that container namespaces must
+ // match the namespace of the pod they join.
+ // We already checked the pod namespace, so we should be fine.
+
ctr := podCtrs.Get(ctrID)
if ctr != nil {
exists = true
@@ -837,6 +965,10 @@ func (s *BoltState) PodContainersByID(pod *Pod) ([]string, error) {
return nil, ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return nil, errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
podID := []byte(pod.ID())
ctrs := []string{}
@@ -895,6 +1027,10 @@ func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) {
return nil, ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return nil, errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
podID := []byte(pod.ID())
ctrs := []*Container{}
@@ -961,9 +1097,18 @@ func (s *BoltState) AddPod(pod *Pod) error {
return ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
podID := []byte(pod.ID())
podName := []byte(pod.Name())
+ var podNamespace []byte
+ if pod.config.Namespace != "" {
+ podNamespace = []byte(pod.config.Namespace)
+ }
+
podConfigJSON, err := json.Marshal(pod.config)
if err != nil {
return errors.Wrapf(err, "error marshalling pod %s config to JSON", pod.ID())
@@ -1001,6 +1146,11 @@ func (s *BoltState) AddPod(pod *Pod) error {
return err
}
+ nsBkt, err := getNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
// Check if we already have something with the given ID and name
idExist := idsBkt.Get(podID)
if idExist != nil {
@@ -1031,6 +1181,15 @@ func (s *BoltState) AddPod(pod *Pod) error {
return errors.Wrapf(err, "error storing pod %s state JSON in DB", pod.ID())
}
+ if podNamespace != nil {
+ if err := newPod.Put(namespaceKey, podNamespace); err != nil {
+ return errors.Wrapf(err, "error storing pod %s namespace in DB", pod.ID())
+ }
+ if err := nsBkt.Put(podID, podNamespace); err != nil {
+ return errors.Wrapf(err, "error storing pod %s namespace in DB", pod.ID())
+ }
+ }
+
// Add us to the ID and names buckets
if err := idsBkt.Put(podID, podName); err != nil {
return errors.Wrapf(err, "error storing pod %s ID in DB", pod.ID())
@@ -1062,6 +1221,10 @@ func (s *BoltState) RemovePod(pod *Pod) error {
return ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
podID := []byte(pod.ID())
podName := []byte(pod.Name())
@@ -1092,6 +1255,11 @@ func (s *BoltState) RemovePod(pod *Pod) error {
return err
}
+ nsBkt, err := getNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
// Check if the pod exists
podDB := podBkt.Bucket(podID)
if podDB == nil {
@@ -1120,6 +1288,9 @@ func (s *BoltState) RemovePod(pod *Pod) error {
if err := namesBkt.Delete(podName); err != nil {
return errors.Wrapf(err, "error removing pod %s name (%s) from DB", pod.ID(), pod.Name())
}
+ if err := nsBkt.Delete(podID); err != nil {
+ return errors.Wrapf(err, "error removing pod %s namespace from DB", pod.ID())
+ }
if err := allPodsBkt.Delete(podID); err != nil {
return errors.Wrapf(err, "error removing pod %s ID from all pods bucket in DB", pod.ID())
}
@@ -1146,6 +1317,10 @@ func (s *BoltState) RemovePodContainers(pod *Pod) error {
return ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
podID := []byte(pod.ID())
db, err := s.getDBCon()
@@ -1292,6 +1467,15 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
return ErrPodRemoved
}
+ if s.namespace != "" {
+ if s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+ if s.namespace != ctr.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "container %s in in namespace %q but we are in namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
+ }
+ }
+
if ctr.config.Pod == "" {
return errors.Wrapf(ErrNoSuchPod, "container %s is not part of a pod, use RemoveContainer instead", ctr.ID())
}
@@ -1307,7 +1491,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 s.removeContainer(ctr, pod, tx)
})
return err
}
@@ -1322,6 +1506,10 @@ func (s *BoltState) UpdatePod(pod *Pod) error {
return ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
newState := new(podState)
db, err := s.getDBCon()
@@ -1375,6 +1563,10 @@ func (s *BoltState) SavePod(pod *Pod) error {
return ErrPodRemoved
}
+ if s.namespace != "" && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q but we are in namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+
stateJSON, err := json.Marshal(pod.state)
if err != nil {
return errors.Wrapf(err, "error marshalling pod %s state to JSON", pod.ID())
@@ -1451,9 +1643,15 @@ func (s *BoltState) AllPods() ([]*Pod, error) {
pod.config = new(PodConfig)
pod.state = new(podState)
- pods = append(pods, pod)
+ if err := s.getPodFromDB(id, pod, podBucket); err != nil {
+ if errors.Cause(err) != ErrNSMismatch {
+ return err
+ }
+ } else {
+ pods = append(pods, pod)
+ }
- return s.getPodFromDB(id, pod, podBucket)
+ return nil
})
return err
})
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index 69e7bee21..b03c11531 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "bytes"
"encoding/json"
"path/filepath"
"runtime"
@@ -15,6 +16,7 @@ import (
const (
idRegistryName = "id-registry"
nameRegistryName = "name-registry"
+ nsRegistryName = "ns-registry"
ctrName = "ctr"
allCtrsName = "all-ctrs"
podName = "pod"
@@ -27,11 +29,13 @@ const (
netNSName = "netns"
containersName = "containers"
podIDName = "pod-id"
+ namespaceName = "namespace"
)
var (
idRegistryBkt = []byte(idRegistryName)
nameRegistryBkt = []byte(nameRegistryName)
+ nsRegistryBkt = []byte(nsRegistryName)
ctrBkt = []byte(ctrName)
allCtrsBkt = []byte(allCtrsName)
podBkt = []byte(podName)
@@ -44,6 +48,7 @@ var (
netNSKey = []byte(netNSName)
containersBkt = []byte(containersName)
podIDKey = []byte(podIDName)
+ namespaceKey = []byte(namespaceName)
)
// Check if the configuration of the database is compatible with the
@@ -165,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 {
@@ -211,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))
@@ -246,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)
@@ -262,6 +287,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
ctrID := []byte(ctr.ID())
ctrName := []byte(ctr.Name())
+ var ctrNamespace []byte
+ if ctr.config.Namespace != "" {
+ ctrNamespace = []byte(ctr.config.Namespace)
+ }
+
db, err := s.getDBCon()
if err != nil {
return err
@@ -279,6 +309,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
@@ -309,6 +344,12 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
if podCtrs == nil {
return errors.Wrapf(ErrInternal, "pod %s does not have a containers bucket", pod.ID())
}
+
+ podNS := podDB.Get(namespaceKey)
+ if !bytes.Equal(podNS, ctrNamespace) {
+ return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and pod %s is in namespace %s",
+ ctr.ID(), ctr.config.Namespace, pod.ID(), pod.config.Namespace)
+ }
}
// Check if we already have a container with the given ID and name
@@ -329,6 +370,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())
}
@@ -344,6 +390,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
if err := newCtrBkt.Put(stateKey, stateJSON); err != nil {
return errors.Wrapf(err, "error adding container %s state to DB", ctr.ID())
}
+ if ctrNamespace != nil {
+ if err := newCtrBkt.Put(namespaceKey, ctrNamespace); err != nil {
+ return errors.Wrapf(err, "error adding container %s namespace to DB", ctr.ID())
+ }
+ }
if pod != nil {
if err := newCtrBkt.Put(podIDKey, []byte(pod.ID())); err != nil {
return errors.Wrapf(err, "error adding container %s pod to DB", ctr.ID())
@@ -384,6 +435,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)
@@ -408,7 +464,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 (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
ctrID := []byte(ctr.ID())
ctrName := []byte(ctr.Name())
@@ -427,6 +483,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
@@ -456,6 +517,17 @@ 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 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)
+ }
+ if pod != nil && s.namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "pod %s is in namespace %q, does not match out namespace %q", pod.ID(), pod.config.Namespace, s.namespace)
+ }
+ }
+
if podDB != nil {
// Check if the container is in the pod, remove it if it is
podCtrs := podDB.Bucket(containersBkt)
@@ -502,6 +574,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/container.go b/libpod/container.go
index b4a1eeb12..456fc412d 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -185,6 +185,8 @@ type ContainerConfig struct {
Name string `json:"name"`
// Full ID of the pood the container belongs to
Pod string `json:"pod,omitempty"`
+ // Namespace the container is in
+ Namespace string `json:"namespace,omitempty"`
// TODO consider breaking these subsections up into smaller structs
@@ -372,6 +374,12 @@ func (c *Container) PodID() string {
return c.config.Pod
}
+// Namespace returns the libpod namespace the container is in.
+// Namespaces are used to logically separate containers and pods in the state.
+func (c *Container) Namespace() string {
+ return c.config.Namespace
+}
+
// Image returns the ID and name of the image used as the container's rootfs
func (c *Container) Image() (string, string) {
return c.config.RootfsImageID, c.config.RootfsImageName
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index a1070cf99..dec0b47b4 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -69,6 +69,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
ImageID: config.RootfsImageID,
ImageName: config.RootfsImageName,
ExitCommand: config.ExitCommand,
+ Namespace: config.Namespace,
Rootfs: config.Rootfs,
ResolvConfPath: resolvPath,
HostnamePath: hostnamePath,
diff --git a/libpod/errors.go b/libpod/errors.go
index ddd586e29..75b4928da 100644
--- a/libpod/errors.go
+++ b/libpod/errors.go
@@ -63,6 +63,10 @@ var (
// was created by a libpod with a different config
ErrDBBadConfig = errors.New("database configuration mismatch")
+ // ErrNSMismatch indicates that the requested pod or container is in a
+ // different namespace and cannot be accessed or modified.
+ ErrNSMismatch = errors.New("target is in a different namespace")
+
// ErrNotImplemented indicates that the requested functionality is not
// yet present
ErrNotImplemented = errors.New("not yet implemented")
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 36077b9d1..8bdd0881c 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -14,12 +14,30 @@ 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
+ // Maps pod ID to pod struct.
+ pods map[string]*Pod
+ // Maps container ID to container struct.
+ containers map[string]*Container
+ // Maps container ID to a list of IDs of dependencies.
+ ctrDepends map[string][]string
+ // Maps pod ID to a map of container ID to container struct.
podContainers map[string]map[string]*Container
- nameIndex *registrar.Registrar
- idIndex *truncindex.TruncIndex
+ // Global name registry - ensures name uniqueness and performs lookups.
+ nameIndex *registrar.Registrar
+ // Global ID registry - ensures ID uniqueness and performs lookups.
+ idIndex *truncindex.TruncIndex
+ // Namespace the state is joined to.
+ namespace string
+ // Maps namespace name to local ID and name registries for looking up
+ // pods and containers in a specific namespace.
+ namespaceIndexes map[string]*namespaceIndex
+}
+
+// namespaceIndex contains name and ID registries for a specific namespace.
+// This is used for namespaces lookup operations.
+type namespaceIndex struct {
+ nameIndex *registrar.Registrar
+ idIndex *truncindex.TruncIndex
}
// NewInMemoryState initializes a new, empty in-memory state
@@ -36,6 +54,10 @@ func NewInMemoryState() (State, error) {
state.nameIndex = registrar.NewRegistrar()
state.idIndex = truncindex.NewTruncIndex([]string{})
+ state.namespace = ""
+
+ state.namespaceIndexes = make(map[string]*namespaceIndex)
+
return state, nil
}
@@ -51,6 +73,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 == "" {
@@ -62,20 +91,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)
@@ -102,9 +154,12 @@ func (s *InMemoryState) HasContainer(id string) (bool, error) {
return false, ErrEmptyID
}
- _, ok := s.containers[id]
+ ctr, ok := s.containers[id]
+ if !ok || (s.namespace != "" && s.namespace != ctr.config.Namespace) {
+ return false, nil
+ }
- return ok, nil
+ return true, nil
}
// AddContainer adds a container to the state
@@ -122,6 +177,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.
@@ -133,6 +192,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 {
@@ -146,6 +208,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)
@@ -160,6 +241,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 {
@@ -180,6 +265,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 {
@@ -204,7 +300,7 @@ func (s *InMemoryState) UpdateContainer(ctr *Container) error {
return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
}
- return nil
+ return s.checkNSMatch(ctr.ID(), ctr.Namespace())
}
// SaveContainer saves a container's state
@@ -223,7 +319,7 @@ func (s *InMemoryState) SaveContainer(ctr *Container) error {
return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
}
- return nil
+ return s.checkNSMatch(ctr.ID(), ctr.Namespace())
}
// ContainerInUse checks if the given container is being used by other containers
@@ -232,6 +328,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
@@ -244,7 +350,9 @@ 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 == "" || ctr.config.Namespace == s.namespace {
+ ctrs = append(ctrs, ctr)
+ }
}
return ctrs, nil
@@ -261,21 +369,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)
@@ -302,9 +433,12 @@ func (s *InMemoryState) HasPod(id string) (bool, error) {
return false, ErrEmptyID
}
- _, ok := s.pods[id]
+ pod, ok := s.pods[id]
+ if !ok || (s.namespace != "" && s.namespace != pod.config.Namespace) {
+ return false, nil
+ }
- return ok, nil
+ return true, nil
}
// PodHasContainer checks if the given pod has a container with the given ID
@@ -317,6 +451,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
@@ -333,6 +471,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
@@ -358,6 +500,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
@@ -383,6 +529,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())
}
@@ -404,6 +554,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
}
@@ -413,6 +582,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())
@@ -433,6 +606,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
}
@@ -445,6 +629,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 {
@@ -494,6 +682,15 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
return errors.Wrapf(ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID())
}
+ if ctr.config.Namespace != pod.config.Namespace {
+ return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and pod %s is in namespace %s",
+ 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 {
@@ -514,9 +711,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
@@ -538,6 +739,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)
@@ -556,6 +776,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 {
@@ -595,6 +819,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 {
@@ -611,6 +846,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())
@@ -626,6 +865,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())
@@ -638,7 +881,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
@@ -683,3 +932,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/options.go b/libpod/options.go
index 718b44930..7bb4a3632 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -284,6 +284,26 @@ func WithCNIPluginDir(dir string) RuntimeOption {
}
}
+// WithNamespace sets the namespace for libpod.
+// 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.
@@ -388,8 +408,9 @@ func WithStdin() CtrCreateOption {
}
// WithPod adds the container to a pod.
-// Containers which join a pod can only join the namespaces of other containers
-// in the same pod.
+// Containers which join a pod can only join the Linux namespaces of other
+// containers in the same pod.
+// Containers can only join pods in the same libpod namespace.
func (r *Runtime) WithPod(pod *Pod) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
@@ -944,7 +965,8 @@ func WithCommand(command []string) CtrCreateOption {
}
}
-// WithRootFS sets the rootfs for the container
+// WithRootFS sets the rootfs for the container.
+// This creates a container from a directory on disk and not an image.
func WithRootFS(rootfs string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
@@ -961,6 +983,22 @@ func WithRootFS(rootfs string) CtrCreateOption {
}
}
+// 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 WithCtrNamespace(ns string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+
+ ctr.config.Namespace = ns
+
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodName sets the name of the pod.
@@ -1025,3 +1063,20 @@ func WithPodCgroups() PodCreateOption {
return nil
}
}
+
+// WithPodNamespace sets the namespace for the created pod.
+// 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.
+// Containers must belong to the same namespace as the pod they join.
+func WithPodNamespace(ns string) PodCreateOption {
+ return func(pod *Pod) error {
+ if pod.valid {
+ return ErrPodFinalized
+ }
+
+ pod.config.Namespace = ns
+
+ return nil
+ }
+}
diff --git a/libpod/pod.go b/libpod/pod.go
index fb69787ed..a5b87f8b5 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -27,6 +27,8 @@ type Pod struct {
type PodConfig struct {
ID string `json:"id"`
Name string `json:"name"`
+ // Namespace the pod is in
+ Namespace string `json:"namespace,omitempty"`
// Labels contains labels applied to the pod
Labels map[string]string `json:"labels"`
@@ -58,6 +60,12 @@ func (p *Pod) Name() string {
return p.config.Name
}
+// Namespace returns the pod's libpod namespace.
+// Namespaces are used to logically separate containers and pods in the state.
+func (p *Pod) Namespace() string {
+ return p.config.Namespace
+}
+
// Labels returns the pod's labels
func (p *Pod) Labels() map[string]string {
labels := make(map[string]string)
diff --git a/libpod/runtime.go b/libpod/runtime.go
index a551c9134..1a384fde2 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 (
@@ -493,6 +505,11 @@ func makeRuntime(runtime *Runtime) (err error) {
return errors.Wrapf(ErrInvalidArg, "unrecognized state type passed")
}
+ if err := runtime.state.SetNamespace(runtime.config.Namespace); err != nil {
+ return errors.Wrapf(err, "error setting libpod namespace in state")
+ }
+ logrus.Debugf("Set libpod namespace to %q", runtime.config.Namespace)
+
// We now need to see if the system has restarted
// We check for the presence of a file in our tmp directory to verify this
// This check must be locked to prevent races
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 82a2fed19..709775e4a 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -42,6 +42,12 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ..
}
ctr.config.StopTimeout = CtrRemoveTimeout
+ // Set namespace based on current runtime namespace
+ // Do so before options run so they can override it
+ if r.config.Namespace != "" {
+ ctr.config.Namespace = r.config.Namespace
+ }
+
for _, option := range options {
if err := option(ctr); err != nil {
return nil, errors.Wrapf(err, "error running container create option")
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 25340abdb..3355e4322 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -27,6 +27,12 @@ func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) {
return nil, errors.Wrapf(err, "error creating pod")
}
+ // Set default namespace to runtime's namespace
+ // Do so before options run so they can override it
+ if r.config.Namespace != "" {
+ pod.config.Namespace = r.config.Namespace
+ }
+
for _, option := range options {
if err := option(pod); err != nil {
return nil, errors.Wrapf(err, "error running pod create option")
diff --git a/libpod/state.go b/libpod/state.go
index b71f811ea..1b82349b3 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -9,67 +9,110 @@ type State interface {
// Refresh clears container and pod states after a reboot
Refresh() error
- // Return a container from the database from its full ID
+ // SetNamespace() sets the namespace for the store, and will determine
+ // what containers are retrieved with container and pod retrieval calls.
+ // A namespace of "", the empty string, acts as no namespace, and
+ // containers and pods in all namespaces will be returned.
+ SetNamespace(ns string) error
+
+ // Return a container from the database from its full ID.
+ // If the container is not in the set namespace, an error will be
+ // returned.
Container(id string) (*Container, error)
// Return a container from the database by full or partial ID or full
- // name
+ // name.
+ // Containers not in the set namespace will be ignored.
LookupContainer(idOrName string) (*Container, error)
- // Check if a container with the given full ID exists in the database
+ // Check if a container with the given full ID exists in the database.
+ // If the container exists but is not in the set namespace, false will
+ // be returned.
HasContainer(id string) (bool, error)
- // Adds container to state
- // The container cannot be part of a pod
+ // Adds container to state.
+ // The container cannot be part of a pod.
// The container must have globally unique name and ID - pod names and
- // IDs also conflict with container names and IDs
+ // IDs also conflict with container names and IDs.
+ // The container must be in the set namespace if a namespace has been
+ // set.
+ // All containers this container depends on must be part of the same
+ // namespace and must not be joined to a pod.
AddContainer(ctr *Container) error
- // Removes container from state
- // Containers that are part of pods must use RemoveContainerFromPod
+ // Removes container from state.
+ // Containers that are part of pods must use RemoveContainerFromPod.
+ // The container must be part of the set namespace.
RemoveContainer(ctr *Container) error
- // UpdateContainer updates a container's state from the backing store
+ // UpdateContainer updates a container's state from the backing store.
+ // The container must be part of the set namespace.
UpdateContainer(ctr *Container) error
- // SaveContainer saves a container's current state to the backing store
+ // SaveContainer saves a container's current state to the backing store.
+ // The container must be part of the set namespace.
SaveContainer(ctr *Container) error
// ContainerInUse checks if other containers depend upon a given
- // container
+ // container.
// It returns a slice of the IDs of containers which depend on the given
// container. If the slice is empty, no container depend on the given
// container.
- // A container cannot be removed if other containers depend on it
+ // A container cannot be removed if other containers depend on it.
+ // The container being checked must be part of the set namespace.
ContainerInUse(ctr *Container) ([]string, error)
- // Retrieves all containers presently in state
+ // Retrieves all containers presently in state.
+ // If a namespace is set, only containers within the namespace will be
+ // returned.
AllContainers() ([]*Container, error)
- // Accepts full ID of pod
+ // Accepts full ID of pod.
+ // If the pod given is not in the set namespace, an error will be
+ // returned.
Pod(id string) (*Pod, error)
- // Accepts full or partial IDs (as long as they are unique) and names
+ // Accepts full or partial IDs (as long as they are unique) and names.
+ // Pods not in the set namespace are ignored.
LookupPod(idOrName string) (*Pod, error)
- // Checks if a pod with the given ID is present in the state
+ // Checks if a pod with the given ID is present in the state.
+ // If the given pod is not in the set namespace, false is returned.
HasPod(id string) (bool, error)
- // Check if a pod has a container with the given ID
+ // Check if a pod has a container with the given ID.
+ // The pod must be part of the set namespace.
PodHasContainer(pod *Pod, ctrID string) (bool, error)
- // Get the IDs of all containers in a pod
+ // Get the IDs of all containers in a pod.
+ // The pod must be part of the set namespace.
PodContainersByID(pod *Pod) ([]string, error)
- // Get all the containers in a pod
+ // Get all the containers in a pod.
+ // The pod must be part of the set namespace.
PodContainers(pod *Pod) ([]*Container, error)
- // Adds pod to state
+ // Adds pod to state.
+ // The pod must be part of the set namespace.
+ // The pod's name and ID must be globally unique.
AddPod(pod *Pod) error
- // Removes pod from state
- // Only empty pods can be removed from the state
+ // Removes pod from state.
+ // Only empty pods can be removed from the state.
+ // The pod must be part of the set namespace.
RemovePod(pod *Pod) error
- // Remove all containers from a pod
+ // Remove all containers from a pod.
// Used to simultaneously remove containers that might otherwise have
- // dependency issues
- // Will fail if a dependency outside the pod is encountered
+ // dependency issues.
+ // Will fail if a dependency outside the pod is encountered.
+ // The pod must be part of the set namespace.
RemovePodContainers(pod *Pod) error
- // AddContainerToPod adds a container to an existing pod
- // The container given will be added to the state and the pod
+ // AddContainerToPod adds a container to an existing pod.
+ // The container given will be added to the state and the pod.
+ // The container and its dependencies must be part of the given pod,
+ // and the given pod's namespace.
+ // The pod must be part of the set namespace.
+ // The pod must already exist in the state.
+ // The container's name and ID must be globally unique.
AddContainerToPod(pod *Pod, ctr *Container) error
- // RemoveContainerFromPod removes a container from an existing pod
- // The container will also be removed from the state
+ // RemoveContainerFromPod removes a container from an existing pod.
+ // The container will also be removed from the state.
+ // The container must be in the given pod, and the pod must be in the
+ // set namespace.
RemoveContainerFromPod(pod *Pod, ctr *Container) error
- // UpdatePod updates a pod's state from the database
+ // UpdatePod updates a pod's state from the database.
+ // The pod must be in the set namespace.
UpdatePod(pod *Pod) error
- // SavePod saves a pod's state to the database
+ // SavePod saves a pod's state to the database.
+ // The pod must be in the set namespace.
SavePod(pod *Pod) error
- // Retrieves all pods presently in state
+ // Retrieves all pods presently in state.
+ // If a namespace has been set, only pods in that namespace will be
+ // returned.
AllPods() ([]*Pod, error)
}
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 4d5eb9713..30638024c 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -281,6 +281,95 @@ 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 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")
@@ -295,6 +384,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("")
@@ -398,6 +540,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("")
@@ -441,6 +643,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)
@@ -466,6 +704,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)
@@ -484,6 +751,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"}})
@@ -502,6 +786,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)
@@ -534,6 +835,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()
@@ -577,6 +905,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{})
@@ -584,6 +956,32 @@ 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 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)
+ })
+}
+
func TestContainerInUseOneContainer(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
@@ -972,6 +1370,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("")
@@ -1076,6 +1510,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("")
@@ -1133,6 +1630,42 @@ func TestHasPodCtrIDFalse(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 TestHasPodDifferentNamespaceFails(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")
+
+ exist, err := state.HasPod(testPod.ID())
+ assert.NoError(t, err)
+ assert.False(t, exist)
+ })
+}
+
func TestAddPodInvalidPodErrors(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
err := state.AddPod(&Pod{config: &PodConfig{}})
@@ -1257,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{}})
@@ -1368,6 +1942,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()
@@ -1430,6 +2027,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))
@@ -1506,6 +2149,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{}})
@@ -1608,6 +2268,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{}})
@@ -1615,7 +2292,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)
@@ -1711,6 +2388,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{}})
@@ -1867,6 +2561,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)
@@ -2224,6 +2935,225 @@ 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)
+ 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 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)
@@ -2397,6 +3327,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{}})
@@ -2414,6 +3414,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{}})
@@ -2431,6 +3448,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)
@@ -2455,3 +3489,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)
+ })
+}
diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go
index 5b5a27c3d..d2c9e79a5 100644
--- a/pkg/inspect/inspect.go
+++ b/pkg/inspect/inspect.go
@@ -169,6 +169,7 @@ type ContainerInspectData struct {
Dependencies []string `json:"Dependencies"`
NetworkSettings *NetworkSettings `json:"NetworkSettings"` //TODO
ExitCommand []string `json:"ExitCommand"`
+ Namespace string `json:"Namespace"`
}
// ContainerInspectState represents the state of a container.
diff --git a/test/e2e/namespace_test.go b/test/e2e/namespace_test.go
new file mode 100644
index 000000000..7cc6dc114
--- /dev/null
+++ b/test/e2e/namespace_test.go
@@ -0,0 +1,51 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman namespaces", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+
+ })
+
+ It("podman namespace test", func() {
+ podman1 := podmanTest.Podman([]string{"--namespace", "test1", "run", "-d", ALPINE, "echo", "hello"})
+ podman1.WaitWithDefaultTimeout()
+ Expect(podman1.ExitCode()).To(Equal(0))
+
+ podman2 := podmanTest.Podman([]string{"--namespace", "test2", "ps", "-aq"})
+ podman2.WaitWithDefaultTimeout()
+ Expect(podman2.ExitCode()).To(Equal(0))
+ output := podman2.OutputToStringArray()
+ numCtrs := 0
+ for _, outputLine := range output {
+ if outputLine != "" {
+ numCtrs = numCtrs + 1
+ }
+ }
+ Expect(numCtrs).To(Equal(0))
+
+ numberOfCtrsNoNamespace := podmanTest.NumberOfContainers()
+ Expect(numberOfCtrsNoNamespace).To(Equal(1))
+ })
+})