diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/checkpoint_restore.go | 29 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 4 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 5 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 3 | ||||
-rw-r--r-- | pkg/adapter/terminal_linux.go | 6 | ||||
-rw-r--r-- | pkg/cgroups/blkio.go | 2 | ||||
-rw-r--r-- | pkg/cgroups/cgroups.go | 41 | ||||
-rw-r--r-- | pkg/cgroups/cpu.go | 2 | ||||
-rw-r--r-- | pkg/cgroups/cpuset.go | 3 | ||||
-rw-r--r-- | pkg/cgroups/memory.go | 3 | ||||
-rw-r--r-- | pkg/cgroups/pids.go | 3 | ||||
-rw-r--r-- | pkg/netns/netns_linux.go | 17 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 24 | ||||
-rw-r--r-- | pkg/spec/spec.go | 56 | ||||
-rw-r--r-- | pkg/spec/spec_linux.go | 42 | ||||
-rw-r--r-- | pkg/spec/spec_unsupported.go | 7 | ||||
-rw-r--r-- | pkg/spec/storage.go | 7 |
17 files changed, 210 insertions, 44 deletions
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go index 97ba5ecf7..ec1464fb1 100644 --- a/pkg/adapter/checkpoint_restore.go +++ b/pkg/adapter/checkpoint_restore.go @@ -4,16 +4,19 @@ package adapter import ( "context" + "io" + "io/ioutil" + "os" + "path/filepath" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/errorhandling" "github.com/containers/storage/pkg/archive" jsoniter "github.com/json-iterator/go" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "io" - "io/ioutil" - "os" - "path/filepath" + "github.com/sirupsen/logrus" ) // Prefixing the checkpoint/restore related functions with 'cr' @@ -25,7 +28,7 @@ func crImportFromJSON(filePath string, v interface{}) error { if err != nil { return errors.Wrapf(err, "Failed to open container definition %s for restore", filePath) } - defer jsonFile.Close() + defer errorhandling.CloseQuiet(jsonFile) content, err := ioutil.ReadAll(jsonFile) if err != nil { @@ -48,7 +51,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri if err != nil { return nil, errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input) } - defer archiveFile.Close() + defer errorhandling.CloseQuiet(archiveFile) options := &archive.TarOptions{ // Here we only need the files config.dump and spec.dump ExcludePatterns: []string{ @@ -62,15 +65,19 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri if err != nil { return nil, err } - defer os.RemoveAll(dir) + defer func() { + if err := os.RemoveAll(dir); err != nil { + logrus.Errorf("could not recursively remove %s: %q", dir, err) + } + }() err = archive.Untar(archiveFile, dir, options) if err != nil { return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) } // Load spec.dump from temporary directory - spec := new(spec.Spec) - if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), spec); err != nil { + dumpSpec := new(spec.Spec) + if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil { return nil, err } @@ -112,7 +119,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri } // Now create a new container from the just loaded information - container, err := runtime.RestoreContainer(ctx, spec, config) + container, err := runtime.RestoreContainer(ctx, dumpSpec, config) if err != nil { return nil, err } @@ -127,7 +134,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName) } - if newName == false { + if !newName { // Only check ID for a restore with the same name. // Using -n to request a new name for the restored container, will also create a new ID if containerConfig.ID != ctrID { diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 1cf9d686a..86e9c0266 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -213,8 +213,8 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa c := c pool.Add(shared.Job{ - c.ID(), - func() error { + ID: c.ID(), + Fn: func() error { err := r.RemoveContainer(ctx, c, cli.Force, cli.Volumes) if err != nil { logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error()) diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index a28e1ab4b..b45b02d09 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -70,8 +70,9 @@ func (r *LocalRuntime) PrunePods(ctx context.Context, cli *cliconfig.PodPruneVal for _, p := range pods { p := p - pool.Add(shared.Job{p.ID(), - func() error { + pool.Add(shared.Job{ + ID: p.ID(), + Fn: func() error { err := r.Runtime.RemovePod(ctx, p, cli.Force, cli.Force) if err != nil { logrus.Debugf("Failed to remove pod %s: %s", p.ID(), err.Error()) diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 8ef88f36b..e65f07898 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -359,9 +359,6 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { if eventsError != nil { return eventsError } - if err != nil { - return errors.Wrapf(err, "unable to tail the events log") - } w := bufio.NewWriter(os.Stdout) for event := range eventChannel { if len(c.Format) > 0 { diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go index be7dc0cb6..e3255ecb6 100644 --- a/pkg/adapter/terminal_linux.go +++ b/pkg/adapter/terminal_linux.go @@ -39,7 +39,11 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, return err } - defer restoreTerminal(oldTermState) + defer func() { + if err := restoreTerminal(oldTermState); err != nil { + logrus.Errorf("unable to restore terminal: %q", err) + } + }() } streams := new(libpod.AttachStreams) diff --git a/pkg/cgroups/blkio.go b/pkg/cgroups/blkio.go index 9c2a811d9..bacd4eb93 100644 --- a/pkg/cgroups/blkio.go +++ b/pkg/cgroups/blkio.go @@ -37,7 +37,7 @@ func (c *blkioHandler) Create(ctr *CgroupControl) (bool, error) { // Destroy the cgroup func (c *blkioHandler) Destroy(ctr *CgroupControl) error { - return os.Remove(ctr.getCgroupv1Path(Blkio)) + return rmDirRecursively(ctr.getCgroupv1Path(Blkio)) } // Stat fills a metrics structure with usage stats for the controller diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index fc9847a51..fda19bff8 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -332,6 +332,13 @@ func Load(path string) (*CgroupControl, error) { systemd: false, } if !cgroup2 { + controllers, err := getAvailableControllers(handlers, false) + if err != nil { + return nil, err + } + control.additionalControllers = controllers + } + if !cgroup2 { for name := range handlers { p := control.getCgroupv1Path(name) if _, err := os.Stat(p); err != nil { @@ -359,11 +366,40 @@ func (c *CgroupControl) Delete() error { return c.DeleteByPath(c.path) } +// rmDirRecursively delete recursively a cgroup directory. +// It differs from os.RemoveAll as it doesn't attempt to unlink files. +// On cgroupfs we are allowed only to rmdir empty directories. +func rmDirRecursively(path string) error { + if err := os.Remove(path); err == nil || os.IsNotExist(err) { + return nil + } + entries, err := ioutil.ReadDir(path) + if err != nil { + return errors.Wrapf(err, "read %s", path) + } + for _, i := range entries { + if i.IsDir() { + if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil { + return err + } + } + } + if os.Remove(path); err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "remove %s", path) + } + } + return nil +} + // DeleteByPath deletes the specified cgroup path func (c *CgroupControl) DeleteByPath(path string) error { if c.systemd { return systemdDestroy(path) } + if c.cgroup2 { + return rmDirRecursively(filepath.Join(cgroupRoot, c.path)) + } var lastError error for _, h := range handlers { if err := h.Destroy(c); err != nil { @@ -372,8 +408,11 @@ func (c *CgroupControl) DeleteByPath(path string) error { } for _, ctr := range c.additionalControllers { + if ctr.symlink { + continue + } p := c.getCgroupv1Path(ctr.name) - if err := os.Remove(p); err != nil { + if err := rmDirRecursively(p); err != nil { lastError = errors.Wrapf(err, "remove %s", p) } } diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index 1c8610cc4..03677f1ef 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -68,7 +68,7 @@ func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) { // Destroy the cgroup func (c *cpuHandler) Destroy(ctr *CgroupControl) error { - return os.Remove(ctr.getCgroupv1Path(CPU)) + return rmDirRecursively(ctr.getCgroupv1Path(CPU)) } // Stat fills a metrics structure with usage stats for the controller diff --git a/pkg/cgroups/cpuset.go b/pkg/cgroups/cpuset.go index 25d2f7f76..46d0484f2 100644 --- a/pkg/cgroups/cpuset.go +++ b/pkg/cgroups/cpuset.go @@ -3,7 +3,6 @@ package cgroups import ( "fmt" "io/ioutil" - "os" "path/filepath" "strings" @@ -77,7 +76,7 @@ func (c *cpusetHandler) Create(ctr *CgroupControl) (bool, error) { // Destroy the cgroup func (c *cpusetHandler) Destroy(ctr *CgroupControl) error { - return os.Remove(ctr.getCgroupv1Path(CPUset)) + return rmDirRecursively(ctr.getCgroupv1Path(CPUset)) } // Stat fills a metrics structure with usage stats for the controller diff --git a/pkg/cgroups/memory.go b/pkg/cgroups/memory.go index 80e88d17c..b3991f7e3 100644 --- a/pkg/cgroups/memory.go +++ b/pkg/cgroups/memory.go @@ -2,7 +2,6 @@ package cgroups import ( "fmt" - "os" "path/filepath" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -33,7 +32,7 @@ func (c *memHandler) Create(ctr *CgroupControl) (bool, error) { // Destroy the cgroup func (c *memHandler) Destroy(ctr *CgroupControl) error { - return os.Remove(ctr.getCgroupv1Path(Memory)) + return rmDirRecursively(ctr.getCgroupv1Path(Memory)) } // Stat fills a metrics structure with usage stats for the controller diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go index ffbde100d..65b9b5b34 100644 --- a/pkg/cgroups/pids.go +++ b/pkg/cgroups/pids.go @@ -3,7 +3,6 @@ package cgroups import ( "fmt" "io/ioutil" - "os" "path/filepath" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -40,7 +39,7 @@ func (c *pidHandler) Create(ctr *CgroupControl) (bool, error) { // Destroy the cgroup func (c *pidHandler) Destroy(ctr *CgroupControl) error { - return os.Remove(ctr.getCgroupv1Path(Pids)) + return rmDirRecursively(ctr.getCgroupv1Path(Pids)) } // Stat fills a metrics structure with usage stats for the controller diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index 4a515c72a..1d6fb873c 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -28,6 +28,7 @@ import ( "sync" "github.com/containernetworking/plugins/pkg/ns" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -90,7 +91,9 @@ func NewNS() (ns.NetNS, error) { // Ensure the mount point is cleaned up on errors; if the namespace // was successfully mounted this will have no effect because the file // is in-use - defer os.RemoveAll(nsPath) + defer func() { + _ = os.RemoveAll(nsPath) + }() var wg sync.WaitGroup wg.Add(1) @@ -109,7 +112,11 @@ func NewNS() (ns.NetNS, error) { if err != nil { return } - defer origNS.Close() + defer func() { + if err := origNS.Close(); err != nil { + logrus.Errorf("unable to close namespace: %q", err) + } + }() // create a new netns on the current thread err = unix.Unshare(unix.CLONE_NEWNET) @@ -118,7 +125,11 @@ func NewNS() (ns.NetNS, error) { } // Put this thread back to the orig ns, since it might get reused (pre go1.10) - defer origNS.Set() + defer func() { + if err := origNS.Set(); err != nil { + logrus.Errorf("unable to set namespace: %q", err) + } + }() // bind mount the netns from the current thread (from /proc) onto the // mount point. This causes the namespace to persist, even when there diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index d7c2de81d..99a0eb729 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -220,7 +220,11 @@ func EnableLinger() (string, error) { conn, err := dbus.SystemBus() if err == nil { - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + logrus.Errorf("unable to close dbus connection: %q", err) + } + }() } lingerEnabled := false @@ -310,13 +314,21 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { if err != nil { return false, -1, err } - defer userNS.Close() + defer func() { + if err := userNS.Close(); err != nil { + logrus.Errorf("unable to close namespace: %q", err) + } + }() mountNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/mnt", pid)) if err != nil { return false, -1, err } - defer userNS.Close() + defer func() { + if err := mountNS.Close(); err != nil { + logrus.Errorf("unable to close namespace: %q", err) + } + }() fd, err := getUserNSFirstChild(userNS.Fd()) if err != nil { @@ -364,7 +376,11 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, defer errorhandling.CloseQuiet(r) defer errorhandling.CloseQuiet(w) - defer w.Write([]byte("0")) + defer func() { + if _, err := w.Write([]byte("0")); err != nil { + logrus.Errorf("failed to write byte 0: %q", err) + } + }() pidC := C.reexec_in_user_namespace(C.int(r.Fd()), cPausePid, cFileToRead, fileOutputFD) pid := int(pidC) diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 5cc021bf5..53b73296a 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -20,6 +20,12 @@ import ( const cpuPeriod = 100000 +type systemUlimit struct { + name string + max uint64 + cur uint64 +} + func getAvailableGids() (int64, error) { idMap, err := user.ParseIDMapFile("/proc/self/gid_map") if err != nil { @@ -80,23 +86,41 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM g.AddLinuxMaskedPaths("/sys/kernel") } } + gid5Available := true if isRootless { nGids, err := getAvailableGids() if err != nil { return nil, err } - if nGids < 5 { - // If we have no GID mappings, the gid=5 default option would fail, so drop it. - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + gid5Available = nGids >= 5 + } + // When using a different user namespace, check that the GID 5 is mapped inside + // the container. + if gid5Available && len(config.IDMappings.GIDMap) > 0 { + mappingFound := false + for _, r := range config.IDMappings.GIDMap { + if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { + mappingFound = true + break } - g.AddMount(devPts) } + if !mappingFound { + gid5Available = false + } + + } + if !gid5Available { + // If we have no GID mappings, the gid=5 default option would fail, so drop it. + g.RemoveMount("/dev/pts") + devPts := spec.Mount{ + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, + } + g.AddMount(devPts) } + if inUserNS && config.IpcMode.IsHost() { g.RemoveMount("/dev/mqueue") devMqueue := spec.Mount{ @@ -557,6 +581,20 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error { ) for _, u := range config.Resources.Ulimit { + if u == "host" { + if len(config.Resources.Ulimit) != 1 { + return errors.New("ulimit can use host only once") + } + hostLimits, err := getHostRlimits() + if err != nil { + return err + } + for _, i := range hostLimits { + g.AddProcessRlimits(i.name, i.max, i.cur) + } + break + } + ul, err := units.ParseUlimit(u) if err != nil { return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) diff --git a/pkg/spec/spec_linux.go b/pkg/spec/spec_linux.go new file mode 100644 index 000000000..fcdfc5c4e --- /dev/null +++ b/pkg/spec/spec_linux.go @@ -0,0 +1,42 @@ +//+build linux + +package createconfig + +import ( + "syscall" + + "github.com/pkg/errors" +) + +type systemRlimit struct { + name string + value int +} + +var systemLimits = []systemRlimit{ + {"RLIMIT_AS", syscall.RLIMIT_AS}, + {"RLIMIT_CORE", syscall.RLIMIT_CORE}, + {"RLIMIT_CPU", syscall.RLIMIT_CPU}, + {"RLIMIT_DATA", syscall.RLIMIT_DATA}, + {"RLIMIT_FSIZE", syscall.RLIMIT_FSIZE}, + {"RLIMIT_NOFILE", syscall.RLIMIT_NOFILE}, + {"RLIMIT_STACK", syscall.RLIMIT_STACK}, +} + +func getHostRlimits() ([]systemUlimit, error) { + ret := []systemUlimit{} + for _, i := range systemLimits { + var l syscall.Rlimit + if err := syscall.Getrlimit(i.value, &l); err != nil { + return nil, errors.Wrapf(err, "cannot read limits for %s", i.name) + } + s := systemUlimit{ + name: i.name, + max: l.Max, + cur: l.Cur, + } + ret = append(ret, s) + } + return ret, nil + +} diff --git a/pkg/spec/spec_unsupported.go b/pkg/spec/spec_unsupported.go new file mode 100644 index 000000000..0f6a9acdc --- /dev/null +++ b/pkg/spec/spec_unsupported.go @@ -0,0 +1,7 @@ +//+build !linux + +package createconfig + +func getHostRlimits() ([]systemUlimit, error) { + return nil, nil +} diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index ed767f5ba..88f1f6dc1 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -211,6 +211,13 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, } mount.Options = opts } + if mount.Type == TypeBind { + absSrc, err := filepath.Abs(mount.Source) + if err != nil { + return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + } + mount.Source = absSrc + } finalMounts = append(finalMounts, mount) } finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes)) |