summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml4
-rw-r--r--.papr.yml1
-rw-r--r--Dockerfile5
-rw-r--r--Dockerfile.CentOS2
-rw-r--r--Dockerfile.Fedora4
-rw-r--r--cmd/podman/checkpoint.go9
-rw-r--r--cmd/podman/commit.go2
-rw-r--r--cmd/podman/common.go10
-rw-r--r--cmd/podman/images.go4
-rw-r--r--cmd/podman/import.go2
-rw-r--r--cmd/podman/inspect.go6
-rw-r--r--cmd/podman/kube.go1
-rw-r--r--cmd/podman/ps.go2
-rw-r--r--cmd/podman/restore.go13
-rw-r--r--cmd/podman/stop.go7
-rw-r--r--completions/bash/podman1067
-rw-r--r--contrib/python/podman/podman/libs/containers.py35
-rw-r--r--contrib/python/podman/podman/libs/images.py5
-rw-r--r--contrib/python/podman/test/test_containers.py2
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/commit_action.py18
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/ps_action.py19
-rw-r--r--docs/podman-container-checkpoint.1.md7
-rw-r--r--docs/podman-container-restore.1.md8
-rw-r--r--docs/podman-create.1.md3
-rw-r--r--docs/podman-ps.1.md2
-rw-r--r--docs/podman-run.1.md3
-rw-r--r--libpod/container.go7
-rw-r--r--libpod/container_api.go52
-rw-r--r--libpod/container_easyjson.go13
-rw-r--r--libpod/container_internal.go32
-rw-r--r--libpod/container_internal_linux.go13
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/oci.go15
-rw-r--r--libpod/oci_linux.go6
-rw-r--r--libpod/oci_unsupported.go2
-rw-r--r--libpod/options.go4
-rw-r--r--libpod/pod_internal.go2
-rw-r--r--libpod/runtime_pod_infra_linux.go6
-rw-r--r--libpod/util.go44
-rw-r--r--pkg/namespaces/namespaces.go7
-rw-r--r--pkg/resolvconf/resolvconf.go12
-rw-r--r--pkg/spec/createconfig.go4
-rw-r--r--pkg/spec/spec.go3
-rw-r--r--pkg/varlinkapi/containers_create.go7
-rw-r--r--test/e2e/checkpoint_test.go165
-rw-r--r--test/e2e/libpod_suite_test.go6
-rw-r--r--test/e2e/rootless_test.go12
-rw-r--r--test/e2e/run_test.go22
-rw-r--r--test/e2e/stop_test.go14
-rw-r--r--test/utils/podmantest_test.go4
-rw-r--r--test/utils/utils.go6
51 files changed, 1062 insertions, 639 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index c5b73fdc9..983ee1237 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -18,8 +18,8 @@ gce_instance:
env:
FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554"
CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
- CRIO_COMMIT: "662dbb31b5d4f5ed54511a47cde7190c61c28677"
- CRIU_COMMIT: "584cbe4643c3fc7dc901ff08bf923ca0fe7326f9"
+ CRIO_COMMIT: "7a283c391abb7bd25086a8ff91dbb36ebdd24466"
+ CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a"
RUNC_COMMIT: "78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d"
# File to update in home-dir with task-specific env. var values
ENVLIB: ".bash_profile"
diff --git a/.papr.yml b/.papr.yml
index 26b527a36..b82da28e5 100644
--- a/.papr.yml
+++ b/.papr.yml
@@ -122,7 +122,6 @@ packages:
- python3-varlink
- python3-dateutil
- python3-psutil
- - https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm
tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
diff --git a/Dockerfile b/Dockerfile
index 70d1a7629..3eb7b0a07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -64,7 +64,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install conmon
-ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
+ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
@@ -112,8 +112,7 @@ RUN set -x \
&& go get -u github.com/mailru/easyjson/... \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
-# Install criu
-ENV CRIU_COMMIT 584cbe4643c3fc7dc901ff08bf923ca0fe7326f9
+# Install latest stable criu version
RUN set -x \
&& cd /tmp \
&& git clone https://github.com/checkpoint-restore/criu.git \
diff --git a/Dockerfile.CentOS b/Dockerfile.CentOS
index 67b7ddce1..3e14e59db 100644
--- a/Dockerfile.CentOS
+++ b/Dockerfile.CentOS
@@ -68,7 +68,7 @@ RUN set -x \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install conmon
-ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
+ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
diff --git a/Dockerfile.Fedora b/Dockerfile.Fedora
index 38cd599d4..2080b597b 100644
--- a/Dockerfile.Fedora
+++ b/Dockerfile.Fedora
@@ -17,7 +17,7 @@ RUN dnf -y install btrfs-progs-devel \
libseccomp-devel \
libselinux-devel \
skopeo-containers \
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm \
+ runc \
make \
ostree-devel \
python \
@@ -72,7 +72,7 @@ RUN set -x \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install conmon
-ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
+ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go
index ddfd12bc3..824c97662 100644
--- a/cmd/podman/checkpoint.go
+++ b/cmd/podman/checkpoint.go
@@ -28,6 +28,10 @@ var (
Usage: "leave the container running after writing checkpoint to disk",
},
cli.BoolFlag{
+ Name: "tcp-established",
+ Usage: "checkpoint a container with established TCP connections",
+ },
+ cli.BoolFlag{
Name: "all, a",
Usage: "checkpoint all running containers",
},
@@ -55,8 +59,9 @@ func checkpointCmd(c *cli.Context) error {
defer runtime.Shutdown(false)
options := libpod.ContainerCheckpointOptions{
- Keep: c.Bool("keep"),
- KeepRunning: c.Bool("leave-running"),
+ Keep: c.Bool("keep"),
+ KeepRunning: c.Bool("leave-running"),
+ TCPEstablished: c.Bool("tcp-established"),
}
if err := checkAllAndLatest(c); err != nil {
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go
index b09c6b0d9..02ede4f73 100644
--- a/cmd/podman/commit.go
+++ b/cmd/podman/commit.go
@@ -95,7 +95,7 @@ func commitCmd(c *cli.Context) error {
for _, change := range c.StringSlice("change") {
splitChange := strings.Split(strings.ToUpper(change), "=")
if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) {
- return errors.Errorf("invalid syntax for --change ", change)
+ return errors.Errorf("invalid syntax for --change: %s", change)
}
}
}
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index f9e746b28..c4016698a 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/buildah"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/fatih/camelcase"
"github.com/pkg/errors"
@@ -161,6 +162,13 @@ func getContext() context.Context {
return context.TODO()
}
+func getDefaultNetwork() string {
+ if rootless.IsRootless() {
+ return "slirp4netns"
+ }
+ return "bridge"
+}
+
// Common flags shared between commands
var createFlags = []cli.Flag{
cli.StringSliceFlag{
@@ -372,7 +380,7 @@ var createFlags = []cli.Flag{
cli.StringFlag{
Name: "net, network",
Usage: "Connect a container to a network",
- Value: "bridge",
+ Value: getDefaultNetwork(),
},
cli.BoolFlag{
Name: "oom-kill-disable",
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index c52b26260..3351123ed 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -376,13 +376,13 @@ func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, i
case "before":
before, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
if err != nil {
- return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
+ return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, image.CreatedBeforeFilter(before.Created()))
case "after":
after, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
if err != nil {
- return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
+ return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, image.CreatedAfterFilter(after.Created()))
case "dangling":
diff --git a/cmd/podman/import.go b/cmd/podman/import.go
index be516e4fa..144354fa6 100644
--- a/cmd/podman/import.go
+++ b/cmd/podman/import.go
@@ -139,7 +139,7 @@ func downloadFromURL(source string) (string, error) {
_, err = io.Copy(outFile, response.Body)
if err != nil {
- return "", errors.Wrapf(err, "error saving %q to %q", source, outFile)
+ return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name())
}
return outFile.Name(), nil
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index bd9e8c13c..6ffcde55f 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -119,7 +119,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
}
libpodInspectData, err := ctr.Inspect(c.Bool("size"))
if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
+ inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
@@ -154,12 +154,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
} else {
libpodInspectData, err := ctr.Inspect(c.Bool("size"))
if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
+ inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
if err != nil {
- inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID)
+ inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
break
}
}
diff --git a/cmd/podman/kube.go b/cmd/podman/kube.go
index ced87e2bd..2cb407c09 100644
--- a/cmd/podman/kube.go
+++ b/cmd/podman/kube.go
@@ -18,5 +18,6 @@ var (
Subcommands: kubeSubCommands,
UseShortOptionHandling: true,
OnUsageError: usageErrorHandler,
+ Hidden: true,
}
)
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 83274c9a8..0b03388a2 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -184,7 +184,7 @@ var (
Usage: "Display the extended information",
},
cli.BoolFlag{
- Name: "pod",
+ Name: "pod, p",
Usage: "Print the ID and name of the pod the containers are associated with",
},
cli.BoolFlag{
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index 067a2b5d4..bc2a71ba0 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -27,6 +27,10 @@ var (
// dedicated state for container which are checkpointed.
// TODO: add ContainerStateCheckpointed
cli.BoolFlag{
+ Name: "tcp-established",
+ Usage: "checkpoint a container with established TCP connections",
+ },
+ cli.BoolFlag{
Name: "all, a",
Usage: "restore all checkpointed containers",
},
@@ -53,16 +57,19 @@ func restoreCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
- keep := c.Bool("keep")
+ options := libpod.ContainerCheckpointOptions{
+ Keep: c.Bool("keep"),
+ TCPEstablished: c.Bool("tcp-established"),
+ }
if err := checkAllAndLatest(c); err != nil {
return err
}
- containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "checkpointed")
+ containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateExited, "checkpointed")
for _, ctr := range containers {
- if err = ctr.Restore(context.TODO(), keep); err != nil {
+ if err = ctr.Restore(context.TODO(), options); err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index 04022839a..ade51705e 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
@@ -77,7 +78,11 @@ func stopCmd(c *cli.Context) error {
stopTimeout = ctr.StopTimeout()
}
f := func() error {
- return con.StopWithTimeout(stopTimeout)
+ if err := con.StopWithTimeout(stopTimeout); err != nil && errors.Cause(err) != libpod.ErrCtrStopped {
+ return err
+ }
+ return nil
+
}
stopFuncs = append(stopFuncs, shared.ParallelWorkerInput{
ContainerID: con.ID(),
diff --git a/completions/bash/podman b/completions/bash/podman
index 3c6b6ec50..3cccf2192 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -5,7 +5,7 @@ __podman_previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
__podman_q() {
- podman ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
+ podman ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
}
# __podman_containers returns a list of containers. Additional options to
@@ -232,7 +232,7 @@ __podman_services() {
fields='$2' # names only
shift
fi
- __podman_q service ls "$@" | awk "NR>1 {print $fields}"
+ __podman_q service ls "$@" | awk "NR>1 {print $fields}"
}
# __podman_complete_services applies completion of services based on the current
@@ -694,20 +694,20 @@ _podman_attach() {
--detach-keys
"
local boolean_options="
- --help
- -h
- --latest
- -l
- --no-stdin
- --sig-proxy
+ --help
+ -h
+ --latest
+ -l
+ --no-stdin
+ --sig-proxy
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -716,20 +716,26 @@ _podman_container_attach() {
}
_podman_container_checkpoint() {
- local options_with_args="
- --help -h
- "
local boolean_options="
- --keep
+ -a
+ --all
+ -h
+ --help
-k
+ --keep
+ -l
+ --latest
+ -R
+ --leave-running
+ --tcp-established
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -785,6 +791,8 @@ _podman_container_refresh() {
local options_with_args="
"
local boolean_options="
+ --help
+ -h
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -794,20 +802,24 @@ _podman_container_restart() {
}
_podman_container_restore() {
- local options_with_args="
- --help -h
- "
local boolean_options="
- --keep
- -k
+ -a
+ --all
+ -h
+ --help
+ -k
+ --keep
+ -l
+ --latest
+ --tcp-established
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_created
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_created
+ ;;
esac
}
@@ -815,10 +827,6 @@ _podman_container_rm() {
_podman_rm
}
-_podman_container_run() {
- _podman_run
-}
-
_podman_container_start() {
_podman_start
}
@@ -921,29 +929,29 @@ _podman_commit() {
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
_podman_build() {
local boolean_options="
- --force-rm
- --help
- -h
- --layers
- --no-cache
- --pull
- --pull-always
- --quiet
- -q
- --rm
- --squash
- --tls-verify
+ --force-rm
+ --help
+ -h
+ --layers
+ --no-cache
+ --pull
+ --pull-always
+ --quiet
+ -q
+ --rm
+ --squash
+ --tls-verify
"
local options_with_args="
@@ -996,18 +1004,18 @@ _podman_build() {
local all_options="$options_with_args $boolean_options"
case "$prev" in
- --runtime)
- COMPREPLY=($(compgen -W 'runc runv' -- "$cur"))
- ;;
- $(__podman_to_extglob "$options_with_args"))
+ --runtime)
+ COMPREPLY=($(compgen -W 'runc runv' -- "$cur"))
+ ;;
+ $(__podman_to_extglob "$options_with_args"))
return
;;
esac
case "$cur" in
-*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
esac
}
@@ -1016,16 +1024,18 @@ _podman_diff() {
--format
"
local boolean_options="
- "
+ --help
+ -h
+ "
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
@@ -1037,19 +1047,21 @@ _podman_exec() {
-u
"
local boolean_options="
- --latest
- -l
- --privileged
- --tty
- -t
+ --help
+ -h
+ --latest
+ -l
+ --privileged
+ --tty
+ -t
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -1059,14 +1071,16 @@ _podman_export() {
-o
"
local boolean_options="
+ --help
+ -h
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
@@ -1075,19 +1089,21 @@ _podman_history() {
--format
"
local boolean_options="
- --human -H
- --no-trunc
- --quiet -q
+ --help
+ -h
+ --human -H
+ --no-trunc
+ --quiet -q
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
@@ -1108,20 +1124,20 @@ _podman_import() {
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_list_images
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_list_images
+ ;;
esac
}
_podman_info() {
local boolean_options="
- --help
- -h
- --debug
+ --help
+ -h
+ --debug
"
local options_with_args="
--format
@@ -1130,12 +1146,12 @@ _podman_info() {
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_list_images
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_list_images
+ ;;
esac
}
@@ -1191,7 +1207,7 @@ _podman_image() {
local boolean_options="
--help
-h
- "
+ "
subcommands="
build
history
@@ -1222,20 +1238,20 @@ _podman_image() {
_podman_images() {
local boolean_options="
- -a
- --all
- --digests
- --digests
- -f
- --filter
- -h
- --help
- --no-trunc
- --notruncate
- -n
- --noheading
- -q
- --quiet
+ -a
+ --all
+ --digests
+ --digests
+ -f
+ --filter
+ -h
+ --help
+ --no-trunc
+ --notruncate
+ -n
+ --noheading
+ -q
+ --quiet
"
local options_with_args="
--format
@@ -1245,22 +1261,22 @@ _podman_images() {
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
_podman_inspect() {
local boolean_options="
- --help
- -h
- --latest
- -l
- "
+ --help
+ -h
+ --latest
+ -l
+ "
local options_with_args="
--format
-f
@@ -1325,20 +1341,20 @@ _podman_kill() {
--signal -s
"
local boolean_options="
- --all
- -a
- --help
- -h
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -1348,22 +1364,24 @@ _podman_logs() {
--tail
"
local boolean_options="
- --follow
- -f
- --latest
- -l
- --timestamps
- -t
+ --follow
+ -f
+ --help
+ -h
+ --latest
+ -l
+ --timestamps
+ -t
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_list_containers
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_list_containers
+ ;;
esac
}
@@ -1375,10 +1393,13 @@ _podman_pull() {
--signature-policy
"
local boolean_options="
- --all-tags -a
- --quiet
- -q
- --tls-verify
+ --all-tags
+ -a
+ --help
+ -h
+ --quiet
+ -q
+ --tls-verify
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -1391,7 +1412,9 @@ _podman_search() {
--limit
"
local boolean_options="
- --no-trunc
+ --help
+ -h
+ --no-trunc
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -1402,56 +1425,58 @@ _podman_unmount() {
_podman_umount() {
local boolean_options="
- --all
- -a
- --force
- -f
- --help
- -h
- "
+ --all
+ -a
+ --help
+ -h
+ --force
+ -f
+ "
local options_with_args="
- "
+ "
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
_podman_mount() {
local boolean_options="
- --help
- -h
- --notruncate
- "
+ --help
+ -h
+ --notruncate
+ "
local options_with_args="
- --format
- "
+ --format
+ "
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
_podman_push() {
local boolean_options="
- --compress
- --quiet
- -q
- --remove-signatures
- --tls-verify
+ --compress
+ --help
+ -h
+ --quiet
+ -q
+ --remove-signatures
+ --tls-verify
"
local options_with_args="
@@ -1466,12 +1491,12 @@ _podman_push() {
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
@@ -1553,16 +1578,17 @@ _podman_container_run() {
"
local boolean_options="
- --disable-content-trust=false
- --help
- --init
- --interactive -i
- --oom-kill-disable
- --privileged
- --publish-all -P
- --quiet
- --read-only
- --tty -t
+ --disable-content-trust=false
+ --help
+ -h
+ --init
+ --interactive -i
+ --oom-kill-disable
+ --privileged
+ --publish-all -P
+ --quiet
+ --read-only
+ --tty -t
"
if [ "$command" = "run" -o "$subcommand" = "run" ] ; then
@@ -1583,12 +1609,13 @@ _podman_container_run() {
fi
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ return
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
@@ -1771,33 +1798,38 @@ _podman_restart() {
--timeout -t
"
local boolean_options="
- --all
- -a
- --latest
- -l
- --running
- --timeout
- -t"
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
+ --running
+ --timeout
+ -t
+ "
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
_podman_rm() {
local boolean_options="
- --all
- -a
- --force
- -f
- --latest
- -l
- --volumes
- -v
+ --all
+ -a
+ --force
+ -f
+ --help
+ -h
+ --latest
+ -l
+ --volumes
+ -v
"
local options_with_args="
@@ -1806,52 +1838,53 @@ _podman_rm() {
local all_options="$options_with_args $boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
_podman_rmi() {
local boolean_options="
- --help
- -h
- --force
- -f
- -a
- --all
+ --all
+ -a
+ --force
+ -f
+ --help
+ -h
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
_podman_stats() {
local boolean_options="
- --help
- --all
- -a
- --no-stream
- --format
- --no-reset
+ --all
+ -a
+ --help
+ -h
+ --no-stream
+ --format
+ --no-reset
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -1859,49 +1892,51 @@ _podman_tag() {
local options_with_args="
"
local boolean_options="
+ --help
+ -h
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images
+ ;;
esac
}
__podman_top_descriptors() {
- podman top --list-descriptors
+ podman top --list-descriptors
}
__podman_complete_top_descriptors() {
- COMPREPLY=($(compgen -W "$(__podman_top_descriptors)" -- "$cur"))
+ COMPREPLY=($(compgen -W "$(__podman_top_descriptors)" -- "$cur"))
}
_podman_top() {
local options_with_args="
"
local boolean_options="
- --help
- -h
- --latest
- -l
+ --help
+ -h
+ --latest
+ -l
"
# podman-top works on only *one* container, which means that when we have
# three or more arguments, we can complete with top descriptors.
if [[ "${COMP_CWORD}" -ge 3 ]]; then
- __podman_complete_top_descriptors
- return
+ __podman_complete_top_descriptors
+ return
fi
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
@@ -1924,56 +1959,61 @@ _podman_save() {
--format
"
local boolean_options="
- --compress
- -q
- --quiet
+ --compress
+ --help
+ -h
+ -q
+ --quiet
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
_podman_pause() {
local boolean_options="
- -a
- --all
+ --all
+ -a
+ --help
+ -h
"
local options_with_args="
- --help -h
"
local boolean_options=""
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
_podman_port() {
local options_with_args="
- --help -h
"
local boolean_options="
- --all
- -a
- -l
- --latest"
+ --all
+ -a
+ --help
+ -h
+ -l
+ --latest
+ "
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
@@ -1989,13 +2029,14 @@ _podman_ps() {
--sort
"
local boolean_options="
- --all -a
- --latest -l
- --no-trunc
- --pod
- --quiet -q
- --size -s
- --namespace --ns
+ --all -a
+ --help -h
+ --latest -l
+ --no-trunc
+ --pod -p
+ --quiet -q
+ --size -s
+ --namespace --ns
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2006,23 +2047,23 @@ _podman_start() {
"
local boolean_options="
- -h
- --help
- -a
- --attach
- -i
- --interactive
- --latest
- -l
- --sig-proxy
+ --attach
+ -a
+ -h
+ --help
+ -i
+ --interactive
+ --latest
+ -l
+ --sig-proxy
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
_podman_stop() {
@@ -2030,64 +2071,70 @@ _podman_stop() {
--timeout -t
"
local boolean_options="
- --all
- -a
- --latest
- -l"
+ --all
+ -a
+ -h
+ --help
+ --latest
+ -l
+ "
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_running
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_running
+ ;;
esac
}
_podman_unpause() {
local boolean_options="
- -a
- --all
+ --all
+ -a
+ --help
+ -h
"
local options_with_args="
- --help -h
"
- local boolean_options=""
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_containers_unpauseable
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_unpauseable
+ ;;
esac
}
_podman_varlink() {
local options_with_args="
- --help -h
--timeout -t
"
- local boolean_options=""
+ local boolean_options="
+ --help
+ -h
+ "
_complete_ "$options_with_args" "$boolean_options"
}
_podman_wait() {
local options_with_args=""
local boolean_options="
- --help
- -h
- -i
+ --help
+ -h
+ -i
-l
- --interval
- --latest"
+ --interval
+ --latest
+ "
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_container_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_container_names
+ ;;
esac
}
@@ -2114,8 +2161,10 @@ _podman_load() {
--signature-policy
"
local boolean_options="
- --quiet
- -q
+ --help
+ -h
+ --quiet
+ -q
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2129,8 +2178,8 @@ _podman_login() {
--authfile
"
local boolean_options="
- --help
- -h
+ --help
+ -h
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2140,10 +2189,10 @@ _podman_logout() {
--authfile
"
local boolean_options="
- --all
- -a
- --help
- -h
+ --all
+ -a
+ --help
+ -h
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2158,23 +2207,23 @@ _podman_container_runlabel() {
"
local boolean_options="
- --display
- --help
- -h
- -p
- --pull
- -q
- --quiet
- --tls-verify
+ --display
+ --help
+ -h
+ -p
+ --pull
+ -q
+ --quiet
+ --tls-verify
"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_images --id
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_images --id
+ ;;
esac
}
@@ -2210,7 +2259,9 @@ _podman_pod_create() {
"
local boolean_options="
- --infra
+ --help
+ -h
+ --infra
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2220,21 +2271,23 @@ _podman_pod_kill() {
"
local boolean_options="
- --all
- -a
- --signal
- -s
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --signal
+ -s
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2247,16 +2300,18 @@ __podman_pod_ps() {
"
local boolean_options="
- --cgroup
- --ctr-ids
- --ctr-names
- --ctr-status
- -q
- --quiet
- --no-trunc
- --labels
- -l
- --latest
+ --cgroup
+ --ctr-ids
+ --ctr-names
+ --ctr-status
+ --help
+ -h
+ -q
+ --quiet
+ --no-trunc
+ --labels
+ -l
+ --latest
"
_complete_ "$options_with_args" "$boolean_options"
}
@@ -2278,19 +2333,21 @@ _podman_pod_restart() {
"
local boolean_options="
- --all
- -a
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2299,21 +2356,23 @@ _podman_pod_rm() {
"
local boolean_options="
- -a
- --all
- -f
- --force
- --latest
- -l
+ -a
+ --all
+ --help
+ -h
+ -f
+ --force
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2322,19 +2381,21 @@ _podman_pod_start() {
"
local boolean_options="
- --all
- -a
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2343,20 +2404,22 @@ _podman_pod_stop() {
"
local boolean_options="
- --all
- -a
- --cleanup
- --latest
- -l
+ --all
+ -a
+ --cleanup
+ --help
+ -h
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2365,19 +2428,21 @@ _podman_pod_pause() {
"
local boolean_options="
- --all
- -a
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
@@ -2386,26 +2451,28 @@ _podman_pod_unpause() {
"
local boolean_options="
- --all
- -a
- --latest
- -l
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
- -*)
- COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
- ;;
- *)
- __podman_complete_pod_names
- ;;
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
esac
}
_podman_pod() {
local boolean_options="
- --help
- -h
+ --help
+ -h
"
subcommands="
create
@@ -2428,29 +2495,31 @@ _podman_pod() {
case "$cur" in
-*)
- COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
- ;;
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
*)
- COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
- ;;
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
esac
}
_podman_podman() {
local options_with_args="
- --config -c
- --cpu-profile
- --root
- --runroot
- --storage-driver
- --storage-opt
- --log-level
- --namespace
+ --config -c
+ --cpu-profile
+ --root
+ --runroot
+ --storage-driver
+ --storage-opt
+ --log-level
+ --namespace
"
local boolean_options="
- --help -h
- --version -v
- --syslog
+ --help
+ -h
+ --version
+ -v
+ --syslog
"
commands="
attach
diff --git a/contrib/python/podman/podman/libs/containers.py b/contrib/python/podman/podman/libs/containers.py
index 21a94557a..7adecea8f 100644
--- a/contrib/python/podman/podman/libs/containers.py
+++ b/contrib/python/podman/podman/libs/containers.py
@@ -108,19 +108,16 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
results = podman.ExportContainer(self._id, target)
return results['tarfile']
- def commit(self,
- image_name,
- *args,
- changes=[],
- message='',
- pause=True,
- **kwargs): # pylint: disable=unused-argument
+ def commit(self, image_name, **kwargs):
"""Create image from container.
- All changes overwrite existing values.
- See inspect() to obtain current settings.
+ Keyword arguments:
+ author -- change image's author
+ message -- change image's message, docker format only.
+ pause -- pause container during commit
+ change -- Additional properties to change
- Changes:
+ Change examples:
CMD=/usr/bin/zsh
ENTRYPOINT=/bin/sh date
ENV=TEST=test_containers.TestContainers.test_commit
@@ -129,21 +126,23 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
USER=bozo:circus
VOLUME=/data
WORKDIR=/data/application
+
+ All changes overwrite existing values.
+ See inspect() to obtain current settings.
"""
- # TODO: Clean up *args, **kwargs after Commit() is complete
- try:
- author = kwargs.get('author', getpass.getuser())
- except Exception: # pylint: disable=broad-except
- author = ''
+ author = kwargs.get('author', None) or getpass.getuser()
+ change = kwargs.get('change', None) or []
+ message = kwargs.get('message', None) or ''
+ pause = kwargs.get('pause', None) or True
- for c in changes:
+ for c in change:
if c.startswith('LABEL=') and c.count('=') < 2:
raise ValueError(
'LABEL should have the format: LABEL=label=value, not {}'.
format(c))
with self._client() as podman:
- results = podman.Commit(self._id, image_name, changes, author,
+ results = podman.Commit(self._id, image_name, change, author,
message, pause)
return results['image']
@@ -194,7 +193,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
podman.UnpauseContainer(self._id)
return self._refresh(podman)
- def update_container(self, *args, **kwargs): \
+ def update_container(self, *args, **kwargs): \
# pylint: disable=unused-argument
"""TODO: Update container..., return id on success."""
with self._client() as podman:
diff --git a/contrib/python/podman/podman/libs/images.py b/contrib/python/podman/podman/libs/images.py
index 9453fb416..6dfc4a656 100644
--- a/contrib/python/podman/podman/libs/images.py
+++ b/contrib/python/podman/podman/libs/images.py
@@ -27,9 +27,10 @@ class Image(collections.UserDict):
@staticmethod
def _split_token(values=None, sep='='):
+ if not values:
+ return {}
return {
- k: v1
- for k, v1 in (v0.split(sep, 1) for v0 in values if values)
+ k: v1 for k, v1 in (v0.split(sep, 1) for v0 in values)
}
def create(self, *args, **kwargs):
diff --git a/contrib/python/podman/test/test_containers.py b/contrib/python/podman/test/test_containers.py
index 3de1e54bc..a7a6ac304 100644
--- a/contrib/python/podman/test/test_containers.py
+++ b/contrib/python/podman/test/test_containers.py
@@ -152,7 +152,7 @@ class TestContainers(PodmanTestCase):
changes.append('WORKDIR=/data/application')
id = self.alpine_ctnr.commit(
- 'alpine3', author='Bozo the clown', changes=changes, pause=True)
+ 'alpine3', author='Bozo the clown', change=changes, pause=True)
img = self.pclient.images.get(id)
self.assertIsNotNone(img)
diff --git a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
index 21665ad0b..21924e938 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
@@ -30,7 +30,8 @@ class Commit(AbstractActionBase):
choices=('oci', 'docker'),
default='oci',
type=str.lower,
- help='Set the format of the image manifest and metadata',
+ help='Set the format of the image manifest and metadata.'
+ ' (Ignored.)',
)
parser.add_argument(
'--iidfile',
@@ -40,7 +41,8 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--message',
'-m',
- help='Set commit message for committed image',
+ help='Set commit message for committed image'
+ ' (Only on docker images.)',
)
parser.add_argument(
'--pause',
@@ -80,8 +82,16 @@ class Commit(AbstractActionBase):
flush=True)
return 1
else:
- ident = ctnr.commit(self.opts['image'][0], **self.opts)
- print(ident)
+ ident = ctnr.commit(
+ self.opts['image'][0],
+ change=self.opts.get('change', None),
+ message=self.opts.get('message', None),
+ pause=self.opts['pause'],
+ author=self.opts.get('author', None),
+ )
+
+ if not self.opts['quiet']:
+ print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
diff --git a/contrib/python/pypodman/pypodman/lib/actions/ps_action.py b/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
index cd7a7947d..62ceb2e67 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/ps_action.py
@@ -16,6 +16,7 @@ class Ps(AbstractActionBase):
"""Add Images command to parent parser."""
parser = parent.add_parser('ps', help='list containers')
super().subparser(parser)
+
parser.add_argument(
'--sort',
choices=('createdat', 'id', 'image', 'names', 'runningfor', 'size',
@@ -32,9 +33,9 @@ class Ps(AbstractActionBase):
self.columns = OrderedDict({
'id':
- ReportColumn('id', 'CONTAINER ID', 14),
+ ReportColumn('id', 'CONTAINER ID', 12),
'image':
- ReportColumn('image', 'IMAGE', 30),
+ ReportColumn('image', 'IMAGE', 31),
'command':
ReportColumn('column', 'COMMAND', 20),
'createdat':
@@ -49,10 +50,15 @@ class Ps(AbstractActionBase):
def list(self):
"""List containers."""
+ if self._args.all:
+ ictnrs = self.client.containers.list()
+ else:
+ ictnrs = filter(
+ lambda c: podman.FoldedString(c['status']) == 'running',
+ self.client.containers.list())
+
# TODO: Verify sorting on dates and size
- ctnrs = sorted(
- self.client.containers.list(),
- key=operator.attrgetter(self._args.sort))
+ ctnrs = sorted(ictnrs, key=operator.attrgetter(self._args.sort))
if not ctnrs:
return
@@ -65,9 +71,6 @@ class Ps(AbstractActionBase):
'createdat':
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
})
-
- if self._args.truncate:
- fields.update({'image': ctnr.image[-30:]})
rows.append(fields)
with Report(self.columns, heading=self._args.heading) as report:
diff --git a/docs/podman-container-checkpoint.1.md b/docs/podman-container-checkpoint.1.md
index 6f454dfd1..94e52dc78 100644
--- a/docs/podman-container-checkpoint.1.md
+++ b/docs/podman-container-checkpoint.1.md
@@ -29,6 +29,13 @@ Instead of providing the container name or ID, checkpoint the last created conta
Leave the container running after checkpointing instead of stopping it.
+**--tcp-established**
+
+Checkpoint a container with established TCP connections. If the checkpoint
+image contains established TCP connections, this options is required during
+restore. Defaults to not checkpointing containers with established TCP
+connections.
+
## EXAMPLE
podman container checkpoint mywebserver
diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md
index 4dd5ea7c7..44219f3ef 100644
--- a/docs/podman-container-restore.1.md
+++ b/docs/podman-container-restore.1.md
@@ -32,6 +32,14 @@ Restore all checkpointed containers.
Instead of providing the container name or ID, restore the last created container.
+**--tcp-established**
+
+Restore a container with established TCP connections. If the checkpoint image
+contains established TCP connections, this option is required during restore.
+If the checkpoint image does not contain established TCP connections this
+option is ignored. Defaults to not restoring containers with established TCP
+connections.
+
## EXAMPLE
podman container restore mywebserver
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index ab2cb8c60..6fbdd0d03 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -426,7 +426,8 @@ Set the Network mode for the container
'container:<name|id>': reuse another container's network stack
'host': use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
'<network-name>|<network-id>': connect to a user-defined network
- 'ns:<path>' path to a network namespace to join
+ 'ns:<path>': path to a network namespace to join
+ 'slirp4netns': use slirp4netns to create a user network stack. This is the default for rootless containers
**--network-alias**=[]
diff --git a/docs/podman-ps.1.md b/docs/podman-ps.1.md
index 2cb77ffed..7333a1095 100644
--- a/docs/podman-ps.1.md
+++ b/docs/podman-ps.1.md
@@ -24,7 +24,7 @@ all the containers information. By default it lists:
Show all the containers, default is only running containers
-**--pod**
+**--pod, -p**
Display the pods the containers are associated with
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index f99d2f863..a6761a393 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -408,7 +408,8 @@ Set the Network mode for the container:
- `container:<name|id>`: reuse another container's network stack
- `host`: use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
- `<network-name>|<network-id>`: connect to a user-defined network
-- `ns:<path>` path to a network namespace to join
+- `ns:<path>`: path to a network namespace to join
+- `slirp4netns`: use slirp4netns to create a user network stack. This is the default for rootless containers
**--network-alias**=[]
diff --git a/libpod/container.go b/libpod/container.go
index 16f61d021..b6155a809 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -9,6 +9,7 @@ import (
"github.com/containernetworking/cni/pkg/types"
cnitypes "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/storage"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -296,6 +297,8 @@ type ContainerConfig struct {
HostAdd []string `json:"hostsAdd,omitempty"`
// Network names (CNI) to add container to. Empty to use default network.
Networks []string `json:"networks,omitempty"`
+ // Network mode specified for the default network.
+ NetMode namespaces.NetworkMode `json:"networkMode,omitempty"`
// Image Config
@@ -826,7 +829,7 @@ func (c *Container) IPs() ([]net.IPNet, error) {
}
if !c.config.CreateNetNS {
- return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod")
+ return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID())
}
ips := make([]net.IPNet, 0)
@@ -854,7 +857,7 @@ func (c *Container) Routes() ([]types.Route, error) {
}
if !c.config.CreateNetNS {
- return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod")
+ return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID())
}
routes := make([]types.Route, 0)
diff --git a/libpod/container_api.go b/libpod/container_api.go
index df6b6e962..bc92cae69 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -39,7 +39,7 @@ func (c *Container) Init(ctx context.Context) (err error) {
notRunning, err := c.checkDependenciesRunning()
if err != nil {
- return errors.Wrapf(err, "error checking dependencies for container %s")
+ return errors.Wrapf(err, "error checking dependencies for container %s", c.ID())
}
if len(notRunning) > 0 {
depString := strings.Join(notRunning, ",")
@@ -93,7 +93,7 @@ func (c *Container) Start(ctx context.Context) (err error) {
notRunning, err := c.checkDependenciesRunning()
if err != nil {
- return errors.Wrapf(err, "error checking dependencies for container %s")
+ return errors.Wrapf(err, "error checking dependencies for container %s", c.ID())
}
if len(notRunning) > 0 {
depString := strings.Join(notRunning, ",")
@@ -159,7 +159,7 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams,
notRunning, err := c.checkDependenciesRunning()
if err != nil {
- return nil, errors.Wrapf(err, "error checking dependencies for container %s")
+ return nil, errors.Wrapf(err, "error checking dependencies for container %s", c.ID())
}
if len(notRunning) > 0 {
depString := strings.Join(notRunning, ",")
@@ -328,6 +328,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
if err != nil {
return errors.Wrapf(err, "error exec %s", c.ID())
}
+ chWait := make(chan error)
+ go func() {
+ chWait <- execCmd.Wait()
+ }()
+ defer close(chWait)
pidFile := c.execPidPath(sessionID)
// 60 second seems a reasonable time to wait
@@ -336,18 +341,12 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
const pidWaitTimeout = 60000
// Wait until the runtime makes the pidfile
- // TODO: If runtime errors before the PID file is created, we have to
- // wait for timeout here
- if err := WaitForFile(pidFile, pidWaitTimeout*time.Millisecond); err != nil {
- logrus.Debugf("Timed out waiting for pidfile from runtime for container %s exec", c.ID())
-
- // Check if an error occurred in the process before we made a pidfile
- // TODO: Wait() here is a poor choice - is there a way to see if
- // a process has finished, instead of waiting for it to finish?
- if err := execCmd.Wait(); err != nil {
+ exited, err := WaitForFile(pidFile, chWait, pidWaitTimeout*time.Millisecond)
+ if err != nil {
+ if exited {
+ // If the runtime exited, propagate the error we got from the process.
return err
}
-
return errors.Wrapf(err, "timed out waiting for runtime to create pidfile for exec session in container %s", c.ID())
}
@@ -389,7 +388,10 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
locked = false
}
- waitErr := execCmd.Wait()
+ var waitErr error
+ if !exited {
+ waitErr = <-chWait
+ }
// Lock again
if !c.batched {
@@ -716,7 +718,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e
notRunning, err := c.checkDependenciesRunning()
if err != nil {
- return errors.Wrapf(err, "error checking dependencies for container %s")
+ return errors.Wrapf(err, "error checking dependencies for container %s", c.ID())
}
if len(notRunning) > 0 {
depString := strings.Join(notRunning, ",")
@@ -801,7 +803,7 @@ func (c *Container) Refresh(ctx context.Context) error {
return err
}
- logrus.Debugf("Successfully refresh container %s state")
+ logrus.Debugf("Successfully refresh container %s state", c.ID())
// Initialize the container if it was created in runc
if wasCreated || wasRunning || wasPaused {
@@ -831,15 +833,21 @@ func (c *Container) Refresh(ctx context.Context) error {
}
// ContainerCheckpointOptions is a struct used to pass the parameters
-// for checkpointing to corresponding functions
+// for checkpointing (and restoring) to the corresponding functions
type ContainerCheckpointOptions struct {
- Keep bool
+ // Keep tells the API to not delete checkpoint artifacts
+ Keep bool
+ // KeepRunning tells the API to keep the container running
+ // after writing the checkpoint to disk
KeepRunning bool
+ // TCPEstablished tells the API to checkpoint a container
+ // even if it contains established TCP connections
+ TCPEstablished bool
}
// Checkpoint checkpoints a container
func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
- logrus.Debugf("Trying to checkpoint container %s", c)
+ logrus.Debugf("Trying to checkpoint container %s", c.ID())
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -853,8 +861,8 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
}
// Restore restores a container
-func (c *Container) Restore(ctx context.Context, keep bool) (err error) {
- logrus.Debugf("Trying to restore container %s", c)
+func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
+ logrus.Debugf("Trying to restore container %s", c.ID())
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -864,5 +872,5 @@ func (c *Container) Restore(ctx context.Context, keep bool) (err error) {
}
}
- return c.restore(ctx, keep)
+ return c.restore(ctx, options)
}
diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go
index 041cc08ac..8bf5cb64f 100644
--- a/libpod/container_easyjson.go
+++ b/libpod/container_easyjson.go
@@ -8,6 +8,7 @@ import (
json "encoding/json"
types "github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/current"
+ namespaces "github.com/containers/libpod/pkg/namespaces"
storage "github.com/containers/storage"
idtools "github.com/containers/storage/pkg/idtools"
ocicni "github.com/cri-o/ocicni/pkg/ocicni"
@@ -1550,6 +1551,8 @@ func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, ou
}
in.Delim(']')
}
+ case "networkMode":
+ out.NetMode = namespaces.NetworkMode(in.String())
case "userVolumes":
if in.IsNull() {
in.Skip()
@@ -2177,6 +2180,16 @@ func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer,
out.RawByte(']')
}
}
+ if in.NetMode != "" {
+ const prefix string = ",\"networkMode\":"
+ if first {
+ first = false
+ out.RawString(prefix[1:])
+ } else {
+ out.RawString(prefix)
+ }
+ out.String(string(in.NetMode))
+ }
if len(in.UserVolumes) != 0 {
const prefix string = ",\"userVolumes\":"
if first {
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index a426191a4..e31a8099c 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -607,7 +607,7 @@ func (c *Container) completeNetworkSetup() error {
if err := c.syncContainer(); err != nil {
return err
}
- if rootless.IsRootless() {
+ if c.config.NetMode == "slirp4netns" {
return c.runtime.setupRootlessNetNS(c)
}
return c.runtime.setupNetNS(c)
@@ -627,7 +627,7 @@ func (c *Container) init(ctx context.Context) error {
}
// With the spec complete, do an OCI create
- if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, false); err != nil {
+ if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, nil); err != nil {
return err
}
@@ -842,28 +842,22 @@ func (c *Container) mountStorage() (string, error) {
return c.state.Mountpoint, nil
}
- if !rootless.IsRootless() {
- // TODO: generalize this mount code so it will mount every mount in ctr.config.Mounts
- mounted, err := mount.Mounted(c.config.ShmDir)
- if err != nil {
- return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
- }
+ mounted, err := mount.Mounted(c.config.ShmDir)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
+ }
+ if !mounted {
+ shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize)
+ if err := c.mountSHM(shmOptions); err != nil {
+ return "", err
+ }
if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil {
return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir)
}
-
- if !mounted {
- shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize)
- if err := c.mountSHM(shmOptions); err != nil {
- return "", err
- }
- if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil {
- return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir)
- }
- }
}
+ // TODO: generalize this mount code so it will mount every mount in ctr.config.Mounts
mountPoint := c.config.Rootfs
if mountPoint == "" {
mountPoint, err = c.mount()
@@ -1212,7 +1206,7 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten
if c.runtime.config.HooksDirNotExistFatal || !os.IsNotExist(err) {
return nil, err
}
- logrus.Warnf("failed to load hooks: {}", err)
+ logrus.Warnf("failed to load hooks: %q", err)
return nil, nil
}
hooks, err := manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0)
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index ffb82cc94..8861d7728 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -514,7 +514,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return c.save()
}
-func (c *Container) restore(ctx context.Context, keep bool) (err error) {
+func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
if !criu.CheckForCriu() {
return errors.Errorf("restoring a container requires at least CRIU %d", criu.MinCriuVersion)
@@ -602,7 +602,7 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) {
// Cleanup for a working restore.
c.removeConmonFiles()
- if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, true); err != nil {
+ if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, &options); err != nil {
return err
}
@@ -610,7 +610,7 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) {
c.state.State = ContainerStateRunning
- if !keep {
+ if !options.Keep {
// Delete all checkpoint related files. At this point, in theory, all files
// should exist. Still ignoring errors for now as the container should be
// restored and running. Not erroring out just because some cleanup operation
@@ -729,9 +729,10 @@ func (c *Container) generateResolvConf() (string, error) {
return "", errors.Wrapf(err, "unable to read %s", resolvPath)
}
- // Process the file to remove localhost nameservers
+ // Ensure that the container's /etc/resolv.conf is compatible with its
+ // network configuration.
// TODO: set ipv6 enable bool more sanely
- resolv, err := resolvconf.FilterResolvDNS(contents, true)
+ resolv, err := resolvconf.FilterResolvDNS(contents, true, c.config.CreateNetNS)
if err != nil {
return "", errors.Wrapf(err, "error parsing host resolv.conf")
}
@@ -764,7 +765,7 @@ func (c *Container) generateResolvConf() (string, error) {
// Build resolv.conf
if _, err = resolvconf.Build(destPath, nameservers, search, options); err != nil {
- return "", errors.Wrapf(err, "error building resolv.conf for container %s")
+ return "", errors.Wrapf(err, "error building resolv.conf for container %s", c.ID())
}
// Relabel resolv.conf for the container
diff --git a/libpod/kube.go b/libpod/kube.go
index 00db0033b..1a5f80878 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -240,7 +240,7 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
if c.User() != "" {
// It is *possible* that
- logrus.Debug("Looking in container for user: %s", c.User())
+ logrus.Debugf("Looking in container for user: %s", c.User())
u, err := lookup.GetUser(c.state.Mountpoint, c.User())
if err != nil {
return nil, err
diff --git a/libpod/oci.go b/libpod/oci.go
index ee1677b67..6ca3ef2e6 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -227,7 +227,7 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
return files, nil
}
-func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
+func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, restoreOptions *ContainerCheckpointOptions) (err error) {
var stderrBuf bytes.Buffer
runtimeDir, err := util.GetRootlessRuntimeDir()
@@ -289,8 +289,11 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
args = append(args, "--syslog")
}
- if restoreContainer {
+ if restoreOptions != nil {
args = append(args, "--restore", ctr.CheckpointPath())
+ if restoreOptions.TCPEstablished {
+ args = append(args, "--restore-arg", "--tcp-established")
+ }
}
logrus.WithFields(logrus.Fields{
@@ -333,7 +336,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
cmd.ExtraFiles = append(cmd.ExtraFiles, ports...)
}
- if rootless.IsRootless() {
+ if ctr.config.NetMode.IsSlirp4netns() {
ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
if err != nil {
return errors.Wrapf(err, "failed to create rootless network sync pipe")
@@ -588,6 +591,9 @@ func (r *OCIRuntime) startContainer(ctr *Container) error {
return err
}
env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok {
+ env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify))
+ }
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "start", ctr.ID()); err != nil {
return err
}
@@ -866,6 +872,9 @@ func (r *OCIRuntime) checkpointContainer(ctr *Container, options ContainerCheckp
if options.KeepRunning {
args = append(args, "--leave-running")
}
+ if options.TCPEstablished {
+ args = append(args, "--tcp-established")
+ }
args = append(args, ctr.ID())
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
}
diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go
index b159eae78..2737a641e 100644
--- a/libpod/oci_linux.go
+++ b/libpod/oci_linux.go
@@ -65,10 +65,10 @@ func newPipe() (parent *os.File, child *os.File, err error) {
// CreateContainer creates a container in the OCI runtime
// TODO terminal support for container
// Presently just ignoring conmon opts related to it
-func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
+func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreOptions *ContainerCheckpointOptions) (err error) {
if ctr.state.UserNSRoot == "" {
// no need of an intermediate mount ns
- return r.createOCIContainer(ctr, cgroupParent, restoreContainer)
+ return r.createOCIContainer(ctr, cgroupParent, restoreOptions)
}
var wg sync.WaitGroup
wg.Add(1)
@@ -106,7 +106,7 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor
if err != nil {
return
}
- err = r.createOCIContainer(ctr, cgroupParent, restoreContainer)
+ err = r.createOCIContainer(ctr, cgroupParent, restoreOptions)
}()
wg.Wait()
diff --git a/libpod/oci_unsupported.go b/libpod/oci_unsupported.go
index b133eb402..8c084d1e2 100644
--- a/libpod/oci_unsupported.go
+++ b/libpod/oci_unsupported.go
@@ -15,7 +15,7 @@ func newPipe() (parent *os.File, child *os.File, err error) {
return nil, nil, ErrNotImplemented
}
-func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
+func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreOptions *ContainerCheckpointOptions) (err error) {
return ErrNotImplemented
}
diff --git a/libpod/options.go b/libpod/options.go
index 507847d65..7f4e3ac6b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -7,6 +7,7 @@ import (
"regexp"
"syscall"
+ "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/cri-o/ocicni/pkg/ocicni"
@@ -817,7 +818,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
// namespace with a minimal configuration.
// An optional array of port mappings can be provided.
// Conflicts with WithNetNSFrom().
-func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, networks []string) CtrCreateOption {
+func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
@@ -831,6 +832,7 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netwo
ctr.config.CreateNetNS = true
ctr.config.PortMappings = portMappings
ctr.config.Networks = networks
+ ctr.config.NetMode = namespaces.NetworkMode(netmode)
return nil
}
diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go
index 46162c7ef..39a25c004 100644
--- a/libpod/pod_internal.go
+++ b/libpod/pod_internal.go
@@ -48,7 +48,7 @@ func (p *Pod) updatePod() error {
// Save pod state to database
func (p *Pod) save() error {
if err := p.runtime.state.SavePod(p); err != nil {
- return errors.Wrapf(err, "error saving pod %s state")
+ return errors.Wrapf(err, "error saving pod %s state", p.ID())
}
return nil
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 450a2fb32..8a5dbef56 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -50,7 +50,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID
// Since user namespace sharing is not implemented, we only need to check if it's rootless
networks := make([]string, 0)
- options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, networks))
+ netmode := "bridge"
+ if isRootless {
+ netmode = "slirp4netns"
+ }
+ options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, netmode, networks))
return r.newContainer(ctx, g.Config, options...)
}
diff --git a/libpod/util.go b/libpod/util.go
index 7007b29cd..aa3494529 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/image/signature"
"github.com/containers/image/types"
"github.com/containers/libpod/pkg/util"
+ "github.com/fsnotify/fsnotify"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
@@ -90,31 +91,64 @@ func MountExists(specMounts []spec.Mount, dest string) bool {
}
// WaitForFile waits until a file has been created or the given timeout has occurred
-func WaitForFile(path string, timeout time.Duration) error {
+func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, error) {
done := make(chan struct{})
chControl := make(chan struct{})
+
+ var inotifyEvents chan fsnotify.Event
+ var timer chan struct{}
+ watcher, err := fsnotify.NewWatcher()
+ if err == nil {
+ if err := watcher.Add(filepath.Dir(path)); err == nil {
+ inotifyEvents = watcher.Events
+ }
+ defer watcher.Close()
+ }
+ if inotifyEvents == nil {
+ // If for any reason we fail to create the inotify
+ // watcher, fallback to polling the file
+ timer = make(chan struct{})
+ go func() {
+ select {
+ case <-chControl:
+ close(timer)
+ return
+ default:
+ time.Sleep(25 * time.Millisecond)
+ timer <- struct{}{}
+ }
+ }()
+ }
+
go func() {
for {
select {
case <-chControl:
return
- default:
+ case <-timer:
+ _, err := os.Stat(path)
+ if err == nil {
+ close(done)
+ return
+ }
+ case <-inotifyEvents:
_, err := os.Stat(path)
if err == nil {
close(done)
return
}
- time.Sleep(25 * time.Millisecond)
}
}
}()
select {
+ case e := <-chWait:
+ return true, e
case <-done:
- return nil
+ return false, nil
case <-time.After(timeout):
close(chControl)
- return errors.Wrapf(ErrInternal, "timed out waiting for file %s", path)
+ return false, errors.Wrapf(ErrInternal, "timed out waiting for file %s", path)
}
}
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index bee833fa9..832efd554 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -223,7 +223,12 @@ func (n NetworkMode) IsBridge() bool {
return n == "bridge"
}
+// IsSlirp4netns indicates if we are running a rootless network stack
+func (n NetworkMode) IsSlirp4netns() bool {
+ return n == "slirp4netns"
+}
+
// IsUserDefined indicates user-created network
func (n NetworkMode) IsUserDefined() bool {
- return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
+ return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns()
}
diff --git a/pkg/resolvconf/resolvconf.go b/pkg/resolvconf/resolvconf.go
index fccd60093..e85bcb377 100644
--- a/pkg/resolvconf/resolvconf.go
+++ b/pkg/resolvconf/resolvconf.go
@@ -103,13 +103,21 @@ func GetLastModified() *File {
}
// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs:
-// 1. It looks for localhost (127.*|::1) entries in the provided
+// 1. If a netns is enabled, it looks for localhost (127.*|::1) entries in the provided
// resolv.conf, removing local nameserver entries, and, if the resulting
// cleaned config has no defined nameservers left, adds default DNS entries
// 2. Given the caller provides the enable/disable state of IPv6, the filter
// code will remove all IPv6 nameservers if it is not enabled for containers
//
-func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
+func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool, netnsEnabled bool) (*File, error) {
+ // If we're using the host netns, we have nothing to do besides hash the file.
+ if !netnsEnabled {
+ hash, err := ioutils.HashData(bytes.NewReader(resolvConf))
+ if err != nil {
+ return nil, err
+ }
+ return &File{Content: resolvConf, Hash: hash}, nil
+ }
cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{})
// if IPv6 is not enabled, also clean out any IPv6 address nameserver
if !ipv6Enabled {
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 6a0642ee7..a0fd40318 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -391,11 +391,11 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
isRootless := rootless.IsRootless()
- postConfigureNetNS := isRootless || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
+ postConfigureNetNS := c.NetMode.IsSlirp4netns() || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
if isRootless && len(portBindings) > 0 {
return nil, errors.New("port bindings are not yet supported by rootless containers")
}
- options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, networks))
+ options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
if c.PidMode.IsContainer() {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index b1cca2c9e..05be00864 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -453,6 +453,9 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error {
} else if IsPod(string(netMode)) {
logrus.Debug("Using pod netmode, unless pod is not sharing")
return nil
+ } else if netMode.IsSlirp4netns() {
+ logrus.Debug("Using slirp4netns netmode")
+ return nil
} else if netMode.IsUserDefined() {
logrus.Debug("Using user defined netmode")
return nil
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index ca1a57048..f9a2db9c8 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/pkg/signal"
@@ -126,7 +127,11 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru
// NETWORK MODE
networkMode := create.Net_mode
if networkMode == "" {
- networkMode = "bridge"
+ if rootless.IsRootless() {
+ networkMode = "slirp4netns"
+ } else {
+ networkMode = "bridge"
+ }
}
// WORKING DIR
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 1e4f1eeac..fe614e911 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -2,6 +2,7 @@ package integration
import (
"fmt"
+ "net"
"os"
"github.com/containers/libpod/pkg/criu"
@@ -27,6 +28,10 @@ var _ = Describe("Podman checkpoint", func() {
if !criu.CheckForCriu() {
Skip("CRIU is missing or too old.")
}
+ hostInfo := podmanTest.Host
+ if hostInfo.Distribution == "fedora" && hostInfo.Version == "29" {
+ Skip("Checkpoint tests appear to fail on F29.")
+ }
})
AfterEach(func() {
@@ -126,4 +131,164 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
})
+
+ It("podman checkpoint latest running container", func() {
+ session1 := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--name", "first", "-d", ALPINE, "top"})
+ session1.WaitWithDefaultTimeout()
+ Expect(session1.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--name", "second", "-d", ALPINE, "top"})
+ session2.WaitWithDefaultTimeout()
+ Expect(session2.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"container", "checkpoint", "-l"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+ ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps.ExitCode()).To(Equal(0))
+ Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeTrue())
+ Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse())
+
+ result = podmanTest.Podman([]string{"container", "restore", "-l"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+ Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited")))
+
+ result = podmanTest.Podman([]string{"rm", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("podman checkpoint all running container", func() {
+ session1 := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--name", "first", "-d", ALPINE, "top"})
+ session1.WaitWithDefaultTimeout()
+ Expect(session1.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--name", "second", "-d", ALPINE, "top"})
+ session2.WaitWithDefaultTimeout()
+ Expect(session2.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"container", "checkpoint", "-a"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps.ExitCode()).To(Equal(0))
+ Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse())
+ Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse())
+
+ result = podmanTest.Podman([]string{"container", "restore", "-a"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+ Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited")))
+
+ result = podmanTest.Podman([]string{"rm", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("podman checkpoint container with established tcp connections", func() {
+ Skip("Seems to not work (yet) in CI")
+ podmanTest.RestoreArtifact(redis)
+ session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--network", "host", "-d", redis})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // Open a network connection to the redis server
+ conn, err := net.Dial("tcp", "127.0.0.1:6379")
+ if err != nil {
+ os.Exit(1)
+ }
+ // This should fail as the container has established TCP connections
+ result := podmanTest.Podman([]string{"container", "checkpoint", "-l"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(125))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ // Now it should work thanks to "--tcp-established"
+ result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "--tcp-established"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ // Restore should fail as the checkpoint image contains established TCP connections
+ result = podmanTest.Podman([]string{"container", "restore", "-l"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(125))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ // Now it should work thanks to "--tcp-established"
+ result = podmanTest.Podman([]string{"container", "restore", "-l", "--tcp-established"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"rm", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ conn.Close()
+ })
+
+ It("podman checkpoint with --leave-running", func() {
+ session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "-d", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+
+ // Checkpoint container, but leave it running
+ result := podmanTest.Podman([]string{"container", "checkpoint", "--leave-running", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ // Make sure it is still running
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ // Stop the container
+ result = podmanTest.Podman([]string{"container", "stop", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ // Restore the stopped container from the previous checkpoint
+ result = podmanTest.Podman([]string{"container", "restore", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"rm", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
})
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 52507c083..f4f154ef2 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -181,6 +181,12 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration
return &PodmanSessionIntegration{podmanSession}
}
+// PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment
+func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, env []string) *PodmanSessionIntegration {
+ podmanSession := p.PodmanAsUserBase(args, uid, gid, env)
+ return &PodmanSessionIntegration{podmanSession}
+}
+
// PodmanPID execs podman and returns its PID
func (p *PodmanTestIntegration) PodmanPID(args []string) (*PodmanSessionIntegration, int) {
podmanOptions := p.MakeOptions(args)
diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go
index 676459416..b15e6731b 100644
--- a/test/e2e/rootless_test.go
+++ b/test/e2e/rootless_test.go
@@ -56,6 +56,7 @@ var _ = Describe("Podman rootless", func() {
commands := []string{"help", "version"}
for _, v := range commands {
env := os.Environ()
+ env = append(env, "USER=foo")
cmd := podmanTest.PodmanAsUser([]string{v}, 1000, 1000, env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
@@ -122,6 +123,7 @@ var _ = Describe("Podman rootless", func() {
env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir))
env = append(env, fmt.Sprintf("HOME=%s", home))
env = append(env, "PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS=1")
+ env = append(env, "USER=foo")
cmd := rootlessTest.PodmanAsUser([]string{"pod", "create", "--infra=false"}, 1000, 1000, env)
cmd.WaitWithDefaultTimeout()
@@ -152,6 +154,7 @@ var _ = Describe("Podman rootless", func() {
env := os.Environ()
env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir))
env = append(env, fmt.Sprintf("HOME=%s", home))
+ env = append(env, "USER=foo")
cmd := podmanTest.PodmanAsUser([]string{"search", "docker.io/busybox"}, 1000, 1000, env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
@@ -165,6 +168,7 @@ var _ = Describe("Podman rootless", func() {
env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir))
env = append(env, fmt.Sprintf("HOME=%s", home))
env = append(env, "PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS=1")
+ env = append(env, "USER=foo")
allArgs := append([]string{"run"}, args...)
allArgs = append(allArgs, "--rootfs", mountPath, "echo", "hello")
@@ -221,6 +225,14 @@ var _ = Describe("Podman rootless", func() {
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
+ if len(args) == 0 {
+ cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l"}, 1000, 1000, env)
+ cmd.WaitWithDefaultTimeout()
+ Expect(cmd.ExitCode()).To(Equal(0))
+ data := cmd.InspectContainerToJSON()
+ Expect(data[0].HostConfig.NetworkMode).To(ContainSubstring("slirp4netns"))
+ }
+
if !canUseExec {
Skip("ioctl(NS_GET_PARENT) not supported.")
}
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 4ed1fbed7..38504828b 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -3,6 +3,7 @@ package integration
import (
"fmt"
"io/ioutil"
+ "net"
"os"
"path/filepath"
"strings"
@@ -287,14 +288,27 @@ var _ = Describe("Podman run", func() {
})
It("podman run notify_socket", func() {
- sock := "/run/notify"
+ host := GetHostDistributionInfo()
+ if host.Distribution != "rhel" && host.Distribution != "centos" && host.Distribution != "fedora" {
+ Skip("this test requires a working runc")
+ }
+ sock := filepath.Join(podmanTest.TempDir, "notify")
+ addr := net.UnixAddr{
+ Name: sock,
+ Net: "unixgram",
+ }
+ socket, err := net.ListenUnixgram("unixgram", &addr)
+ Expect(err).To(BeNil())
+ defer os.Remove(sock)
+ defer socket.Close()
+
os.Setenv("NOTIFY_SOCKET", sock)
+ defer os.Unsetenv("NOTIFY_SOCKET")
+
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "NOTIFY_SOCKET"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- match, _ := session.GrepString(sock)
- Expect(match).Should(BeTrue())
- os.Unsetenv("NOTIFY_SOCKET")
+ Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
})
It("podman run log-opt", func() {
diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go
index b172cd81e..5c229b9b4 100644
--- a/test/e2e/stop_test.go
+++ b/test/e2e/stop_test.go
@@ -57,6 +57,20 @@ var _ = Describe("Podman stop", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman stop stopped container", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"stop", "test1"})
+ session2.WaitWithDefaultTimeout()
+ Expect(session2.ExitCode()).To(Equal(0))
+
+ session3 := podmanTest.Podman([]string{"stop", "test1"})
+ session3.WaitWithDefaultTimeout()
+ Expect(session3.ExitCode()).To(Equal(0))
+ })
+
It("podman stop all containers", func() {
session := podmanTest.RunTopContainer("test1")
session.WaitWithDefaultTimeout()
diff --git a/test/utils/podmantest_test.go b/test/utils/podmantest_test.go
index 87f756920..60e3e2a97 100644
--- a/test/utils/podmantest_test.go
+++ b/test/utils/podmantest_test.go
@@ -19,11 +19,11 @@ var _ = Describe("PodmanTest test", func() {
FakeOutputs = make(map[string][]string)
})
- It("Test PodmanAsUser", func() {
+ It("Test PodmanAsUserBase", func() {
FakeOutputs["check"] = []string{"check"}
os.Setenv("HOOK_OPTION", "hook_option")
env := os.Environ()
- session := podmanTest.PodmanAsUser([]string{"check"}, 1000, 1000, env)
+ session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, env)
os.Unsetenv("HOOK_OPTION")
session.WaitWithDefaultTimeout()
Expect(session.Command.Process).ShouldNot(BeNil())
diff --git a/test/utils/utils.go b/test/utils/utils.go
index c9409c9d4..288c768d4 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -56,9 +56,9 @@ func (p *PodmanTest) MakeOptions(args []string) []string {
return p.PodmanMakeOptions(args)
}
-// PodmanAsUser exec podman as user. uid and gid is set for credentials useage. env is used
+// PodmanAsUserBase exec podman as user. uid and gid is set for credentials useage. env is used
// to record the env for debugging
-func (p *PodmanTest) PodmanAsUser(args []string, uid, gid uint32, env []string) *PodmanSession {
+func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, env []string) *PodmanSession {
var command *exec.Cmd
podmanOptions := p.MakeOptions(args)
@@ -86,7 +86,7 @@ func (p *PodmanTest) PodmanAsUser(args []string, uid, gid uint32, env []string)
// PodmanBase exec podman with default env.
func (p *PodmanTest) PodmanBase(args []string) *PodmanSession {
- return p.PodmanAsUser(args, 0, 0, nil)
+ return p.PodmanAsUserBase(args, 0, 0, nil)
}
// WaitForContainer waits on a started container