mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00
Add constraint that dependencies must be in the same ns
Dependency containers must be in the same namespace, to ensure there are never problems resolving a dependency. Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package libpod
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -11,10 +12,12 @@ import (
|
|||||||
|
|
||||||
// BoltState is a state implementation backed by a Bolt DB
|
// BoltState is a state implementation backed by a Bolt DB
|
||||||
type BoltState struct {
|
type BoltState struct {
|
||||||
valid bool
|
valid bool
|
||||||
dbPath string
|
dbPath string
|
||||||
lockDir string
|
namespace string
|
||||||
runtime *Runtime
|
namespaceBytes []byte
|
||||||
|
lockDir string
|
||||||
|
runtime *Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBoltState creates a new bolt-backed state database
|
// NewBoltState creates a new bolt-backed state database
|
||||||
@ -23,6 +26,8 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
|
|||||||
state.dbPath = path
|
state.dbPath = path
|
||||||
state.lockDir = lockDir
|
state.lockDir = lockDir
|
||||||
state.runtime = runtime
|
state.runtime = runtime
|
||||||
|
state.namespace = ""
|
||||||
|
state.namespaceBytes = nil
|
||||||
|
|
||||||
// Make the directory that will hold container lockfiles
|
// Make the directory that will hold container lockfiles
|
||||||
if err := os.MkdirAll(lockDir, 0750); err != nil {
|
if err := os.MkdirAll(lockDir, 0750); err != nil {
|
||||||
@ -47,6 +52,9 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
|
|||||||
if _, err := tx.CreateBucketIfNotExists(nameRegistryBkt); err != nil {
|
if _, err := tx.CreateBucketIfNotExists(nameRegistryBkt); err != nil {
|
||||||
return errors.Wrapf(err, "error creating name-registry bucket")
|
return errors.Wrapf(err, "error creating name-registry bucket")
|
||||||
}
|
}
|
||||||
|
if _, err := tx.CreateBucketIfNotExists(nsRegistryBkt); err != nil {
|
||||||
|
return errors.Wrapf(err, "error creating ns-registry bucket")
|
||||||
|
}
|
||||||
if _, err := tx.CreateBucketIfNotExists(ctrBkt); err != nil {
|
if _, err := tx.CreateBucketIfNotExists(ctrBkt); err != nil {
|
||||||
return errors.Wrapf(err, "error creating containers bucket")
|
return errors.Wrapf(err, "error creating containers bucket")
|
||||||
}
|
}
|
||||||
@ -193,6 +201,20 @@ func (s *BoltState) Refresh() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNamespace sets the namespace that will be used for container and pod
|
||||||
|
// retrieval
|
||||||
|
func (s *BoltState) SetNamespace(ns string) error {
|
||||||
|
s.namespace = ns
|
||||||
|
|
||||||
|
if ns != "" {
|
||||||
|
s.namespaceBytes = []byte(ns)
|
||||||
|
} else {
|
||||||
|
s.namespaceBytes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Container retrieves a single container from the state by its full ID
|
// Container retrieves a single container from the state by its full ID
|
||||||
func (s *BoltState) Container(id string) (*Container, error) {
|
func (s *BoltState) Container(id string) (*Container, error) {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@ -262,6 +284,11 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsBucket, err := getNSBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// First, check if the ID given was the actual container ID
|
// First, check if the ID given was the actual container ID
|
||||||
var id []byte
|
var id []byte
|
||||||
ctrExists := ctrBucket.Bucket([]byte(idOrName))
|
ctrExists := ctrBucket.Bucket([]byte(idOrName))
|
||||||
@ -274,6 +301,14 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
|
|||||||
// Use else-if in case the name is set to a partial ID
|
// Use else-if in case the name is set to a partial ID
|
||||||
exists := false
|
exists := false
|
||||||
err = idBucket.ForEach(func(checkID, checkName []byte) error {
|
err = idBucket.ForEach(func(checkID, checkName []byte) error {
|
||||||
|
// If the container isn't in our namespace, we
|
||||||
|
// can't match it
|
||||||
|
if s.namespaceBytes != nil {
|
||||||
|
ns := nsBucket.Get(checkID)
|
||||||
|
if !bytes.Equal(ns, s.namespaceBytes) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
if string(checkName) == idOrName {
|
if string(checkName) == idOrName {
|
||||||
if exists {
|
if exists {
|
||||||
return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
|
return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
|
||||||
@ -334,7 +369,14 @@ func (s *BoltState) HasContainer(id string) (bool, error) {
|
|||||||
|
|
||||||
ctrExists := ctrBucket.Bucket(ctrID)
|
ctrExists := ctrBucket.Bucket(ctrID)
|
||||||
if ctrExists != nil {
|
if ctrExists != nil {
|
||||||
exists = true
|
if s.namespaceBytes != nil {
|
||||||
|
nsBytes := ctrBucket.Get(namespaceKey)
|
||||||
|
if bytes.Equal(nsBytes, nsBytes) {
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -383,7 +425,7 @@ func (s *BoltState) RemoveContainer(ctr *Container) error {
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.Update(func(tx *bolt.Tx) error {
|
err = db.Update(func(tx *bolt.Tx) error {
|
||||||
return removeContainer(ctr, nil, tx)
|
return removeContainer(ctr, nil, tx, s.namespace)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -398,6 +440,12 @@ func (s *BoltState) UpdateContainer(ctr *Container) error {
|
|||||||
return ErrCtrRemoved
|
return ErrCtrRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.namespace != "" {
|
||||||
|
if s.namespace != ctr.config.Namespace {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newState := new(containerState)
|
newState := new(containerState)
|
||||||
netNSPath := ""
|
netNSPath := ""
|
||||||
|
|
||||||
@ -460,6 +508,12 @@ func (s *BoltState) SaveContainer(ctr *Container) error {
|
|||||||
return ErrCtrRemoved
|
return ErrCtrRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.namespace != "" {
|
||||||
|
if s.namespace != ctr.config.Namespace {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stateJSON, err := json.Marshal(ctr.state)
|
stateJSON, err := json.Marshal(ctr.state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID())
|
return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID())
|
||||||
@ -519,6 +573,12 @@ func (s *BoltState) ContainerInUse(ctr *Container) ([]string, error) {
|
|||||||
return nil, ErrCtrRemoved
|
return nil, ErrCtrRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.namespace != "" {
|
||||||
|
if s.namespace != ctr.config.Namespace {
|
||||||
|
return nil, errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
depCtrs := []string{}
|
depCtrs := []string{}
|
||||||
|
|
||||||
db, err := s.getDBCon()
|
db, err := s.getDBCon()
|
||||||
@ -602,9 +662,20 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
|
|||||||
ctr.config = new(ContainerConfig)
|
ctr.config = new(ContainerConfig)
|
||||||
ctr.state = new(containerState)
|
ctr.state = new(containerState)
|
||||||
|
|
||||||
ctrs = append(ctrs, ctr)
|
if err := s.getContainerFromDB(id, ctr, ctrBucket); err != nil {
|
||||||
|
// If the error is a namespace mismatch, we can
|
||||||
|
// ignore it safely.
|
||||||
|
// We just won't include the container in the
|
||||||
|
// results.
|
||||||
|
if errors.Cause(err) != ErrNSMismatch {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctrs = append(ctrs, ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
return s.getContainerFromDB(id, ctr, ctrBucket)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -682,6 +753,11 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsBkt, err := getNSBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// First, check if the ID given was the actual pod ID
|
// First, check if the ID given was the actual pod ID
|
||||||
var id []byte
|
var id []byte
|
||||||
podExists := podBkt.Bucket([]byte(idOrName))
|
podExists := podBkt.Bucket([]byte(idOrName))
|
||||||
@ -694,6 +770,14 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
|
|||||||
// Use else-if in case the name is set to a partial ID
|
// Use else-if in case the name is set to a partial ID
|
||||||
exists := false
|
exists := false
|
||||||
err = idBucket.ForEach(func(checkID, checkName []byte) error {
|
err = idBucket.ForEach(func(checkID, checkName []byte) error {
|
||||||
|
// If the pod isn't in our namespace, we
|
||||||
|
// can't match it
|
||||||
|
if s.namespaceBytes != nil {
|
||||||
|
ns := nsBkt.Get(checkID)
|
||||||
|
if !bytes.Equal(ns, s.namespaceBytes) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
if string(checkName) == idOrName {
|
if string(checkName) == idOrName {
|
||||||
if exists {
|
if exists {
|
||||||
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
|
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
|
||||||
@ -719,7 +803,6 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
|
|||||||
|
|
||||||
// We might have found a container ID, but it's OK
|
// We might have found a container ID, but it's OK
|
||||||
// We'll just fail in getPodFromDB with ErrNoSuchPod
|
// We'll just fail in getPodFromDB with ErrNoSuchPod
|
||||||
|
|
||||||
return s.getPodFromDB(id, pod, podBkt)
|
return s.getPodFromDB(id, pod, podBkt)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -757,7 +840,14 @@ func (s *BoltState) HasPod(id string) (bool, error) {
|
|||||||
|
|
||||||
podDB := podBkt.Bucket(podID)
|
podDB := podBkt.Bucket(podID)
|
||||||
if podDB != nil {
|
if podDB != nil {
|
||||||
exists = true
|
if s.namespaceBytes != nil {
|
||||||
|
podNS := podDB.Get(namespaceKey)
|
||||||
|
if bytes.Equal(s.namespaceBytes, podNS) {
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exists = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1318,7 +1408,7 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.Update(func(tx *bolt.Tx) error {
|
err = db.Update(func(tx *bolt.Tx) error {
|
||||||
return removeContainer(ctr, pod, tx)
|
return removeContainer(ctr, pod, tx, s.namespace)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
idRegistryName = "id-registry"
|
idRegistryName = "id-registry"
|
||||||
nameRegistryName = "name-registry"
|
nameRegistryName = "name-registry"
|
||||||
|
nsRegistryName = "ns-registry"
|
||||||
ctrName = "ctr"
|
ctrName = "ctr"
|
||||||
allCtrsName = "all-ctrs"
|
allCtrsName = "all-ctrs"
|
||||||
podName = "pod"
|
podName = "pod"
|
||||||
@ -34,6 +35,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
idRegistryBkt = []byte(idRegistryName)
|
idRegistryBkt = []byte(idRegistryName)
|
||||||
nameRegistryBkt = []byte(nameRegistryName)
|
nameRegistryBkt = []byte(nameRegistryName)
|
||||||
|
nsRegistryBkt = []byte(nsRegistryName)
|
||||||
ctrBkt = []byte(ctrName)
|
ctrBkt = []byte(ctrName)
|
||||||
allCtrsBkt = []byte(allCtrsName)
|
allCtrsBkt = []byte(allCtrsName)
|
||||||
podBkt = []byte(podName)
|
podBkt = []byte(podName)
|
||||||
@ -168,6 +170,14 @@ func getNamesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
|||||||
return bkt, nil
|
return bkt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNSBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
|
bkt := tx.Bucket(nsRegistryBkt)
|
||||||
|
if bkt == nil {
|
||||||
|
return nil, errors.Wrapf(ErrDBBadConfig, "namespace registry bucket not found in DB")
|
||||||
|
}
|
||||||
|
return bkt, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getCtrBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
func getCtrBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
|
||||||
bkt := tx.Bucket(ctrBkt)
|
bkt := tx.Bucket(ctrBkt)
|
||||||
if bkt == nil {
|
if bkt == nil {
|
||||||
@ -214,6 +224,13 @@ func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error
|
|||||||
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found", string(id))
|
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found", string(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.namespaceBytes != nil {
|
||||||
|
podNamespaceBytes := podDB.Get(namespaceKey)
|
||||||
|
if !bytes.Equal(s.namespaceBytes, podNamespaceBytes) {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "cannot retrieve pod %s as it is part of namespace %q and we are in namespace %q", string(id), string(podNamespaceBytes), s.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
podConfigBytes := podDB.Get(configKey)
|
podConfigBytes := podDB.Get(configKey)
|
||||||
if podConfigBytes == nil {
|
if podConfigBytes == nil {
|
||||||
return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
|
return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
|
||||||
@ -287,6 +304,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsBucket, err := getNSBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
ctrBucket, err := getCtrBucket(tx)
|
ctrBucket, err := getCtrBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -343,6 +365,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
|
|||||||
if err := namesBucket.Put(ctrName, ctrID); err != nil {
|
if err := namesBucket.Put(ctrName, ctrID); err != nil {
|
||||||
return errors.Wrapf(err, "error adding container %s name (%s) to DB", ctr.ID(), ctr.Name())
|
return errors.Wrapf(err, "error adding container %s name (%s) to DB", ctr.ID(), ctr.Name())
|
||||||
}
|
}
|
||||||
|
if ctrNamespace != nil {
|
||||||
|
if err := nsBucket.Put(ctrID, ctrNamespace); err != nil {
|
||||||
|
return errors.Wrapf(err, "error adding container %s namespace (%q) to DB", ctr.ID(), ctr.Namespace())
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := allCtrsBucket.Put(ctrID, ctrName); err != nil {
|
if err := allCtrsBucket.Put(ctrID, ctrName); err != nil {
|
||||||
return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
|
return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
|
||||||
}
|
}
|
||||||
@ -403,6 +430,11 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
depNamespace := depCtrBkt.Get(namespaceKey)
|
||||||
|
if !bytes.Equal(ctrNamespace, depNamespace) {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s in namespace %q depends on container %s in namespace %q - namespaces must match", ctr.ID(), ctr.config.Namespace, dependsCtr, string(depNamespace))
|
||||||
|
}
|
||||||
|
|
||||||
depCtrDependsBkt := depCtrBkt.Bucket(dependenciesBkt)
|
depCtrDependsBkt := depCtrBkt.Bucket(dependenciesBkt)
|
||||||
if depCtrDependsBkt == nil {
|
if depCtrDependsBkt == nil {
|
||||||
return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", dependsCtr)
|
return errors.Wrapf(ErrInternal, "container %s does not have a dependencies bucket", dependsCtr)
|
||||||
@ -427,7 +459,7 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
|
|||||||
// Remove a container from the DB
|
// Remove a container from the DB
|
||||||
// If pod is not nil, the container is treated as belonging to a pod, and
|
// If pod is not nil, the container is treated as belonging to a pod, and
|
||||||
// will be removed from the pod as well
|
// will be removed from the pod as well
|
||||||
func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
|
func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx, namespace string) error {
|
||||||
ctrID := []byte(ctr.ID())
|
ctrID := []byte(ctr.ID())
|
||||||
ctrName := []byte(ctr.Name())
|
ctrName := []byte(ctr.Name())
|
||||||
|
|
||||||
@ -446,6 +478,11 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsBucket, err := getNSBucket(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
allCtrsBucket, err := getAllCtrsBucket(tx)
|
allCtrsBucket, err := getAllCtrsBucket(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -475,6 +512,14 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
|
|||||||
return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
|
return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare namespace
|
||||||
|
// We can't remove containers not in our namespace
|
||||||
|
if namespace != "" {
|
||||||
|
if namespace != ctr.config.Namespace {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if podDB != nil {
|
if podDB != nil {
|
||||||
// Check if the container is in the pod, remove it if it is
|
// Check if the container is in the pod, remove it if it is
|
||||||
podCtrs := podDB.Bucket(containersBkt)
|
podCtrs := podDB.Bucket(containersBkt)
|
||||||
@ -521,6 +566,9 @@ func removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error {
|
|||||||
if err := namesBucket.Delete(ctrName); err != nil {
|
if err := namesBucket.Delete(ctrName); err != nil {
|
||||||
return errors.Wrapf(err, "error deleting container %s name in DB", ctr.ID())
|
return errors.Wrapf(err, "error deleting container %s name in DB", ctr.ID())
|
||||||
}
|
}
|
||||||
|
if err := nsBucket.Delete(ctrID); err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting container %s namespace in DB", ctr.ID())
|
||||||
|
}
|
||||||
if err := allCtrsBucket.Delete(ctrID); err != nil {
|
if err := allCtrsBucket.Delete(ctrID); err != nil {
|
||||||
return errors.Wrapf(err, "error deleting container %s from all containers bucket in DB", ctr.ID())
|
return errors.Wrapf(err, "error deleting container %s from all containers bucket in DB", ctr.ID())
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package libpod
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -19,6 +20,13 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.
|
|||||||
return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id))
|
return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.namespaceBytes != nil {
|
||||||
|
ctrNamespaceBytes := ctrBkt.Get(namespaceKey)
|
||||||
|
if !bytes.Equal(s.namespaceBytes, ctrNamespaceBytes) {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "cannot retrieve container %s as it is part of namespace %q and we are in namespace %q", string(id), string(ctrNamespaceBytes), s.namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configBytes := ctrBkt.Get(configKey)
|
configBytes := ctrBkt.Get(configKey)
|
||||||
if configBytes == nil {
|
if configBytes == nil {
|
||||||
return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id))
|
return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id))
|
||||||
|
@ -20,6 +20,7 @@ type InMemoryState struct {
|
|||||||
podContainers map[string]map[string]*Container
|
podContainers map[string]map[string]*Container
|
||||||
nameIndex *registrar.Registrar
|
nameIndex *registrar.Registrar
|
||||||
idIndex *truncindex.TruncIndex
|
idIndex *truncindex.TruncIndex
|
||||||
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInMemoryState initializes a new, empty in-memory state
|
// NewInMemoryState initializes a new, empty in-memory state
|
||||||
@ -36,6 +37,8 @@ func NewInMemoryState() (State, error) {
|
|||||||
state.nameIndex = registrar.NewRegistrar()
|
state.nameIndex = registrar.NewRegistrar()
|
||||||
state.idIndex = truncindex.NewTruncIndex([]string{})
|
state.idIndex = truncindex.NewTruncIndex([]string{})
|
||||||
|
|
||||||
|
state.namespace = ""
|
||||||
|
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +54,13 @@ func (s *InMemoryState) Refresh() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNamespace sets the namespace for container and pod retrieval.
|
||||||
|
func (s *InMemoryState) SetNamespace(ns string) error {
|
||||||
|
s.namespace = ns
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Container retrieves a container from its full ID
|
// Container retrieves a container from its full ID
|
||||||
func (s *InMemoryState) Container(id string) (*Container, error) {
|
func (s *InMemoryState) Container(id string) (*Container, error) {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@ -133,6 +143,9 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
|
|||||||
} else if depCtr.config.Pod != "" {
|
} else if depCtr.config.Pod != "" {
|
||||||
return errors.Wrapf(ErrInvalidArg, "cannot depend on container in a pod if not part of same pod")
|
return errors.Wrapf(ErrInvalidArg, "cannot depend on container in a pod if not part of same pod")
|
||||||
}
|
}
|
||||||
|
if depCtr.config.Namespace != ctr.config.Namespace {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depID, depCtr.config.Namespace)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil {
|
if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil {
|
||||||
@ -519,9 +532,13 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
|
|||||||
if _, ok = s.containers[depCtr]; !ok {
|
if _, ok = s.containers[depCtr]; !ok {
|
||||||
return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr)
|
return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr)
|
||||||
}
|
}
|
||||||
if _, ok = podCtrs[depCtr]; !ok {
|
depCtrStruct, ok := podCtrs[depCtr]
|
||||||
|
if !ok {
|
||||||
return errors.Wrapf(ErrInvalidArg, "cannot depend on container %s as it is not in pod %s", depCtr, pod.ID())
|
return errors.Wrapf(ErrInvalidArg, "cannot depend on container %s as it is not in pod %s", depCtr, pod.ID())
|
||||||
}
|
}
|
||||||
|
if depCtrStruct.config.Namespace != ctr.config.Namespace {
|
||||||
|
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depCtr, depCtrStruct.config.Namespace)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add container to state
|
// Add container to state
|
||||||
|
@ -284,6 +284,27 @@ func WithCNIPluginDir(dir string) RuntimeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithNamespace sets the namespace for libpod.
|
||||||
|
// Namespace is the libpod namespace to use.
|
||||||
|
// Namespaces are used to create scopes to separate containers and pods
|
||||||
|
// in the state.
|
||||||
|
// When namespace is set, libpod will only view containers and pods in
|
||||||
|
// the same namespace. All containers and pods created will default to
|
||||||
|
// the namespace set here.
|
||||||
|
// A namespace of "", the empty string, is equivalent to no namespace,
|
||||||
|
// and all containers and pods will be visible.
|
||||||
|
func WithNamespace(ns string) RuntimeOption {
|
||||||
|
return func(rt *Runtime) error {
|
||||||
|
if rt.valid {
|
||||||
|
return ErrRuntimeFinalized
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.config.Namespace = ns
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Container Creation Options
|
// Container Creation Options
|
||||||
|
|
||||||
// WithShmDir sets the directory that should be mounted on /dev/shm.
|
// WithShmDir sets the directory that should be mounted on /dev/shm.
|
||||||
@ -963,11 +984,11 @@ func WithRootFS(rootfs string) CtrCreateOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithNamespace sets the namespace the container will be created in.
|
// WithCtrNamespace sets the namespace the container will be created in.
|
||||||
// Namespaces are used to create separate views of Podman's state - runtimes can
|
// Namespaces are used to create separate views of Podman's state - runtimes can
|
||||||
// join a specific namespace and see only containers and pods in that namespace.
|
// join a specific namespace and see only containers and pods in that namespace.
|
||||||
// Empty string namespaces are allowed, and correspond to a lack of namespace.
|
// Empty string namespaces are allowed, and correspond to a lack of namespace.
|
||||||
func WithNamespace(ns string) CtrCreateOption {
|
func WithCtrNamespace(ns string) CtrCreateOption {
|
||||||
return func(ctr *Container) error {
|
return func(ctr *Container) error {
|
||||||
if ctr.valid {
|
if ctr.valid {
|
||||||
return ErrCtrFinalized
|
return ErrCtrFinalized
|
||||||
|
@ -136,10 +136,22 @@ type RuntimeConfig struct {
|
|||||||
// CNIDefaultNetwork is the network name of the default CNI network
|
// CNIDefaultNetwork is the network name of the default CNI network
|
||||||
// to attach pods to
|
// to attach pods to
|
||||||
CNIDefaultNetwork string `toml:"cni_default_network,omitempty"`
|
CNIDefaultNetwork string `toml:"cni_default_network,omitempty"`
|
||||||
// HooksDirNotExistFatal switches between fatal errors and non-fatal warnings if the configured HooksDir does not exist.
|
// HooksDirNotExistFatal switches between fatal errors and non-fatal
|
||||||
|
// warnings if the configured HooksDir does not exist.
|
||||||
HooksDirNotExistFatal bool `toml:"hooks_dir_not_exist_fatal"`
|
HooksDirNotExistFatal bool `toml:"hooks_dir_not_exist_fatal"`
|
||||||
// DefaultMountsFile is the path to the default mounts file for testing purposes only
|
// DefaultMountsFile is the path to the default mounts file for testing
|
||||||
|
// purposes only
|
||||||
DefaultMountsFile string `toml:"-"`
|
DefaultMountsFile string `toml:"-"`
|
||||||
|
// Namespace is the libpod namespace to use.
|
||||||
|
// Namespaces are used to create scopes to separate containers and pods
|
||||||
|
// in the state.
|
||||||
|
// When namespace is set, libpod will only view containers and pods in
|
||||||
|
// the same namespace. All containers and pods created will default to
|
||||||
|
// the namespace set here.
|
||||||
|
// A namespace of "", the empty string, is equivalent to no namespace,
|
||||||
|
// and all containers and pods will be visible.
|
||||||
|
// The default namespace is "".
|
||||||
|
Namespace string `toml:"namespace,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -9,6 +9,10 @@ type State interface {
|
|||||||
// Refresh clears container and pod states after a reboot
|
// Refresh clears container and pod states after a reboot
|
||||||
Refresh() error
|
Refresh() error
|
||||||
|
|
||||||
|
// SetNamespace() sets the namespace for the store, and will determine
|
||||||
|
// what containers are retrieved with container and pod retrieval calls
|
||||||
|
SetNamespace(ns string) error
|
||||||
|
|
||||||
// Return a container from the database from its full ID
|
// Return a container from the database from its full ID
|
||||||
Container(id string) (*Container, error)
|
Container(id string) (*Container, error)
|
||||||
// Return a container from the database by full or partial ID or full
|
// Return a container from the database by full or partial ID or full
|
||||||
|
@ -281,6 +281,56 @@ func TestAddCtrDepInPodFails(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddCtrDepInSameNamespaceSucceeds(t *testing.T) {
|
||||||
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
|
testCtr1, err := getTestCtr1(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr2, err := getTestCtr2(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
testCtr2.config.UserNsCtr = testCtr1.config.ID
|
||||||
|
|
||||||
|
testCtr1.config.Namespace = "test1"
|
||||||
|
testCtr2.config.Namespace = "test1"
|
||||||
|
|
||||||
|
err = state.AddContainer(testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainer(testCtr2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
ctrs, err := state.AllContainers()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(ctrs))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddCtrDepInDifferentNamespaceFails(t *testing.T) {
|
||||||
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
|
testCtr1, err := getTestCtr1(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr2, err := getTestCtr2(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
testCtr2.config.UserNsCtr = testCtr1.config.ID
|
||||||
|
|
||||||
|
testCtr1.config.Namespace = "test1"
|
||||||
|
testCtr2.config.Namespace = "test2"
|
||||||
|
|
||||||
|
err = state.AddContainer(testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainer(testCtr2)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
ctrs, err := state.AllContainers()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(ctrs))
|
||||||
|
|
||||||
|
testContainersEqual(t, testCtr1, ctrs[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetNonexistentContainerFails(t *testing.T) {
|
func TestGetNonexistentContainerFails(t *testing.T) {
|
||||||
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
_, err := state.Container("does not exist")
|
_, err := state.Container("does not exist")
|
||||||
@ -2224,6 +2274,79 @@ func TestAddContainerToPodDependencyOutsidePodFails(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddContainerToPodDependencyInSameNamespaceSucceeds(t *testing.T) {
|
||||||
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
|
testPod, err := getTestPod1(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testPod.config.Namespace = "test1"
|
||||||
|
|
||||||
|
testCtr1, err := getTestCtr2(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr1.config.Pod = testPod.ID()
|
||||||
|
testCtr1.config.Namespace = "test1"
|
||||||
|
|
||||||
|
testCtr2, err := getTestCtrN("3", lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr2.config.Pod = testPod.ID()
|
||||||
|
testCtr2.config.IPCNsCtr = testCtr1.ID()
|
||||||
|
testCtr2.config.Namespace = "test1"
|
||||||
|
|
||||||
|
err = state.AddPod(testPod)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainerToPod(testPod, testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainerToPod(testPod, testCtr2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
deps, err := state.ContainerInUse(testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(deps))
|
||||||
|
assert.Equal(t, testCtr2.ID(), deps[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddContainerToPodDependencyInSeparateNamespaceFails(t *testing.T) {
|
||||||
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
|
testPod, err := getTestPod1(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testPod.config.Namespace = "test1"
|
||||||
|
|
||||||
|
testCtr1, err := getTestCtr2(lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr1.config.Pod = testPod.ID()
|
||||||
|
testCtr1.config.Namespace = "test1"
|
||||||
|
|
||||||
|
testCtr2, err := getTestCtrN("3", lockPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testCtr2.config.Pod = testPod.ID()
|
||||||
|
testCtr2.config.IPCNsCtr = testCtr1.ID()
|
||||||
|
testCtr2.config.Namespace = "test2"
|
||||||
|
|
||||||
|
err = state.AddPod(testPod)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainerToPod(testPod, testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.AddContainerToPod(testPod, testCtr2)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
ctrs, err := state.PodContainers(testPod)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(ctrs))
|
||||||
|
|
||||||
|
allCtrs, err := state.AllContainers()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(allCtrs))
|
||||||
|
|
||||||
|
deps, err := state.ContainerInUse(testCtr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(deps))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAddContainerToPodSameNamespaceSucceeds(t *testing.T) {
|
func TestAddContainerToPodSameNamespaceSucceeds(t *testing.T) {
|
||||||
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
|
||||||
testPod, err := getTestPod1(lockPath)
|
testPod, err := getTestPod1(lockPath)
|
||||||
|
Reference in New Issue
Block a user