Tear out pod containers map. Instead rely on state

This ensures that there is only one canonical place where
containers in a pod are stored, in the state itself.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #268
Approved by: rhatdan
This commit is contained in:
Matthew Heon 2018-01-18 10:49:01 -05:00 committed by Atomic Bot
parent bf981fc873
commit 6214be07c2
5 changed files with 154 additions and 117 deletions

View file

@ -8,15 +8,20 @@ import (
"github.com/projectatomic/libpod/pkg/registrar"
)
// TODO: unified name/ID registry to ensure no name and ID conflicts between
// containers and pods
// This can probably be used to replace the existing trunc index and registrars
// An InMemoryState is a purely in-memory state store
type InMemoryState struct {
pods map[string]*Pod
containers map[string]*Container
ctrDepends map[string][]string
podNameIndex *registrar.Registrar
podIDIndex *truncindex.TruncIndex
ctrNameIndex *registrar.Registrar
ctrIDIndex *truncindex.TruncIndex
pods map[string]*Pod
containers map[string]*Container
ctrDepends map[string][]string
podContainers map[string]map[string]*Container
podNameIndex *registrar.Registrar
podIDIndex *truncindex.TruncIndex
ctrNameIndex *registrar.Registrar
ctrIDIndex *truncindex.TruncIndex
}
// NewInMemoryState initializes a new, empty in-memory state
@ -28,6 +33,8 @@ func NewInMemoryState() (State, error) {
state.ctrDepends = make(map[string][]string)
state.podContainers = make(map[string]map[string]*Container)
state.podNameIndex = registrar.NewRegistrar()
state.ctrNameIndex = registrar.NewRegistrar()
@ -293,22 +300,73 @@ func (s *InMemoryState) HasPod(id string) (bool, error) {
return ok, nil
}
// PodContainers retrieves the containers from a pod given the pod's full ID
func (s *InMemoryState) PodContainers(id string) ([]*Container, error) {
if id == "" {
return nil, ErrEmptyID
// PodHasContainer checks if the given pod has a container with the given ID
func (s *InMemoryState) PodHasContainer(pod *Pod, ctrID string) (bool, error) {
if !pod.valid {
return false, errors.Wrapf(ErrPodRemoved, "pod %s is not valid")
}
pod, ok := s.pods[id]
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
return nil, errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found", id)
pod.valid = false
return false, errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in state", pod.ID())
}
return pod.GetContainers()
_, ok = podCtrs[ctrID]
return ok, nil
}
// PodContainersByID returns the IDs of all containers in the given pod
func (s *InMemoryState) PodContainersByID(pod *Pod) ([]string, error) {
if !pod.valid {
return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid")
}
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
pod.valid = false
return nil, errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in state", pod.ID())
}
length := len(podCtrs)
if length == 0 {
return []string{}, nil
}
ctrs := make([]string, 0, length)
for _, ctr := range podCtrs {
ctrs = append(ctrs, ctr.ID())
}
return ctrs, nil
}
// PodContainers retrieves the containers from a pod
func (s *InMemoryState) PodContainers(pod *Pod) ([]*Container, error) {
if !pod.valid {
return nil, errors.Wrapf(ErrPodRemoved, "pod %s is not valid")
}
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
pod.valid = false
return nil, errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in state", pod.ID())
}
length := len(podCtrs)
if length == 0 {
return []*Container{}, nil
}
ctrs := make([]*Container, 0, length)
for _, ctr := range podCtrs {
ctrs = append(ctrs, ctr)
}
return ctrs, nil
}
// AddPod adds a given pod to the state
// Only empty pods can be added to the state
func (s *InMemoryState) AddPod(pod *Pod) error {
if !pod.valid {
return errors.Wrapf(ErrPodRemoved, "pod %s is not valid and cannot be added", pod.ID())
@ -318,8 +376,8 @@ func (s *InMemoryState) AddPod(pod *Pod) error {
return errors.Wrapf(ErrPodExists, "pod with ID %s already exists in state", pod.ID())
}
if len(pod.containers) != 0 {
return errors.Wrapf(ErrInternal, "only empty pods can be added to the state")
if _, ok := s.podContainers[pod.ID()]; ok {
return errors.Wrapf(ErrPodExists, "pod with ID %s already exists in state", pod.ID())
}
if err := s.podNameIndex.Reserve(pod.Name(), pod.ID()); err != nil {
@ -333,11 +391,13 @@ func (s *InMemoryState) AddPod(pod *Pod) error {
s.pods[pod.ID()] = pod
s.podContainers[pod.ID()] = make(map[string]*Container)
return nil
}
// RemovePod removes a given pod from the state
// Containers within the pod will not be removed or changed
// Only empty pods can be removed
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
@ -345,22 +405,24 @@ func (s *InMemoryState) RemovePod(pod *Pod) error {
if _, ok := s.pods[pod.ID()]; !ok {
return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID())
}
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID())
}
if len(podCtrs) != 0 {
return errors.Wrapf(ErrCtrExists, "pod %s is not empty and cannot be removed", pod.ID())
}
if err := s.podIDIndex.Delete(pod.ID()); err != nil {
return errors.Wrapf(err, "error removing pod ID %s from index", pod.ID())
}
delete(s.pods, pod.ID())
delete(s.podContainers, pod.ID())
s.podNameIndex.Release(pod.Name())
return nil
}
// UpdatePod updates a pod's state from the backing database
// As in-memory states have no database this is a no-op
func (s *InMemoryState) UpdatePod(pod *Pod) error {
return nil
}
// AddContainerToPod adds a container to the given pod, also adding it to the
// state
func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
@ -375,13 +437,20 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
return errors.Wrapf(ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID())
}
// Add container to pod
if err := pod.addContainer(ctr); err != nil {
return err
// Retrieve pod containers list
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
pod.valid = false
return errors.Wrapf(ErrPodRemoved, "pod %s not found in state", pod.ID())
}
// Is the container already in the pod?
if _, ok := podCtrs[ctr.ID()]; ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in pod %s", ctr.ID(), pod.ID())
}
// Add container to state
_, ok := s.containers[ctr.ID()]
_, ok = s.containers[ctr.ID()]
if ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in state", ctr.ID())
}
@ -397,6 +466,9 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
s.containers[ctr.ID()] = ctr
// Add container to pod containers
podCtrs[ctr.ID()] = ctr
return nil
}
@ -410,18 +482,16 @@ 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())
}
// Is the container in the pod?
exists, err := pod.HasContainer(ctr.ID())
if err != nil {
return errors.Wrapf(err, "error checking for container %s in pod %s", ctr.ID(), pod.ID())
}
if !exists {
return errors.Wrapf(ErrNoSuchCtr, "no container %s in pod %s", ctr.ID(), pod.ID())
// Retrieve pod containers
podCtrs, ok := s.podContainers[pod.ID()]
if !ok {
pod.valid = false
return errors.Wrapf(ErrPodRemoved, "pod %s has been removed", pod.ID())
}
// Remove container from pod
if err := pod.removeContainer(ctr); err != nil {
return err
// Is the container in the pod?
if _, ok := podCtrs[ctr.ID()]; !ok {
return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in pod %s", ctr.ID(), pod.ID())
}
// Remove container from state
@ -435,6 +505,9 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
delete(s.containers, ctr.ID())
s.ctrNameIndex.Release(ctr.Name())
// Remove the container from the pod
delete(podCtrs, ctr.ID())
return nil
}

View file

@ -15,10 +15,9 @@ type Pod struct {
name string
labels map[string]string
containers map[string]*Container
valid bool
lock storage.Locker
valid bool
runtime *Runtime
lock storage.Locker
}
// ID retrieves the pod's ID
@ -42,17 +41,11 @@ func (p *Pod) Labels() map[string]string {
}
// Creates a new, empty pod
func newPod(lockDir string) (*Pod, error) {
func newPod(lockDir string, runtime *Runtime) (*Pod, error) {
pod := new(Pod)
pod.id = stringid.GenerateNonCryptoID()
pod.name = namesgenerator.GetRandomName(0)
pod.containers = make(map[string]*Container)
// TODO: containers and pods share a locks folder, but not tables in the
// database
// As the locks are 256-bit pseudorandom integers, collision is unlikely
// But it's something worth looking into
pod.runtime = runtime
// Path our lock file will reside at
lockPath := filepath.Join(lockDir, pod.id)
@ -66,47 +59,9 @@ func newPod(lockDir string) (*Pod, error) {
return pod, nil
}
// Adds a container to the pod
// Does not check that container's pod ID is set correctly, or attempt to set
// pod ID after adding
func (p *Pod) addContainer(ctr *Container) error {
p.lock.Lock()
defer p.lock.Unlock()
if !p.valid {
return ErrPodRemoved
}
if !ctr.valid {
return ErrCtrRemoved
}
if _, ok := p.containers[ctr.ID()]; ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in pod %s", ctr.ID(), p.id)
}
p.containers[ctr.ID()] = ctr
return nil
}
// Removes a container from the pod
// Does not perform any checks on the container
func (p *Pod) removeContainer(ctr *Container) error {
p.lock.Lock()
defer p.lock.Unlock()
if !p.valid {
return ErrPodRemoved
}
if _, ok := p.containers[ctr.ID()]; !ok {
return errors.Wrapf(ErrNoSuchCtr, "no container with id %s in pod %s", ctr.ID(), p.id)
}
delete(p.containers, ctr.ID())
return nil
// Init() initializes all containers within a pod that have not been initialized
func (p *Pod) Init() error {
return ErrNotImplemented
}
// Start starts all containers within a pod that are not already running
@ -126,20 +81,15 @@ func (p *Pod) Kill(signal uint) error {
// HasContainer checks if a container is present in the pod
func (p *Pod) HasContainer(id string) (bool, error) {
p.lock.Lock()
defer p.lock.Unlock()
if !p.valid {
return false, ErrPodRemoved
}
_, ok := p.containers[id]
return ok, nil
return p.runtime.state.PodHasContainer(p, id)
}
// GetContainers retrieves the containers in the pod
func (p *Pod) GetContainers() ([]*Container, error) {
// AllContainersID returns the container IDs of all the containers in the pod
func (p *Pod) AllContainersByID() ([]string, error) {
p.lock.Lock()
defer p.lock.Unlock()
@ -147,12 +97,19 @@ func (p *Pod) GetContainers() ([]*Container, error) {
return nil, ErrPodRemoved
}
ctrs := make([]*Container, 0, len(p.containers))
for _, ctr := range p.containers {
ctrs = append(ctrs, ctr)
return p.runtime.state.PodContainersByID(p)
}
// AllContainers retrieves the containers in the pod
func (p *Pod) AllContainers() ([]*Container, error) {
p.lock.Lock()
defer p.lock.Unlock()
if !p.valid {
return nil, ErrPodRemoved
}
return ctrs, nil
return p.runtime.state.PodContainers(p)
}
// Status gets the status of all containers in the pod

View file

@ -24,7 +24,7 @@ func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) {
return nil, ErrRuntimeStopped
}
pod, err := newPod(r.lockDir)
pod, err := newPod(r.lockDir, r)
if err != nil {
return nil, errors.Wrapf(err, "error creating pod")
}

View file

@ -839,8 +839,20 @@ func (s *SQLState) HasPod(id string) (bool, error) {
return false, ErrNotImplemented
}
// PodHasContainer checks if the given pod containers a container with the given
// ID
func (s *SQLState) PodHasContainer(pod *Pod, ctrID string) (bool, error) {
return false, ErrNotImplemented
}
// PodContainersByID returns the container IDs of all containers in the given
// pod
func (s *SQLState) PodContainersByID(pod *Pod) ([]string, error) {
return nil, ErrNotImplemented
}
// PodContainers returns all the containers in a pod given the pod's full ID
func (s *SQLState) PodContainers(id string) ([]*Container, error) {
func (s *SQLState) PodContainers(pod *Pod) ([]*Container, error) {
return nil, ErrNotImplemented
}
@ -856,11 +868,6 @@ func (s *SQLState) RemovePod(pod *Pod) error {
return ErrNotImplemented
}
// UpdatePod updates a pod from the database
func (s *SQLState) UpdatePod(pod *Pod) error {
return ErrNotImplemented
}
// AddContainerToPod adds a container to the given pod
func (s *SQLState) AddContainerToPod(pod *Pod, ctr *Container) error {
return ErrNotImplemented

View file

@ -42,17 +42,17 @@ type State interface {
LookupPod(idOrName string) (*Pod, error)
// Checks if a pod with the given ID is present in the state
HasPod(id string) (bool, error)
// Get all the containers in a pod. Accepts full ID of pod.
PodContainers(id string) ([]*Container, error)
// Check if a pod has a container with the given ID
PodHasContainer(pod *Pod, ctrID string) (bool, error)
// Get the IDs of all containers in a pod
PodContainersByID(pod *Pod) ([]string, error)
// Get all the containers in a pod
PodContainers(pod *Pod) ([]*Container, error)
// Adds pod to state
// Only empty pods can be added to the state
AddPod(pod *Pod) error
// Removes pod from state
// Containers within a pod will not be removed from the state, and will
// not be changed to remove them from the now-removed pod
// Only empty pods can be removed from the state
RemovePod(pod *Pod) error
// UpdatePod updates a pod's state from the backing store
UpdatePod(pod *Pod) error
// AddContainerToPod adds a container to an existing pod
// The container given will be added to the state and the pod
AddContainerToPod(pod *Pod, ctr *Container) error