diff options
-rw-r--r-- | cmd/kpod/ps.go | 11 | ||||
-rw-r--r-- | libkpod/config.go | 5 | ||||
-rw-r--r-- | libkpod/container.go | 157 | ||||
-rw-r--r-- | libkpod/container_server.go | 775 | ||||
-rw-r--r-- | libkpod/kill.go | 45 | ||||
-rw-r--r-- | libkpod/rename.go | 114 | ||||
-rw-r--r-- | libkpod/sandbox/sandbox.go | 484 | ||||
-rw-r--r-- | libkpod/stats.go | 111 | ||||
-rw-r--r-- | libkpod/stop.go | 36 | ||||
-rw-r--r-- | libkpod/wait.go | 42 | ||||
-rw-r--r-- | oci/container.go | 260 | ||||
-rw-r--r-- | oci/finished.go | 14 | ||||
-rw-r--r-- | oci/finished_32.go | 14 | ||||
-rw-r--r-- | oci/history.go | 31 | ||||
-rw-r--r-- | oci/memory_store.go | 92 | ||||
-rw-r--r-- | oci/oci.go | 748 | ||||
-rw-r--r-- | oci/store.go | 27 |
17 files changed, 7 insertions, 2959 deletions
diff --git a/cmd/kpod/ps.go b/cmd/kpod/ps.go index 36ce18515..4148edf68 100644 --- a/cmd/kpod/ps.go +++ b/cmd/kpod/ps.go @@ -15,7 +15,6 @@ import ( "github.com/pkg/errors" "github.com/projectatomic/libpod/cmd/kpod/formats" "github.com/projectatomic/libpod/libpod" - "github.com/projectatomic/libpod/oci" "github.com/urfave/cli" "k8s.io/apimachinery/pkg/fields" ) @@ -491,7 +490,7 @@ func getJSONOutput(containers []*libpod.Container, nSpace bool) ([]psJSONParams, Names: cc.Name, Labels: cc.Labels, Mounts: cc.Spec.Mounts, - ContainerRunning: conState.String() == oci.ContainerStateRunning, + ContainerRunning: conState == libpod.ContainerStateRunning, Namespaces: ns, } psOutput = append(psOutput, params) @@ -605,7 +604,7 @@ type FilterParamsPS struct { } // parseFilter takes a filter string and a list of containers and filters it -func parseFilter(filter string, containers []*oci.Container) (*FilterParamsPS, error) { +func parseFilter(filter string, containers []*libpod.Container) (*FilterParamsPS, error) { params := new(FilterParamsPS) allFilters := strings.Split(filter, ",") @@ -630,13 +629,13 @@ func parseFilter(filter string, containers []*oci.Container) (*FilterParamsPS, e params.ancestor = pair[1] case "before": if ctr, err := findContainer(containers, pair[1]); err == nil { - params.before = ctr.CreatedAt() + params.before = ctr.Config().CreatedTime } else { return nil, errors.Wrapf(err, "no such container %q", pair[1]) } case "since": if ctr, err := findContainer(containers, pair[1]); err == nil { - params.before = ctr.CreatedAt() + params.before = ctr.Config().CreatedTime } else { return nil, errors.Wrapf(err, "no such container %q", pair[1]) } @@ -650,7 +649,7 @@ func parseFilter(filter string, containers []*oci.Container) (*FilterParamsPS, e } // findContainer finds a container with a specific name or id from a list of containers -func findContainer(containers []*oci.Container, ref string) (*oci.Container, error) { +func findContainer(containers []*libpod.Container, ref string) (*libpod.Container, error) { for _, ctr := range containers { if strings.HasPrefix(ctr.ID(), ref) || ctr.Name() == ref { return ctr, nil diff --git a/libkpod/config.go b/libkpod/config.go index 84d3ce897..b95ff18ca 100644 --- a/libkpod/config.go +++ b/libkpod/config.go @@ -6,7 +6,6 @@ import ( "github.com/BurntSushi/toml" "github.com/opencontainers/selinux/go-selinux" - "github.com/projectatomic/libpod/oci" ) // Default paths if none are specified @@ -21,9 +20,9 @@ const ( apparmorProfileName = "crio-default" cniConfigDir = "/etc/cni/net.d/" cniBinDir = "/opt/cni/bin/" - cgroupManager = oci.CgroupfsCgroupsManager + cgroupManager = "" //oci.CgroupfsCgroupsManager lockPath = "/run/crio.lock" - containerExitsDir = oci.ContainerExitsDir + containerExitsDir = "" //oci.ContainerExitsDir ) // Config represents the entire set of configuration values that can be set for diff --git a/libkpod/container.go b/libkpod/container.go deleted file mode 100644 index e0ff348d0..000000000 --- a/libkpod/container.go +++ /dev/null @@ -1,157 +0,0 @@ -package libkpod - -import ( - "fmt" - - cstorage "github.com/containers/storage" - "github.com/pkg/errors" - "github.com/projectatomic/libpod/libkpod/sandbox" - "github.com/projectatomic/libpod/oci" - "github.com/projectatomic/libpod/pkg/registrar" -) - -// GetStorageContainer searches for a container with the given name or ID in the given store -func (c *ContainerServer) GetStorageContainer(container string) (*cstorage.Container, error) { - ociCtr, err := c.LookupContainer(container) - if err != nil { - return nil, err - } - return c.store.Container(ociCtr.ID()) -} - -// GetContainerTopLayerID gets the ID of the top layer of the given container -func (c *ContainerServer) GetContainerTopLayerID(containerID string) (string, error) { - ctr, err := c.GetStorageContainer(containerID) - if err != nil { - return "", err - } - return ctr.LayerID, nil -} - -// GetContainerRwSize Gets the size of the mutable top layer of the container -func (c *ContainerServer) GetContainerRwSize(containerID string) (int64, error) { - container, err := c.store.Container(containerID) - if err != nil { - return 0, err - } - - // Get the size of the top layer by calculating the size of the diff - // between the layer and its parent. The top layer of a container is - // the only RW layer, all others are immutable - layer, err := c.store.Layer(container.LayerID) - if err != nil { - return 0, err - } - return c.store.DiffSize(layer.Parent, layer.ID) -} - -// GetContainerRootFsSize gets the size of the container's root filesystem -// A container FS is split into two parts. The first is the top layer, a -// mutable layer, and the rest is the RootFS: the set of immutable layers -// that make up the image on which the container is based -func (c *ContainerServer) GetContainerRootFsSize(containerID string) (int64, error) { - container, err := c.store.Container(containerID) - if err != nil { - return 0, err - } - - // Ignore the size of the top layer. The top layer is a mutable RW layer - // and is not considered a part of the rootfs - rwLayer, err := c.store.Layer(container.LayerID) - if err != nil { - return 0, err - } - layer, err := c.store.Layer(rwLayer.Parent) - if err != nil { - return 0, err - } - - size := int64(0) - for layer.Parent != "" { - layerSize, err := c.store.DiffSize(layer.Parent, layer.ID) - if err != nil { - return size, errors.Wrapf(err, "getting diffsize of layer %q and its parent %q", layer.ID, layer.Parent) - } - size += layerSize - layer, err = c.store.Layer(layer.Parent) - if err != nil { - return 0, err - } - } - // Get the size of the last layer. Has to be outside of the loop - // because the parent of the last layer is "", andlstore.Get("") - // will return an error - layerSize, err := c.store.DiffSize(layer.Parent, layer.ID) - return size + layerSize, err -} - -// GetContainerFromRequest gets an oci container matching the specified full or partial id -func (c *ContainerServer) GetContainerFromRequest(cid string) (*oci.Container, error) { - if cid == "" { - return nil, fmt.Errorf("container ID should not be empty") - } - - containerID, err := c.ctrIDIndex.Get(cid) - if err != nil { - return nil, fmt.Errorf("container with ID starting with %s not found: %v", cid, err) - } - - ctr := c.GetContainer(containerID) - if ctr == nil { - return nil, fmt.Errorf("specified container not found: %s", containerID) - } - return ctr, nil -} - -func (c *ContainerServer) getSandboxFromRequest(pid string) (*sandbox.Sandbox, error) { - if pid == "" { - return nil, fmt.Errorf("pod ID should not be empty") - } - - podID, err := c.podIDIndex.Get(pid) - if err != nil { - return nil, fmt.Errorf("pod with ID starting with %s not found: %v", pid, err) - } - - sb := c.GetSandbox(podID) - if sb == nil { - return nil, fmt.Errorf("specified pod not found: %s", podID) - } - return sb, nil -} - -// LookupContainer returns the container with the given name or full or partial id -func (c *ContainerServer) LookupContainer(idOrName string) (*oci.Container, error) { - if idOrName == "" { - return nil, fmt.Errorf("container ID or name should not be empty") - } - - ctrID, err := c.ctrNameIndex.Get(idOrName) - if err != nil { - if err == registrar.ErrNameNotReserved { - ctrID = idOrName - } else { - return nil, err - } - } - - return c.GetContainerFromRequest(ctrID) -} - -// LookupSandbox returns the pod sandbox with the given name or full or partial id -func (c *ContainerServer) LookupSandbox(idOrName string) (*sandbox.Sandbox, error) { - if idOrName == "" { - return nil, fmt.Errorf("container ID or name should not be empty") - } - - podID, err := c.podNameIndex.Get(idOrName) - if err != nil { - if err == registrar.ErrNameNotReserved { - podID = idOrName - } else { - return nil, err - } - } - - return c.getSandboxFromRequest(podID) -} diff --git a/libkpod/container_server.go b/libkpod/container_server.go deleted file mode 100644 index 8e0e09978..000000000 --- a/libkpod/container_server.go +++ /dev/null @@ -1,775 +0,0 @@ -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/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/projectatomic/libpod/libkpod/sandbox" - "github.com/projectatomic/libpod/oci" - "github.com/projectatomic/libpod/pkg/annotations" - "github.com/projectatomic/libpod/pkg/registrar" - "github.com/projectatomic/libpod/pkg/storage" - "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() -} diff --git a/libkpod/kill.go b/libkpod/kill.go deleted file mode 100644 index 74c525818..000000000 --- a/libkpod/kill.go +++ /dev/null @@ -1,45 +0,0 @@ -package libkpod - -import ( - "github.com/docker/docker/pkg/signal" - "github.com/pkg/errors" - "github.com/projectatomic/libpod/oci" - "github.com/projectatomic/libpod/utils" - "os" - "syscall" -) - -// Reverse lookup signal string from its map -func findStringInSignalMap(killSignal syscall.Signal) (string, error) { - for k, v := range signal.SignalMap { - if v == killSignal { - return k, nil - } - } - return "", errors.Errorf("unable to convert signal to string") - -} - -// ContainerKill sends the user provided signal to the containers primary process. -func (c *ContainerServer) ContainerKill(container string, killSignal syscall.Signal) (string, error) { // nolint - ctr, err := c.LookupContainer(container) - if err != nil { - return "", errors.Wrapf(err, "failed to find container %s", container) - } - c.runtime.UpdateStatus(ctr) - cStatus := c.runtime.ContainerStatus(ctr) - - // If the container is not running, error and move on. - if cStatus.Status != oci.ContainerStateRunning { - return "", errors.Errorf("cannot kill container %s: it is not running", container) - } - signalString, err := findStringInSignalMap(killSignal) - if err != nil { - return "", err - } - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, c.runtime.Path(ctr), "kill", ctr.ID(), signalString); err != nil { - return "", err - } - c.ContainerStateToDisk(ctr) - return ctr.ID(), nil -} diff --git a/libkpod/rename.go b/libkpod/rename.go deleted file mode 100644 index cdb2bfc04..000000000 --- a/libkpod/rename.go +++ /dev/null @@ -1,114 +0,0 @@ -package libkpod - -import ( - "encoding/json" - "path/filepath" - - "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" - - "github.com/docker/docker/pkg/ioutils" - "github.com/opencontainers/runtime-tools/generate" - "github.com/projectatomic/libpod/oci" - "github.com/projectatomic/libpod/pkg/annotations" -) - -const configFile = "config.json" - -// ContainerRename renames the given container -func (c *ContainerServer) ContainerRename(container, name string) error { - ctr, err := c.LookupContainer(container) - if err != nil { - return err - } - - oldName := ctr.Name() - _, err = c.ReserveContainerName(ctr.ID(), name) - if err != nil { - return err - } - defer func() { - if err != nil { - c.ReleaseContainerName(name) - } else { - c.ReleaseContainerName(oldName) - } - }() - - // Update state.json - if err = c.updateStateName(ctr, name); err != nil { - return err - } - - // Update config.json - configRuntimePath := filepath.Join(ctr.BundlePath(), configFile) - if err = updateConfigName(configRuntimePath, name); err != nil { - return err - } - configStoragePath := filepath.Join(ctr.Dir(), configFile) - if err = updateConfigName(configStoragePath, name); err != nil { - return err - } - - // Update containers.json - if err = c.store.SetNames(ctr.ID(), []string{name}); err != nil { - return err - } - return nil -} - -func updateConfigName(configPath, name string) error { - specgen, err := generate.NewFromFile(configPath) - if err != nil { - return err - } - specgen.AddAnnotation(annotations.Name, name) - specgen.AddAnnotation(annotations.Metadata, updateMetadata(specgen.Spec().Annotations, name)) - - return specgen.SaveToFile(configPath, generate.ExportOptions{}) -} - -func (c *ContainerServer) updateStateName(ctr *oci.Container, name string) error { - ctr.State().Annotations[annotations.Name] = name - ctr.State().Annotations[annotations.Metadata] = updateMetadata(ctr.State().Annotations, name) - // This is taken directly from c.ContainerStateToDisk(), which can't be used because of the call to UpdateStatus() in the first line - 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)) -} - -// Attempts to update a metadata annotation -func updateMetadata(specAnnotations map[string]string, name string) string { - oldMetadata := specAnnotations[annotations.Metadata] - containerType := specAnnotations[annotations.ContainerType] - if containerType == "container" { - metadata := runtime.ContainerMetadata{} - err := json.Unmarshal([]byte(oldMetadata), metadata) - if err != nil { - return oldMetadata - } - metadata.Name = name - m, err := json.Marshal(metadata) - if err != nil { - return oldMetadata - } - return string(m) - } else if containerType == "sandbox" { - metadata := runtime.PodSandboxMetadata{} - err := json.Unmarshal([]byte(oldMetadata), metadata) - if err != nil { - return oldMetadata - } - metadata.Name = name - m, err := json.Marshal(metadata) - if err != nil { - return oldMetadata - } - return string(m) - } else { - return specAnnotations[annotations.Metadata] - } -} diff --git a/libkpod/sandbox/sandbox.go b/libkpod/sandbox/sandbox.go deleted file mode 100644 index a61c5a4f7..000000000 --- a/libkpod/sandbox/sandbox.go +++ /dev/null @@ -1,484 +0,0 @@ -package sandbox - -import ( - "crypto/rand" - "errors" - "fmt" - "os" - "path/filepath" - "sync" - - "github.com/containernetworking/plugins/pkg/ns" - "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/pkg/symlink" - "github.com/projectatomic/libpod/oci" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" - "k8s.io/apimachinery/pkg/fields" - pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" - "k8s.io/kubernetes/pkg/kubelet/network/hostport" -) - -// NetNs handles data pertaining a network namespace -type NetNs struct { - sync.Mutex - ns ns.NetNS - symlink *os.File - closed bool - restored bool -} - -func (ns *NetNs) symlinkCreate(name string) error { - b := make([]byte, 4) - _, randErr := rand.Reader.Read(b) - if randErr != nil { - return randErr - } - - nsName := fmt.Sprintf("%s-%x", name, b) - symlinkPath := filepath.Join(NsRunDir, nsName) - - if err := os.Symlink(ns.ns.Path(), symlinkPath); err != nil { - return err - } - - fd, err := os.Open(symlinkPath) - if err != nil { - if removeErr := os.RemoveAll(symlinkPath); removeErr != nil { - return removeErr - } - - return err - } - - ns.symlink = fd - - return nil -} - -func (ns *NetNs) symlinkRemove() error { - if err := ns.symlink.Close(); err != nil { - return err - } - - return os.RemoveAll(ns.symlink.Name()) -} - -func isSymbolicLink(path string) (bool, error) { - fi, err := os.Lstat(path) - if err != nil { - return false, err - } - - return fi.Mode()&os.ModeSymlink == os.ModeSymlink, nil -} - -// NetNsGet returns the NetNs associated with the given nspath and name -func NetNsGet(nspath, name string) (*NetNs, error) { - if err := ns.IsNSorErr(nspath); err != nil { - return nil, ErrClosedNetNS - } - - symlink, symlinkErr := isSymbolicLink(nspath) - if symlinkErr != nil { - return nil, symlinkErr - } - - var resolvedNsPath string - if symlink { - path, err := os.Readlink(nspath) - if err != nil { - return nil, err - } - resolvedNsPath = path - } else { - resolvedNsPath = nspath - } - - netNS, err := ns.GetNS(resolvedNsPath) - if err != nil { - return nil, err - } - - netNs := &NetNs{ns: netNS, closed: false, restored: true} - - if symlink { - fd, err := os.Open(nspath) - if err != nil { - return nil, err - } - - netNs.symlink = fd - } else { - if err := netNs.symlinkCreate(name); err != nil { - return nil, err - } - } - - return netNs, nil -} - -// HostNetNsPath returns the current network namespace for the host -func HostNetNsPath() (string, error) { - netNS, err := ns.GetCurrentNS() - if err != nil { - return "", err - } - - defer netNS.Close() - return netNS.Path(), nil -} - -// Sandbox contains data surrounding kubernetes sandboxes on the server -type Sandbox struct { - id string - namespace string - // OCI pod name (eg "<namespace>-<name>-<attempt>") - name string - // Kubernetes pod name (eg, "<name>") - kubeName string - logDir string - labels fields.Set - annotations map[string]string - infraContainer *oci.Container - containers oci.ContainerStorer - processLabel string - mountLabel string - netns *NetNs - metadata *pb.PodSandboxMetadata - shmPath string - cgroupParent string - privileged bool - trusted bool - resolvPath string - hostnamePath string - hostname string - portMappings []*hostport.PortMapping - stopped bool - // ipv4 or ipv6 cache - ip string -} - -const ( - // DefaultShmSize is the default shm size - DefaultShmSize = 64 * 1024 * 1024 - // NsRunDir is the default directory in which running network namespaces - // are stored - NsRunDir = "/var/run/netns" - // PodInfraCommand is the default command when starting a pod infrastructure - // container - PodInfraCommand = "/pause" -) - -var ( - // ErrIDEmpty is the erro returned when the id of the sandbox is empty - ErrIDEmpty = errors.New("PodSandboxId should not be empty") - // ErrClosedNetNS is the error returned when the network namespace of the - // sandbox is closed - ErrClosedNetNS = errors.New("PodSandbox networking namespace is closed") -) - -// New creates and populates a new pod sandbox -// New sandboxes have no containers, no infra container, and no network namespaces associated with them -// An infra container must be attached before the sandbox is added to the state -func New(id, namespace, name, kubeName, logDir string, labels, annotations map[string]string, processLabel, mountLabel string, metadata *pb.PodSandboxMetadata, shmPath, cgroupParent string, privileged, trusted bool, resolvPath, hostname string, portMappings []*hostport.PortMapping) (*Sandbox, error) { - sb := new(Sandbox) - sb.id = id - sb.namespace = namespace - sb.name = name - sb.kubeName = kubeName - sb.logDir = logDir - sb.labels = labels - sb.annotations = annotations - sb.containers = oci.NewMemoryStore() - sb.processLabel = processLabel - sb.mountLabel = mountLabel - sb.metadata = metadata - sb.shmPath = shmPath - sb.cgroupParent = cgroupParent - sb.privileged = privileged - sb.trusted = trusted - sb.resolvPath = resolvPath - sb.hostname = hostname - sb.portMappings = portMappings - - return sb, nil -} - -// AddIP stores the ip in the sandbox -func (s *Sandbox) AddIP(ip string) { - s.ip = ip -} - -// IP returns the ip of the sandbox -func (s *Sandbox) IP() string { - return s.ip -} - -// ID returns the id of the sandbox -func (s *Sandbox) ID() string { - return s.id -} - -// Namespace returns the namespace for the sandbox -func (s *Sandbox) Namespace() string { - return s.namespace -} - -// Name returns the name of the sandbox -func (s *Sandbox) Name() string { - return s.name -} - -// KubeName returns the kubernetes name for the sandbox -func (s *Sandbox) KubeName() string { - return s.kubeName -} - -// LogDir returns the location of the logging directory for the sandbox -func (s *Sandbox) LogDir() string { - return s.logDir -} - -// Labels returns the labels associated with the sandbox -func (s *Sandbox) Labels() fields.Set { - return s.labels -} - -// Annotations returns a list of annotations for the sandbox -func (s *Sandbox) Annotations() map[string]string { - return s.annotations -} - -// InfraContainer returns the infrastructure container for the sandbox -func (s *Sandbox) InfraContainer() *oci.Container { - return s.infraContainer -} - -// Containers returns the ContainerStorer that contains information on all -// of the containers in the sandbox -func (s *Sandbox) Containers() oci.ContainerStorer { - return s.containers -} - -// ProcessLabel returns the process label for the sandbox -func (s *Sandbox) ProcessLabel() string { - return s.processLabel -} - -// MountLabel returns the mount label for the sandbox -func (s *Sandbox) MountLabel() string { - return s.mountLabel -} - -// Metadata returns a set of metadata about the sandbox -func (s *Sandbox) Metadata() *pb.PodSandboxMetadata { - return s.metadata -} - -// ShmPath returns the shm path of the sandbox -func (s *Sandbox) ShmPath() string { - return s.shmPath -} - -// CgroupParent returns the cgroup parent of the sandbox -func (s *Sandbox) CgroupParent() string { - return s.cgroupParent -} - -// Privileged returns whether or not the containers in the sandbox are -// privileged containers -func (s *Sandbox) Privileged() bool { - return s.privileged -} - -// Trusted returns whether or not the containers in the sandbox are trusted -func (s *Sandbox) Trusted() bool { - return s.trusted -} - -// ResolvPath returns the resolv path for the sandbox -func (s *Sandbox) ResolvPath() string { - return s.resolvPath -} - -// AddHostnamePath adds the hostname path to the sandbox -func (s *Sandbox) AddHostnamePath(hostname string) { - s.hostnamePath = hostname -} - -// HostnamePath retrieves the hostname path from a sandbox -func (s *Sandbox) HostnamePath() string { - return s.hostnamePath -} - -// Hostname returns the hsotname of the sandbox -func (s *Sandbox) Hostname() string { - return s.hostname -} - -// PortMappings returns a list of port mappings between the host and the sandbox -func (s *Sandbox) PortMappings() []*hostport.PortMapping { - return s.portMappings -} - -// AddContainer adds a container to the sandbox -func (s *Sandbox) AddContainer(c *oci.Container) { - s.containers.Add(c.Name(), c) -} - -// GetContainer retrieves a container from the sandbox -func (s *Sandbox) GetContainer(name string) *oci.Container { - return s.containers.Get(name) -} - -// RemoveContainer deletes a container from the sandbox -func (s *Sandbox) RemoveContainer(c *oci.Container) { - s.containers.Delete(c.Name()) -} - -// SetInfraContainer sets the infrastructure container of a sandbox -// Attempts to set the infrastructure container after one is already present will throw an error -func (s *Sandbox) SetInfraContainer(infraCtr *oci.Container) error { - if s.infraContainer != nil { - return fmt.Errorf("sandbox already has an infra container") - } else if infraCtr == nil { - return fmt.Errorf("must provide non-nil infra container") - } - - s.infraContainer = infraCtr - - return nil -} - -// RemoveInfraContainer removes the infrastructure container of a sandbox -func (s *Sandbox) RemoveInfraContainer() { - s.infraContainer = nil -} - -// NetNs retrieves the network namespace of the sandbox -// If the sandbox uses the host namespace, nil is returned -func (s *Sandbox) NetNs() ns.NetNS { - if s.netns == nil { - return nil - } - - return s.netns.ns -} - -// NetNsPath returns the path to the network namespace of the sandbox. -// If the sandbox uses the host namespace, nil is returned -func (s *Sandbox) NetNsPath() string { - if s.netns == nil { - return "" - } - - return s.netns.symlink.Name() -} - -// NetNsCreate creates a new network namespace for the sandbox -func (s *Sandbox) NetNsCreate() error { - if s.netns != nil { - return fmt.Errorf("net NS already created") - } - - netNS, err := ns.NewNS() - if err != nil { - return err - } - - s.netns = &NetNs{ - ns: netNS, - closed: false, - } - - if err := s.netns.symlinkCreate(s.name); err != nil { - logrus.Warnf("Could not create nentns symlink %v", err) - - if err1 := s.netns.ns.Close(); err1 != nil { - return err1 - } - - return err - } - - return nil -} - -// SetStopped sets the sandbox state to stopped. -// This should be set after a stop operation succeeds -// so that subsequent stops can return fast. -func (s *Sandbox) SetStopped() { - s.stopped = true -} - -// Stopped returns whether the sandbox state has been -// set to stopped. -func (s *Sandbox) Stopped() bool { - return s.stopped -} - -// NetNsJoin attempts to join the sandbox to an existing network namespace -// This will fail if the sandbox is already part of a network namespace -func (s *Sandbox) NetNsJoin(nspath, name string) error { - if s.netns != nil { - return fmt.Errorf("sandbox already has a network namespace, cannot join another") - } - - netNS, err := NetNsGet(nspath, name) - if err != nil { - return err - } - - s.netns = netNS - - return nil -} - -// NetNsRemove removes the network namespace associated with the sandbox -func (s *Sandbox) NetNsRemove() error { - if s.netns == nil { - logrus.Warn("no networking namespace") - return nil - } - - s.netns.Lock() - defer s.netns.Unlock() - - if s.netns.closed { - // netNsRemove() can be called multiple - // times without returning an error. - return nil - } - - if err := s.netns.symlinkRemove(); err != nil { - return err - } - - if err := s.netns.ns.Close(); err != nil { - return err - } - - if s.netns.restored { - // we got namespaces in the form of - // /var/run/netns/cni-0d08effa-06eb-a963-f51a-e2b0eceffc5d - // but /var/run on most system is symlinked to /run so we first resolve - // the symlink and then try and see if it's mounted - fp, err := symlink.FollowSymlinkInScope(s.netns.ns.Path(), "/") - if err != nil { - return err - } - if mounted, err := mount.Mounted(fp); err == nil && mounted { - if err := unix.Unmount(fp, unix.MNT_DETACH); err != nil { - return err - } - } - - if err := os.RemoveAll(s.netns.ns.Path()); err != nil { - return err - } - } - - s.netns.closed = true - return nil -} diff --git a/libkpod/stats.go b/libkpod/stats.go deleted file mode 100644 index 8e74577d4..000000000 --- a/libkpod/stats.go +++ /dev/null @@ -1,111 +0,0 @@ -package libkpod - -import ( - "path/filepath" - "syscall" - "time" - - "strings" - - "github.com/opencontainers/runc/libcontainer" - "github.com/projectatomic/libpod/oci" -) - -// ContainerStats contains the statistics information for a running container -type ContainerStats struct { - Container string - CPU float64 - cpuNano uint64 - systemNano uint64 - MemUsage uint64 - MemLimit uint64 - MemPerc float64 - NetInput uint64 - NetOutput uint64 - BlockInput uint64 - BlockOutput uint64 - PIDs uint64 -} - -// GetContainerStats gets the running stats for a given container -func (c *ContainerServer) GetContainerStats(ctr *oci.Container, previousStats *ContainerStats) (*ContainerStats, error) { - previousCPU := previousStats.cpuNano - previousSystem := previousStats.systemNano - libcontainerStats, err := c.LibcontainerStats(ctr) - if err != nil { - return nil, err - } - cgroupStats := libcontainerStats.CgroupStats - stats := new(ContainerStats) - stats.Container = ctr.ID() - stats.CPU = calculateCPUPercent(libcontainerStats, previousCPU, previousSystem) - stats.MemUsage = cgroupStats.MemoryStats.Usage.Usage - stats.MemLimit = getMemLimit(cgroupStats.MemoryStats.Usage.Limit) - stats.MemPerc = float64(stats.MemUsage) / float64(stats.MemLimit) - stats.PIDs = cgroupStats.PidsStats.Current - stats.BlockInput, stats.BlockOutput = calculateBlockIO(libcontainerStats) - stats.NetInput, stats.NetOutput = getContainerNetIO(libcontainerStats) - - return stats, nil -} - -func loadFactory(root string) (libcontainer.Factory, error) { - abs, err := filepath.Abs(root) - if err != nil { - return nil, err - } - cgroupManager := libcontainer.Cgroupfs - return libcontainer.New(abs, cgroupManager, libcontainer.CriuPath("")) -} - -// getMemory limit returns the memory limit for a given cgroup -// If the configured memory limit is larger than the total memory on the sys, the -// physical system memory size is returned -func getMemLimit(cgroupLimit uint64) uint64 { - si := &syscall.Sysinfo_t{} - err := syscall.Sysinfo(si) - if err != nil { - return cgroupLimit - } - - physicalLimit := uint64(si.Totalram) - if cgroupLimit > physicalLimit { - return physicalLimit - } - return cgroupLimit -} - -// Returns the total number of bytes transmitted and received for the given container stats -func getContainerNetIO(stats *libcontainer.Stats) (received uint64, transmitted uint64) { - for _, iface := range stats.Interfaces { - received += iface.RxBytes - transmitted += iface.TxBytes - } - return -} - -func calculateCPUPercent(stats *libcontainer.Stats, previousCPU, previousSystem uint64) float64 { - var ( - cpuPercent = 0.0 - cpuDelta = float64(stats.CgroupStats.CpuStats.CpuUsage.TotalUsage - previousCPU) - systemDelta = float64(uint64(time.Now().UnixNano()) - previousSystem) - ) - if systemDelta > 0.0 && cpuDelta > 0.0 { - // gets a ratio of container cpu usage total, multiplies it by the number of cores (4 cores running - // at 100% utilization should be 400% utilization), and multiplies that by 100 to get a percentage - cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage)) * 100 - } - return cpuPercent -} - -func calculateBlockIO(stats *libcontainer.Stats) (read uint64, write uint64) { - for _, blkIOEntry := range stats.CgroupStats.BlkioStats.IoServiceBytesRecursive { - switch strings.ToLower(blkIOEntry.Op) { - case "read": - read += blkIOEntry.Value - case "write": - write += blkIOEntry.Value - } - } - return -} diff --git a/libkpod/stop.go b/libkpod/stop.go deleted file mode 100644 index 4ce7db740..000000000 --- a/libkpod/stop.go +++ /dev/null @@ -1,36 +0,0 @@ -package libkpod - -import ( - "github.com/pkg/errors" - "github.com/projectatomic/libpod/oci" - "golang.org/x/net/context" -) - -// ContainerStop stops a running container with a grace period (i.e., timeout). -func (c *ContainerServer) ContainerStop(ctx context.Context, container string, timeout int64) (string, error) { - ctr, err := c.LookupContainer(container) - if err != nil { - return "", errors.Wrapf(err, "failed to find container %s", container) - } - ctrID := ctr.ID() - - cStatus := c.runtime.ContainerStatus(ctr) - switch cStatus.Status { - - case oci.ContainerStatePaused: - return "", errors.Errorf("cannot stop paused container %s", ctrID) - default: - if cStatus.Status != oci.ContainerStateStopped { - if err := c.runtime.StopContainer(ctx, ctr, timeout); err != nil { - return "", errors.Wrapf(err, "failed to stop container %s", ctrID) - } - if err := c.storageRuntimeServer.StopContainer(ctrID); err != nil { - return "", errors.Wrapf(err, "failed to unmount container %s", ctrID) - } - } - } - - c.ContainerStateToDisk(ctr) - - return ctrID, nil -} diff --git a/libkpod/wait.go b/libkpod/wait.go deleted file mode 100644 index e972a0ba0..000000000 --- a/libkpod/wait.go +++ /dev/null @@ -1,42 +0,0 @@ -package libkpod - -import ( - "github.com/pkg/errors" - "github.com/projectatomic/libpod/oci" - "k8s.io/apimachinery/pkg/util/wait" -) - -func isStopped(c *ContainerServer, ctr *oci.Container) bool { - c.runtime.UpdateStatus(ctr) - cStatus := c.runtime.ContainerStatus(ctr) - if cStatus.Status == oci.ContainerStateStopped { - return true - } - return false -} - -// ContainerWait stops a running container with a grace period (i.e., timeout). -func (c *ContainerServer) ContainerWait(container string) (int32, error) { - ctr, err := c.LookupContainer(container) - if err != nil { - return 0, errors.Wrapf(err, "failed to find container %s", container) - } - - err = wait.PollImmediateInfinite(1, - func() (bool, error) { - if !isStopped(c, ctr) { - return false, nil - } else { // nolint - return true, nil // nolint - } // nolint - - }, - ) - - if err != nil { - return 0, err - } - exitCode := ctr.State().ExitCode - c.ContainerStateToDisk(ctr) - return exitCode, nil -} diff --git a/oci/container.go b/oci/container.go deleted file mode 100644 index c0eff2fd1..000000000 --- a/oci/container.go +++ /dev/null @@ -1,260 +0,0 @@ -package oci - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/containernetworking/plugins/pkg/ns" - "github.com/docker/docker/pkg/signal" - specs "github.com/opencontainers/runtime-spec/specs-go" - "k8s.io/apimachinery/pkg/fields" - pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" -) - -const ( - defaultStopSignal = "TERM" -) - -// Container represents a runtime container. -type Container struct { - id string - name string - logPath string - labels fields.Set - annotations fields.Set - crioAnnotations fields.Set - image string - sandbox string - netns ns.NetNS - terminal bool - stdin bool - stdinOnce bool - privileged bool - trusted bool - state *ContainerState - metadata *pb.ContainerMetadata - opLock sync.Locker - // this is the /var/run/storage/... directory, erased on reboot - bundlePath string - // this is the /var/lib/storage/... directory - dir string - stopSignal string - imageName string - imageRef string - volumes []ContainerVolume - mountPoint string - spec *specs.Spec -} - -// ContainerVolume is a bind mount for the container. -type ContainerVolume struct { - ContainerPath string `json:"container_path"` - HostPath string `json:"host_path"` - Readonly bool `json:"readonly"` -} - -// ContainerState represents the status of a container. -type ContainerState struct { - specs.State - Created time.Time `json:"created"` - Started time.Time `json:"started,omitempty"` - Finished time.Time `json:"finished,omitempty"` - ExitCode int32 `json:"exitCode,omitempty"` - OOMKilled bool `json:"oomKilled,omitempty"` - Error string `json:"error,omitempty"` -} - -// NewContainer creates a container object. -func NewContainer(id string, name string, bundlePath string, logPath string, netns ns.NetNS, labels map[string]string, crioAnnotations map[string]string, annotations map[string]string, image string, imageName string, imageRef string, metadata *pb.ContainerMetadata, sandbox string, terminal bool, stdin bool, stdinOnce bool, privileged bool, trusted bool, dir string, created time.Time, stopSignal string) (*Container, error) { - state := &ContainerState{} - state.Created = created - c := &Container{ - id: id, - name: name, - bundlePath: bundlePath, - logPath: logPath, - labels: labels, - sandbox: sandbox, - netns: netns, - terminal: terminal, - stdin: stdin, - stdinOnce: stdinOnce, - privileged: privileged, - trusted: trusted, - metadata: metadata, - annotations: annotations, - crioAnnotations: crioAnnotations, - image: image, - imageName: imageName, - imageRef: imageRef, - dir: dir, - state: state, - stopSignal: stopSignal, - opLock: new(sync.Mutex), - } - return c, nil -} - -// SetSpec loads the OCI spec in the container struct -func (c *Container) SetSpec(s *specs.Spec) { - c.spec = s -} - -// Spec returns a copy of the spec for the container -func (c *Container) Spec() specs.Spec { - return *c.spec -} - -// GetStopSignal returns the container's own stop signal configured from the -// image configuration or the default one. -func (c *Container) GetStopSignal() string { - if c.stopSignal == "" { - return defaultStopSignal - } - cleanSignal := strings.TrimPrefix(strings.ToUpper(c.stopSignal), "SIG") - _, ok := signal.SignalMap[cleanSignal] - if !ok { - return defaultStopSignal - } - return cleanSignal -} - -// FromDisk restores container's state from disk -func (c *Container) FromDisk() error { - jsonSource, err := os.Open(c.StatePath()) - if err != nil { - return err - } - defer jsonSource.Close() - - dec := json.NewDecoder(jsonSource) - return dec.Decode(c.state) -} - -// StatePath returns the containers state.json path -func (c *Container) StatePath() string { - return filepath.Join(c.dir, "state.json") -} - -// CreatedAt returns the container creation time -func (c *Container) CreatedAt() time.Time { - return c.state.Created -} - -// Name returns the name of the container. -func (c *Container) Name() string { - return c.name -} - -// ID returns the id of the container. -func (c *Container) ID() string { - return c.id -} - -// BundlePath returns the bundlePath of the container. -func (c *Container) BundlePath() string { - return c.bundlePath -} - -// LogPath returns the log path of the container. -func (c *Container) LogPath() string { - return c.logPath -} - -// Labels returns the labels of the container. -func (c *Container) Labels() map[string]string { - return c.labels -} - -// Annotations returns the annotations of the container. -func (c *Container) Annotations() map[string]string { - return c.annotations -} - -// CrioAnnotations returns the crio annotations of the container. -func (c *Container) CrioAnnotations() map[string]string { - return c.crioAnnotations -} - -// Image returns the image of the container. -func (c *Container) Image() string { - return c.image -} - -// ImageName returns the image name of the container. -func (c *Container) ImageName() string { - return c.imageName -} - -// ImageRef returns the image ref of the container. -func (c *Container) ImageRef() string { - return c.imageRef -} - -// Sandbox returns the sandbox name of the container. -func (c *Container) Sandbox() string { - return c.sandbox -} - -// Dir returns the the dir of the container -func (c *Container) Dir() string { - return c.dir -} - -// NetNsPath returns the path to the network namespace of the container. -func (c *Container) NetNsPath() (string, error) { - if c.state == nil { - return "", fmt.Errorf("container state is not populated") - } - - if c.netns == nil { - return fmt.Sprintf("/proc/%d/ns/net", c.state.Pid), nil - } - - return c.netns.Path(), nil -} - -// Metadata returns the metadata of the container. -func (c *Container) Metadata() *pb.ContainerMetadata { - return c.metadata -} - -// State returns the state of the running container -func (c *Container) State() *ContainerState { - c.opLock.Lock() - defer c.opLock.Unlock() - return c.state -} - -// AddVolume adds a volume to list of container volumes. -func (c *Container) AddVolume(v ContainerVolume) { - c.volumes = append(c.volumes, v) -} - -// Volumes returns the list of container volumes. -func (c *Container) Volumes() []ContainerVolume { - return c.volumes - -} - -// SetMountPoint sets the container mount point -func (c *Container) SetMountPoint(mp string) { - c.mountPoint = mp -} - -// MountPoint returns the container mount point -func (c *Container) MountPoint() string { - return c.mountPoint -} - -// SetState sets the conainer state -// -// XXX: DO NOT EVER USE THIS, THIS IS JUST USEFUL FOR MOCKING!!! -func (c *Container) SetState(state *ContainerState) { - c.state = state -} diff --git a/oci/finished.go b/oci/finished.go deleted file mode 100644 index 9fedbfb7a..000000000 --- a/oci/finished.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !arm,!386 - -package oci - -import ( - "os" - "syscall" - "time" -) - -func getFinishedTime(fi os.FileInfo) time.Time { - st := fi.Sys().(*syscall.Stat_t) - return time.Unix(st.Ctim.Sec, st.Ctim.Nsec) -} diff --git a/oci/finished_32.go b/oci/finished_32.go deleted file mode 100644 index 3f24b1baa..000000000 --- a/oci/finished_32.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build arm 386 - -package oci - -import ( - "os" - "syscall" - "time" -) - -func getFinishedTime(fi os.FileInfo) time.Time { - st := fi.Sys().(*syscall.Stat_t) - return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)) -} diff --git a/oci/history.go b/oci/history.go deleted file mode 100644 index 2ced41d61..000000000 --- a/oci/history.go +++ /dev/null @@ -1,31 +0,0 @@ -package oci - -import "sort" - -// History is a convenience type for storing a list of containers, -// sorted by creation date in descendant order. -type History []*Container - -// Len returns the number of containers in the history. -func (history *History) Len() int { - return len(*history) -} - -// Less compares two containers and returns true if the second one -// was created before the first one. -func (history *History) Less(i, j int) bool { - containers := *history - // FIXME: state access should be serialized - return containers[j].state.Created.Before(containers[i].state.Created) -} - -// Swap switches containers i and j positions in the history. -func (history *History) Swap(i, j int) { - containers := *history - containers[i], containers[j] = containers[j], containers[i] -} - -// sort orders the history by creation date in descendant order. -func (history *History) sort() { - sort.Sort(history) -} diff --git a/oci/memory_store.go b/oci/memory_store.go deleted file mode 100644 index 6223ce7f0..000000000 --- a/oci/memory_store.go +++ /dev/null @@ -1,92 +0,0 @@ -package oci - -import "sync" - -// memoryStore implements a Store in memory. -type memoryStore struct { - s map[string]*Container - sync.RWMutex -} - -// NewMemoryStore initializes a new memory store. -func NewMemoryStore() ContainerStorer { - return &memoryStore{ - s: make(map[string]*Container), - } -} - -// Add appends a new container to the memory store. -// It overrides the id if it existed before. -func (c *memoryStore) Add(id string, cont *Container) { - c.Lock() - c.s[id] = cont - c.Unlock() -} - -// Get returns a container from the store by id. -func (c *memoryStore) Get(id string) *Container { - c.RLock() - res := c.s[id] - c.RUnlock() - return res -} - -// Delete removes a container from the store by id. -func (c *memoryStore) Delete(id string) { - c.Lock() - delete(c.s, id) - c.Unlock() -} - -// List returns a sorted list of containers from the store. -// The containers are ordered by creation date. -func (c *memoryStore) List() []*Container { - containers := History(c.all()) - containers.sort() - return containers -} - -// Size returns the number of containers in the store. -func (c *memoryStore) Size() int { - c.RLock() - defer c.RUnlock() - return len(c.s) -} - -// First returns the first container found in the store by a given filter. -func (c *memoryStore) First(filter StoreFilter) *Container { - for _, cont := range c.all() { - if filter(cont) { - return cont - } - } - return nil -} - -// ApplyAll calls the reducer function with every container in the store. -// This operation is asynchronous in the memory store. -// NOTE: Modifications to the store MUST NOT be done by the StoreReducer. -func (c *memoryStore) ApplyAll(apply StoreReducer) { - wg := new(sync.WaitGroup) - for _, cont := range c.all() { - wg.Add(1) - go func(container *Container) { - apply(container) - wg.Done() - }(cont) - } - - wg.Wait() -} - -func (c *memoryStore) all() []*Container { - c.RLock() - containers := make([]*Container, 0, len(c.s)) - for _, cont := range c.s { - containers = append(containers, cont) - } - c.RUnlock() - return containers -} - -var _ ContainerStorer = &memoryStore{} diff --git a/oci/oci.go b/oci/oci.go deleted file mode 100644 index 2e7e44b84..000000000 --- a/oci/oci.go +++ /dev/null @@ -1,748 +0,0 @@ -package oci - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containerd/cgroups" - rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/projectatomic/libpod/utils" - "github.com/sirupsen/logrus" - "golang.org/x/net/context" - "golang.org/x/sys/unix" - kwait "k8s.io/apimachinery/pkg/util/wait" -) - -const ( - // ContainerStateCreated represents the created state of a container - ContainerStateCreated = "created" - // ContainerStatePaused represents the paused state of a container - ContainerStatePaused = "paused" - // ContainerStateRunning represents the running state of a container - ContainerStateRunning = "running" - // ContainerStateStopped represents the stopped state of a container - ContainerStateStopped = "stopped" - // ContainerCreateTimeout represents the value of container creating timeout - ContainerCreateTimeout = 240 * time.Second - - // CgroupfsCgroupsManager represents cgroupfs native cgroup manager - CgroupfsCgroupsManager = "cgroupfs" - // SystemdCgroupsManager represents systemd native cgroup manager - SystemdCgroupsManager = "systemd" - // ContainerExitsDir is the location of container exit dirs - ContainerExitsDir = "/var/run/crio/exits" - // ContainerAttachSocketDir is the location for container attach sockets - ContainerAttachSocketDir = "/var/run/crio" - - // killContainerTimeout is the timeout that we wait for the container to - // be SIGKILLed. - killContainerTimeout = 2 * time.Minute -) - -// New creates a new Runtime with options provided -func New(runtimeTrustedPath string, - runtimeUntrustedPath string, - trustLevel string, - conmonPath string, - conmonEnv []string, - cgroupManager string, - containerExitsDir string, - logSizeMax int64, - noPivot bool) (*Runtime, error) { - r := &Runtime{ - name: filepath.Base(runtimeTrustedPath), - trustedPath: runtimeTrustedPath, - untrustedPath: runtimeUntrustedPath, - trustLevel: trustLevel, - conmonPath: conmonPath, - conmonEnv: conmonEnv, - cgroupManager: cgroupManager, - containerExitsDir: containerExitsDir, - logSizeMax: logSizeMax, - noPivot: noPivot, - } - return r, nil -} - -// Runtime stores the information about a oci runtime -type Runtime struct { - name string - trustedPath string - untrustedPath string - trustLevel string - conmonPath string - conmonEnv []string - cgroupManager string - containerExitsDir string - logSizeMax int64 - noPivot bool -} - -// syncInfo is used to return data from monitor process to daemon -type syncInfo struct { - Pid int `json:"pid"` - Message string `json:"message,omitempty"` -} - -// exitCodeInfo is used to return the monitored process exit code to the daemon -type exitCodeInfo struct { - ExitCode int32 `json:"exit_code"` - Message string `json:"message,omitempty"` -} - -// Name returns the name of the OCI Runtime -func (r *Runtime) Name() string { - return r.name -} - -// Path returns the full path the OCI Runtime executable. -// Depending if the container is privileged and/or trusted, -// this will return either the trusted or untrusted runtime path. -func (r *Runtime) Path(c *Container) string { - if !c.trusted { - // We have an explicitly untrusted container. - if c.privileged { - logrus.Warnf("Running an untrusted but privileged container") - return r.trustedPath - } - - if r.untrustedPath != "" { - return r.untrustedPath - } - - return r.trustedPath - } - - // Our container is trusted. Let's look at the configured trust level. - if r.trustLevel == "trusted" { - return r.trustedPath - } - - // Our container is trusted, but we are running untrusted. - // We will use the untrusted container runtime if it's set - // and if it's not a privileged container. - if c.privileged || r.untrustedPath == "" { - return r.trustedPath - } - - return r.untrustedPath -} - -// Version returns the version of the OCI Runtime -func (r *Runtime) Version() (string, error) { - runtimeVersion, err := getOCIVersion(r.trustedPath, "-v") - if err != nil { - return "", err - } - return runtimeVersion, nil -} - -func getOCIVersion(name string, args ...string) (string, error) { - out, err := utils.ExecCmd(name, args...) - if err != nil { - return "", err - } - - firstLine := out[:strings.Index(out, "\n")] - v := firstLine[strings.LastIndex(firstLine, " ")+1:] - return v, nil -} - -// CreateContainer creates a container. -func (r *Runtime) CreateContainer(c *Container, cgroupParent string) (err error) { - var stderrBuf bytes.Buffer - parentPipe, childPipe, err := newPipe() - childStartPipe, parentStartPipe, err := newPipe() - if err != nil { - return fmt.Errorf("error creating socket pair: %v", err) - } - defer parentPipe.Close() - defer parentStartPipe.Close() - - var args []string - if r.cgroupManager == SystemdCgroupsManager { - args = append(args, "-s") - } - args = append(args, "-c", c.id) - args = append(args, "-u", c.id) - args = append(args, "-r", r.Path(c)) - args = append(args, "-b", c.bundlePath) - args = append(args, "-p", filepath.Join(c.bundlePath, "pidfile")) - args = append(args, "-l", c.logPath) - args = append(args, "--exit-dir", r.containerExitsDir) - args = append(args, "--socket-dir-path", ContainerAttachSocketDir) - if r.logSizeMax >= 0 { - args = append(args, "--log-size-max", fmt.Sprintf("%v", r.logSizeMax)) - } - if r.noPivot { - args = append(args, "--no-pivot") - } - if c.terminal { - args = append(args, "-t") - } else if c.stdin { - args = append(args, "-i") - } - logrus.WithFields(logrus.Fields{ - "args": args, - }).Debugf("running conmon: %s", r.conmonPath) - - cmd := exec.Command(r.conmonPath, args...) - cmd.Dir = c.bundlePath - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if c.terminal { - cmd.Stderr = &stderrBuf - } - cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe, childStartPipe) - // 0, 1 and 2 are stdin, stdout and stderr - cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3)) - cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", 4)) - - err = cmd.Start() - if err != nil { - childPipe.Close() - return err - } - - // We don't need childPipe on the parent side - childPipe.Close() - childStartPipe.Close() - - // Move conmon to specified cgroup - if r.cgroupManager == SystemdCgroupsManager { - logrus.Infof("Running conmon under slice %s and unitName %s", cgroupParent, createUnitName("crio-conmon", c.id)) - if err = utils.RunUnderSystemdScope(cmd.Process.Pid, cgroupParent, createUnitName("crio-conmon", c.id)); err != nil { - logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err) - } - } else { - control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(filepath.Join(cgroupParent, "/crio-conmon-"+c.id)), &rspec.LinuxResources{}) - if err != nil { - logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) - } else { - // Here we should defer a crio-connmon- cgroup hierarchy deletion, but it will - // always fail as conmon's pid is still there. - // Fortunately, kubelet takes care of deleting this for us, so the leak will - // only happens in corner case where one does a manual deletion of the container - // through e.g. runc. This should be handled by implementing a conmon monitoring - // routine that does the cgroup cleanup once conmon is terminated. - if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil { - logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) - } - } - } - - /* We set the cgroup, now the child can start creating children */ - someData := []byte{0} - _, err = parentStartPipe.Write(someData) - if err != nil { - return err - } - - /* Wait for initial setup and fork, and reap child */ - err = cmd.Wait() - if err != nil { - return err - } - - // We will delete all container resources if creation fails - defer func() { - if err != nil { - r.DeleteContainer(c) - } - }() - - // Wait to get container pid from conmon - type syncStruct struct { - si *syncInfo - err error - } - ch := make(chan syncStruct) - go func() { - var si *syncInfo - if err = json.NewDecoder(parentPipe).Decode(&si); err != nil { - ch <- syncStruct{err: err} - return - } - ch <- syncStruct{si: si} - }() - - select { - case ss := <-ch: - if ss.err != nil { - return fmt.Errorf("error reading container (probably exited) json message: %v", ss.err) - } - logrus.Debugf("Received container pid: %d", ss.si.Pid) - if ss.si.Pid == -1 { - if ss.si.Message != "" { - logrus.Errorf("Container creation error: %s", ss.si.Message) - return fmt.Errorf("container create failed: %s", ss.si.Message) - } - logrus.Errorf("Container creation failed") - return fmt.Errorf("container create failed") - } - case <-time.After(ContainerCreateTimeout): - logrus.Errorf("Container creation timeout (%v)", ContainerCreateTimeout) - return fmt.Errorf("create container timeout") - } - return nil -} - -func createUnitName(prefix string, name string) string { - return fmt.Sprintf("%s-%s.scope", prefix, name) -} - -// StartContainer starts a container. -func (r *Runtime) StartContainer(c *Container) error { - c.opLock.Lock() - defer c.opLock.Unlock() - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "start", c.id); err != nil { - return err - } - c.state.Started = time.Now() - return nil -} - -// ExecSyncResponse is returned from ExecSync. -type ExecSyncResponse struct { - Stdout []byte - Stderr []byte - ExitCode int32 -} - -// ExecSyncError wraps command's streams, exit code and error on ExecSync error. -type ExecSyncError struct { - Stdout bytes.Buffer - Stderr bytes.Buffer - ExitCode int32 - Err error -} - -func (e ExecSyncError) Error() string { - return fmt.Sprintf("command error: %+v, stdout: %s, stderr: %s, exit code %d", e.Err, e.Stdout.Bytes(), e.Stderr.Bytes(), e.ExitCode) -} - -func prepareExec() (pidFile, parentPipe, childPipe *os.File, err error) { - parentPipe, childPipe, err = os.Pipe() - if err != nil { - return nil, nil, nil, err - } - - pidFile, err = ioutil.TempFile("", "pidfile") - if err != nil { - parentPipe.Close() - childPipe.Close() - return nil, nil, nil, err - } - - return -} - -func parseLog(log []byte) (stdout, stderr []byte) { - // Split the log on newlines, which is what separates entries. - lines := bytes.SplitAfter(log, []byte{'\n'}) - for _, line := range lines { - // Ignore empty lines. - if len(line) == 0 { - continue - } - - // The format of log lines is "DATE pipe REST". - parts := bytes.SplitN(line, []byte{' '}, 3) - if len(parts) < 3 { - // Ignore the line if it's formatted incorrectly, but complain - // about it so it can be debugged. - logrus.Warnf("hit invalid log format: %q", string(line)) - continue - } - - pipe := string(parts[1]) - content := parts[2] - - switch pipe { - case "stdout": - stdout = append(stdout, content...) - case "stderr": - stderr = append(stderr, content...) - default: - // Complain about unknown pipes. - logrus.Warnf("hit invalid log format [unknown pipe %s]: %q", pipe, string(line)) - continue - } - } - - return stdout, stderr -} - -// ExecSync execs a command in a container and returns it's stdout, stderr and return code. -func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp *ExecSyncResponse, err error) { - pidFile, parentPipe, childPipe, err := prepareExec() - if err != nil { - return nil, ExecSyncError{ - ExitCode: -1, - Err: err, - } - } - defer parentPipe.Close() - defer func() { - if e := os.Remove(pidFile.Name()); e != nil { - logrus.Warnf("could not remove temporary PID file %s", pidFile.Name()) - } - }() - - logFile, err := ioutil.TempFile("", "crio-log-"+c.id) - if err != nil { - return nil, ExecSyncError{ - ExitCode: -1, - Err: err, - } - } - logPath := logFile.Name() - defer func() { - logFile.Close() - os.RemoveAll(logPath) - }() - - f, err := ioutil.TempFile("", "exec-process") - if err != nil { - return nil, ExecSyncError{ - ExitCode: -1, - Err: err, - } - } - defer os.RemoveAll(f.Name()) - - var args []string - args = append(args, "-c", c.id) - args = append(args, "-r", r.Path(c)) - args = append(args, "-p", pidFile.Name()) - args = append(args, "-e") - if c.terminal { - args = append(args, "-t") - } - if timeout > 0 { - args = append(args, "-T") - args = append(args, fmt.Sprintf("%d", timeout)) - } - args = append(args, "-l", logPath) - args = append(args, "--socket-dir-path", ContainerAttachSocketDir) - - pspec := c.Spec().Process - pspec.Env = append(pspec.Env, r.conmonEnv...) - pspec.Args = command - processJSON, err := json.Marshal(pspec) - if err != nil { - return nil, ExecSyncError{ - ExitCode: -1, - Err: err, - } - } - - if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil { - return nil, ExecSyncError{ - ExitCode: -1, - Err: err, - } - } - - args = append(args, "--exec-process-spec", f.Name()) - - cmd := exec.Command(r.conmonPath, args...) - - var stdoutBuf, stderrBuf bytes.Buffer - cmd.Stdout = &stdoutBuf - cmd.Stderr = &stderrBuf - cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe) - // 0, 1 and 2 are stdin, stdout and stderr - cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3)) - - err = cmd.Start() - if err != nil { - childPipe.Close() - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: -1, - Err: err, - } - } - - // We don't need childPipe on the parent side - childPipe.Close() - - err = cmd.Wait() - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - if status, ok := exitErr.Sys().(unix.WaitStatus); ok { - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: int32(status.ExitStatus()), - Err: err, - } - } - } else { - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: -1, - Err: err, - } - } - } - - var ec *exitCodeInfo - if err := json.NewDecoder(parentPipe).Decode(&ec); err != nil { - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: -1, - Err: err, - } - } - - logrus.Infof("Received container exit code: %v, message: %s", ec.ExitCode, ec.Message) - - if ec.ExitCode == -1 { - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: -1, - Err: fmt.Errorf(ec.Message), - } - } - - // The actual logged output is not the same as stdoutBuf and stderrBuf, - // which are used for getting error information. For the actual - // ExecSyncResponse we have to read the logfile. - // XXX: Currently runC dups the same console over both stdout and stderr, - // so we can't differentiate between the two. - - logBytes, err := ioutil.ReadFile(logPath) - if err != nil { - return nil, ExecSyncError{ - Stdout: stdoutBuf, - Stderr: stderrBuf, - ExitCode: -1, - Err: err, - } - } - - // We have to parse the log output into {stdout, stderr} buffers. - stdoutBytes, stderrBytes := parseLog(logBytes) - return &ExecSyncResponse{ - Stdout: stdoutBytes, - Stderr: stderrBytes, - ExitCode: ec.ExitCode, - }, nil -} - -func waitContainerStop(ctx context.Context, c *Container, timeout time.Duration) error { - done := make(chan struct{}) - // we could potentially re-use "done" channel to exit the loop on timeout - // but we use another channel "chControl" so that we won't never incur in the - // case the "done" channel is closed in the "default" select case and we also - // reach the timeout in the select below. If that happens we could raise - // a panic closing a closed channel so better be safe and use another new - // channel just to control the loop. - chControl := make(chan struct{}) - go func() { - for { - select { - case <-chControl: - return - default: - // Check if the process is still around - err := unix.Kill(c.state.Pid, 0) - if err == unix.ESRCH { - close(done) - return - } - time.Sleep(100 * time.Millisecond) - } - } - }() - select { - case <-done: - return nil - case <-ctx.Done(): - close(chControl) - return ctx.Err() - case <-time.After(timeout): - close(chControl) - err := unix.Kill(c.state.Pid, unix.SIGKILL) - if err != nil && err != unix.ESRCH { - return fmt.Errorf("failed to kill process: %v", err) - } - } - - c.state.Finished = time.Now() - return nil -} - -// StopContainer stops a container. Timeout is given in seconds. -func (r *Runtime) StopContainer(ctx context.Context, c *Container, timeout int64) error { - c.opLock.Lock() - defer c.opLock.Unlock() - - // Check if the process is around before sending a signal - err := unix.Kill(c.state.Pid, 0) - if err == unix.ESRCH { - c.state.Finished = time.Now() - return nil - } - - if timeout > 0 { - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.id, c.GetStopSignal()); err != nil { - return fmt.Errorf("failed to stop container %s, %v", c.id, err) - } - err = waitContainerStop(ctx, c, time.Duration(timeout)*time.Second) - if err == nil { - return nil - } - logrus.Warnf("Stop container %q timed out: %v", c.ID(), err) - } - - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", "--all", c.id, "KILL"); err != nil { - return fmt.Errorf("failed to stop container %s, %v", c.id, err) - } - - return waitContainerStop(ctx, c, killContainerTimeout) -} - -// DeleteContainer deletes a container. -func (r *Runtime) DeleteContainer(c *Container) error { - c.opLock.Lock() - defer c.opLock.Unlock() - _, err := utils.ExecCmd(r.Path(c), "delete", "--force", c.id) - return err -} - -// SetStartFailed sets the container state appropriately after a start failure -func (r *Runtime) SetStartFailed(c *Container, err error) { - c.opLock.Lock() - defer c.opLock.Unlock() - // adjust finished and started times - c.state.Finished, c.state.Started = c.state.Created, c.state.Created - c.state.Error = err.Error() -} - -// UpdateStatus refreshes the status of the container. -func (r *Runtime) UpdateStatus(c *Container) error { - c.opLock.Lock() - defer c.opLock.Unlock() - out, err := exec.Command(r.Path(c), "state", c.id).CombinedOutput() - if err != nil { - // there are many code paths that could lead to have a bad state in the - // underlying runtime. - // On any error like a container went away or we rebooted and containers - // went away we do not error out stopping kubernetes to recover. - // We always populate the fields below so kube can restart/reschedule - // containers failing. - c.state.Status = ContainerStateStopped - c.state.Finished = time.Now() - c.state.ExitCode = 255 - return nil - } - if err := json.NewDecoder(bytes.NewBuffer(out)).Decode(&c.state); err != nil { - return fmt.Errorf("failed to decode container status for %s: %s", c.id, err) - } - - if c.state.Status == ContainerStateStopped { - exitFilePath := filepath.Join(r.containerExitsDir, c.id) - var fi os.FileInfo - err = kwait.ExponentialBackoff( - kwait.Backoff{ - Duration: 500 * time.Millisecond, - Factor: 1.2, - Steps: 6, - }, - func() (bool, error) { - var err error - fi, err = os.Stat(exitFilePath) - if err != nil { - // wait longer - return false, nil - } - return true, nil - }) - if err != nil { - logrus.Warnf("failed to find container exit file: %v", err) - c.state.ExitCode = -1 - } else { - c.state.Finished = getFinishedTime(fi) - statusCodeStr, err := ioutil.ReadFile(exitFilePath) - if err != nil { - return fmt.Errorf("failed to read exit file: %v", err) - } - statusCode, err := strconv.Atoi(string(statusCodeStr)) - if err != nil { - return fmt.Errorf("status code conversion failed: %v", err) - } - c.state.ExitCode = int32(statusCode) - } - - oomFilePath := filepath.Join(c.bundlePath, "oom") - if _, err = os.Stat(oomFilePath); err == nil { - c.state.OOMKilled = true - } - } - - return nil -} - -// ContainerStatus returns the state of a container. -func (r *Runtime) ContainerStatus(c *Container) *ContainerState { - c.opLock.Lock() - defer c.opLock.Unlock() - return c.state -} - -// newPipe creates a unix socket pair for communication -func newPipe() (parent *os.File, child *os.File, err error) { - fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) - if err != nil { - return nil, nil, err - } - return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil -} - -// RuntimeReady checks if the runtime is up and ready to accept -// basic containers e.g. container only needs host network. -func (r *Runtime) RuntimeReady() (bool, error) { - return true, nil -} - -// NetworkReady checks if the runtime network is up and ready to -// accept containers which require container network. -func (r *Runtime) NetworkReady() (bool, error) { - return true, nil -} - -// PauseContainer pauses a container. -func (r *Runtime) PauseContainer(c *Container) error { - c.opLock.Lock() - defer c.opLock.Unlock() - _, err := utils.ExecCmd(r.Path(c), "pause", c.id) - return err -} - -// UnpauseContainer unpauses a container. -func (r *Runtime) UnpauseContainer(c *Container) error { - c.opLock.Lock() - defer c.opLock.Unlock() - _, err := utils.ExecCmd(r.Path(c), "resume", c.id) - return err -} diff --git a/oci/store.go b/oci/store.go deleted file mode 100644 index 1d27a0f9e..000000000 --- a/oci/store.go +++ /dev/null @@ -1,27 +0,0 @@ -package oci - -// StoreFilter defines a function to filter -// container in the store. -type StoreFilter func(*Container) bool - -// StoreReducer defines a function to -// manipulate containers in the store -type StoreReducer func(*Container) - -// ContainerStorer defines an interface that any container store must implement. -type ContainerStorer interface { - // Add appends a new container to the store. - Add(string, *Container) - // Get returns a container from the store by the identifier it was stored with. - Get(string) *Container - // Delete removes a container from the store by the identifier it was stored with. - Delete(string) - // List returns a list of containers from the store. - List() []*Container - // Size returns the number of containers in the store. - Size() int - // First returns the first container found in the store by a given filter. - First(StoreFilter) *Container - // ApplyAll calls the reducer function with every container in the store. - ApplyAll(StoreReducer) -} |