Working in-memory state implementation
Signed-off-by: Matthew Heon <mheon@redhat.com>
This commit is contained in:
parent
3991a0531c
commit
92def27645
9 changed files with 352 additions and 22 deletions
|
@ -30,11 +30,6 @@ func (c *Container) ID() string {
|
|||
|
||||
// Name returns the container's name
|
||||
func (c *Container) Name() string {
|
||||
// Name can potentially be changed while a container is running
|
||||
// So lock access to it
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.name
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,11 @@ var (
|
|||
|
||||
// ErrInvalidArg indicates that an invalid argument was passed
|
||||
ErrInvalidArg = errors.New("invalid argument")
|
||||
// ErrEmptyID indicates that an empty ID was passed
|
||||
ErrEmptyID = errors.New("name or ID cannot be empty")
|
||||
|
||||
// ErrInternal indicates an internal library error
|
||||
ErrInternal = errors.New("internal libpod error")
|
||||
|
||||
// ErrRuntimeStopped indicates that the runtime has already been shut
|
||||
// down and no further operations can be performed on it
|
||||
|
|
273
libpod/in_memory_state.go
Normal file
273
libpod/in_memory_state.go
Normal file
|
@ -0,0 +1,273 @@
|
|||
package libpod
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/registrar"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// An InMemoryState is a purely in-memory state store
|
||||
type InMemoryState struct {
|
||||
pods map[string]*Pod
|
||||
containers map[string]*Container
|
||||
podNameIndex *registrar.Registrar
|
||||
podIDIndex *truncindex.TruncIndex
|
||||
ctrNameIndex *registrar.Registrar
|
||||
ctrIDIndex *truncindex.TruncIndex
|
||||
}
|
||||
|
||||
// NewInMemoryState initializes a new, empty in-memory state
|
||||
func NewInMemoryState() (State, error) {
|
||||
state := new(InMemoryState)
|
||||
|
||||
state.pods = make(map[string]*Pod)
|
||||
state.containers = make(map[string]*Container)
|
||||
|
||||
state.podNameIndex = registrar.NewRegistrar()
|
||||
state.ctrNameIndex = registrar.NewRegistrar()
|
||||
|
||||
state.podIDIndex = truncindex.NewTruncIndex([]string{})
|
||||
state.ctrIDIndex = truncindex.NewTruncIndex([]string{})
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// Container retrieves a container from its full ID
|
||||
func (s *InMemoryState) Container(id string) (*Container, error) {
|
||||
if id == "" {
|
||||
return nil, ErrEmptyID
|
||||
}
|
||||
|
||||
ctr, ok := s.containers[id]
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found", id)
|
||||
}
|
||||
|
||||
return ctr, nil
|
||||
}
|
||||
|
||||
// LookupContainer retrieves a container by full ID, unique partial ID, or name
|
||||
func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
|
||||
if idOrName == "" {
|
||||
return nil, ErrEmptyID
|
||||
}
|
||||
|
||||
fullID, err := s.ctrNameIndex.Get(idOrName)
|
||||
if err != nil {
|
||||
if err == registrar.ErrNameNotReserved {
|
||||
// What was passed is not a name, assume it's an ID
|
||||
fullID, err = s.ctrIDIndex.Get(idOrName)
|
||||
if err != nil {
|
||||
if err == truncindex.ErrNotExist {
|
||||
return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
|
||||
}
|
||||
return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
|
||||
}
|
||||
}
|
||||
|
||||
ctr, ok := s.containers[fullID]
|
||||
if !ok {
|
||||
// This should never happen
|
||||
return nil, errors.Wrapf(ErrInternal, "mismatch in container ID registry and containers map for ID %s", fullID)
|
||||
}
|
||||
|
||||
return ctr, nil
|
||||
}
|
||||
|
||||
// HasContainer checks if a container with the given ID is present in the state
|
||||
func (s *InMemoryState) HasContainer(id string) (bool, error) {
|
||||
if id == "" {
|
||||
return false, ErrEmptyID
|
||||
}
|
||||
|
||||
_, ok := s.containers[id]
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// AddContainer adds a container to the state
|
||||
// If the container belongs to a pod, the pod must already be present when the
|
||||
// container is added, and the container must be present in the pod
|
||||
func (s *InMemoryState) AddContainer(ctr *Container) error {
|
||||
if !ctr.valid {
|
||||
return errors.Wrapf(ErrCtrRemoved, "container with ID %s is not valid", ctr.ID())
|
||||
}
|
||||
|
||||
_, ok := s.containers[ctr.ID()]
|
||||
if ok {
|
||||
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in state", ctr.ID())
|
||||
}
|
||||
|
||||
if ctr.pod != nil {
|
||||
if _, ok := s.pods[ctr.pod.ID()]; !ok {
|
||||
return errors.Wrapf(ErrNoSuchPod, "pod %s does not exist, cannot add container %s", ctr.pod.ID(), ctr.ID())
|
||||
}
|
||||
|
||||
hasCtr, err := ctr.pod.HasContainer(ctr.ID())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error checking if container %s is present in pod %s", ctr.ID(), ctr.pod.ID())
|
||||
} else if !hasCtr {
|
||||
return errors.Wrapf(ErrNoSuchCtr, "container %s is not present in pod %s", ctr.ID(), ctr.pod.ID())
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.ctrNameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil {
|
||||
return errors.Wrapf(err, "error registering container name %s", ctr.Name())
|
||||
}
|
||||
|
||||
if err := s.ctrIDIndex.Add(ctr.ID()); err != nil {
|
||||
s.ctrNameIndex.Release(ctr.Name())
|
||||
return errors.Wrapf(err, "error registering container ID %s", ctr.ID())
|
||||
}
|
||||
|
||||
s.containers[ctr.ID()] = ctr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveContainer removes a container from the state
|
||||
// The container will only be removed from the state, not from the pod the container belongs to
|
||||
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 _, ok := s.containers[ctr.ID()]; !ok {
|
||||
return errors.Wrapf(ErrNoSuchCtr, "no container exists in state with ID %s", ctr.ID())
|
||||
}
|
||||
|
||||
if err := s.ctrIDIndex.Delete(ctr.ID()); err != nil {
|
||||
return errors.Wrapf(err, "error removing container ID from index")
|
||||
}
|
||||
delete(s.containers, ctr.ID())
|
||||
s.ctrNameIndex.Release(ctr.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllContainers retrieves all containers from the state
|
||||
func (s *InMemoryState) AllContainers() ([]*Container, error) {
|
||||
ctrs := make([]*Container, 0, len(s.containers))
|
||||
for _, ctr := range s.containers {
|
||||
ctrs = append(ctrs, ctr)
|
||||
}
|
||||
|
||||
return ctrs, nil
|
||||
}
|
||||
|
||||
// Pod retrieves a pod from the state from its full ID
|
||||
func (s *InMemoryState) Pod(id string) (*Pod, error) {
|
||||
if id == "" {
|
||||
return nil, ErrEmptyID
|
||||
}
|
||||
|
||||
pod, ok := s.pods[id]
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(ErrNoSuchPod, "no pod with id %s found", id)
|
||||
}
|
||||
|
||||
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) {
|
||||
if idOrName == "" {
|
||||
return nil, ErrEmptyID
|
||||
}
|
||||
|
||||
fullID, err := s.podNameIndex.Get(idOrName)
|
||||
if err != nil {
|
||||
if err == registrar.ErrNameNotReserved {
|
||||
// What was passed is not a name, assume it's an ID
|
||||
fullID, err = s.podIDIndex.Get(idOrName)
|
||||
if err != nil {
|
||||
if err == truncindex.ErrNotExist {
|
||||
return nil, errors.Wrapf(ErrNoSuchPod, "no pod found with name or ID %s", idOrName)
|
||||
}
|
||||
return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
|
||||
}
|
||||
}
|
||||
|
||||
pod, ok := s.pods[fullID]
|
||||
if !ok {
|
||||
// This should never happen
|
||||
return nil, errors.Wrapf(ErrInternal, "mismatch in pod ID registry and pod map for ID %s", fullID)
|
||||
}
|
||||
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
// HasPod checks if a pod with the given ID is present in the state
|
||||
func (s *InMemoryState) HasPod(id string) (bool, error) {
|
||||
if id == "" {
|
||||
return false, ErrEmptyID
|
||||
}
|
||||
|
||||
_, ok := s.pods[id]
|
||||
|
||||
return ok, 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())
|
||||
}
|
||||
|
||||
if _, ok := s.pods[pod.ID()]; ok {
|
||||
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 err := s.podNameIndex.Reserve(pod.Name(), pod.ID()); err != nil {
|
||||
return errors.Wrapf(err, "error registering pod name %s", pod.Name())
|
||||
}
|
||||
|
||||
if err := s.podIDIndex.Add(pod.ID()); err != nil {
|
||||
s.podNameIndex.Release(pod.Name())
|
||||
return errors.Wrapf(err, "error registering pod ID %s", pod.ID())
|
||||
}
|
||||
|
||||
s.pods[pod.ID()] = pod
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePod removes a given pod from the state
|
||||
// Containers within the pod will not be removed or changed
|
||||
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 _, ok := s.pods[pod.ID()]; !ok {
|
||||
return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", 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())
|
||||
s.podNameIndex.Release(pod.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllPods retrieves all pods currently in the state
|
||||
func (s *InMemoryState) AllPods() ([]*Pod, error) {
|
||||
pods := make([]*Pod, 0, len(s.pods))
|
||||
for _, pod := range s.pods {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
|
||||
return pods, nil
|
||||
}
|
|
@ -228,12 +228,12 @@ func (r *Runtime) WithPod(pod *Pod) CtrCreateOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithLabels adds labels to the pod
|
||||
// WithLabels adds labels to the container
|
||||
func WithLabels(labels map[string]string) CtrCreateOption {
|
||||
return ctrNotImplemented
|
||||
}
|
||||
|
||||
// WithAnnotations adds annotations to the pod
|
||||
// WithAnnotations adds annotations to the container
|
||||
func WithAnnotations(annotations map[string]string) CtrCreateOption {
|
||||
return ctrNotImplemented
|
||||
}
|
||||
|
|
|
@ -93,6 +93,20 @@ func (p *Pod) Kill(signal uint) error {
|
|||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
// HasContainer checks if a container is present in the pod
|
||||
func (p *Pod) HasContainer(id string) (bool, error) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
if !p.valid {
|
||||
return false, ErrPodRemoved
|
||||
}
|
||||
|
||||
_, ok := p.containers[id]
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// GetContainers retrieves the containers in the pod
|
||||
func (p *Pod) GetContainers() ([]*Container, error) {
|
||||
p.lock.RLock()
|
||||
|
|
|
@ -86,6 +86,13 @@ func NewRuntime(options ...RuntimeOption) (*Runtime, error) {
|
|||
SignaturePolicyPath: runtime.config.SignaturePolicyPath,
|
||||
}
|
||||
|
||||
// Set up the state
|
||||
state, err := NewInMemoryState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime.state = state
|
||||
|
||||
runtime.seccompEnabled = seccomp.IsEnabled()
|
||||
runtime.apparmorEnabled = apparmor.IsEnabled()
|
||||
|
||||
|
|
|
@ -60,7 +60,42 @@ func (r *Runtime) NewContainer(spec *spec.Spec, options ...CtrCreateOption) (*Co
|
|||
// If force is specified, the container will be stopped first
|
||||
// Otherwise, RemoveContainer will return an error if the container is running
|
||||
func (r *Runtime) RemoveContainer(c *Container, force bool) error {
|
||||
return ErrNotImplemented
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if !r.valid {
|
||||
return ErrRuntimeStopped
|
||||
}
|
||||
|
||||
if !c.valid {
|
||||
return ErrCtrRemoved
|
||||
}
|
||||
|
||||
// TODO check container status and unmount storage
|
||||
// TODO check that no other containers depend on this container's
|
||||
// namespaces
|
||||
if err := c.Status(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.state.RemoveContainer(c); err != nil {
|
||||
return errors.Wrapf(err, "error removing container from state")
|
||||
}
|
||||
|
||||
// Set container as invalid so it can no longer be used
|
||||
c.valid = false
|
||||
|
||||
// Remove container from pod, if it joined one
|
||||
if c.pod != nil {
|
||||
if err := c.pod.removeContainer(c); err != nil {
|
||||
return errors.Wrapf(err, "error removing container from pod %s", c.pod.ID())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetContainer retrieves a container by its ID
|
||||
|
@ -72,7 +107,7 @@ func (r *Runtime) GetContainer(id string) (*Container, error) {
|
|||
return nil, ErrRuntimeStopped
|
||||
}
|
||||
|
||||
return r.state.GetContainer(id)
|
||||
return r.state.Container(id)
|
||||
}
|
||||
|
||||
// HasContainer checks if a container with the given ID is present
|
||||
|
@ -112,7 +147,7 @@ func (r *Runtime) GetContainers(filters ...ContainerFilter) ([]*Container, error
|
|||
return nil, ErrRuntimeStopped
|
||||
}
|
||||
|
||||
ctrs, err := r.state.GetAllContainers()
|
||||
ctrs, err := r.state.AllContainers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (r *Runtime) GetPod(id string) (*Pod, error) {
|
|||
return nil, ErrRuntimeStopped
|
||||
}
|
||||
|
||||
return r.state.GetPod(id)
|
||||
return r.state.Pod(id)
|
||||
}
|
||||
|
||||
// HasPod checks to see if a pod with the given ID exists
|
||||
|
@ -101,7 +101,7 @@ func (r *Runtime) Pods(filters ...PodFilter) ([]*Pod, error) {
|
|||
return nil, ErrRuntimeStopped
|
||||
}
|
||||
|
||||
pods, err := r.state.GetAllPods()
|
||||
pods, err := r.state.AllPods()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,35 +3,36 @@ package libpod
|
|||
// State is a storage backend for libpod's current state
|
||||
type State interface {
|
||||
// Accepts full ID of container
|
||||
GetContainer(id string) (*Container, error)
|
||||
Container(id string) (*Container, error)
|
||||
// Accepts full or partial IDs (as long as they are unique) and names
|
||||
LookupContainer(idOrName string) (*Container, error)
|
||||
// Checks if a container with the given ID is present in the state
|
||||
HasContainer(id string) (bool, error)
|
||||
// Adds container to state
|
||||
// If the container belongs to a pod, that pod must already be present
|
||||
// in the state when the container is added
|
||||
// in the state when the container is added, and the container must be
|
||||
// present in the pod
|
||||
AddContainer(ctr *Container) error
|
||||
// Removes container from state
|
||||
// If the container belongs to a pod, it will be removed from the pod
|
||||
// as well
|
||||
// The container will only be removed from the state, not from the pod
|
||||
// which the container belongs to
|
||||
RemoveContainer(ctr *Container) error
|
||||
// Retrieves all containers presently in state
|
||||
GetAllContainers() ([]*Container, error)
|
||||
AllContainers() ([]*Container, error)
|
||||
|
||||
// Accepts full ID of pod
|
||||
GetPod(id string) (*Pod, error)
|
||||
Pod(id string) (*Pod, error)
|
||||
// Accepts full or partial IDs (as long as they are unique) and names
|
||||
LookupPod(idOrName string) (*Pod, error)
|
||||
// Checks if a pod with the given ID is present in the state
|
||||
HasPod(id string) (bool, error)
|
||||
// Adds pod to state
|
||||
// Any containers within the pod not already in the state will be added
|
||||
// with it
|
||||
// Only empty pods can be added to the state
|
||||
AddPod(pod *Pod) error
|
||||
// Removes pod from state
|
||||
// All containers within the pod will also be removed
|
||||
// Containers within a pod will not be removed from the state, and will
|
||||
// not be changed to remove them from the now-removed pod
|
||||
RemovePod(pod *Pod) error
|
||||
// Retrieves all pods presently in state
|
||||
GetAllPods() ([]*Pod, error)
|
||||
AllPods() ([]*Pod, error)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue