mirror of
https://github.com/containers/podman.git
synced 2025-09-25 15:55:32 +08:00
Merge pull request #1116 from mheon/namespaces
Add Pod and Container namespaces
This commit is contained in:
@ -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")))
|
||||
}
|
||||
|
@ -172,6 +172,11 @@ func main() {
|
||||
Usage: "log messages above specified level: debug, info, warn, error (default), fatal or panic",
|
||||
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",
|
||||
|
@ -2224,6 +2224,7 @@ _podman_podman() {
|
||||
--storage-driver
|
||||
--storage-opt
|
||||
--log-level
|
||||
--namespace
|
||||
"
|
||||
local boolean_options="
|
||||
--help -h
|
||||
|
@ -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**
|
||||
|
||||
|
@ -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 = ""
|
||||
|
@ -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
|
||||
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,10 +400,17 @@ func (s *BoltState) HasContainer(id string) (bool, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
ctrExists := ctrBucket.Bucket(ctrID)
|
||||
if ctrExists != nil {
|
||||
ctrDB := ctrBucket.Bucket(ctrID)
|
||||
if ctrDB != nil {
|
||||
if s.namespaceBytes != nil {
|
||||
nsBytes := 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)
|
||||
|
||||
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,8 +869,15 @@ func (s *BoltState) HasPod(id string) (bool, error) {
|
||||
|
||||
podDB := podBkt.Bucket(podID)
|
||||
if podDB != nil {
|
||||
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)
|
||||
|
||||
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
|
||||
})
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -14,10 +14,28 @@ import (
|
||||
|
||||
// An InMemoryState is a purely in-memory state store
|
||||
type InMemoryState struct {
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
@ -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,8 +350,10 @@ 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 {
|
||||
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,8 +881,14 @@ 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 {
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
107
libpod/state.go
107
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)
|
||||
}
|
||||
|
1065
libpod/state_test.go
1065
libpod/state_test.go
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
51
test/e2e/namespace_test.go
Normal file
51
test/e2e/namespace_test.go
Normal file
@ -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))
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user