summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml115
-rw-r--r--Makefile4
-rw-r--r--README.md2
-rw-r--r--cmd/podman/attach.go2
-rw-r--r--cmd/podman/build.go2
-rw-r--r--cmd/podman/cliconfig/config.go3
-rw-r--r--cmd/podman/commands.go2
-rw-r--r--cmd/podman/common.go6
-rw-r--r--cmd/podman/container.go1
-rw-r--r--cmd/podman/exec.go2
-rw-r--r--cmd/podman/export.go7
-rw-r--r--cmd/podman/generate_kube.go4
-rw-r--r--cmd/podman/load.go70
-rw-r--r--cmd/podman/login.go2
-rw-r--r--cmd/podman/logs.go58
-rw-r--r--cmd/podman/main.go40
-rw-r--r--cmd/podman/mount.go2
-rw-r--r--cmd/podman/play_kube.go17
-rw-r--r--cmd/podman/pod_inspect.go9
-rw-r--r--cmd/podman/pod_kill.go2
-rw-r--r--cmd/podman/pod_restart.go14
-rw-r--r--cmd/podman/pod_stats.go2
-rw-r--r--cmd/podman/pod_top.go26
-rw-r--r--cmd/podman/ps.go4
-rw-r--r--cmd/podman/pull.go2
-rw-r--r--cmd/podman/push.go2
-rw-r--r--cmd/podman/restart.go83
-rw-r--r--cmd/podman/rm.go12
-rw-r--r--cmd/podman/run.go2
-rw-r--r--cmd/podman/runlabel.go30
-rw-r--r--cmd/podman/save.go8
-rw-r--r--cmd/podman/search.go21
-rw-r--r--cmd/podman/shared/container.go80
-rw-r--r--cmd/podman/shared/create.go2
-rw-r--r--cmd/podman/start.go10
-rw-r--r--cmd/podman/tree.go2
-rw-r--r--cmd/podman/umount.go2
-rw-r--r--cmd/podman/varlink/io.podman.varlink10
-rw-r--r--cmd/podman/version.go4
-rw-r--r--commands.md2
-rw-r--r--completions/bash/podman1
-rw-r--r--completions/zsh/_podman385
-rw-r--r--contrib/cirrus/lib.sh45
-rwxr-xr-xcontrib/cirrus/notice_master_failure.sh19
-rwxr-xr-xcontrib/cirrus/rootless_test.sh3
-rwxr-xr-xcontrib/cirrus/setup_environment.sh15
-rw-r--r--contrib/spec/podman.spec.in1
-rw-r--r--docs/podman-build.1.md2
-rw-r--r--docs/podman-container-exists.1.md6
-rw-r--r--docs/podman-container-prune.1.md3
-rw-r--r--docs/podman-container-runlabel.1.md6
-rw-r--r--docs/podman-cp.1.md44
-rw-r--r--docs/podman-events.1.md2
-rw-r--r--docs/podman-export.1.md2
-rw-r--r--docs/podman-generate-kube.1.md5
-rw-r--r--docs/podman-generate.1.md2
-rw-r--r--docs/podman-healthcheck-run.1.md2
-rw-r--r--docs/podman-healthcheck.1.md2
-rw-r--r--docs/podman-history.1.md2
-rw-r--r--docs/podman-image-exists.1.md6
-rw-r--r--docs/podman-image-prune.1.md4
-rw-r--r--docs/podman-image-sign.1.md2
-rw-r--r--docs/podman-image-tree.1.md2
-rw-r--r--docs/podman-image-trust.1.md4
-rw-r--r--docs/podman-image.1.md6
-rw-r--r--docs/podman-load.1.md18
-rw-r--r--docs/podman-logs.1.md18
-rw-r--r--docs/podman-play-kube.1.md2
-rw-r--r--docs/podman-play.1.md2
-rw-r--r--docs/podman-pod-exists.1.md6
-rw-r--r--docs/podman-pod-top.1.md2
-rw-r--r--docs/podman-pod.1.md2
-rw-r--r--docs/podman-ps.1.md9
-rw-r--r--docs/podman-push.1.md2
-rw-r--r--docs/podman-restart.1.md4
-rw-r--r--docs/podman-run.1.md11
-rw-r--r--docs/podman-save.1.md5
-rw-r--r--docs/podman-volume-inspect.1.md2
-rw-r--r--docs/podman-volume-prune.1.md2
-rw-r--r--docs/podman-wait.1.md2
-rw-r--r--docs/podman.1.md20
-rwxr-xr-xhack/get_ci_vm.sh13
-rwxr-xr-xhack/man-page-checker105
-rw-r--r--install.md1
-rw-r--r--libpod/container_inspect.go1
-rw-r--r--libpod/container_internal.go19
-rw-r--r--libpod/container_internal_linux.go17
-rw-r--r--libpod/container_internal_unsupported.go4
-rw-r--r--libpod/container_log.go213
-rw-r--r--libpod/events.go6
-rw-r--r--libpod/image/image.go3
-rw-r--r--libpod/networking_linux.go5
-rw-r--r--libpod/oci.go7
-rw-r--r--libpod/runtime.go185
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--pkg/adapter/containers.go27
-rw-r--r--pkg/adapter/containers_remote.go47
-rw-r--r--pkg/inspect/inspect.go1
-rw-r--r--pkg/rootless/rootless_linux.go2
-rw-r--r--pkg/spec/createconfig.go2
-rw-r--r--pkg/util/utils.go57
-rw-r--r--pkg/varlinkapi/containers.go54
-rw-r--r--test/certs/README.md9
-rw-r--r--test/certs/domain.cfg30
-rw-r--r--test/certs/domain.crt47
-rw-r--r--test/certs/domain.key45
-rw-r--r--test/e2e/common_test.go5
-rw-r--r--test/e2e/create_staticip_test.go1
-rw-r--r--test/e2e/e2e.coverprofile18
-rw-r--r--test/e2e/generate_kube_test.go4
-rw-r--r--test/e2e/healthcheck_run_test.go1
-rw-r--r--test/e2e/info_test.go2
-rw-r--r--test/e2e/inspect_test.go10
-rw-r--r--test/e2e/libpod_suite_remoteclient_test.go9
-rw-r--r--test/e2e/libpod_suite_test.go13
-rw-r--r--test/e2e/logs_test.go38
-rw-r--r--test/e2e/pause_test.go1
-rw-r--r--test/e2e/pod_create_test.go4
-rw-r--r--test/e2e/pod_pause_test.go1
-rw-r--r--test/e2e/pod_stats_test.go1
-rw-r--r--test/e2e/ps_test.go19
-rw-r--r--test/e2e/push_test.go1
-rw-r--r--test/e2e/rm_test.go14
-rw-r--r--test/e2e/rootless_test.go16
-rw-r--r--test/e2e/run_cgroup_parent_test.go1
-rw-r--r--test/e2e/run_cpu_test.go6
-rw-r--r--test/e2e/run_device_test.go5
-rw-r--r--test/e2e/run_dns_test.go1
-rw-r--r--test/e2e/run_memory_test.go1
-rw-r--r--test/e2e/run_networking_test.go5
-rw-r--r--test/e2e/run_ns_test.go2
-rw-r--r--test/e2e/run_privileged_test.go3
-rw-r--r--test/e2e/run_selinux_test.go2
-rw-r--r--test/e2e/run_staticip_test.go1
-rw-r--r--test/e2e/run_test.go19
-rw-r--r--test/e2e/run_userns_test.go1
-rw-r--r--test/e2e/stats_test.go1
-rw-r--r--test/e2e/systemd_test.go1
-rw-r--r--test/e2e/version_test.go20
-rw-r--r--test/system/000-TEMPLATE2
-rw-r--r--test/system/015-help.bats19
-rw-r--r--test/system/035-logs.bats27
-rw-r--r--test/system/060-mount.bats2
-rw-r--r--test/system/075-exec.bats30
-rw-r--r--test/system/120-load.bats96
-rw-r--r--test/system/200-pod-top.bats14
-rw-r--r--test/system/300-cli-parsing.bats15
-rw-r--r--test/test_podman_build.sh7
148 files changed, 2143 insertions, 544 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 8e26ce72e..d097c9f47 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -28,15 +28,15 @@ env:
#### Cache-image names to test with
###
ACTIVE_CACHE_IMAGE_NAMES: >-
- fedora-29-libpod-d6d53e40
- fedora-28-libpod-d6d53e40
- ubuntu-18-libpod-d6d53e40
- rhel-7-libpod-7f4cd1f7
+ fedora-28-libpod-6318419153518592
+ fedora-29-libpod-6318419153518592
+ ubuntu-18-libpod-6318419153518592
+ rhel-7-libpod-6318419153518592
image-builder-image-1541772081
- FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-d6d53e40"
- PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-d6d53e40"
- UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-d6d53e40"
- PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-7f4cd1f7"
+ FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-6318419153518592"
+ PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-6318419153518592"
+ UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-6318419153518592"
+ PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-6318419153518592"
# RHEL_CACHE_IMAGE_NAME: "rhel-8-notready"
# CENTOS_CACHE_IMAGE_NAME: "centos-7-notready"
@@ -69,9 +69,7 @@ env:
####
#### Default to NOT running in rootless-testing mode
####
- ROOTLESS_USER:
- ROOTLESS_UID:
- ROOTLESS_GID:
+ ROOTLESS_USER: ""
####
#### Credentials and other secret-sauces, decrypted at runtime when authorized.
@@ -101,7 +99,7 @@ env:
CIRRUS_TASK_ID CIRRUS_REPO_NAME CIRRUS_REPO_OWNER CIRRUS_REPO_FULL_NAME
CIRRUS_REPO_CLONE_URL CIRRUS_SHELL CIRRUS_USER_COLLABORATOR CIRRUS_USER_PERMISSION
CIRRUS_WORKING_DIR CIRRUS_HTTP_CACHE_HOST PACKER_BUILDS BUILT_IMAGE_SUFFIX
- XDG_DATA_DIRS XDG_RUNTIME_DIR XDG_SESSION_ID ROOTLESS_USER ROOTLESS_UID ROOTLESS_GID
+ XDG_DATA_DIRS XDG_RUNTIME_DIR XDG_SESSION_ID ROOTLESS_USER
# Every *_task runs in parallel in separate VMsd. The name prefix only for reference
@@ -118,20 +116,13 @@ gating_task:
cpu: 4
memory: 12
+ timeout_in: 20m
+
gate_script:
- '/usr/local/bin/entrypoint.sh validate'
- '/usr/local/bin/entrypoint.sh lint'
- '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/test/test_dot_cirrus_yaml.py'
- # This task runs `make vendor` followed by ./hack/tree_status.sh to check
- # whether the git tree is clean. The reasoning for that is to make sure
- # that the vendor.conf, the code and the vendored packages in ./vendor are
- # in sync at all times.
- vendor_script:
- - '/usr/local/bin/entrypoint.sh .install.vndr'
- - '/usr/local/bin/entrypoint.sh vendor'
- - 'cd /go/src/github.com/containers/libpod && ./hack/tree_status.sh'
-
# This task builds Podman with different buildtags to ensure the build does
# not break.
build_script:
@@ -140,6 +131,39 @@ gating_task:
- '/usr/local/bin/entrypoint.sh clean podman BUILDTAGS="exclude_graphdriver_devicemapper selinux seccomp"'
- '/usr/local/bin/entrypoint.sh clean podman-remote-darwin'
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+
+
+# This task runs `make vendor` followed by ./hack/tree_status.sh to check
+# whether the git tree is clean. The reasoning for that is to make sure
+# that the vendor.conf, the code and the vendored packages in ./vendor are
+# in sync at all times.
+vendor_task:
+
+ depends_on:
+ - "gating"
+
+ env:
+ CIRRUS_WORKING_DIR: "/usr/src/libpod"
+
+ # Runs within Cirrus's "community cluster"
+ container:
+ image: "quay.io/libpod/gate:latest"
+ cpu: 4
+ memory: 12
+
+ timeout_in: 30m
+
+ vendor_script:
+ - '/usr/local/bin/entrypoint.sh .install.vndr'
+ - '/usr/local/bin/entrypoint.sh vendor'
+ - 'cd /go/src/github.com/containers/libpod && ./hack/tree_status.sh'
+
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+
+
build_each_commit_task:
depends_on:
@@ -160,9 +184,12 @@ build_each_commit_task:
timeout_in: 30m
script:
- - $SCRIPT_BASE/setup_environment.sh
- - git fetch --depth $CIRRUS_CLONE_DEPTH origin $CIRRUS_BASE_BRANCH
- - env GOPATH=/var/tmp/go/ make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH
+ - '$SCRIPT_BASE/setup_environment.sh'
+ - 'git fetch --depth $CIRRUS_CLONE_DEPTH origin $CIRRUS_BASE_BRANCH'
+ - 'env GOPATH=/var/tmp/go/ make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH'
+
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
# Update metadata on VM images referenced by this repository state
@@ -186,7 +213,7 @@ meta_task:
GCPPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f]
CIRRUS_CLONE_DEPTH: 1 # source not used
- script: /usr/local/bin/entrypoint.sh
+ script: '/usr/local/bin/entrypoint.sh'
# This task does the unit and integration testing for every platform
@@ -219,14 +246,12 @@ testing_task:
# Every *_script runs in sequence, for each task. The name prefix is for
# WebUI reference. The values may be strings...
- setup_environment_script: $SCRIPT_BASE/setup_environment.sh
-
- # ...or lists of strings
- unit_test_script:
- - go version
- - $SCRIPT_BASE/unit_test.sh
+ setup_environment_script: '$SCRIPT_BASE/setup_environment.sh'
+ unit_test_script: '$SCRIPT_BASE/unit_test.sh'
+ integration_test_script: '$SCRIPT_BASE/integration_test.sh'
- integration_test_script: $SCRIPT_BASE/integration_test.sh
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
# This task executes tests as a regular user on a system
@@ -247,17 +272,18 @@ rootless_testing_task:
env:
ROOTLESS_USER: "olympiclongjumpingwithjesus"
- ROOTLESS_UID: 123456
- ROOTLESS_GID: 123456
timeout_in: 120m
- setup_environment_script: $SCRIPT_BASE/setup_environment.sh
+ setup_environment_script: '$SCRIPT_BASE/setup_environment.sh'
rootless_test_script: >-
ssh $ROOTLESS_USER@localhost
-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=no
$CIRRUS_WORKING_DIR/$SCRIPT_BASE/rootless_test.sh
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+
# Because system tests are stored within the repository, it is sometimes
# necessary to execute them within a PR to validate changes.
@@ -283,8 +309,8 @@ optional_testing_task:
timeout_in: 60m
- setup_environment_script: $SCRIPT_BASE/setup_environment.sh
- system_test_script: $SCRIPT_BASE/system_test.sh
+ setup_environment_script: '$SCRIPT_BASE/setup_environment.sh'
+ system_test_script: '$SCRIPT_BASE/system_test.sh'
# Build new cache-images for future PR testing, but only after a PR merge.
@@ -317,8 +343,8 @@ cache_images_task:
scopes:
- compute
- devstorage.full_control
- environment_script: $SCRIPT_BASE/setup_environment.sh
- build_vm_images_script: $SCRIPT_BASE/build_vm_images.sh
+ environment_script: '$SCRIPT_BASE/setup_environment.sh'
+ build_vm_images_script: '$SCRIPT_BASE/build_vm_images.sh'
# TODO,Continuous Delivery: Automatically open a libpod PR after using 'sed' to replace
# the image_names with the new (just build) images. That will
@@ -331,16 +357,21 @@ cache_images_task:
# - modify_cirrus_yaml_image_names.sh
# - commit_and_create_upstream_pr.sh
+ on_failure:
+ master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+
# Post message to IRC if everything passed
success_task:
+ only_if: $CIRRUS_BRANCH != 'master'
+
depends_on: # ignores any dependent task conditions
- "gating"
- - "vendor_check"
+ - "build_each_commit_task"
- "testing"
+ - "rootless_testing_task"
- "optional_testing"
- - "cache_images"
env:
CIRRUS_WORKING_DIR: "/usr/src/libpod"
@@ -350,4 +381,4 @@ success_task:
cpu: 1
memory: 1
- success_script: $SCRIPT_BASE/success.sh
+ success_script: '$SCRIPT_BASE/success.sh'
diff --git a/Makefile b/Makefile
index 782fa4e62..03e21cbe1 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
OCI_RUNTIME ?= ""
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
+ZSHINSTALLDIR=${PREFIX}/share/zsh/site-functions
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e | grep -v system )
@@ -247,6 +248,8 @@ install.config:
install.completions:
install ${SELINUXOPT} -d -m 755 ${BASHINSTALLDIR}
install ${SELINUXOPT} -m 644 completions/bash/podman ${BASHINSTALLDIR}
+ install ${SELINUXOPT} -d -m 755 ${ZSHINSTALLDIR}
+ install ${SELINUXOPT} -m 644 completions/zsh/_podman ${ZSHINSTALLDIR}
install.cni:
install ${SELINUXOPT} -d -m 755 ${ETCDIR}/cni/net.d/
@@ -332,6 +335,7 @@ API.md: cmd/podman/varlink/io.podman.varlink
validate.completions: completions/bash/podman
. completions/bash/podman
+ if [ -x /bin/zsh ]; then /bin/zsh completions/zsh/_podman; fi
validate: gofmt .gitvalidation validate.completions
diff --git a/README.md b/README.md
index 636a6fa5b..fe78c9d58 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`
At a high level, the scope of libpod and podman is the following:
-* Support multiple image formats including the existing Docker/OCI image formats.
+* Support multiple image formats including the OCI and Docker image formats.
* Support for multiple means to download images including trust & image verification.
* Container image management (managing image layers, overlay filesystems, etc).
* Full management of container lifecycle
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go
index 08976cdaa..86e89cfd7 100644
--- a/cmd/podman/attach.go
+++ b/cmd/podman/attach.go
@@ -35,7 +35,7 @@ func init() {
flags := attachCommand.Flags()
flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false")
- flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)")
+ flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
markFlagHiddenForRemoteClient("latest", flags)
}
diff --git a/cmd/podman/build.go b/cmd/podman/build.go
index 72d78aff9..f0a67791a 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/build.go
@@ -52,7 +52,7 @@ func init() {
buildCommand.SetHelpTemplate(HelpTemplate())
buildCommand.SetUsageTemplate(UsageTemplate())
flags := buildCommand.Flags()
- flags.SetInterspersed(false)
+ flags.SetInterspersed(true)
budFlags := buildahcli.GetBudFlags(&budFlagsValues)
flag := budFlags.Lookup("pull")
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index cb9d9a338..1461c9f03 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -421,14 +421,15 @@ type RmiValues struct {
type RunlabelValues struct {
PodmanCommand
Authfile string
- Display bool
CertDir string
Creds string
+ Display bool
Name string
Opt1 string
Opt2 string
Opt3 string
Quiet bool
+ Replace bool
SignaturePolicy string
TlsVerify bool
}
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index d37af70c1..810c5a6f6 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -21,7 +21,6 @@ func getMainCommands() []*cobra.Command {
&_psCommand,
_loginCommand,
_logoutCommand,
- _logsCommand,
_mountCommand,
_pauseCommand,
_portCommand,
@@ -63,7 +62,6 @@ func getContainerSubCommands() []*cobra.Command {
_execCommand,
_exportCommand,
_killCommand,
- _logsCommand,
_mountCommand,
_pauseCommand,
_portCommand,
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index b76037297..8b42ed673 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -264,7 +264,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"entrypoint", "",
"Overwrite the default ENTRYPOINT of the image",
)
- createFlags.StringSliceP(
+ createFlags.StringArrayP(
"env", "e", []string{},
"Set environment variables in container",
)
@@ -313,7 +313,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
)
createFlags.String(
"image-volume", "bind",
- "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')",
+ "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore'",
)
createFlags.Bool(
"init", false,
@@ -374,7 +374,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
)
createFlags.Int64(
"memory-swappiness", -1,
- "Tune container memory swappiness (0 to 100) (default -1)",
+ "Tune container memory swappiness (0 to 100, or -1 for system default)",
)
createFlags.String(
"name", "",
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index ce6ad8883..2e9cedbaa 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -53,6 +53,7 @@ var (
_containerExistsCommand,
_contInspectSubCommand,
_listSubCommand,
+ _logsCommand,
}
)
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index aa81edf56..a6afbf75a 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -41,7 +41,7 @@ func init() {
execCommand.SetUsageTemplate(UsageTemplate())
flags := execCommand.Flags()
flags.SetInterspersed(false)
- flags.StringSliceVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables")
+ flags.StringArrayVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables")
flags.BoolVarP(&execCommand.Interfactive, "interactive", "i", false, "Not supported. All exec commands are interactive by default")
flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&execCommand.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false")
diff --git a/cmd/podman/export.go b/cmd/podman/export.go
index e5dc410a7..92633facd 100644
--- a/cmd/podman/export.go
+++ b/cmd/podman/export.go
@@ -36,7 +36,7 @@ func init() {
exportCommand.SetHelpTemplate(HelpTemplate())
exportCommand.SetUsageTemplate(UsageTemplate())
flags := exportCommand.Flags()
- flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
+ flags.StringVarP(&exportCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
}
// exportCmd saves a container to a tarball on disk
@@ -60,15 +60,16 @@ func exportCmd(c *cliconfig.ExportValues) error {
}
output := c.Output
- if runtime.Remote && (output == "/dev/stdout" || len(output) == 0) {
+ if runtime.Remote && len(output) == 0 {
return errors.New("remote client usage must specify an output file (-o)")
}
- if output == "/dev/stdout" {
+ if len(output) == 0 {
file := os.Stdout
if logrus.IsTerminal(file) {
return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
}
+ output = "/dev/stdout"
}
if err := parse.ValidateFileName(output); err != nil {
diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go
index e3db14af3..42cfba8d8 100644
--- a/cmd/podman/generate_kube.go
+++ b/cmd/podman/generate_kube.go
@@ -57,8 +57,8 @@ func generateKubeYAMLCmd(c *cliconfig.GenerateKubeValues) error {
return errors.Wrapf(libpod.ErrNotImplemented, "rootless users")
}
args := c.InputArgs
- if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) {
- return errors.Errorf("you must provide one container|pod ID or name or --latest")
+ if len(args) != 1 {
+ return errors.Errorf("you must provide exactly one container|pod ID or name")
}
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
diff --git a/cmd/podman/load.go b/cmd/podman/load.go
index 303c23bc7..04ff9fcca 100644
--- a/cmd/podman/load.go
+++ b/cmd/podman/load.go
@@ -5,21 +5,24 @@ import (
"io"
"io/ioutil"
"os"
+ "strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "golang.org/x/crypto/ssh/terminal"
)
var (
loadCommand cliconfig.LoadValues
- loadDescription = "Loads the image from docker-archive stored on the local machine."
- _loadCommand = &cobra.Command{
- Use: "load [flags] [PATH]",
- Short: "Load an image from docker archive",
+ loadDescription = "Loads an image from a locally stored archive (tar file) into container storage."
+
+ _loadCommand = &cobra.Command{
+ Use: "load [flags] [NAME[:TAG]]",
+ Short: "Load an image from container archive",
Long: loadDescription,
RunE: func(cmd *cobra.Command, args []string) error {
loadCommand.InputArgs = args
@@ -34,7 +37,7 @@ func init() {
loadCommand.SetHelpTemplate(HelpTemplate())
loadCommand.SetUsageTemplate(UsageTemplate())
flags := loadCommand.Flags()
- flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN")
+ flags.StringVarP(&loadCommand.Input, "input", "i", "", "Read from specified archive file (default: stdin)")
flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output")
flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)")
@@ -60,46 +63,43 @@ func loadCmd(c *cliconfig.LoadValues) error {
}
defer runtime.Shutdown(false)
- input := c.Input
- if runtime.Remote && len(input) == 0 {
- return errors.New("the remote client requires you to load via -i and a tarball")
- }
- if input == "/dev/stdin" {
- fi, err := os.Stdin.Stat()
- if err != nil {
+ if len(c.Input) > 0 {
+ if err := parse.ValidateFileName(c.Input); err != nil {
return err
}
- // checking if loading from pipe
- if !fi.Mode().IsRegular() {
- outFile, err := ioutil.TempFile("/var/tmp", "podman")
- if err != nil {
- return errors.Errorf("error creating file %v", err)
- }
- defer os.Remove(outFile.Name())
- defer outFile.Close()
-
- inFile, err := os.OpenFile(input, 0, 0666)
- if err != nil {
- return errors.Errorf("error reading file %v", err)
- }
- defer inFile.Close()
-
- _, err = io.Copy(outFile, inFile)
- if err != nil {
- return errors.Errorf("error copying file %v", err)
- }
+ } else {
+ if terminal.IsTerminal(int(os.Stdin.Fd())) {
+ return errors.Errorf("cannot read from terminal. Use command-line redirection or the --input flag.")
+ }
+ outFile, err := ioutil.TempFile("/var/tmp", "podman")
+ if err != nil {
+ return errors.Errorf("error creating file %v", err)
+ }
+ defer os.Remove(outFile.Name())
+ defer outFile.Close()
- input = outFile.Name()
+ _, err = io.Copy(outFile, os.Stdin)
+ if err != nil {
+ return errors.Errorf("error copying file %v", err)
}
- }
- if err := parse.ValidateFileName(input); err != nil {
- return err
+
+ c.Input = outFile.Name()
}
names, err := runtime.LoadImage(getContext(), imageName, c)
if err != nil {
return err
}
+ if len(imageName) > 0 {
+ split := strings.Split(names, ",")
+ newImage, err := runtime.NewImageFromLocal(split[0])
+ if err != nil {
+ return err
+ }
+ if err := newImage.TagImage(imageName); err != nil {
+ return errors.Wrapf(err, "error adding '%s' to image %q", imageName, newImage.InputName)
+ }
+ }
fmt.Println("Loaded image(s): " + names)
return nil
}
diff --git a/cmd/podman/login.go b/cmd/podman/login.go
index 43a7d246e..4e96b43cb 100644
--- a/cmd/podman/login.go
+++ b/cmd/podman/login.go
@@ -45,7 +45,7 @@ func init() {
flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry")
flags.BoolVar(&loginCommand.GetLogin, "get-login", true, "Return the current login user for the registry")
flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry")
- flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry")
flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin")
diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go
index c3416fe57..a1b5fb4cc 100644
--- a/cmd/podman/logs.go
+++ b/cmd/podman/logs.go
@@ -1,27 +1,24 @@
package main
import (
- "os"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/logs"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
logsCommand cliconfig.LogsValues
- logsDescription = `Retrieves logs for a container.
+ logsDescription = `Retrieves logs for one or more containers.
This does not guarantee execution order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs.
`
_logsCommand = &cobra.Command{
- Use: "logs [flags] CONTAINER",
+ Use: "logs [flags] CONTAINER [CONTAINER...]",
Short: "Fetch the logs of a container",
Long: logsDescription,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -29,9 +26,19 @@ var (
logsCommand.GlobalFlags = MainGlobalOpts
return logsCmd(&logsCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 && logsCommand.Latest {
+ return errors.New("no containers can be specified when using 'latest'")
+ }
+ if !logsCommand.Latest && len(args) < 1 {
+ return errors.New("specify at least one container name or ID to log")
+ }
+ return nil
+ },
Example: `podman logs ctrID
podman logs --tail 2 mywebserver
- podman logs --follow=true --since 10m ctrID`,
+ podman logs --follow=true --since 10m ctrID
+ podman logs mywebserver mydbserver`,
}
)
@@ -54,20 +61,14 @@ func init() {
}
func logsCmd(c *cliconfig.LogsValues) error {
- var ctr *libpod.Container
var err error
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- args := c.InputArgs
- if len(args) != 1 && !c.Latest {
- return errors.Errorf("'podman logs' requires exactly one container name/ID")
- }
-
sinceTime := time.Time{}
if c.Flag("since").Changed {
// parse time, error out if something is wrong
@@ -78,7 +79,7 @@ func logsCmd(c *cliconfig.LogsValues) error {
sinceTime = since
}
- opts := &logs.LogOptions{
+ opts := &libpod.LogOptions{
Details: c.Details,
Follow: c.Follow,
Since: sinceTime,
@@ -86,30 +87,5 @@ func logsCmd(c *cliconfig.LogsValues) error {
Timestamps: c.Timestamps,
}
- if c.Latest {
- ctr, err = runtime.GetLatestContainer()
- } else {
- ctr, err = runtime.LookupContainer(args[0])
- }
- if err != nil {
- return err
- }
-
- logPath := ctr.LogPath()
-
- state, err := ctr.State()
- if err != nil {
- return err
- }
-
- // If the log file does not exist yet and the container is in the
- // Configured state, it has never been started before and no logs exist
- // Exit cleanly in this case
- if _, err := os.Stat(logPath); err != nil {
- if state == libpod.ContainerStateConfigured {
- logrus.Debugf("Container has not been started, no logs exist yet")
- return nil
- }
- }
- return logs.ReadLogs(logPath, ctr, opts)
+ return runtime.Log(c, opts)
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index af6731d96..dd8b61408 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -45,6 +45,7 @@ var mainCommands = []*cobra.Command{
&_inspectCommand,
_killCommand,
_loadCommand,
+ _logsCommand,
podCommand.Command,
_pullCommand,
_pushCommand,
@@ -66,23 +67,26 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{
_exportCommand: true,
//// `info` must be executed in an user namespace.
//// If this change, please also update libpod.refreshRootless()
- _loginCommand: true,
- _logoutCommand: true,
- _mountCommand: true,
- _killCommand: true,
- _pauseCommand: true,
- _podRmCommand: true,
- _podKillCommand: true,
- _podStatsCommand: true,
- _podStopCommand: true,
- _restartCommand: true,
- _rmCommand: true,
- _runCommand: true,
- _unpauseCommand: true,
- _searchCommand: true,
- _statsCommand: true,
- _stopCommand: true,
- _topCommand: true,
+ _loginCommand: true,
+ _logoutCommand: true,
+ _mountCommand: true,
+ _killCommand: true,
+ _pauseCommand: true,
+ _podRmCommand: true,
+ _podKillCommand: true,
+ _podRestartCommand: true,
+ _podStatsCommand: true,
+ _podStopCommand: true,
+ _podTopCommand: true,
+ _restartCommand: true,
+ &_psCommand: true,
+ _rmCommand: true,
+ _runCommand: true,
+ _unpauseCommand: true,
+ _searchCommand: true,
+ _statsCommand: true,
+ _stopCommand: true,
+ _topCommand: true,
}
var rootCmd = &cobra.Command{
@@ -115,7 +119,7 @@ func init() {
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file")
rootCmd.PersistentFlags().MarkHidden("defaults-mount-file")
rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error (default), fatal or panic")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic")
rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
rootCmd.PersistentFlags().MarkHidden("max-workers")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system")
diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go
index 4381074ab..d074551ce 100644
--- a/cmd/podman/mount.go
+++ b/cmd/podman/mount.go
@@ -25,7 +25,7 @@ var (
`
_mountCommand = &cobra.Command{
- Use: "mount [flags] CONTAINER",
+ Use: "mount [flags] [CONTAINER]",
Short: "Mount a working container's root filesystem",
Long: mountDescription,
RunE: func(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index 44aa4776b..10221a339 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -59,7 +60,7 @@ func init() {
flags.StringVar(&playKubeCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
- flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
}
func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error {
@@ -186,7 +187,7 @@ func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error {
if err != nil {
return err
}
- createConfig, err := kubeContainerToCreateConfig(container, runtime, newImage, namespaces, volumes)
+ createConfig, err := kubeContainerToCreateConfig(ctx, container, runtime, newImage, namespaces, volumes)
if err != nil {
return err
}
@@ -231,7 +232,7 @@ func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
}
// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
-func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Runtime, newImage *image2.Image, namespaces map[string]string, volumes map[string]string) (*createconfig.CreateConfig, error) {
+func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image2.Image, namespaces map[string]string, volumes map[string]string) (*createconfig.CreateConfig, error) {
var (
containerConfig createconfig.CreateConfig
envs map[string]string
@@ -243,6 +244,14 @@ func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Run
containerConfig.Name = containerYAML.Name
containerConfig.Tty = containerYAML.TTY
containerConfig.WorkDir = containerYAML.WorkingDir
+
+ imageData, _ := newImage.Inspect(ctx)
+
+ containerConfig.User = "0"
+ if imageData != nil {
+ containerConfig.User = imageData.Config.User
+ }
+
if containerConfig.SecurityOpts != nil {
if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
@@ -280,6 +289,7 @@ func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Run
for _, e := range containerYAML.Env {
envs[e.Name] = e.Value
}
+ containerConfig.Env = envs
for _, volume := range containerYAML.VolumeMounts {
host_path, exists := volumes[volume.Name]
@@ -291,6 +301,5 @@ func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Run
}
containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", host_path, volume.MountPath))
}
- containerConfig.Env = envs
return &containerConfig, nil
}
diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go
index 79ffe2e6f..851f39aa0 100644
--- a/cmd/podman/pod_inspect.go
+++ b/cmd/podman/pod_inspect.go
@@ -14,7 +14,7 @@ var (
podInspectCommand cliconfig.PodInspectValues
podInspectDescription = `Display the configuration for a pod by name or id
- By default, this will render all results in a JSON array. If the container and image have the same name, this command returns the container JSON.`
+ By default, this will render all results in a JSON array.`
_podInspectCommand = &cobra.Command{
Use: "inspect [flags] POD",
@@ -34,7 +34,7 @@ func init() {
podInspectCommand.SetHelpTemplate(HelpTemplate())
podInspectCommand.SetUsageTemplate(UsageTemplate())
flags := podInspectCommand.Flags()
- flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
markFlagHiddenForRemoteClient("latest", flags)
}
@@ -44,6 +44,11 @@ func podInspectCmd(c *cliconfig.PodInspectValues) error {
pod *adapter.Pod
)
args := c.InputArgs
+
+ if len(args) < 1 && !c.Latest {
+ return errors.Errorf("you must provide the name or id of a pod")
+ }
+
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go
index ebd7db762..c538674a4 100644
--- a/cmd/podman/pod_kill.go
+++ b/cmd/podman/pod_kill.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -48,6 +49,7 @@ func init() {
// podKillCmd kills one or more pods with a signal
func podKillCmd(c *cliconfig.PodKillValues) error {
+ rootless.SetSkipStorageSetup(true)
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
index 0765b98db..9c8d28424 100644
--- a/cmd/podman/pod_restart.go
+++ b/cmd/podman/pod_restart.go
@@ -2,9 +2,11 @@ package main
import (
"fmt"
+ "os"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -46,12 +48,24 @@ func init() {
func podRestartCmd(c *cliconfig.PodRestartValues) error {
var lastError error
+ if os.Geteuid() != 0 {
+ rootless.SetSkipStorageSetup(true)
+ }
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
+ if rootless.IsRootless() {
+ var err error
+
+ c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ return err
+ }
+ }
+
restartIDs, conErrors, restartErrors := runtime.RestartPods(getContext(), c)
for _, p := range restartIDs {
diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go
index 701051938..e8ff322ce 100644
--- a/cmd/podman/pod_stats.go
+++ b/cmd/podman/pod_stats.go
@@ -25,7 +25,7 @@ var (
podStatsDescription = `For each specified pod this command will display percentage of CPU, memory, network I/O, block I/O and PIDs for containers in one the pods.`
_podStatsCommand = &cobra.Command{
- Use: "stats [flags] POD [POD...]",
+ Use: "stats [flags] [POD...]",
Short: "Display a live stream of resource usage statistics for the containers in one or more pods",
Long: podStatsDescription,
RunE: func(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go
index c9a6d8822..f65d66df6 100644
--- a/cmd/podman/pod_top.go
+++ b/cmd/podman/pod_top.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -53,6 +54,10 @@ func podTopCmd(c *cliconfig.PodTopValues) error {
)
args := c.InputArgs
+ if os.Geteuid() != 0 {
+ rootless.SetSkipStorageSetup(true)
+ }
+
if c.ListDescriptors {
descriptors, err := libpod.GetContainerPidInformationDescriptors()
if err != nil {
@@ -77,6 +82,27 @@ func podTopCmd(c *cliconfig.PodTopValues) error {
} else {
descriptors = args[1:]
}
+
+ if os.Geteuid() != 0 {
+ var pod *adapter.Pod
+ var err error
+ if c.Latest {
+ pod, err = runtime.GetLatestPod()
+ } else {
+ pod, err = runtime.LookupPod(c.InputArgs[0])
+ }
+ if err != nil {
+ return errors.Wrapf(err, "unable to lookup requested container")
+ }
+ became, ret, err := runtime.JoinOrCreateRootlessPod(pod)
+ if err != nil {
+ return err
+ }
+ if became {
+ os.Exit(ret)
+ }
+ }
+
w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
psOutput, err := runtime.PodTop(c, descriptors)
if err != nil {
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index de6966c3b..ad942da2e 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -17,6 +17,7 @@ import (
"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/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
@@ -200,6 +201,9 @@ func init() {
}
func psCmd(c *cliconfig.PsValues) error {
+ if os.Geteuid() != 0 {
+ rootless.SetSkipStorageSetup(true)
+ }
if c.Bool("trace") {
span, _ := opentracing.StartSpanFromContext(Ctx, "psCmd")
defer span.Finish()
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index 7986d5530..8888c5e28 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -52,7 +52,7 @@ func init() {
flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
- flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
}
diff --git a/cmd/podman/push.go b/cmd/podman/push.go
index afc385527..a1dac24ae 100644
--- a/cmd/podman/push.go
+++ b/cmd/podman/push.go
@@ -54,7 +54,7 @@ func init() {
flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key")
- flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
}
func pushCmd(c *cliconfig.PushValues) error {
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index 341cbf978..e6a6d8434 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -1,7 +1,6 @@
package main
import (
- "fmt"
"os"
"github.com/containers/libpod/cmd/podman/cliconfig"
@@ -61,6 +60,15 @@ func restartCmd(c *cliconfig.RestartValues) error {
if os.Geteuid() != 0 {
rootless.SetSkipStorageSetup(true)
}
+ if rootless.IsRootless() {
+ // If we are in the re-execed rootless environment,
+ // override the arg to deal only with one container.
+ if os.Geteuid() == 0 {
+ c.All = false
+ c.Latest = false
+ c.InputArgs = []string{rootless.Argument()}
+ }
+ }
args := c.InputArgs
runOnly := c.Running
@@ -107,6 +115,20 @@ func restartCmd(c *cliconfig.RestartValues) error {
}
}
+ if os.Geteuid() != 0 {
+ // In rootless mode we can deal with one container at at time.
+ for _, c := range restartContainers {
+ _, ret, err := joinContainerOrCreateRootlessUserNS(runtime, c)
+ if err != nil {
+ return err
+ }
+ if ret != 0 {
+ os.Exit(ret)
+ }
+ }
+ os.Exit(0)
+ }
+
maxWorkers := shared.Parallelize("restart")
if c.GlobalIsSet("max-workers") {
maxWorkers = c.GlobalFlags.MaxWorks
@@ -114,22 +136,6 @@ func restartCmd(c *cliconfig.RestartValues) error {
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
- if rootless.IsRootless() {
- // With rootless containers we cannot really restart an existing container
- // as we would need to join the mount namespace as well to be able to reuse
- // the storage.
- if err := stopRootlessContainers(restartContainers, timeout, useTimeout, maxWorkers); err != nil {
- return err
- }
- became, ret, err := rootless.BecomeRootInUserNS()
- if err != nil {
- return err
- }
- if became {
- os.Exit(ret)
- }
- }
-
// 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 {
@@ -152,46 +158,3 @@ func restartCmd(c *cliconfig.RestartValues) error {
restartErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, restartFuncs)
return printParallelOutput(restartErrors, errCount)
}
-
-func stopRootlessContainers(stopContainers []*libpod.Container, timeout uint, useTimeout bool, maxWorkers int) error {
- var stopFuncs []shared.ParallelWorkerInput
- for _, ctr := range stopContainers {
- state, err := ctr.State()
- if err != nil {
- return err
- }
- if state != libpod.ContainerStateRunning {
- continue
- }
-
- ctrTimeout := ctr.StopTimeout()
- if useTimeout {
- ctrTimeout = timeout
- }
-
- c := ctr
- f := func() error {
- return c.StopWithTimeout(ctrTimeout)
- }
-
- stopFuncs = append(stopFuncs, shared.ParallelWorkerInput{
- ContainerID: c.ID(),
- ParallelFunc: f,
- })
-
- restartErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
- var lastError error
- for _, result := range restartErrors {
- if result != nil {
- if errCount > 1 {
- fmt.Println(result.Error())
- }
- lastError = result
- }
- }
- if lastError != nil {
- return lastError
- }
- }
- return nil
-}
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 56aaae9eb..253771e14 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -108,6 +108,7 @@ func rmCmd(c *cliconfig.RmValues) error {
c.Latest = false
c.InputArgs = []string{rootless.Argument()}
} else {
+ exitCode = 0
var containers []*libpod.Container
if c.All {
containers, err = runtime.GetContainers()
@@ -121,6 +122,10 @@ func rmCmd(c *cliconfig.RmValues) error {
for _, c := range c.InputArgs {
container, err = runtime.LookupContainer(c)
if err != nil {
+ if errors.Cause(err) == libpod.ErrNoSuchCtr {
+ exitCode = 1
+ continue
+ }
return err
}
containers = append(containers, container)
@@ -136,7 +141,7 @@ func rmCmd(c *cliconfig.RmValues) error {
os.Exit(ret)
}
}
- os.Exit(0)
+ os.Exit(exitCode)
}
}
@@ -195,5 +200,10 @@ func rmCmd(c *cliconfig.RmValues) error {
exitCode = 1
}
}
+
+ if failureCnt > 0 {
+ exitCode = 125
+ }
+
return err
}
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index a92d5d3db..32e7b3510 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -44,7 +44,7 @@ func init() {
runCommand.SetUsageTemplate(UsageTemplate())
flags := runCommand.Flags()
flags.SetInterspersed(false)
- flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)")
+ flags.Bool("sig-proxy", true, "Proxy received signals to the process")
getCreateFlags(&runCommand.PodmanCommand)
}
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index 68621e095..f79aa8b0e 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -10,9 +10,11 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/utils"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -45,6 +47,7 @@ func init() {
flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
flags.StringVar(&runlabelCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVar(&runlabelCommand.Display, "display", false, "Preview the command that the label would run")
+ flags.BoolVar(&runlabelCommand.Replace, "replace", false, "Replace existing container with a new one from the image")
flags.StringVar(&runlabelCommand.Name, "name", "", "Assign a name to the container")
flags.StringVar(&runlabelCommand.Opt1, "opt1", "", "Optional parameter to pass for install")
@@ -57,7 +60,7 @@ func init() {
flags.BoolP("pull", "p", false, "Pull the image if it does not exist locally prior to executing the label contents")
flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images")
flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
- flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
flags.MarkDeprecated("pull", "podman will pull if not found in local storage")
}
@@ -146,10 +149,33 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error {
return err
}
if !c.Quiet {
- fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
+ fmt.Printf("command: %s\n", strings.Join(cmd, " "))
if c.Display {
return nil
}
}
+
+ // If container already exists && --replace given -- Nuke it
+ if c.Replace {
+ for i, entry := range cmd {
+ if entry == "--name" {
+ name := cmd[i+1]
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ if errors.Cause(err) != libpod.ErrNoSuchCtr {
+ logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error())
+ return err
+ }
+ } else {
+ logrus.Debugf("Runlabel --replace option given. Container %s will be deleted. The new container will be named %s", ctr.ID(), name)
+ if err := runtime.RemoveContainer(ctx, ctr, true, false); err != nil {
+ return err
+ }
+ }
+ break
+ }
+ }
+ }
+
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
}
diff --git a/cmd/podman/save.go b/cmd/podman/save.go
index df016b069..c10679740 100644
--- a/cmd/podman/save.go
+++ b/cmd/podman/save.go
@@ -58,7 +58,7 @@ func init() {
flags := saveCommand.Flags()
flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&saveCommand.Format, "format", v2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
- flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
+ flags.StringVarP(&saveCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output")
}
@@ -79,14 +79,14 @@ func saveCmd(c *cliconfig.SaveValues) error {
return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
}
- output := c.Output
- if output == "/dev/stdout" {
+ if len(c.Output) == 0 {
fi := os.Stdout
if logrus.IsTerminal(fi) {
return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
}
+ c.Output = "/dev/stdout"
}
- if err := parse.ValidateFileName(output); err != nil {
+ if err := parse.ValidateFileName(c.Output); err != nil {
return err
}
return runtime.SaveImage(getContext(), c)
diff --git a/cmd/podman/search.go b/cmd/podman/search.go
index 25f5a98b7..a10b9d419 100644
--- a/cmd/podman/search.go
+++ b/cmd/podman/search.go
@@ -1,6 +1,7 @@
package main
import (
+ "reflect"
"strings"
"github.com/containers/buildah/pkg/formats"
@@ -46,7 +47,7 @@ func init() {
flags.StringVar(&searchCommand.Format, "format", "", "Change the output format to a Go template")
flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results")
flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output")
- flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
+ flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
}
func searchCmd(c *cliconfig.SearchValues) error {
@@ -79,7 +80,10 @@ func searchCmd(c *cliconfig.SearchValues) error {
return err
}
format := genSearchFormat(c.Format)
- out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: results[0].HeaderMap()}
+ if len(results) == 0 {
+ return nil
+ }
+ out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: genSearchOutputMap()}
formats.Writer(out).Out()
return nil
}
@@ -99,3 +103,16 @@ func searchToGeneric(params []image.SearchResult) (genericParams []interface{})
}
return genericParams
}
+
+func genSearchOutputMap() map[string]string {
+ io := image.SearchResult{}
+ v := reflect.Indirect(reflect.ValueOf(io))
+ values := make(map[string]string)
+
+ for i := 0; i < v.NumField(); i++ {
+ key := v.Type().Field(i).Name
+ value := key
+ values[key] = strings.ToUpper(splitCamelCase(value))
+ }
+ return values
+}
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index 41950928e..6826191c5 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -3,11 +3,11 @@ package shared
import (
"context"
"fmt"
- "github.com/google/shlex"
"io"
"os"
"path/filepath"
"regexp"
+ "sort"
"strconv"
"strings"
"sync"
@@ -21,6 +21,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
+ "github.com/google/shlex"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -583,18 +584,93 @@ func getCgroup(spec *specs.Spec) string {
return cgroup
}
+func comparePorts(i, j ocicni.PortMapping) bool {
+ if i.ContainerPort != j.ContainerPort {
+ return i.ContainerPort < j.ContainerPort
+ }
+
+ if i.HostIP != j.HostIP {
+ return i.HostIP < j.HostIP
+ }
+
+ if i.HostPort != j.HostPort {
+ return i.HostPort < j.HostPort
+ }
+
+ return i.Protocol < j.Protocol
+}
+
+// returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
+// e.g 0.0.0.0:1000-1006->1000-1006/tcp
+func formatGroup(key string, start, last int32) string {
+ parts := strings.Split(key, "/")
+ groupType := parts[0]
+ var ip string
+ if len(parts) > 1 {
+ ip = parts[0]
+ groupType = parts[1]
+ }
+ group := strconv.Itoa(int(start))
+ if start != last {
+ group = fmt.Sprintf("%s-%d", group, last)
+ }
+ if ip != "" {
+ group = fmt.Sprintf("%s:%s->%s", ip, group, group)
+ }
+ return fmt.Sprintf("%s/%s", group, groupType)
+}
+
// portsToString converts the ports used to a string of the from "port1, port2"
+// also groups continuous list of ports in readable format.
func portsToString(ports []ocicni.PortMapping) string {
+ type portGroup struct {
+ first int32
+ last int32
+ }
var portDisplay []string
if len(ports) == 0 {
return ""
}
+ //Sort the ports, so grouping continuous ports become easy.
+ sort.Slice(ports, func(i, j int) bool {
+ return comparePorts(ports[i], ports[j])
+ })
+
+ // portGroupMap is used for grouping continuous ports
+ portGroupMap := make(map[string]*portGroup)
+ var groupKeyList []string
+
for _, v := range ports {
+
hostIP := v.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
- portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
+ // if hostPort and containerPort are not same, consider as individual port.
+ if v.ContainerPort != v.HostPort {
+ portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
+ continue
+ }
+
+ portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
+
+ portgroup, ok := portGroupMap[portMapKey]
+ if !ok {
+ portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
+ // this list is required to travese portGroupMap
+ groupKeyList = append(groupKeyList, portMapKey)
+ continue
+ }
+
+ if portgroup.last == (v.ContainerPort - 1) {
+ portgroup.last = v.ContainerPort
+ continue
+ }
+ }
+ // for each portMapKey, format group list and appned to output string
+ for _, portKey := range groupKeyList {
+ group := portGroupMap[portKey]
+ portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
}
return strings.Join(portDisplay, ", ")
}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 32a80e9f9..55eb3ce83 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -499,7 +499,7 @@ func ParseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l
}
}
}
- if err := parse.ReadKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil {
+ if err := parse.ReadKVStrings(env, c.StringSlice("env-file"), c.StringArray("env")); err != nil {
return nil, errors.Wrapf(err, "unable to process environment variables")
}
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index e942c1ccd..cf406cf66 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -41,7 +41,7 @@ func init() {
flags.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)")
+ flags.BoolVar(&startCommand.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)")
markFlagHiddenForRemoteClient("latest", flags)
}
@@ -62,14 +62,10 @@ func startCmd(c *cliconfig.StartValues) error {
return errors.Errorf("you cannot start and attach multiple containers at once")
}
- sigProxy := c.SigProxy
+ sigProxy := c.SigProxy || attach
if sigProxy && !attach {
- if c.Flag("sig-proxy").Changed {
- return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
- } else {
- sigProxy = false
- }
+ return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
}
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
index ebda18cdb..c56e35aef 100644
--- a/cmd/podman/tree.go
+++ b/cmd/podman/tree.go
@@ -23,7 +23,7 @@ var (
treeDescription = "Prints layer hierarchy of an image in a tree format"
_treeCommand = &cobra.Command{
- Use: "tree",
+ Use: "tree [flags] IMAGE",
Short: treeDescription,
Long: treeDescription,
RunE: func(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go
index c57d5794c..a938c7c38 100644
--- a/cmd/podman/umount.go
+++ b/cmd/podman/umount.go
@@ -31,7 +31,7 @@ var (
return umountCmd(&umountCommand)
},
Args: func(cmd *cobra.Command, args []string) error {
- return checkAllAndLatest(cmd, args, true)
+ return checkAllAndLatest(cmd, args, false)
},
Example: `podman umount ctrID
podman umount ctrID1 ctrID2 ctrID3
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 791790e2e..517a7a2a1 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -19,6 +19,14 @@ type StringResponse (
message: string
)
+type LogLine (
+ device: string,
+ parseLogType : string,
+ time: string,
+ msg: string,
+ cid: string
+)
+
# ContainerChanges describes the return struct for ListContainerChanges
type ContainerChanges (
changed: []string,
@@ -522,6 +530,8 @@ method ListContainerProcesses(name: string, opts: []string) -> (container: []str
# capability of varlink if the client invokes it.
method GetContainerLogs(name: string) -> (container: []string)
+method GetContainersLogs(names: []string, follow: bool, latest: bool, since: string, tail: int, timestamps: bool) -> (log: LogLine)
+
# ListContainerChanges takes a name or ID of a container and returns changes between the container and
# its base image. It returns a struct of changed, deleted, and added path names.
method ListContainerChanges(name: string) -> (container: ContainerChanges)
diff --git a/cmd/podman/version.go b/cmd/podman/version.go
index 336be892e..31b0b8e82 100644
--- a/cmd/podman/version.go
+++ b/cmd/podman/version.go
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
+ "strings"
"text/tabwriter"
"time"
@@ -43,6 +44,9 @@ func versionCmd(c *cliconfig.VersionValues) error {
versionOutputFormat := c.Format
if versionOutputFormat != "" {
+ if strings.Join(strings.Fields(versionOutputFormat), "") == "{{json.}}" {
+ versionOutputFormat = formats.JSONString
+ }
var out formats.Writer
switch versionOutputFormat {
case formats.JSONString:
diff --git a/commands.md b/commands.md
index 6c5fad2f6..31a77c0c4 100644
--- a/commands.md
+++ b/commands.md
@@ -34,7 +34,7 @@
| [podman-info(1)](/docs/podman-info.1.md) | Display system information |[![...](/docs/play.png)](https://asciinema.org/a/yKbi5fQ89y5TJ8e1RfJd4ivTD)|
| [podman-inspect(1)](/docs/podman-inspect.1.md) | Display the configuration of a container or image |[![...](/docs/play.png)](https://asciinema.org/a/133418)|
| [podman-kill(1)](/docs/podman-kill.1.md) | Kill the main process in one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/3jNos0A5yzO4hChu7ddKkUPw7)|
-| [podman-load(1)](/docs/podman-load.1.md) | Load an image from docker archive or oci |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
+| [podman-load(1)](/docs/podman-load.1.md) | Load an image from a container image archive |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
| [podman-login(1)](/docs/podman-login.1.md) | Login to a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [podman-logout(1)](/docs/podman-logout.1.md) | Logout of a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [podman-logs(1)](/docs/podman-logs.1.md) | Display the logs of a container |[![...](/docs/play.png)](https://asciinema.org/a/MZPTWD5CVs3dMREkBxQBY9C5z)|
diff --git a/completions/bash/podman b/completions/bash/podman
index d8354fa80..1976bff44 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2469,6 +2469,7 @@ _podman_container_runlabel() {
-h
-q
--quiet
+ --replace
--tls-verify
"
diff --git a/completions/zsh/_podman b/completions/zsh/_podman
new file mode 100644
index 000000000..530649c0c
--- /dev/null
+++ b/completions/zsh/_podman
@@ -0,0 +1,385 @@
+#compdef podman
+
+# To get zsh to reread this file: unset -f _podman;rm -f ~/.zcompdump;compinit
+
+# On rereads, reset cache. (Not that the cacheing works, but some day it might)
+unset -m '_podman_*'
+
+###############################################################################
+# BEGIN 'podman help' parsers -- for options, subcommands, and usage
+
+# Run 'podman XX --help', set _podman_commands to a formatted list of cmds
+_read_podman_commands() {
+ local line
+
+ # Cache: the intention here is to run each 'podman help' only once.
+ # Unfortunately it doesn't seem to actually be working: even though
+ # I can see the var_ref in my shell, it's not visible here.
+ local _var_ref=_podman_commands_"${*// /_}"
+ typeset -ga _podman_commands
+ _podman_commands=(${(P)_var_ref})
+ (( $#_podman_commands )) && return
+
+ _call_program podman podman "$@" --help |\
+ sed -n -e '0,/^Available Commands/d' -e '/^[A-Z]/q;p' |\
+ sed -e 's/^ \+\([^ ]\+\) \+/\1:/' |\
+ egrep . | while read line; do
+ _podman_commands+=($line)
+ done
+
+ eval "typeset -ga $_var_ref"
+ eval "$_var_ref=(\$_podman_commands)"
+}
+
+# Run 'podman XX --help', set _podman_flag_list to a formatted list
+# of flag options for XX
+_read_podman_flags() {
+ local line
+
+ local _var_ref=_podman_flags_"${*// /_}"
+ eval "typeset -ga ${_var_ref}"
+ typeset -ga _podman_flag_list
+ _podman_flag_list=(${(P)_var_ref})
+ (( $#_podman_flag_list )) && return
+
+ # Extract the Flags; strip leading whitespace; pack '-f, --foo'
+ # as '-f,--foo' (no space); then add '=' to '--foo string'.
+ # The result will be, e.g. '-f,--foo=string Description of Option'
+ _call_program podman podman "$@" --help |\
+ sed -n -e '0,/^Flags:/d' -e '/^$/q;p' |\
+ sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\
+ sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\
+ while read flags desc;do
+ # flags like --foo=string: split into --foo & string
+ local -a tmpa
+ local optval=
+ tmpa=(${(s.=.)flags})
+ if [ -n "$tmpa[2]" ]; then
+ flags=$tmpa[1]
+ optval=$tmpa[2]
+ fi
+
+ # 'podman attach --detach-keys' includes ']' in help msg
+ desc=${desc//\]/\\]}
+
+ for flag in ${(s:,:)flags}; do
+ if [ -n "$optval" ]; then
+ _podman_flag_list+=("${flag}[$desc]:$(_podman_find_helper ${flags} ${optval} ${desc})")
+ else
+ _podman_flag_list+=("${flag}[$desc]")
+ fi
+ done
+ done
+
+ eval "typeset -ga $_var_ref=(\$_podman_flag_list)"
+}
+
+# Run 'podman XXX --help', set _podman_usage to the line after "Usage:"
+_read_podman_usage() {
+ local _var_ref=_podman_usage_"${*// /_}"
+ typeset -ga _podman_usage
+ _podman_usage=${(P)_var_ref}
+ (( $#_podman_usage )) && return
+
+ _podman_usage=$(_call_program podman podman "$@" --help |\
+ grep -A1 'Usage:'|\
+ tail -1 |\
+ sed -e 's/^ *//')
+
+ eval "typeset -ga $_var_ref"
+ eval "$_var_ref=\$_podman_usage"
+}
+
+# END 'podman help' parsers
+###############################################################################
+# BEGIN custom helpers for individual option arguments
+
+# Find a zsh helper for a given flag or command-line option
+_podman_find_helper() {
+ local flags=$1
+ local optval=$2
+ local desc=$3
+ local helper=
+
+ # Yes, this is a lot of hardcoding. IMHO it's still better than
+ # hardcoding every possible podman option.
+ # FIXME: there are many more options that could use helpers.
+ if expr "$desc" : ".*[Dd]irectory" >/dev/null; then
+ optval="directory"
+ helper="_files -/"
+ elif expr "$desc" : ".*[Pp]ath" >/dev/null; then
+ optval="path"
+ helper=_files
+ elif [ "$flags" = "--cgroup-manager" ]; then
+ optval="cgroup manager"
+ helper="(cgroupfs systemd)"
+ elif [ "$flags" = "--log-level" ]; then
+ optval="log level"
+ # 'Log messages above specified level: debug, ... (default "...")'
+ # Strip off the description and all 'default' strings
+ desc=${desc/Log*:/} # debug, info, ... (default "...")
+ desc=${(S)desc//\(*\)/} # debug, info, ... or panic
+ desc=${desc//,/} # debug info ... or panic
+ desc=${desc// or / } # debug info ... panic
+ desc=${desc// / } # collapse multiple spaces
+ # FIXME: how to present values _in order_, not sorted alphabetically?
+ helper="($desc)"
+ fi
+ echo "$optval:$helper"
+}
+
+# END custom helpers for individual option arguments
+###############################################################################
+# BEGIN helpers for command-line args (containers, images)
+
+__podman_helper_generic() {
+ local expl line
+ local -a results
+
+ local foo1=$1; shift
+ local name=$2; shift
+
+ _call_program $foo1 podman "$@" |\
+ while read line; do
+ results+=(${=line})
+ done
+
+ _wanted $foo1 expl $name compadd ${(u)results}
+}
+
+_podman_helper_image() {
+ __podman_helper_generic podman-images 'images' \
+ images --format '{{.ID}}\ {{.Repository}}:{{.Tag}}'
+}
+
+# FIXME: at some point, distinguish between running & stopped containers
+_podman_helper_container() {
+ __podman_helper_generic podman-containers 'containers' \
+ ps -a --format '{{.Names}}\ {{.ID}}'
+}
+
+_podman_helper_pod() {
+ __podman_helper_generic podman-pods 'pods' pod list --format '{{.Name}}'
+}
+
+_podman_helper_volume() {
+ __podman_helper_generic podman-volumes 'volumes' volume ls --format '{{.Name}}'
+}
+
+# Combinations. This one seen in diff & inspect
+_podman_helper_container-or-image() {
+ _podman_helper_image
+ _podman_helper_container
+}
+
+# Seen in generate-kube
+_podman_helper_container-or-pod() {
+ _podman_helper_container
+ _podman_helper_pod
+}
+
+# For top and pod-top
+_podman_helper_format-descriptors() {
+ __podman_helper_generic top-format-descriptors 'format descriptors' \
+ top --list-descriptors
+}
+
+# for push, login/logout, and trust
+# FIXME: some day, use this to define a helper for IMAGE-PATH (in 'pull')
+_podman_helper_registry() {
+ local expl
+ local -a registries
+
+ # Suggestions for improvement more than welcome.
+ python3 -c 'from configparser import ConfigParser;cp=ConfigParser();cp.read("/etc/containers/registries.conf");registries=eval(cp.get("registries.search","registries"));[print(r) for r in registries]' 2>/dev/null | while read line; do
+ registries+=($line)
+ done
+
+ if (( $#registries )); then
+ _wanted podman-registry expl "registry" compadd ${(u)registries}
+ else
+ _hosts
+ fi
+}
+
+# END helpers for command-line args
+###############################################################################
+# BEGIN figure out completion helpers for a given (sub)command
+
+# Read Usage string for this subcommand, set up helpers for its subargs
+_set_up_podman_args() {
+ _read_podman_usage "$@"
+
+ typeset -ga _podman_args=()
+ # E.g. 'podman exec [flags] CONTAINER [...' -> 'CONTAINER [....'
+ local usage_rhs=$(expr "$_podman_usage" : ".*\[flags\] \+\(.*\)")
+
+ # e.g. podman pod ps which takes no further args
+ if [ -z "$usage_rhs" ]; then
+ return
+ fi
+
+ # podman diff & inspect accept 'CONTAINER | IMAGE'; make into one keyword.
+ usage_rhs=${usage_rhs// | /-OR-}
+
+ # Arg parsing. There are three possibilities in Usage messages:
+ #
+ # [IMAGE] - optional image arg (zero or one)
+ # IMAGE - exactly one image arg
+ # IMAGE [IMAGE...] - one or more image args
+ # and, theoretically:
+ # [IMAGE...] - zero or more? Haven't seen it in practice. Defer.
+ #
+ # For completion purposes, we only need to provide two options:
+ # one, or more than one? That is: continue offering completion
+ # suggestions after the first one? For that, we make two passes;
+ # in the first, mark an option as either '' (only one) or
+
+ # Parse each command-line arg seen in usage message
+ local word
+ local -A _seen=()
+ for word in ${=usage_rhs}; do
+ local unbracketed=$(expr "$word" : "\[\(.*\)\]")
+
+ if [ -n "$unbracketed" ]; then
+ # Remove all dots; assume(!?) that they'll all be at the end
+ unbracketed=${unbracketed//./}
+
+ if (( $_seen[$unbracketed] )); then
+ # Is this the same word as the previous arg?
+ if expr "$_podman_args[-1]" : ":$unbracketed:" >/dev/null; then
+ # Yes. Make it '*:...' instead of ':...', indicating >1
+ _podman_args[-1]="*$_podman_args[-1]"
+ fi
+ continue
+ fi
+
+ word=$unbracketed
+ fi
+
+ # As of 2019-03 all such instances are '[COMMAND [ARG...]]' and are,
+ # of course, at the end of the line. We can't offer completion for
+ # these, because the container will have different commands than
+ # the host system... but try anyway.
+ if [ "$word" = '[COMMAND' ]; then
+ # e.g. podman create, exec, run
+ _podman_args+=(
+ ":command: _command_names -e"
+ "*::arguments: _normal"
+ )
+ return
+ fi
+
+ # Look for an existing helper, e.g. IMAGE -> _podman_helper_image
+ local helper="_podman_helper_${(L)word}"
+ if (( $+functions[$helper] )); then
+ :
+ else
+ # No defined helper. Reset, but check for known expressions.
+ helper=
+ case "$word" in
+ KUBEFILE) helper='_files -g "*.y(|a)ml(-.)"' ;;
+ PATH) helper='_files' ;;
+ esac
+ fi
+
+ # Another special case: 'top' actually takes multiple options
+ local multi=
+ if [ "$word" = "FORMAT-DESCRIPTORS" ]; then
+ multi='*'
+ fi
+ _podman_args+=("$multi:${(L)word}:$helper")
+ _seen[$word]=1
+ done
+}
+
+# For an endpoint command, i.e. not a subcommand.
+_podman_terminus() {
+ typeset -A opt_args
+ typeset -ga _podman_flag_list
+ typeset -ga _podman_args
+ integer ret=1
+
+ # Find out what args it takes (e.g. image(s), container(s)) and see
+ # if we have helpers for them.
+ _set_up_podman_args "$@"
+ _arguments -C $_podman_flag_list $_podman_args && ret=0
+
+ return ret
+}
+
+# END figure out completion helpers for a given (sub)command
+################################################################################
+# BEGIN actual entry point
+
+# This is the main entry point; it's also where we (recursively) come in
+# to handle nested subcommands such as 'podman container' or even 3-level
+# ones like 'podman generate kube'. Nesting is complicated, so here's
+# my best understanding as of 2019-03-12:
+#
+# Easy first: when you do "podman <TAB>" zsh calls us, we run 'podman --help',
+# figure out the global options and subcommands, and run the magic _arguments
+# command. That will offer those options/subcommands, and complete, etc.
+#
+# Where it gets harder is when you do "podman container mount <TAB>".
+# zsh first calls us with words=(podman container mount) but we don't
+# want all that full context yet! We want to go a piece at a time,
+# handling 'container' first, then 'mount'; ending up with our
+# final 'podman container mount --help' giving us suitable flags
+# and no subcommands; from which we determine that it's a terminus
+# and jump to a function that handles non-subcommand arguments.
+#
+# This is the closest I've yet come to understanding zsh completion;
+# it is still incomplete and may in fact be incorrect. But it works
+# better than anything I've played with so far.
+_podman_subcommand() {
+ local curcontext="$curcontext" state line
+ typeset -A opt_args
+ integer ret=1
+
+ # Run 'podman --help' / 'podman system --help' for our context (initially
+ # empty, then possibly under subcommands); from those, get a list of
+ # flags appropriate for this context and, if applicable, subcommands.
+ _read_podman_flags "$@"
+ _read_podman_commands "$@"
+
+ # Now, is this a sub-subcommand, or does it have args?
+ if (( $#_podman_commands )); then
+ # Subcommands required (podman, podman system, etc)
+ local cmd=${words// /_}
+ _arguments -C $_podman_flag_list \
+ "(-): :->command" \
+ "(-)*:: :->option-or-argument" \
+ && ret=0
+
+ case $state in
+ (command)
+ _describe -t command "podman $* command" _podman_commands && ret=0
+ ;;
+ (option-or-argument)
+ # I think this is when we have a _completed_ subcommand.
+ # Recurse back into here, offering options for that subcommand.
+ curcontext=${curcontext%:*:*}:podman-${words[1]}:
+ _podman_subcommand "$@" ${words[1]} && ret=0
+ ;;
+ esac
+ else
+ # At a terminus, i.e. podman info, podman history; find out
+ # what args it takes.
+ _podman_terminus "$@" && ret=0
+ fi
+
+ return ret
+}
+
+_podman() {
+ _podman_subcommand
+}
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 4
+# indent-tabs-mode: nil
+# sh-basic-offset: 4
+# End:
+# vim: ft=zsh sw=4 ts=4 et
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 9419dad05..e941610e2 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -54,8 +54,6 @@ show_env_vars() {
BUILDTAGS $BUILDTAGS
BUILT_IMAGE_SUFFIX $BUILT_IMAGE_SUFFIX
ROOTLESS_USER $ROOTLESS_USER
-ROOTLESS_UID $ROOTLESS_UID
-ROOTLESS_GID $ROOTLESS_GID
CI $CI
CIRRUS_CI $CIRRUS_CI
CI_NODE_INDEX $CI_NODE_INDEX
@@ -103,6 +101,15 @@ clean_env() {
unset -v UNSET_ENV_VARS $UNSET_ENV_VARS || true # don't fail on read-only
}
+die() {
+ req_env_var "
+ 1 $1
+ 2 $2
+ "
+ echo "$2"
+ exit $1
+}
+
# Return a GCE image-name compatible string representation of distribution name
os_release_id() {
eval "$(egrep -m 1 '^ID=' /etc/os-release | tr -d \' | tr -d \")"
@@ -121,7 +128,7 @@ bad_os_id_ver() {
}
run_rootless() {
- if [[ -z "$ROOTLESS_USER" ]] && [[ -z "$ROOTLESS_UID" ]] && [[ -z "$ROOTLESS_GID" ]]
+ if [[ -z "$ROOTLESS_USER" ]]
then
return 1
else
@@ -136,14 +143,14 @@ stub() {
ircmsg() {
req_env_var "
CIRRUS_TASK_ID $CIRRUS_TASK_ID
- 1 $1
+ @ $@
"
# Sometimes setup_environment.sh didn't run
SCRIPT="$(dirname $0)/podbot.py"
NICK="podbot_$CIRRUS_TASK_ID"
NICK="${NICK:0:15}" # Any longer will break things
set +e
- $SCRIPT $NICK $1
+ $SCRIPT $NICK $@
echo "Ignoring exit($?)"
set -e
}
@@ -161,30 +168,46 @@ record_timestamp() {
setup_rootless() {
req_env_var "
ROOTLESS_USER $ROOTLESS_USER
- ROOTLESS_UID $ROOTLESS_UID
- ROOTLESS_GID $ROOTLESS_GID
GOSRC $GOSRC
ENVLIB $ENVLIB
"
+
+ if passwd --status $ROOTLESS_USER
+ then
+ echo "Updating $ROOTLESS_USER user permissions on possibly changed libpod code"
+ chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOSRC"
+ return 0
+ fi
+
+ # Guarantee independence from specific values
+ ROOTLESS_UID=$[RANDOM+1000]
+ ROOTLESS_GID=$[RANDOM+1000]
echo "creating $ROOTLESS_UID:$ROOTLESS_GID $ROOTLESS_USER user"
groupadd -g $ROOTLESS_GID $ROOTLESS_USER
useradd -g $ROOTLESS_GID -u $ROOTLESS_UID --no-user-group --create-home $ROOTLESS_USER
- chown -R $ROOTLESS_UID:$ROOTLESS_GID "$GOSRC"
+ chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOSRC"
echo "creating ssh keypair for $USER"
ssh-keygen -P "" -f $HOME/.ssh/id_rsa
echo "Allowing ssh key for $ROOTLESS_USER"
(umask 077 && mkdir "/home/$ROOTLESS_USER/.ssh")
- chown -R $ROOTLESS_UID:$ROOTLESS_GID "/home/$ROOTLESS_USER/.ssh"
- install -o $ROOTLESS_UID -g $ROOTLESS_GID -m 0600 \
+ chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh"
+ install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0600 \
"$HOME/.ssh/id_rsa.pub" "/home/$ROOTLESS_USER/.ssh/authorized_keys"
+ # Makes debugging easier
+ cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys"
+
+ echo "Configuring subuid and subgid"
+ grep -q "${ROOTLESS_USER}" /etc/subuid || \
+ echo "${ROOTLESS_USER}:$[ROOTLESS_UID * 100]:65536" | \
+ tee -a /etc/subuid >> /etc/subgid
echo "Setting permissions on automation files"
chmod 666 "$TIMESTAMPS_FILEPATH"
echo "Copying $HOME/$ENVLIB"
- install -o $ROOTLESS_UID -g $ROOTLESS_GID -m 0700 \
+ install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0700 \
"$HOME/$ENVLIB" "/home/$ROOTLESS_USER/$ENVLIB"
echo "Configuring user's go environment variables"
diff --git a/contrib/cirrus/notice_master_failure.sh b/contrib/cirrus/notice_master_failure.sh
new file mode 100755
index 000000000..4b09331d3
--- /dev/null
+++ b/contrib/cirrus/notice_master_failure.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+source $(dirname $0)/lib.sh
+
+# mIRC "escape" codes are the most standard, for a non-standard client-side interpretation.
+ETX="$(echo -n -e '\x03')"
+RED="${ETX}4"
+NOR="$(echo -n -e '\x0f')"
+
+if [[ "$CIRRUS_BRANCH" =~ "master" ]]
+then
+ BURL="https://cirrus-ci.com/build/$CIRRUS_BUILD_ID"
+ ircmsg "${RED}[Action Recommended]: ${NOR}Post-merge testing ${RED}$CIRRUS_BRANCH failed${NOR} in $CIRRUS_TASK_NAME on $(os_release_id)-$(os_release_ver): $BURL. Please investigate, and re-run if appropriate."
+fi
+
+# This script assumed to be executed on failure
+die 1 "Testing Failed"
diff --git a/contrib/cirrus/rootless_test.sh b/contrib/cirrus/rootless_test.sh
index 811b7cf2e..d0e2ceb95 100755
--- a/contrib/cirrus/rootless_test.sh
+++ b/contrib/cirrus/rootless_test.sh
@@ -29,6 +29,9 @@ case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
fedora-29) ;&
fedora-28)
make
+ make varlink_generate
+ make test-binaries
+ make ginkgo
;;
*) bad_os_id_ver ;;
esac
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index d8d97904b..96d0e1b55 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -44,8 +44,6 @@ then
"export OS_RELEASE_VER=\"$(os_release_ver)\"" \
"export OS_REL_VER=\"$(os_release_id)-$(os_release_ver)\"" \
"export ROOTLESS_USER=$ROOTLESS_USER" \
- "export ROOTLESS_UID=$ROOTLESS_UID" \
- "export ROOTLESS_GID=$ROOTLESS_GID" \
"export BUILT_IMAGE_SUFFIX=\"-$CIRRUS_REPO_NAME-${CIRRUS_CHANGE_IN_REPO:0:8}\"" \
"export GOPATH=\"/var/tmp/go\"" \
'export PATH="$HOME/bin:$GOPATH/bin:/usr/local/bin:$PATH"' \
@@ -74,18 +72,19 @@ then
esac
cd "${GOSRC}/"
+ # Reload to incorporate any changes from above
source "$SCRIPT_BASE/lib.sh"
if run_rootless
then
setup_rootless
- else
- # Includes some $HOME relative details
- go env | while read envline
- do
- X=$(echo "export $envline" | tee -a "$HOME/$ENVLIB") && eval "$X" && echo "$X"
- done
+ make install.catatonit
+ go get github.com/onsi/ginkgo/ginkgo
+ go get github.com/onsi/gomega/...
+ dnf -y update runc
fi
fi
+show_env_vars
+
record_timestamp "env. setup end"
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 703b942b6..3324ee8f9 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -472,6 +472,7 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath}
%{_mandir}/man1/*.1*
%{_mandir}/man5/*.5*
%{_datadir}/bash-completion/completions/*
+%{_datadir}/zsh/site-functions/*
%{_libexecdir}/%{name}/conmon
%config(noreplace) %{_sysconfdir}/cni/net.d/87-%{name}-bridge.conflist
%{_datadir}/containers/%{repo}.conf
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index 845103ca1..ccc8bd900 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -1,7 +1,7 @@
% podman-build(1)
## NAME
-podman\-build - Build a container image using a Dockerfile.
+podman\-build - Build a container image using a Dockerfile
## SYNOPSIS
**podman build** [*options*] *context*
diff --git a/docs/podman-container-exists.1.md b/docs/podman-container-exists.1.md
index 76701e2c2..8feb736f8 100644
--- a/docs/podman-container-exists.1.md
+++ b/docs/podman-container-exists.1.md
@@ -2,12 +2,10 @@
% Brent Baude
% November 2018
# NAME
-podman-container-exists- Check if a container exists in local storage
+podman-container-exists - Check if a container exists in local storage
# SYNOPSIS
-**podman container exists**
-[**-h**|**--help**]
-CONTAINER
+**podman container exists** [*-h*|*--help*] *container*
# DESCRIPTION
**podman container exists** checks if a container exists in local storage. The **ID** or **Name**
diff --git a/docs/podman-container-prune.1.md b/docs/podman-container-prune.1.md
index 1f3ef1d41..194dd3dae 100644
--- a/docs/podman-container-prune.1.md
+++ b/docs/podman-container-prune.1.md
@@ -5,8 +5,7 @@
podman-container-prune - Remove all stopped containers
# SYNOPSIS
-**podman container prune**
-[**-h**|**--help**]
+**podman container prune** [*-h*|*--help*]
# DESCRIPTION
**podman container prune** removes all stopped containers from local storage.
diff --git a/docs/podman-container-runlabel.1.md b/docs/podman-container-runlabel.1.md
index 7547f7187..7fa9805e6 100644
--- a/docs/podman-container-runlabel.1.md
+++ b/docs/podman-container-runlabel.1.md
@@ -12,6 +12,7 @@ podman-container-runlabel - Execute Image Label Method
[**--rootfs**=*ROOTFS*]
[**--set**=*NAME*=*VALUE*]
[**--storage**]
+[**--replace**]
LABEL IMAGE [ARG...]
# DESCRIPTION
@@ -85,6 +86,11 @@ Print usage statement
Suppress output information when pulling images
+**--replace**
+
+If a container exists of the default or given name, as needed it will be stopped, deleted and a new container will be
+created from this image.
+
**--signature-policy="PATHNAME"**
Pathname of a signature policy file to use. It is not recommended that this
diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md
index 7774542e8..44612003d 100644
--- a/docs/podman-cp.1.md
+++ b/docs/podman-cp.1.md
@@ -4,12 +4,12 @@
podman\-cp - Copy files/folders between a container and the local filesystem
## SYNOPSIS
-**podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH**
+**podman cp** [*container*:]*src_path* [*container*:]*dest_path*
## DESCRIPTION
-Copies the contents of **SRC_PATH** to the **DEST_PATH**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container.
+Copies the contents of **src_path** to the **dest_path**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container.
-The CONTAINER can be a running or stopped container. The **SRC_PATH** or **DEST_PATH** can be a file or directory.
+The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory.
The **podman cp** command assumes container paths are relative to the container's / (root) directory.
@@ -20,36 +20,36 @@ The command sees **compassionate_darwin:/tmp/foo/myfile.txt** and **compassionat
Local machine paths can be an absolute or relative value.
The command interprets a local machine's relative paths as relative to the current working directory where **podman cp** is run.
-Assuming a path separator of /, a first argument of **SRC_PATH** and second argument of **DEST_PATH**, the behavior is as follows:
+Assuming a path separator of /, a first argument of **src_path** and second argument of **dest_path**, the behavior is as follows:
-**SRC_PATH** specifies a file
- - **DEST_PATH** does not exist
- - the file is saved to a file created at **DEST_PATH**
- - **DEST_PATH** does not exist and ends with /
- - **DEST_PATH** is created as a directory and the file is copied into this directory using the basename from **SRC_PATH**
- - **DEST_PATH** exists and is a file
+**src_path** specifies a file
+ - **dest_path** does not exist
+ - the file is saved to a file created at **dest_path**
+ - **dest_path** does not exist and ends with /
+ - **dest_path** is created as a directory and the file is copied into this directory using the basename from **src_path**
+ - **dest_path** exists and is a file
- the destination is overwritten with the source file's contents
- - **DEST_PATH** exists and is a directory
- - the file is copied into this directory using the basename from **SRC_PATH**
+ - **dest_path** exists and is a directory
+ - the file is copied into this directory using the basename from **src_path**
-**SRC_PATH** specifies a directory
- - **DEST_PATH** does not exist
- - **DEST_PATH** is created as a directory and the contents of the source directory are copied into this directory
- - **DEST_PATH** exists and is a file
+**src_path** specifies a directory
+ - **dest_path** does not exist
+ - **dest_path** is created as a directory and the contents of the source directory are copied into this directory
+ - **dest_path** exists and is a file
- Error condition: cannot copy a directory to a file
- - **DEST_PATH** exists and is a directory
- - **SRC_PATH** ends with /
+ - **dest_path** exists and is a directory
+ - **src_path** ends with /
- the source directory is copied into this directory
- - **SRC_PATH** ends with /. (that is: slash followed by dot)
+ - **src_path** ends with /. (that is: slash followed by dot)
- the content of the source directory is copied into this directory
-The command requires **SRC_PATH** and **DEST_PATH** to exist according to the above rules.
+The command requires **src_path** and **dest_path** to exist according to the above rules.
-If **SRC_PATH** is local and is a symbolic link, the symbolic target, is copied by default.
+If **src_path** is local and is a symbolic link, the symbolic target, is copied by default.
A colon (:) is used as a delimiter between CONTAINER and its path.
-You can also use : when specifying paths to a **SRC_PATH** or **DEST_PATH** on a local machine, for example, `file:name.txt`.
+You can also use : when specifying paths to a **src_path** or **dest_path** on a local machine, for example, `file:name.txt`.
If you use a : in a local machine path, you must be explicit with a relative or absolute path, for example:
`/path/to/file:name.txt` or `./file:name.txt`
diff --git a/docs/podman-events.1.md b/docs/podman-events.1.md
index b4ebe7649..3463d2aaa 100644
--- a/docs/podman-events.1.md
+++ b/docs/podman-events.1.md
@@ -1,7 +1,7 @@
% podman-events(1)
## NAME
-podman\-events- Monitor Podman events
+podman\-events - Monitor Podman events
## SYNOPSIS
**podman events** [*options*]
diff --git a/docs/podman-export.1.md b/docs/podman-export.1.md
index a8810f534..d0e365056 100644
--- a/docs/podman-export.1.md
+++ b/docs/podman-export.1.md
@@ -1,7 +1,7 @@
% podman-export(1)
## NAME
-podman\-export - Export container's filesystem contents as a tar archive
+podman\-export - Export a container's filesystem contents as a tar archive
## SYNOPSIS
**podman export** [*options*] *container*
diff --git a/docs/podman-generate-kube.1.md b/docs/podman-generate-kube.1.md
index d4bed8ab1..b6fa78606 100644
--- a/docs/podman-generate-kube.1.md
+++ b/docs/podman-generate-kube.1.md
@@ -5,10 +5,7 @@
podman-generate-kube - Generate Kubernetes YAML
# SYNOPSIS
-**podman generate kube **
-[**-h**|**--help**]
-[**-s**][**--service**]
-CONTAINER|POD
+**podman generate kube** [*-s*][*--service*] *container* | *pod*
# DESCRIPTION
**podman generate kube** will generate Kubernetes Pod YAML (v1 specification) from a podman container or pod. Whether
diff --git a/docs/podman-generate.1.md b/docs/podman-generate.1.md
index 66afacd0b..d1736f38e 100644
--- a/docs/podman-generate.1.md
+++ b/docs/podman-generate.1.md
@@ -1,7 +1,7 @@
% podman-generate(1)
## NAME
-podman\-container - generate structured data based for a containers and pods
+podman\-generate - Generate structured data based for a containers and pods
## SYNOPSIS
**podman generate** *subcommand*
diff --git a/docs/podman-healthcheck-run.1.md b/docs/podman-healthcheck-run.1.md
index 8cecb8eaa..e19c6250c 100644
--- a/docs/podman-healthcheck-run.1.md
+++ b/docs/podman-healthcheck-run.1.md
@@ -1,7 +1,7 @@
% podman-healthcheck-run(1)
## NAME
-podman\-healthcheck\-run- Run a container healthcheck
+podman\-healthcheck\-run - Run a container healthcheck
## SYNOPSIS
**podman healthcheck run** [*options*] *container*
diff --git a/docs/podman-healthcheck.1.md b/docs/podman-healthcheck.1.md
index 68d403231..91c3e4345 100644
--- a/docs/podman-healthcheck.1.md
+++ b/docs/podman-healthcheck.1.md
@@ -1,7 +1,7 @@
% podman-healthcheck(1)
## NAME
-podman\-healthcheck- Manage healthchecks for containers
+podman\-healthcheck - Manage healthchecks for containers
## SYNOPSIS
**podman healthcheck** *subcommand*
diff --git a/docs/podman-history.1.md b/docs/podman-history.1.md
index bca8cb1d2..8335428a8 100644
--- a/docs/podman-history.1.md
+++ b/docs/podman-history.1.md
@@ -1,7 +1,7 @@
% podman-history(1)
## NAME
-podman\-history - Shows the history of an image
+podman\-history - Show the history of an image
## SYNOPSIS
**podman history** [*options*] *image*[:*tag*|@*digest*]
diff --git a/docs/podman-image-exists.1.md b/docs/podman-image-exists.1.md
index e04c23721..1dc264a6b 100644
--- a/docs/podman-image-exists.1.md
+++ b/docs/podman-image-exists.1.md
@@ -2,12 +2,10 @@
% Brent Baude
% November 2018
# NAME
-podman-image-exists- Check if an image exists in local storage
+podman-image-exists - Check if an image exists in local storage
# SYNOPSIS
-**podman image exists**
-[**-h**|**--help**]
-IMAGE
+**podman image exists** [*-h*|*--help*] *image*
# DESCRIPTION
**podman image exists** checks if an image exists in local storage. The **ID** or **Name**
diff --git a/docs/podman-image-prune.1.md b/docs/podman-image-prune.1.md
index df912c380..7d5fd2fc8 100644
--- a/docs/podman-image-prune.1.md
+++ b/docs/podman-image-prune.1.md
@@ -5,9 +5,7 @@
podman-image-prune - Remove all unused images
# SYNOPSIS
-**podman image prune**
-[**-a**|**--all**]
-[**-h**|**--help**]
+**podman image prune** [*-a*|*--all*] [*-h*|*--help*]
# DESCRIPTION
**podman image prune** removes all dangling images from local storage. With the `all` option,
diff --git a/docs/podman-image-sign.1.md b/docs/podman-image-sign.1.md
index 232bc87fe..804ee03db 100644
--- a/docs/podman-image-sign.1.md
+++ b/docs/podman-image-sign.1.md
@@ -1,7 +1,7 @@
% podman-image-sign(1)
# NAME
-podman-image-sign- Create a signature for an image
+podman-image-sign - Create a signature for an image
# SYNOPSIS
**podman image sign**
diff --git a/docs/podman-image-tree.1.md b/docs/podman-image-tree.1.md
index 014499d6a..acd5ffcbf 100644
--- a/docs/podman-image-tree.1.md
+++ b/docs/podman-image-tree.1.md
@@ -4,7 +4,7 @@
podman\-image\-tree - Prints layer hierarchy of an image in a tree format
## SYNOPSIS
-**podman image tree** [*image*:*tag*]**|**[*image-id*]
+**podman image tree** [*image*:*tag*]|[*image-id*]
[**--help**|**-h**]
## DESCRIPTION
diff --git a/docs/podman-image-trust.1.md b/docs/podman-image-trust.1.md
index 819035040..f96a7996f 100644
--- a/docs/podman-image-trust.1.md
+++ b/docs/podman-image-trust.1.md
@@ -1,11 +1,11 @@
% podman-image-trust "1"
# NAME
-podman\-trust - Manage container registry image trust policy
+podman\-image\-trust - Manage container registry image trust policy
# SYNOPSIS
-**podman image trust set|show**
+**podman image trust** set|show
[**-h**|**--help**]
[**-j**|**--json**]
[**--raw**]
diff --git a/docs/podman-image.1.md b/docs/podman-image.1.md
index 54960045f..01cf08d62 100644
--- a/docs/podman-image.1.md
+++ b/docs/podman-image.1.md
@@ -14,21 +14,21 @@ The image command allows you to manage images
| Command | Man Page | Description |
| -------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. |
-| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if a image exists in local storage. |
+| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if an image exists in local storage. |
| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. |
| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. |
| list | [podman-images(1)](podman-images.1.md) | List the container images on the system.(alias ls) |
| load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. |
-| prune | [podman-image-prune(1)](podman-image-prune.1.md)| Removed all unused images from the local store. |
+| prune | [podman-image-prune(1)](podman-image-prune.1.md)| Remove all unused images from the local store. |
| pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. |
| push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. |
| rm | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. |
| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. |
| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Sign an image. |
| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
+| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. |
| trust | [podman-image-trust(1)](podman-image-trust.1.md)| Manage container image trust policy. |
-| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format |
## SEE ALSO
podman
diff --git a/docs/podman-load.1.md b/docs/podman-load.1.md
index 8b6501a5c..8a1660c63 100644
--- a/docs/podman-load.1.md
+++ b/docs/podman-load.1.md
@@ -1,22 +1,24 @@
% podman-load(1)
## NAME
-podman\-load - Load an image from docker archive
+podman\-load - Load an image from a container image archive into container storage
## SYNOPSIS
-**podman load** *name*[:*tag*|@*digest*]
+**podman load** [*name*[:*tag*]]
## DESCRIPTION
-**podman load** copies an image from either **docker-archive** or **oci-archive** stored
-on the local machine. **podman load** reads from stdin by default or a file if the **input** flag is set.
-The **quiet** flag suppresses the output when set.
+**podman load** loads an image from either an **oci-archive** or **docker-archive** stored on the local machine into container storage. **podman load** reads from stdin by default or a file if the **input** option is set.
+You can also specify a name for the image if the archive does not contain a named reference, of if you want an additonal name for the local image.
+
+The **quiet** option suppresses the progress output when set.
Note: `:` is a restricted character and cannot be part of the file name.
+
**podman [GLOBAL OPTIONS]**
**podman load [GLOBAL OPTIONS]**
-**podman load [OPTIONS] NAME[:TAG|@DIGEST]**
+**podman load [OPTIONS] NAME[:TAG]**
## OPTIONS
@@ -28,7 +30,7 @@ The remote client requires the use of this option.
**--quiet, -q**
-Suppress the output
+Suppress the progress output
**--signature-policy="PATHNAME"**
@@ -75,7 +77,7 @@ Loaded image: registry.fedoraproject.org/fedora:latest
```
## SEE ALSO
-podman(1), podman-save(1), crio(8)
+podman(1), podman-save(1), podman-tag(1), crio(8)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/podman-logs.1.md b/docs/podman-logs.1.md
index bc02df954..ce5d890ce 100644
--- a/docs/podman-logs.1.md
+++ b/docs/podman-logs.1.md
@@ -1,13 +1,15 @@
-% podman-logs(1)
+% podman-container-logs(1)
## NAME
-podman\-logs - Fetch the logs of a container
+podman\-container\-logs (podman\-logs) - Fetch the logs of one or more containers
## SYNOPSIS
-**podman** **logs** [*options*] *container*
+**podman** **container** **logs** [*options*] *container* [*container...*]
+
+**podman** **logs** [*options*] *container* [*container...*]
## DESCRIPTION
-The podman logs command batch-retrieves whatever logs are present for a container at the time of execution.
+The podman logs command batch-retrieves whatever logs are present for one or more containers at the time of execution.
This does not guarantee execution order when combined with podman run (i.e. your run may not have generated
any logs at the time you execute podman logs
@@ -15,7 +17,11 @@ any logs at the time you execute podman logs
**--follow, -f**
-Follow log output. Default is false
+Follow log output. Default is false.
+
+Note: If you are following a container which is removed `podman container rm`
+or removed on exit `podman run --rm ...`, then there is a chance the the log
+file will be removed before `podman logs` reads the final content.
**--latest, -l**
@@ -86,7 +92,7 @@ podman logs --since 10m myserver
```
## SEE ALSO
-podman(1)
+podman(1), podman-run(1), podman-container-rm(1)
## HISTORY
February 2018, Updated by Brent Baude <bbaude@redhat.com>
diff --git a/docs/podman-play-kube.1.md b/docs/podman-play-kube.1.md
index 2264f7a88..a9af961cd 100644
--- a/docs/podman-play-kube.1.md
+++ b/docs/podman-play-kube.1.md
@@ -5,7 +5,7 @@
podman-play-kube - Create pods and containers based on Kubernetes YAML
# SYNOPSIS
-**podman play kube **
+**podman play kube**
[**-h**|**--help**]
[**--authfile**]
[**--cert-dir**]
diff --git a/docs/podman-play.1.md b/docs/podman-play.1.md
index 6d2a7beba..f0bf8ea41 100644
--- a/docs/podman-play.1.md
+++ b/docs/podman-play.1.md
@@ -1,7 +1,7 @@
% podman-play(1)
## NAME
-podman\-container - play pods and containers based on a structured input file
+podman\-play - Play pods and containers based on a structured input file
## SYNOPSIS
**podman play** *subcommand*
diff --git a/docs/podman-pod-exists.1.md b/docs/podman-pod-exists.1.md
index 8fb2fc90e..da3947511 100644
--- a/docs/podman-pod-exists.1.md
+++ b/docs/podman-pod-exists.1.md
@@ -2,12 +2,10 @@
% Brent Baude
% December 2018
# NAME
-podman-pod-exists- Check if a pod exists in local storage
+podman-pod-exists - Check if a pod exists in local storage
# SYNOPSIS
-**podman pod exists**
-[**-h**|**--help**]
-POD
+**podman pod exists** [*-h*|*--help*] *pod*
# DESCRIPTION
**podman pod exists** checks if a pod exists in local storage. The **ID** or **Name**
diff --git a/docs/podman-pod-top.1.md b/docs/podman-pod-top.1.md
index a77ca2b37..b235a70ad 100644
--- a/docs/podman-pod-top.1.md
+++ b/docs/podman-pod-top.1.md
@@ -4,7 +4,7 @@
podman\-pod\-top - Display the running processes of containers in a pod
## SYNOPSIS
-**podman top** [*options*] *pod* [*format-descriptors*]
+**podman pod top** [*options*] *pod* [*format-descriptors*]
## DESCRIPTION
Display the running process of containers in a pod. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process.
diff --git a/docs/podman-pod.1.md b/docs/podman-pod.1.md
index 8f8403a40..1846d0411 100644
--- a/docs/podman-pod.1.md
+++ b/docs/podman-pod.1.md
@@ -19,7 +19,7 @@ podman pod is a set of subcommands that manage pods, or groups of containers.
| kill | [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. |
| pause | [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. |
| ps | [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. |
-| restart | [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or mode pods. |
+| restart | [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or more pods. |
| rm | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more pods. |
| start | [podman-pod-start(1)](podman-pod-start.1.md) | Start one or more pods. |
| stats | [podman-pod-stats(1)](podman-pod-stats.1.md) | Display live stream resource usage stats for containers in one or more pods. |
diff --git a/docs/podman-ps.1.md b/docs/podman-ps.1.md
index b8b1c3d62..811fbbc2f 100644
--- a/docs/podman-ps.1.md
+++ b/docs/podman-ps.1.md
@@ -148,6 +148,15 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
69ed779d8ef9f redis:alpine "redis-server" 25 hours ago Created 6379/tcp k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1
02f65160e14ca redis:alpine "redis-server" 19 hours ago Exited (-1) 19 hours ago 6379/tcp k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0
```
+
+```
+$ podman ps
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+4089df24d4f3 docker.io/library/centos:latest /bin/bash 2 minutes ago Up 2 minutes ago 0.0.0.0:80->8080/tcp, 0.0.0.0:2000-2006->2000-2006/tcp manyports
+92f58933c28c docker.io/library/centos:latest /bin/bash 3 minutes ago Up 3 minutes ago 192.168.99.100:1000-1006->1000-1006/tcp zen_sanderson
+
+```
+
## ps
Print a list of containers
diff --git a/docs/podman-push.1.md b/docs/podman-push.1.md
index 3ce156010..bb17c7e03 100644
--- a/docs/podman-push.1.md
+++ b/docs/podman-push.1.md
@@ -4,7 +4,7 @@
podman\-push - Push an image from local storage to elsewhere
## SYNOPSIS
-**podman push** [*options*] **image** [**destination**]
+**podman push** [*options*] *image* [*destination*]
## DESCRIPTION
Pushes an image from local storage to a specified destination.
diff --git a/docs/podman-restart.1.md b/docs/podman-restart.1.md
index dd28e34c7..a20eee243 100644
--- a/docs/podman-restart.1.md
+++ b/docs/podman-restart.1.md
@@ -1,10 +1,10 @@
% podman-restart(1)
## NAME
-podman\-restart - Restart a container
+podman\-restart - Restart one or more containers
## SYNOPSIS
-**podman attach** [*options*] *container* ...
+**podman restart** [*options*] *container* ...
## DESCRIPTION
The restart command allows containers to be restarted using their ID or name.
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index fe98e43ca..b8b3d51f0 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -889,7 +889,7 @@ During container image development, containers often need to write to the image
content. Installing packages into /usr, for example. In production,
applications seldom need to write to the image. Container applications write
to volumes if they need to write to file systems at all. Applications can be
-made more secure by running them in read-only mode using the - -read-only switch.
+made more secure by running them in read-only mode using the --read-only switch.
This protects the containers image from modification. Read only containers may
still need to write temporary data. The best way to handle this is to mount
tmpfs directories on /run and /tmp.
@@ -1129,6 +1129,15 @@ KillMode=process
WantedBy=multi-user.target
```
+### Configuring Storage Options from the command line
+
+Podman allows for the configuration of storage by changing the values
+in the /etc/container/storage.conf or by using global options. This
+shows how to setup and use fuse-overlayfs for a one time run of busybox
+using global options.
+
+podman --log-level=debug --storage-driver overlay --storage-opt "overlay.mount_program=/usr/bin/fuse-overlayfs" run busybox /bin/sh
+
### Rootless Containers
Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
diff --git a/docs/podman-save.1.md b/docs/podman-save.1.md
index a0b04d633..75aeda797 100644
--- a/docs/podman-save.1.md
+++ b/docs/podman-save.1.md
@@ -1,14 +1,13 @@
% podman-save(1)
## NAME
-podman\-save - Save an image to docker-archive or oci-archive
+podman\-save - Save an image to a container archive
## SYNOPSIS
**podman save** [*options*] *name*[:*tag*]
## DESCRIPTION
-**podman save** saves an image to either **docker-archive**, **oci-archive**, **oci-dir** (directory
-with oci manifest type), or **docker-dir** (directory with v2s2 manifest type) on the local machine,
+**podman save** saves an image to either **docker-archive**, **oci-archive**, **oci-dir** (directory with oci manifest type), or **docker-dir** (directory with v2s2 manifest type) on the local machine,
default is **docker-archive**. **podman save** writes to STDOUT by default and can be redirected to a
file using the **output** flag. The **quiet** flag suppresses the output when set.
Note: `:` is a restricted character and cannot be part of the file name.
diff --git a/docs/podman-volume-inspect.1.md b/docs/podman-volume-inspect.1.md
index 6d5b184ee..c22c80bb7 100644
--- a/docs/podman-volume-inspect.1.md
+++ b/docs/podman-volume-inspect.1.md
@@ -4,7 +4,7 @@
podman\-volume\-inspect - Inspect one or more volumes
## SYNOPSIS
-**podman volume inspect** [*options*]
+**podman volume inspect** [*options*] *volume*...
## DESCRIPTION
diff --git a/docs/podman-volume-prune.1.md b/docs/podman-volume-prune.1.md
index a06bb2fa4..437cad4e5 100644
--- a/docs/podman-volume-prune.1.md
+++ b/docs/podman-volume-prune.1.md
@@ -4,7 +4,7 @@
podman\-volume\-prune - Remove all unused volumes
## SYNOPSIS
-**podman volume rm** [*options*]
+**podman volume prune** [*options*]
## DESCRIPTION
diff --git a/docs/podman-wait.1.md b/docs/podman-wait.1.md
index 672316eef..2d145527b 100644
--- a/docs/podman-wait.1.md
+++ b/docs/podman-wait.1.md
@@ -1,7 +1,7 @@
% podman-wait "1"
## NAME
-podman\-wait - Waits on one or more containers to stop and prints exit code
+podman\-wait - Wait on one or more containers to stop and print their exit codes
## SYNOPSIS
**podman wait** [*options*] *container*
diff --git a/docs/podman.1.md b/docs/podman.1.md
index c4dc168c7..b808a7fa5 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -131,9 +131,9 @@ the exit codes follow the `chroot` standard, see below:
| Command | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| [podman-attach(1)](podman-attach.1.md) | Attach to a running container. |
-| [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. |
+| [podman-build(1)](podman-build.1.md) | Build a container image using a Dockerfile. |
| [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. |
-| [podman-container(1)](podman-container.1.md) | Manage Containers. |
+| [podman-container(1)](podman-container.1.md) | Manage containers. |
| [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. |
| [podman-create(1)](podman-create.1.md) | Create a new container. |
| [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. |
@@ -143,13 +143,13 @@ the exit codes follow the `chroot` standard, see below:
| [podman-generate(1)](podman-generate.1.md)| Generate structured data based for a containers and pods. |
| [podman-healthcheck(1)](podman-healthcheck.1.md)| Manage healthchecks for containers |
| [podman-history(1)](podman-history.1.md) | Show the history of an image. |
-| [podman-image(1)](podman-image.1.md) | Manage Images. |
+| [podman-image(1)](podman-image.1.md) | Manage images. |
| [podman-images(1)](podman-images.1.md) | List images in local storage. |
| [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
| [podman-info(1)](podman-info.1.md) | Displays Podman related system information. |
| [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
| [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
-| [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. |
+| [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. |
| [podman-login(1)](podman-login.1.md) | Login to a container registry. |
| [podman-logout(1)](podman-logout.1.md) | Logout of a container registry. |
| [podman-logs(1)](podman-logs.1.md) | Display the logs of a container. |
@@ -157,17 +157,17 @@ the exit codes follow the `chroot` standard, see below:
| [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. |
| [podman-play(1)](podman-play.1.md) | Play pods and containers based on a structured input file. |
| [podman-pod(1)](podman-pod.1.md) | Management tool for groups of containers, called pods. |
-| [podman-port(1)](podman-port.1.md) | List port mappings for the container. |
+| [podman-port(1)](podman-port.1.md) | List port mappings for a container. |
| [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. |
| [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. |
| [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. |
| [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. |
| [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. |
| [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. |
-| [podman-run(1)](podman-run.1.md) | Run a command in a container. |
-| [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. |
+| [podman-run(1)](podman-run.1.md) | Run a command in a new container. |
+| [podman-save(1)](podman-save.1.md) | Save an image to a container archive. |
| [podman-search(1)](podman-search.1.md) | Search a registry for an image. |
-| [podman-start(1)](podman-start.1.md) | Starts one or more containers. |
+| [podman-start(1)](podman-start.1.md) | Start one or more containers. |
| [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. |
| [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
| [podman-system(1)](podman-system.1.md) | Manage podman. |
@@ -175,8 +175,8 @@ the exit codes follow the `chroot` standard, see below:
| [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
| [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
-| [podman-varlink(1)](podman-varlink.1.md) | Display the Podman version information. |
-| [podman-version(1)](podman-version.1.md) | Runs the varlink backend interface. |
+| [podman-version(1)](podman-varlink.1.md) | Runs the varlink backend interface. |
+| [podman-varlink(1)](podman-version.1.md) | Display the Podman version information. |
| [podman-volume(1)](podman-volume.1.md) | Manage Volumes. |
| [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh
index 3c2d193af..071b62434 100755
--- a/hack/get_ci_vm.sh
+++ b/hack/get_ci_vm.sh
@@ -19,6 +19,7 @@ PROJECT="libpod-218412"
GOSRC="/var/tmp/go/src/github.com/containers/libpod"
GCLOUD_IMAGE=${GCLOUD_IMAGE:-quay.io/cevich/gcloud_centos:latest}
GCLOUD_SUDO=${GCLOUD_SUDO-sudo}
+ROOTLESS_USER="madcowdog"
# Shared tmp directory between container and us
TMPDIR=$(mktemp -d --tmpdir $(basename $0)_tmpdir_XXXXXX)
@@ -69,7 +70,9 @@ image_hints() {
show_usage() {
echo -e "\n${RED}ERROR: $1${NOR}"
- echo -e "${YEL}Usage: $(basename $0) [-s | -p] <image_name>${NOR}\n"
+ echo -e "${YEL}Usage: $(basename $0) [-s | -p | -r] <image_name>${NOR}"
+ echo "Use -s / -p to select source or package based dependencies"
+ echo -e "Use -r to setup and run tests as a regular user.\n"
if [[ -r ".cirrus.yml" ]]
then
echo -e "${YEL}Some possible image_name values (from .cirrus.yml):${NOR}"
@@ -104,6 +107,10 @@ parse_args(){
then
DEPS="PACKAGE_DEPS=false SOURCE_DEPS=true"
IMAGE_NAME="$2"
+ elif [[ "$1" == "-r" ]]
+ then
+ DEPS="ROOTLESS_USER=$ROOTLESS_USER"
+ IMAGE_NAME="$2"
else # no -s or -p
DEPS="$(get_env_vars)"
IMAGE_NAME="$1"
@@ -209,4 +216,8 @@ echo -e "\n${YEL}Executing environment setup${NOR}"
showrun $SSH_CMD --command "$SETUP_CMD"
echo -e "\n${YEL}Connecting to $VMNAME ${RED}(option to delete VM upon logout).${NOR}\n"
+if [[ "$1" == "-r" ]]
+then
+ SSH_CMD="$PGCLOUD compute ssh $ROOTLESS_USER@$VMNAME"
+fi
showrun $SSH_CMD -- -t "cd $GOSRC && exec env $DEPS bash -il"
diff --git a/hack/man-page-checker b/hack/man-page-checker
new file mode 100755
index 000000000..8e9b5a50d
--- /dev/null
+++ b/hack/man-page-checker
@@ -0,0 +1,105 @@
+#!/bin/bash
+#
+# man-page-name-checker - validate and cross-reference man page names
+#
+# FIXME as of 2019-03-20 there are still four files with inconsistent names:
+#
+# podman-logs.1.md NAME= podman-container-logs
+# podman-info.1.md NAME= podman-system-info
+# podman-rm.1.md NAME= podman-container-rm
+# podman-rmi.1.md NAME= podman-image-rm
+#
+# If those four get renamed (with suitable symlink fixes), this script
+# can be enabled in CI to prevent future inconsistencies.
+#
+
+die() {
+ echo "$(basename $0): $*" >&2
+ exit 1
+}
+
+cd $(dirname $0)/../docs || die "Please run me from top-level libpod dir"
+
+rc=0
+
+for md in *.1.md;do
+ # Read the first line after '# NAME' (or '## NAME'). (FIXME: # and ##
+ # are not the same; should we stick to one convention?)
+ # There may be more than one name, e.g. podman-info.1.md has
+ # podman-system-info then another line with podman-info. We
+ # care only about the first.
+ name=$(egrep -A1 '^#* NAME' $md|tail -1|awk '{print $1}' | tr -d \\\\)
+
+ if [ "$name" != "$(basename $md .1.md)" ]; then
+ printf "%-32s NAME= %s\n" $md $name
+ rc=1
+ fi
+done
+
+# Pass 2: compare descriptions.
+#
+# Make sure the descriptive text in podman-foo.1.md matches the one
+# in the table in podman.1.md.
+for md in *.1.md;do
+ desc=$(egrep -A1 '^#* NAME' $md|tail -1|sed -e 's/^podman[^ ]\+ - //')
+
+ # podman.1.md has a two-column table; podman-*.1.md all have three.
+ parent=$(echo $md | sed -e 's/^\(.*\)-.*$/\1.1.md/')
+ x=3
+ if expr -- "$parent" : ".*-" >/dev/null; then
+ x=4
+ fi
+
+ # Find the descriptive text in the parent man page.
+ # Strip off the final period; let's not warn about such minutia.
+ parent_desc=$(grep $md $parent | awk -F'|' "{print \$$x}" | sed -e 's/^ \+//' -e 's/ \+$//' -e 's/\.$//')
+
+ if [ "$desc" != "$parent_desc" ]; then
+ echo
+ printf " %-32s = '%s'\n" $md "$desc"
+ printf " %-32s = '%s'\n" $parent "$parent_desc"
+ rc=1
+ fi
+done
+
+# Pass 3: compare synopses.
+#
+# Make sure the SYNOPSIS line in podman-foo.1.md reads '**podman foo** ...'
+for md in *.1.md;do
+ # FIXME: several pages have a multi-line form of SYNOPSIS in which
+ # many or all flags are enumerated. Some of these are trivial
+ # and really should be made into one line (podman-container-exists,
+ # container-prune, others); some are more complicated and I
+ # would still like to see them one-lined (container-runlabel,
+ # image-trust) but I'm not 100% comfortable doing so myself.
+ # To view those:
+ # $ less $(for i in docs/*.1.md;do x=$(grep -A2 '^#* SYNOPSIS' $i|tail -1); if [ -n "$x" ]; then echo $i;fi;done)
+ #
+ synopsis=$(egrep -A1 '^#* SYNOPSIS' $md|tail -1)
+
+ # Command name must be bracketed by double asterisks; options and
+ # arguments are bracketed by single ones.
+ # E.g. '**podman volume inspect** [*options*] *volume*...'
+ # Get the command name, and confirm that it matches the md file name.
+ cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \* | tr ' ' '-')
+ if [ "$md" != "$cmd.1.md" ]; then
+ printf " %-32s SYNOPSIS = %s\n" $md "$cmd"
+ rc=1
+ fi
+
+ # The convention is to use UPPER CASE in 'podman foo --help',
+ # but *lower case bracketed by asterisks* in the man page
+ if expr "$synopsis" : ".*[A-Z]" >/dev/null; then
+ printf " %-32s UPPER-CASE '%s'\n" $md "$synopsis"
+ rc=1
+ fi
+
+ # (for debugging, and getting a sense of standard conventions)
+ #printf " %-32s ------ '%s'\n" $md "$synopsis"
+
+ # FIXME: some day: run ./bin/podman "args", extract Usage,
+ # strip off [flags] and [options], then compare arguments
+done
+
+
+exit $rc
diff --git a/install.md b/install.md
index 071eeff67..5fe150db2 100644
--- a/install.md
+++ b/install.md
@@ -268,7 +268,6 @@ registries = []
# If you need to block pull access from a registry, uncomment the section below
# and add the registries fully-qualified name.
#
-# Docker only
[registries.block]
registries = []
```
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index a76163692..aa3a07888 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -93,6 +93,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
HostsPath: hostsPath,
StaticDir: config.StaticDir,
LogPath: config.LogPath,
+ ConmonPidFile: config.ConmonPidFile,
Name: config.Name,
Driver: driverData.Name,
MountLabel: config.MountLabel,
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index bea7acd69..13e660dc3 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -51,6 +51,9 @@ func (c *Container) rootFsSize() (int64, error) {
if c.config.Rootfs != "" {
return 0, nil
}
+ if c.runtime.store == nil {
+ return 0, nil
+ }
container, err := c.runtime.store.Container(c.ID())
if err != nil {
@@ -337,11 +340,13 @@ func (c *Container) setupStorage(ctx context.Context) error {
}
// Set the default Entrypoint and Command
- if c.config.Entrypoint == nil {
- c.config.Entrypoint = containerInfo.Config.Config.Entrypoint
- }
- if c.config.Command == nil {
- c.config.Command = containerInfo.Config.Config.Cmd
+ if containerInfo.Config != nil {
+ if c.config.Entrypoint == nil {
+ c.config.Entrypoint = containerInfo.Config.Config.Entrypoint
+ }
+ if c.config.Command == nil {
+ c.config.Command = containerInfo.Config.Config.Cmd
+ }
}
artifacts := filepath.Join(c.config.StaticDir, artifactsDir)
@@ -1427,5 +1432,9 @@ func (c *Container) copyWithTarFromImage(src, dest string) error {
}
a := archive.NewDefaultArchiver()
source := filepath.Join(mountpoint, src)
+
+ if err = c.copyOwnerAndPerms(source, dest); err != nil {
+ return err
+ }
return a.CopyWithTar(source, dest)
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index a7b4aed9f..2a7808bdf 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -982,3 +982,20 @@ func (c *Container) generatePasswd() (string, error) {
}
return passwdFile, nil
}
+
+func (c *Container) copyOwnerAndPerms(source, dest string) error {
+ info, err := os.Stat(source)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return errors.Wrapf(err, "cannot stat `%s`", dest)
+ }
+ if err := os.Chmod(dest, info.Mode()); err != nil {
+ return errors.Wrapf(err, "cannot chmod `%s`", dest)
+ }
+ if err := os.Chown(dest, int(info.Sys().(*syscall.Stat_t).Uid), int(info.Sys().(*syscall.Stat_t).Gid)); err != nil {
+ return errors.Wrapf(err, "cannot chown `%s`", dest)
+ }
+ return nil
+}
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index 4af0cd56c..f707b350c 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -35,3 +35,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) error {
return ErrNotImplemented
}
+
+func (c *Container) copyOwnerAndPerms(source, dest string) error {
+ return nil
+}
diff --git a/libpod/container_log.go b/libpod/container_log.go
new file mode 100644
index 000000000..e998ad316
--- /dev/null
+++ b/libpod/container_log.go
@@ -0,0 +1,213 @@
+package libpod
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/hpcloud/tail"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ // logTimeFormat is the time format used in the log.
+ // It is a modified version of RFC3339Nano that guarantees trailing
+ // zeroes are not trimmed, taken from
+ // https://github.com/golang/go/issues/19635
+ logTimeFormat = "2006-01-02T15:04:05.000000000Z07:00"
+)
+
+// LogOptions is the options you can use for logs
+type LogOptions struct {
+ Details bool
+ Follow bool
+ Since time.Time
+ Tail uint64
+ Timestamps bool
+ Multi bool
+ WaitGroup *sync.WaitGroup
+}
+
+// LogLine describes the information for each line of a log
+type LogLine struct {
+ Device string
+ ParseLogType string
+ Time time.Time
+ Msg string
+ CID string
+}
+
+// Log is a runtime function that can read one or more container logs.
+func (r *Runtime) Log(containers []*Container, options *LogOptions, logChannel chan *LogLine) error {
+ for _, ctr := range containers {
+ if err := ctr.ReadLog(options, logChannel); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ReadLog reads a containers log based on the input options and returns loglines over a channel
+func (c *Container) ReadLog(options *LogOptions, logChannel chan *LogLine) error {
+ t, tailLog, err := getLogFile(c.LogPath(), options)
+ if err != nil {
+ // If the log file does not exist, this is not fatal.
+ if os.IsNotExist(errors.Cause(err)) {
+ return nil
+ }
+ return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath())
+ }
+ options.WaitGroup.Add(1)
+ if len(tailLog) > 0 {
+ for _, nll := range tailLog {
+ nll.CID = c.ID()
+ if nll.Since(options.Since) {
+ logChannel <- nll
+ }
+ }
+ }
+
+ go func() {
+ var partial string
+ for line := range t.Lines {
+ nll, err := newLogLine(line.Text)
+ if err != nil {
+ logrus.Error(err)
+ continue
+ }
+ if nll.Partial() {
+ partial = partial + nll.Msg
+ continue
+ } else if !nll.Partial() && len(partial) > 1 {
+ nll.Msg = partial
+ partial = ""
+ }
+ nll.CID = c.ID()
+ if nll.Since(options.Since) {
+ logChannel <- nll
+ }
+ }
+ options.WaitGroup.Done()
+ }()
+ return nil
+}
+
+// getLogFile returns an hp tail for a container given options
+func getLogFile(path string, options *LogOptions) (*tail.Tail, []*LogLine, error) {
+ var (
+ whence int
+ err error
+ logTail []*LogLine
+ )
+ // whence 0=origin, 2=end
+ if options.Tail > 0 {
+ whence = 2
+ logTail, err = getTailLog(path, int(options.Tail))
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ seek := tail.SeekInfo{
+ Offset: 0,
+ Whence: whence,
+ }
+
+ t, err := tail.TailFile(path, tail.Config{MustExist: true, Poll: true, Follow: options.Follow, Location: &seek, Logger: tail.DiscardingLogger})
+ return t, logTail, err
+}
+
+func getTailLog(path string, tail int) ([]*LogLine, error) {
+ var (
+ tailLog []*LogLine
+ nlls []*LogLine
+ tailCounter int
+ partial string
+ )
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ splitContent := strings.Split(string(content), "\n")
+ // We read the content in reverse and add each nll until we have the same
+ // number of F type messages as the desired tail
+ for i := len(splitContent) - 1; i >= 0; i-- {
+ if len(splitContent[i]) == 0 {
+ continue
+ }
+ nll, err := newLogLine(splitContent[i])
+ if err != nil {
+ return nil, err
+ }
+ nlls = append(nlls, nll)
+ if !nll.Partial() {
+ tailCounter = tailCounter + 1
+ }
+ if tailCounter == tail {
+ break
+ }
+ }
+ // Now we iterate the results and assemble partial messages to become full messages
+ for _, nll := range nlls {
+ if nll.Partial() {
+ partial = partial + nll.Msg
+ } else {
+ nll.Msg = nll.Msg + partial
+ tailLog = append(tailLog, nll)
+ partial = ""
+ }
+ }
+ return tailLog, nil
+}
+
+// String converts a logline to a string for output given whether a detail
+// bool is specified.
+func (l *LogLine) String(options *LogOptions) string {
+ var out string
+ if options.Multi {
+ cid := l.CID
+ if len(cid) > 12 {
+ cid = cid[:12]
+ }
+ out = fmt.Sprintf("%s ", cid)
+ }
+ if options.Timestamps {
+ out = out + fmt.Sprintf("%s ", l.Time.Format(logTimeFormat))
+ }
+ return out + l.Msg
+}
+
+// Since returns a bool as to whether a log line occurred after a given time
+func (l *LogLine) Since(since time.Time) bool {
+ return l.Time.After(since)
+}
+
+// newLogLine creates a logLine struct from a container log string
+func newLogLine(line string) (*LogLine, error) {
+ splitLine := strings.Split(line, " ")
+ if len(splitLine) < 4 {
+ return nil, errors.Errorf("'%s' is not a valid container log line", line)
+ }
+ logTime, err := time.Parse(time.RFC3339Nano, splitLine[0])
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert time %s from container log", splitLine[0])
+ }
+ l := LogLine{
+ Time: logTime,
+ Device: splitLine[1],
+ ParseLogType: splitLine[2],
+ Msg: strings.Join(splitLine[3:], " "),
+ }
+ return &l, nil
+}
+
+// Partial returns a bool if the log line is a partial log type
+func (l *LogLine) Partial() bool {
+ if l.ParseLogType == "P" {
+ return true
+ }
+ return false
+}
diff --git a/libpod/events.go b/libpod/events.go
index 879aeb6c5..139600982 100644
--- a/libpod/events.go
+++ b/libpod/events.go
@@ -1,6 +1,8 @@
package libpod
import (
+ "os"
+
"github.com/containers/libpod/libpod/events"
"github.com/hpcloud/tail"
"github.com/pkg/errors"
@@ -85,10 +87,10 @@ func (r *Runtime) Events(fromStart, stream bool, options []events.EventFilter, e
func (r *Runtime) getTail(fromStart, stream bool) (*tail.Tail, error) {
reopen := true
- seek := tail.SeekInfo{Offset: 0, Whence: 2}
+ seek := tail.SeekInfo{Offset: 0, Whence: os.SEEK_END}
if fromStart || !stream {
seek.Whence = 0
reopen = false
}
- return tail.TailFile(r.config.EventsLogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek})
+ return tail.TailFile(r.config.EventsLogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek, Logger: tail.DiscardingLogger})
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index c5939e055..f79bc3dc2 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -1152,9 +1152,6 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
}
}
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
- if err2 := os.Remove(output); err2 != nil {
- logrus.Errorf("error deleting %q: %v", output, err)
- }
return errors.Wrapf(err, "unable to save %q", source)
}
defer i.newImageEvent(events.Save)
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index d8b0cffcb..2450bd6b1 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -215,9 +215,12 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if pid != cmd.Process.Pid {
continue
}
- if status.Exited() || status.Signaled() {
+ if status.Exited() {
return errors.New("slirp4netns failed")
}
+ if status.Signaled() {
+ return errors.New("slirp4netns killed by signal")
+ }
continue
}
return errors.Wrapf(err, "failed to read from slirp4netns sync pipe")
diff --git a/libpod/oci.go b/libpod/oci.go
index 30360d289..69cff6d3c 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -183,6 +183,7 @@ func waitPidsStop(pids []int, timeout time.Duration) error {
func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
var files []*os.File
+ notifySCTP := false
for _, i := range ports {
switch i.Protocol {
case "udp":
@@ -218,6 +219,12 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
}
files = append(files, f)
break
+ case "sctp":
+ if !notifySCTP {
+ notifySCTP = true
+ logrus.Warnf("port reservation for SCTP is not supported")
+ }
+ break
default:
return nil, fmt.Errorf("unknown protocol %s", i.Protocol)
diff --git a/libpod/runtime.go b/libpod/runtime.go
index fa208a2ca..b3b75d791 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -241,6 +241,12 @@ type runtimeConfiguredFrom struct {
libpodStaticDirSet bool
libpodTmpDirSet bool
volPathSet bool
+ conmonPath bool
+ conmonEnvVars bool
+ ociRuntimes bool
+ runtimePath bool
+ cniPluginDir bool
+ noPivotRoot bool
}
var (
@@ -324,6 +330,22 @@ func SetXdgRuntimeDir(val string) error {
// NewRuntime creates a new container runtime
// Options can be passed to override the default configuration for the runtime
func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
+ return newRuntimeFromConfig("", options...)
+}
+
+// NewRuntimeFromConfig creates a new container runtime using the given
+// configuration file for its default configuration. Passed RuntimeOption
+// functions can be used to mutate this configuration further.
+// An error will be returned if the configuration file at the given path does
+// not exist or cannot be loaded
+func NewRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) {
+ if userConfigPath == "" {
+ return nil, errors.New("invalid configuration file specified")
+ }
+ return newRuntimeFromConfig(userConfigPath, options...)
+}
+
+func newRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) {
runtime = new(Runtime)
runtime.config = new(RuntimeConfig)
runtime.configuredFrom = new(runtimeConfiguredFrom)
@@ -358,11 +380,6 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
rootlessConfigPath = filepath.Join(home, ".config/containers/libpod.conf")
- configPath = rootlessConfigPath
- if _, err := os.Stat(configPath); err != nil {
- foundConfig = false
- }
-
runtimeDir, err := util.GetRootlessRuntimeDir()
if err != nil {
return nil, err
@@ -374,6 +391,20 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
}
+ }
+
+ if userConfigPath != "" {
+ configPath = userConfigPath
+ if _, err := os.Stat(configPath); err != nil {
+ // If the user specified a config file, we must fail immediately
+ // when it doesn't exist
+ return nil, errors.Wrapf(err, "cannot stat %s", configPath)
+ }
+ } else if rootless.IsRootless() {
+ configPath = rootlessConfigPath
+ if _, err := os.Stat(configPath); err != nil {
+ foundConfig = false
+ }
} else if _, err := os.Stat(OverrideConfigPath); err == nil {
// Use the override configuration path
configPath = OverrideConfigPath
@@ -409,6 +440,24 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
if tmpConfig.VolumePath != "" {
runtime.configuredFrom.volPathSet = true
}
+ if tmpConfig.ConmonPath != nil {
+ runtime.configuredFrom.conmonPath = true
+ }
+ if tmpConfig.ConmonEnvVars != nil {
+ runtime.configuredFrom.conmonEnvVars = true
+ }
+ if tmpConfig.OCIRuntimes != nil {
+ runtime.configuredFrom.ociRuntimes = true
+ }
+ if tmpConfig.RuntimePath != nil {
+ runtime.configuredFrom.runtimePath = true
+ }
+ if tmpConfig.CNIPluginDir != nil {
+ runtime.configuredFrom.cniPluginDir = true
+ }
+ if tmpConfig.NoPivotRoot {
+ runtime.configuredFrom.noPivotRoot = true
+ }
if _, err := toml.Decode(string(contents), runtime.config); err != nil {
return nil, errors.Wrapf(err, "error decoding configuration file %s", configPath)
@@ -428,12 +477,24 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
}
// Cherry pick the settings we want from the global configuration
- runtime.config.ConmonPath = tmpConfig.ConmonPath
- runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars
- runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes
- runtime.config.RuntimePath = tmpConfig.RuntimePath
- runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir
- runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot
+ if !runtime.configuredFrom.conmonPath {
+ runtime.config.ConmonPath = tmpConfig.ConmonPath
+ }
+ if !runtime.configuredFrom.conmonEnvVars {
+ runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars
+ }
+ if !runtime.configuredFrom.ociRuntimes {
+ runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes
+ }
+ if !runtime.configuredFrom.runtimePath {
+ runtime.config.RuntimePath = tmpConfig.RuntimePath
+ }
+ if !runtime.configuredFrom.cniPluginDir {
+ runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir
+ }
+ if !runtime.configuredFrom.noPivotRoot {
+ runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot
+ }
break
}
}
@@ -444,101 +505,39 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
return nil, errors.Wrapf(err, "error configuring runtime")
}
}
- if err := makeRuntime(runtime); err != nil {
- return nil, err
- }
-
- if !foundConfig && rootlessConfigPath != "" {
- os.MkdirAll(filepath.Dir(rootlessConfigPath), 0755)
- file, err := os.OpenFile(rootlessConfigPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
- if err != nil && !os.IsExist(err) {
- return nil, errors.Wrapf(err, "cannot open file %s", rootlessConfigPath)
- }
- if err == nil {
- defer file.Close()
- enc := toml.NewEncoder(file)
- if err := enc.Encode(runtime.config); err != nil {
- os.Remove(rootlessConfigPath)
+ if rootlessConfigPath != "" {
+ // storage.conf
+ storageConfFile := util.StorageConfigFile()
+ if _, err := os.Stat(storageConfFile); os.IsNotExist(err) {
+ if err := util.WriteStorageConfigFile(&runtime.config.StorageConfig, storageConfFile); err != nil {
+ return nil, errors.Wrapf(err, "cannot write config file %s", storageConfFile)
}
}
- }
- return runtime, nil
-}
-
-// NewRuntimeFromConfig creates a new container runtime using the given
-// configuration file for its default configuration. Passed RuntimeOption
-// functions can be used to mutate this configuration further.
-// An error will be returned if the configuration file at the given path does
-// not exist or cannot be loaded
-func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime *Runtime, err error) {
- runtime = new(Runtime)
- runtime.config = new(RuntimeConfig)
- runtime.configuredFrom = new(runtimeConfiguredFrom)
-
- // Set three fields not in the TOML config
- runtime.config.StateType = defaultRuntimeConfig.StateType
- runtime.config.OCIRuntime = defaultRuntimeConfig.OCIRuntime
-
- storageConf, err := util.GetDefaultStoreOptions()
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving storage config")
- }
- runtime.config.StorageConfig = storageConf
- runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod")
- runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes")
-
- tmpDir, err := getDefaultTmpDir()
- if err != nil {
- return nil, err
- }
- runtime.config.TmpDir = tmpDir
- if rootless.IsRootless() {
- runtimeDir, err := util.GetRootlessRuntimeDir()
- if err != nil {
- return nil, err
- }
- // containers/image uses XDG_RUNTIME_DIR to locate the auth file.
- // So make sure the env variable is set.
- if err := SetXdgRuntimeDir(runtimeDir); err != nil {
- return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
- }
- }
-
- // Check to see if the given configuration file exists
- if _, err := os.Stat(configPath); err != nil {
- return nil, errors.Wrapf(err, "error checking existence of configuration file %s", configPath)
- }
-
- // Read contents of the config file
- contents, err := ioutil.ReadFile(configPath)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading configuration file %s", configPath)
- }
-
- // Decode configuration file
- if _, err := toml.Decode(string(contents), runtime.config); err != nil {
- return nil, errors.Wrapf(err, "error decoding configuration from file %s", configPath)
- }
- // Overwrite the config with user-given configuration options
- for _, opt := range options {
- if err := opt(runtime); err != nil {
- return nil, errors.Wrapf(err, "error configuring runtime")
+ if !foundConfig {
+ os.MkdirAll(filepath.Dir(rootlessConfigPath), 0755)
+ file, err := os.OpenFile(rootlessConfigPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err != nil && !os.IsExist(err) {
+ return nil, errors.Wrapf(err, "cannot open file %s", rootlessConfigPath)
+ }
+ if err == nil {
+ defer file.Close()
+ enc := toml.NewEncoder(file)
+ if err := enc.Encode(runtime.config); err != nil {
+ os.Remove(rootlessConfigPath)
+ }
+ }
}
}
-
if err := makeRuntime(runtime); err != nil {
return nil, err
}
-
return runtime, nil
}
// Make a new runtime based on the given configuration
// Sets up containers/storage, state store, OCI runtime
func makeRuntime(runtime *Runtime) (err error) {
- runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log")
-
// Backward compatibility for `runtime_path`
if runtime.config.RuntimePath != nil {
// Don't print twice in rootless mode.
@@ -697,6 +696,8 @@ func makeRuntime(runtime *Runtime) (err error) {
runtime.config.VolumePath = dbConfig.VolumePath
}
+ runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log")
+
logrus.Debugf("Using graph driver %s", runtime.config.StorageConfig.GraphDriverName)
logrus.Debugf("Using graph root %s", runtime.config.StorageConfig.GraphRoot)
logrus.Debugf("Using run root %s", runtime.config.StorageConfig.RunRoot)
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index c6f119913..3b74a65dd 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -171,7 +171,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
}()
if rootless.IsRootless() && ctr.config.ConmonPidFile == "" {
- ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid")
+ ctr.config.ConmonPidFile = filepath.Join(ctr.config.StaticDir, "conmon.pid")
}
// Go through the volume mounts and check for named volumes
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 756369196..932d209cd 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -4,7 +4,9 @@ package adapter
import (
"context"
+ "fmt"
"strconv"
+ "sync"
"syscall"
"time"
@@ -127,3 +129,28 @@ func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.Wait
}
return ok, failures, err
}
+
+// Log logs one or more containers
+func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error {
+ var wg sync.WaitGroup
+ options.WaitGroup = &wg
+ if len(c.InputArgs) > 1 {
+ options.Multi = true
+ }
+ logChannel := make(chan *libpod.LogLine, int(c.Tail)*len(c.InputArgs)+1)
+ containers, err := shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ return err
+ }
+ if err := r.Runtime.Log(containers, options, logChannel); err != nil {
+ return err
+ }
+ go func() {
+ wg.Wait()
+ close(logChannel)
+ }()
+ for line := range logChannel {
+ fmt.Println(line.String(options))
+ }
+ return nil
+}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 5646d2297..a8146567a 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -5,18 +5,19 @@ package adapter
import (
"context"
"encoding/json"
- "errors"
+ "fmt"
"strconv"
"syscall"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/sirupsen/logrus"
-
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/varlink/go/varlink"
)
// Inspect returns an inspect struct from varlink
@@ -223,3 +224,41 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai
}
return bcs, nil
}
+
+// Logs one or more containers over a varlink connection
+func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error {
+ //GetContainersLogs
+ reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), int64(c.Tail), c.Timestamps)
+ if err != nil {
+ return errors.Wrapf(err, "failed to get container logs")
+ }
+ if len(c.InputArgs) > 1 {
+ options.Multi = true
+ }
+ for {
+ log, flags, err := reply()
+ if err != nil {
+ return err
+ }
+ if log.Time == "" && log.Msg == "" {
+ // We got a blank log line which can signal end of stream
+ break
+ }
+ lTime, err := time.Parse(time.RFC3339Nano, log.Time)
+ if err != nil {
+ return errors.Wrapf(err, "unable to parse time of log %s", log.Time)
+ }
+ logLine := libpod.LogLine{
+ Device: log.Device,
+ ParseLogType: log.ParseLogType,
+ Time: lTime,
+ Msg: log.Msg,
+ CID: log.Cid,
+ }
+ fmt.Println(logLine.String(options))
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+ return nil
+}
diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go
index 82fe37f18..270e431ad 100644
--- a/pkg/inspect/inspect.go
+++ b/pkg/inspect/inspect.go
@@ -158,6 +158,7 @@ type ContainerInspectData struct {
HostsPath string `json:"HostsPath"`
StaticDir string `json:"StaticDir"`
LogPath string `json:"LogPath"`
+ ConmonPidFile string `json:"ConmonPidFile"`
Name string `json:"Name"`
RestartCount int32 `json:"RestartCount"` //TODO
Driver string `json:"Driver"`
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index b2677f7d9..baceebee3 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -72,7 +72,7 @@ func GetRootlessUID() int {
u, _ := strconv.Atoi(uidEnv)
return u
}
- return os.Getuid()
+ return os.Geteuid()
}
func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error {
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index a61c88d58..118fbad72 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -363,7 +363,7 @@ func (c *CreateConfig) createExitCommand() []string {
command = append(command, []string{"--storage-driver", config.StorageConfig.GraphDriverName}...)
}
if c.Syslog {
- command = append(command, "--syslog")
+ command = append(command, "--syslog", "true")
}
command = append(command, []string{"container", "cleanup"}...)
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index d7e1ddd38..a408ad34b 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -190,15 +190,15 @@ func GetRootlessRuntimeDir() (string, error) {
tmpDir := filepath.Join("/run", "user", uid)
os.MkdirAll(tmpDir, 0700)
st, err := os.Stat(tmpDir)
- if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 {
+ if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
}
}
if runtimeDir == "" {
- tmpDir := filepath.Join(os.TempDir(), "user", uid)
+ tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("run-%s", uid))
os.MkdirAll(tmpDir, 0700)
st, err := os.Stat(tmpDir)
- if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 {
+ if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && st.Mode().Perm() == 0700 {
runtimeDir = tmpDir
}
}
@@ -311,36 +311,37 @@ func GetDefaultStoreOptions() (storage.StoreOptions, error) {
storageOpts = storage.StoreOptions{}
storage.ReloadConfigurationFile(storageConf, &storageOpts)
}
-
- if rootless.IsRootless() {
- if os.IsNotExist(err) {
- os.MkdirAll(filepath.Dir(storageConf), 0755)
- file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
- if err != nil {
- return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf)
- }
-
- tomlConfiguration := getTomlStorage(&storageOpts)
- defer file.Close()
- enc := toml.NewEncoder(file)
- if err := enc.Encode(tomlConfiguration); err != nil {
- os.Remove(storageConf)
- }
- } else if err == nil {
- // If the file did not specify a graphroot or runroot,
- // set sane defaults so we don't try and use root-owned
- // directories
- if storageOpts.RunRoot == "" {
- storageOpts.RunRoot = defaultRootlessRunRoot
- }
- if storageOpts.GraphRoot == "" {
- storageOpts.GraphRoot = defaultRootlessGraphRoot
- }
+ if rootless.IsRootless() && err == nil {
+ // If the file did not specify a graphroot or runroot,
+ // set sane defaults so we don't try and use root-owned
+ // directories
+ if storageOpts.RunRoot == "" {
+ storageOpts.RunRoot = defaultRootlessRunRoot
+ }
+ if storageOpts.GraphRoot == "" {
+ storageOpts.GraphRoot = defaultRootlessGraphRoot
}
}
return storageOpts, nil
}
+// WriteStorageConfigFile writes the configuration to a file
+func WriteStorageConfigFile(storageOpts *storage.StoreOptions, storageConf string) error {
+ os.MkdirAll(filepath.Dir(storageConf), 0755)
+ file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open %s", storageConf)
+ }
+ tomlConfiguration := getTomlStorage(storageOpts)
+ defer file.Close()
+ enc := toml.NewEncoder(file)
+ if err := enc.Encode(tomlConfiguration); err != nil {
+ os.Remove(storageConf)
+ return err
+ }
+ return nil
+}
+
// StorageConfigFile returns the path to the storage config file used
func StorageConfigFile() string {
if rootless.IsRootless() {
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index fe38a7cdc..3185ba0e9 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"os"
+ "sync"
"syscall"
"time"
@@ -602,3 +603,56 @@ func ContainerStatsToLibpodContainerStats(stats iopodman.ContainerStats) libpod.
}
return cstats
}
+
+// GetContainersLogs is the varlink endpoint to obtain one or more container logs
+func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error {
+ var wg sync.WaitGroup
+ if call.WantsMore() {
+ call.Continues = true
+ }
+ sinceTime, err := time.Parse(time.RFC3339Nano, since)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ options := libpod.LogOptions{
+ Follow: follow,
+ Since: sinceTime,
+ Tail: uint64(tail),
+ Timestamps: timestamps,
+ }
+
+ options.WaitGroup = &wg
+ if len(names) > 1 {
+ options.Multi = true
+ }
+ logChannel := make(chan *libpod.LogLine, int(tail)*len(names)+1)
+ containers, err := shortcuts.GetContainersByContext(false, latest, names, i.Runtime)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if err := i.Runtime.Log(containers, &options, logChannel); err != nil {
+ return err
+ }
+ go func() {
+ wg.Wait()
+ close(logChannel)
+ }()
+ for line := range logChannel {
+ call.ReplyGetContainersLogs(newPodmanLogLine(line))
+ if !call.Continues {
+ break
+ }
+
+ }
+ return call.ReplyGetContainersLogs(iopodman.LogLine{})
+}
+
+func newPodmanLogLine(line *libpod.LogLine) iopodman.LogLine {
+ return iopodman.LogLine{
+ Device: line.Device,
+ ParseLogType: line.ParseLogType,
+ Time: line.Time.Format(time.RFC3339Nano),
+ Msg: line.Msg,
+ Cid: line.CID,
+ }
+}
diff --git a/test/certs/README.md b/test/certs/README.md
new file mode 100644
index 000000000..3aab01440
--- /dev/null
+++ b/test/certs/README.md
@@ -0,0 +1,9 @@
+# How to generate key and cert:
+
+## Make private key without a password
+
+certtool --rsa --generate-privkey --null-password --outfile=domain.key
+
+## Use ``domain.cfg`` template to make self-signed cert
+
+certtool --generate-self-signed --load-privkey=domain.key --template=domain.cfg --outfile=domain.crt --load-ca-privkey=domain.key --null-password --no-text
diff --git a/test/certs/domain.cfg b/test/certs/domain.cfg
new file mode 100644
index 000000000..5baeb5631
--- /dev/null
+++ b/test/certs/domain.cfg
@@ -0,0 +1,30 @@
+# X.509 Certificate options
+organization = "Koko inc."
+unit = "sleeping dept."
+locality = "foobar"
+state = "Attiki"
+country = GR
+cn = "Cindy Lauper"
+uid = "clauper"
+dc = "name"
+dc = "domain"
+serial = 1234
+dns_name = "localhost"
+# Use -1 if there is no expiration date.
+expiration_days = -1
+email = "none@none.org"
+signing_key
+encryption_key
+cert_signing_key
+crl_signing_key
+data_encipherment
+non_repudiation
+tls_www_client
+tls_www_server
+code_signing_key
+ocsp_signing_key
+time_stamping_key
+email_protection_key
+ipsec_ike_key
+# for any purpose (must not be used in intermediate CA certificates)
+key_purpose_oid = 2.5.29.37.0
diff --git a/test/certs/domain.crt b/test/certs/domain.crt
index 881fc124d..8a697d7b2 100644
--- a/test/certs/domain.crt
+++ b/test/certs/domain.crt
@@ -1,18 +1,33 @@
-----BEGIN CERTIFICATE-----
-MIIC3zCCAmSgAwIBAgIUdbnvx7lLf8OANP37QTKoxfNAl5EwCgYIKoZIzj0EAwMw
-gawxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
-YW4gRnJhbmNpc2NvMSowKAYDVQQKEyFIb25lc3QgQWNobWVkJ3MgVXNlZCBDZXJ0
-aWZpY2F0ZXMxKTAnBgNVBAsTIEhhc3RpbHktR2VuZXJhdGVkIFZhbHVlcyBEaXZp
-c29uMRkwFwYDVQQDExBBdXRvZ2VuZXJhdGVkIENBMB4XDTE4MDMyMDExMDUwMFoX
-DTE5MDMyMDExMDUwMFowWzEVMBMGA1UEBxMMdGhlIGludGVybmV0MRYwFAYDVQQK
-Ew1hdXRvZ2VuZXJhdGVkMRQwEgYDVQQLEwtwb2RtYW4gdGVzdDEUMBIGA1UEAxML
-cG9kbWFuLXRlc3QwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATA65F+T8sreSnTm+I2
-IjeKN8rb5W2j3QKXz8n9JkPWiWX16HGIWso1JWPhhjvpmVkfSzD91niQwrsm6PhP
-ypZUzkX5iL7JE8jVjflEiUbflSzc+fgT/scqRUUQ3evmqUCjgZYwgZMwDgYDVR0P
-AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMB
-Af8EAjAAMB0GA1UdDgQWBBQCgkUh4aBOTl5KHettBluuE7rccDAfBgNVHSMEGDAW
-gBTPyUqMxUVdwC4K+kh9jHtnf7GrETAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYI
-KoZIzj0EAwMDaQAwZgIxAKsrYLbXSJs473tlfX3OF/BmfTvDwBO5TfPoZ1yNDhVk
-UvoYn2szSEVMwR7uX1gKWgIxALz00G6umVkSh0MgIwSaYpJU/N1eVNgbIXRFV+5+
-lK/0jLWm4aAFkVhqUkkueTzG2g==
+MIIFozCCBAugAwIBAgICBNIwDQYJKoZIhvcNAQELBQAwgboxFTATBgNVBAMTDENp
+bmR5IExhdXBlcjEXMBUGCgmSJomT8ixkAQETB2NsYXVwZXIxFzAVBgNVBAsTDnNs
+ZWVwaW5nIGRlcHQuMRIwEAYDVQQKEwlLb2tvIGluYy4xDzANBgNVBAcTBmZvb2Jh
+cjEPMA0GA1UECBMGQXR0aWtpMQswCQYDVQQGEwJHUjEUMBIGCgmSJomT8ixkARkW
+BG5hbWUxFjAUBgoJkiaJk/IsZAEZFgZkb21haW4wIBcNMTkwMzIwMTcyNjI4WhgP
+OTk5OTEyMzEyMzU5NTlaMIG6MRUwEwYDVQQDEwxDaW5keSBMYXVwZXIxFzAVBgoJ
+kiaJk/IsZAEBEwdjbGF1cGVyMRcwFQYDVQQLEw5zbGVlcGluZyBkZXB0LjESMBAG
+A1UEChMJS29rbyBpbmMuMQ8wDQYDVQQHEwZmb29iYXIxDzANBgNVBAgTBkF0dGlr
+aTELMAkGA1UEBhMCR1IxFDASBgoJkiaJk/IsZAEZFgRuYW1lMRYwFAYKCZImiZPy
+LGQBGRYGZG9tYWluMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAsegi
+5zWmOYejptJSm32ZV7ttJxSycbCIF8/dGTigFwE9IWMitWWz5VyohVYKYYV0ZZTI
+SZOZFfL8ElQ6jTnHHxewWi/w1qINyTESGS2jOSBj3uXyGj94gXv9guzbWLNKAD8v
+4iUHYq/7zZPyLqtzFGep3kuKFlrTxsdjgho2NyiuCCLcm+H3dpBoI24QIp1vCIX/
+LCqaAfkxzMyF5sawL7H82gAfYhvGTFoLb5Dy3HdUwlhelTW746HSqtTb9fEqUjNZ
+MmBrCGexW8hN2WheIAoKOGc3UGZgpFj9q6LgWkVKsosZ9PhdxtOKH5XZrT6d5ZFb
+HSfpsxZ0LMLDUK/H+kJaFJaSl0HyxbyZMDExtjCjLzk81+A9t9jkh8Jnag9Ylpec
+ZHxKKl3K7lJuq7s69uKbN9XbTTUkmw4AMa7WdNadj/t8SfpM3ZxZWzX6U81wQg0Z
+Zb/fKxu8lAMZNxfgW7bnaIUBAFzNd4AE/mCOO941v8cizXCNBZ5EyZGVIhxtAgMB
+AAGjga4wgaswVQYDVR0lBE4wTAYEVR0lAAYIKwYBBQUHAwIGCCsGAQUFBwMBBggr
+BgEFBQcDEQYIKwYBBQUHAwkGCCsGAQUFBwMDBggrBgEFBQcDCAYIKwYBBQUHAwQw
+DAYDVR0TAQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwDwYDVR0PAQH/BAUD
+AwfwADAdBgNVHQ4EFgQU28OkcdKIoWfHK19QXiFCYud345YwDQYJKoZIhvcNAQEL
+BQADggGBAJyA0YiSiCzVS6M+/2hEvKamlYUz6L7pb5fdlQ3dPL/KlbUPc31bLD7b
+G3Su9iz1bL1qVGebgoJTEDOD/6F9DbMY4Nsh4oe1rS+eBZe6fG6VnaxE4U2XIWKD
+0aijGGfUvEB8W66BtCrtjY3bAb9v1OLvLxktd9do4ACjskq+q+CE/8ID9tCkAn8J
+q0aHv5a5H3bPu3WGuiifZHER+08OwEsO7jsnZAJdgrAK8D0Rodd1rbdIFh2klu1p
+LOCCMhgrQtCFVhoqWA2XaGexqmlTTXjabFDyMA8RvdKozwLSpyMMHJX6Q38Wdr8Z
+nAXfqkCAcXuVwk2llAW7f073Ze3xFWUiW6gH0w7H1KvHHkglBM4H3HyG2T2pu9lb
+mrqRRnfx2AuQplUrFVvz5V7sKmTwZCPueXkfcv/+6bxcLA0N0p3Dk+LGc2JzBLFb
+n6AzrjKwiEg5ri4dQEuaYnJAYDvp+/vo4AEPrUc9nKwOo1h+3rAY2ZkuCy06/3cj
+nzp56Tpaaw==
-----END CERTIFICATE-----
diff --git a/test/certs/domain.key b/test/certs/domain.key
index b0f15eb75..4fe293cac 100644
--- a/test/certs/domain.key
+++ b/test/certs/domain.key
@@ -1,6 +1,39 @@
------BEGIN EC PRIVATE KEY-----
-MIGkAgEBBDB7ZI5Q6dOSwOqpJ2FVlFuDJN/sJB3epR2S+rOvCPua+rQ8uv6lpZDx
-CQ4ioUMFo6agBwYFK4EEACKhZANiAATA65F+T8sreSnTm+I2IjeKN8rb5W2j3QKX
-z8n9JkPWiWX16HGIWso1JWPhhjvpmVkfSzD91niQwrsm6PhPypZUzkX5iL7JE8jV
-jflEiUbflSzc+fgT/scqRUUQ3evmqUA=
------END EC PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4gIBAAKCAYEAsegi5zWmOYejptJSm32ZV7ttJxSycbCIF8/dGTigFwE9IWMi
+tWWz5VyohVYKYYV0ZZTISZOZFfL8ElQ6jTnHHxewWi/w1qINyTESGS2jOSBj3uXy
+Gj94gXv9guzbWLNKAD8v4iUHYq/7zZPyLqtzFGep3kuKFlrTxsdjgho2NyiuCCLc
+m+H3dpBoI24QIp1vCIX/LCqaAfkxzMyF5sawL7H82gAfYhvGTFoLb5Dy3HdUwlhe
+lTW746HSqtTb9fEqUjNZMmBrCGexW8hN2WheIAoKOGc3UGZgpFj9q6LgWkVKsosZ
+9PhdxtOKH5XZrT6d5ZFbHSfpsxZ0LMLDUK/H+kJaFJaSl0HyxbyZMDExtjCjLzk8
+1+A9t9jkh8Jnag9YlpecZHxKKl3K7lJuq7s69uKbN9XbTTUkmw4AMa7WdNadj/t8
+SfpM3ZxZWzX6U81wQg0ZZb/fKxu8lAMZNxfgW7bnaIUBAFzNd4AE/mCOO941v8ci
+zXCNBZ5EyZGVIhxtAgMBAAECggGAfvporw2jrrwZGiBTxZdHs06bAaHMG0kcWaKK
+9E1uNf00XHgddcs5MyOHRGO81Q4jnb0rlxg502iycYKcp9/tN0v5GuXMx+SyYj8b
+48ynC0cLATSuL/3NTN3qe2ACzrRoxPRUgNxdARsKZhiKarUEVjQHEhpoXLxHG0GE
+zH9Y4tWuITCAtOH7dixrp54O9iXX8gVxs1xUv8PUv4/aonR9nA01o4Mi4ytfxW8f
+amnSbXjejjf0ihroF/iQHE4BEPEnSw/noudboZqW1fX30QJsbk13VH9BGIN2zQ06
+vnDU/VrUYfgKNGSjaeECaN7iZQ7nekzSomKFGm8yoLpgsFYjpqnGhyplDY1jE/J3
+BL4r7PVeUWiQdLqkjLxeAhcySUjGvt79UsX0N8Oa3dgOGUHz3H986l3G9Hlcgvnc
+L18qzsyIo4WNFrLAWt3nwiWM8Olj3Y6S3S8EiBeXVe1g+nuYBFwLFDCkw26WhtJ0
+sy90q6pRvL1MXOq2etcT5RcNfichAoHBANXES2CtpE7GdgAnVpYoBUng0n8qieTG
+qLkkPx5E9pr64c3pUqXVGjFY0XvZ16x0UMOC2+V1DuLea2WyPO0FmitWEXH7xfVN
+KM+3ORT5afSG0fhaIDvkPdG3QwfGZCib9g5ZOR0rxnDHVAzCx242YITUa8rn8158
+tpH0nDsvvAnVeUIMHOq5DO7zx1jcPQtcWP4vF3a37j2dOgBe4c7CCMOpP8gCGVlX
+Okr6HtNoKiKA4n5NbJNo+BKAJ8o7NrHdNwKBwQDVDiWqYve2kyGcRSowJk9gHH1f
+Ls+0MP6gLvdRoIhAihrNLws8fhI/mLBxHTBCogoumU5nWavZyKqDTf0dRJSKgQfJ
+hdkRPVWIy6crshtSRZ+dX7yFKAnXU+D+cgcximonA4Uza0WFh9OP4702VpcdlFQ8
+qmKZvFI1orLw37h8ej/Bk4MeocDBWjpa5Q/0Nk7JsuwyW/OadXM48NfJM88ZrEi/
+ZuDjNIshynG3tddoIs0nNfkoslFGOzxpMFnbRXsCgcBpEkwOoCsUAV684pkfw1oe
+HyC4GtuelLsYDaXspe8k7E4THS1fj6iJOuP04XWuMZoFD4wwc+I2Ryc43GwwAMHv
+rSV0BlIeKaf2uVOYaKPY6m/Ih9wyNBTiwRZ0euJ+R3KhSN/W485tXryEbTUDijzU
+7WhyWqJ3/grrIPWt7d+aYdBxU2zfPsgJp8+DcPWcYO7pOZJp6yxyIpcA2aJaM2uF
+aOqNz+JP1J01f02pkhirzvgFJt9IcZ8F0PI95+8Ra+8CgcAD6tesc1dkpv3mNqtY
+6UtqU/vGJUEyafg0j8iCWrZGoYNupF/Lg/Hn83HDEqtRflM7mhwD8HUlcvgXo/Z0
+dE9a4JZ5ERn1pDAPbNctCYBRGfCeXyVDOYI80FEBvKz/LzFWeE0Zre5AT0gHjENt
+XVg39gM6flODyh+k1tH9dc+ZklHbyE+P35+Arp0GENIjRmBaewy2vFQVUfWFZYBC
+Nc6oBS/tPQIDi3LHc0Z1/0TvqDwnbWmgYu71oJ8yu+3bB0MCgcB0katYynavMg3W
+w15sH/1+be5dFTXZoiajjqXmYDNkZzyLoghteu/eyxqFw8Au4hyVy5koYHN/jrzf
+1ZfXFCs3P5asCAptTH3kwgT/ER58KPOoq7cGEaut9eQ1UAc8E34b/ZISbTl/Zw05
+swOMC6ipzn70kNoPcjJN2ujSUCD9t0NKyyCAKokjFICGpCHDOdOwqtZRbKFVFSix
+/T4HAV9puGT6kDMCtjLyZ+fUOf9hgCHK3sBkBt5XrU8j6Wf/zyg=
+-----END RSA PRIVATE KEY-----
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index afd6d3cf3..54b2cbec2 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -3,6 +3,7 @@ package integration
import (
"encoding/json"
"fmt"
+ "github.com/containers/libpod/pkg/rootless"
"io/ioutil"
"os"
"os/exec"
@@ -213,7 +214,11 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
if os.Getenv("STORAGE_OPTIONS") != "" {
storageOptions = os.Getenv("STORAGE_OPTIONS")
}
+
cgroupManager := CGROUP_MANAGER
+ if rootless.IsRootless() {
+ cgroupManager = "cgroupfs"
+ }
if os.Getenv("CGROUP_MANAGER") != "" {
cgroupManager = os.Getenv("CGROUP_MANAGER")
}
diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go
index a67c1a5a8..6c4ca1cb8 100644
--- a/test/e2e/create_staticip_test.go
+++ b/test/e2e/create_staticip_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman create with --ip flag", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/e2e.coverprofile b/test/e2e/e2e.coverprofile
index b5382604f..d413679ea 100644
--- a/test/e2e/e2e.coverprofile
+++ b/test/e2e/e2e.coverprofile
@@ -1,11 +1,11 @@
mode: atomic
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:14.46,21.20 2 1
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:31.2,31.19 1 1
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:38.2,38.53 1 1
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:65.2,65.52 1 1
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:21.20,23.17 2 2
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:26.3,28.36 3 2
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:14.46,21.20 2 3
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:32.2,32.19 1 3
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:39.2,39.53 1 3
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:66.2,66.52 1 3
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:21.20,23.17 2 6
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:26.3,29.36 4 6
github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:23.17,25.4 1 0
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:31.19,36.3 4 2
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:38.53,63.3 20 1
-github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:65.52,90.3 20 1
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:32.19,37.3 3 6
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:39.53,64.3 20 3
+github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:66.52,91.3 20 3 \ No newline at end of file
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index 2f0af7e5f..5bcf3b347 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -48,6 +48,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman generate kube on container", func() {
+ SkipIfRootless()
session := podmanTest.RunTopContainer("top")
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -61,6 +62,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman generate service kube on container", func() {
+ SkipIfRootless()
session := podmanTest.RunTopContainer("top")
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -74,6 +76,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman generate kube on pod", func() {
+ SkipIfRootless()
_, rc, _ := podmanTest.CreatePod("toppod")
Expect(rc).To(Equal(0))
@@ -90,6 +93,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman generate service kube on pod", func() {
+ SkipIfRootless()
_, rc, _ := podmanTest.CreatePod("toppod")
Expect(rc).To(Equal(0))
diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go
index 921d325c3..f178e8ad5 100644
--- a/test/e2e/healthcheck_run_test.go
+++ b/test/e2e/healthcheck_run_test.go
@@ -42,6 +42,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck on valid container", func() {
+ SkipIfRootless()
podmanTest.RestoreArtifact(healthcheck)
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index 046297bc0..c960fb311 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -41,7 +41,7 @@ var _ = Describe("Podman Info", func() {
})
It("podman system info json output", func() {
session := podmanTest.Podman([]string{"system", "info", "--format=json"})
- session.Wait()
+ session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index ebe610e6a..34328828f 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -66,6 +66,16 @@ var _ = Describe("Podman inspect", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman inspect container with GO format for ConmonPidFile", func() {
+ SkipIfRemote()
+ session, ec, _ := podmanTest.RunLsContainer("test1")
+ Expect(ec).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman inspect container with size", func() {
SkipIfRemote()
_, ec, _ := podmanTest.RunLsContainer("")
diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go
index e6bc00397..44c5edf07 100644
--- a/test/e2e/libpod_suite_remoteclient_test.go
+++ b/test/e2e/libpod_suite_remoteclient_test.go
@@ -18,6 +18,12 @@ func SkipIfRemote() {
ginkgo.Skip("This function is not enabled for remote podman")
}
+func SkipIfRootless() {
+ if os.Geteuid() != 0 {
+ ginkgo.Skip("This function is not enabled for remote podman")
+ }
+}
+
// Cleanup cleans up the temporary store
func (p *PodmanTestIntegration) Cleanup() {
p.StopVarlink()
@@ -133,6 +139,9 @@ func (p *PodmanTestIntegration) CleanupVolume() {
}
func PodmanTestCreate(tempDir string) *PodmanTestIntegration {
+ if os.Geteuid() != 0 {
+ ginkgo.Skip("This function is not enabled for rootless podman")
+ }
pti := PodmanTestCreateUtil(tempDir, true)
pti.StartVarlink()
return pti
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 1a3f37e23..685a08340 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -14,12 +14,23 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
. "github.com/containers/libpod/test/utils"
+ "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
-func SkipIfRemote() {}
+func SkipIfRemote() {
+ if os.Geteuid() != 0 {
+ ginkgo.Skip("This function is not enabled for rootless podman")
+ }
+}
+
+func SkipIfRootless() {
+ if os.Geteuid() != 0 {
+ ginkgo.Skip("This function is not enabled for rootless podman")
+ }
+}
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index b7d959de9..d051e3dba 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -4,6 +4,7 @@ package integration
import (
"os"
+ "strings"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
@@ -34,7 +35,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() {
logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
logc.WaitWithDefaultTimeout()
@@ -106,4 +106,40 @@ var _ = Describe("Podman logs", func() {
Expect(results.ExitCode()).To(Equal(0))
Expect(len(results.OutputToStringArray())).To(Equal(3))
})
+
+ It("podman logs latest and container name should fail", func() {
+ results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman logs two containers and should display short container IDs", func() {
+ log1 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
+ log1.WaitWithDefaultTimeout()
+ Expect(log1.ExitCode()).To(Equal(0))
+ cid1 := log1.OutputToString()
+
+ log2 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
+ log2.WaitWithDefaultTimeout()
+ Expect(log2.ExitCode()).To(Equal(0))
+ cid2 := log2.OutputToString()
+
+ results := podmanTest.Podman([]string{"logs", cid1, cid2})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(Equal(0))
+
+ output := results.OutputToStringArray()
+ Expect(len(output)).To(Equal(6))
+ Expect(strings.Contains(output[0], cid1[:12]) || strings.Contains(output[0], cid2[:12])).To(BeTrue())
+ })
+
+ It("podman logs on a created container should result in 0 exit code", func() {
+ session := podmanTest.Podman([]string{"create", "-dt", "--name", "log", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ results := podmanTest.Podman([]string{"logs", "log"})
+ results.WaitWithDefaultTimeout()
+ Expect(results.ExitCode()).To(BeZero())
+ })
})
diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go
index 2d4c1d303..e28c31c3a 100644
--- a/test/e2e/pause_test.go
+++ b/test/e2e/pause_test.go
@@ -22,6 +22,7 @@ var _ = Describe("Podman pause", func() {
createdState := "Created"
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index 5ffc0f779..de0734e9f 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -100,7 +100,7 @@ var _ = Describe("Podman pod create", func() {
It("podman create pod with network portbindings", func() {
name := "test"
- session := podmanTest.Podman([]string{"pod", "create", "--name", name, "-p", "80:80"})
+ session := podmanTest.Podman([]string{"pod", "create", "--name", name, "-p", "8080:80"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
pod := session.OutputToString()
@@ -109,7 +109,7 @@ var _ = Describe("Podman pod create", func() {
webserver.WaitWithDefaultTimeout()
Expect(webserver.ExitCode()).To(Equal(0))
- check := SystemExec("nc", []string{"-z", "localhost", "80"})
+ check := SystemExec("nc", []string{"-z", "localhost", "8080"})
Expect(check.ExitCode()).To(Equal(0))
})
diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go
index 62dc919b6..59a4da176 100644
--- a/test/e2e/pod_pause_test.go
+++ b/test/e2e/pod_pause_test.go
@@ -20,6 +20,7 @@ var _ = Describe("Podman pod pause", func() {
pausedState := "Paused"
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go
index ceabb9dc1..6018b4494 100644
--- a/test/e2e/pod_stats_test.go
+++ b/test/e2e/pod_stats_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman pod stats", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index 58697acde..957c69aa8 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -82,6 +82,8 @@ var _ = Describe("Podman ps", func() {
})
It("podman ps size flag", func() {
+ SkipIfRootless()
+
_, ec, _ := podmanTest.RunLsContainer("")
Expect(ec).To(Equal(0))
@@ -233,6 +235,8 @@ var _ = Describe("Podman ps", func() {
})
It("podman --sort by size", func() {
+ SkipIfRootless()
+
session := podmanTest.Podman([]string{"create", "busybox", "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -303,4 +307,19 @@ var _ = Describe("Podman ps", func() {
Expect(session.OutputToString()).To(ContainSubstring(podid))
})
+
+ It("podman ps test with port range", func() {
+ SkipIfRootless()
+ session := podmanTest.RunTopContainer("")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "-dt", "-p", "1000-1006:1000-1006", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:1000-1006"))
+ })
})
diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go
index 89df62d42..009067482 100644
--- a/test/e2e/push_test.go
+++ b/test/e2e/push_test.go
@@ -80,6 +80,7 @@ var _ = Describe("Podman push", func() {
})
It("podman push to local registry with authorization", func() {
+ SkipIfRootless()
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index 1f67780da..9bf742a63 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -139,9 +139,23 @@ var _ = Describe("Podman rm", func() {
Expect(podmanTest.NumberOfContainers()).To(Equal(1))
})
+
It("podman rm bogus container", func() {
session := podmanTest.Podman([]string{"rm", "bogus"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(1))
})
+ It("podman rm bogus container and a running container", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"rm", "bogus", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+
+ session = podmanTest.Podman([]string{"rm", "test1", "bogus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
})
diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go
index cd771e2ba..51544ff8b 100644
--- a/test/e2e/rootless_test.go
+++ b/test/e2e/rootless_test.go
@@ -38,6 +38,7 @@ var _ = Describe("Podman rootless", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -138,6 +139,21 @@ var _ = Describe("Podman rootless", func() {
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
Expect(cmd.LineInOutputContains("hello")).To(BeTrue())
+
+ args = []string{"pod", "top", podId}
+ cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env)
+ cmd.WaitWithDefaultTimeout()
+ Expect(cmd.ExitCode()).To(Not(Equal(0)))
+
+ args = []string{"run", "--pod", podId, "-d", "--rootfs", mountPath, "sleep", "100"}
+ cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env)
+ cmd.WaitWithDefaultTimeout()
+ Expect(cmd.ExitCode()).To(Equal(0))
+
+ args = []string{"pod", "top", podId}
+ cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env)
+ cmd.WaitWithDefaultTimeout()
+ Expect(cmd.ExitCode()).To(Equal(0))
}
runInRootlessContext(f)
})
diff --git a/test/e2e/run_cgroup_parent_test.go b/test/e2e/run_cgroup_parent_test.go
index a6955591f..0d04c5f03 100644
--- a/test/e2e/run_cgroup_parent_test.go
+++ b/test/e2e/run_cgroup_parent_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman run with --cgroup-parent", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/run_cpu_test.go b/test/e2e/run_cpu_test.go
index a2dd5b9b8..42a66865c 100644
--- a/test/e2e/run_cpu_test.go
+++ b/test/e2e/run_cpu_test.go
@@ -35,6 +35,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpu-period", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpu-period=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -42,6 +43,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpu-quota", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -49,6 +51,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpus", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -61,6 +64,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpu-shares", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=2", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.shares"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -68,6 +72,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpuset-cpus", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpuset-cpus=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -75,6 +80,7 @@ var _ = Describe("Podman run cpu", func() {
})
It("podman run cpuset-mems", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"run", "--rm", "--cpuset-mems=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go
index 5f59fbe37..fac09b78d 100644
--- a/test/e2e/run_device_test.go
+++ b/test/e2e/run_device_test.go
@@ -41,6 +41,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg", ALPINE, "ls", "--color=never", "/dev/kmsg"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -48,6 +49,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device rename test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "ls", "--color=never", "/dev/kmsg1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -55,6 +57,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device permission test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:r", ALPINE, "ls", "--color=never", "/dev/kmsg"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -62,6 +65,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device rename and permission test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "ls", "--color=never", "/dev/kmsg1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -74,6 +78,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device host device and container device parameter are directories", func() {
+ SkipIfRootless()
SystemExec("mkdir", []string{"/dev/foodevdir"})
SystemExec("mknod", []string{"/dev/foodevdir/null", "c", "1", "3"})
session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/foodevdir:/dev/bar", ALPINE, "ls", "/dev/bar/null"})
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
index 875c90d73..0f4dd6742 100644
--- a/test/e2e/run_dns_test.go
+++ b/test/e2e/run_dns_test.go
@@ -88,6 +88,7 @@ var _ = Describe("Podman run dns", func() {
})
It("podman run add hostname sets /etc/hosts", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-t", "-i", "--hostname=foobar", ALPINE, "cat", "/etc/hosts"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go
index 790cdf743..05d0b7a18 100644
--- a/test/e2e/run_memory_test.go
+++ b/test/e2e/run_memory_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman run memory", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 80378dc7b..93919925c 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -54,6 +54,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network expose port 222", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-dt", "--expose", "222-223", "-P", ALPINE, "/bin/sh"})
session.Wait(30)
Expect(session.ExitCode()).To(Equal(0))
@@ -64,6 +65,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network expose host port 80 to container port 8000", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"})
session.Wait(30)
Expect(session.ExitCode()).To(Equal(0))
@@ -146,6 +148,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run --net container: copies hosts and resolv", func() {
+ SkipIfRootless()
ctrName := "ctr1"
ctr1 := podmanTest.RunTopContainer(ctrName)
ctr1.WaitWithDefaultTimeout()
@@ -177,6 +180,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network in user created network namespace", func() {
+ SkipIfRootless()
if Containerized() {
Skip("Can not be run within a container.")
}
@@ -193,6 +197,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run n user created network namespace with resolv.conf", func() {
+ SkipIfRootless()
if Containerized() {
Skip("Can not be run within a container.")
}
diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go
index 51f921bce..5236e6584 100644
--- a/test/e2e/run_ns_test.go
+++ b/test/e2e/run_ns_test.go
@@ -63,6 +63,7 @@ var _ = Describe("Podman run ns", func() {
})
It("podman run ipcns ipcmk host test", func() {
+ SkipIfRootless()
setup := SystemExec("ipcmk", []string{"-M", "1024"})
Expect(setup.ExitCode()).To(Equal(0))
output := strings.Split(setup.OutputToString(), " ")
@@ -76,6 +77,7 @@ var _ = Describe("Podman run ns", func() {
})
It("podman run ipcns ipcmk container test", func() {
+ SkipIfRootless()
setup := podmanTest.Podman([]string{"run", "-d", "--name", "test1", fedoraMinimal, "sleep", "999"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go
index a4500e421..16011b2fd 100644
--- a/test/e2e/run_privileged_test.go
+++ b/test/e2e/run_privileged_test.go
@@ -45,6 +45,7 @@ var _ = Describe("Podman privileged container tests", func() {
})
It("podman privileged CapEff", func() {
+ SkipIfRootless()
cap := SystemExec("grep", []string{"CapEff", "/proc/self/status"})
Expect(cap.ExitCode()).To(Equal(0))
@@ -55,6 +56,7 @@ var _ = Describe("Podman privileged container tests", func() {
})
It("podman cap-add CapEff", func() {
+ SkipIfRootless()
cap := SystemExec("grep", []string{"CapEff", "/proc/self/status"})
Expect(cap.ExitCode()).To(Equal(0))
@@ -80,6 +82,7 @@ var _ = Describe("Podman privileged container tests", func() {
})
It("podman privileged should inherit host devices", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "-l", "/dev"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go
index 282806562..4d2bad49c 100644
--- a/test/e2e/run_selinux_test.go
+++ b/test/e2e/run_selinux_test.go
@@ -112,6 +112,7 @@ var _ = Describe("Podman run", func() {
})
It("podman test selinux label /run/secrets", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-dZ", "/run/secrets"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -144,6 +145,7 @@ var _ = Describe("Podman run", func() {
})
It("podman test selinux --privileged label /run/secrets", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-dZ", "/run/secrets"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go
index 464f9513a..318a7a62d 100644
--- a/test/e2e/run_staticip_test.go
+++ b/test/e2e/run_staticip_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman run with --ip flag", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index e0d2e21b7..b0dc66707 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -47,6 +47,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container based on a complex local image name", func() {
+ SkipIfRootless()
imageName := strings.TrimPrefix(nginx, "quay.io/")
podmanTest.RestoreArtifact(nginx)
session := podmanTest.Podman([]string{"run", imageName, "ls"})
@@ -151,10 +152,10 @@ var _ = Describe("Podman run", func() {
})
It("podman run environment test", func() {
- session := podmanTest.Podman([]string{"run", "--rm", "--env", "FOO=BAR", ALPINE, "printenv", "FOO"})
+ session := podmanTest.Podman([]string{"run", "--rm", "--env", "FOO=BAR,BAZ", ALPINE, "printenv", "FOO"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- match, _ := session.GrepString("BAR")
+ match, _ := session.GrepString("BAR,BAZ")
Expect(match).Should(BeTrue())
session = podmanTest.Podman([]string{"run", "--rm", "--env", "PATH=/bin", ALPINE, "printenv", "PATH"})
@@ -185,6 +186,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run limits test", func() {
+ SkipIfRootless()
podmanTest.RestoreArtifact(fedoraMinimal)
session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "rtprio=99", "--cap-add=sys_nice", fedoraMinimal, "cat", "/proc/self/sched"})
session.WaitWithDefaultTimeout()
@@ -211,6 +213,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with volume flag", func() {
+ SkipIfRootless()
Skip("Skip until we diagnose the regression of volume mounts")
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
@@ -275,6 +278,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run sysctl test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--sysctl", "net.core.somaxconn=65535", ALPINE, "sysctl", "net.core.somaxconn"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -282,6 +286,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run blkio-weight test", func() {
+ SkipIfRootless()
if _, err := os.Stat("/sys/fs/cgroup/blkio/blkio.weight"); os.IsNotExist(err) {
Skip("Kernel does not support blkio.weight")
}
@@ -292,6 +297,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run device-read-bps test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--device-read-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_bps_device"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -299,6 +305,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run device-write-bps test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--device-write-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_bps_device"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -306,6 +313,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run device-read-iops test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--device-read-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_iops_device"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -313,6 +321,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run device-write-iops test", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--device-write-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_iops_device"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -416,6 +425,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with FIPS mode secrets", func() {
+ SkipIfRootless()
fipsFile := "/etc/system-fips"
err = ioutil.WriteFile(fipsFile, []byte{}, 0755)
Expect(err).To(BeNil())
@@ -430,6 +440,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run without group-add", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -437,6 +448,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with group-add", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--group-add=audio", "--group-add=nogroup", "--group-add=777", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -444,6 +456,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with user (default)", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -458,6 +471,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with user (integer, in /etc/passwd)", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--user=8", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -465,6 +479,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run with user (username)", func() {
+ SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--user=mail", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index b67b694b0..c6c94d2f6 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -18,6 +18,7 @@ var _ = Describe("Podman UserNS support", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go
index e95265617..05f24539f 100644
--- a/test/e2e/stats_test.go
+++ b/test/e2e/stats_test.go
@@ -19,6 +19,7 @@ var _ = Describe("Podman stats", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go
index 558635d70..52efc9fca 100644
--- a/test/e2e/systemd_test.go
+++ b/test/e2e/systemd_test.go
@@ -20,6 +20,7 @@ var _ = Describe("Podman systemd", func() {
)
BeforeEach(func() {
+ SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index b66291734..f546158a9 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -36,4 +36,24 @@ var _ = Describe("Podman version", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2))
})
+
+ It("podman version --format json", func() {
+ session := podmanTest.Podman([]string{"version", "--format", "json"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ })
+
+ It("podman version --format json", func() {
+ session := podmanTest.Podman([]string{"version", "--format", "{{ json .}}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ })
+
+ It("podman version --format GO template", func() {
+ session := podmanTest.Podman([]string{"version", "--format", "{{ .Version }}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/system/000-TEMPLATE b/test/system/000-TEMPLATE
index 296ed4d58..85e25e921 100644
--- a/test/system/000-TEMPLATE
+++ b/test/system/000-TEMPLATE
@@ -52,7 +52,7 @@ function teardown() {
@test "podman FOO - description of test" {
# FIXME: please try to remove this line; that is, try to write tests
# that will pass as both root and rootless.
- skip_if_rootless
+ skip_if_rootless "Short explanation of why this doesn't work rootless"
# FIXME: template for run commands. Always use 'run_podman'!
# FIXME: The '?' means 'ignore exit status'; use a number if you
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index b648599f7..8e07b8822 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -46,15 +46,34 @@ function check_help() {
# Confirm that by running with 'invalid-arg' and expecting failure.
if expr "$usage" : '.*\[flags\]$' >/dev/null; then
if [ "$cmd" != "help" ]; then
+ dprint "podman $@ $cmd invalid-arg"
run_podman 125 "$@" $cmd invalid-arg
is "$output" "Error: .* takes no arguments" \
"'podman $@ $cmd' with extra (invalid) arguments"
fi
fi
+ # If usage has required arguments, try running without them
+ if expr "$usage" : '.*\[flags\] [A-Z]' >/dev/null; then
+ dprint "podman $@ $cmd (without required args)"
+ run_podman 125 "$@" $cmd
+ is "$output" "Error:"
+ fi
+
count=$(expr $count + 1)
done
+ # Any command that takes subcommands, must throw error if called
+ # without one.
+ dprint "podman $@"
+ run_podman 125 "$@"
+ is "$output" "Error: missing command .*$@ COMMAND"
+
+ # Assume that 'NoSuchCommand' is not a command
+ dprint "podman $@ NoSuchCommand"
+ run_podman 125 "$@" NoSuchCommand
+ is "$output" "Error: unrecognized command .*$@ NoSuchCommand"
+
# This can happen if the output of --help changes, such as between
# the old command parser and cobra.
[ $count -gt 0 ] || \
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index debec29b6..055865c8d 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -18,7 +18,34 @@ load helpers
is "$output" "$rand_string" "output from podman-start on created ctr"
is "$output" "$rand_string" "logs of started container"
+ run_podman logs $cid
+ is "$output" "$rand_string" "output from podman-logs after container is run"
+
run_podman rm $cid
}
+@test "podman logs - multi" {
+ # Simple helper to make the container starts, below, easier to read
+ local -a cid
+ doit() {
+ run_podman run --rm -d --name "$1" $IMAGE sh -c "$2";
+ cid+=($(echo "${output:0:12}"))
+ }
+
+ # Not really a guarantee that we'll get a-b-c-d in order, but it's
+ # the best we can do. The trailing 'sleep' in each container
+ # minimizes the chance of a race condition in which the container
+ # is removed before 'podman logs' has a chance to wake up and read
+ # the final output.
+ doit c1 "echo a;sleep 10;echo d;sleep 3"
+ doit c2 "sleep 1;echo b;sleep 2;echo c;sleep 3"
+
+ run_podman logs -f c1 c2
+ is "$output" \
+ "${cid[0]} a
+${cid[1]} b
+${cid[1]} c
+${cid[0]} d" "Sequential output from logs"
+}
+
# vim: filetype=sh
diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats
index 3601b7b84..e249b2883 100644
--- a/test/system/060-mount.bats
+++ b/test/system/060-mount.bats
@@ -5,7 +5,7 @@ load helpers
@test "podman mount - basic test" {
# Only works with root (FIXME: does it work with rootless + vfs?)
- skip_if_rootless
+ skip_if_rootless "mount does not work rootless"
f_path=/tmp/tmpfile_$(random_string 8)
f_content=$(random_string 30)
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
new file mode 100644
index 000000000..a12d28b32
--- /dev/null
+++ b/test/system/075-exec.bats
@@ -0,0 +1,30 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# Tests for podman exec
+#
+
+load helpers
+
+@test "podman exec - basic test" {
+ rand_filename=$(random_string 20)
+ rand_content=$(random_string 50)
+
+ # Start a container. Write random content to random file, then stay
+ # alive as long as file exists. (This test will remove that file soon.)
+ run_podman run -d $IMAGE sh -c \
+ "echo $rand_content >/$rand_filename;echo READY;while [ -f /$rand_filename ]; do sleep 1; done"
+ cid="$output"
+ wait_for_ready $cid
+
+ run_podman exec $cid sh -c "cat /$rand_filename"
+ is "$output" "$rand_content" "Can exec and see file in running container"
+
+ run_podman exec $cid rm -f /$rand_filename
+
+ run_podman wait $cid
+ is "$output" "0" "output from podman wait (container exit code)"
+
+ run_podman rm $cid
+}
+
+# vim: filetype=sh
diff --git a/test/system/120-load.bats b/test/system/120-load.bats
new file mode 100644
index 000000000..dedfe6172
--- /dev/null
+++ b/test/system/120-load.bats
@@ -0,0 +1,96 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# tests for podman load
+#
+
+load helpers
+
+# Custom helpers for this test only. These just save us having to duplicate
+# the same thing four times (two tests, each with -i and stdin).
+#
+# initialize, read image ID and name
+get_iid_and_name() {
+ run_podman images --format '{{.ID}} {{.Repository}}:{{.Tag}}'
+ read iid img_name < <(echo "$output")
+
+ archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar
+}
+
+# Simple verification of image ID and name
+verify_iid_and_name() {
+ run_podman images --format '{{.ID}} {{.Repository}}:{{.Tag}}'
+ read new_iid new_img_name < <(echo "$output")
+
+ # Verify
+ is "$new_iid" "$iid" "Image ID of loaded image == original"
+ is "$new_img_name" "$1" "Name & tag of restored image"
+}
+
+
+@test "podman load - by image ID" {
+ # FIXME: how to build a simple archive instead?
+ get_iid_and_name
+
+ # Save image by ID, and remove it.
+ run_podman save $iid -o $archive
+ run_podman rmi $iid
+
+ # Load using -i; IID should be preserved, but name is not.
+ run_podman load -i $archive
+ verify_iid_and_name "<none>:<none>"
+
+ # Same as above, using stdin
+ run_podman rmi $iid
+ run_podman load < $archive
+ verify_iid_and_name "<none>:<none>"
+
+ # Cleanup: since load-by-iid doesn't preserve name, re-tag it;
+ # otherwise our global teardown will rmi and re-pull our standard image.
+ run_podman tag $iid $img_name
+}
+
+@test "podman load - by image name" {
+ get_iid_and_name
+ run_podman save $img_name -o $archive
+ run_podman rmi $iid
+
+ # Load using -i; this time the image should be tagged.
+ run_podman load -i $archive
+ verify_iid_and_name $img_name
+
+ # Same as above, using stdin
+ run_podman rmi $iid
+ run_podman load < $archive
+ verify_iid_and_name $img_name
+}
+
+@test "podman load - NAME and NAME:TAG arguments work (requires: #2674)" {
+ get_iid_and_name
+ run_podman save $iid -o $archive
+ run_podman rmi $iid
+
+ # Load with just a name (note: names must be lower-case)
+ random_name=$(random_string 20 | tr A-Z a-z)
+ run_podman load -i $archive $random_name
+ verify_iid_and_name "localhost/$random_name:latest"
+
+ # Load with NAME:TAG arg
+ run_podman rmi $iid
+ random_tag=$(random_string 10 | tr A-Z a-z)
+ run_podman load -i $archive $random_name:$random_tag
+ verify_iid_and_name "localhost/$random_name:$random_tag"
+
+ # Cleanup: restore desired image name
+ run_podman tag $iid $img_name
+ run_podman rmi "$random_name:$random_tag"
+}
+
+
+@test "podman load - will not read from tty" {
+ run_podman 125 load
+ is "$output" \
+ "Error: cannot read from terminal. Use command-line redirection" \
+ "Diagnostic from 'podman load' without redirection or -i"
+}
+
+# vim: filetype=sh
diff --git a/test/system/200-pod-top.bats b/test/system/200-pod-top.bats
index 81c4be3ff..0200df00d 100644
--- a/test/system/200-pod-top.bats
+++ b/test/system/200-pod-top.bats
@@ -3,9 +3,10 @@
load helpers
@test "podman pod top - containers in different PID namespaces" {
- skip_if_rootless
-
- run_podman pod create
+ # With infra=false, we don't get a /pause container (we also
+ # don't pull k8s.gcr.io/pause )
+ no_infra='--infra=false'
+ run_podman pod create $no_infra
podid="$output"
# Start two containers...
@@ -23,11 +24,14 @@ load helpers
run_podman pod top $podid
is "$output" ".*root.*top -d 2.*root.*top -d 2" "two 'top' containers"
- # There should be a /pause container
+ # By default (podman pod create w/ default --infra) there should be
+ # a /pause container.
# FIXME: sometimes there is, sometimes there isn't. If anyone ever
# actually figures this out, please either reenable this line or
# remove it entirely.
- #is "$output" ".*0 \+1 \+0 \+[0-9. ?s]\+/pause" "there is a /pause container"
+ if [ -z "$no_infra" ]; then
+ is "$output" ".*0 \+1 \+0 \+[0-9. ?s]\+/pause" "there is a /pause container"
+ fi
# Clean up
run_podman pod rm -f $podid
diff --git a/test/system/300-cli-parsing.bats b/test/system/300-cli-parsing.bats
new file mode 100644
index 000000000..92c073102
--- /dev/null
+++ b/test/system/300-cli-parsing.bats
@@ -0,0 +1,15 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# Various command-line parsing regression tests that don't fit in elsewhere
+#
+
+load helpers
+
+@test "podman cli parsing - quoted args - #2574" {
+ # 1.1.2 fails with:
+ # Error: invalid argument "true=\"false\"" for "-l, --label" \
+ # flag: parse error on line 1, column 5: bare " in non-quoted-field
+ run_podman run --rm --label 'true="false"' $IMAGE true
+}
+
+# vim: filetype=sh
diff --git a/test/test_podman_build.sh b/test/test_podman_build.sh
index 9faefc78a..39f1e784d 100644
--- a/test/test_podman_build.sh
+++ b/test/test_podman_build.sh
@@ -34,6 +34,13 @@ echo ########################################################
echo ########################################################
+echo test "build directory before other options create a tag"
+echo ########################################################
+TARGET=tagged-image
+podman build $HOME/test/build/from-scratch --quiet=True -t $TARGET
+podman images | grep tagged-image
+
+echo ########################################################
echo test "build-preserve-subvolumes"
echo ########################################################
TARGET=volume-image