1 Star 0 Fork 1

Derek Ray/podman

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
in_memory_state.go 32.24 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
package libpod
import (
"strings"
"github.com/containers/libpod/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
volumes map[string]*Volume
// Maps container ID to a list of IDs of dependencies.
ctrDepends map[string][]string
volumeDepends map[string][]string
// Maps pod ID to a map of container ID to container struct.
podContainers map[string]map[string]*Container
// 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.ctrDepends = make(map[string][]string)
state.volumeDepends = make(map[string][]string)
state.podContainers = make(map[string]map[string]*Container)
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
}
// 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)
}
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
}
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 = idIndex.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 {
// It's a pod, not a container
return nil, errors.Wrapf(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, 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(ErrCtrRemoved, "container with ID %s is not valid", ctr.ID())
}
if _, ok := s.containers[ctr.ID()]; ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in state", ctr.ID())
}
if ctr.config.Pod != "" {
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.
depCtrs := ctr.Dependencies()
for _, depID := range depCtrs {
depCtr, ok := s.containers[depID]
if !ok {
return errors.Wrapf(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depID)
} 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 {
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)
}
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(ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
if _, ok := s.containers[ctr.ID()]; !ok {
ctr.valid = false
return errors.Wrapf(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(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)
}
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(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(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(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(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, 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
}
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
}
// 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 ErrCtrRemoved
}
// If the container does not exist, return error
stateCtr, ok := s.containers[ctr.ID()]
if !ok {
ctr.valid = false
return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
}
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 ErrPodRemoved
}
// If the pod does not exist, return error
statePod, ok := s.pods[pod.ID()]
if !ok {
pod.valid = false
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found in state", pod.ID())
}
statePod.config = newCfg
return nil
}
// Volume retrieves a volume from its full name
func (s *InMemoryState) Volume(name string) (*Volume, error) {
if name == "" {
return nil, ErrEmptyID
}
vol, ok := s.volumes[name]
if !ok {
return nil, errors.Wrapf(ErrNoSuchCtr, "no volume with name %s found", name)
}
return vol, 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, 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(ErrVolumeRemoved, "volume with name %s is not valid", volume.Name())
}
if _, ok := s.volumes[volume.Name()]; ok {
return errors.Wrapf(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(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(ErrVolumeRemoved, "no volume exists in state with name %s", volume.Name())
}
delete(s.volumes, 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, ErrVolumeRemoved
}
// If the volume does not exist, return error
if _, ok := s.volumes[volume.Name()]; !ok {
volume.valid = false
return nil, errors.Wrapf(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, ErrEmptyID
}
pod, ok := s.pods[id]
if !ok {
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
}
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 = idIndex.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 {
// It's a container not a pod
return nil, errors.Wrapf(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, 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(ErrPodRemoved, "pod %s is not valid", pod.ID())
}
if ctrID == "" {
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
return false, errors.Wrapf(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(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(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", 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(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(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())
}
if _, ok := s.podContainers[pod.ID()]; ok {
return errors.Wrapf(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(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(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.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(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(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(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(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(ErrPodRemoved, "pod %s is not valid", pod.ID())
}
if !ctr.valid {
return errors.Wrapf(ErrCtrRemoved, "container %s is not valid", ctr.ID())
}
if ctr.config.Pod != pod.ID() {
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 {
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())
}
// 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(ErrNoSuchCtr, "cannot depend on nonexistent container %s", depCtr)
}
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
if _, ok = s.containers[ctr.ID()]; ok {
return errors.Wrapf(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)
}
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(ErrPodRemoved, "pod %s is not valid and containers cannot be removed", pod.ID())
}
if !ctr.valid {
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 {
depsStr := strings.Join(deps, ", ")
return errors.Wrapf(ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
// 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())
}
// Does the container exist?
if _, ok := s.containers[ctr.ID()]; !ok {
ctr.valid = false
return errors.Wrapf(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(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(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(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)
}
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 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())
}
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 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())
}
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(ErrNSMismatch, "cannot access %s as it is in namespace %q and we are in namespace %q",
id, ns, s.namespace)
}
return nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/derekhjray/podman.git
git@gitee.com:derekhjray/podman.git
derekhjray
podman
podman
v1.4.2-stable1

搜索帮助