summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/containers_prune.go4
-rw-r--r--cmd/podman/cp.go27
-rw-r--r--cmd/podman/main.go8
-rw-r--r--cmd/podman/main_local.go6
-rw-r--r--cmd/podman/network_rm.go18
-rw-r--r--cmd/podman/pause.go4
-rw-r--r--cmd/podman/restart.go4
-rw-r--r--cmd/podman/unpause.go4
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/podman-network-rm.1.md19
-rw-r--r--libpod/container_api.go13
-rw-r--r--libpod/container_internal.go11
-rw-r--r--libpod/define/exec_codes.go18
-rw-r--r--libpod/oci_attach_linux.go7
-rw-r--r--libpod/pod_api.go12
-rw-r--r--pkg/adapter/containers.go26
-rw-r--r--pkg/adapter/containers_remote.go39
-rw-r--r--pkg/adapter/network.go68
-rw-r--r--pkg/network/devices.go16
-rw-r--r--pkg/network/files.go24
-rw-r--r--pkg/util/utils.go16
-rw-r--r--pkg/varlinkapi/attach.go29
-rw-r--r--pkg/varlinkapi/containers.go14
-rw-r--r--pkg/varlinkapi/pods.go2
-rw-r--r--test/e2e/cp_test.go4
-rw-r--r--test/e2e/run_exit_test.go15
-rw-r--r--test/e2e/run_selinux_test.go2
-rw-r--r--test/system/030-run.bats1
-rw-r--r--test/system/065-cp.bats17
30 files changed, 312 insertions, 119 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index bf88e853b..e0ce202cc 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -280,6 +280,7 @@ type NetworkListValues struct {
type NetworkRmValues struct {
PodmanCommand
+ Force bool
}
type NetworkInspectValues struct {
diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go
index b8a84a0e3..3d0fef37d 100644
--- a/cmd/podman/containers_prune.go
+++ b/cmd/podman/containers_prune.go
@@ -53,7 +53,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error {
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
if len(c.InputArgs) > 1 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
} else {
exitCode = 1
}
@@ -61,7 +61,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error {
return err
}
if len(failures) > 0 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
}
return printCmdResults(ok, failures)
}
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
index 5e1ca8312..661d0a530 100644
--- a/cmd/podman/cp.go
+++ b/cmd/podman/cp.go
@@ -52,7 +52,7 @@ func init() {
cpCommand.Command = _cpCommand
flags := cpCommand.Flags()
flags.BoolVar(&cpCommand.Extract, "extract", false, "Extract the tar file into the destination directory.")
- flags.BoolVar(&cpCommand.Pause, "pause", false, "Pause the container while copying")
+ flags.BoolVar(&cpCommand.Pause, "pause", true, "Pause the container while copying")
cpCommand.SetHelpTemplate(HelpTemplate())
cpCommand.SetUsageTemplate(UsageTemplate())
}
@@ -147,7 +147,6 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
- var glob []string
if isFromHostToCtr {
if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol {
path, err := pathWithVolumeMount(ctr, runtime, volDestName, volName, destPath)
@@ -209,13 +208,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
srcPath = cleanedPath
}
}
- glob, err = filepath.Glob(srcPath)
- if err != nil {
- return errors.Wrapf(err, "invalid glob %q", srcPath)
- }
- if len(glob) == 0 {
- glob = append(glob, srcPath)
- }
+
if !filepath.IsAbs(destPath) {
dir, err := os.Getwd()
if err != nil {
@@ -224,19 +217,11 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
destPath = filepath.Join(dir, destPath)
}
- var lastError error
- for _, src := range glob {
- if src == "-" {
- src = os.Stdin.Name()
- extract = true
- }
- err := copy(src, destPath, dest, idMappingOpts, &destOwner, extract, isFromHostToCtr)
- if lastError != nil {
- logrus.Error(lastError)
- }
- lastError = err
+ if src == "-" {
+ srcPath = os.Stdin.Name()
+ extract = true
}
- return lastError
+ return copy(srcPath, destPath, dest, idMappingOpts, &destOwner, extract, isFromHostToCtr)
}
func getUser(mountPoint string, userspec string) (specs.User, error) {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 2b808b2bc..b83ccd9a5 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
_ "github.com/containers/libpod/pkg/hooks/0.1.0"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/version"
@@ -20,7 +21,7 @@ import (
// This is populated by the Makefile from the VERSION file
// in the repository
var (
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
Ctx context.Context
span opentracing.Span
closer io.Closer
@@ -152,11 +153,12 @@ func main() {
if err := rootCmd.Execute(); err != nil {
outputError(err)
} else {
- // The exitCode modified from 125, indicates an application
+ // The exitCode modified from define.ExecErrorCodeGeneric,
+ // indicates an application
// running inside of a container failed, as opposed to the
// podman command failed. Must exit with that exit code
// otherwise command exited correctly.
- if exitCode == 125 {
+ if exitCode == define.ExecErrorCodeGeneric {
exitCode = 0
}
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
index cad256615..08d7ccaf4 100644
--- a/cmd/podman/main_local.go
+++ b/cmd/podman/main_local.go
@@ -178,9 +178,9 @@ func setupRootless(cmd *cobra.Command, args []string) error {
if !ownsCgroup {
unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
- conf, err := runtime.GetConfig()
- if err != nil {
- return err
+ conf, err2 := runtime.GetConfig()
+ if err2 != nil {
+ return err2
}
if conf.CgroupManager == libpod.SystemdCgroupsManager {
logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err)
diff --git a/cmd/podman/network_rm.go b/cmd/podman/network_rm.go
index 50bd48cea..41e5dbdab 100644
--- a/cmd/podman/network_rm.go
+++ b/cmd/podman/network_rm.go
@@ -3,10 +3,13 @@
package main
import (
+ "fmt"
+
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -31,6 +34,8 @@ func init() {
networkrmCommand.Command = _networkrmCommand
networkrmCommand.SetHelpTemplate(HelpTemplate())
networkrmCommand.SetUsageTemplate(UsageTemplate())
+ flags := networkrmCommand.Flags()
+ flags.BoolVarP(&networkrmCommand.Force, "force", "f", false, "remove any containers using network")
}
func networkrmCmd(c *cliconfig.NetworkRmValues) error {
@@ -40,9 +45,18 @@ func networkrmCmd(c *cliconfig.NetworkRmValues) error {
if len(c.InputArgs) < 1 {
return errors.Errorf("at least one network name is required")
}
- runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil {
return err
}
- return runtime.NetworkRemove(c)
+ deletes, rmErrors, lastErr := runtime.NetworkRemove(getContext(), c)
+ for _, d := range deletes {
+ fmt.Println(d)
+ }
+ // we only want to print errors if there is more
+ // than one
+ for network, removalErr := range rmErrors {
+ logrus.Errorf("unable to remove %q: %q", network, removalErr)
+ }
+ return lastErr
}
diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go
index 3a8f4edb5..247a480e3 100644
--- a/cmd/podman/pause.go
+++ b/cmd/podman/pause.go
@@ -56,7 +56,7 @@ func pauseCmd(c *cliconfig.PauseValues) error {
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
if len(c.InputArgs) > 1 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
} else {
exitCode = 1
}
@@ -64,7 +64,7 @@ func pauseCmd(c *cliconfig.PauseValues) error {
return err
}
if len(failures) > 0 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
}
return printCmdResults(ok, failures)
}
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index 494a9ec06..c97fb0dc1 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -61,7 +61,7 @@ func restartCmd(c *cliconfig.RestartValues) error {
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
if len(c.InputArgs) > 1 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
} else {
exitCode = 1
}
@@ -69,7 +69,7 @@ func restartCmd(c *cliconfig.RestartValues) error {
return err
}
if len(failures) > 0 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
}
return printCmdResults(ok, failures)
}
diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go
index 382b64e97..ae24b0e66 100644
--- a/cmd/podman/unpause.go
+++ b/cmd/podman/unpause.go
@@ -55,7 +55,7 @@ func unpauseCmd(c *cliconfig.UnpauseValues) error {
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
if len(c.InputArgs) > 1 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
} else {
exitCode = 1
}
@@ -63,7 +63,7 @@ func unpauseCmd(c *cliconfig.UnpauseValues) error {
return err
}
if len(failures) > 0 {
- exitCode = 125
+ exitCode = define.ExecErrorCodeGeneric
}
return printCmdResults(ok, failures)
}
diff --git a/completions/bash/podman b/completions/bash/podman
index 041703810..4bc387871 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1032,6 +1032,8 @@ _podman_network_rm() {
local options_with_args="
"
local boolean_options="
+ --force
+ -f
--help
-h
"
diff --git a/docs/podman-network-rm.1.md b/docs/podman-network-rm.1.md
index c95c93cd8..c71f0d8fd 100644
--- a/docs/podman-network-rm.1.md
+++ b/docs/podman-network-rm.1.md
@@ -9,13 +9,26 @@ podman\-network\-rm - Remove one or more CNI networks
## DESCRIPTION
Delete one or more Podman networks.
+## OPTIONS
+**--force**, **-f**
+
+The `force` option will remove all containers that use the named network. If the container is
+running, the container will be stopped and removed.
+
## EXAMPLE
-Delete the `podman9` network
+Delete the `cni-podman9` network
+
+```
+# podman network rm cni-podman9
+Deleted: cni-podman9
+```
+
+Delete the `fred` network and all containers associated with the network.
```
-# podman network rm podman
-Deleted: podman9
+# podman network rm -f fred
+Deleted: fred
```
## SEE ALSO
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 9bf97c5d4..4f0d5301c 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -216,8 +216,8 @@ func (c *Container) Kill(signal uint) error {
}
// Exec starts a new process inside the container
-// Returns an exit code and an error. If Exec was not able to exec in the container before a failure, an exit code of 126 is returned.
-// If another generic error happens, an exit code of 125 is returned.
+// Returns an exit code and an error. If Exec was not able to exec in the container before a failure, an exit code of define.ExecErrorCodeCannotInvoke is returned.
+// If another generic error happens, an exit code of define.ExecErrorCodeGeneric is returned.
// Sometimes, the $RUNTIME exec call errors, and if that is the case, the exit code is the exit code of the call.
// Otherwise, the exit code will be the exit code of the executed call inside of the container.
// TODO investigate allowing exec without attaching
@@ -821,3 +821,12 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti
defer c.newContainerEvent(events.Restore)
return c.restore(ctx, options)
}
+
+// AutoRemove indicates whether the container will be removed after it is executed
+func (c *Container) AutoRemove() bool {
+ spec := c.config.Spec
+ if spec.Annotations == nil {
+ return false
+ }
+ return c.Spec().Annotations[InspectAnnotationAutoremove] == InspectResponseTrue
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0b5a8b946..6bf8439da 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -14,6 +14,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
@@ -1132,6 +1133,16 @@ func (c *Container) pause() error {
return errors.Wrapf(define.ErrNoCgroups, "cannot pause without using CGroups")
}
+ if rootless.IsRootless() {
+ cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return errors.Wrap(err, "failed to determine cgroupversion")
+ }
+ if !cgroupv2 {
+ return errors.Wrap(define.ErrNoCgroups, "can not pause containers on rootless containers with cgroup V1")
+ }
+ }
+
if err := c.ociRuntime.pauseContainer(c); err != nil {
return err
}
diff --git a/libpod/define/exec_codes.go b/libpod/define/exec_codes.go
index 7184f1e59..33d631326 100644
--- a/libpod/define/exec_codes.go
+++ b/libpod/define/exec_codes.go
@@ -1,6 +1,8 @@
package define
import (
+ "strings"
+
"github.com/pkg/errors"
)
@@ -28,3 +30,19 @@ func TranslateExecErrorToExitCode(originalEC int, err error) int {
}
return originalEC
}
+
+// ExitCode reads the error message when failing to executing container process
+// and then returns 0 if no error, ExecErrorCodeNotFound if command does not exist, or ExecErrorCodeCannotInvoke 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 ExecErrorCodeNotFound
+ }
+
+ return ExecErrorCodeCannotInvoke
+}
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index 22afa7416..6cada0801 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -107,8 +107,6 @@ func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-c
logrus.Debugf("Attaching to container %s exec session %s", c.ID(), sessionID)
- registerResizeFunc(resize, c.execBundlePath(sessionID))
-
// set up the socket path, such that it is the correct length and location for exec
socketPath := buildSocketPath(c.execAttachSocketPath(sessionID))
@@ -116,6 +114,7 @@ func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-c
if _, err := readConmonPipeData(attachFd, ""); err != nil {
return err
}
+
// 2: then attach
conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
if err != nil {
@@ -127,6 +126,10 @@ func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-c
}
}()
+ // Register the resize func after we've read the attach socket, as we know at this point the
+ // 'ctl' file has been created in conmon
+ registerResizeFunc(resize, c.execBundlePath(sessionID))
+
// start listening on stdio of the process
receiveStdoutError, stdinDone := setupStdioChannels(streams, conn, detachKeys)
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index e2448e92a..7c786b835 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -5,6 +5,8 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -163,6 +165,16 @@ func (p *Pod) Pause() (map[string]error, error) {
return nil, define.ErrPodRemoved
}
+ if rootless.IsRootless() {
+ cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to determine cgroupversion")
+ }
+ if !cgroupv2 {
+ return nil, errors.Wrap(define.ErrNoCgroups, "can not pause pods containing rootless containers with cgroup V1")
+ }
+ }
+
allCtrs, err := p.runtime.state.PodContainers(p)
if err != nil {
return nil, err
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..01e008e87 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -464,19 +464,22 @@ 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
}
- errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid, true, c.String("detach-keys"))
+ exitChan, errChan, err := r.attach(ctx, os.Stdin, 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 +575,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 +672,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 +689,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 +714,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 +730,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 +742,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 +750,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/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/util/utils.go b/pkg/util/utils.go
index 2261934f0..583bf5d18 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -377,3 +377,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..3bd487849 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"
)
@@ -79,11 +81,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 ...
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index edd9c70c6..9b0cb757d 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -223,7 +223,7 @@ var _ = Describe("Podman cp", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"cp", "testctr:testfile", "testfile1"})
+ session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:testfile", "testfile1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -233,7 +233,7 @@ var _ = Describe("Podman cp", func() {
Expect(err).To(BeNil())
Expect(strings.Contains(string(cmdRet), "testuser")).To(BeFalse())
- session = podmanTest.Podman([]string{"cp", "testfile1", "testctr:testfile2"})
+ session = podmanTest.Podman([]string{"cp", "--pause=false", "testfile1", "testctr:testfile2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_exit_test.go b/test/e2e/run_exit_test.go
index 861d6b3b7..374705879 100644
--- a/test/e2e/run_exit_test.go
+++ b/test/e2e/run_exit_test.go
@@ -1,10 +1,9 @@
-// +build !remoteclient
-
package integration
import (
"os"
+ "github.com/containers/libpod/libpod/define"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -34,22 +33,22 @@ var _ = Describe("Podman run exit", func() {
})
- It("podman run exit 125", func() {
+ It("podman run exit define.ExecErrorCodeGeneric", func() {
result := podmanTest.Podman([]string{"run", "--foobar", ALPINE, "ls", "$tmp"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(125))
+ Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeGeneric))
})
- It("podman run exit 126", func() {
+ It("podman run exit ExecErrorCodeCannotInvoke", func() {
result := podmanTest.Podman([]string{"run", ALPINE, "/etc"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(126))
+ Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeCannotInvoke))
})
- It("podman run exit 127", func() {
+ It("podman run exit ExecErrorCodeNotFound", func() {
result := podmanTest.Podman([]string{"run", ALPINE, "foobar"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(127))
+ Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeNotFound))
})
It("podman run exit 0", func() {
diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go
index dfe71531a..0c78ab15b 100644
--- a/test/e2e/run_selinux_test.go
+++ b/test/e2e/run_selinux_test.go
@@ -162,7 +162,7 @@ var _ = Describe("Podman run", func() {
session = podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", "--security-opt", "label=filetype:foobar", fedoraMinimal, "ls", "-Z", "/dev"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(127))
+ Expect(session.ExitCode()).To(Equal(126))
})
})
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index f279a0c75..760ed6a18 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -60,7 +60,6 @@ echo $rand | 0 | $rand
# 'run --rm' goes through different code paths and may lose exit status.
# See https://github.com/containers/libpod/issues/3795
@test "podman run --rm" {
- skip_if_remote "podman-remote does not handle exit codes"
run_podman 0 run --rm $IMAGE /bin/true
run_podman 1 run --rm $IMAGE /bin/false
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index 204065bdb..0ca730a50 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -27,13 +27,8 @@ load helpers
"echo $rand_content1 >/tmp/$rand_filename1;
echo $rand_content2 >/tmp/$rand_filename2"
- run_podman cp 'cpcontainer:/tmp/*' $dstdir
-
- test -e $dstdir/$rand_filename1 || die "file 1 not copied from container"
- test -e $dstdir/$rand_filename2 || die "file 2 not copied from container"
-
- is "$(<$dstdir/$rand_filename1)" "$rand_content1" "content of file 1"
- is "$(<$dstdir/$rand_filename2)" "$rand_content2" "content of file 2"
+ # cp no longer supports wildcarding
+ run_podman 125 cp 'cpcontainer:/tmp/*' $dstdir
run_podman rm cpcontainer
}
@@ -150,13 +145,13 @@ load helpers
# Copy file from host into container, into a file named 'x'
# Note that the second has a trailing slash; this will trigger mkdir
- run_podman cp $srcdir/$rand_filename1 cpcontainer:/tmp/d1/x
+ run_podman cp --pause=false $srcdir/$rand_filename1 cpcontainer:/tmp/d1/x
is "$output" "" "output from podman cp 1"
- run_podman cp $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/
+ run_podman cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/
is "$output" "" "output from podman cp 3"
- run_podman cp $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x
+ run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x
is "$output" "" "output from podman cp 3"
# Read back.
@@ -205,7 +200,7 @@ load helpers
"mkdir -p $graphroot; trap 'exit 0' 15;while :;do sleep 0.5;done"
# Copy from host into container.
- run_podman cp $srcdir/$rand_filename cpcontainer:$graphroot/$rand_filename
+ run_podman cp --pause=false $srcdir/$rand_filename cpcontainer:$graphroot/$rand_filename
# ls, and confirm it's there.
run_podman exec cpcontainer ls -l $graphroot/$rand_filename