From 40e5ae30d59f070874322a523c800e57a0ee8825 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 13 Apr 2021 13:46:44 -0400 Subject: [PATCH] Remove in-memory state implementation We originally added this in the *very early* days of Podman, before a proper persistent state was written, so we had something to test with. It was retained after the original SQLite state (and current BoltDB state) were written so it could be used for testing Libpod in unit tests with no requirement for on-disk storage. Well, such unit tests never materialized, and if we were to write some now the requirement to have a temporary directory for storing data on disk is not that bad. I can basically guarantee there are no users of this in the wild because, even if you managed to figure out how to configure it when we don't document it, it's completely unusable with Podman since all your containers and pods will disappear every time Podman exits. Given all this, and since it's an ongoing maintenance burden I no longer wish to deal with, let's just remove it. Signed-off-by: Matthew Heon --- libpod/in_memory_state.go | 1706 ------------------------------- libpod/runtime.go | 6 +- libpod/state_test.go | 28 +- pkg/registrar/registrar.go | 127 --- pkg/registrar/registrar_test.go | 213 ---- 5 files changed, 2 insertions(+), 2078 deletions(-) delete mode 100644 libpod/in_memory_state.go delete mode 100644 pkg/registrar/registrar.go delete mode 100644 pkg/registrar/registrar_test.go diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go deleted file mode 100644 index df45f8e734..0000000000 --- a/libpod/in_memory_state.go +++ /dev/null @@ -1,1706 +0,0 @@ -package libpod - -import ( - "strings" - - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/registrar" - "github.com/containers/storage/pkg/truncindex" - "github.com/pkg/errors" -) - -// TODO: Maybe separate idIndex for pod/containers -// As of right now, partial IDs used in Lookup... need to be unique as well -// This may be undesirable? - -// 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 volume ID to volume struct - volumes map[string]*Volume - // Maps exec session ID to ID of associated container - execSessions map[string]string - // Maps container ID to a list of IDs of dependencies. - ctrDepends map[string][]string - // Maps volume ID to IDs of dependencies - volumeDepends map[string][]string - // Maps container ID to IDs of associated exec sessions. - ctrExecSessions map[string][]string - // Maps pod ID to a map of container ID to container struct. - podContainers map[string]map[string]*Container - ctrNetworks map[string][]string - // Maps container ID to network name to list of aliases. - ctrNetworkAliases map[string]map[string][]string - // 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 -func NewInMemoryState() (State, error) { - state := new(InMemoryState) - - state.pods = make(map[string]*Pod) - state.containers = make(map[string]*Container) - state.volumes = make(map[string]*Volume) - state.execSessions = make(map[string]string) - - state.ctrDepends = make(map[string][]string) - state.volumeDepends = make(map[string][]string) - - state.ctrExecSessions = make(map[string][]string) - - state.podContainers = make(map[string]map[string]*Container) - - state.ctrNetworks = make(map[string][]string) - state.ctrNetworkAliases = make(map[string]map[string][]string) - - state.nameIndex = registrar.NewRegistrar() - state.idIndex = truncindex.NewTruncIndex([]string{}) - - state.namespace = "" - - state.namespaceIndexes = make(map[string]*namespaceIndex) - - return state, nil -} - -// Close the state before shutdown -// This is a no-op as we have no backing disk -func (s *InMemoryState) Close() error { - return nil -} - -// Refresh clears container and pod stats after a reboot -// In-memory state won't survive a reboot so this is a no-op -func (s *InMemoryState) Refresh() error { - return nil -} - -// GetDBConfig is not implemented for in-memory state. -// As we do not store a config, return an empty one. -func (s *InMemoryState) GetDBConfig() (*DBConfig, error) { - return &DBConfig{}, nil -} - -// ValidateDBConfig is not implemented for the in-memory state. -// Since we do nothing just return no error. -func (s *InMemoryState) ValidateDBConfig(runtime *Runtime) error { - return nil -} - -// SetNamespace sets the namespace for container and pod retrieval. -func (s *InMemoryState) SetNamespace(ns string) error { - s.namespace = ns - - return nil -} - -// GetName retrieves the name associated with a given ID. -// Works with both Container and Pod IDs. -func (s *InMemoryState) GetName(id string) (string, error) { - if id == "" { - return "", define.ErrEmptyID - } - - var idIndex *truncindex.TruncIndex - if s.namespace != "" { - nsIndex, ok := s.namespaceIndexes[s.namespace] - if !ok { - // We have no containers in the namespace - // Return false - return "", define.ErrNoSuchCtr - } - idIndex = nsIndex.idIndex - } else { - idIndex = s.idIndex - } - - fullID, err := idIndex.Get(id) - if err != nil { - if err == truncindex.ErrNotExist { - return "", define.ErrNoSuchCtr - } - return "", errors.Wrapf(err, "error performing truncindex lookup for ID %s", id) - } - return fullID, nil -} - -// Container retrieves a container from its full ID -func (s *InMemoryState) Container(id string) (*Container, error) { - if id == "" { - return nil, define.ErrEmptyID - } - - ctr, ok := s.containers[id] - if !ok { - return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container with ID %s found", id) - } - - if err := s.checkNSMatch(ctr.ID(), ctr.Namespace()); err != nil { - return nil, err - } - - return ctr, nil -} - -// lookupID retrieves a container or pod ID by full ID, unique partial ID, or -// name -func (s *InMemoryState) lookupID(idOrName string) (string, error) { - var ( - nameIndex *registrar.Registrar - idIndex *truncindex.TruncIndex - ) - - if idOrName == "" { - return "", define.ErrEmptyID - } - - if s.namespace != "" { - nsIndex, ok := s.namespaceIndexes[s.namespace] - if !ok { - // We have no containers in the namespace - // Return false - return "", define.ErrNoSuchCtr - } - 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 = idIndex.Get(idOrName) - if err != nil { - if err == truncindex.ErrNotExist { - return "", define.ErrNoSuchCtr - } - return "", errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName) - } - } else { - return "", errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName) - } - } - - return fullID, nil -} - -// LookupContainerID retrieves a container ID by full ID, unique partial ID, or -// name -func (s *InMemoryState) LookupContainerID(idOrName string) (string, error) { - fullID, err := s.lookupID(idOrName) - - switch err { - case nil: - _, ok := s.containers[fullID] - if !ok { - // It's a pod, not a container - return "", errors.Wrapf(define.ErrNoSuchCtr, "name or ID %s is a pod, not a container", idOrName) - } - - case define.ErrNoSuchCtr: - return "", errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName) - - default: - return "", err - } - - return fullID, nil -} - -// LookupContainer retrieves a container by full ID, unique partial ID, or name -func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) { - fullID, err := s.lookupID(idOrName) - - switch err { - case nil: - - case define.ErrNoSuchCtr: - return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName) - - default: - return nil, err - } - - ctr, ok := s.containers[fullID] - if !ok { - // It's a pod, not a container - return nil, errors.Wrapf(define.ErrNoSuchCtr, "name or ID %s is a pod, not a container", idOrName) - } - - 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, define.ErrEmptyID - } - - ctr, ok := s.containers[id] - if !ok || (s.namespace != "" && s.namespace != ctr.config.Namespace) { - return false, nil - } - - return true, nil -} - -// AddContainer adds a container to the state -// Containers in a pod cannot be added to the state -func (s *InMemoryState) AddContainer(ctr *Container) error { - if !ctr.valid { - return errors.Wrapf(define.ErrCtrRemoved, "container with ID %s is not valid", ctr.ID()) - } - - if _, ok := s.containers[ctr.ID()]; ok { - return errors.Wrapf(define.ErrCtrExists, "container with ID %s already exists in state", ctr.ID()) - } - - if ctr.config.Pod != "" { - return errors.Wrapf(define.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 - } - - // Check networks - for _, net := range ctr.config.Networks { - if net == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names cannot be empty") - } - } - - // Check network aliases - for network := range ctr.config.NetworkAliases { - inNet := false - for _, net := range ctr.config.Networks { - if net == network { - inNet = true - break - } - } - if !inNet { - return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network) - } - } - - // 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. - depCtrs := ctr.Dependencies() - for _, depID := range depCtrs { - depCtr, ok := s.containers[depID] - if !ok { - return errors.Wrapf(define.ErrNoSuchCtr, "cannot depend on nonexistent container %s", depID) - } else if depCtr.config.Pod != "" { - return errors.Wrapf(define.ErrInvalidArg, "cannot depend on container in a pod if not part of same pod") - } - if depCtr.config.Namespace != ctr.config.Namespace { - return errors.Wrapf(define.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 { - return errors.Wrapf(err, "error registering container name %s", ctr.Name()) - } - - if err := s.idIndex.Add(ctr.ID()); err != nil { - s.nameIndex.Release(ctr.Name()) - return errors.Wrapf(err, "error registering container ID %s", ctr.ID()) - } - - 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) - } - - // Add container to volume dependencies - for _, vol := range ctr.config.NamedVolumes { - s.addCtrToVolDependsMap(ctr.ID(), vol.Name) - } - - // Add networks - newNets := make([]string, 0, len(ctr.config.Networks)) - for _, net := range ctr.config.Networks { - if net == "" { - return define.ErrInvalidArg - } - newNets = append(newNets, net) - } - s.ctrNetworks[ctr.ID()] = newNets - - // Add network aliases - s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases - - 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 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 { - depsStr := strings.Join(deps, ", ") - return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr) - } - - // Ensure we don't have active exec sessions - ctrSessions := s.ctrExecSessions[ctr.ID()] - if len(ctrSessions) > 0 { - sessStr := strings.Join(ctrSessions, ", ") - return errors.Wrapf(define.ErrCtrExists, "the following exec sessions are running for container %s: %s", ctr.ID(), sessStr) - } - - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "no container exists in state with ID %s", ctr.ID()) - } - - if err := s.idIndex.Delete(ctr.ID()); err != nil { - return errors.Wrapf(err, "error removing container ID from index") - } - delete(s.containers, ctr.ID()) - s.nameIndex.Release(ctr.Name()) - - delete(s.ctrDepends, ctr.ID()) - - if ctr.config.Namespace != "" { - nsIndex, ok := s.namespaceIndexes[ctr.config.Namespace] - if !ok { - return errors.Wrapf(define.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 { - s.removeCtrFromDependsMap(ctr.ID(), depCtr) - } - - // Remove this container from volume dependencies - for _, vol := range ctr.config.NamedVolumes { - s.removeCtrFromVolDependsMap(ctr.ID(), vol.Name) - } - - // Remove our network aliases - delete(s.ctrNetworkAliases, ctr.ID()) - delete(s.ctrNetworks, ctr.ID()) - - return nil -} - -// UpdateContainer updates a container's state -// As all state is in-memory, no update will be required -// As such this is a no-op -func (s *InMemoryState) UpdateContainer(ctr *Container) error { - // If the container is invalid, return error - if !ctr.valid { - return errors.Wrapf(define.ErrCtrRemoved, "container with ID %s is not valid", ctr.ID()) - } - - // If the container does not exist, return error - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) - } - - return s.checkNSMatch(ctr.ID(), ctr.Namespace()) -} - -// SaveContainer saves a container's state -// As all state is in-memory, any changes are always reflected as soon as they -// are made -// As such this is a no-op -func (s *InMemoryState) SaveContainer(ctr *Container) error { - // If the container is invalid, return error - if !ctr.valid { - return errors.Wrapf(define.ErrCtrRemoved, "container with ID %s is not valid", ctr.ID()) - } - - // If the container does not exist, return error - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) - } - - return s.checkNSMatch(ctr.ID(), ctr.Namespace()) -} - -// ContainerInUse checks if the given container is being used by other containers -func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) { - if !ctr.valid { - return nil, define.ErrCtrRemoved - } - - // If the container does not exist, return error - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return nil, errors.Wrapf(define.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 - } - - return arr, 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 { - if s.namespace == "" || ctr.config.Namespace == s.namespace { - ctrs = append(ctrs, ctr) - } - } - - return ctrs, nil -} - -// Get all networks this container is present in. -func (s *InMemoryState) GetNetworks(ctr *Container) ([]string, error) { - if !ctr.valid { - return nil, define.ErrCtrRemoved - } - - ctr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return nil, define.ErrNoSuchCtr - } - - ctrNetworks, ok := s.ctrNetworks[ctr.ID()] - if !ok { - return nil, define.ErrNoSuchNetwork - } - - return ctrNetworks, nil -} - -// GetNetworkAliases returns network aliases for the given container in the -// given network. -func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]string, error) { - if !ctr.valid { - return nil, define.ErrCtrRemoved - } - - if network == "" { - return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - ctr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return nil, define.ErrNoSuchCtr - } - - inNet := false - for _, net := range ctr.config.Networks { - if net == network { - inNet = true - } - } - if !inNet { - return nil, define.ErrInvalidArg - } - - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if !ok { - return []string{}, nil - } - netAliases, ok := ctrAliases[network] - if !ok { - return []string{}, nil - } - - return netAliases, nil -} - -// GetAllNetworkAliases gets all network aliases for the given container. -func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]string, error) { - if !ctr.valid { - return nil, define.ErrCtrRemoved - } - - ctr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return nil, define.ErrNoSuchCtr - } - - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if !ok { - return map[string][]string{}, nil - } - - return ctrAliases, nil -} - -// NetworkConnect connects to the given network -func (s *InMemoryState) NetworkConnect(ctr *Container, network string, aliases []string) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - - if network == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - ctr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return define.ErrNoSuchCtr - } - - inNet := false - ctrNetworks, ok := s.ctrNetworks[ctr.ID()] - if !ok { - return define.ErrNoSuchNetwork - } - for _, net := range ctrNetworks { - if net == network { - inNet = true - } - } - if inNet { - return define.ErrNoSuchNetwork - } - s.ctrNetworks[ctr.ID()] = append(ctrNetworks, network) - - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if !ok { - ctrAliases = make(map[string][]string) - s.ctrNetworkAliases[ctr.ID()] = ctrAliases - } - ctrAliases[network] = aliases - - return nil -} - -// Disconnect from the given network and remove all aliases in it. -func (s *InMemoryState) NetworkDisconnect(ctr *Container, network string) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - - if network == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") - } - - ctr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return define.ErrNoSuchCtr - } - - ctrNetworks, ok := s.ctrNetworks[ctr.ID()] - if !ok { - return define.ErrNoSuchNetwork - } - inNet := false - remainingNets := make([]string, 0, len(ctrNetworks)) - for _, net := range ctrNetworks { - if net == network { - inNet = true - break - } else { - remainingNets = append(remainingNets, net) - } - } - if !inNet { - return define.ErrNoSuchNetwork - } - s.ctrNetworks[ctr.ID()] = remainingNets - - ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()] - if !ok { - ctrAliases = make(map[string][]string) - s.ctrNetworkAliases[ctr.ID()] = ctrAliases - } - delete(ctrAliases, network) - - return nil -} - -// GetContainerConfig returns a container config from the database by full ID -func (s *InMemoryState) GetContainerConfig(id string) (*ContainerConfig, error) { - ctr, err := s.LookupContainer(id) - if err != nil { - return nil, err - } - - return ctr.Config(), nil -} - -// Add an exec session to the database -func (s *InMemoryState) AddExecSession(ctr *Container, session *ExecSession) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - if session.ContainerID() != ctr.ID() { - return errors.Wrapf(define.ErrInvalidArg, "container ID and exec session ID must match") - } - if _, ok := s.containers[ctr.ID()]; !ok { - return define.ErrNoSuchCtr - } - - if _, ok := s.execSessions[session.ID()]; ok { - return define.ErrExecSessionExists - } - - s.execSessions[session.ID()] = ctr.ID() - - ctrSessions, ok := s.ctrExecSessions[ctr.ID()] - if !ok { - ctrSessions = []string{} - } - - ctrSessions = append(ctrSessions, session.ID()) - s.ctrExecSessions[ctr.ID()] = ctrSessions - - return nil -} - -// Get an exec session from the database by full or partial ID. -func (s *InMemoryState) GetExecSession(id string) (string, error) { - if id == "" { - return "", define.ErrEmptyID - } - - session, ok := s.execSessions[id] - if !ok { - return "", define.ErrNoSuchExecSession - } - - return session, nil -} - -// RemoveExecSession removes an exec session from the database. -func (s *InMemoryState) RemoveExecSession(session *ExecSession) error { - if _, ok := s.execSessions[session.ID()]; !ok { - return define.ErrNoSuchExecSession - } - - ctrSessions, ok := s.ctrExecSessions[session.ContainerID()] - // If !ok - internal state seems inconsistent, but the thing we wanted - // to remove is gone. Continue. - if ok { - newSessions := []string{} - for _, sess := range ctrSessions { - if sess != session.ID() { - newSessions = append(newSessions, sess) - } - } - s.ctrExecSessions[session.ContainerID()] = newSessions - } - - delete(s.execSessions, session.ID()) - - return nil -} - -// GetContainerExecSessions retrieves all exec sessions for the given container. -func (s *InMemoryState) GetContainerExecSessions(ctr *Container) ([]string, error) { - if !ctr.valid { - return nil, define.ErrCtrRemoved - } - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return nil, define.ErrNoSuchCtr - } - - ctrSessions := s.ctrExecSessions[ctr.ID()] - - return ctrSessions, nil -} - -// RemoveContainerExecSessions removes all exec sessions for the given -// container. -func (s *InMemoryState) RemoveContainerExecSessions(ctr *Container) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return define.ErrNoSuchCtr - } - - ctrSessions, ok := s.ctrExecSessions[ctr.ID()] - if !ok { - return nil - } - - for _, sess := range ctrSessions { - if _, ok := s.execSessions[sess]; !ok { - // We have an internal state inconsistency - // Error out - return errors.Wrapf(define.ErrInternal, "inconsistent database state: exec session %s is missing", sess) - } - delete(s.execSessions, sess) - } - delete(s.ctrExecSessions, ctr.ID()) - - return nil -} - -// RewriteContainerConfig rewrites a container's configuration. -// This function is DANGEROUS, even with an in-memory state. -// Please read the full comment on it in state.go before using it. -func (s *InMemoryState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - - // If the container does not exist, return error - stateCtr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) - } - - stateCtr.config = newCfg - - return nil -} - -// SafeRewriteContainerConfig rewrites a container's configuration. -// It's safer than RewriteContainerConfig, but still has limitations. Please -// read the comment in state.go before using. -func (s *InMemoryState) SafeRewriteContainerConfig(ctr *Container, oldName, newName string, newCfg *ContainerConfig) error { - if !ctr.valid { - return define.ErrCtrRemoved - } - - if _, err := s.nameIndex.Get(newName); err == nil { - return errors.Wrapf(define.ErrCtrExists, "name %s is in use", newName) - } - - // If the container does not exist, return error - stateCtr, ok := s.containers[ctr.ID()] - if !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID()) - } - - // Change name in registry. - if s.namespace != "" { - nsIndex, ok := s.namespaceIndexes[s.namespace] - if !ok { - return define.ErrInternal - } - nsIndex.nameIndex.Release(oldName) - if err := nsIndex.nameIndex.Reserve(newName, ctr.ID()); err != nil { - return errors.Wrapf(err, "error registering name %s", newName) - } - } - s.nameIndex.Release(oldName) - if err := s.nameIndex.Reserve(newName, ctr.ID()); err != nil { - return errors.Wrapf(err, "error registering name %s", newName) - } - - stateCtr.config = newCfg - - return nil -} - -// RewritePodConfig rewrites a pod's configuration. -// This function is DANGEROUS, even with in-memory state. -// Please read the full comment on it in state.go before using it. -func (s *InMemoryState) RewritePodConfig(pod *Pod, newCfg *PodConfig) error { - if !pod.valid { - return define.ErrPodRemoved - } - - // If the pod does not exist, return error - statePod, ok := s.pods[pod.ID()] - if !ok { - pod.valid = false - return errors.Wrapf(define.ErrNoSuchPod, "pod with ID %s not found in state", pod.ID()) - } - - statePod.config = newCfg - - return nil -} - -// RewriteVolumeConfig rewrites a volume's configuration. -// This function is DANGEROUS, even with in-memory state. -// Please read the full comment in state.go before using it. -func (s *InMemoryState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig) error { - if !volume.valid { - return define.ErrVolumeRemoved - } - - // If the volume does not exist, return error - stateVol, ok := s.volumes[volume.Name()] - if !ok { - volume.valid = false - return errors.Wrapf(define.ErrNoSuchVolume, "volume with name %q not found in state", volume.Name()) - } - - stateVol.config = newCfg - - return nil -} - -// Volume retrieves a volume from its full name -func (s *InMemoryState) Volume(name string) (*Volume, error) { - if name == "" { - return nil, define.ErrEmptyID - } - - vol, ok := s.volumes[name] - if !ok { - return nil, errors.Wrapf(define.ErrNoSuchCtr, "no volume with name %s found", name) - } - - return vol, nil -} - -// LookupVolume finds a volume from an unambiguous partial ID. -func (s *InMemoryState) LookupVolume(name string) (*Volume, error) { - if name == "" { - return nil, define.ErrEmptyID - } - - vol, ok := s.volumes[name] - if ok { - return vol, nil - } - - // Alright, we've failed to find by full name. Now comes the expensive - // part. - // Loop through all volumes and look for matches. - var ( - foundMatch bool - candidate *Volume - ) - for volName, vol := range s.volumes { - if strings.HasPrefix(volName, name) { - if foundMatch { - return nil, errors.Wrapf(define.ErrVolumeExists, "more than one result for volume name %q", name) - } - candidate = vol - foundMatch = true - } - } - - if !foundMatch { - return nil, errors.Wrapf(define.ErrNoSuchVolume, "no volume with name %q found", name) - } - - return candidate, nil -} - -// HasVolume checks if a volume with the given name is present in the state -func (s *InMemoryState) HasVolume(name string) (bool, error) { - if name == "" { - return false, define.ErrEmptyID - } - - _, ok := s.volumes[name] - if !ok { - return false, nil - } - - return true, nil -} - -// AddVolume adds a volume to the state -func (s *InMemoryState) AddVolume(volume *Volume) error { - if !volume.valid { - return errors.Wrapf(define.ErrVolumeRemoved, "volume with name %s is not valid", volume.Name()) - } - - if _, ok := s.volumes[volume.Name()]; ok { - return errors.Wrapf(define.ErrVolumeExists, "volume with name %s already exists in state", volume.Name()) - } - - s.volumes[volume.Name()] = volume - - return nil -} - -// RemoveVolume removes a volume from the state -func (s *InMemoryState) RemoveVolume(volume *Volume) error { - // Ensure we don't remove a volume which containers depend on - deps, ok := s.volumeDepends[volume.Name()] - if ok && len(deps) != 0 { - depsStr := strings.Join(deps, ", ") - return errors.Wrapf(define.ErrVolumeExists, "the following containers depend on volume %s: %s", volume.Name(), depsStr) - } - - if _, ok := s.volumes[volume.Name()]; !ok { - volume.valid = false - return errors.Wrapf(define.ErrVolumeRemoved, "no volume exists in state with name %s", volume.Name()) - } - - delete(s.volumes, volume.Name()) - - return nil -} - -// UpdateVolume updates a volume from the database. -// For the in-memory state, this is a no-op. -func (s *InMemoryState) UpdateVolume(volume *Volume) error { - if !volume.valid { - return define.ErrVolumeRemoved - } - - if _, ok := s.volumes[volume.Name()]; !ok { - volume.valid = false - return errors.Wrapf(define.ErrNoSuchVolume, "volume with name %q not found in state", volume.Name()) - } - - return nil -} - -// SaveVolume saves a volume's state to the database. -// For the in-memory state, this is a no-op. -func (s *InMemoryState) SaveVolume(volume *Volume) error { - if !volume.valid { - return define.ErrVolumeRemoved - } - - if _, ok := s.volumes[volume.Name()]; !ok { - volume.valid = false - return errors.Wrapf(define.ErrNoSuchVolume, "volume with name %q not found in state", volume.Name()) - } - - return nil -} - -// VolumeInUse checks if the given volume is being used by at least one container -func (s *InMemoryState) VolumeInUse(volume *Volume) ([]string, error) { - if !volume.valid { - return nil, define.ErrVolumeRemoved - } - - // If the volume does not exist, return error - if _, ok := s.volumes[volume.Name()]; !ok { - volume.valid = false - return nil, errors.Wrapf(define.ErrNoSuchVolume, "volume with name %s not found in state", volume.Name()) - } - - arr, ok := s.volumeDepends[volume.Name()] - if !ok { - return []string{}, nil - } - - return arr, nil -} - -// AllVolumes returns all volumes that exist in the state -func (s *InMemoryState) AllVolumes() ([]*Volume, error) { - allVols := make([]*Volume, 0, len(s.volumes)) - for _, v := range s.volumes { - allVols = append(allVols, v) - } - - return allVols, nil -} - -// Pod retrieves a pod from the state from its full ID -func (s *InMemoryState) Pod(id string) (*Pod, error) { - if id == "" { - return nil, define.ErrEmptyID - } - - pod, ok := s.pods[id] - if !ok { - return nil, errors.Wrapf(define.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) { - fullID, err := s.lookupID(idOrName) - - switch err { - case nil: - - case define.ErrNoSuchCtr, define.ErrNoSuchPod: - return nil, errors.Wrapf(define.ErrNoSuchPod, "no pod found with name or ID %s", idOrName) - - default: - return nil, err - } - - pod, ok := s.pods[fullID] - if !ok { - // It's a container not a pod - return nil, errors.Wrapf(define.ErrNoSuchPod, "id or name %s is a container, not a pod", idOrName) - } - - 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, define.ErrEmptyID - } - - pod, ok := s.pods[id] - if !ok || (s.namespace != "" && s.namespace != pod.config.Namespace) { - return false, nil - } - - return true, nil -} - -// 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(define.ErrPodRemoved, "pod %s is not valid", pod.ID()) - } - - if ctrID == "" { - return false, define.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 - return false, errors.Wrapf(define.ErrNoSuchPod, "no pod with ID %s found in state", pod.ID()) - } - - _, 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(define.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 - return nil, errors.Wrapf(define.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(define.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 - return nil, errors.Wrapf(define.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 -func (s *InMemoryState) AddPod(pod *Pod) error { - if !pod.valid { - return errors.Wrapf(define.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(define.ErrPodExists, "pod with ID %s already exists in state", pod.ID()) - } - - if _, ok := s.podContainers[pod.ID()]; ok { - return errors.Wrapf(define.ErrPodExists, "pod with ID %s already exists in state", pod.ID()) - } - - if err := s.nameIndex.Reserve(pod.Name(), pod.ID()); err != nil { - return errors.Wrapf(err, "error registering pod name %s", pod.Name()) - } - - if err := s.idIndex.Add(pod.ID()); err != nil { - s.nameIndex.Release(pod.Name()) - return errors.Wrapf(err, "error registering pod ID %s", pod.ID()) - } - - s.pods[pod.ID()] = pod - - 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 -} - -// RemovePod removes a given pod from the state -// 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 - - 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(define.ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) - } - podCtrs, ok := s.podContainers[pod.ID()] - if !ok { - pod.valid = false - return errors.Wrapf(define.ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) - } - if len(podCtrs) != 0 { - return errors.Wrapf(define.ErrCtrExists, "pod %s is not empty and cannot be removed", pod.ID()) - } - - if err := s.idIndex.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.nameIndex.Release(pod.Name()) - - if pod.config.Namespace != "" { - nsIndex, ok := s.namespaceIndexes[pod.config.Namespace] - if !ok { - return errors.Wrapf(define.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 -} - -// RemovePodContainers removes all containers from a pod -// This is used to simultaneously remove a number of containers with -// many interdependencies -// Will only remove containers if no dependencies outside of the pod are present -func (s *InMemoryState) RemovePodContainers(pod *Pod) error { - if !pod.valid { - return errors.Wrapf(define.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 { - pod.valid = false - return errors.Wrapf(define.ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) - } - - // Go through container dependencies. Check to see if any are outside the pod. - for ctr := range podCtrs { - ctrDeps, ok := s.ctrDepends[ctr] - if ok { - for _, dep := range ctrDeps { - if _, ok := podCtrs[dep]; !ok { - return errors.Wrapf(define.ErrCtrExists, "container %s has dependency %s outside of pod %s", ctr, dep, pod.ID()) - } - } - } - } - - // All dependencies are OK to remove - // Remove all containers - s.podContainers[pod.ID()] = make(map[string]*Container) - for _, ctr := range podCtrs { - if err := s.idIndex.Delete(ctr.ID()); err != nil { - return errors.Wrapf(err, "error removing container ID from index") - } - s.nameIndex.Release(ctr.Name()) - - delete(s.containers, ctr.ID()) - delete(s.ctrDepends, ctr.ID()) - } - - 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 { - if !pod.valid { - return errors.Wrapf(define.ErrPodRemoved, "pod %s is not valid", pod.ID()) - } - if !ctr.valid { - return errors.Wrapf(define.ErrCtrRemoved, "container %s is not valid", ctr.ID()) - } - - if ctr.config.Pod != pod.ID() { - return errors.Wrapf(define.ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID()) - } - - if ctr.config.Namespace != pod.config.Namespace { - return errors.Wrapf(define.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 - } - - // Check networks - for _, net := range ctr.config.Networks { - if net == "" { - return errors.Wrapf(define.ErrInvalidArg, "network names cannot be empty") - } - } - - // Check network aliases - for network := range ctr.config.NetworkAliases { - inNet := false - for _, net := range ctr.config.Networks { - if net == network { - inNet = true - break - } - } - if !inNet { - return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network) - } - } - - // Retrieve pod containers list - podCtrs, ok := s.podContainers[pod.ID()] - if !ok { - pod.valid = false - return errors.Wrapf(define.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(define.ErrCtrExists, "container with ID %s already exists in pod %s", ctr.ID(), pod.ID()) - } - - // 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. - depCtrs := ctr.Dependencies() - for _, depCtr := range depCtrs { - if _, ok = s.containers[depCtr]; !ok { - return errors.Wrapf(define.ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr) - } - depCtrStruct, ok := podCtrs[depCtr] - if !ok { - return errors.Wrapf(define.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(define.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 - if _, ok = s.containers[ctr.ID()]; ok { - return errors.Wrapf(define.ErrCtrExists, "container with ID %s already exists in state", ctr.ID()) - } - - if err := s.nameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil { - return errors.Wrapf(err, "error reserving container name %s", ctr.Name()) - } - - if err := s.idIndex.Add(ctr.ID()); err != nil { - s.nameIndex.Release(ctr.Name()) - return errors.Wrapf(err, "error releasing container ID %s", ctr.ID()) - } - - s.containers[ctr.ID()] = ctr - - // 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) - } - - // Add container to volume dependencies - for _, vol := range ctr.config.NamedVolumes { - s.addCtrToVolDependsMap(ctr.ID(), vol.Name) - } - - // Add networks - newNets := make([]string, 0, len(ctr.config.Networks)) - for _, net := range ctr.config.Networks { - if net == "" { - return define.ErrInvalidArg - } - newNets = append(newNets, net) - } - s.ctrNetworks[ctr.ID()] = newNets - - // Add network aliases - s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases - - return nil -} - -// RemoveContainerFromPod removes the given container from the given pod -// The container is also removed from the state -func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error { - if !pod.valid { - return errors.Wrapf(define.ErrPodRemoved, "pod %s is not valid and containers cannot be removed", pod.ID()) - } - if !ctr.valid { - return errors.Wrapf(define.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 { - depsStr := strings.Join(deps, ", ") - return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr) - } - - // Ensure we don't have active exec sessions - ctrSessions := s.ctrExecSessions[ctr.ID()] - if len(ctrSessions) > 0 { - sessStr := strings.Join(ctrSessions, ", ") - return errors.Wrapf(define.ErrCtrExists, "the following exec sessions are running for container %s: %s", ctr.ID(), sessStr) - } - - // Retrieve pod containers - podCtrs, ok := s.podContainers[pod.ID()] - if !ok { - pod.valid = false - return errors.Wrapf(define.ErrPodRemoved, "pod %s has been removed", pod.ID()) - } - - // Does the container exist? - if _, ok := s.containers[ctr.ID()]; !ok { - ctr.valid = false - return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in state", ctr.ID()) - } - - // Is the container in the pod? - if _, ok := podCtrs[ctr.ID()]; !ok { - return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in pod %s", ctr.ID(), pod.ID()) - } - - // Remove container from state - if _, ok := s.containers[ctr.ID()]; !ok { - return errors.Wrapf(define.ErrNoSuchCtr, "no container exists in state with ID %s", ctr.ID()) - } - - if err := s.idIndex.Delete(ctr.ID()); err != nil { - return errors.Wrapf(err, "error removing container ID from index") - } - delete(s.containers, ctr.ID()) - s.nameIndex.Release(ctr.Name()) - - // 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(define.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 { - s.removeCtrFromDependsMap(ctr.ID(), depCtr) - } - - // Remove our network aliases - delete(s.ctrNetworkAliases, ctr.ID()) - delete(s.ctrNetworks, ctr.ID()) - - return nil -} - -// UpdatePod updates a pod in the state -// This is a no-op as there is no backing store -func (s *InMemoryState) UpdatePod(pod *Pod) error { - if !pod.valid { - return define.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(define.ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) - } - - return nil -} - -// SavePod updates a pod in the state -// This is a no-op at there is no backing store -func (s *InMemoryState) SavePod(pod *Pod) error { - if !pod.valid { - return define.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(define.ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID()) - } - - 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 { - if s.namespace != "" { - if s.namespace == pod.config.Namespace { - pods = append(pods, pod) - } - } else { - pods = append(pods, pod) - } - } - - return pods, nil -} - -// Internal Functions - -// Add a container to the dependency mappings -func (s *InMemoryState) addCtrToDependsMap(ctrID, dependsID string) { - if dependsID != "" { - arr, ok := s.ctrDepends[dependsID] - if !ok { - // Do not have a mapping for that container yet - s.ctrDepends[dependsID] = []string{ctrID} - } else { - // Have a mapping for the container - arr = append(arr, ctrID) - s.ctrDepends[dependsID] = arr - } - } -} - -// Remove a container from dependency mappings -func (s *InMemoryState) removeCtrFromDependsMap(ctrID, dependsID string) { - if dependsID != "" { - arr, ok := s.ctrDepends[dependsID] - if !ok { - // Internal state seems inconsistent - // But the dependency is definitely gone - // So just return - return - } - - newArr := make([]string, 0, len(arr)) - - for _, id := range arr { - if id != ctrID { - newArr = append(newArr, id) - } - } - - s.ctrDepends[dependsID] = newArr - } -} - -// Add a container to the dependency mappings for the volume -func (s *InMemoryState) addCtrToVolDependsMap(depCtrID, volName string) { - if volName != "" { - arr, ok := s.volumeDepends[volName] - if !ok { - // Do not have a mapping for that volume yet - s.volumeDepends[volName] = []string{depCtrID} - } else { - // Have a mapping for the volume - arr = append(arr, depCtrID) - s.volumeDepends[volName] = arr - } - } -} - -// Remove a container from the dependency mappings for the volume -func (s *InMemoryState) removeCtrFromVolDependsMap(depCtrID, volName string) { - if volName != "" { - arr, ok := s.volumeDepends[volName] - if !ok { - // Internal state seems inconsistent - // But the dependency is definitely gone - // So just return - return - } - - newArr := make([]string, 0, len(arr)) - - for _, id := range arr { - if id != depCtrID { - newArr = append(newArr, id) - } - } - - s.volumeDepends[volName] = 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(define.ErrNSMismatch, "cannot access %s as it is in namespace %q and we are in namespace %q", - id, ns, s.namespace) - } - return nil -} diff --git a/libpod/runtime.go b/libpod/runtime.go index d4bb691ef7..3cee7af636 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -282,11 +282,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { // package. switch runtime.config.Engine.StateType { case config.InMemoryStateStore: - state, err := NewInMemoryState() - if err != nil { - return err - } - runtime.state = state + return errors.Wrapf(define.ErrInvalidArg, "in-memory state is currently disabled") case config.SQLiteStateStore: return errors.Wrapf(define.ErrInvalidArg, "SQLite state is currently disabled") case config.BoltDBStateStore: diff --git a/libpod/state_test.go b/libpod/state_test.go index 559c84d1ee..4799d7b8d6 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -28,8 +28,7 @@ const ( var ( testedStates = map[string]emptyStateFunc{ - "in-memory": getEmptyInMemoryState, - "boltdb": getEmptyBoltState, + "boltdb": getEmptyBoltState, } ) @@ -65,31 +64,6 @@ func getEmptyBoltState() (_ State, _ string, _ lock.Manager, retErr error) { return state, tmpDir, lockManager, nil } -// Get an empty in-memory state for use in tests -func getEmptyInMemoryState() (_ State, _ string, _ lock.Manager, retErr error) { - tmpDir, err := ioutil.TempDir("", tmpDirPrefix) - if err != nil { - return nil, "", nil, err - } - defer func() { - if retErr != nil { - os.RemoveAll(tmpDir) - } - }() - - state, err := NewInMemoryState() - if err != nil { - return nil, "", nil, err - } - - lockManager, err := lock.NewInMemoryManager(16) - if err != nil { - return nil, "", nil, err - } - - return state, tmpDir, lockManager, nil -} - func runForAllStates(t *testing.T, testFunc func(*testing.T, State, lock.Manager)) { for stateName, stateFunc := range testedStates { state, path, manager, err := stateFunc() diff --git a/pkg/registrar/registrar.go b/pkg/registrar/registrar.go deleted file mode 100644 index 1e75ee995b..0000000000 --- a/pkg/registrar/registrar.go +++ /dev/null @@ -1,127 +0,0 @@ -// Package registrar provides name registration. It reserves a name to a given key. -package registrar - -import ( - "errors" - "sync" -) - -var ( - // ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved - ErrNameReserved = errors.New("name is reserved") - // ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved - ErrNameNotReserved = errors.New("name is not reserved") - // ErrNoSuchKey is returned when trying to find the names for a key which is not known - ErrNoSuchKey = errors.New("provided key does not exist") -) - -// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registered to -// Names must be unique. -// Registrar is safe for concurrent access. -type Registrar struct { - idx map[string][]string - names map[string]string - mu sync.Mutex -} - -// NewRegistrar creates a new Registrar with the an empty index -func NewRegistrar() *Registrar { - return &Registrar{ - idx: make(map[string][]string), - names: make(map[string]string), - } -} - -// Reserve registers a key to a name -// Reserve is idempotent -// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved` -// A name reservation is globally unique -func (r *Registrar) Reserve(name, key string) error { - r.mu.Lock() - defer r.mu.Unlock() - - if k, exists := r.names[name]; exists { - if k != key { - return ErrNameReserved - } - return nil - } - - r.idx[key] = append(r.idx[key], name) - r.names[name] = key - return nil -} - -// Release releases the reserved name -// Once released, a name can be reserved again -func (r *Registrar) Release(name string) { - r.mu.Lock() - defer r.mu.Unlock() - - key, exists := r.names[name] - if !exists { - return - } - - for i, n := range r.idx[key] { - if n != name { - continue - } - r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...) - break - } - - delete(r.names, name) - - if len(r.idx[key]) == 0 { - delete(r.idx, key) - } -} - -// Delete removes all reservations for the passed in key. -// All names reserved to this key are released. -func (r *Registrar) Delete(key string) { - r.mu.Lock() - for _, name := range r.idx[key] { - delete(r.names, name) - } - delete(r.idx, key) - r.mu.Unlock() -} - -// GetNames lists all the reserved names for the given key -func (r *Registrar) GetNames(key string) ([]string, error) { - r.mu.Lock() - defer r.mu.Unlock() - - names, exists := r.idx[key] - if !exists { - return nil, ErrNoSuchKey - } - return names, nil -} - -// Get returns the key that the passed in name is reserved to -func (r *Registrar) Get(name string) (string, error) { - r.mu.Lock() - key, exists := r.names[name] - r.mu.Unlock() - - if !exists { - return "", ErrNameNotReserved - } - return key, nil -} - -// GetAll returns all registered names -func (r *Registrar) GetAll() map[string][]string { - out := make(map[string][]string) - - r.mu.Lock() - // copy index into out - for id, names := range r.idx { - out[id] = names - } - r.mu.Unlock() - return out -} diff --git a/pkg/registrar/registrar_test.go b/pkg/registrar/registrar_test.go deleted file mode 100644 index dc9942e804..0000000000 --- a/pkg/registrar/registrar_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package registrar_test - -import ( - "testing" - - "github.com/containers/podman/v3/pkg/registrar" - . "github.com/containers/podman/v3/test/framework" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -// TestRegistrar runs the created specs -func TestRegistrar(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Registrar") -} - -// nolint: gochecknoglobals -var t *TestFramework - -var _ = BeforeSuite(func() { - t = NewTestFramework(NilFunc, NilFunc) - t.Setup() -}) - -var _ = AfterSuite(func() { - t.Teardown() -}) - -// The actual test suite -var _ = t.Describe("Registrar", func() { - // Constant test data needed by some tests - const ( - testKey = "testKey" - testName = "testName" - anotherKey = "anotherKey" - ) - - // The system under test - var sut *registrar.Registrar - - // Prepare the system under test and register a test name and key before - // each test - BeforeEach(func() { - sut = registrar.NewRegistrar() - Expect(sut.Reserve(testName, testKey)).To(BeNil()) - }) - - t.Describe("Reserve", func() { - It("should succeed to reserve a new registrar", func() { - // Given - // When - err := sut.Reserve("name", "key") - - // Then - Expect(err).To(BeNil()) - }) - - It("should succeed to reserve a registrar twice", func() { - // Given - // When - err := sut.Reserve(testName, testKey) - - // Then - Expect(err).To(BeNil()) - }) - - It("should fail to reserve an already reserved registrar", func() { - // Given - // When - err := sut.Reserve(testName, anotherKey) - - // Then - Expect(err).NotTo(BeNil()) - Expect(err).To(Equal(registrar.ErrNameReserved)) - }) - }) - - t.Describe("Release", func() { - It("should succeed to release a registered registrar multiple times", func() { - // Given - // When - // Then - sut.Release(testName) - sut.Release(testName) - }) - - It("should succeed to release a unknown registrar multiple times", func() { - // Given - // When - // Then - sut.Release(anotherKey) - sut.Release(anotherKey) - }) - - It("should succeed to release and re-register a registrar", func() { - // Given - // When - sut.Release(testName) - err := sut.Reserve(testName, testKey) - - // Then - Expect(err).To(BeNil()) - }) - }) - - t.Describe("GetNames", func() { - It("should succeed to retrieve a single name for a registrar", func() { - // Given - // When - names, err := sut.GetNames(testKey) - - // Then - Expect(err).To(BeNil()) - Expect(len(names)).To(Equal(1)) - Expect(names[0]).To(Equal(testName)) - }) - - It("should succeed to retrieve all names for a registrar", func() { - // Given - testNames := []string{"test1", "test2"} - for _, name := range testNames { - Expect(sut.Reserve(name, anotherKey)).To(BeNil()) - } - - // When - names, err := sut.GetNames(anotherKey) - - // Then - Expect(err).To(BeNil()) - Expect(len(names)).To(Equal(2)) - Expect(names).To(Equal(testNames)) - }) - }) - - t.Describe("GetNames", func() { - It("should succeed to retrieve a single name for a registrar", func() { - // Given - // When - names, err := sut.GetNames(testKey) - - // Then - Expect(err).To(BeNil()) - Expect(len(names)).To(Equal(1)) - Expect(names[0]).To(Equal(testName)) - }) - - It("should succeed to retrieve all names for a registrar", func() { - // Given - anotherKey := "anotherKey" - testNames := []string{"test1", "test2"} - for _, name := range testNames { - Expect(sut.Reserve(name, anotherKey)).To(BeNil()) - } - - // When - names, err := sut.GetNames(anotherKey) - - // Then - Expect(err).To(BeNil()) - Expect(len(names)).To(Equal(2)) - Expect(names).To(Equal(testNames)) - }) - }) - - t.Describe("Delete", func() { - It("should succeed to delete a registrar", func() { - // Given - // When - sut.Delete(testKey) - - // Then - names, err := sut.GetNames(testKey) - Expect(len(names)).To(BeZero()) - Expect(err).To(Equal(registrar.ErrNoSuchKey)) - }) - }) - - t.Describe("Get", func() { - It("should succeed to get a key for a registrar", func() { - // Given - // When - key, err := sut.Get(testName) - - // Then - Expect(err).To(BeNil()) - Expect(key).To(Equal(testKey)) - }) - - It("should fail to get a key for a not existing registrar", func() { - // Given - // When - key, err := sut.Get("notExistingName") - - // Then - Expect(key).To(BeEmpty()) - Expect(err).To(Equal(registrar.ErrNameNotReserved)) - }) - }) - - t.Describe("GetAll", func() { - It("should succeed to get all names", func() { - // Given - // When - names := sut.GetAll() - - // Then - Expect(len(names)).To(Equal(1)) - Expect(len(names[testKey])).To(Equal(1)) - Expect(names[testKey][0]).To(Equal(testName)) - }) - }) -})