summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml5
-rw-r--r--.papr.sh3
-rw-r--r--.papr.yml1
-rw-r--r--Makefile4
-rw-r--r--RELEASE_NOTES.md35
-rw-r--r--changelog.txt101
-rw-r--r--cmd/podman/kill.go44
-rw-r--r--cmd/podman/pause.go65
-rw-r--r--cmd/podman/restart.go89
-rw-r--r--cmd/podman/rm.go26
-rw-r--r--cmd/podman/run.go2
-rw-r--r--cmd/podman/shared/parallel.go37
-rw-r--r--cmd/podman/stop.go21
-rw-r--r--cmd/podman/unpause.go65
-rw-r--r--cmd/podman/utils.go17
-rw-r--r--completions/bash/podman16
-rw-r--r--contrib/cirrus/README.md75
-rwxr-xr-xcontrib/cirrus/build_vm_images.sh4
-rw-r--r--contrib/cirrus/lib.sh39
-rwxr-xr-xcontrib/cirrus/optional_system_test.sh24
-rw-r--r--contrib/cirrus/packer/fedora_setup.sh3
-rw-r--r--contrib/cirrus/packer/ubuntu_setup.sh7
-rw-r--r--contrib/python/podman/podman/libs/images.py2
-rw-r--r--contrib/python/podman/test/test_pods_ctnrs.py3
-rw-r--r--contrib/python/pypodman/pypodman/lib/__init__.py5
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/commit_action.py32
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/export_action.py22
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/history_action.py2
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/images_action.py8
-rw-r--r--contrib/python/pypodman/pypodman/lib/actions/import_action.py41
-rw-r--r--contrib/python/pypodman/pypodman/lib/parser_actions.py51
-rw-r--r--contrib/python/pypodman/pypodman/lib/report.py29
-rw-r--r--contrib/spec/podman.spec.in2
-rw-r--r--docs/podman-build.1.md2
-rw-r--r--docs/podman-container.1.md1
-rw-r--r--docs/podman-create.1.md12
-rw-r--r--docs/podman-kill.1.md2
-rw-r--r--docs/podman-pause.1.md19
-rw-r--r--docs/podman-restart.1.md26
-rw-r--r--docs/podman-rm.1.md19
-rw-r--r--docs/podman-run.1.md13
-rw-r--r--docs/podman-unpause.1.md20
-rw-r--r--docs/podman.1.md11
-rw-r--r--docs/tutorials/podman_tutorial.md99
-rw-r--r--libpod/container_api.go23
-rw-r--r--libpod/container_internal.go106
-rw-r--r--libpod/container_internal_linux.go99
-rw-r--r--libpod/info.go2
-rw-r--r--libpod/networking_linux.go11
-rw-r--r--libpod/oci.go61
-rw-r--r--libpod/oci_linux.go3
-rw-r--r--libpod/runtime_ctr.go16
-rw-r--r--libpod/runtime_pod_linux.go19
-rw-r--r--pkg/lookup/lookup.go8
-rw-r--r--pkg/rootless/rootless_linux.go3
-rw-r--r--pkg/util/utils.go8
-rw-r--r--seccomp.json33
-rw-r--r--test/e2e/config.go9
-rw-r--r--test/e2e/config_amd64.go11
-rw-r--r--test/e2e/config_ppc64le.go11
-rw-r--r--test/e2e/libpod_suite_test.go15
-rw-r--r--test/e2e/load_test.go3
-rw-r--r--test/e2e/logs_test.go6
-rw-r--r--test/e2e/pause_test.go85
-rw-r--r--test/e2e/pull_test.go4
-rw-r--r--test/e2e/push_test.go6
-rw-r--r--test/e2e/restart_test.go40
-rw-r--r--test/e2e/rmi_test.go11
-rw-r--r--test/e2e/rootless_test.go2
-rw-r--r--test/e2e/run_dns_test.go9
-rw-r--r--test/e2e/run_signal_test.go3
-rw-r--r--test/e2e/run_test.go10
-rw-r--r--test/e2e/search_test.go18
-rw-r--r--version/version.go2
74 files changed, 1319 insertions, 422 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index ae660394b..f78205a49 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -47,6 +47,7 @@ full_vm_testing_task:
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
#image_name: "centos-7-v20180911-libpod-fce09afe"
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
+
timeout_in: 120m
# Every *_script runs in sequence, for each task. The name prefix is for
@@ -62,6 +63,8 @@ full_vm_testing_task:
integration_test_script: $SCRIPT_BASE/integration_test.sh
+ optional_system_test_script: $SCRIPT_BASE/optional_system_test.sh
+
success_script: $SCRIPT_BASE/success.sh
@@ -99,6 +102,8 @@ build_vm_images_task:
# Version of packer to use
PACKER_VER: "1.3.1"
+ # VMs created by packer are not cleaned up by cirrus
+ auto_cancellation: $CI != "true"
gce_instance:
image_name: "image-builder-image" # Simply CentOS 7 + packer dependencies
diff --git a/.papr.sh b/.papr.sh
index 120b3d94b..284326709 100644
--- a/.papr.sh
+++ b/.papr.sh
@@ -139,6 +139,3 @@ if [ $integrationtest -eq 1 ]; then
fi
make ginkgo GOPATH=/go $INTEGRATION_TEST_ENVS
fi
-
-
-exit 0
diff --git a/.papr.yml b/.papr.yml
index 8a043a04a..26b527a36 100644
--- a/.papr.yml
+++ b/.papr.yml
@@ -163,6 +163,7 @@ packages:
- python3-dateutil
- python3-psutil
- container-selinux
+ - https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/54.dev.git00dc700.fc28/x86_64/runc-1.0.0-54.dev.git00dc700.fc28.x86_64.rpm
tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
diff --git a/Makefile b/Makefile
index d8a870fa2..04d6230d6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
GO ?= go
DESTDIR ?= /
-EPOCH_TEST_COMMIT ?= 733cfe96819e1dc044e982b5321b3c902d1a47c6
+EPOCH_TEST_COMMIT ?= 921ccac10c47e0865ec5e4ba00ebb69a03d89473
HEAD ?= HEAD
CHANGELOG_BASE ?= HEAD~
CHANGELOG_TARGET ?= HEAD
@@ -23,7 +23,7 @@ BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude
ifneq (,$(findstring varlink,$(BUILDTAGS)))
PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go
endif
-CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null | echo docker)
+CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
HAS_PYTHON3 := $(shell command -v python3 2>/dev/null)
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 6c7bf1a8f..9cdf3faae 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,40 @@
# Release Notes
+## 0.11.1
+### Features
+- Added `--all` and `--latest` flags to `podman checkpoint` and `podman restore`
+- Added `--max-workers` flag to all Podman commands that support operating in parallel, allowing the maximum number of parallel workers used to be specified
+- Added `--all` flag to `podman restart`
+
+### Bugfixes
+- Fixed a bug where `podman port -l` would segfault if no containers were present
+- Fixed a bug where `podman stats -a` would error if containers were present but not running
+- Fixed a bug where container status checks would sometimes leave zombie OCI runtime processes
+- Fixed checkpoint and restore code to verify an appropriate version of `criu` is being used
+- Fixed a bug where environment variables with no specified value (e.g. `-e FOO`) caused errors (they are now added as empty)
+- Fixed a bug where rootless Podman would attempt to configure the system firewall, causing errors on some systems where iptables is not in the user's PATH
+- Fixed a bug where rootless Podman was unable to successfully write the container ID to a file when `--cid-file` was specified to `podman run`
+- Fixed a bug where `podman unmount` would refuse to unmount a container if it was running (the unmount will now be deferred until the container stops)
+- Fixed a bug where rootless `podman attach` would fail to attach due to a too-long path name
+- Fixed a bug where `podman info` was not properly reporting the Git commit Podman was built from
+- Fixed a bug where `podman run --interactive` was not holding STDIN open when `-a` flag was specified
+- Fixed a bug where Podman with the `cgroupfs` CGroup driver was sometimes not successfully removing pod CGroups
+- Fixed a bug where rootless Podman was unable to run systemd containers (note that this also requires an update to systemd)
+- Fixed a bug where `podman run` with the `--user` flag would fail if the container image did not contain `/etc/passwd` or `/etc/group`
+
+### Misc
+- `podman rm`, `podman restart`, `podman kill`, `podman pause`, and `podman unpause` now operate in parallel, greatly improving speed when multiple containers are specified
+- `podman create`, `podman run`, and `podman ps` have a number of improvements which should greatly increase their speed
+- Greatly improved performance and reduced memory utilization of container status checks, which should improve the speed of most Podman commands
+- Improve ability of `podman runlabel` to run commands that are not Podman
+- Podman containers with an IP address now add their hostnames to `/etc/hosts`
+- Changed default location of temporary libpod files in rootless Podman
+- Updated the default Podman seccomp profile
+
+### Compatability
+Several paths related to rootless Podman had their default values changed in this release.
+If paths were not hardcoded in libpod.conf, your system may lose track of running containers and believe they are newly-created.
+
## 0.10.1.3
### Bugfixes
- Fixed a bug where `podman build` would not work while any containers were running
diff --git a/changelog.txt b/changelog.txt
index ace41f1d9..9aaec0e74 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,104 @@
+- Changelog for v0.11.1 (2018-11-08)
+ * Update release notes for 0.11.1
+ * update seccomp.json
+ * Touch up --log* options and daemons in man pages
+ * Fix run --hostname test that started failing post-merge
+ * move defer'd function declaration ahead of prepare error return
+ * Don't fail if /etc/passwd or /etc/group does not exists
+ * Print error status code if we fail to parse it
+ * Properly set Running state when starting containers
+ * Fix misspelling
+ * Retrieve container PID from conmon
+ * If a container ceases to exist in runc, set exit status
+ * EXPERIMENTAL: Do not call out to runc for sync
+ * Actually save changes from post-stop sync
+ * rootless: mount /sys/fs/cgroup/systemd from the host
+ * rootless: don't bind mount /sys/fs/cgroup/systemd in systemd mode
+ * Add hostname to /etc/hosts
+ * Temporarily fix the Python tests to fix some PRs
+ * Remove conmon cgroup before pod cgroup for cgroupfs
+ * Fix cleanup for "Pause a bunch of running containers"
+ * --interactive shall keep STDIN attached even when not explicitly called out
+ * Do never override podman with docker
+ * Make kill, pause, and unpause parallel.
+ * Fix long image name handling
+ * Make restart parallel and add --all
+ * Add ChangeAction to parse sub-options from --change
+ * replace quay.io/baude to quay.io/libpod
+ * Change humanize to use MB vs MiB.
+ * allow ppc64le to pass libpod integration tests
+ * Cirrus-CI: Add option to run system-tests
+ * Cirrus: Skip rebuilding images unless instructed
+ * Cirrus: Disable image build job abort on push
+ * Cirrus: Add a readme
+ * Ubuntu VM image build: try update twice
+ * Cirrus: Enable updating F28 image
+ * rootless: do not add an additional /run to runroot
+ * rootless: avoid hang on failed slirp4netns
+ * Fix setting of version information
+ * runtime: do not allow runroot longer than 50 characters
+ * attach: fix attach when cuid is too long
+ * truncate command output in ps by default
+ * Update the runc commit used for testing
+ * make various changes to ps output
+ * Sync default config with libpod.conf
+ * Use two spaces to pad PS fields
+ * unmount: fix error logic
+ * get user and group information using securejoin and runc's user library
+ * CONTRIBUTING.md: add section about describing changes
+ * Change to exported name in ParseDevice
+ * Vendor in latest containers/storage
+ * fix bug in rm -fa parallel deletes
+ * Ensure test container in running state
+ * Add tests for selinux labels
+ * Add --max-workers and heuristics for parallel operations
+ * Increase security and performance when looking up groups
+ * run prepare in parallel
+ * downgrade runc due a rootless bug
+ * runlabel: run any command
+ * Eat our own dogfood
+ * vendor: update containers/storage
+ * Add support for /usr/local installation
+ * create: fix writing cidfile when using rootless
+ * Explain the device format in man pages
+ * read conmon output and convert to json in two steps
+ * Cirrus: Use images w/ buildah fix
+ * Add --all and --latest to checkpoint/restore
+ * Use the newly added getAllOrLatestContainers() function
+ * Use the new checkAllAndLatest() function
+ * Also factor out getAllOrLatestContainers() function
+ * Add checkAllAndLatest() function
+ * Downgrade code to support python3.4
+ * Allow containers/storage to handle on SELinux labeling
+ * Use more reliable check for rootless for firewall init
+ * Vendor in latest containers/storage opencontainers/selinux
+ * Make podman ps fast
+ * Support auth file environment variable in podman build
+ * fix environment variable parsing
+ * tests: use existing CRIU version check
+ * Use the CRIU version check in checkpoint/restore
+ * Add helper function to read out CRIU version
+ * vendor in go-criu and dependencies
+ * oci: cleanup process status
+ * Handle http/https in registry given to login/out
+ * re-enable f29 testing
+ * correct stats err with non-running containers
+ * Use restoreArtifacts to save time in integration tests
+ * Make rm faster
+ * Fix man page to show info on storage
+ * Move rootless directory handling to the libpod/pkg/util directory
+ * Fix podman port -l
+ * Fix trivial missing markup in manpage
+ * Cirrus: Install CRIU in test images
+ * Cirrus: Use different CNI_COMMIT for Fedora
+ * Fix Cirrus/Packer VM image building
+ * Revert "Cirrus: Enable debugging delay on non-zero exit"
+ * Cirrus: IRC message when cirrus testing successful
+ * cirrus: Add simple IRC messenger
+ * fix NOTIFY_SOCKET in e2e testfix NOTIFY_SOCKET in e2e tests
+ * Bump gitvalidation epoch
+ * Bump to v0.10.2-dev
+
- Changelog for v0.10.1.3 (2018-10-17)
* Update release notes for 0.10.1.3
* Vendor in new new buildah/ci
diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go
index 7ca5bd7c5..cfe4b4218 100644
--- a/cmd/podman/kill.go
+++ b/cmd/podman/kill.go
@@ -1,15 +1,16 @@
package main
import (
- "os"
+ "fmt"
"syscall"
- "fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -41,6 +42,11 @@ var (
// killCmd kills one or more containers with a signal
func killCmd(c *cli.Context) error {
+ var (
+ killFuncs []shared.ParallelWorkerInput
+ killSignal uint = uint(syscall.SIGTERM)
+ )
+
if err := checkAllAndLatest(c); err != nil {
return err
}
@@ -56,7 +62,6 @@ func killCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
- var killSignal uint = uint(syscall.SIGTERM)
if c.String("signal") != "" {
// Check if the signalString provided by the user is valid
// Invalid signals will return err
@@ -67,17 +72,32 @@ func killCmd(c *cli.Context) error {
killSignal = uint(sysSignal)
}
- containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
+ containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
+ if err != nil {
+ if len(containers) == 0 {
+ return err
+ }
+ fmt.Println(err.Error())
+ }
for _, ctr := range containers {
- if err := ctr.Kill(killSignal); err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
- }
- lastError = errors.Wrapf(err, "unable to find container %v", ctr.ID())
- } else {
- fmt.Println(ctr.ID())
+ con := ctr
+ f := func() error {
+ return con.Kill(killSignal)
}
+
+ killFuncs = append(killFuncs, shared.ParallelWorkerInput{
+ ContainerID: con.ID(),
+ ParallelFunc: f,
+ })
}
- return lastError
+
+ maxWorkers := shared.Parallelize("kill")
+ if c.GlobalIsSet("max-workers") {
+ maxWorkers = c.GlobalInt("max-workers")
+ }
+ logrus.Debugf("Setting maximum workers to %d", maxWorkers)
+
+ killErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, killFuncs)
+ return printParallelOutput(killErrors, errCount)
}
diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go
index 203fa6070..fcb2f3cb8 100644
--- a/cmd/podman/pause.go
+++ b/cmd/podman/pause.go
@@ -1,15 +1,23 @@
package main
import (
- "fmt"
"os"
"github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
+ pauseFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "pause all running containers",
+ },
+ }
pauseDescription = `
podman pause
@@ -19,6 +27,7 @@ var (
Name: "pause",
Usage: "Pauses all the processes in one or more containers",
Description: pauseDescription,
+ Flags: pauseFlags,
Action: pauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
OnUsageError: usageErrorHandler,
@@ -26,6 +35,10 @@ var (
)
func pauseCmd(c *cli.Context) error {
+ var (
+ pauseContainers []*libpod.Container
+ pauseFuncs []shared.ParallelWorkerInput
+ )
if os.Geteuid() != 0 {
return errors.New("pause is not supported for rootless containers")
}
@@ -37,28 +50,44 @@ func pauseCmd(c *cli.Context) error {
defer runtime.Shutdown(false)
args := c.Args()
- if len(args) < 1 {
+ if len(args) < 1 && !c.Bool("all") {
return errors.Errorf("you must provide at least one container name or id")
}
-
- var lastError error
- for _, arg := range args {
- ctr, err := runtime.LookupContainer(arg)
+ if c.Bool("all") {
+ containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
if err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
- }
- lastError = errors.Wrapf(err, "error looking up container %q", arg)
- continue
+ return err
}
- if err = ctr.Pause(); err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
+ pauseContainers = append(pauseContainers, containers...)
+ } else {
+ for _, arg := range args {
+ ctr, err := runtime.LookupContainer(arg)
+ if err != nil {
+ return err
}
- lastError = errors.Wrapf(err, "failed to pause container %v", ctr.ID())
- } else {
- fmt.Println(ctr.ID())
+ pauseContainers = append(pauseContainers, ctr)
+ }
+ }
+
+ // Now assemble the slice of pauseFuncs
+ for _, ctr := range pauseContainers {
+ con := ctr
+
+ f := func() error {
+ return con.Pause()
}
+ pauseFuncs = append(pauseFuncs, shared.ParallelWorkerInput{
+ ContainerID: con.ID(),
+ ParallelFunc: f,
+ })
}
- return lastError
+
+ maxWorkers := shared.Parallelize("pause")
+ if c.GlobalIsSet("max-workers") {
+ maxWorkers = c.GlobalInt("max-workers")
+ }
+ logrus.Debugf("Setting maximum workers to %d", maxWorkers)
+
+ pauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, pauseFuncs)
+ return printParallelOutput(pauseErrors, errCount)
}
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index 7b48ef24e..630493ef4 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -1,18 +1,24 @@
package main
import (
- "context"
- "fmt"
- "os"
-
"github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
restartFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "restart all non-running containers",
+ },
+ cli.BoolFlag{
+ Name: "running",
+ Usage: "restart only running containers when --all is used",
+ },
cli.UintFlag{
Name: "timeout, time, t",
Usage: "Seconds to wait for stop before killing the container",
@@ -35,11 +41,18 @@ var (
)
func restartCmd(c *cli.Context) error {
+ var (
+ restartFuncs []shared.ParallelWorkerInput
+ containers []*libpod.Container
+ restartContainers []*libpod.Container
+ )
+
args := c.Args()
- if len(args) < 1 && !c.Bool("latest") {
+ runOnly := c.Bool("running")
+ all := c.Bool("all")
+ if len(args) < 1 && !c.Bool("latest") && !all {
return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID")
}
-
if err := validateFlags(c, restartFlags); err != nil {
return err
}
@@ -50,8 +63,6 @@ func restartCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
- var lastError error
-
timeout := c.Uint("timeout")
useTimeout := c.IsSet("timeout")
@@ -59,39 +70,57 @@ func restartCmd(c *cli.Context) error {
if c.Bool("latest") {
lastCtr, err := runtime.GetLatestContainer()
if err != nil {
- lastError = errors.Wrapf(err, "unable to get latest container")
- } else {
- ctrTimeout := lastCtr.StopTimeout()
- if useTimeout {
- ctrTimeout = timeout
- }
-
- lastError = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
+ return errors.Wrapf(err, "unable to get latest container")
}
- }
-
- for _, id := range args {
- ctr, err := runtime.LookupContainer(id)
+ restartContainers = append(restartContainers, lastCtr)
+ } else if runOnly {
+ containers, err = getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
if err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
+ return err
+ }
+ restartContainers = append(restartContainers, containers...)
+ } else if all {
+ containers, err = runtime.GetAllContainers()
+ if err != nil {
+ return err
+ }
+ restartContainers = append(restartContainers, containers...)
+ } else {
+ for _, id := range args {
+ ctr, err := runtime.LookupContainer(id)
+ if err != nil {
+ return err
}
- lastError = errors.Wrapf(err, "unable to find container %s", id)
- continue
+ restartContainers = append(restartContainers, ctr)
}
+ }
+ // We now have a slice of all the containers to be restarted. Iterate them to
+ // create restart Funcs with a timeout as needed
+ for _, ctr := range restartContainers {
+ con := ctr
ctrTimeout := ctr.StopTimeout()
if useTimeout {
ctrTimeout = timeout
}
- if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
- }
- lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID())
+ f := func() error {
+ return con.RestartWithTimeout(getContext(), ctrTimeout)
}
+
+ restartFuncs = append(restartFuncs, shared.ParallelWorkerInput{
+ ContainerID: con.ID(),
+ ParallelFunc: f,
+ })
+ }
+
+ maxWorkers := shared.Parallelize("restart")
+ if c.GlobalIsSet("max-workers") {
+ maxWorkers = c.GlobalInt("max-workers")
}
- return lastError
+ logrus.Debugf("Setting maximum workers to %d", maxWorkers)
+
+ restartErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, restartFuncs)
+ return printParallelOutput(restartErrors, errCount)
}
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 0fb5345ee..7c0569b78 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -46,9 +45,7 @@ Running containers will not be removed without the -f option.
// saveCmd saves the image to either docker-archive or oci
func rmCmd(c *cli.Context) error {
var (
- delContainers []*libpod.Container
- lastError error
- deleteFuncs []shared.ParallelWorkerInput
+ deleteFuncs []shared.ParallelWorkerInput
)
ctx := getContext()
@@ -65,7 +62,13 @@ func rmCmd(c *cli.Context) error {
return err
}
- delContainers, lastError = getAllOrLatestContainers(c, runtime, -1, "all")
+ delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all")
+ if err != nil {
+ if len(delContainers) == 0 {
+ return err
+ }
+ fmt.Println(err.Error())
+ }
for _, container := range delContainers {
con := container
@@ -84,14 +87,7 @@ func rmCmd(c *cli.Context) error {
}
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
- deleteErrors := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
- for cid, result := range deleteErrors {
- if result != nil {
- fmt.Println(result.Error())
- lastError = result
- continue
- }
- fmt.Println(cid)
- }
- return lastError
+ // Run the parallel funcs
+ deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
+ return printParallelOutput(deleteErrors, errCount)
}
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index e4b25eaf4..af6ced45d 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -96,8 +96,6 @@ func runCmd(c *cli.Context) error {
inputStream = nil
}
- inputStream = nil
-
attachTo := c.StringSlice("attach")
for _, stream := range attachTo {
switch strings.ToLower(stream) {
diff --git a/cmd/podman/shared/parallel.go b/cmd/podman/shared/parallel.go
index 03eba2f0b..e6ce50f95 100644
--- a/cmd/podman/shared/parallel.go
+++ b/cmd/podman/shared/parallel.go
@@ -30,9 +30,10 @@ func ParallelWorker(wg *sync.WaitGroup, jobs <-chan ParallelWorkerInput, results
// ParallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker
// int determines how many workers/threads should be premade.
-func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map[string]error {
+func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) (map[string]error, int) {
var (
- wg sync.WaitGroup
+ wg sync.WaitGroup
+ errorCount int
)
resultChan := make(chan containerError, len(functions))
@@ -62,9 +63,12 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
close(resultChan)
for ctrError := range resultChan {
results[ctrError.ContainerID] = ctrError.Err
+ if ctrError.Err != nil {
+ errorCount += 1
+ }
}
- return results
+ return results, errorCount
}
// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic
@@ -72,20 +76,37 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
func Parallelize(job string) int {
numCpus := runtime.NumCPU()
switch job {
+ case "kill":
+ if numCpus <= 3 {
+ return numCpus * 3
+ }
+ return numCpus * 4
+ case "pause":
+ if numCpus <= 3 {
+ return numCpus * 3
+ }
+ return numCpus * 4
+ case "ps":
+ return 8
+ case "restart":
+ return numCpus * 2
+ case "rm":
+ if numCpus <= 3 {
+ return numCpus * 3
+ } else {
+ return numCpus * 4
+ }
case "stop":
if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
- case "rm":
+ case "unpause":
if numCpus <= 3 {
return numCpus * 3
- } else {
- return numCpus * 4
}
- case "ps":
- return 8
+ return numCpus * 4
}
return 3
}
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index afeb49f76..04022839a 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -59,7 +59,13 @@ func stopCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
- containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
+ containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
+ if err != nil {
+ if len(containers) == 0 {
+ return err
+ }
+ fmt.Println(err.Error())
+ }
var stopFuncs []shared.ParallelWorkerInput
for _, ctr := range containers {
@@ -85,15 +91,6 @@ func stopCmd(c *cli.Context) error {
}
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
- stopErrors := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
-
- for cid, result := range stopErrors {
- if result != nil && result != libpod.ErrCtrStopped {
- fmt.Println(result.Error())
- lastError = result
- continue
- }
- fmt.Println(cid)
- }
- return lastError
+ stopErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
+ return printParallelOutput(stopErrors, errCount)
}
diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go
index a792aaf6d..d77e056f8 100644
--- a/cmd/podman/unpause.go
+++ b/cmd/podman/unpause.go
@@ -1,15 +1,23 @@
package main
import (
- "fmt"
"os"
"github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
+ unpauseFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "unpause all paused containers",
+ },
+ }
unpauseDescription = `
podman unpause
@@ -19,6 +27,7 @@ var (
Name: "unpause",
Usage: "Unpause the processes in one or more containers",
Description: unpauseDescription,
+ Flags: unpauseFlags,
Action: unpauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
OnUsageError: usageErrorHandler,
@@ -26,6 +35,10 @@ var (
)
func unpauseCmd(c *cli.Context) error {
+ var (
+ unpauseContainers []*libpod.Container
+ unpauseFuncs []shared.ParallelWorkerInput
+ )
if os.Geteuid() != 0 {
return errors.New("unpause is not supported for rootless containers")
}
@@ -37,28 +50,44 @@ func unpauseCmd(c *cli.Context) error {
defer runtime.Shutdown(false)
args := c.Args()
- if len(args) < 1 {
+ if len(args) < 1 && !c.Bool("all") {
return errors.Errorf("you must provide at least one container name or id")
}
-
- var lastError error
- for _, arg := range args {
- ctr, err := runtime.LookupContainer(arg)
+ if c.Bool("all") {
+ cs, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStatePaused, "paused")
if err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
- }
- lastError = errors.Wrapf(err, "error looking up container %q", arg)
- continue
+ return err
}
- if err = ctr.Unpause(); err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
+ unpauseContainers = append(unpauseContainers, cs...)
+ } else {
+ for _, arg := range args {
+ ctr, err := runtime.LookupContainer(arg)
+ if err != nil {
+ return err
}
- lastError = errors.Wrapf(err, "failed to unpause container %v", ctr.ID())
- } else {
- fmt.Println(ctr.ID())
+ unpauseContainers = append(unpauseContainers, ctr)
+ }
+ }
+
+ // Assemble the unpause funcs
+ for _, ctr := range unpauseContainers {
+ con := ctr
+ f := func() error {
+ return con.Unpause()
}
+
+ unpauseFuncs = append(unpauseFuncs, shared.ParallelWorkerInput{
+ ContainerID: con.ID(),
+ ParallelFunc: f,
+ })
}
- return lastError
+
+ maxWorkers := shared.Parallelize("unpause")
+ if c.GlobalIsSet("max-workers") {
+ maxWorkers = c.GlobalInt("max-workers")
+ }
+ logrus.Debugf("Setting maximum workers to %d", maxWorkers)
+
+ unpauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, unpauseFuncs)
+ return printParallelOutput(unpauseErrors, errCount)
}
diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go
index afeccb668..5735156c2 100644
--- a/cmd/podman/utils.go
+++ b/cmd/podman/utils.go
@@ -207,3 +207,20 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error
}
return pods, lastError
}
+
+//printParallelOutput takes the map of parallel worker results and outputs them
+// to stdout
+func printParallelOutput(m map[string]error, errCount int) error {
+ var lastError error
+ for cid, result := range m {
+ if result != nil {
+ if errCount > 1 {
+ fmt.Println(result.Error())
+ }
+ lastError = result
+ continue
+ }
+ fmt.Println(cid)
+ }
+ return lastError
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index 5cfed348f..c029f893a 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1,5 +1,6 @@
: ${PROG:=$(basename ${BASH_SOURCE})}
+
__podman_previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
@@ -1770,8 +1771,13 @@ _podman_restart() {
--timeout -t
"
local boolean_options="
+ --all
+ -a
--latest
- -l"
+ -l
+ --running
+ --timeout
+ -t"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@@ -1929,6 +1935,10 @@ _podman_save() {
}
_podman_pause() {
+ local boolean_options="
+ -a
+ --all
+ "
local options_with_args="
--help -h
"
@@ -2030,6 +2040,10 @@ _podman_stop() {
}
_podman_unpause() {
+ local boolean_options="
+ -a
+ --all
+ "
local options_with_args="
--help -h
"
diff --git a/contrib/cirrus/README.md b/contrib/cirrus/README.md
new file mode 100644
index 000000000..0d315c4f5
--- /dev/null
+++ b/contrib/cirrus/README.md
@@ -0,0 +1,75 @@
+![PODMAN logo](../../logo/podman-logo-source.svg)
+
+# Cirrus-CI
+
+Similar to other integrated github CI/CD services, Cirrus utilizes a simple
+YAML-based configuration/description file: ``.cirrus.yml``. Ref: https://cirrus-ci.org/
+
+## Workflow
+
+All tasks execute in parallel, unless there are conditions or dependencies
+which alter this behavior. Within each task, each script executes in sequence,
+so long as any previous script exited successfully. The overall state of each
+task (pass or fail) is set based on the exit status of the last script to execute.
+
+### ``full_vm_testing`` Task
+
+1. Unconditionally, spin up one VM per ``matrix: image_name`` item defined
+ in ``.cirrus.yml``. Once accessible, ``ssh`` into each VM and run the following
+ scripts.
+
+2. ``setup_environment.sh``: Configure root's ``.bash_profile``
+ for all subsequent scripts (each run in a new shell). Any
+ distribution-specific environment variables are also defined
+ here. For example, setting tags/flags to use compiling.
+
+3. ``verify_source.sh``: Perform per-distribution source
+ verification, lint-checking, etc. This acts as a minimal
+ gate, blocking extended use of VMs when a PR's code or commits
+ would otherwise not be accepted. Should run for less than a minute.
+
+4. ``unit_test.sh``: Execute unit-testing, as defined by the ``Makefile``.
+ This should execute within 10-minutes, but often much faster.
+
+5. ``integration_test.sh``: Execute integration-testing. This is
+ much more involved, and relies on access to external
+ resources like container images and code from other repositories.
+ Total execution time is capped at 2-hours (includes all the above)
+ but this script normally completes in less than an hour.
+
+### ``build_vm_images`` Task
+
+1. When a PR is merged (``$CIRRUS_BRANCH`` == ``master``), run another
+ round of the ``full_vm_testing`` task (above).
+
+2. After confirming the tests all pass post-merge, spin up a special VM
+ capable of communicating with the GCE API. Once accessible, ``ssh`` into
+ the special VM and run the following scripts.
+
+3. ``setup_environment.sh``: Configure root's ``.bash_profile``
+ for all subsequent scripts (each run in a new shell). Any
+ distribution-specific environment variables are also defined
+ here. For example, setting tags/flags to use compiling.
+
+4. ``build_vm_images.sh``: Examine the merged PR's description on github.
+ If it contains the magic string ``***CIRRUS: REBUILD IMAGES***``, then
+ continue. Otherwise display a message, take no further action, and
+ exit successfully. This prevents production of new VM images unless
+ they are called for, thereby saving the cost of needlessly storing them.
+
+5. If the magic string was found, utilize [the packer tool](http://packer.io/docs/)
+ to produce new VM images. Create a new VM from each base-image, connect
+ to them with ``ssh``, and perform these steps as defined by the
+ ``libpod_images.json`` file.
+
+ 1. Copy the current state of the repository into ``/tmp/libpod``.
+ 2. Execute distribution-specific scripts to prepare the image for
+ use by the ``full_vm_testing`` task (above).
+ 3. If successful, shut down each VM and create a new GCE Image
+ named after the base image and the commit sha of the merge.
+
+***Note:*** The ``.cirrus.yml`` file must be manually updated with the new
+images names, then the change sent in via a secondary pull-request. This
+ensures that all the ``full_vm_testing`` tasks can pass with the new images,
+before subjecting all future PRs to them. A workflow to automate this
+process is described in comments at the end of the ``.cirrus.yml`` file.
diff --git a/contrib/cirrus/build_vm_images.sh b/contrib/cirrus/build_vm_images.sh
index 80c689a6c..ffbb2d5d5 100755
--- a/contrib/cirrus/build_vm_images.sh
+++ b/contrib/cirrus/build_vm_images.sh
@@ -22,9 +22,7 @@ SCRIPT_BASE $SCRIPT_BASE
PACKER_BASE $PACKER_BASE
"
-# TODO: Skip building images if $CIRRUS_BRANCH =~ "master" and
-# commit message of $CIRRUS_CHANGE_IN_REPO contains a magic word
-# produced by 'commit_and_create_upstream_pr.sh' script (see .cirrus.yml)
+require_regex '\*\*\*\s*CIRRUS:\s*REBUILD\s*IMAGES\s*\*\*\*' 'Not re-building VM images'
show_env_vars
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 1e0052a65..4a3efb8ff 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -106,7 +106,10 @@ ircmsg() {
SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py"
NICK="podbot_$CIRRUS_TASK_ID"
NICK="${NICK:0:15}" # Any longer will break things
+ set +e
$SCRIPT $NICK $1
+ echo "Ignoring exit($?)"
+ set -e
}
# Run sudo in directory with GOPATH set
@@ -117,6 +120,22 @@ cdsudo() {
sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD"
}
+# Skip a build if $1 does not match in the PR Title/Description with message $2
+require_regex() {
+ req_env_var "
+ CIRRUS_CHANGE_MESSAGE $CIRRUS_CHANGE_MESSAGE
+ 1 $1
+ 2 $2
+ "
+ regex="$1"
+ msg="$2"
+ if ! echo "$CIRRUS_CHANGE_MESSAGE" | egrep -q "$regex"
+ then
+ echo "***** The PR Title/Description did not match the regular expression: $MAGIC_RE"
+ echo "***** $msg"
+ exit 0
+ fi
+}
# Helper/wrapper script to only show stderr/stdout on non-zero exit
install_ooe() {
@@ -142,8 +161,8 @@ EOF
install_cni_plugins() {
echo "Installing CNI Plugins from commit $CNI_COMMIT"
req_env_var "
- GOPATH $GOPATH
- CNI_COMMIT $CNI_COMMIT
+ GOPATH $GOPATH
+ CNI_COMMIT $CNI_COMMIT
"
DEST="$GOPATH/src/github.com/containernetworking/plugins"
rm -rf "$DEST"
@@ -160,9 +179,9 @@ install_runc(){
echo "Installing RunC from commit $RUNC_COMMIT"
echo "Platform is $OS_RELEASE_ID"
req_env_var "
- GOPATH $GOPATH
- RUNC_COMMIT $RUNC_COMMIT
- OS_RELEASE_ID $OS_RELEASE_ID
+ GOPATH $GOPATH
+ RUNC_COMMIT $RUNC_COMMIT
+ OS_RELEASE_ID $OS_RELEASE_ID
"
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]; then
echo "Running make install.libseccomp.sudo for ubuntu"
@@ -202,8 +221,8 @@ install_buildah() {
install_conmon(){
echo "Installing conmon from commit $CRIO_COMMIT"
req_env_var "
- GOPATH $GOPATH
- CRIO_COMMIT $CRIO_COMMIT
+ GOPATH $GOPATH
+ CRIO_COMMIT $CRIO_COMMIT
"
DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git"
rm -rf "$DEST"
@@ -234,8 +253,8 @@ install_criu(){
install_testing_dependencies() {
echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH"
req_env_var "
- GOPATH $GOPATH
- GOSRC $GOSRC
+ GOPATH $GOPATH
+ GOSRC $GOSRC
"
cd "$GOSRC"
ooe.sh go get -u github.com/onsi/ginkgo/ginkgo
@@ -263,7 +282,7 @@ install_varlink(){
_finalize(){
echo "Removing leftover giblets from cloud-init"
cd /
- sudo rm -rf /var/lib/cloud
+ sudo rm -rf /var/lib/cloud/instance?
sudo rm -rf /root/.ssh/*
sudo rm -rf /home/*
}
diff --git a/contrib/cirrus/optional_system_test.sh b/contrib/cirrus/optional_system_test.sh
new file mode 100755
index 000000000..705dda5ad
--- /dev/null
+++ b/contrib/cirrus/optional_system_test.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -e
+source $(dirname $0)/lib.sh
+
+MAGIC_RE='\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*'
+if ! echo "$CIRRUS_CHANGE_MESSAGE" | egrep -q "$MAGIC_RE"
+then
+ echo "Skipping system-testing because PR title or description"
+ echo "does not match regular expression: $MAGIC_RE"
+ exit 0
+fi
+
+req_env_var "
+GOSRC $GOSRC
+OS_RELEASE_ID $OS_RELEASE_ID
+OS_RELEASE_VER $OS_RELEASE_VER
+"
+
+show_env_vars
+
+set -x
+cd "$GOSRC"
+make localsystem
diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh
index 16b6e4e6b..f9fea04a7 100644
--- a/contrib/cirrus/packer/fedora_setup.sh
+++ b/contrib/cirrus/packer/fedora_setup.sh
@@ -21,8 +21,7 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
-# breaks networking on f28/29 in GCE
-# ooe.sh sudo dnf update -y
+ooe.sh sudo dnf update -y
ooe.sh sudo dnf install -y \
atomic-registries \
diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh
index ff20944dc..4cf1f335b 100644
--- a/contrib/cirrus/packer/ubuntu_setup.sh
+++ b/contrib/cirrus/packer/ubuntu_setup.sh
@@ -21,9 +21,10 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
-ooe.sh sudo apt-get -qq update
-ooe.sh sudo apt-get -qq update # sometimes it needs to get it twice :S
-ooe.sh sudo apt-get -qq upgrade
+# Try twice as workaround for minor networking problems
+echo "Updating system and installing package dependencies"
+ooe.sh sudo apt-get -qq update || sudo apt-get -qq update
+ooe.sh sudo apt-get -qq upgrade || sudo apt-get -qq upgrade
ooe.sh sudo apt-get -qq install --no-install-recommends \
apparmor \
autoconf \
diff --git a/contrib/python/podman/podman/libs/images.py b/contrib/python/podman/podman/libs/images.py
index 982546cd2..9453fb416 100644
--- a/contrib/python/podman/podman/libs/images.py
+++ b/contrib/python/podman/podman/libs/images.py
@@ -137,7 +137,7 @@ class Images():
results = podman.DeleteUnusedImages()
return results['images']
- def import_image(self, source, reference, message=None, changes=None):
+ def import_image(self, source, reference, message='', changes=None):
"""Read image tarball from source and save in image store."""
with self._client() as podman:
results = podman.ImportImage(source, reference, message, changes)
diff --git a/contrib/python/podman/test/test_pods_ctnrs.py b/contrib/python/podman/test/test_pods_ctnrs.py
index 14ce95c8a..009e30720 100644
--- a/contrib/python/podman/test/test_pods_ctnrs.py
+++ b/contrib/python/podman/test/test_pods_ctnrs.py
@@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase):
status = FoldedString(pod.containersinfo[0]['status'])
self.assertIn(status, ('stopped', 'exited', 'running'))
- killed = pod.kill()
+ # Pod kill is broken, so use stop for now
+ killed = pod.stop()
self.assertEqual(pod, killed)
def test_999_remove(self):
diff --git a/contrib/python/pypodman/pypodman/lib/__init__.py b/contrib/python/pypodman/pypodman/lib/__init__.py
index 5525ddaef..be1b5f467 100644
--- a/contrib/python/pypodman/pypodman/lib/__init__.py
+++ b/contrib/python/pypodman/pypodman/lib/__init__.py
@@ -4,14 +4,15 @@ import sys
import podman
from pypodman.lib.action_base import AbstractActionBase
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate,
- PathAction, PositiveIntAction,
- UnitAction)
+ ChangeAction, PathAction,
+ PositiveIntAction, UnitAction)
from pypodman.lib.podman_parser import PodmanArgumentParser
from pypodman.lib.report import Report, ReportColumn
# Silence pylint overlording...
assert BooleanAction
assert BooleanValidate
+assert ChangeAction
assert PathAction
assert PositiveIntAction
assert UnitAction
diff --git a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
index 0da6a2078..21665ad0b 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/commit_action.py
@@ -2,7 +2,7 @@
import sys
import podman
-from pypodman.lib import AbstractActionBase, BooleanAction
+from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
class Commit(AbstractActionBase):
@@ -12,7 +12,9 @@ class Commit(AbstractActionBase):
def subparser(cls, parent):
"""Add Commit command to parent parser."""
parser = parent.add_parser(
- 'commit', help='create image from container')
+ 'commit',
+ help='create image from container',
+ )
parser.add_argument(
'--author',
help='Set the author for the committed image',
@@ -20,11 +22,7 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--change',
'-c',
- choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
- 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
- action='append',
- type=str.upper,
- help='Apply the following possible changes to the created image',
+ action=ChangeAction,
)
parser.add_argument(
'--format',
@@ -69,27 +67,11 @@ class Commit(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='commit')
- def __init__(self, args):
- """Construct Commit class."""
- if not args.container:
- raise ValueError('You must supply one container id'
- ' or name to be used as source.')
- if not args.image:
- raise ValueError('You must supply one image id'
- ' or name to be created.')
- super().__init__(args)
-
- # used only on client
- del self.opts['image']
- del self.opts['container']
-
def commit(self):
"""Create image from container."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
- ident = ctnr.commit(**self.opts)
- print(ident)
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@@ -97,6 +79,9 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ else:
+ ident = ctnr.commit(self.opts['image'][0], **self.opts)
+ print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@@ -104,3 +89,4 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/actions/export_action.py b/contrib/python/pypodman/pypodman/lib/actions/export_action.py
index f62cd3535..7ef178c4c 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/export_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/export_action.py
@@ -12,13 +12,16 @@ class Export(AbstractActionBase):
def subparser(cls, parent):
"""Add Export command to parent parser."""
parser = parent.add_parser(
- 'export', help='export container to tarball')
+ 'export',
+ help='export container to tarball',
+ )
parser.add_argument(
'--output',
'-o',
metavar='PATH',
nargs=1,
- help='Write to a file',
+ required=True,
+ help='Write to this file on host',
)
parser.add_argument(
'container',
@@ -27,23 +30,11 @@ class Export(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='export')
- def __init__(self, args):
- """Construct Export class."""
- if not args.container:
- raise ValueError('You must supply one container id'
- ' or name to be used as source.')
-
- if not args.output:
- raise ValueError('You must supply one filename'
- ' to be created as tarball using --output.')
- super().__init__(args)
-
def export(self):
"""Create tarball from container filesystem."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
- ctnr.export(self._args.output[0])
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@@ -51,6 +42,8 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ else:
+ ctnr.export(self._args.output[0])
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@@ -58,3 +51,4 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/actions/history_action.py b/contrib/python/pypodman/pypodman/lib/actions/history_action.py
index 3e3f539fc..f9aaa54f6 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/history_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/history_action.py
@@ -60,7 +60,7 @@ class History(AbstractActionBase):
if self._args.human:
fields.update({
'size':
- humanize.naturalsize(details.size, binary=True),
+ humanize.naturalsize(details.size),
'created':
humanize.naturaldate(
podman.datetime_parse(details.created)),
diff --git a/contrib/python/pypodman/pypodman/lib/actions/images_action.py b/contrib/python/pypodman/pypodman/lib/actions/images_action.py
index d28e32db9..29bf90dd2 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/images_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/images_action.py
@@ -37,7 +37,7 @@ class Images(AbstractActionBase):
self.columns = OrderedDict({
'name':
- ReportColumn('name', 'REPOSITORY', 40),
+ ReportColumn('name', 'REPOSITORY', 0),
'tag':
ReportColumn('tag', 'TAG', 10),
'id':
@@ -65,18 +65,18 @@ class Images(AbstractActionBase):
'created':
humanize.naturaldate(podman.datetime_parse(image.created)),
'size':
- humanize.naturalsize(int(image.size), binary=True),
+ humanize.naturalsize(int(image.size)),
'repoDigests':
' '.join(image.repoDigests),
})
for r in image.repoTags:
- name, tag = r.split(':', 1)
+ name, tag = r.rsplit(':', 1)
fields.update({
'name': name,
'tag': tag,
})
- rows.append(fields)
+ rows.append(fields)
if not self._args.digests:
del self.columns['repoDigests']
diff --git a/contrib/python/pypodman/pypodman/lib/actions/import_action.py b/contrib/python/pypodman/pypodman/lib/actions/import_action.py
index 49b8a5a57..43448144a 100644
--- a/contrib/python/pypodman/pypodman/lib/actions/import_action.py
+++ b/contrib/python/pypodman/pypodman/lib/actions/import_action.py
@@ -2,7 +2,7 @@
import sys
import podman
-from pypodman.lib import AbstractActionBase
+from pypodman.lib import AbstractActionBase, ChangeAction
class Import(AbstractActionBase):
@@ -12,18 +12,19 @@ class Import(AbstractActionBase):
def subparser(cls, parent):
"""Add Import command to parent parser."""
parser = parent.add_parser(
- 'import', help='import tarball as image filesystem')
+ 'import',
+ help='import tarball as image filesystem',
+ )
parser.add_argument(
'--change',
'-c',
- action='append',
- choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL',
- 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
- type=str.upper,
- help='Apply the following possible instructions',
+ action=ChangeAction,
)
parser.add_argument(
- '--message', '-m', help='Set commit message for imported image.')
+ '--message',
+ '-m',
+ help='Set commit message for imported image.',
+ )
parser.add_argument(
'source',
metavar='PATH',
@@ -38,18 +39,25 @@ class Import(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='import_')
- def __init__(self, args):
- """Construct Import class."""
- super().__init__(args)
-
def import_(self):
"""Import tarball as image filesystem."""
+ # ImportImage() validates it's parameters therefore we need to create
+ # pristine dict() for keywords
+ options = {}
+ if 'message' in self.opts:
+ options['message'] = self.opts['message']
+ if 'change' in self.opts and self.opts['change']:
+ options['changes'] = self.opts['change']
+
+ reference = self.opts['reference'][0] if 'reference' in self.opts\
+ else None
+
try:
ident = self.client.images.import_image(
- self.opts.source,
- self.opts.reference,
- message=self.opts.message,
- changes=self.opts.change)
+ self.opts['source'][0],
+ reference,
+ **options,
+ )
print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
@@ -58,3 +66,4 @@ class Import(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
+ return 0
diff --git a/contrib/python/pypodman/pypodman/lib/parser_actions.py b/contrib/python/pypodman/pypodman/lib/parser_actions.py
index 2a5859e47..c10b85495 100644
--- a/contrib/python/pypodman/pypodman/lib/parser_actions.py
+++ b/contrib/python/pypodman/pypodman/lib/parser_actions.py
@@ -4,9 +4,10 @@ Supplimental argparse.Action converters and validaters.
The constructors are very verbose but remain for IDE support.
"""
import argparse
+import copy
import os
-# API defined by argparse.Action shut up pylint
+# API defined by argparse.Action therefore shut up pylint
# pragma pylint: disable=redefined-builtin
# pragma pylint: disable=too-few-public-methods
# pragma pylint: disable=too-many-arguments
@@ -63,6 +64,54 @@ class BooleanAction(argparse.Action):
setattr(namespace, self.dest, val)
+class ChangeAction(argparse.Action):
+ """Convert and validate change argument."""
+
+ def __init__(self,
+ option_strings,
+ dest,
+ nargs=None,
+ const=None,
+ default=[],
+ type=None,
+ choices=None,
+ required=False,
+ help=None,
+ metavar='OPT=VALUE'):
+ """Create ChangeAction object."""
+ help = (help or '') + ('Apply change(s) to the new image.'
+ ' May be given multiple times.')
+
+ super().__init__(
+ option_strings=option_strings,
+ dest=dest,
+ nargs=nargs,
+ const=const,
+ default=default,
+ type=type,
+ choices=choices,
+ required=required,
+ help=help,
+ metavar=metavar)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ """Convert and Validate input."""
+ print(self.dest)
+ items = getattr(namespace, self.dest, None) or []
+ items = copy.copy(items)
+
+ choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
+ 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
+
+ opt, val = values.split('=', 1)
+ if opt not in choices:
+ parser.error('{} is not a supported "--change" option,'
+ ' valid options are: {}'.format(
+ opt, ', '.join(choices)))
+ items.append(values)
+ setattr(namespace, self.dest, items)
+
+
class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix."""
diff --git a/contrib/python/pypodman/pypodman/lib/report.py b/contrib/python/pypodman/pypodman/lib/report.py
index 1db4268da..b689390fd 100644
--- a/contrib/python/pypodman/pypodman/lib/report.py
+++ b/contrib/python/pypodman/pypodman/lib/report.py
@@ -1,8 +1,23 @@
"""Report Manager."""
+import string
import sys
from collections import namedtuple
+class ReportFormatter(string.Formatter):
+ """Custom formatter to default missing keys to '<none>'."""
+
+ def get_value(self, key, args, kwargs):
+ """Map missing key to value '<none>'."""
+ try:
+ if isinstance(key, int):
+ return args[key]
+ else:
+ return kwargs[key]
+ except KeyError:
+ return '<none>'
+
+
class ReportColumn(namedtuple('ReportColumn', 'key display width default')):
"""Hold attributes of output column."""
@@ -26,18 +41,24 @@ class Report():
"""
self._columns = columns
self._file = file
+ self._format_string = None
+ self._formatter = ReportFormatter()
self._heading = heading
self.epilog = epilog
- self._format = None
def row(self, **fields):
"""Print row for report."""
if self._heading:
hdrs = {k: v.display for (k, v) in self._columns.items()}
- print(self._format.format(**hdrs), flush=True, file=self._file)
+ print(
+ self._formatter.format(self._format_string, **hdrs),
+ flush=True,
+ file=self._file,
+ )
self._heading = False
+
fields = {k: str(v) for k, v in fields.items()}
- print(self._format.format(**fields))
+ print(self._formatter.format(self._format_string, **fields))
def __enter__(self):
"""Return `self` upon entering the runtime context."""
@@ -63,4 +84,4 @@ class Report():
display_len = info.width
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
- self._format = ' '.join(fmt)
+ self._format_string = ' '.join(fmt)
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index c2d8fc59d..f6ebfa148 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -39,7 +39,7 @@
%global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7})
Name: podman
-Version: 0.10.2
+Version: 0.11.2
Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
Summary: Manage Pods, Containers and Container Images
License: ASL 2.0
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index 0cbce15c0..f887d68cd 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -171,7 +171,7 @@ value can be entered. The password is entered without echo.
**--disable-content-trust**
This is a Docker specific option to disable image verification to a Docker
-registry and is not supported by Buildah. This flag is a NOOP and provided
+registry and is not supported by Podman. This flag is a NOOP and provided
soley for scripting compatibility.
**--file, -f** *Dockerfile*
diff --git a/docs/podman-container.1.md b/docs/podman-container.1.md
index eac3343d5..67d42bfef 100644
--- a/docs/podman-container.1.md
+++ b/docs/podman-container.1.md
@@ -38,7 +38,6 @@ The container command allows you to manage containers
| stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
| umount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
-| unmount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 5a4d7fb5a..68c00685b 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -66,7 +66,7 @@ Write the container ID to the file
**--conmon-pidfile**=""
-Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers.
+Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers.
**--cpu-count**=*0*
@@ -321,13 +321,13 @@ Not implemented
**--log-driver**="*json-file*"
-Logging driver for the container. Default is defined by daemon `--log-driver` flag.
-**Warning**: the `podman logs` command works only for the `json-file` and
-`journald` logging drivers.
+Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility.
**--log-opt**=[]
-Logging driver specific options.
+Logging driver specific options. Used to set the path to the container log file. For example:
+
+`--log-opt path=/var/log/container/mycontainer.json`
**--mac-address**=""
@@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”)
Name (“jonah”)
podman generates a UUID for each container, and if a name is not assigned
-to the container with **--name** then the daemon will also generate a random
+to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
diff --git a/docs/podman-kill.1.md b/docs/podman-kill.1.md
index 14066d151..85f68a73d 100644
--- a/docs/podman-kill.1.md
+++ b/docs/podman-kill.1.md
@@ -4,7 +4,7 @@
podman\-kill - Kills one or more containers with a signal
## SYNOPSIS
-**podman kill** [*options*] *container* ...
+**podman kill** [*options*] [*container* ...]
## DESCRIPTION
The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal.
diff --git a/docs/podman-pause.1.md b/docs/podman-pause.1.md
index b4930de8d..f19fa5d6a 100644
--- a/docs/podman-pause.1.md
+++ b/docs/podman-pause.1.md
@@ -4,16 +4,33 @@
podman\-pause - Pause one or more containers
## SYNOPSIS
-**podman pause** [*options*] *container* ...
+**podman pause** [*options*] [*container*...]
## DESCRIPTION
Pauses all the processes in one or more containers. You may use container IDs or names as input.
+## OPTIONS
+
+**--all, -a**
+
+Pause all running containers.
+
## EXAMPLE
+Pause a container named 'mywebserver'
+```
podman pause mywebserver
+```
+Pause a container by partial container ID.
+```
podman pause 860a4b23
+```
+
+Pause all **running** containers.
+```
+podman stop -a
+```
## SEE ALSO
podman(1), podman-unpause(1)
diff --git a/docs/podman-restart.1.md b/docs/podman-restart.1.md
index caacaf31d..875afa385 100644
--- a/docs/podman-restart.1.md
+++ b/docs/podman-restart.1.md
@@ -12,33 +12,51 @@ Containers will be stopped if they are running and then restarted. Stopped
containers will not be stopped and will only be started.
## OPTIONS
-**--timeout**
-
-Timeout to wait before forcibly stopping the container
+**--all, -a**
+Restart all containers regardless of their current state.
**--latest, -l**
-
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+**--running**
+Restart all containers that are already in the *running* state.
+
+**--timeout**
+Timeout to wait before forcibly stopping the container.
+
+
## EXAMPLES ##
+Restart the latest container
```
$ podman restart -l
ec588fc80b05e19d3006bf2e8aa325f0a2e2ff1f609b7afb39176ca8e3e13467
```
+Restart a specific container by partial container ID
```
$ podman restart ff6cf1
ff6cf1e5e77e6dba1efc7f3fcdb20e8b89ad8947bc0518be1fcb2c78681f226f
```
+Restart two containers by name with a timeout of 4 seconds
```
$ podman restart --timeout 4 test1 test2
c3bb026838c30e5097f079fa365c9a4769d52e1017588278fa00d5c68ebc1502
17e13a63081a995136f907024bcfe50ff532917988a152da229db9d894c5a9ec
```
+Restart all running containers
+```
+$ podman restart --running
+```
+
+Restart all containers
+```
+$ podman restart --all
+```
+
## SEE ALSO
podman(1), podman-run(1), podman-start(1), podman-create(1)
diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md
index 7474a0d1f..56664a8c1 100644
--- a/docs/podman-rm.1.md
+++ b/docs/podman-rm.1.md
@@ -13,7 +13,7 @@ podman\-rm - Remove one or more containers
**--force, f**
-Force the removal of a running container
+Force the removal of a running and paused containers
**--all, a**
@@ -29,16 +29,29 @@ to run containers such as CRI-O, the last started container could be from either
Remove the volumes associated with the container. (Not yet implemented)
## EXAMPLE
-
+Remove a container by its name *mywebserver*
+```
podman rm mywebserver
-
+```
+Remove several containers by name and container id.
+```
podman rm mywebserver myflaskserver 860a4b23
+```
+Forcibly remove a container by container ID.
+```
podman rm -f 860a4b23
+```
+Remove all containers regardless of its run state.
+```
podman rm -f -a
+```
+Forcibly remove the latest container created.
+```
podman rm -f --latest
+```
## SEE ALSO
podman(1), podman-rmi(1)
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index b708e3407..912026a55 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -78,7 +78,7 @@ Write the container ID to the file
**--conmon-pidfile**=""
-Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers.
+Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers.
**--cpu-period**=*0*
@@ -333,16 +333,13 @@ Not implemented
**--log-driver**="*json-file*"
-Logging driver for the container. Default is defined by daemon `--log-driver` flag.
-
-**Warning**: the `podman logs` command works only for the `json-file` and
-`journald` logging drivers.
+Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility.
**--log-opt**=[]
-Logging driver specific options.
+Logging driver specific options. Used to set the path to the container log file. For example:
-`path=/var/log/container/mycontainer.json`: Set the path to the container log file.
+`--log-opt path=/var/log/container/mycontainer.json`
**--mac-address**=""
@@ -399,7 +396,7 @@ The operator can identify a container in three ways:
- Name (“jonah”)
podman generates a UUID for each container, and if a name is not assigned
-to the container with **--name** then the daemon will also generate a random
+to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
diff --git a/docs/podman-unpause.1.md b/docs/podman-unpause.1.md
index 9404e7648..acfab0930 100644
--- a/docs/podman-unpause.1.md
+++ b/docs/podman-unpause.1.md
@@ -4,16 +4,34 @@
podman\-unpause - Unpause one or more containers
## SYNOPSIS
-**podman unpause** [*options*] *container* ...
+**podman unpause** [*options*] [*container*...]
## DESCRIPTION
Unpauses the processes in one or more containers. You may use container IDs or names as input.
+## OPTIONS
+
+**--all, -a**
+
+Unpause all paused containers.
+
## EXAMPLE
+Unpause a container called 'mywebserver'
+```
podman unpause mywebserver
+```
+Unpause a container by a partial container ID.
+
+```
podman unpause 860a4b23
+```
+
+Unpause all **paused** containers.
+```
+podman unpause -a
+```
## SEE ALSO
podman(1), podman-pause(1)
diff --git a/docs/podman.1.md b/docs/podman.1.md
index 085af97ff..b7433d850 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -56,7 +56,7 @@ Path to the OCI compatible binary used to run containers
**--storage-driver, -s**=**value**
-Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for other users. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all.
+Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for non-root users when *fuse-overlayfs* is not available. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all.
Overriding this option will cause the *storage-opt* settings in /etc/containers/storage.conf to be ignored. The user must
specify additional options via the `--storage-opt` flag.
@@ -192,7 +192,7 @@ the exit codes follow the `chroot` standard, see below:
When Podman runs in rootless mode, the file `$HOME/.config/containers/storage.conf` is also loaded.
## Rootless mode
-Podman can also be used as non-root user. When podman runs in rootless mode, an user namespace is automatically created.
+Podman can also be used as non-root user. When podman runs in rootless mode, a user namespace is automatically created for the user, defined in /etc/subuid and /etc/subgid.
Containers created by a non-root user are not visible to other users and are not seen or managed by podman running as root.
@@ -209,13 +209,14 @@ Or just add the content manually.
$ echo USERNAME:10000:65536 >> /etc/subuid
$ echo USERNAME:10000:65536 >> /etc/subgid
+See the `subuid(5)` and `subgid(5)` man pages for more information.
+
Images are pulled under `XDG_DATA_HOME` when specified, otherwise in the home directory of the user under `.local/share/containers/storage`.
-Currently it is not possible to create a network device, so rootless containers need to run in the host network namespace. If a rootless container creates a network namespace,
-then only the loopback device will be available.
+Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host.
## SEE ALSO
-`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)`
+`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)`
## HISTORY
Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md
index 152d65a59..5a8f997b8 100644
--- a/docs/tutorials/podman_tutorial.md
+++ b/docs/tutorials/podman_tutorial.md
@@ -5,10 +5,13 @@ Podman is a utility provided as part of the libpod library. It can be used to c
containers. The following tutorial will teach you how to set up Podman and perform some basic
commands with Podman.
+**NOTE**: the code samples are intended to be run as a non-root user, and use `sudo` where
+root escalation is required.
+
## Install Podman on Fedora from RPM Repositories
Fedora 27 and later provide Podman via the package manager.
```console
-$ sudo dnf install -y podman
+sudo dnf install -y podman
```
## Install Podman on Fedora from Source
@@ -18,10 +21,10 @@ acquire the source, and build it.
### Installing build and runtime dependencies
```console
-$ sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \
- gpgme-devel glib2-devel device-mapper-devel libseccomp-devel \
- atomic-registries iptables skopeo-containers containernetworking-cni \
- conmon
+sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \
+ gpgme-devel glib2-devel device-mapper-devel libseccomp-devel \
+ atomic-registries iptables skopeo-containers containernetworking-cni \
+ conmon
```
### Building and installing podman
@@ -29,12 +32,12 @@ First, configure a `GOPATH` (if you are using go1.8 or later, this defaults to `
and make libpod.
```console
-$ export GOPATH=~/go
-$ mkdir -p $GOPATH
-$ git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod
-$ cd $GOPATH/src/github.com/containers/libpod
-$ make
-$ sudo make install PREFIX=/usr
+export GOPATH=~/go
+mkdir -p $GOPATH
+git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod
+cd $GOPATH/src/github.com/containers/libpod
+make
+sudo make install PREFIX=/usr
```
You now have a working podman environment. Jump to [Familiarizing yourself with Podman](#familiarizing-yourself-with-podman)
@@ -50,8 +53,8 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use
#### Installing base packages
```console
-$ sudo apt-get update
-$ sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \
+sudo apt-get update
+sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \
go-md2man libprotobuf-dev libprotobuf-c0-dev libseccomp-dev python3-setuptools
```
#### Building and installing conmon
@@ -59,42 +62,42 @@ First, configure a `GOPATH` (if you are using go1.8 or later, this defaults to `
and make libpod.
```console
-$ export GOPATH=~/go
-$ mkdir -p $GOPATH
-$ git clone https://github.com/kubernetes-sigs/cri-o $GOPATH/src/github.com/kubernetes-sigs/cri-o
-$ cd $GOPATH/src/github.com/kubernetes-sigs/cri-o
-$ mkdir bin
-$ make bin/conmon
-$ sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon
+export GOPATH=~/go
+mkdir -p $GOPATH
+git clone https://github.com/kubernetes-sigs/cri-o $GOPATH/src/github.com/kubernetes-sigs/cri-o
+cd $GOPATH/src/github.com/kubernetes-sigs/cri-o
+mkdir bin
+make bin/conmon
+sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon
```
#### Adding required configuration files
```console
-$ sudo mkdir -p /etc/containers
-$ sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf
-$ sudo curl https://raw.githubusercontent.com/containers/skopeo/master/default-policy.json -o /etc/containers/policy.json
+sudo mkdir -p /etc/containers
+sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf
+sudo curl https://raw.githubusercontent.com/containers/skopeo/master/default-policy.json -o /etc/containers/policy.json
```
#### Installing CNI plugins
```console
-$ git clone https://github.com/containernetworking/plugins.git $GOPATH/src/github.com/containernetworking/plugins
-$ cd $GOPATH/src/github.com/containernetworking/plugins
-$ ./build.sh
-$ sudo mkdir -p /usr/libexec/cni
-$ sudo cp bin/* /usr/libexec/cni
+git clone https://github.com/containernetworking/plugins.git $GOPATH/src/github.com/containernetworking/plugins
+cd $GOPATH/src/github.com/containernetworking/plugins
+./build_linux.sh
+sudo mkdir -p /usr/libexec/cni
+sudo cp bin/* /usr/libexec/cni
```
#### Installing runc
```console
-$ git clone https://github.com/opencontainers/runc.git $GOPATH/src/github.com/opencontainers/runc
-$ cd $GOPATH/src/github.com/opencontainers/runc
-$ make BUILDTAGS="seccomp"
-$ sudo cp runc /usr/bin/runc
+git clone https://github.com/opencontainers/runc.git $GOPATH/src/github.com/opencontainers/runc
+cd $GOPATH/src/github.com/opencontainers/runc
+make BUILDTAGS="seccomp"
+sudo cp runc /usr/bin/runc
```
### Building and installing Podman
```console
-$ git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod
-$ cd $GOPATH/src/github.com/containers/libpod
-$ make
-$ sudo make install PREFIX=/usr
+git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod
+cd $GOPATH/src/github.com/containers/libpod
+make
+sudo make install PREFIX=/usr
```
## Familiarizing yourself with Podman
@@ -103,10 +106,10 @@ $ sudo make install PREFIX=/usr
This sample container will run a very basic httpd server that serves only its index
page.
```console
-$ sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \
- -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \
- -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \
- registry.fedoraproject.org/f27/httpd /usr/bin/run-httpd
+sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \
+ -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \
+ -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \
+ registry.fedoraproject.org/f27/httpd /usr/bin/run-httpd
```
Because the container is being run in detached mode, represented by the *-d* in the podman run command, podman
will print the container ID after it has run.
@@ -114,7 +117,7 @@ will print the container ID after it has run.
### Listing running containers
The Podman *ps* command is used to list creating and running containers.
```console
-$ sudo podman ps
+sudo podman ps
```
Note: If you add *-a* to the *ps* command, Podman will show all containers.
@@ -134,7 +137,7 @@ Now that we have the IP address of the container, we can test the network commun
operating system and the container using curl. The following command should display the index page of our
containerized httpd server.
```console
-# curl http://<IP_address>:8080
+curl http://<IP_address>:8080
```
### Viewing the container's logs
@@ -163,7 +166,7 @@ With this a container can later be restored and continue running at exactly the
checkpoint. This capability requires CRIU 3.11 or later installed on the system.
To checkpoint the container use:
```console
-$ sudo podman container checkpoint <container_id>
+sudo podman container checkpoint <container_id>
```
### Restoring the container
@@ -171,29 +174,29 @@ Restoring a container is only possible for a previously checkpointed container.
continue to run at exactly the same point in time it was checkpointed.
To restore the container use:
```console
-$ sudo podman container restore <container_id>
+sudo podman container restore <container_id>
```
After being restored, the container will answer requests again as it did before checkpointing.
```console
-# curl http://<IP_address>:8080
+curl http://<IP_address>:8080
```
### Stopping the container
To stop the httpd container:
```console
-$ sudo podman stop --latest
+sudo podman stop --latest
```
You can also check the status of one or more containers using the *ps* subcommand. In this case, we should
use the *-a* argument to list all containers.
```console
-$ sudo podman ps -a
+sudo podman ps -a
```
### Removing the container
To remove the httpd container:
```console
-$ sudo podman rm --latest
+sudo podman rm --latest
```
You can verify the deletion of the container by running *podman ps -a*.
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 30c67eb2a..d99aec5b4 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -46,9 +46,6 @@ func (c *Container) Init(ctx context.Context) (err error) {
return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString)
}
- if err := c.prepare(); err != nil {
- return err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -57,6 +54,10 @@ func (c *Container) Init(ctx context.Context) (err error) {
}
}()
+ if err := c.prepare(); err != nil {
+ return err
+ }
+
if c.state.State == ContainerStateStopped {
// Reinitialize the container
return c.reinit(ctx)
@@ -99,9 +100,6 @@ func (c *Container) Start(ctx context.Context) (err error) {
return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString)
}
- if err := c.prepare(); err != nil {
- return err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -110,6 +108,10 @@ func (c *Container) Start(ctx context.Context) (err error) {
}
}()
+ if err := c.prepare(); err != nil {
+ return err
+ }
+
if c.state.State == ContainerStateStopped {
// Reinitialize the container if we need to
if err := c.reinit(ctx); err != nil {
@@ -164,9 +166,6 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams,
return nil, errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString)
}
- if err := c.prepare(); err != nil {
- return nil, err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -175,6 +174,10 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams,
}
}()
+ if err := c.prepare(); err != nil {
+ return nil, err
+ }
+
if c.state.State == ContainerStateStopped {
// Reinitialize the container if we need to
if err := c.reinit(ctx); err != nil {
@@ -685,7 +688,7 @@ func (c *Container) Sync() error {
(c.state.State != ContainerStateConfigured) {
oldState := c.state.State
// TODO: optionally replace this with a stat for the exit file
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, true); err != nil {
return err
}
// Only save back to DB if state changed
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index d928c4aed..d2f48d661 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -5,7 +5,6 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/opencontainers/runc/libcontainer/user"
"io"
"io/ioutil"
"os"
@@ -13,8 +12,10 @@ import (
"strconv"
"strings"
"syscall"
+ "time"
"github.com/containers/buildah/imagebuildah"
+ "github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
"github.com/containers/libpod/pkg/lookup"
@@ -25,12 +26,14 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive"
"github.com/containers/storage/pkg/mount"
+ "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/text/language"
+ kwait "k8s.io/apimachinery/pkg/util/wait"
)
const (
@@ -147,6 +150,77 @@ func (c *Container) execPidPath(sessionID string) string {
return filepath.Join(c.state.RunDir, "exec_pid_"+sessionID)
}
+// exitFilePath gets the path to the container's exit file
+func (c *Container) exitFilePath() string {
+ return filepath.Join(c.runtime.ociRuntime.exitsDir, c.ID())
+}
+
+// Wait for the container's exit file to appear.
+// When it does, update our state based on it.
+func (c *Container) waitForExitFileAndSync() error {
+ exitFile := c.exitFilePath()
+
+ err := kwait.ExponentialBackoff(
+ kwait.Backoff{
+ Duration: 500 * time.Millisecond,
+ Factor: 1.2,
+ Steps: 6,
+ },
+ func() (bool, error) {
+ _, err := os.Stat(exitFile)
+ if err != nil {
+ // wait longer
+ return false, nil
+ }
+ return true, nil
+ })
+ if err != nil {
+ // Exit file did not appear
+ // Reset our state
+ c.state.ExitCode = -1
+ c.state.FinishedTime = time.Now()
+ c.state.State = ContainerStateStopped
+
+ if err2 := c.save(); err2 != nil {
+ logrus.Errorf("Error saving container %s state: %v", c.ID(), err2)
+ }
+
+ return err
+ }
+
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil {
+ return err
+ }
+
+ return c.save()
+}
+
+// Handle the container exit file.
+// The exit file is used to supply container exit time and exit code.
+// This assumes the exit file already exists.
+func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error {
+ c.state.FinishedTime = ctime.Created(fi)
+ statusCodeStr, err := ioutil.ReadFile(exitFile)
+ if err != nil {
+ return errors.Wrapf(err, "failed to read exit file for container %s", c.ID())
+ }
+ statusCode, err := strconv.Atoi(string(statusCodeStr))
+ if err != nil {
+ return errors.Wrapf(err, "error converting exit status code (%q) for container %s to int",
+ c.ID(), statusCodeStr)
+ }
+ c.state.ExitCode = int32(statusCode)
+
+ oomFilePath := filepath.Join(c.bundlePath(), "oom")
+ if _, err = os.Stat(oomFilePath); err == nil {
+ c.state.OOMKilled = true
+ }
+
+ c.state.Exited = true
+
+ return nil
+}
+
// Sync this container with on-disk state and runtime status
// Should only be called with container lock held
// This function should suffice to ensure a container's state is accurate and
@@ -162,7 +236,7 @@ func (c *Container) syncContainer() error {
(c.state.State != ContainerStateExited) {
oldState := c.state.State
// TODO: optionally replace this with a stat for the exit file
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
+ if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil {
return err
}
// Only save back to DB if state changed
@@ -623,9 +697,6 @@ func (c *Container) initAndStart(ctx context.Context) (err error) {
return errors.Wrapf(ErrCtrStateInvalid, "cannot start paused container %s", c.ID())
}
- if err := c.prepare(); err != nil {
- return err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -634,6 +705,10 @@ func (c *Container) initAndStart(ctx context.Context) (err error) {
}
}()
+ if err := c.prepare(); err != nil {
+ return err
+ }
+
// If we are ContainerStateStopped we need to remove from runtime
// And reset to ContainerStateConfigured
if c.state.State == ContainerStateStopped {
@@ -673,13 +748,8 @@ func (c *Container) stop(timeout uint) error {
return err
}
- // Sync the container's state to pick up return code
- if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
- return err
- }
-
- // Container should clean itself up
- return nil
+ // Wait until we have an exit file, and sync once we do
+ return c.waitForExitFileAndSync()
}
// Internal, non-locking function to pause a container
@@ -719,9 +789,6 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
return err
}
}
- if err := c.prepare(); err != nil {
- return err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -729,6 +796,9 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
}
}
}()
+ if err := c.prepare(); err != nil {
+ return err
+ }
if c.state.State == ContainerStateStopped {
// Reinitialize the container if we need to
@@ -1069,7 +1139,7 @@ func (c *Container) generatePasswd() (string, error) {
}
originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd")
orig, err := ioutil.ReadFile(originPasswdFile)
- if err != nil {
+ if err != nil && !os.IsNotExist(err) {
return "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile)
}
@@ -1157,6 +1227,10 @@ func (c *Container) generateHosts() (string, error) {
hosts += fmt.Sprintf("%s %s\n", fields[1], fields[0])
}
}
+ if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 {
+ ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0]
+ hosts += fmt.Sprintf("%s\t%s\n", ipAddress, c.Hostname())
+ }
return c.writeStringToRundir("hosts", hosts)
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 7bf2c71ca..66c7e8a04 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -57,7 +57,7 @@ func (c *Container) prepare() (err error) {
networkStatus []*cnitypes.Result
createNetNSErr, mountStorageErr error
mountPoint string
- saveNetworkStatus bool
+ tmpStateLock sync.Mutex
)
wg.Add(2)
@@ -66,17 +66,55 @@ func (c *Container) prepare() (err error) {
defer wg.Done()
// Set up network namespace if not already set up
if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS {
- saveNetworkStatus = true
netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
+
+ tmpStateLock.Lock()
+ defer tmpStateLock.Unlock()
+
+ // Assign NetNS attributes to container
+ if createNetNSErr == nil {
+ c.state.NetNS = netNS
+ c.state.NetworkStatus = networkStatus
+ }
}
}()
// Mount storage if not mounted
go func() {
defer wg.Done()
mountPoint, mountStorageErr = c.mountStorage()
+
+ if mountStorageErr != nil {
+ return
+ }
+
+ tmpStateLock.Lock()
+ defer tmpStateLock.Unlock()
+
+ // Finish up mountStorage
+ c.state.Mounted = true
+ c.state.Mountpoint = mountPoint
+ if c.state.UserNSRoot == "" {
+ c.state.RealMountpoint = c.state.Mountpoint
+ } else {
+ c.state.RealMountpoint = filepath.Join(c.state.UserNSRoot, "mountpoint")
+ }
+
+ logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint)
+ }()
+
+ defer func() {
+ if err != nil {
+ if err2 := c.cleanupNetwork(); err2 != nil {
+ logrus.Errorf("Error cleaning up container %s network: %v", c.ID(), err2)
+ }
+ if err2 := c.cleanupStorage(); err2 != nil {
+ logrus.Errorf("Error cleaning up container %s storage: %v", c.ID(), err2)
+ }
+ }
}()
wg.Wait()
+
if createNetNSErr != nil {
if mountStorageErr != nil {
logrus.Error(createNetNSErr)
@@ -88,22 +126,6 @@ func (c *Container) prepare() (err error) {
return mountStorageErr
}
- // Assign NetNS attributes to container
- if saveNetworkStatus {
- c.state.NetNS = netNS
- c.state.NetworkStatus = networkStatus
- }
-
- // Finish up mountStorage
- c.state.Mounted = true
- c.state.Mountpoint = mountPoint
- if c.state.UserNSRoot == "" {
- c.state.RealMountpoint = c.state.Mountpoint
- } else {
- c.state.RealMountpoint = filepath.Join(c.state.UserNSRoot, "mountpoint")
- }
-
- logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint)
// Save the container
return c.save()
}
@@ -360,19 +382,31 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro
g.AddMount(tmpfsMnt)
}
- cgroupPath, err := c.CGroupPath()
- if err != nil {
- return err
- }
- sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath)
+ // rootless containers have no write access to /sys/fs/cgroup, so don't
+ // add any mount into the container.
+ if !rootless.IsRootless() {
+ cgroupPath, err := c.CGroupPath()
+ if err != nil {
+ return err
+ }
+ sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath)
- systemdMnt := spec.Mount{
- Destination: "/sys/fs/cgroup/systemd",
- Type: "bind",
- Source: sourcePath,
- Options: []string{"bind", "private"},
+ systemdMnt := spec.Mount{
+ Destination: "/sys/fs/cgroup/systemd",
+ Type: "bind",
+ Source: sourcePath,
+ Options: []string{"bind", "private"},
+ }
+ g.AddMount(systemdMnt)
+ } else {
+ systemdMnt := spec.Mount{
+ Destination: "/sys/fs/cgroup/systemd",
+ Type: "bind",
+ Source: "/sys/fs/cgroup/systemd",
+ Options: []string{"bind", "nodev", "noexec", "nosuid"},
+ }
+ g.AddMount(systemdMnt)
}
- g.AddMount(systemdMnt)
return nil
}
@@ -484,9 +518,6 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) {
}
}
- if err := c.prepare(); err != nil {
- return err
- }
defer func() {
if err != nil {
if err2 := c.cleanup(ctx); err2 != nil {
@@ -495,6 +526,10 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) {
}
}()
+ if err := c.prepare(); err != nil {
+ return err
+ }
+
// TODO: use existing way to request static IPs, once it is merged in ocicni
// https://github.com/cri-o/ocicni/pull/23/
diff --git a/libpod/info.go b/libpod/info.go
index 4cbf3f734..5d8d160c8 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -12,6 +12,7 @@ import (
"strings"
"time"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/system"
"github.com/pkg/errors"
@@ -30,6 +31,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
info["os"] = runtime.GOOS
info["arch"] = runtime.GOARCH
info["cpus"] = runtime.NumCPU()
+ info["rootless"] = rootless.IsRootless()
mi, err := system.ReadMemInfo()
if err != nil {
return nil, errors.Wrapf(err, "error reading memory info")
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 863a764e2..43d0a61a4 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -64,20 +64,20 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
}
}()
- networkStatus := make([]*cnitypes.Result, 1)
+ networkStatus := make([]*cnitypes.Result, 0)
for idx, r := range results {
logrus.Debugf("[%d] CNI result: %v", idx, r.String())
resultCurrent, err := cnitypes.GetResult(r)
if err != nil {
return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.String(), err)
}
- networkStatus = append(ctr.state.NetworkStatus, resultCurrent)
+ networkStatus = append(networkStatus, resultCurrent)
}
// Add firewall rules to ensure the container has network access.
// Will not be necessary once CNI firewall plugin merges upstream.
// https://github.com/containernetworking/plugins/pull/75
- for _, netStatus := range ctr.state.NetworkStatus {
+ for _, netStatus := range networkStatus {
firewallConf := &firewall.FirewallNetConf{
PrevResult: netStatus,
}
@@ -90,13 +90,16 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
}
// Create and configure a new network namespace for a container
-func (r *Runtime) createNetNS(ctr *Container) (ns.NetNS, []*cnitypes.Result, error) {
+func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) {
ctrNS, err := netns.NewNS()
if err != nil {
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
}
defer func() {
if err != nil {
+ if err2 := netns.UnmountNS(ctrNS); err2 != nil {
+ logrus.Errorf("Error unmounting partially created network namespace for container %s: %v", ctr.ID(), err2)
+ }
if err2 := ctrNS.Close(); err2 != nil {
logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err2)
}
diff --git a/libpod/oci.go b/libpod/oci.go
index ca8f967c4..233bacfbb 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -11,12 +11,10 @@ import (
"os/exec"
"path/filepath"
"runtime"
- "strconv"
"strings"
"syscall"
"time"
- "github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/coreos/go-systemd/activation"
@@ -443,6 +441,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
}
return errors.Wrapf(ErrInternal, "container create failed")
}
+ ctr.state.PID = ss.si.Pid
case <-time.After(ContainerCreateTimeout):
return errors.Wrapf(ErrInternal, "container creation timeout")
}
@@ -451,17 +450,47 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
// updateContainerStatus retrieves the current status of the container from the
// runtime. It updates the container's state but does not save it.
-func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
- state := new(spec.State)
+// If useRunc is false, we will not directly hit runc to see the container's
+// status, but will instead only check for the existence of the conmon exit file
+// and update state to stopped if it exists.
+func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRunc bool) error {
+ exitFile := ctr.exitFilePath()
runtimeDir, err := util.GetRootlessRuntimeDir()
if err != nil {
return err
}
+ // If not using runc, we don't need to do most of this.
+ if !useRunc {
+ // If the container's not running, nothing to do.
+ if ctr.state.State != ContainerStateRunning {
+ return nil
+ }
+
+ // Check for the exit file conmon makes
+ info, err := os.Stat(exitFile)
+ if err != nil {
+ if os.IsNotExist(err) {
+ // Container is still running, no error
+ return nil
+ }
+
+ return errors.Wrapf(err, "error running stat on container %s exit file", ctr.ID())
+ }
+
+ // Alright, it exists. Transition to Stopped state.
+ ctr.state.State = ContainerStateStopped
+
+ // Read the exit file to get our stopped time and exit code.
+ return ctr.handleExitFile(exitFile, info)
+ }
+
// Store old state so we know if we were already stopped
oldState := ctr.state.State
+ state := new(spec.State)
+
cmd := exec.Command(r.path, "state", ctr.ID())
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
outPipe, err := cmd.StdoutPipe()
@@ -480,6 +509,8 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
}
if strings.Contains(string(out), "does not exist") {
ctr.removeConmonFiles()
+ ctr.state.ExitCode = -1
+ ctr.state.FinishedTime = time.Now()
ctr.state.State = ContainerStateExited
return nil
}
@@ -514,7 +545,6 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
// Only grab exit status if we were not already stopped
// If we were, it should already be in the database
if ctr.state.State == ContainerStateStopped && oldState != ContainerStateStopped {
- exitFile := filepath.Join(r.exitsDir, ctr.ID())
var fi os.FileInfo
err = kwait.ExponentialBackoff(
kwait.Backoff{
@@ -538,24 +568,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
return nil
}
- ctr.state.FinishedTime = ctime.Created(fi)
- statusCodeStr, err := ioutil.ReadFile(exitFile)
- if err != nil {
- return errors.Wrapf(err, "failed to read exit file for container %s", ctr.ID())
- }
- statusCode, err := strconv.Atoi(string(statusCodeStr))
- if err != nil {
- return errors.Wrapf(err, "error converting exit status code for container %s to int",
- ctr.ID())
- }
- ctr.state.ExitCode = int32(statusCode)
-
- oomFilePath := filepath.Join(ctr.bundlePath(), "oom")
- if _, err = os.Stat(oomFilePath); err == nil {
- ctr.state.OOMKilled = true
- }
-
- ctr.state.Exited = true
+ return ctr.handleExitFile(exitFile, fi)
}
return nil
@@ -601,6 +614,8 @@ func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error {
// Does not set finished time for container, assumes you will run updateStatus
// after to pull the exit code
func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error {
+ logrus.Debugf("Stopping container %s (PID %d)", ctr.ID(), ctr.state.PID)
+
// Ping the container to see if it's alive
// If it's not, it's already stopped, return
err := unix.Kill(ctr.state.PID, 0)
diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go
index 0447670b3..e6b7cbe4f 100644
--- a/libpod/oci_linux.go
+++ b/libpod/oci_linux.go
@@ -74,7 +74,8 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor
defer wg.Done()
runtime.LockOSThread()
- fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid()))
+ var fd *os.File
+ fd, err = os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid()))
if err != nil {
return
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index b63726f29..09d0ec042 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -246,7 +246,19 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool)
}
if c.state.State == ContainerStatePaused {
- return errors.Wrapf(ErrCtrStateInvalid, "container %s is paused, cannot remove until unpaused", c.ID())
+ if !force {
+ return errors.Wrapf(ErrCtrStateInvalid, "container %s is paused, cannot remove until unpaused", c.ID())
+ }
+ if err := c.runtime.ociRuntime.killContainer(c, 9); err != nil {
+ return err
+ }
+ if err := c.unpause(); err != nil {
+ return err
+ }
+ // Need to update container state to make sure we know it's stopped
+ if err := c.waitForExitFileAndSync(); err != nil {
+ return err
+ }
}
// Check that the container's in a good state to be removed
@@ -256,7 +268,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool)
}
// Need to update container state to make sure we know it's stopped
- if err := c.syncContainer(); err != nil {
+ if err := c.waitForExitFileAndSync(); err != nil {
return err
}
} else if !(c.state.State == ContainerStateConfigured ||
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index eb3d471dd..3d6fad52f 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -265,15 +265,26 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
}
case CgroupfsCgroupsManager:
// Delete the cgroupfs cgroup
+ // Make sure the conmon cgroup is deleted first
+ // Since the pod is almost gone, don't bother failing
+ // hard - instead, just log errors.
v1CGroups := GetV1CGroups(getExcludedCGroups())
+ conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon")
+ conmonCgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(conmonCgroupPath))
+ if err != nil && err != cgroups.ErrCgroupDeleted {
+ return err
+ }
+ if err == nil {
+ if err := conmonCgroup.Delete(); err != nil {
+ logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err)
+ }
+ }
cgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(p.state.CgroupPath))
if err != nil && err != cgroups.ErrCgroupDeleted {
return err
- } else if err == nil {
+ }
+ if err == nil {
if err := cgroup.Delete(); err != nil {
- // The pod is already almost gone.
- // No point in hard-failing if we fail
- // this bit of cleanup.
logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
}
}
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go
index b27e2a724..a9d975b4b 100644
--- a/pkg/lookup/lookup.go
+++ b/pkg/lookup/lookup.go
@@ -1,10 +1,12 @@
package lookup
import (
+ "os"
+ "strconv"
+
"github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/sirupsen/logrus"
- "strconv"
)
const (
@@ -116,7 +118,7 @@ func GetUser(containerMount, userIDorName string) (*user.User, error) {
}
return u.Uid == uid
})
- if err != nil {
+ if err != nil && !os.IsNotExist(err) {
return nil, err
}
if len(users) > 0 {
@@ -146,7 +148,7 @@ func GetGroup(containerMount, groupIDorName string) (*user.Group, error) {
}
return g.Gid == gid
})
- if err != nil {
+ if err != nil && !os.IsNotExist(err) {
return nil, err
}
if len(groups) > 0 {
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 5c45f2694..ff8c8fe34 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -187,6 +187,9 @@ func BecomeRootInUserNS() (bool, int, error) {
if username == "" {
user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid()))
if err != nil && os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" {
+ if os.IsNotExist(err) {
+ return false, 0, errors.Wrapf(err, "/etc/subuid or /etc/subgid does not exist, see subuid/subgid man pages for information on these files")
+ }
return false, 0, errors.Wrapf(err, "could not find user by UID nor USER env was set")
}
if err == nil {
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 69f49e72a..3b43489b2 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -3,6 +3,7 @@ package util
import (
"fmt"
"os"
+ "os/exec"
"path/filepath"
"strconv"
"strings"
@@ -273,7 +274,12 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) {
dataDir = filepath.Join(resolvedHome, ".local", "share")
}
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
- opts.GraphDriverName = "vfs"
+ if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
+ opts.GraphDriverName = "overlay"
+ opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
+ } else {
+ opts.GraphDriverName = "vfs"
+ }
return opts, nil
}
diff --git a/seccomp.json b/seccomp.json
index 19fadb4bb..fd0681a86 100644
--- a/seccomp.json
+++ b/seccomp.json
@@ -322,13 +322,13 @@
"stat64",
"statfs",
"statfs64",
+ "statx",
"symlink",
"symlinkat",
"sync",
"sync_file_range",
"syncfs",
"sysinfo",
- "syslog",
"tee",
"tgkill",
"time",
@@ -565,6 +565,7 @@
"setdomainname",
"sethostname",
"setns",
+ "syslog",
"umount",
"umount2",
"unshare"
@@ -750,6 +751,36 @@
]
},
"excludes": {}
+ },
+ {
+ "names": [
+ "get_mempolicy",
+ "mbind",
+ "set_mempolicy"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_NICE"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "syslog"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYSLOG"
+ ]
+ },
+ "excludes": {}
}
]
}
diff --git a/test/e2e/config.go b/test/e2e/config.go
new file mode 100644
index 000000000..8116d993b
--- /dev/null
+++ b/test/e2e/config.go
@@ -0,0 +1,9 @@
+package integration
+
+var (
+ redis = "docker.io/library/redis:alpine"
+ fedoraMinimal = "registry.fedoraproject.org/fedora-minimal:latest"
+ ALPINE = "docker.io/library/alpine:latest"
+ infra = "k8s.gcr.io/pause:3.1"
+ BB = "docker.io/library/busybox:latest"
+)
diff --git a/test/e2e/config_amd64.go b/test/e2e/config_amd64.go
new file mode 100644
index 000000000..3459bea6d
--- /dev/null
+++ b/test/e2e/config_amd64.go
@@ -0,0 +1,11 @@
+package integration
+
+var (
+ STORAGE_OPTIONS = "--storage-driver vfs"
+ ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs"
+ CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels}
+ nginx = "quay.io/libpod/alpine_nginx:latest"
+ BB_GLIBC = "docker.io/library/busybox:glibc"
+ registry = "docker.io/library/registry:2"
+ labels = "quay.io/libpod/alpine_labels:latest"
+)
diff --git a/test/e2e/config_ppc64le.go b/test/e2e/config_ppc64le.go
new file mode 100644
index 000000000..d1737fa6f
--- /dev/null
+++ b/test/e2e/config_ppc64le.go
@@ -0,0 +1,11 @@
+package integration
+
+var (
+ STORAGE_OPTIONS = "--storage-driver overlay"
+ ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs"
+ CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, infra, labels}
+ nginx = "quay.io/libpod/alpine_nginx-ppc64le:latest"
+ BB_GLIBC = "docker.io/ppc64le/busybox:glibc"
+ labels = "quay.io/libpod/alpine_labels-ppc64le:latest"
+ registry string
+)
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 56a603f3e..ec274cc34 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "runtime"
"strings"
"testing"
"time"
@@ -29,19 +30,8 @@ var (
RUNC_BINARY string
INTEGRATION_ROOT string
CGROUP_MANAGER = "systemd"
- STORAGE_OPTIONS = "--storage-driver vfs"
ARTIFACT_DIR = "/tmp/.artifacts"
- CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels}
RESTORE_IMAGES = []string{ALPINE, BB}
- ALPINE = "docker.io/library/alpine:latest"
- BB = "docker.io/library/busybox:latest"
- BB_GLIBC = "docker.io/library/busybox:glibc"
- fedoraMinimal = "registry.fedoraproject.org/fedora-minimal:latest"
- nginx = "quay.io/baude/alpine_nginx:latest"
- redis = "docker.io/library/redis:alpine"
- registry = "docker.io/library/registry:2"
- infra = "k8s.gcr.io/pause:3.1"
- labels = "quay.io/baude/alpine_labels:latest"
defaultWaitTimeout = 90
)
@@ -70,6 +60,7 @@ type PodmanTest struct {
type HostOS struct {
Distribution string
Version string
+ Arch string
}
// TestLibpod ginkgo master function
@@ -245,7 +236,6 @@ func (p *PodmanTest) Cleanup() {
// Remove all containers
stopall := p.Podman([]string{"stop", "-a", "--timeout", "0"})
stopall.WaitWithDefaultTimeout()
-
session := p.Podman([]string{"rm", "-fa"})
session.Wait(90)
// Nuke tempdir
@@ -662,6 +652,7 @@ func GetHostDistributionInfo() HostOS {
l := bufio.NewScanner(f)
host := HostOS{}
+ host.Arch = runtime.GOARCH
for l.Scan() {
if strings.HasPrefix(l.Text(), "ID=") {
host.Distribution = strings.Replace(strings.TrimSpace(strings.Join(strings.Split(l.Text(), "=")[1:], "")), "\"", "", -1)
diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go
index 4f288c216..21e8a4859 100644
--- a/test/e2e/load_test.go
+++ b/test/e2e/load_test.go
@@ -139,6 +139,9 @@ var _ = Describe("Podman load", func() {
})
It("podman load multiple tags", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("skip on ppc64le")
+ }
outfile := filepath.Join(podmanTest.TempDir, "alpine.tar")
alpVersion := "docker.io/library/alpine:3.2"
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 871987db0..6888863ca 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -33,7 +33,6 @@ var _ = Describe("Podman logs", func() {
//sudo bin/podman run -it --rm fedora-minimal bash -c 'for a in `seq 5`; do echo hello; done'
It("podman logs for container", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
@@ -46,7 +45,6 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs tail two lines", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
@@ -59,7 +57,6 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs tail 99 lines", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
@@ -72,7 +69,6 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs tail 2 lines with timestamps", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
@@ -85,7 +81,6 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs latest with since time", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
@@ -98,7 +93,6 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs latest with since duration", func() {
- podmanTest.RestoreArtifact(fedoraMinimal)
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
Expect(logc.ExitCode()).To(Equal(0))
diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go
index c34964f59..e80915670 100644
--- a/test/e2e/pause_test.go
+++ b/test/e2e/pause_test.go
@@ -91,7 +91,7 @@ var _ = Describe("Podman pause", func() {
})
- It("podman remove a paused container by id", func() {
+ It("podman remove a paused container by id without force", func() {
session := podmanTest.RunTopContainer("")
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -111,25 +111,26 @@ var _ = Describe("Podman pause", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState))
- result = podmanTest.Podman([]string{"rm", "--force", cid})
- result.WaitWithDefaultTimeout()
+ })
- Expect(result.ExitCode()).To(Equal(125))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
- Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState))
+ It("podman remove a paused container by id with force", func() {
+ session := podmanTest.RunTopContainer("")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
- result = podmanTest.Podman([]string{"unpause", cid})
+ result := podmanTest.Podman([]string{"pause", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState))
result = podmanTest.Podman([]string{"rm", "--force", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
-
})
It("podman stop a paused container by id", func() {
@@ -213,4 +214,70 @@ var _ = Describe("Podman pause", func() {
result.WaitWithDefaultTimeout()
})
+ It("Pause all containers (no containers exist)", func() {
+ result := podmanTest.Podman([]string{"pause", "--all"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ })
+
+ It("Unpause all containers (no paused containers exist)", func() {
+ result := podmanTest.Podman([]string{"unpause", "--all"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("Pause a bunch of running containers", func() {
+ podmanTest.RestoreArtifact(nginx)
+ for i := 0; i < 3; i++ {
+ name := fmt.Sprintf("test%d", i)
+ run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ }
+ running := podmanTest.Podman([]string{"ps", "-q"})
+ running.WaitWithDefaultTimeout()
+ Expect(running.ExitCode()).To(Equal(0))
+ Expect(len(running.OutputToStringArray())).To(Equal(3))
+
+ pause := podmanTest.Podman([]string{"pause", "--all"})
+ pause.WaitWithDefaultTimeout()
+ Expect(pause.ExitCode()).To(Equal(0))
+
+ running = podmanTest.Podman([]string{"ps", "-q"})
+ running.WaitWithDefaultTimeout()
+ Expect(running.ExitCode()).To(Equal(0))
+ Expect(len(running.OutputToStringArray())).To(Equal(0))
+
+ unpause := podmanTest.Podman([]string{"unpause", "--all"})
+ unpause.WaitWithDefaultTimeout()
+ Expect(unpause.ExitCode()).To(Equal(0))
+ })
+
+ It("Unpause a bunch of running containers", func() {
+ podmanTest.RestoreArtifact(nginx)
+ for i := 0; i < 3; i++ {
+ name := fmt.Sprintf("test%d", i)
+ run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ }
+ pause := podmanTest.Podman([]string{"pause", "--all"})
+ pause.WaitWithDefaultTimeout()
+ Expect(pause.ExitCode()).To(Equal(0))
+
+ unpause := podmanTest.Podman([]string{"unpause", "--all"})
+ unpause.WaitWithDefaultTimeout()
+ Expect(unpause.ExitCode()).To(Equal(0))
+
+ running := podmanTest.Podman([]string{"ps", "-q"})
+ running.WaitWithDefaultTimeout()
+ Expect(running.ExitCode()).To(Equal(0))
+ Expect(len(running.OutputToStringArray())).To(Equal(3))
+ })
+
})
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index 821476f39..606160198 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -63,11 +63,11 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from alternate registry without tag", func() {
- session := podmanTest.Podman([]string{"pull", "quay.io/baude/alpine_nginx"})
+ session := podmanTest.Podman([]string{"pull", "quay.io/libpod/alpine_nginx"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"rmi", "quay.io/baude/alpine_nginx"})
+ session = podmanTest.Podman([]string{"rmi", "quay.io/libpod/alpine_nginx"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go
index 3ee150551..5e3d3745a 100644
--- a/test/e2e/push_test.go
+++ b/test/e2e/push_test.go
@@ -58,6 +58,9 @@ var _ = Describe("Podman push", func() {
})
It("podman push to local registry", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
session.WaitWithDefaultTimeout()
@@ -73,6 +76,9 @@ var _ = Describe("Podman push", func() {
})
It("podman push to local registry with authorization", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
authPath := filepath.Join(podmanTest.TempDir, "auth")
os.Mkdir(authPath, os.ModePerm)
os.MkdirAll("/etc/containers/certs.d/localhost:5000", os.ModePerm)
diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go
index d2fc35485..eca2bbcda 100644
--- a/test/e2e/restart_test.go
+++ b/test/e2e/restart_test.go
@@ -136,4 +136,44 @@ var _ = Describe("Podman restart", func() {
Expect(timeSince < 10*time.Second).To(BeTrue())
Expect(timeSince > 2*time.Second).To(BeTrue())
})
+
+ It("Podman restart --all", func() {
+ _, exitCode, _ := podmanTest.RunLsContainer("test1")
+ Expect(exitCode).To(Equal(0))
+
+ test2 := podmanTest.RunTopContainer("test2")
+ test2.WaitWithDefaultTimeout()
+ Expect(test2.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ startTime.WaitWithDefaultTimeout()
+
+ session := podmanTest.Podman([]string{"restart", "-all"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0])))
+ Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
+ })
+
+ It("Podman restart --all --running", func() {
+ _, exitCode, _ := podmanTest.RunLsContainer("test1")
+ Expect(exitCode).To(Equal(0))
+
+ test2 := podmanTest.RunTopContainer("test2")
+ test2.WaitWithDefaultTimeout()
+ Expect(test2.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ startTime.WaitWithDefaultTimeout()
+
+ session := podmanTest.Podman([]string{"restart", "-a", "--running"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0]))
+ Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
+ })
})
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 8224cd345..2a1a0da77 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -13,8 +13,6 @@ var _ = Describe("Podman rmi", func() {
tempdir string
err error
podmanTest PodmanTest
- image1 = "docker.io/library/alpine:latest"
- image3 = "docker.io/library/busybox:glibc"
)
BeforeEach(func() {
@@ -42,7 +40,7 @@ var _ = Describe("Podman rmi", func() {
})
It("podman rmi with fq name", func() {
- session := podmanTest.Podman([]string{"rmi", image1})
+ session := podmanTest.Podman([]string{"rmi", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -56,15 +54,18 @@ var _ = Describe("Podman rmi", func() {
})
It("podman rmi all images", func() {
- podmanTest.PullImages([]string{image3})
+ podmanTest.PullImages([]string{nginx})
session := podmanTest.Podman([]string{"rmi", "-a"})
session.WaitWithDefaultTimeout()
+ images := podmanTest.Podman([]string{"images"})
+ images.WaitWithDefaultTimeout()
+ fmt.Println(images.OutputToStringArray())
Expect(session.ExitCode()).To(Equal(0))
})
It("podman rmi all images forcibly with short options", func() {
- podmanTest.PullImages([]string{image3})
+ podmanTest.PullImages([]string{nginx})
session := podmanTest.Podman([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go
index bd782a5fc..876e10969 100644
--- a/test/e2e/rootless_test.go
+++ b/test/e2e/rootless_test.go
@@ -40,6 +40,7 @@ var _ = Describe("Podman rootless", func() {
}
podmanTest = PodmanCreate(tempdir)
podmanTest.CgroupManager = "cgroupfs"
+ podmanTest.StorageOptions = ROOTLESS_STORAGE_OPTIONS
podmanTest.RestoreAllArtifacts()
})
@@ -92,6 +93,7 @@ var _ = Describe("Podman rootless", func() {
Expect(err).To(BeNil())
rootlessTest := PodmanCreate(tempdir)
rootlessTest.CgroupManager = "cgroupfs"
+ rootlessTest.StorageOptions = ROOTLESS_STORAGE_OPTIONS
err = filepath.Walk(tempdir, chownFunc)
Expect(err).To(BeNil())
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
index c5a02c776..a617035a1 100644
--- a/test/e2e/run_dns_test.go
+++ b/test/e2e/run_dns_test.go
@@ -1,9 +1,9 @@
package integration
import (
+ "fmt"
"os"
- "fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
@@ -83,4 +83,11 @@ var _ = Describe("Podman run dns", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("foobar"))
})
+
+ It("podman run add hostname sets /etc/hosts", func() {
+ session := podmanTest.Podman([]string{"run", "-t", "-i", "--hostname=foobar", ALPINE, "cat", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.LineInOutputContains("foobar")).To(BeTrue())
+ })
})
diff --git a/test/e2e/run_signal_test.go b/test/e2e/run_signal_test.go
index 02b6f4941..5de17108c 100644
--- a/test/e2e/run_signal_test.go
+++ b/test/e2e/run_signal_test.go
@@ -56,6 +56,9 @@ var _ = Describe("Podman run with --sig-proxy", func() {
})
Specify("signals are forwarded to container using sig-proxy", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("Doesnt work on ppc64le")
+ }
signal := syscall.SIGFPE
// Set up a socket for communication
udsDir := filepath.Join(tmpdir, "socket")
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index cb436ccca..98bf66a67 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -42,8 +42,9 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container based on a complex local image name", func() {
+ imageName := strings.TrimPrefix(nginx, "quay.io/")
podmanTest.RestoreArtifact(nginx)
- session := podmanTest.Podman([]string{"run", "baude/alpine_nginx:latest", "ls"})
+ session := podmanTest.Podman([]string{"run", imageName, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull"))
Expect(session.ExitCode()).To(Equal(0))
@@ -51,13 +52,13 @@ var _ = Describe("Podman run", func() {
It("podman run a container based on on a short name with localhost", func() {
podmanTest.RestoreArtifact(nginx)
- tag := podmanTest.Podman([]string{"tag", nginx, "localhost/baude/alpine_nginx:latest"})
+ tag := podmanTest.Podman([]string{"tag", nginx, "localhost/libpod/alpine_nginx:latest"})
tag.WaitWithDefaultTimeout()
rmi := podmanTest.Podman([]string{"rmi", nginx})
rmi.WaitWithDefaultTimeout()
- session := podmanTest.Podman([]string{"run", "baude/alpine_nginx:latest", "ls"})
+ session := podmanTest.Podman([]string{"run", "libpod/alpine_nginx:latest", "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull"))
Expect(session.ExitCode()).To(Equal(0))
@@ -203,6 +204,9 @@ var _ = Describe("Podman run", func() {
})
It("podman run with mount flag", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("skip failing test on ppc64le")
+ }
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
session := podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index 2848da259..84f1efbca 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -128,6 +128,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search attempts HTTP if tls-verify flag is set false", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
fakereg := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
fakereg.WaitWithDefaultTimeout()
@@ -148,6 +151,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search in local registry", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry3", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
registry.WaitWithDefaultTimeout()
@@ -168,6 +174,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
registry := podmanTest.Podman([]string{"run", "-d", "--name", "registry4", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"})
registry.WaitWithDefaultTimeout()
@@ -197,6 +206,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search doesn't attempt HTTP if force secure is true", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry5", registry})
registry.WaitWithDefaultTimeout()
@@ -225,6 +237,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
registry := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry6", registry})
registry.WaitWithDefaultTimeout()
@@ -253,6 +268,9 @@ var _ = Describe("Podman search", func() {
})
It("podman search doesn't attempt HTTP if one registry is not listed as insecure", func() {
+ if podmanTest.Host.Arch == "ppc64le" {
+ Skip("No registry image for ppc64le")
+ }
podmanTest.RestoreArtifact(registry)
registryLocal := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry7", registry})
registryLocal.WaitWithDefaultTimeout()
diff --git a/version/version.go b/version/version.go
index 0fd4e5aeb..01b9b7a8d 100644
--- a/version/version.go
+++ b/version/version.go
@@ -4,4 +4,4 @@ package version
// NOTE: remember to bump the version at the top
// of the top-level README.md file when this is
// bumped.
-const Version = "0.10.2-dev"
+const Version = "0.11.2-dev"