diff options
Diffstat (limited to 'libpod/in_memory_state.go')
-rw-r--r-- | libpod/in_memory_state.go | 295 |
1 files changed, 277 insertions, 18 deletions
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 36077b9d1..8bdd0881c 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -14,12 +14,30 @@ import ( // An InMemoryState is a purely in-memory state store type InMemoryState struct { - pods map[string]*Pod - containers map[string]*Container - ctrDepends map[string][]string + // Maps pod ID to pod struct. + pods map[string]*Pod + // Maps container ID to container struct. + containers map[string]*Container + // Maps container ID to a list of IDs of dependencies. + ctrDepends map[string][]string + // Maps pod ID to a map of container ID to container struct. podContainers map[string]map[string]*Container - nameIndex *registrar.Registrar - idIndex *truncindex.TruncIndex + // Global name registry - ensures name uniqueness and performs lookups. + nameIndex *registrar.Registrar + // Global ID registry - ensures ID uniqueness and performs lookups. + idIndex *truncindex.TruncIndex + // Namespace the state is joined to. + namespace string + // Maps namespace name to local ID and name registries for looking up + // pods and containers in a specific namespace. + namespaceIndexes map[string]*namespaceIndex +} + +// namespaceIndex contains name and ID registries for a specific namespace. +// This is used for namespaces lookup operations. +type namespaceIndex struct { + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex } // NewInMemoryState initializes a new, empty in-memory state @@ -36,6 +54,10 @@ func NewInMemoryState() (State, error) { state.nameIndex = registrar.NewRegistrar() state.idIndex = truncindex.NewTruncIndex([]string{}) + state.namespace = "" + + state.namespaceIndexes = make(map[string]*namespaceIndex) + return state, nil } @@ -51,6 +73,13 @@ func (s *InMemoryState) Refresh() error { return nil } +// SetNamespace sets the namespace for container and pod retrieval. +func (s *InMemoryState) SetNamespace(ns string) error { + s.namespace = ns + + return nil +} + // Container retrieves a container from its full ID func (s *InMemoryState) Container(id string) (*Container, error) { if id == "" { @@ -62,20 +91,43 @@ func (s *InMemoryState) Container(id string) (*Container, error) { return nil, errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found", id) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return nil, err + } + return ctr, nil } // LookupContainer retrieves a container by full ID, unique partial ID, or name func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) { + var ( + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex + ) + if idOrName == "" { return nil, ErrEmptyID } - fullID, err := s.nameIndex.Get(idOrName) + if s.namespace != "" { + nsIndex, ok := s.namespaceIndexes[s.namespace] + if !ok { + // We have no containers in the namespace + // Return false + return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) + } + nameIndex = nsIndex.nameIndex + idIndex = nsIndex.idIndex + } else { + nameIndex = s.nameIndex + idIndex = s.idIndex + } + + fullID, err := nameIndex.Get(idOrName) if err != nil { if err == registrar.ErrNameNotReserved { // What was passed is not a name, assume it's an ID - fullID, err = s.idIndex.Get(idOrName) + fullID, err = idIndex.Get(idOrName) if err != nil { if err == truncindex.ErrNotExist { return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) @@ -102,9 +154,12 @@ func (s *InMemoryState) HasContainer(id string) (bool, error) { return false, ErrEmptyID } - _, ok := s.containers[id] + ctr, ok := s.containers[id] + if !ok || (s.namespace != "" && s.namespace != ctr.config.Namespace) { + return false, nil + } - return ok, nil + return true, nil } // AddContainer adds a container to the state @@ -122,6 +177,10 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { return errors.Wrapf(ErrInvalidArg, "cannot add a container that is in a pod with AddContainer, use AddContainerToPod") } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // There are potential race conditions with this // But in-memory state is intended purely for testing and not production // use, so this should be fine. @@ -133,6 +192,9 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { } else if depCtr.config.Pod != "" { return errors.Wrapf(ErrInvalidArg, "cannot depend on container in a pod if not part of same pod") } + if depCtr.config.Namespace != ctr.config.Namespace { + return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depID, depCtr.config.Namespace) + } } if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { @@ -146,6 +208,25 @@ func (s *InMemoryState) AddContainer(ctr *Container) error { s.containers[ctr.ID()] = ctr + // If we're in a namespace, add us to that namespace's indexes + if ctr.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[ctr.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", ctr.Name()) + } + if err := nsIndex.idIndex.Add(ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", ctr.ID()) + } + } + // Add containers this container depends on for _, depCtr := range depCtrs { s.addCtrToDependsMap(ctr.ID(), depCtr) @@ -160,6 +241,10 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { // Almost no validity checks are performed, to ensure we can kick // misbehaving containers out of the state + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Ensure we don't remove a container which other containers depend on deps, ok := s.ctrDepends[ctr.ID()] if ok && len(deps) != 0 { @@ -180,6 +265,17 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error { delete(s.ctrDepends, ctr.ID()) + if ctr.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", ctr.config.Namespace) + } + if err := nsIndex.idIndex.Delete(ctr.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", ctr.ID()) + } + nsIndex.nameIndex.Release(ctr.Name()) + } + // Remove us from container dependencies depCtrs := ctr.Dependencies() for _, depCtr := range depCtrs { @@ -204,7 +300,7 @@ func (s *InMemoryState) UpdateContainer(ctr *Container) error { return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) } - return nil + return s.checkNSMatch(ctr.ID(), ctr.Namespace()) } // SaveContainer saves a container's state @@ -223,7 +319,7 @@ func (s *InMemoryState) SaveContainer(ctr *Container) error { return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) } - return nil + return s.checkNSMatch(ctr.ID(), ctr.Namespace()) } // ContainerInUse checks if the given container is being used by other containers @@ -232,6 +328,16 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) { return nil, ErrCtrRemoved } + // If the container does not exist, return error + if _, ok := s.containers[ctr.ID()]; !ok { + ctr.valid = false + return nil, errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) + } + + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return nil, err + } + arr, ok := s.ctrDepends[ctr.ID()] if !ok { return []string{}, nil @@ -244,7 +350,9 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) { func (s *InMemoryState) AllContainers() ([]*Container, error) { ctrs := make([]*Container, 0, len(s.containers)) for _, ctr := range s.containers { - ctrs = append(ctrs, ctr) + if s.namespace == "" || ctr.config.Namespace == s.namespace { + ctrs = append(ctrs, ctr) + } } return ctrs, nil @@ -261,21 +369,44 @@ func (s *InMemoryState) Pod(id string) (*Pod, error) { return nil, errors.Wrapf(ErrNoSuchPod, "no pod with id %s found", id) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + return pod, nil } // LookupPod retrieves a pod from the state from a full or unique partial ID or // a full name func (s *InMemoryState) LookupPod(idOrName string) (*Pod, error) { + var ( + nameIndex *registrar.Registrar + idIndex *truncindex.TruncIndex + ) + if idOrName == "" { return nil, ErrEmptyID } - fullID, err := s.nameIndex.Get(idOrName) + if s.namespace != "" { + nsIndex, ok := s.namespaceIndexes[s.namespace] + if !ok { + // We have no containers in the namespace + // Return false + return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName) + } + nameIndex = nsIndex.nameIndex + idIndex = nsIndex.idIndex + } else { + nameIndex = s.nameIndex + idIndex = s.idIndex + } + + fullID, err := nameIndex.Get(idOrName) if err != nil { if err == registrar.ErrNameNotReserved { // What was passed is not a name, assume it's an ID - fullID, err = s.idIndex.Get(idOrName) + fullID, err = idIndex.Get(idOrName) if err != nil { if err == truncindex.ErrNotExist { return nil, errors.Wrapf(ErrNoSuchPod, "no pod found with name or ID %s", idOrName) @@ -302,9 +433,12 @@ func (s *InMemoryState) HasPod(id string) (bool, error) { return false, ErrEmptyID } - _, ok := s.pods[id] + pod, ok := s.pods[id] + if !ok || (s.namespace != "" && s.namespace != pod.config.Namespace) { + return false, nil + } - return ok, nil + return true, nil } // PodHasContainer checks if the given pod has a container with the given ID @@ -317,6 +451,10 @@ func (s *InMemoryState) PodHasContainer(pod *Pod, ctrID string) (bool, error) { return false, ErrEmptyID } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return false, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -333,6 +471,10 @@ func (s *InMemoryState) PodContainersByID(pod *Pod) ([]string, error) { return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -358,6 +500,10 @@ func (s *InMemoryState) PodContainers(pod *Pod) ([]*Container, error) { return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return nil, err + } + podCtrs, ok := s.podContainers[pod.ID()] if !ok { pod.valid = false @@ -383,6 +529,10 @@ func (s *InMemoryState) AddPod(pod *Pod) error { return errors.Wrapf(ErrPodRemoved, "pod %s is not valid and cannot be added", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; ok { return errors.Wrapf(ErrPodExists, "pod with ID %s already exists in state", pod.ID()) } @@ -404,6 +554,25 @@ func (s *InMemoryState) AddPod(pod *Pod) error { s.podContainers[pod.ID()] = make(map[string]*Container) + // If we're in a namespace, add us to that namespace's indexes + if pod.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[pod.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[pod.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(pod.Name(), pod.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", pod.Name()) + } + if err := nsIndex.idIndex.Add(pod.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", pod.ID()) + } + } + return nil } @@ -413,6 +582,10 @@ func (s *InMemoryState) RemovePod(pod *Pod) error { // Don't make many validity checks to ensure we can kick badly formed // pods out of the state + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -433,6 +606,17 @@ func (s *InMemoryState) RemovePod(pod *Pod) error { delete(s.podContainers, pod.ID()) s.nameIndex.Release(pod.Name()) + if pod.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[pod.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", pod.config.Namespace) + } + if err := nsIndex.idIndex.Delete(pod.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", pod.ID()) + } + nsIndex.nameIndex.Release(pod.Name()) + } + return nil } @@ -445,6 +629,10 @@ func (s *InMemoryState) RemovePodContainers(pod *Pod) error { return errors.Wrapf(ErrPodRemoved, "pod %s is not valid", pod.ID()) } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + // Get pod containers podCtrs, ok := s.podContainers[pod.ID()] if !ok { @@ -494,6 +682,15 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { return errors.Wrapf(ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID()) } + if ctr.config.Namespace != pod.config.Namespace { + return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and pod %s is in namespace %s", + ctr.ID(), ctr.config.Namespace, pod.ID(), pod.config.Namespace) + } + + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Retrieve pod containers list podCtrs, ok := s.podContainers[pod.ID()] if !ok { @@ -514,9 +711,13 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { if _, ok = s.containers[depCtr]; !ok { return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr) } - if _, ok = podCtrs[depCtr]; !ok { + depCtrStruct, ok := podCtrs[depCtr] + if !ok { return errors.Wrapf(ErrInvalidArg, "cannot depend on container %s as it is not in pod %s", depCtr, pod.ID()) } + if depCtrStruct.config.Namespace != ctr.config.Namespace { + return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %s and cannot depend on container %s in namespace %s", ctr.ID(), ctr.config.Namespace, depCtr, depCtrStruct.config.Namespace) + } } // Add container to state @@ -538,6 +739,25 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error { // Add container to pod containers podCtrs[ctr.ID()] = ctr + // If we're in a namespace, add us to that namespace's indexes + if ctr.config.Namespace != "" { + var nsIndex *namespaceIndex + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + nsIndex = new(namespaceIndex) + nsIndex.nameIndex = registrar.NewRegistrar() + nsIndex.idIndex = truncindex.NewTruncIndex([]string{}) + s.namespaceIndexes[ctr.config.Namespace] = nsIndex + } + // Should be no errors here, the previous index adds should have caught that + if err := nsIndex.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container name %s", ctr.Name()) + } + if err := nsIndex.idIndex.Add(ctr.ID()); err != nil { + return errors.Wrapf(err, "error registering container ID %s", ctr.ID()) + } + } + // Add containers this container depends on for _, depCtr := range depCtrs { s.addCtrToDependsMap(ctr.ID(), depCtr) @@ -556,6 +776,10 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { return errors.Wrapf(ErrCtrRemoved, "container %s is not valid and cannot be removed from the pod", ctr.ID()) } + if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { + return err + } + // Ensure we don't remove a container which other containers depend on deps, ok := s.ctrDepends[ctr.ID()] if ok && len(deps) != 0 { @@ -595,6 +819,17 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { // Remove the container from the pod delete(podCtrs, ctr.ID()) + if ctr.config.Namespace != "" { + nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] + if !ok { + return errors.Wrapf(ErrInternal, "error retrieving index for namespace %q", ctr.config.Namespace) + } + if err := nsIndex.idIndex.Delete(ctr.ID()); err != nil { + return errors.Wrapf(err, "error removing container %s from namespace ID index", ctr.ID()) + } + nsIndex.nameIndex.Release(ctr.Name()) + } + // Remove us from container dependencies depCtrs := ctr.Dependencies() for _, depCtr := range depCtrs { @@ -611,6 +846,10 @@ func (s *InMemoryState) UpdatePod(pod *Pod) error { return ErrPodRemoved } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -626,6 +865,10 @@ func (s *InMemoryState) SavePod(pod *Pod) error { return ErrPodRemoved } + if err := s.checkNSMatch(pod.ID(), pod.Namespace()); err != nil { + return err + } + if _, ok := s.pods[pod.ID()]; !ok { pod.valid = false return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) @@ -638,7 +881,13 @@ func (s *InMemoryState) SavePod(pod *Pod) error { func (s *InMemoryState) AllPods() ([]*Pod, error) { pods := make([]*Pod, 0, len(s.pods)) for _, pod := range s.pods { - pods = append(pods, pod) + if s.namespace != "" { + if s.namespace == pod.config.Namespace { + pods = append(pods, pod) + } + } else { + pods = append(pods, pod) + } } return pods, nil @@ -683,3 +932,13 @@ func (s *InMemoryState) removeCtrFromDependsMap(ctrID, dependsID string) { s.ctrDepends[dependsID] = newArr } } + +// Check if we can access a pod or container, or if that is blocked by +// namespaces. +func (s *InMemoryState) checkNSMatch(id, ns string) error { + if s.namespace != "" && s.namespace != ns { + return errors.Wrapf(ErrNSMismatch, "cannot access %s as it is in namespace %q and we are in namespace %q", + id, ns, s.namespace) + } + return nil +} |