summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/checkpoint_restore.go29
-rw-r--r--pkg/adapter/containers.go16
-rw-r--r--pkg/adapter/pods.go5
-rw-r--r--pkg/adapter/runtime.go3
-rw-r--r--pkg/adapter/runtime_remote.go8
-rw-r--r--pkg/adapter/sigproxy_linux.go4
-rw-r--r--pkg/adapter/terminal_linux.go10
-rw-r--r--pkg/cgroups/blkio.go2
-rw-r--r--pkg/cgroups/cgroups.go41
-rw-r--r--pkg/cgroups/cpu.go2
-rw-r--r--pkg/cgroups/cpuset.go3
-rw-r--r--pkg/cgroups/memory.go3
-rw-r--r--pkg/cgroups/pids.go3
-rw-r--r--pkg/channelwriter/channelwriter.go34
-rw-r--r--pkg/errorhandling/errorhandling.go23
-rw-r--r--pkg/firewall/firewalld.go5
-rw-r--r--pkg/firewall/iptables.go9
-rw-r--r--pkg/hooks/exec/exec.go5
-rw-r--r--pkg/logs/logs.go7
-rw-r--r--pkg/netns/netns_linux.go21
-rw-r--r--pkg/rootless/rootless_linux.go78
-rw-r--r--pkg/spec/spec.go28
-rw-r--r--pkg/spec/spec_linux.go42
-rw-r--r--pkg/spec/spec_unsupported.go7
-rw-r--r--pkg/util/utils.go16
-rw-r--r--pkg/util/utils_supported.go9
-rw-r--r--pkg/varlinkapi/images.go108
-rw-r--r--pkg/varlinkapi/util.go40
28 files changed, 413 insertions, 148 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 0ea89a72c..86e9c0266 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -95,8 +95,8 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa
}
pool.Add(shared.Job{
- c.ID(),
- func() error {
+ ID: c.ID(),
+ Fn: func() error {
err := c.StopWithTimeout(*timeout)
if err != nil {
if errors.Cause(err) == define.ErrCtrStopped {
@@ -134,8 +134,8 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
c := c
pool.Add(shared.Job{
- c.ID(),
- func() error {
+ ID: c.ID(),
+ Fn: func() error {
return c.Kill(uint(signal))
},
})
@@ -163,8 +163,8 @@ func (r *LocalRuntime) InitContainers(ctx context.Context, cli *cliconfig.InitVa
ctr := c
pool.Add(shared.Job{
- ctr.ID(),
- func() error {
+ ID: ctr.ID(),
+ Fn: func() error {
err := ctr.Init(ctx)
if err != nil {
// If we're initializing all containers, ignore invalid state errors
@@ -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/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 800ed7569..db3f23629 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -97,6 +97,14 @@ func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime,
}, nil
}
+// DeferredShutdown is a bogus wrapper for compaat with the libpod
+// runtime and should only be run when a defer is being used
+func (r RemoteRuntime) DeferredShutdown(force bool) {
+ if err := r.Shutdown(force); err != nil {
+ logrus.Error("unable to shutdown runtime")
+ }
+}
+
// Shutdown is a bogus wrapper for compat with the libpod runtime
func (r RemoteRuntime) Shutdown(force bool) error {
return nil
diff --git a/pkg/adapter/sigproxy_linux.go b/pkg/adapter/sigproxy_linux.go
index af968cb89..efa6afa7b 100644
--- a/pkg/adapter/sigproxy_linux.go
+++ b/pkg/adapter/sigproxy_linux.go
@@ -27,7 +27,9 @@ func ProxySignals(ctr *libpod.Container) {
if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err)
signal.StopCatch(sigBuffer)
- syscall.Kill(syscall.Getpid(), s.(syscall.Signal))
+ if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil {
+ logrus.Errorf("failed to kill pid %d", syscall.Getpid())
+ }
}
}
}()
diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go
index 3c4c3bd38..e3255ecb6 100644
--- a/pkg/adapter/terminal_linux.go
+++ b/pkg/adapter/terminal_linux.go
@@ -35,9 +35,15 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
}
logrus.SetFormatter(&RawTtyFormatter{})
- term.SetRawTerminal(os.Stdin.Fd())
+ if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil {
+ 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 1dad45d7f..081db772f 100644
--- a/pkg/cgroups/cgroups.go
+++ b/pkg/cgroups/cgroups.go
@@ -328,6 +328,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 {
@@ -355,11 +362,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 {
@@ -368,8 +404,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/channelwriter/channelwriter.go b/pkg/channelwriter/channelwriter.go
new file mode 100644
index 000000000..d51400eb3
--- /dev/null
+++ b/pkg/channelwriter/channelwriter.go
@@ -0,0 +1,34 @@
+package channelwriter
+
+import "github.com/pkg/errors"
+
+// Writer is an io.writer-like object that "writes" to a channel
+// instead of a buffer or file, etc. It is handy for varlink endpoints when
+// needing to handle endpoints that do logging "real-time"
+type Writer struct {
+ ByteChannel chan []byte
+}
+
+// NewChannelWriter creates a new channel writer and adds a
+// byte slice channel into it.
+func NewChannelWriter() *Writer {
+ byteChannel := make(chan []byte)
+ return &Writer{
+ ByteChannel: byteChannel,
+ }
+}
+
+// Write method for Writer
+func (c *Writer) Write(w []byte) (int, error) {
+ if c.ByteChannel == nil {
+ return 0, errors.New("channel writer channel cannot be nil")
+ }
+ c.ByteChannel <- w
+ return len(w), nil
+}
+
+// Close method for Writer
+func (c *Writer) Close() error {
+ close(c.ByteChannel)
+ return nil
+}
diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go
new file mode 100644
index 000000000..970d47636
--- /dev/null
+++ b/pkg/errorhandling/errorhandling.go
@@ -0,0 +1,23 @@
+package errorhandling
+
+import (
+ "os"
+
+ "github.com/sirupsen/logrus"
+)
+
+// SyncQuiet syncs a file and logs any error. Should only be used within
+// a defer.
+func SyncQuiet(f *os.File) {
+ if err := f.Sync(); err != nil {
+ logrus.Errorf("unable to sync file %s: %q", f.Name(), err)
+ }
+}
+
+// CloseQuiet closes a file and logs any error. Should only be used within
+// a defer.
+func CloseQuiet(f *os.File) {
+ if err := f.Close(); err != nil {
+ logrus.Errorf("unable to close file %s: %q", f.Name(), err)
+ }
+}
diff --git a/pkg/firewall/firewalld.go b/pkg/firewall/firewalld.go
index 32c2337a0..15e845cb7 100644
--- a/pkg/firewall/firewalld.go
+++ b/pkg/firewall/firewalld.go
@@ -18,6 +18,7 @@ package firewall
import (
"fmt"
+ "github.com/sirupsen/logrus"
"strings"
"github.com/godbus/dbus"
@@ -113,7 +114,9 @@ func (fb *fwdBackend) Del(conf *FirewallNetConf) error {
// Remove firewalld rules which assigned the given source IP to the given zone
firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
var res string
- firewalldObj.Call(firewalldZoneInterface+"."+firewalldRemoveSourceMethod, 0, getFirewalldZone(conf), ipStr).Store(&res)
+ if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldRemoveSourceMethod, 0, getFirewalldZone(conf), ipStr).Store(&res); err != nil {
+ logrus.Errorf("unable to store firewallobj")
+ }
}
return nil
}
diff --git a/pkg/firewall/iptables.go b/pkg/firewall/iptables.go
index 59d81b287..92d249f7b 100644
--- a/pkg/firewall/iptables.go
+++ b/pkg/firewall/iptables.go
@@ -21,6 +21,7 @@ package firewall
import (
"fmt"
+ "github.com/sirupsen/logrus"
"net"
"github.com/coreos/go-iptables/iptables"
@@ -53,7 +54,9 @@ func generateFilterRule(privChainName string) []string {
func cleanupRules(ipt *iptables.IPTables, privChainName string, rules [][]string) {
for _, rule := range rules {
- ipt.Delete("filter", privChainName, rule...)
+ if err := ipt.Delete("filter", privChainName, rule...); err != nil {
+ logrus.Errorf("failed to delete iptables rule %s", privChainName)
+ }
}
}
@@ -185,7 +188,9 @@ func (ib *iptablesBackend) Add(conf *FirewallNetConf) error {
func (ib *iptablesBackend) Del(conf *FirewallNetConf) error {
for proto, ipt := range ib.protos {
- ib.delRules(conf, ipt, proto)
+ if err := ib.delRules(conf, ipt, proto); err != nil {
+ logrus.Errorf("failed to delete iptables backend rule %s", conf.IptablesAdminChainName)
+ }
}
return nil
}
diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go
index 0dd091561..4038e3d94 100644
--- a/pkg/hooks/exec/exec.go
+++ b/pkg/hooks/exec/exec.go
@@ -5,6 +5,7 @@ import (
"bytes"
"context"
"fmt"
+ "github.com/sirupsen/logrus"
"io"
osexec "os/exec"
"time"
@@ -54,7 +55,9 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer,
case err = <-exit:
return err, err
case <-ctx.Done():
- cmd.Process.Kill()
+ if err := cmd.Process.Kill(); err != nil {
+ logrus.Errorf("failed to kill pid %v", cmd.Process)
+ }
timer := time.NewTimer(postKillTimeout)
defer timer.Stop()
select {
diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go
index 0f684750e..89e4e5686 100644
--- a/pkg/logs/logs.go
+++ b/pkg/logs/logs.go
@@ -30,6 +30,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -153,7 +154,7 @@ func ReadLogs(logPath string, ctr *libpod.Container, opts *LogOptions) error {
if err != nil {
return errors.Wrapf(err, "failed to open log file %q", logPath)
}
- defer file.Close()
+ defer errorhandling.CloseQuiet(file)
msg := &logMessage{}
opts.bytes = -1
@@ -161,9 +162,9 @@ func ReadLogs(logPath string, ctr *libpod.Container, opts *LogOptions) error {
reader := bufio.NewReader(file)
if opts.Follow {
- followLog(reader, writer, opts, ctr, msg, logPath)
+ err = followLog(reader, writer, opts, ctr, msg, logPath)
} else {
- dumpLog(reader, writer, opts, msg, logPath)
+ err = dumpLog(reader, writer, opts, msg, logPath)
}
return err
}
diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go
index a72a2d098..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"
)
@@ -83,12 +84,16 @@ func NewNS() (ns.NetNS, error) {
if err != nil {
return nil, err
}
- mountPointFd.Close()
+ if err := mountPointFd.Close(); err != nil {
+ return nil, err
+ }
// 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)
@@ -107,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)
@@ -116,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 8028a359c..99a0eb729 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -17,6 +17,7 @@ import (
"syscall"
"unsafe"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/storage/pkg/idtools"
"github.com/docker/docker/pkg/signal"
"github.com/godbus/dbus"
@@ -41,8 +42,7 @@ const (
)
func runInUser() error {
- os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done")
- return nil
+ return os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done")
}
var (
@@ -57,9 +57,15 @@ func IsRootless() bool {
rootlessGIDInit := int(C.rootless_gid())
if rootlessUIDInit != 0 {
// This happens if we joined the user+mount namespace as part of
- os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done")
- os.Setenv("_CONTAINERS_ROOTLESS_UID", fmt.Sprintf("%d", rootlessUIDInit))
- os.Setenv("_CONTAINERS_ROOTLESS_GID", fmt.Sprintf("%d", rootlessGIDInit))
+ if err := os.Setenv("_CONTAINERS_USERNS_CONFIGURED", "done"); err != nil {
+ logrus.Errorf("failed to set environment variable %s as %s", "_CONTAINERS_USERNS_CONFIGURED", "done")
+ }
+ if err := os.Setenv("_CONTAINERS_ROOTLESS_UID", fmt.Sprintf("%d", rootlessUIDInit)); err != nil {
+ logrus.Errorf("failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_UID", rootlessUIDInit)
+ }
+ if err := os.Setenv("_CONTAINERS_ROOTLESS_GID", fmt.Sprintf("%d", rootlessGIDInit)); err != nil {
+ logrus.Errorf("failed to set environment variable %s as %d", "_CONTAINERS_ROOTLESS_GID", rootlessGIDInit)
+ }
}
isRootless = os.Geteuid() != 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != ""
})
@@ -185,18 +191,24 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
}
if ns == currentNS {
- syscall.Close(int(nextFd))
+ if err := syscall.Close(int(nextFd)); err != nil {
+ return nil, err
+ }
// Drop O_CLOEXEC for the fd.
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
if errno != 0 {
- syscall.Close(int(fd))
+ if err := syscall.Close(int(fd)); err != nil {
+ logrus.Errorf("failed to close file descriptor %d", fd)
+ }
return nil, errno
}
return os.NewFile(fd, "userns child"), nil
}
- syscall.Close(int(fd))
+ if err := syscall.Close(int(fd)); err != nil {
+ return nil, err
+ }
fd = nextFd
}
}
@@ -208,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
@@ -252,7 +268,9 @@ func EnableLinger() (string, error) {
if lingerEnabled && lingerFile != "" {
f, err := os.Create(lingerFile)
if err == nil {
- f.Close()
+ if err := f.Close(); err != nil {
+ logrus.Errorf("failed to close %s", f.Name())
+ }
} else {
logrus.Debugf("could not create linger file: %v", err)
}
@@ -296,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 {
@@ -348,9 +374,13 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
}
r, w := os.NewFile(uintptr(fds[0]), "sync host"), os.NewFile(uintptr(fds[1]), "sync child")
- defer r.Close()
- defer w.Close()
- defer w.Write([]byte("0"))
+ defer errorhandling.CloseQuiet(r)
+ defer errorhandling.CloseQuiet(w)
+ 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)
@@ -361,9 +391,9 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
var uids, gids []idtools.IDMap
username := os.Getenv("USER")
if username == "" {
- user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid()))
+ userID, err := user.LookupId(fmt.Sprintf("%d", os.Getuid()))
if err == nil {
- username = user.Username
+ username = userID.Username
}
}
mappings, err := idtools.NewIDMappings(username, username)
@@ -458,7 +488,9 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
continue
}
- syscall.Kill(int(pidC), s.(syscall.Signal))
+ if err := syscall.Kill(int(pidC), s.(syscall.Signal)); err != nil {
+ logrus.Errorf("failed to kill %d", int(pidC))
+ }
}
}()
@@ -519,17 +551,19 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st
r, w := os.NewFile(uintptr(fds[0]), "read file"), os.NewFile(uintptr(fds[1]), "write file")
- defer w.Close()
- defer r.Close()
+ defer errorhandling.CloseQuiet(w)
+ defer errorhandling.CloseQuiet(r)
if _, _, err := becomeRootInUserNS("", path, w); err != nil {
lastErr = err
continue
}
- w.Close()
+ if err := w.Close(); err != nil {
+ return false, 0, err
+ }
defer func() {
- r.Close()
+ errorhandling.CloseQuiet(r)
C.reexec_in_user_namespace_wait(-1, 0)
}()
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 06d1ac12d..d44beb3e4 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 {
@@ -475,7 +481,9 @@ func addPidNS(config *CreateConfig, g *generate.Generator) error {
func addUserNS(config *CreateConfig, g *generate.Generator) error {
if IsNS(string(config.UsernsMode)) {
- g.AddOrReplaceLinuxNamespace(spec.UserNamespace, NS(string(config.UsernsMode)))
+ if err := g.AddOrReplaceLinuxNamespace(spec.UserNamespace, NS(string(config.UsernsMode))); err != nil {
+ return err
+ }
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
@@ -483,7 +491,9 @@ func addUserNS(config *CreateConfig, g *generate.Generator) error {
}
if (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost() {
- g.AddOrReplaceLinuxNamespace(spec.UserNamespace, "")
+ if err := g.AddOrReplaceLinuxNamespace(spec.UserNamespace, ""); err != nil {
+ return err
+ }
}
return nil
}
@@ -553,6 +563,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/util/utils.go b/pkg/util/utils.go
index 9e49f08a0..fba34a337 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -12,12 +12,14 @@ import (
"github.com/BurntSushi/toml"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"golang.org/x/crypto/ssh/terminal"
)
@@ -272,16 +274,20 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
// WriteStorageConfigFile writes the configuration to a file
func WriteStorageConfigFile(storageOpts *storage.StoreOptions, storageConf string) error {
- os.MkdirAll(filepath.Dir(storageConf), 0755)
- file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
+ return err
+ }
+ storageFile, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
return errors.Wrapf(err, "cannot open %s", storageConf)
}
tomlConfiguration := getTomlStorage(storageOpts)
- defer file.Close()
- enc := toml.NewEncoder(file)
+ defer errorhandling.CloseQuiet(storageFile)
+ enc := toml.NewEncoder(storageFile)
if err := enc.Encode(tomlConfiguration); err != nil {
- os.Remove(storageConf)
+ if err := os.Remove(storageConf); err != nil {
+ logrus.Errorf("unable to remove file %s", storageConf)
+ }
return err
}
return nil
diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go
index 99c9e4f1e..af55689a6 100644
--- a/pkg/util/utils_supported.go
+++ b/pkg/util/utils_supported.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// GetRootlessRuntimeDir returns the runtime directory when running as non root
@@ -24,7 +25,9 @@ func GetRootlessRuntimeDir() (string, error) {
uid := fmt.Sprintf("%d", rootless.GetRootlessUID())
if runtimeDir == "" {
tmpDir := filepath.Join("/run", "user", uid)
- os.MkdirAll(tmpDir, 0700)
+ if err := os.MkdirAll(tmpDir, 0700); err != nil {
+ logrus.Errorf("unable to make temp dir %s", tmpDir)
+ }
st, err := os.Stat(tmpDir)
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
@@ -32,7 +35,9 @@ func GetRootlessRuntimeDir() (string, error) {
}
if runtimeDir == "" {
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("run-%s", uid))
- os.MkdirAll(tmpDir, 0700)
+ if err := os.MkdirAll(tmpDir, 0700); err != nil {
+ logrus.Errorf("unable to make temp dir %s", tmpDir)
+ }
st, err := os.Stat(tmpDir)
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 0e2ad6bbf..2bebfd406 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -25,6 +25,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/channelwriter"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
@@ -495,9 +496,19 @@ func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error {
// Commit ...
func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error {
- var newImage *image.Image
+ var (
+ newImage *image.Image
+ log []string
+ mimeType string
+ )
+ output := channelwriter.NewChannelWriter()
+ channelClose := func() {
+ if err := output.Close(); err != nil {
+ logrus.Errorf("failed to close channel writer: %q", err)
+ }
+ }
+ defer channelClose()
- output := bytes.NewBuffer([]byte{})
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
@@ -507,7 +518,6 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
return call.ReplyErrorOccurred(err.Error())
}
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
- var mimeType string
switch manifestType {
case "oci", "": //nolint
mimeType = buildah.OCIv1ImageManifest
@@ -535,6 +545,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
}
c := make(chan error)
+ defer close(c)
go func() {
newImage, err = ctr.Commit(getContext(), imageName, options)
@@ -542,48 +553,22 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
c <- err
}
c <- nil
- close(c)
}()
- var log []string
- done := false
- for {
- line, err := output.ReadString('\n')
- if err == nil {
- log = append(log, line)
- continue
- } else if err == io.EOF {
- select {
- case err := <-c:
- if err != nil {
- logrus.Errorf("reading of output during commit failed for %s", name)
- return call.ReplyErrorOccurred(err.Error())
- }
- done = true
- default:
- if !call.WantsMore() {
- break
- }
- br := iopodman.MoreResponse{
- Logs: log,
- }
- call.ReplyCommit(br)
- log = []string{}
- }
- } else {
- return call.ReplyErrorOccurred(err.Error())
- }
- if done {
- break
- }
+ // reply is the func being sent to the output forwarder. in this case it is replying
+ // with a more response struct
+ reply := func(br iopodman.MoreResponse) error {
+ return call.ReplyCommit(br)
+ }
+ log, err = forwardOutput(log, c, call.WantsMore(), output, reply)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
}
call.Continues = false
-
br := iopodman.MoreResponse{
Logs: log,
Id: newImage.ID(),
}
-
return call.ReplyCommit(br)
}
@@ -636,6 +621,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str
func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error {
var (
imageID string
+ err error
)
dockerRegistryOptions := image.DockerRegistryOptions{}
so := image.SigningOptions{}
@@ -643,8 +629,16 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error {
if call.WantsMore() {
call.Continues = true
}
- output := bytes.NewBuffer([]byte{})
+ output := channelwriter.NewChannelWriter()
+ channelClose := func() {
+ if err := output.Close(); err != nil {
+ logrus.Errorf("failed to close channel writer: %q", err)
+ }
+ }
+ defer channelClose()
c := make(chan error)
+ defer close(c)
+
go func() {
if strings.HasPrefix(name, dockerarchive.Transport.Name()+":") {
srcRef, err := alltransports.ParseImageName(name)
@@ -666,43 +660,17 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error {
}
}
c <- nil
- close(c)
}()
var log []string
- done := false
- for {
- line, err := output.ReadString('\n')
- if err == nil {
- log = append(log, line)
- continue
- } else if err == io.EOF {
- select {
- case err := <-c:
- if err != nil {
- logrus.Errorf("reading of output during pull failed for %s", name)
- return call.ReplyErrorOccurred(err.Error())
- }
- done = true
- default:
- if !call.WantsMore() {
- break
- }
- br := iopodman.MoreResponse{
- Logs: log,
- }
- call.ReplyPullImage(br)
- log = []string{}
- }
- } else {
- return call.ReplyErrorOccurred(err.Error())
- }
- if done {
- break
- }
+ reply := func(br iopodman.MoreResponse) error {
+ return call.ReplyPullImage(br)
+ }
+ log, err = forwardOutput(log, c, call.WantsMore(), output, reply)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
}
call.Continues = false
-
br := iopodman.MoreResponse{
Logs: log,
Id: imageID,
diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go
index a74105795..e8f74e6aa 100644
--- a/pkg/varlinkapi/util.go
+++ b/pkg/varlinkapi/util.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/channelwriter"
"github.com/containers/storage/pkg/archive"
)
@@ -196,3 +197,42 @@ func makePsOpts(inOpts iopodman.PsOpts) shared.PsOptions {
Sync: derefBool(inOpts.Sync),
}
}
+
+// forwardOutput is a helper method for varlink endpoints that employ both more and without
+// more. it is capable of sending updates as the output writer gets them or append them
+// all to a log. the chan error is the error from the libpod call so we can honor
+// and error event in that case.
+func forwardOutput(log []string, c chan error, wantsMore bool, output *channelwriter.Writer, reply func(br iopodman.MoreResponse) error) ([]string, error) {
+ done := false
+ for {
+ select {
+ // We need to check if the libpod func being called has returned an
+ // error yet
+ case err := <-c:
+ if err != nil {
+ return nil, err
+ }
+ done = true
+ // if no error is found, we pull what we can from the log writer and
+ // append it to log string slice
+ case line := <-output.ByteChannel:
+ log = append(log, string(line))
+ // If the end point is being used in more mode, send what we have
+ if wantsMore {
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ if err := reply(br); err != nil {
+ return nil, err
+ }
+ // "reset" the log to empty because we are sending what we
+ // get as we get it
+ log = []string{}
+ }
+ }
+ if done {
+ break
+ }
+ }
+ return log, nil
+}