summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/client.go2
-rw-r--r--pkg/adapter/client_unix.go8
-rw-r--r--pkg/adapter/containers.go26
-rw-r--r--pkg/adapter/containers_remote.go44
-rw-r--r--pkg/adapter/network.go68
-rw-r--r--pkg/adapter/pods.go7
-rw-r--r--pkg/cgroups/cgroups_supported.go62
-rw-r--r--pkg/cgroups/cgroups_unsupported.go6
-rw-r--r--pkg/lookup/lookup.go30
-rw-r--r--pkg/netns/netns_linux.go35
-rw-r--r--pkg/network/devices.go16
-rw-r--r--pkg/network/files.go24
-rw-r--r--pkg/rootless/rootless_linux.go2
-rw-r--r--pkg/spec/createconfig.go2
-rw-r--r--pkg/spec/spec_test.go38
-rw-r--r--pkg/spec/storage.go41
-rw-r--r--pkg/util/utils.go16
-rw-r--r--pkg/varlinkapi/attach.go33
-rw-r--r--pkg/varlinkapi/containers.go14
-rw-r--r--pkg/varlinkapi/pods.go2
20 files changed, 377 insertions, 99 deletions
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go
index da6ff5fd0..1805c758d 100644
--- a/pkg/adapter/client.go
+++ b/pkg/adapter/client.go
@@ -35,7 +35,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) {
if len(r.cmd.RemoteUserName) < 1 {
return nil, errors.New("you must provide a username when providing a remote host name")
}
- rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false}
+ rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port}
remoteEndpoint, err = newBridgeConnection("", &rc, r.cmd.LogLevel)
// if the user has a config file with connections in it
} else if len(remoteConfigConnections.Connections) > 0 {
diff --git a/pkg/adapter/client_unix.go b/pkg/adapter/client_unix.go
index 4781acd06..a7bc7c1c0 100644
--- a/pkg/adapter/client_unix.go
+++ b/pkg/adapter/client_unix.go
@@ -10,7 +10,11 @@ import (
)
func formatDefaultBridge(remoteConn *remoteclientconfig.RemoteConnection, logLevel string) string {
+ port := remoteConn.Port
+ if port == 0 {
+ port = 22
+ }
return fmt.Sprintf(
- `ssh -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
- remoteConn.Username, remoteConn.Destination, logLevel)
+ `ssh -p %d -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
+ port, remoteConn.Username, remoteConn.Destination, logLevel)
}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 41607145d..47db5c0dc 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -341,12 +341,7 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
// if the container was created as part of a pod, also start its dependencies, if any.
if err := ctr.Start(ctx, c.IsSet("pod")); err != nil {
// This means the command did not exist
- exitCode = 127
- e := strings.ToLower(err.Error())
- if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") || strings.Contains(e, "file not found") || strings.Contains(e, "no such file or directory") {
- exitCode = 126
- }
- return exitCode, err
+ return define.ExitCode(err), err
}
fmt.Printf("%s\n", ctr.ID())
@@ -401,21 +396,14 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
// Do not perform cleanup, or wait for container exit code
// Just exit immediately
if errors.Cause(err) == define.ErrDetach {
- exitCode = 0
- return exitCode, nil
- }
- // This means the command did not exist
- exitCode = 127
- e := strings.ToLower(err.Error())
- if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") {
- exitCode = 126
+ return 0, nil
}
if c.IsSet("rm") {
if deleteError := r.Runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil {
logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID())
}
}
- return exitCode, err
+ return define.ExitCode(err), err
}
if ecode, err := ctr.Wait(); err != nil {
@@ -424,7 +412,7 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited)
if err != nil {
logrus.Errorf("Cannot get exit code: %v", err)
- exitCode = 127
+ exitCode = define.ExecErrorCodeNotFound
} else {
exitCode = event.ContainerExitCode
}
@@ -576,7 +564,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
// Start will start a container
func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigProxy bool) (int, error) {
var (
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
lastError error
)
@@ -636,7 +624,7 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP
event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited)
if err != nil {
logrus.Errorf("Cannot get exit code: %v", err)
- exitCode = 127
+ exitCode = define.ExecErrorCodeNotFound
} else {
exitCode = event.ContainerExitCode
}
@@ -914,7 +902,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
cmd []string
)
// default invalid command exit code
- ec := 125
+ ec := define.ExecErrorCodeGeneric
if cli.Latest {
if ctr, err = r.GetLatestContainer(); err != nil {
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 590fef43f..6cecb92da 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -464,19 +464,27 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
results := shared.NewIntermediateLayer(&c.PodmanCommand, true)
cid, err := iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink())
if err != nil {
- return 0, err
+ return exitCode, err
}
if c.Bool("detach") {
- _, err := iopodman.StartContainer().Call(r.Conn, cid)
+ if _, err := iopodman.StartContainer().Call(r.Conn, cid); err != nil {
+ return exitCode, err
+ }
fmt.Println(cid)
- return 0, err
+ return 0, nil
+ }
+ inputStream := os.Stdin
+ // If -i is not set, clear stdin
+ if !c.Bool("interactive") {
+ inputStream = nil
}
- errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid, true, c.String("detach-keys"))
+ exitChan, errChan, err := r.attach(ctx, inputStream, os.Stdout, cid, true, c.String("detach-keys"))
if err != nil {
- return 0, err
+ return exitCode, err
}
+ exitCode = <-exitChan
finalError := <-errChan
- return 0, finalError
+ return exitCode, finalError
}
func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
@@ -572,7 +580,7 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er
return err
}
}
- errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0], false, c.DetachKeys)
+ _, errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0], false, c.DetachKeys)
if err != nil {
return err
}
@@ -669,7 +677,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigProxy bool) (int, error) {
var (
finalErr error
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
)
// TODO Figure out how to deal with exit codes
inputStream := os.Stdin
@@ -686,12 +694,13 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP
}
// start.go makes sure that if attach, there can be only one ctr
if c.Attach {
- errChan, err := r.attach(ctx, inputStream, os.Stdout, containerIDs[0], true, c.DetachKeys)
+ exitChan, errChan, err := r.attach(ctx, inputStream, os.Stdout, containerIDs[0], true, c.DetachKeys)
if err != nil {
return exitCode, nil
}
+ exitCode := <-exitChan
err = <-errChan
- return 0, err
+ return exitCode, err
}
// TODO the notion of starting a pod container and its deps still needs to be worked through
@@ -710,13 +719,13 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP
return exitCode, finalErr
}
-func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool, detachKeys string) (chan error, error) {
+func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool, detachKeys string) (chan int, chan error, error) {
var (
oldTermState *term.State
)
spec, err := r.Spec(cid)
if err != nil {
- return nil, err
+ return nil, nil, err
}
resize := make(chan remotecommand.TerminalSize, 5)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
@@ -726,7 +735,7 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s
if haveTerminal && spec.Process.Terminal {
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
if err != nil {
- return nil, err
+ return nil, nil, err
}
defer cancel()
defer restoreTerminal(oldTermState)
@@ -738,7 +747,7 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s
reply, err := iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start)
if err != nil {
restoreTerminal(oldTermState)
- return nil, err
+ return nil, nil, err
}
// See if the server accepts the upgraded connection or returns an error
@@ -746,11 +755,12 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s
if err != nil {
restoreTerminal(oldTermState)
- return nil, err
+ return nil, nil, err
}
- errChan := configureVarlinkAttachStdio(r.Conn.Reader, r.Conn.Writer, stdin, stdout, oldTermState, resize, nil)
- return errChan, nil
+ ecChan := make(chan int, 1)
+ errChan := configureVarlinkAttachStdio(r.Conn.Reader, r.Conn.Writer, stdin, stdout, oldTermState, resize, ecChan)
+ return ecChan, errChan, nil
}
// PauseContainers pauses container(s) based on CLI inputs.
diff --git a/pkg/adapter/network.go b/pkg/adapter/network.go
index e4a160767..d407984ce 100644
--- a/pkg/adapter/network.go
+++ b/pkg/adapter/network.go
@@ -3,9 +3,9 @@
package adapter
import (
+ "context"
"encoding/json"
"fmt"
- "github.com/containers/libpod/pkg/util"
"io/ioutil"
"os"
"path/filepath"
@@ -14,6 +14,7 @@ import (
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/network"
+ "github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
@@ -85,16 +86,69 @@ func (r *LocalRuntime) NetworkInspect(cli *cliconfig.NetworkInspectValues) error
}
// NetworkRemove deletes one or more CNI networks
-func (r *LocalRuntime) NetworkRemove(cli *cliconfig.NetworkRmValues) error {
+func (r *LocalRuntime) NetworkRemove(ctx context.Context, cli *cliconfig.NetworkRmValues) ([]string, map[string]error, error) {
+ var (
+ networkRmSuccesses []string
+ lastError error
+ )
+ networkRmErrors := make(map[string]error)
+
for _, name := range cli.InputArgs {
- cniPath, err := network.GetCNIConfigPathByName(name)
+ containers, err := r.GetAllContainers()
if err != nil {
- return err
+ return networkRmSuccesses, networkRmErrors, err
}
- if err := os.Remove(cniPath); err != nil {
- return err
+ if err := r.removeNetwork(ctx, name, containers, cli.Force); err != nil {
+ if lastError != nil {
+ networkRmErrors[name] = lastError
+ }
+ lastError = err
+ } else {
+ networkRmSuccesses = append(networkRmSuccesses, fmt.Sprintf("Deleted: %s\n", name))
+ }
+ }
+ return networkRmSuccesses, networkRmErrors, lastError
+}
+
+// removeNetwork removes a single network and its containers given a force bool
+func (r *LocalRuntime) removeNetwork(ctx context.Context, name string, containers []*Container, force bool) error {
+ cniPath, err := network.GetCNIConfigPathByName(name)
+ if err != nil {
+ return err
+ }
+ // We need to iterate containers looking to see if they belong to the given network
+ for _, c := range containers {
+ if util.StringInSlice(name, c.Config().Networks) {
+ // if user passes force, we nuke containers
+ if force {
+ if err := r.RemoveContainer(ctx, c.Container, true, true); err != nil {
+ return err
+ }
+ } else {
+ // Without the the force option, we return an error
+ return errors.Errorf("%q has associated containers with it. use -f to forcibly delete containers", name)
+ }
+
}
- fmt.Printf("Deleted: %s\n", name)
+ }
+ // Before we delete the configuration file, we need to make sure we can read and parse
+ // it to get the network interface name so we can remove that too
+ interfaceName, err := network.GetInterfaceNameFromConfig(cniPath)
+ if err != nil {
+ return errors.Wrapf(err, "failed to find network interface name in %q", cniPath)
+ }
+ liveNetworkNames, err := network.GetLiveNetworkNames()
+ if err != nil {
+ return errors.Wrapf(err, "failed to get live network names")
+ }
+ if util.StringInSlice(interfaceName, liveNetworkNames) {
+ if err := network.RemoveInterface(interfaceName); err != nil {
+ return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName)
+ }
+ }
+ // Remove the configuration file
+ if err := os.Remove(cniPath); err != nil {
+ return errors.Wrapf(err, "failed to remove network configuration file %q", cniPath)
}
return nil
}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 70293a2c5..c8d57e2a2 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -467,8 +467,15 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa
return nil, errors.Wrapf(err, "unable to read %s as YAML", yamlFile)
}
+ if podYAML.Kind != "Pod" {
+ return nil, errors.Errorf("Invalid YAML kind: %s. Pod is the only supported Kubernetes YAML kind", podYAML.Kind)
+ }
+
// check for name collision between pod and container
podName := podYAML.ObjectMeta.Name
+ if podName == "" {
+ return nil, errors.Errorf("pod does not have a name")
+ }
for _, n := range podYAML.Spec.Containers {
if n.Name == podName {
fmt.Printf("a container exists with the same name (%s) as the pod in your YAML file; changing pod name to %s_pod\n", podName, podName)
diff --git a/pkg/cgroups/cgroups_supported.go b/pkg/cgroups/cgroups_supported.go
index fcd44dfc8..2a36777d4 100644
--- a/pkg/cgroups/cgroups_supported.go
+++ b/pkg/cgroups/cgroups_supported.go
@@ -3,8 +3,15 @@
package cgroups
import (
+ "bufio"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
"sync"
"syscall"
+
+ "github.com/pkg/errors"
)
var (
@@ -25,3 +32,58 @@ func IsCgroup2UnifiedMode() (bool, error) {
})
return isUnified, isUnifiedErr
}
+
+// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
+// current cgroup.
+func UserOwnsCurrentSystemdCgroup() (bool, error) {
+ uid := os.Geteuid()
+
+ cgroup2, err := IsCgroup2UnifiedMode()
+ if err != nil {
+ return false, err
+ }
+
+ f, err := os.Open("/proc/self/cgroup")
+ if err != nil {
+ return false, errors.Wrapf(err, "open file /proc/self/cgroup")
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ line := scanner.Text()
+ parts := strings.SplitN(line, ":", 3)
+
+ if len(parts) < 3 {
+ continue
+ }
+
+ var cgroupPath string
+
+ if cgroup2 {
+ cgroupPath = filepath.Join(cgroupRoot, parts[2])
+ } else {
+ if parts[1] != "name=systemd" {
+ continue
+ }
+ cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
+ }
+
+ st, err := os.Stat(cgroupPath)
+ if err != nil {
+ return false, err
+ }
+ s := st.Sys()
+ if s == nil {
+ return false, fmt.Errorf("error stat cgroup path %s", cgroupPath)
+ }
+
+ if int(s.(*syscall.Stat_t).Uid) != uid {
+ return false, nil
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return false, errors.Wrapf(err, "parsing file /proc/self/cgroup")
+ }
+ return true, nil
+}
diff --git a/pkg/cgroups/cgroups_unsupported.go b/pkg/cgroups/cgroups_unsupported.go
index 9dc196e42..cd140fbf3 100644
--- a/pkg/cgroups/cgroups_unsupported.go
+++ b/pkg/cgroups/cgroups_unsupported.go
@@ -6,3 +6,9 @@ package cgroups
func IsCgroup2UnifiedMode() (bool, error) {
return false, nil
}
+
+// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
+// current cgroup.
+func UserOwnsCurrentSystemdCgroup() (bool, error) {
+ return false, nil
+}
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go
index 70b97144f..a249dd753 100644
--- a/pkg/lookup/lookup.go
+++ b/pkg/lookup/lookup.go
@@ -29,17 +29,30 @@ func GetUserGroupInfo(containerMount, containerUser string, override *Overrides)
defaultExecUser *user.ExecUser
err error
)
- passwdPath := etcpasswd
- groupPath := etcgroup
if override != nil {
// Check for an override /etc/passwd path
if override.ContainerEtcPasswdPath != "" {
- passwdPath = override.ContainerEtcPasswdPath
+ passwdDest = override.ContainerEtcPasswdPath
}
// Check for an override for /etc/group path
if override.ContainerEtcGroupPath != "" {
- groupPath = override.ContainerEtcGroupPath
+ groupDest = override.ContainerEtcGroupPath
+ }
+ }
+
+ if passwdDest == "" {
+ // Make sure the /etc/passwd destination is not a symlink to something naughty
+ if passwdDest, err = securejoin.SecureJoin(containerMount, etcpasswd); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ }
+ if groupDest == "" {
+ // Make sure the /etc/group destination is not a symlink to something naughty
+ if groupDest, err = securejoin.SecureJoin(containerMount, etcgroup); err != nil {
+ logrus.Debug(err)
+ return nil, err
}
}
@@ -56,15 +69,6 @@ func GetUserGroupInfo(containerMount, containerUser string, override *Overrides)
}
- // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty
- if passwdDest, err = securejoin.SecureJoin(containerMount, passwdPath); err != nil {
- logrus.Debug(err)
- return nil, err
- }
- if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
- logrus.Debug(err)
- return nil, err
- }
return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest)
}
diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go
index 1d6fb873c..a62296549 100644
--- a/pkg/netns/netns_linux.go
+++ b/pkg/netns/netns_linux.go
@@ -23,23 +23,42 @@ import (
"fmt"
"os"
"path"
+ "path/filepath"
"runtime"
"strings"
"sync"
"github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/util"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
-const nsRunDir = "/var/run/netns"
+// get NSRunDir returns the dir of where to create the netNS. When running
+// rootless, it needs to be at a location writable by user.
+func getNSRunDir() (string, error) {
+ if rootless.IsRootless() {
+ rootlessDir, err := util.GetRuntimeDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(rootlessDir, "netns"), nil
+ }
+ return "/var/run/netns", nil
+}
// NewNS creates a new persistent (bind-mounted) network namespace and returns
// an object representing that namespace, without switching to it.
func NewNS() (ns.NetNS, error) {
+ nsRunDir, err := getNSRunDir()
+ if err != nil {
+ return nil, err
+ }
+
b := make([]byte, 16)
- _, err := rand.Reader.Read(b)
+ _, err = rand.Reader.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
@@ -127,14 +146,15 @@ func NewNS() (ns.NetNS, error) {
// Put this thread back to the orig ns, since it might get reused (pre go1.10)
defer func() {
if err := origNS.Set(); err != nil {
- logrus.Errorf("unable to set namespace: %q", err)
+ logrus.Warnf("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
- // are no threads in the ns.
- err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
+ // are no threads in the ns. Make this a shared mount; it needs to be
+ // back-propogated to the host
+ err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND|unix.MS_SHARED|unix.MS_REC, "")
if err != nil {
err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err)
}
@@ -150,6 +170,11 @@ func NewNS() (ns.NetNS, error) {
// UnmountNS unmounts the NS held by the netns object
func UnmountNS(ns ns.NetNS) error {
+ nsRunDir, err := getNSRunDir()
+ if err != nil {
+ return err
+ }
+
nsPath := ns.Path()
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
if strings.HasPrefix(nsPath, nsRunDir) {
diff --git a/pkg/network/devices.go b/pkg/network/devices.go
index 26101b6f7..85068a7d1 100644
--- a/pkg/network/devices.go
+++ b/pkg/network/devices.go
@@ -2,8 +2,10 @@ package network
import (
"fmt"
- "github.com/containers/libpod/pkg/util"
+ "os/exec"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/containers/libpod/utils"
"github.com/sirupsen/logrus"
)
@@ -39,3 +41,15 @@ func GetFreeDeviceName() (string, error) {
}
return deviceName, nil
}
+
+// RemoveInterface removes an interface by the given name
+func RemoveInterface(interfaceName string) error {
+ // Make sure we have the ip command on the system
+ ipPath, err := exec.LookPath("ip")
+ if err != nil {
+ return err
+ }
+ // Delete the network interface
+ _, err = utils.ExecCmd(ipPath, []string{"link", "del", interfaceName}...)
+ return err
+}
diff --git a/pkg/network/files.go b/pkg/network/files.go
index 80fde5e17..d55ec2dfd 100644
--- a/pkg/network/files.go
+++ b/pkg/network/files.go
@@ -86,6 +86,7 @@ func GetNetworksFromFilesystem() ([]*allocator.Net, error) {
return nil, err
}
cniNetworks = append(cniNetworks, &ipamConf)
+ break
}
}
}
@@ -105,3 +106,26 @@ func GetNetworkNamesFromFileSystem() ([]string, error) {
}
return networkNames, nil
}
+
+// GetInterfaceNameFromConfig returns the interface name for the bridge plugin
+func GetInterfaceNameFromConfig(path string) (string, error) {
+ var name string
+ conf, err := libcni.ConfListFromFile(path)
+ if err != nil {
+ return "", err
+ }
+ for _, cniplugin := range conf.Plugins {
+ if cniplugin.Network.Type == "bridge" {
+ plugin := make(map[string]interface{})
+ if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
+ return "", err
+ }
+ name = plugin["bridge"].(string)
+ break
+ }
+ }
+ if len(name) == 0 {
+ return "", errors.New("unable to find interface name for network")
+ }
+ return name, nil
+}
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index ecb84f6a9..6f6239e5f 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -365,7 +365,7 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) {
}
mappings, err := idtools.NewIDMappings(username, username)
if err != nil {
- logrus.Warnf("cannot find mappings for user %s: %v", username, err)
+ logrus.Errorf("cannot find mappings for user %s: %v", username, err)
} else {
uids = mappings.UIDs()
gids = mappings.GIDs()
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index c17172016..7c3195be4 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -275,7 +275,7 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
- postConfigureNetNS := c.NetMode.IsSlirp4netns() || (hasUserns && !c.UsernsMode.IsHost())
+ postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
diff --git a/pkg/spec/spec_test.go b/pkg/spec/spec_test.go
index 0abff491b..2f91e1b21 100644
--- a/pkg/spec/spec_test.go
+++ b/pkg/spec/spec_test.go
@@ -4,6 +4,8 @@ import (
"runtime"
"testing"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/sysinfo"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
@@ -26,14 +28,30 @@ func makeTestCreateConfig() *CreateConfig {
return cc
}
-// TestPIDsLimit verifies the given pid-limit is correctly defined in the spec
-func TestPIDsLimit(t *testing.T) {
+func doCommonSkipChecks(t *testing.T) {
// The default configuration of podman enables seccomp, which is not available on non-Linux systems.
// Thus, any tests that use the default seccomp setting would fail.
// Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result.
if runtime.GOOS != "linux" {
t.Skip("seccomp, which is enabled by default, is only supported on Linux")
}
+
+ if rootless.IsRootless() {
+ isCgroupV2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ if !isCgroupV2 {
+ t.Skip("cgroups v1 cannot be used when rootless")
+ }
+ }
+}
+
+// TestPIDsLimit verifies the given pid-limit is correctly defined in the spec
+func TestPIDsLimit(t *testing.T) {
+ doCommonSkipChecks(t)
+
if !sysInfo.PidsLimit {
t.Skip("running test not supported by the host system")
}
@@ -50,12 +68,8 @@ func TestPIDsLimit(t *testing.T) {
// TestBLKIOWeightDevice verifies the given blkio weight is correctly set in the
// spec.
func TestBLKIOWeightDevice(t *testing.T) {
- // The default configuration of podman enables seccomp, which is not available on non-Linux systems.
- // Thus, any tests that use the default seccomp setting would fail.
- // Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result.
- if runtime.GOOS != "linux" {
- t.Skip("seccomp, which is enabled by default, is only supported on Linux")
- }
+ doCommonSkipChecks(t)
+
if !sysInfo.BlkioWeightDevice {
t.Skip("running test not supported by the host system")
}
@@ -75,12 +89,8 @@ func TestBLKIOWeightDevice(t *testing.T) {
// TestMemorySwap verifies that the given swap memory limit is correctly set in
// the spec.
func TestMemorySwap(t *testing.T) {
- // The default configuration of podman enables seccomp, which is not available on non-Linux systems.
- // Thus, any tests that use the default seccomp setting would fail.
- // Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result.
- if runtime.GOOS != "linux" {
- t.Skip("seccomp, which is enabled by default, is only supported on Linux")
- }
+ doCommonSkipChecks(t)
+
if !sysInfo.SwapLimit {
t.Skip("running test not supported by the host system")
}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index bc0eaad6d..93919dd0a 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -168,6 +168,9 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
if _, ok := baseMounts[dest]; ok {
continue
}
+ if _, ok := baseVolumes[dest]; ok {
+ continue
+ }
localOpts := options
if dest == "/run" {
localOpts = append(localOpts, "noexec", "size=65536k")
@@ -220,6 +223,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
// volumes, and return a list of them.
// Conflicts are resolved simply - the last container specified wins.
// Container names may be suffixed by mount options after a colon.
+// TODO: We should clean these paths if possible
func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
// Both of these are maps of mount destination to mount type.
// We ensure that each destination is only mounted to once in this way.
@@ -389,7 +393,7 @@ func getBindMount(args []string) (spec.Mount, error) {
Type: TypeBind,
}
- var setSource, setDest, setRORW, setSuid, setDev, setExec bool
+ var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
for _, val := range args {
kv := strings.Split(val, "=")
@@ -465,8 +469,24 @@ func getBindMount(args []string) (spec.Mount, error) {
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err
}
- newMount.Destination = kv[1]
+ newMount.Destination = filepath.Clean(kv[1])
setDest = true
+ case "relabel":
+ if setRelabel {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
+ }
+ setRelabel = true
+ if len(kv) != 2 {
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
+ switch kv[1] {
+ case "private":
+ newMount.Options = append(newMount.Options, "z")
+ case "shared":
+ newMount.Options = append(newMount.Options, "Z")
+ default:
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
+ }
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
}
@@ -543,7 +563,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err
}
- newMount.Destination = kv[1]
+ newMount.Destination = filepath.Clean(kv[1])
setDest = true
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
@@ -607,7 +627,7 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return nil, err
}
- newVolume.Dest = kv[1]
+ newVolume.Dest = filepath.Clean(kv[1])
setDest = true
default:
return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
@@ -662,10 +682,12 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
return nil, nil, err
}
+ cleanDest := filepath.Clean(dest)
+
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
// This is not a named volume
newMount := spec.Mount{
- Destination: dest,
+ Destination: cleanDest,
Type: string(TypeBind),
Source: src,
Options: options,
@@ -678,7 +700,7 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
// This is a named volume
newNamedVol := new(libpod.ContainerNamedVolume)
newNamedVol.Name = src
- newNamedVol.Dest = dest
+ newNamedVol.Dest = cleanDest
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
@@ -703,10 +725,11 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string
}
for vol := range config.BuiltinImgVolumes {
+ cleanDest := filepath.Clean(vol)
if config.ImageVolumeType == "tmpfs" {
// Tmpfs image volumes are handled as mounts
mount := spec.Mount{
- Destination: vol,
+ Destination: cleanDest,
Source: TypeTmpfs,
Type: TypeTmpfs,
Options: []string{"rprivate", "rw", "nodev"},
@@ -716,7 +739,7 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string
namedVolume := new(libpod.ContainerNamedVolume)
namedVolume.Name = stringid.GenerateNonCryptoID()
namedVolume.Options = []string{"rprivate", "rw", "nodev"}
- namedVolume.Dest = vol
+ namedVolume.Dest = cleanDest
volumes[vol] = namedVolume
}
}
@@ -744,7 +767,7 @@ func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) {
}
mount := spec.Mount{
- Destination: destPath,
+ Destination: filepath.Clean(destPath),
Type: string(TypeTmpfs),
Options: options,
Source: string(TypeTmpfs),
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 67cb5c24b..edcad1d1b 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -424,3 +424,19 @@ func ValidatePullType(pullType string) (PullType, error) {
return PullImageMissing, errors.Errorf("invalid pull type %q", pullType)
}
}
+
+// ExitCode reads the error message when failing to executing container process
+// and then returns 0 if no error, 126 if command does not exist, or 127 for
+// all other errors
+func ExitCode(err error) int {
+ if err == nil {
+ return 0
+ }
+ e := strings.ToLower(err.Error())
+ if strings.Contains(e, "file not found") ||
+ strings.Contains(e, "no such file or directory") {
+ return 127
+ }
+
+ return 126
+}
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index 1f8d48eb9..f8557ae0c 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -9,7 +9,9 @@ import (
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/client-go/tools/remotecommand"
)
@@ -63,7 +65,9 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
}
// ACK the client upgrade request
- call.ReplyAttach()
+ if err := call.ReplyAttach(); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
reader, writer, _, pw, streams := setupStreams(call)
@@ -79,11 +83,36 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan)
}
+ exitCode := define.ExitCode(finalErr)
if finalErr != define.ErrDetach && finalErr != nil {
logrus.Error(finalErr)
+ } else {
+ if ecode, err := ctr.Wait(); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ // Check events
+ event, err := i.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited)
+ if err != nil {
+ logrus.Errorf("Cannot get exit code: %v", err)
+ exitCode = define.ExecErrorCodeNotFound
+ } else {
+ exitCode = event.ContainerExitCode
+ }
+ } else {
+ exitCode = define.ExitCode(err)
+ }
+ } else {
+ exitCode = int(ecode)
+ }
+ }
+
+ if ctr.AutoRemove() {
+ err := i.Runtime.RemoveContainer(getContext(), ctr, false, false)
+ if err != nil {
+ logrus.Errorf("Failed to remove container %s: %s", ctr.ID(), err.Error())
+ }
}
- if err = virtwriter.HangUp(writer, 0); err != nil {
+ if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil {
logrus.Errorf("Failed to HANG-UP attach to %s: %s", ctr.ID(), err.Error())
}
return call.Writer.Flush()
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 2dcdbc089..93f9d4fe3 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -319,12 +319,14 @@ func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath str
// GetContainerStats ...
func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error {
- cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
- if rootless.IsRootless() && !cgroupv2 {
- return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats")
+ if rootless.IsRootless() {
+ cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if !cgroupv2 {
+ return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats")
+ }
}
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index c0fd8b1f7..9b659f66b 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -5,12 +5,12 @@ package varlinkapi
import (
"encoding/json"
"fmt"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
"syscall"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter/shortcuts"
)
// CreatePod ...