summaryrefslogtreecommitdiff
path: root/libkpod/container_server.go
diff options
context:
space:
mode:
Diffstat (limited to 'libkpod/container_server.go')
-rw-r--r--libkpod/container_server.go775
1 files changed, 775 insertions, 0 deletions
diff --git a/libkpod/container_server.go b/libkpod/container_server.go
new file mode 100644
index 000000000..8da465fdf
--- /dev/null
+++ b/libkpod/container_server.go
@@ -0,0 +1,775 @@
+package libkpod
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sync"
+ "time"
+
+ "github.com/containers/image/types"
+ cstorage "github.com/containers/storage"
+ "github.com/docker/docker/pkg/ioutils"
+ "github.com/docker/docker/pkg/truncindex"
+ "github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
+ "github.com/kubernetes-incubator/cri-o/oci"
+ "github.com/kubernetes-incubator/cri-o/pkg/annotations"
+ "github.com/kubernetes-incubator/cri-o/pkg/registrar"
+ "github.com/kubernetes-incubator/cri-o/pkg/storage"
+ "github.com/opencontainers/runc/libcontainer"
+ rspec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/selinux/go-selinux"
+ "github.com/opencontainers/selinux/go-selinux/label"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
+)
+
+// ContainerServer implements the ImageServer
+type ContainerServer struct {
+ runtime *oci.Runtime
+ store cstorage.Store
+ storageImageServer storage.ImageServer
+ storageRuntimeServer storage.RuntimeServer
+ updateLock sync.RWMutex
+ ctrNameIndex *registrar.Registrar
+ ctrIDIndex *truncindex.TruncIndex
+ podNameIndex *registrar.Registrar
+ podIDIndex *truncindex.TruncIndex
+ hooks map[string]HookParams
+
+ imageContext *types.SystemContext
+ stateLock sync.Locker
+ state *containerServerState
+ config *Config
+}
+
+// Runtime returns the oci runtime for the ContainerServer
+func (c *ContainerServer) Runtime() *oci.Runtime {
+ return c.runtime
+}
+
+// Hooks returns the oci hooks for the ContainerServer
+func (c *ContainerServer) Hooks() map[string]HookParams {
+ return c.hooks
+}
+
+// Store returns the Store for the ContainerServer
+func (c *ContainerServer) Store() cstorage.Store {
+ return c.store
+}
+
+// StorageImageServer returns the ImageServer for the ContainerServer
+func (c *ContainerServer) StorageImageServer() storage.ImageServer {
+ return c.storageImageServer
+}
+
+// CtrNameIndex returns the Registrar for the ContainerServer
+func (c *ContainerServer) CtrNameIndex() *registrar.Registrar {
+ return c.ctrNameIndex
+}
+
+// CtrIDIndex returns the TruncIndex for the ContainerServer
+func (c *ContainerServer) CtrIDIndex() *truncindex.TruncIndex {
+ return c.ctrIDIndex
+}
+
+// PodNameIndex returns the index of pod names
+func (c *ContainerServer) PodNameIndex() *registrar.Registrar {
+ return c.podNameIndex
+}
+
+// PodIDIndex returns the index of pod IDs
+func (c *ContainerServer) PodIDIndex() *truncindex.TruncIndex {
+ return c.podIDIndex
+}
+
+// ImageContext returns the SystemContext for the ContainerServer
+func (c *ContainerServer) ImageContext() *types.SystemContext {
+ return c.imageContext
+}
+
+// Config gets the configuration for the ContainerServer
+func (c *ContainerServer) Config() *Config {
+ return c.config
+}
+
+// StorageRuntimeServer gets the runtime server for the ContainerServer
+func (c *ContainerServer) StorageRuntimeServer() storage.RuntimeServer {
+ return c.storageRuntimeServer
+}
+
+// New creates a new ContainerServer with options provided
+func New(config *Config) (*ContainerServer, error) {
+ store, err := cstorage.GetStore(cstorage.StoreOptions{
+ RunRoot: config.RunRoot,
+ GraphRoot: config.Root,
+ GraphDriverName: config.Storage,
+ GraphDriverOptions: config.StorageOptions,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ imageService, err := storage.GetImageService(store, config.DefaultTransport, config.InsecureRegistries, config.Registries)
+ if err != nil {
+ return nil, err
+ }
+
+ storageRuntimeService := storage.GetRuntimeService(imageService, config.PauseImage)
+ if err != nil {
+ return nil, err
+ }
+
+ runtime, err := oci.New(config.Runtime, config.RuntimeUntrustedWorkload, config.DefaultWorkloadTrust, config.Conmon, config.ConmonEnv, config.CgroupManager, config.ContainerExitsDir, config.LogSizeMax, config.NoPivot)
+ if err != nil {
+ return nil, err
+ }
+
+ var lock sync.Locker
+ if config.FileLocking {
+ fileLock, err := cstorage.GetLockfile(lockPath)
+ if err != nil {
+ return nil, fmt.Errorf("error obtaining lockfile: %v", err)
+ }
+ lock = fileLock
+ } else {
+ lock = new(sync.Mutex)
+ }
+
+ hooks := make(map[string]HookParams)
+ // If hooks directory is set in config use it
+ if config.HooksDirPath != "" {
+ if err := readHooks(config.HooksDirPath, hooks); err != nil {
+ return nil, err
+ }
+ // If user overrode default hooks, this means it is in a test, so don't
+ // use OverrideHooksDirPath
+ if config.HooksDirPath == DefaultHooksDirPath {
+ if err := readHooks(OverrideHooksDirPath, hooks); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return &ContainerServer{
+ runtime: runtime,
+ store: store,
+ storageImageServer: imageService,
+ storageRuntimeServer: storageRuntimeService,
+ ctrNameIndex: registrar.NewRegistrar(),
+ ctrIDIndex: truncindex.NewTruncIndex([]string{}),
+ podNameIndex: registrar.NewRegistrar(),
+ podIDIndex: truncindex.NewTruncIndex([]string{}),
+ imageContext: &types.SystemContext{SignaturePolicyPath: config.SignaturePolicyPath},
+ hooks: hooks,
+ stateLock: lock,
+ state: &containerServerState{
+ containers: oci.NewMemoryStore(),
+ infraContainers: oci.NewMemoryStore(),
+ sandboxes: make(map[string]*sandbox.Sandbox),
+ processLevels: make(map[string]int),
+ },
+ config: config,
+ }, nil
+}
+
+// Update makes changes to the server's state (lists of pods and containers) to
+// reflect the list of pods and containers that are stored on disk, possibly
+// having been modified by other parties
+func (c *ContainerServer) Update() error {
+ c.updateLock.Lock()
+ defer c.updateLock.Unlock()
+
+ containers, err := c.store.Containers()
+ if err != nil && !os.IsNotExist(errors.Cause(err)) {
+ logrus.Warnf("could not read containers and sandboxes: %v", err)
+ return err
+ }
+ newPods := map[string]*storage.RuntimeContainerMetadata{}
+ oldPods := map[string]string{}
+ removedPods := map[string]string{}
+ newPodContainers := map[string]*storage.RuntimeContainerMetadata{}
+ oldPodContainers := map[string]string{}
+ removedPodContainers := map[string]string{}
+ for _, container := range containers {
+ if c.HasSandbox(container.ID) {
+ // FIXME: do we need to reload/update any info about the sandbox?
+ oldPods[container.ID] = container.ID
+ oldPodContainers[container.ID] = container.ID
+ continue
+ }
+ if c.GetContainer(container.ID) != nil {
+ // FIXME: do we need to reload/update any info about the container?
+ oldPodContainers[container.ID] = container.ID
+ continue
+ }
+ // not previously known, so figure out what it is
+ metadata, err2 := c.storageRuntimeServer.GetContainerMetadata(container.ID)
+ if err2 != nil {
+ logrus.Errorf("error parsing metadata for %s: %v, ignoring", container.ID, err2)
+ continue
+ }
+ if metadata.Pod {
+ newPods[container.ID] = &metadata
+ } else {
+ newPodContainers[container.ID] = &metadata
+ }
+ }
+ c.ctrIDIndex.Iterate(func(id string) {
+ if _, ok := oldPodContainers[id]; !ok {
+ // this container's ID wasn't in the updated list -> removed
+ removedPodContainers[id] = id
+ } else {
+ ctr := c.GetContainer(id)
+ if ctr != nil {
+ // if the container exists, update its state
+ c.ContainerStateFromDisk(c.GetContainer(id))
+ }
+ }
+ })
+ for removedPodContainer := range removedPodContainers {
+ // forget this container
+ ctr := c.GetContainer(removedPodContainer)
+ if ctr == nil {
+ logrus.Warnf("bad state when getting container removed %+v", removedPodContainer)
+ continue
+ }
+ c.ReleaseContainerName(ctr.Name())
+ c.RemoveContainer(ctr)
+ if err = c.ctrIDIndex.Delete(ctr.ID()); err != nil {
+ return err
+ }
+ logrus.Debugf("forgetting removed pod container %s", ctr.ID())
+ }
+ c.PodIDIndex().Iterate(func(id string) {
+ if _, ok := oldPods[id]; !ok {
+ // this pod's ID wasn't in the updated list -> removed
+ removedPods[id] = id
+ }
+ })
+ for removedPod := range removedPods {
+ // forget this pod
+ sb := c.GetSandbox(removedPod)
+ if sb == nil {
+ logrus.Warnf("bad state when getting pod to remove %+v", removedPod)
+ continue
+ }
+ podInfraContainer := sb.InfraContainer()
+ c.ReleaseContainerName(podInfraContainer.Name())
+ c.RemoveContainer(podInfraContainer)
+ if err = c.ctrIDIndex.Delete(podInfraContainer.ID()); err != nil {
+ return err
+ }
+ sb.RemoveInfraContainer()
+ c.ReleasePodName(sb.Name())
+ c.RemoveSandbox(sb.ID())
+ if err = c.podIDIndex.Delete(sb.ID()); err != nil {
+ return err
+ }
+ logrus.Debugf("forgetting removed pod %s", sb.ID())
+ }
+ for sandboxID := range newPods {
+ // load this pod
+ if err = c.LoadSandbox(sandboxID); err != nil {
+ logrus.Warnf("could not load new pod sandbox %s: %v, ignoring", sandboxID, err)
+ } else {
+ logrus.Debugf("loaded new pod sandbox %s", sandboxID, err)
+ }
+ }
+ for containerID := range newPodContainers {
+ // load this container
+ if err = c.LoadContainer(containerID); err != nil {
+ logrus.Warnf("could not load new sandbox container %s: %v, ignoring", containerID, err)
+ } else {
+ logrus.Debugf("loaded new pod container %s", containerID, err)
+ }
+ }
+ return nil
+}
+
+// LoadSandbox loads a sandbox from the disk into the sandbox store
+func (c *ContainerServer) LoadSandbox(id string) error {
+ config, err := c.store.FromContainerDirectory(id, "config.json")
+ if err != nil {
+ return err
+ }
+ var m rspec.Spec
+ if err = json.Unmarshal(config, &m); err != nil {
+ return err
+ }
+ labels := make(map[string]string)
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Labels]), &labels); err != nil {
+ return err
+ }
+ name := m.Annotations[annotations.Name]
+ name, err = c.ReservePodName(id, name)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ c.ReleasePodName(name)
+ }
+ }()
+ var metadata pb.PodSandboxMetadata
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Metadata]), &metadata); err != nil {
+ return err
+ }
+
+ ip := m.Annotations[annotations.IP]
+
+ processLabel, mountLabel, err := label.InitLabels(label.DupSecOpt(m.Process.SelinuxLabel))
+ if err != nil {
+ return err
+ }
+
+ kubeAnnotations := make(map[string]string)
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Annotations]), &kubeAnnotations); err != nil {
+ return err
+ }
+
+ privileged := isTrue(m.Annotations[annotations.PrivilegedRuntime])
+ trusted := isTrue(m.Annotations[annotations.TrustedSandbox])
+
+ sb, err := sandbox.New(id, name, m.Annotations[annotations.KubeName], filepath.Dir(m.Annotations[annotations.LogPath]), "", labels, kubeAnnotations, processLabel, mountLabel, &metadata, m.Annotations[annotations.ShmPath], "", privileged, trusted, m.Annotations[annotations.ResolvPath], "", nil)
+ if err != nil {
+ return err
+ }
+ sb.AddHostnamePath(m.Annotations[annotations.HostnamePath])
+ sb.AddIP(ip)
+
+ // We add a netNS only if we can load a permanent one.
+ // Otherwise, the sandbox will live in the host namespace.
+ netNsPath, err := configNetNsPath(m)
+ if err == nil {
+ nsErr := sb.NetNsJoin(netNsPath, sb.Name())
+ // If we can't load the networking namespace
+ // because it's closed, we just set the sb netns
+ // pointer to nil. Otherwise we return an error.
+ if nsErr != nil && nsErr != sandbox.ErrClosedNetNS {
+ return nsErr
+ }
+ }
+
+ c.AddSandbox(sb)
+
+ defer func() {
+ if err != nil {
+ c.RemoveSandbox(sb.ID())
+ }
+ }()
+
+ sandboxPath, err := c.store.ContainerRunDirectory(id)
+ if err != nil {
+ return err
+ }
+
+ sandboxDir, err := c.store.ContainerDirectory(id)
+ if err != nil {
+ return err
+ }
+
+ cname, err := c.ReserveContainerName(m.Annotations[annotations.ContainerID], m.Annotations[annotations.ContainerName])
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ c.ReleaseContainerName(cname)
+ }
+ }()
+
+ created, err := time.Parse(time.RFC3339Nano, m.Annotations[annotations.Created])
+ if err != nil {
+ return err
+ }
+
+ scontainer, err := oci.NewContainer(m.Annotations[annotations.ContainerID], cname, sandboxPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, m.Annotations, kubeAnnotations, "", "", "", nil, id, false, false, false, privileged, trusted, sandboxDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
+ if err != nil {
+ return err
+ }
+ scontainer.SetSpec(&m)
+ scontainer.SetMountPoint(m.Annotations[annotations.MountPoint])
+
+ if m.Annotations[annotations.Volumes] != "" {
+ containerVolumes := []oci.ContainerVolume{}
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Volumes]), &containerVolumes); err != nil {
+ return fmt.Errorf("failed to unmarshal container volumes: %v", err)
+ }
+ if containerVolumes != nil {
+ for _, cv := range containerVolumes {
+ scontainer.AddVolume(cv)
+ }
+ }
+ }
+
+ c.ContainerStateFromDisk(scontainer)
+
+ if err = label.ReserveLabel(processLabel); err != nil {
+ return err
+ }
+ sb.SetInfraContainer(scontainer)
+ if err = c.ctrIDIndex.Add(scontainer.ID()); err != nil {
+ return err
+ }
+ if err = c.podIDIndex.Add(id); err != nil {
+ return err
+ }
+ return nil
+}
+
+func configNetNsPath(spec rspec.Spec) (string, error) {
+ for _, ns := range spec.Linux.Namespaces {
+ if ns.Type != rspec.NetworkNamespace {
+ continue
+ }
+
+ if ns.Path == "" {
+ return "", fmt.Errorf("empty networking namespace")
+ }
+
+ return ns.Path, nil
+ }
+
+ return "", fmt.Errorf("missing networking namespace")
+}
+
+// LoadContainer loads a container from the disk into the container store
+func (c *ContainerServer) LoadContainer(id string) error {
+ config, err := c.store.FromContainerDirectory(id, "config.json")
+ if err != nil {
+ return err
+ }
+ var m rspec.Spec
+ if err = json.Unmarshal(config, &m); err != nil {
+ return err
+ }
+ labels := make(map[string]string)
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Labels]), &labels); err != nil {
+ return err
+ }
+ name := m.Annotations[annotations.Name]
+ name, err = c.ReserveContainerName(id, name)
+ if err != nil {
+ return err
+ }
+
+ defer func() {
+ if err != nil {
+ c.ReleaseContainerName(name)
+ }
+ }()
+
+ var metadata pb.ContainerMetadata
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Metadata]), &metadata); err != nil {
+ return err
+ }
+ sb := c.GetSandbox(m.Annotations[annotations.SandboxID])
+ if sb == nil {
+ return fmt.Errorf("could not get sandbox with id %s, skipping", m.Annotations[annotations.SandboxID])
+ }
+
+ tty := isTrue(m.Annotations[annotations.TTY])
+ stdin := isTrue(m.Annotations[annotations.Stdin])
+ stdinOnce := isTrue(m.Annotations[annotations.StdinOnce])
+
+ containerPath, err := c.store.ContainerRunDirectory(id)
+ if err != nil {
+ return err
+ }
+
+ containerDir, err := c.store.ContainerDirectory(id)
+ if err != nil {
+ return err
+ }
+
+ img, ok := m.Annotations[annotations.Image]
+ if !ok {
+ img = ""
+ }
+
+ imgName, ok := m.Annotations[annotations.ImageName]
+ if !ok {
+ imgName = ""
+ }
+
+ imgRef, ok := m.Annotations[annotations.ImageRef]
+ if !ok {
+ imgRef = ""
+ }
+
+ kubeAnnotations := make(map[string]string)
+ if err = json.Unmarshal([]byte(m.Annotations[annotations.Annotations]), &kubeAnnotations); err != nil {
+ return err
+ }
+
+ created, err := time.Parse(time.RFC3339Nano, m.Annotations[annotations.Created])
+ if err != nil {
+ return err
+ }
+
+ ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, m.Annotations, kubeAnnotations, img, imgName, imgRef, &metadata, sb.ID(), tty, stdin, stdinOnce, sb.Privileged(), sb.Trusted(), containerDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
+ if err != nil {
+ return err
+ }
+ ctr.SetSpec(&m)
+ ctr.SetMountPoint(m.Annotations[annotations.MountPoint])
+
+ c.ContainerStateFromDisk(ctr)
+
+ c.AddContainer(ctr)
+ return c.ctrIDIndex.Add(id)
+}
+
+func isTrue(annotaton string) bool {
+ return annotaton == "true"
+}
+
+// ContainerStateFromDisk retrieves information on the state of a running container
+// from the disk
+func (c *ContainerServer) ContainerStateFromDisk(ctr *oci.Container) error {
+ if err := ctr.FromDisk(); err != nil {
+ return err
+ }
+ // ignore errors, this is a best effort to have up-to-date info about
+ // a given container before its state gets stored
+ c.runtime.UpdateStatus(ctr)
+
+ return nil
+}
+
+// ContainerStateToDisk writes the container's state information to a JSON file
+// on disk
+func (c *ContainerServer) ContainerStateToDisk(ctr *oci.Container) error {
+ // ignore errors, this is a best effort to have up-to-date info about
+ // a given container before its state gets stored
+ c.Runtime().UpdateStatus(ctr)
+
+ jsonSource, err := ioutils.NewAtomicFileWriter(ctr.StatePath(), 0644)
+ if err != nil {
+ return err
+ }
+ defer jsonSource.Close()
+ enc := json.NewEncoder(jsonSource)
+ return enc.Encode(c.runtime.ContainerStatus(ctr))
+}
+
+// ReserveContainerName holds a name for a container that is being created
+func (c *ContainerServer) ReserveContainerName(id, name string) (string, error) {
+ if err := c.ctrNameIndex.Reserve(name, id); err != nil {
+ if err == registrar.ErrNameReserved {
+ id, err := c.ctrNameIndex.Get(name)
+ if err != nil {
+ logrus.Warnf("conflict, ctr name %q already reserved", name)
+ return "", err
+ }
+ return "", fmt.Errorf("conflict, name %q already reserved for ctr %q", name, id)
+ }
+ return "", fmt.Errorf("error reserving ctr name %s", name)
+ }
+ return name, nil
+}
+
+// ReleaseContainerName releases a container name from the index so that it can
+// be used by other containers
+func (c *ContainerServer) ReleaseContainerName(name string) {
+ c.ctrNameIndex.Release(name)
+}
+
+// ReservePodName holds a name for a pod that is being created
+func (c *ContainerServer) ReservePodName(id, name string) (string, error) {
+ if err := c.podNameIndex.Reserve(name, id); err != nil {
+ if err == registrar.ErrNameReserved {
+ id, err := c.podNameIndex.Get(name)
+ if err != nil {
+ logrus.Warnf("conflict, pod name %q already reserved", name)
+ return "", err
+ }
+ return "", fmt.Errorf("conflict, name %q already reserved for pod %q", name, id)
+ }
+ return "", fmt.Errorf("error reserving pod name %q", name)
+ }
+ return name, nil
+}
+
+// ReleasePodName releases a pod name from the index so it can be used by other
+// pods
+func (c *ContainerServer) ReleasePodName(name string) {
+ c.podNameIndex.Release(name)
+}
+
+// Shutdown attempts to shut down the server's storage cleanly
+func (c *ContainerServer) Shutdown() error {
+ _, err := c.store.Shutdown(false)
+ if err != nil && errors.Cause(err) != cstorage.ErrLayerUsedByContainer {
+ return err
+ }
+ return nil
+}
+
+type containerServerState struct {
+ containers oci.ContainerStorer
+ infraContainers oci.ContainerStorer
+ sandboxes map[string]*sandbox.Sandbox
+ // processLevels The number of sandboxes using the same SELinux MCS level. Need to release MCS Level, when count reaches 0
+ processLevels map[string]int
+}
+
+// AddContainer adds a container to the container state store
+func (c *ContainerServer) AddContainer(ctr *oci.Container) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ sandbox := c.state.sandboxes[ctr.Sandbox()]
+ sandbox.AddContainer(ctr)
+ c.state.containers.Add(ctr.ID(), ctr)
+}
+
+// AddInfraContainer adds a container to the container state store
+func (c *ContainerServer) AddInfraContainer(ctr *oci.Container) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ c.state.infraContainers.Add(ctr.ID(), ctr)
+}
+
+// GetContainer returns a container by its ID
+func (c *ContainerServer) GetContainer(id string) *oci.Container {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ return c.state.containers.Get(id)
+}
+
+// GetInfraContainer returns a container by its ID
+func (c *ContainerServer) GetInfraContainer(id string) *oci.Container {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ return c.state.infraContainers.Get(id)
+}
+
+// HasContainer checks if a container exists in the state
+func (c *ContainerServer) HasContainer(id string) bool {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ ctr := c.state.containers.Get(id)
+ return ctr != nil
+}
+
+// RemoveContainer removes a container from the container state store
+func (c *ContainerServer) RemoveContainer(ctr *oci.Container) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ sbID := ctr.Sandbox()
+ sb := c.state.sandboxes[sbID]
+ sb.RemoveContainer(ctr)
+ c.state.containers.Delete(ctr.ID())
+}
+
+// RemoveInfraContainer removes a container from the container state store
+func (c *ContainerServer) RemoveInfraContainer(ctr *oci.Container) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ c.state.infraContainers.Delete(ctr.ID())
+}
+
+// listContainers returns a list of all containers stored by the server state
+func (c *ContainerServer) listContainers() []*oci.Container {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ return c.state.containers.List()
+}
+
+// ListContainers returns a list of all containers stored by the server state
+// that match the given filter function
+func (c *ContainerServer) ListContainers(filters ...func(*oci.Container) bool) ([]*oci.Container, error) {
+ containers := c.listContainers()
+ if len(filters) == 0 {
+ return containers, nil
+ }
+ filteredContainers := make([]*oci.Container, 0, len(containers))
+ for _, container := range containers {
+ for _, filter := range filters {
+ if filter(container) {
+ filteredContainers = append(filteredContainers, container)
+ }
+ }
+ }
+ return filteredContainers, nil
+}
+
+// AddSandbox adds a sandbox to the sandbox state store
+func (c *ContainerServer) AddSandbox(sb *sandbox.Sandbox) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ c.state.sandboxes[sb.ID()] = sb
+ c.state.processLevels[selinux.NewContext(sb.ProcessLabel())["level"]]++
+}
+
+// GetSandbox returns a sandbox by its ID
+func (c *ContainerServer) GetSandbox(id string) *sandbox.Sandbox {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ return c.state.sandboxes[id]
+}
+
+// GetSandboxContainer returns a sandbox's infra container
+func (c *ContainerServer) GetSandboxContainer(id string) *oci.Container {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ sb, ok := c.state.sandboxes[id]
+ if !ok {
+ return nil
+ }
+ return sb.InfraContainer()
+}
+
+// HasSandbox checks if a sandbox exists in the state
+func (c *ContainerServer) HasSandbox(id string) bool {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ _, ok := c.state.sandboxes[id]
+ return ok
+}
+
+// RemoveSandbox removes a sandbox from the state store
+func (c *ContainerServer) RemoveSandbox(id string) {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ processLabel := c.state.sandboxes[id].ProcessLabel()
+ delete(c.state.sandboxes, id)
+ level := selinux.NewContext(processLabel)["level"]
+ c.state.processLevels[level]--
+ if c.state.processLevels[level] == 0 {
+ label.ReleaseLabel(processLabel)
+ delete(c.state.processLevels, level)
+ }
+}
+
+// ListSandboxes lists all sandboxes in the state store
+func (c *ContainerServer) ListSandboxes() []*sandbox.Sandbox {
+ c.stateLock.Lock()
+ defer c.stateLock.Unlock()
+ sbArray := make([]*sandbox.Sandbox, 0, len(c.state.sandboxes))
+ for _, sb := range c.state.sandboxes {
+ sbArray = append(sbArray, sb)
+ }
+
+ return sbArray
+}
+
+// LibcontainerStats gets the stats for the container with the given id from runc/libcontainer
+func (c *ContainerServer) LibcontainerStats(ctr *oci.Container) (*libcontainer.Stats, error) {
+ // TODO: make this not hardcoded
+ // was: c.runtime.Path(ociContainer) but that returns /usr/bin/runc - how do we get /run/runc?
+ // runroot is /var/run/runc
+ // Hardcoding probably breaks ClearContainers compatibility
+ factory, err := loadFactory("/run/runc")
+ if err != nil {
+ return nil, err
+ }
+ container, err := factory.Load(ctr.ID())
+ if err != nil {
+ return nil, err
+ }
+ return container.Stats()
+}