diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 12 | ||||
-rw-r--r-- | libpod/container_darwin.go | 14 | ||||
-rw-r--r-- | libpod/container_internal.go | 43 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 52 | ||||
-rw-r--r-- | libpod/container_internal_unsupported.go | 7 | ||||
-rw-r--r-- | libpod/container_linux.go | 23 | ||||
-rw-r--r-- | libpod/container_windows.go | 14 | ||||
-rw-r--r-- | libpod/errors.go | 4 | ||||
-rw-r--r-- | libpod/networking_linux.go (renamed from libpod/networking.go) | 16 | ||||
-rw-r--r-- | libpod/oci.go | 27 | ||||
-rw-r--r-- | libpod/oci_linux.go | 41 | ||||
-rw-r--r-- | libpod/oci_unsupported.go | 7 | ||||
-rw-r--r-- | libpod/runtime_pod.go | 246 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 249 | ||||
-rw-r--r-- | libpod/runtime_pod_unsupported.go | 16 | ||||
-rw-r--r-- | libpod/stats.go | 35 | ||||
-rw-r--r-- | libpod/stats_config.go | 18 | ||||
-rw-r--r-- | libpod/stats_unsupported.go | 8 |
18 files changed, 482 insertions, 350 deletions
diff --git a/libpod/container.go b/libpod/container.go index b7189ae0b..2931aee30 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -8,7 +8,6 @@ import ( "github.com/containernetworking/cni/pkg/types" cnitypes "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -141,10 +140,6 @@ type containerState struct { OOMKilled bool `json:"oomKilled,omitempty"` // PID is the PID of a running container PID int `json:"pid,omitempty"` - // NetNSPath is the path of the container's network namespace - // Will only be set if config.CreateNetNS is true, or the container was - // told to join another container's network namespace - NetNS ns.NetNS `json:"-"` // ExecSessions contains active exec sessions for container // Exec session ID is mapped to PID of exec process ExecSessions map[string]*ExecSession `json:"execSessions,omitempty"` @@ -177,6 +172,13 @@ type containerState struct { // ExtensionStageHooks holds hooks which will be executed by libpod // and not delegated to the OCI runtime. ExtensionStageHooks map[string][]spec.Hook `json:"extensionStageHooks,omitempty"` + + // Special container state attributes for Linux + containerStateLinux + // Special container state attributes for Windows + containerStateWindows + // Special container state attributes for Darwin + containerStateDarwin } // ExecSession contains information on an active exec session diff --git a/libpod/container_darwin.go b/libpod/container_darwin.go new file mode 100644 index 000000000..948ed9f2a --- /dev/null +++ b/libpod/container_darwin.go @@ -0,0 +1,14 @@ +// +build darwin + +package libpod + +type containerStateDarwin struct { +} + +// containerStateLinux is intentionally left as a blank stub +type containerStateLinux struct { +} + +// containerStateWindows is intentionally left as a blank stub +type containerStateWindows struct { +} diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 07bb5c6d1..22b398709 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -14,7 +14,6 @@ import ( "syscall" "time" - "github.com/containerd/cgroups" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/chrootarchive" @@ -810,47 +809,6 @@ func (c *Container) prepare() (err error) { return nil } -// cleanupCgroup cleans up residual CGroups after container execution -// This is a no-op for the systemd cgroup driver -func (c *Container) cleanupCgroups() error { - if !c.state.CgroupCreated { - logrus.Debugf("Cgroups are not present, ignoring...") - return nil - } - - if c.runtime.config.CgroupManager == SystemdCgroupsManager { - return nil - } - - // Remove the base path of the container's cgroups - path := filepath.Join(c.config.CgroupParent, fmt.Sprintf("libpod-%s", c.ID())) - - logrus.Debugf("Removing CGroup %s", path) - - cgroup, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(path)) - if err != nil { - // It's fine for the cgroup to not exist - // We want it gone, it's gone - if err == cgroups.ErrCgroupDeleted { - return nil - } - - return err - } - - if err := cgroup.Delete(); err != nil { - return err - } - - c.state.CgroupCreated = false - - if c.valid { - return c.save() - } - - return nil -} - // cleanupNetwork unmounts and cleans up the container's network func (c *Container) cleanupNetwork() error { if c.state.NetNS == nil { @@ -1258,6 +1216,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, errors.Wrapf(err, "error setting up OCI Hooks") } } + // Bind builtin image volumes if c.config.Rootfs == "" && c.config.ImageVolumes { if err := c.addImageVolumes(ctx, &g); err != nil { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go new file mode 100644 index 000000000..41b8a86fc --- /dev/null +++ b/libpod/container_internal_linux.go @@ -0,0 +1,52 @@ +// +build linux + +package libpod + +import ( + "fmt" + "path/filepath" + + "github.com/containerd/cgroups" + "github.com/sirupsen/logrus" +) + +// cleanupCgroup cleans up residual CGroups after container execution +// This is a no-op for the systemd cgroup driver +func (c *Container) cleanupCgroups() error { + if !c.state.CgroupCreated { + logrus.Debugf("Cgroups are not present, ignoring...") + return nil + } + + if c.runtime.config.CgroupManager == SystemdCgroupsManager { + return nil + } + + // Remove the base path of the container's cgroups + path := filepath.Join(c.config.CgroupParent, fmt.Sprintf("libpod-%s", c.ID())) + + logrus.Debugf("Removing CGroup %s", path) + + cgroup, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(path)) + if err != nil { + // It's fine for the cgroup to not exist + // We want it gone, it's gone + if err == cgroups.ErrCgroupDeleted { + return nil + } + + return err + } + + if err := cgroup.Delete(); err != nil { + return err + } + + c.state.CgroupCreated = false + + if c.valid { + return c.save() + } + + return nil +} diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go new file mode 100644 index 000000000..015536850 --- /dev/null +++ b/libpod/container_internal_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux + +package libpod + +func (c *Container) cleanupCgroups() error { + return ErrOSNotSupported +} diff --git a/libpod/container_linux.go b/libpod/container_linux.go new file mode 100644 index 000000000..b271a3089 --- /dev/null +++ b/libpod/container_linux.go @@ -0,0 +1,23 @@ +// +build linux + +package libpod + +import ( + "github.com/containernetworking/plugins/pkg/ns" +) + +type containerStateLinux struct { + + // NetNSPath is the path of the container's network namespace + // Will only be set if config.CreateNetNS is true, or the container was + // told to join another container's network namespace + NetNS ns.NetNS `json:"-"` +} + +// containerStateWindows is intentionally left as a blank stub +type containerStateWindows struct { +} + +// containerStateDarwin is intentionally left as a blank stub +type containerStateDarwin struct { +} diff --git a/libpod/container_windows.go b/libpod/container_windows.go new file mode 100644 index 000000000..db2bf24d7 --- /dev/null +++ b/libpod/container_windows.go @@ -0,0 +1,14 @@ +// +build windows + +package libpod + +type containerStateWindows struct { +} + +// containerStateLinux is intentionally left as a blank stub +type containerStateLinux struct { +} + +// containerStateDarwin is intentionally left as a blank stub +type containerStateDarwin struct { +} diff --git a/libpod/errors.go b/libpod/errors.go index 782104cf0..ddd586e29 100644 --- a/libpod/errors.go +++ b/libpod/errors.go @@ -66,4 +66,8 @@ var ( // ErrNotImplemented indicates that the requested functionality is not // yet present ErrNotImplemented = errors.New("not yet implemented") + + // ErrOSNotSupported indicates the function is not available on the particular + // OS. + ErrOSNotSupported = errors.New("No support for this OS yet") ) diff --git a/libpod/networking.go b/libpod/networking_linux.go index afa18b534..ee90b765c 100644 --- a/libpod/networking.go +++ b/libpod/networking_linux.go @@ -1,3 +1,5 @@ +// +build linux + package libpod import ( @@ -15,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/projectatomic/libpod/utils" "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" "golang.org/x/sys/unix" ) @@ -212,3 +215,16 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { return nil } + +func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { + var netStats *netlink.LinkStatistics + err := ns.WithNetNSPath(ctr.state.NetNS.Path(), func(_ ns.NetNS) error { + link, err := netlink.LinkByName(ocicni.DefaultInterfaceName) + if err != nil { + return err + } + netStats = link.Attrs().Statistics + return nil + }) + return netStats, err +} diff --git a/libpod/oci.go b/libpod/oci.go index dfed3d6b8..3a7d6be3a 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -15,11 +15,10 @@ import ( "syscall" "time" - "github.com/containerd/cgroups" "github.com/containers/storage/pkg/idtools" "github.com/coreos/go-systemd/activation" spec "github.com/opencontainers/runtime-spec/specs-go" - selinux "github.com/opencontainers/selinux/go-selinux" + "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -323,7 +322,6 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er fds := activation.Files(false) cmd.ExtraFiles = append(cmd.ExtraFiles, fds...) } - if selinux.GetEnabled() { // Set the label of the conmon process to be level :s0 // This will allow the container processes to talk to fifo-files @@ -361,27 +359,8 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er childStartPipe.Close() // Move conmon to specified cgroup - if os.Getuid() == 0 { - if r.cgroupManager == SystemdCgroupsManager { - unitName := createUnitName("libpod-conmon", ctr.ID()) - - logrus.Infof("Running conmon under slice %s and unitName %s", cgroupParent, unitName) - if err = utils.RunUnderSystemdScope(cmd.Process.Pid, cgroupParent, unitName); err != nil { - logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err) - } - } else { - cgroupPath := filepath.Join(ctr.config.CgroupParent, fmt.Sprintf("libpod-%s", ctr.ID()), "conmon") - control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(cgroupPath), &spec.LinuxResources{}) - if err != nil { - logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) - } else { - // we need to remove this defer and delete the cgroup once conmon exits - // maybe need a conmon monitor? - if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil { - logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) - } - } - } + if err := r.moveConmonToCgroup(ctr, cgroupParent, cmd); err != nil { + return err } /* We set the cgroup, now the child can start creating children */ diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go new file mode 100644 index 000000000..14373cbb2 --- /dev/null +++ b/libpod/oci_linux.go @@ -0,0 +1,41 @@ +// +build linux + +package libpod + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/containerd/cgroups" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/projectatomic/libpod/utils" + "github.com/sirupsen/logrus" +) + +func (r *OCIRuntime) moveConmonToCgroup(ctr *Container, cgroupParent string, cmd *exec.Cmd) error { + if os.Getuid() == 0 { + if r.cgroupManager == SystemdCgroupsManager { + unitName := createUnitName("libpod-conmon", ctr.ID()) + + logrus.Infof("Running conmon under slice %s and unitName %s", cgroupParent, unitName) + if err := utils.RunUnderSystemdScope(cmd.Process.Pid, cgroupParent, unitName); err != nil { + logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err) + } + } else { + cgroupPath := filepath.Join(ctr.config.CgroupParent, fmt.Sprintf("libpod-%s", ctr.ID()), "conmon") + control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(cgroupPath), &spec.LinuxResources{}) + if err != nil { + logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) + } else { + // we need to remove this defer and delete the cgroup once conmon exits + // maybe need a conmon monitor? + if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil { + logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) + } + } + } + } + return nil +} diff --git a/libpod/oci_unsupported.go b/libpod/oci_unsupported.go new file mode 100644 index 000000000..6e5601eac --- /dev/null +++ b/libpod/oci_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux + +package libpod + +func moveConmonToCgroup() error { + return ErrOSNotSupported +} diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go index 067a110fa..af277c5cc 100644 --- a/libpod/runtime_pod.go +++ b/libpod/runtime_pod.go @@ -1,16 +1,5 @@ package libpod -import ( - "context" - "path" - "path/filepath" - "strings" - - "github.com/containerd/cgroups" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - // Contains the public Runtime API for pods // A PodCreateOption is a functional option which alters the Pod created by @@ -22,241 +11,6 @@ type PodCreateOption func(*Pod) error // will include the pod, a false return will exclude it. type PodFilter func(*Pod) bool -// NewPod makes a new, empty pod -func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { - r.lock.Lock() - defer r.lock.Unlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - - pod, err := newPod(r.lockDir, r) - if err != nil { - return nil, errors.Wrapf(err, "error creating pod") - } - - for _, option := range options { - if err := option(pod); err != nil { - return nil, errors.Wrapf(err, "error running pod create option") - } - } - - if pod.config.Name == "" { - name, err := r.generateName() - if err != nil { - return nil, err - } - pod.config.Name = name - } - - pod.valid = true - - // Check CGroup parent sanity, and set it if it was not set - switch r.config.CgroupManager { - case CgroupfsCgroupsManager: - if pod.config.CgroupParent == "" { - pod.config.CgroupParent = CgroupfsDefaultCgroupParent - } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") - } - // Creating CGroup path is currently a NOOP until proper systemd - // cgroup management is merged - case SystemdCgroupsManager: - if pod.config.CgroupParent == "" { - pod.config.CgroupParent = SystemdDefaultCgroupParent - } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") - } - // If we are set to use pod cgroups, set the cgroup parent that - // all containers in the pod will share - // No need to create it with cgroupfs - the first container to - // launch should do it for us - if pod.config.UsePodCgroup { - pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) - } - default: - return nil, errors.Wrapf(ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.CgroupManager) - } - - if err := r.state.AddPod(pod); err != nil { - return nil, errors.Wrapf(err, "error adding pod to state") - } - - return nil, ErrNotImplemented -} - -// RemovePod removes a pod -// If removeCtrs is specified, containers will be removed -// Otherwise, a pod that is not empty will return an error and not be removed -// If force is specified with removeCtrs, all containers will be stopped before -// being removed -// Otherwise, the pod will not be removed if any containers are running -func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { - r.lock.Lock() - defer r.lock.Unlock() - - if !r.valid { - return ErrRuntimeStopped - } - - p.lock.Lock() - defer p.lock.Unlock() - - if !p.valid { - return ErrPodRemoved - } - - ctrs, err := r.state.PodContainers(p) - if err != nil { - return err - } - - numCtrs := len(ctrs) - - if !removeCtrs && numCtrs > 0 { - return errors.Wrapf(ErrCtrExists, "pod %s contains containers and cannot be removed", p.ID()) - } - - // Go through and lock all containers so we can operate on them all at once - dependencies := make(map[string][]string) - for _, ctr := range ctrs { - ctr.lock.Lock() - defer ctr.lock.Unlock() - - // Sync all containers - if err := ctr.syncContainer(); err != nil { - return err - } - - // Check if the container is in a good state to be removed - if ctr.state.State == ContainerStatePaused { - return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains paused container %s, cannot remove", p.ID(), ctr.ID()) - } - - if ctr.state.State == ContainerStateUnknown { - return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s with invalid state", p.ID(), ctr.ID()) - } - - // If the container is running and force is not set we can't do anything - if ctr.state.State == ContainerStateRunning && !force { - return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which is running", p.ID(), ctr.ID()) - } - - // If the container has active exec sessions and force is not set we can't do anything - if len(ctr.state.ExecSessions) != 0 && !force { - return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which has active exec sessions", p.ID(), ctr.ID()) - } - - deps, err := r.state.ContainerInUse(ctr) - if err != nil { - return err - } - dependencies[ctr.ID()] = deps - } - - // Check if containers have dependencies - // If they do, and the dependencies are not in the pod, error - for ctr, deps := range dependencies { - for _, dep := range deps { - if _, ok := dependencies[dep]; !ok { - return errors.Wrapf(ErrCtrExists, "container %s depends on container %s not in pod %s", ctr, dep, p.ID()) - } - } - } - - // First loop through all containers and stop them - // Do not remove in this loop to ensure that we don't remove unless all - // containers are in a good state - if force { - for _, ctr := range ctrs { - // If force is set and the container is running, stop it now - if ctr.state.State == ContainerStateRunning { - if err := r.ociRuntime.stopContainer(ctr, ctr.StopTimeout()); err != nil { - return errors.Wrapf(err, "error stopping container %s to remove pod %s", ctr.ID(), p.ID()) - } - - // Sync again to pick up stopped state - if err := ctr.syncContainer(); err != nil { - return err - } - } - // If the container has active exec sessions, stop them now - if len(ctr.state.ExecSessions) != 0 { - if err := r.ociRuntime.execStopContainer(ctr, ctr.StopTimeout()); err != nil { - return err - } - } - } - } - - // Start removing containers - // We can remove containers even if they have dependencies now - // As we have guaranteed their dependencies are in the pod - for _, ctr := range ctrs { - // Clean up network namespace, cgroups, mounts - if err := ctr.cleanup(); err != nil { - return err - } - - // Stop container's storage - if err := ctr.teardownStorage(); err != nil { - return err - } - - // Delete the container from runtime (only if we are not - // ContainerStateConfigured) - if ctr.state.State != ContainerStateConfigured { - if err := ctr.delete(ctx); err != nil { - return err - } - } - } - - // Remove containers from the state - if err := r.state.RemovePodContainers(p); err != nil { - return err - } - - // Mark containers invalid - for _, ctr := range ctrs { - ctr.valid = false - } - - // Remove pod cgroup, if present - if p.state.CgroupPath != "" { - switch p.runtime.config.CgroupManager { - case SystemdCgroupsManager: - // NOOP for now, until proper systemd cgroup management - // is implemented - case CgroupfsCgroupsManager: - // Delete the cgroupfs cgroup - logrus.Debugf("Removing pod cgroup %s", p.state.CgroupPath) - - cgroup, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(p.state.CgroupPath)) - if err != nil && err != cgroups.ErrCgroupDeleted { - return err - } else if err == nil { - if err := cgroup.Delete(); err != nil { - return err - } - } - default: - return errors.Wrapf(ErrInvalidArg, "unknown cgroups manager %s specified", p.runtime.config.CgroupManager) - } - } - - // Remove pod from state - if err := r.state.RemovePod(p); err != nil { - return err - } - - // Mark pod invalid - p.valid = false - - return nil -} - // GetPod retrieves a pod by its ID func (r *Runtime) GetPod(id string) (*Pod, error) { r.lock.RLock() diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go new file mode 100644 index 000000000..0edac9dbe --- /dev/null +++ b/libpod/runtime_pod_linux.go @@ -0,0 +1,249 @@ +// +build linux + +package libpod + +import ( + "context" + "path" + "path/filepath" + "strings" + + "github.com/containerd/cgroups" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NewPod makes a new, empty pod +func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return nil, ErrRuntimeStopped + } + + pod, err := newPod(r.lockDir, r) + if err != nil { + return nil, errors.Wrapf(err, "error creating pod") + } + + for _, option := range options { + if err := option(pod); err != nil { + return nil, errors.Wrapf(err, "error running pod create option") + } + } + + if pod.config.Name == "" { + name, err := r.generateName() + if err != nil { + return nil, err + } + pod.config.Name = name + } + + pod.valid = true + + // Check CGroup parent sanity, and set it if it was not set + switch r.config.CgroupManager { + case CgroupfsCgroupsManager: + if pod.config.CgroupParent == "" { + pod.config.CgroupParent = CgroupfsDefaultCgroupParent + } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") + } + // Creating CGroup path is currently a NOOP until proper systemd + // cgroup management is merged + case SystemdCgroupsManager: + if pod.config.CgroupParent == "" { + pod.config.CgroupParent = SystemdDefaultCgroupParent + } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") + } + // If we are set to use pod cgroups, set the cgroup parent that + // all containers in the pod will share + // No need to create it with cgroupfs - the first container to + // launch should do it for us + if pod.config.UsePodCgroup { + pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + } + default: + return nil, errors.Wrapf(ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.CgroupManager) + } + + if err := r.state.AddPod(pod); err != nil { + return nil, errors.Wrapf(err, "error adding pod to state") + } + + return nil, ErrNotImplemented +} + +// RemovePod removes a pod +// If removeCtrs is specified, containers will be removed +// Otherwise, a pod that is not empty will return an error and not be removed +// If force is specified with removeCtrs, all containers will be stopped before +// being removed +// Otherwise, the pod will not be removed if any containers are running +func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return ErrRuntimeStopped + } + + p.lock.Lock() + defer p.lock.Unlock() + + if !p.valid { + return ErrPodRemoved + } + + ctrs, err := r.state.PodContainers(p) + if err != nil { + return err + } + + numCtrs := len(ctrs) + + if !removeCtrs && numCtrs > 0 { + return errors.Wrapf(ErrCtrExists, "pod %s contains containers and cannot be removed", p.ID()) + } + + // Go through and lock all containers so we can operate on them all at once + dependencies := make(map[string][]string) + for _, ctr := range ctrs { + ctr.lock.Lock() + defer ctr.lock.Unlock() + + // Sync all containers + if err := ctr.syncContainer(); err != nil { + return err + } + + // Check if the container is in a good state to be removed + if ctr.state.State == ContainerStatePaused { + return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains paused container %s, cannot remove", p.ID(), ctr.ID()) + } + + if ctr.state.State == ContainerStateUnknown { + return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s with invalid state", p.ID(), ctr.ID()) + } + + // If the container is running and force is not set we can't do anything + if ctr.state.State == ContainerStateRunning && !force { + return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which is running", p.ID(), ctr.ID()) + } + + // If the container has active exec sessions and force is not set we can't do anything + if len(ctr.state.ExecSessions) != 0 && !force { + return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which has active exec sessions", p.ID(), ctr.ID()) + } + + deps, err := r.state.ContainerInUse(ctr) + if err != nil { + return err + } + dependencies[ctr.ID()] = deps + } + + // Check if containers have dependencies + // If they do, and the dependencies are not in the pod, error + for ctr, deps := range dependencies { + for _, dep := range deps { + if _, ok := dependencies[dep]; !ok { + return errors.Wrapf(ErrCtrExists, "container %s depends on container %s not in pod %s", ctr, dep, p.ID()) + } + } + } + + // First loop through all containers and stop them + // Do not remove in this loop to ensure that we don't remove unless all + // containers are in a good state + if force { + for _, ctr := range ctrs { + // If force is set and the container is running, stop it now + if ctr.state.State == ContainerStateRunning { + if err := r.ociRuntime.stopContainer(ctr, ctr.StopTimeout()); err != nil { + return errors.Wrapf(err, "error stopping container %s to remove pod %s", ctr.ID(), p.ID()) + } + + // Sync again to pick up stopped state + if err := ctr.syncContainer(); err != nil { + return err + } + } + // If the container has active exec sessions, stop them now + if len(ctr.state.ExecSessions) != 0 { + if err := r.ociRuntime.execStopContainer(ctr, ctr.StopTimeout()); err != nil { + return err + } + } + } + } + + // Start removing containers + // We can remove containers even if they have dependencies now + // As we have guaranteed their dependencies are in the pod + for _, ctr := range ctrs { + // Clean up network namespace, cgroups, mounts + if err := ctr.cleanup(); err != nil { + return err + } + + // Stop container's storage + if err := ctr.teardownStorage(); err != nil { + return err + } + + // Delete the container from runtime (only if we are not + // ContainerStateConfigured) + if ctr.state.State != ContainerStateConfigured { + if err := ctr.delete(ctx); err != nil { + return err + } + } + } + + // Remove containers from the state + if err := r.state.RemovePodContainers(p); err != nil { + return err + } + + // Mark containers invalid + for _, ctr := range ctrs { + ctr.valid = false + } + + // Remove pod cgroup, if present + if p.state.CgroupPath != "" { + switch p.runtime.config.CgroupManager { + case SystemdCgroupsManager: + // NOOP for now, until proper systemd cgroup management + // is implemented + case CgroupfsCgroupsManager: + // Delete the cgroupfs cgroup + logrus.Debugf("Removing pod cgroup %s", p.state.CgroupPath) + + cgroup, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(p.state.CgroupPath)) + if err != nil && err != cgroups.ErrCgroupDeleted { + return err + } else if err == nil { + if err := cgroup.Delete(); err != nil { + return err + } + } + default: + return errors.Wrapf(ErrInvalidArg, "unknown cgroups manager %s specified", p.runtime.config.CgroupManager) + } + } + + // Remove pod from state + if err := r.state.RemovePod(p); err != nil { + return err + } + + // Mark pod invalid + p.valid = false + + return nil +} diff --git a/libpod/runtime_pod_unsupported.go b/libpod/runtime_pod_unsupported.go new file mode 100644 index 000000000..b22f151d8 --- /dev/null +++ b/libpod/runtime_pod_unsupported.go @@ -0,0 +1,16 @@ +// +build !linux + +package libpod + +import ( + "context" +) + +// NewPod makes a new, empty pod +func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { + return nil, ErrOSNotSupported +} + +func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { + return ErrOSNotSupported +} diff --git a/libpod/stats.go b/libpod/stats.go index 52bcc901d..262c1ac00 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -1,3 +1,5 @@ +// +build linux + package libpod import ( @@ -6,29 +8,9 @@ import ( "time" "github.com/containerd/cgroups" - "github.com/containernetworking/plugins/pkg/ns" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" - "github.com/vishvananda/netlink" ) -// ContainerStats contains the statistics information for a running container -type ContainerStats struct { - ContainerID string - Name 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 *Container) GetContainerStats(previousStats *ContainerStats) (*ContainerStats, error) { stats := new(ContainerStats) @@ -103,19 +85,6 @@ func getMemLimit(cgroupLimit uint64) uint64 { return cgroupLimit } -func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { - var netStats *netlink.LinkStatistics - err := ns.WithNetNSPath(ctr.state.NetNS.Path(), func(_ ns.NetNS) error { - link, err := netlink.LinkByName(ocicni.DefaultInterfaceName) - if err != nil { - return err - } - netStats = link.Attrs().Statistics - return nil - }) - return netStats, err -} - func calculateCPUPercent(stats *cgroups.Metrics, previousCPU, previousSystem uint64) float64 { var ( cpuPercent = 0.0 diff --git a/libpod/stats_config.go b/libpod/stats_config.go new file mode 100644 index 000000000..9c7d97298 --- /dev/null +++ b/libpod/stats_config.go @@ -0,0 +1,18 @@ +package libpod + +// ContainerStats contains the statistics information for a running container +type ContainerStats struct { + ContainerID string + Name string + CPU float64 + CPUNano uint64 + SystemNano uint64 + MemUsage uint64 + MemLimit uint64 + MemPerc float64 + NetInput uint64 + NetOutput uint64 + BlockInput uint64 + BlockOutput uint64 + PIDs uint64 +} diff --git a/libpod/stats_unsupported.go b/libpod/stats_unsupported.go new file mode 100644 index 000000000..7117413eb --- /dev/null +++ b/libpod/stats_unsupported.go @@ -0,0 +1,8 @@ +// +build !linux + +package libpod + +// GetContainerStats gets the running stats for a given container +func (c *Container) GetContainerStats(previousStats *ContainerStats) (*ContainerStats, error) { + return nil, ErrOSNotSupported +} |