summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml83
-rwxr-xr-xAPI.md2
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Makefile9
-rw-r--r--cmd/podman/common.go17
-rw-r--r--cmd/podman/create.go13
-rw-r--r--cmd/podman/inspect.go42
-rw-r--r--cmd/podman/pod_ps.go3
-rw-r--r--cmd/podman/ps.go5
-rw-r--r--cmd/podman/run.go1
-rw-r--r--cmd/podman/shared/container.go4
-rw-r--r--cmd/podman/shared/container_inspect.go211
-rw-r--r--cmd/podman/shared/create.go72
-rw-r--r--cmd/podman/shared/create_cli.go2
-rw-r--r--cmd/podman/shared/intermediate.go12
-rw-r--r--cmd/podman/shared/parse/parse.go19
-rw-r--r--cmd/podman/stats.go3
-rw-r--r--cmd/podman/system_df.go2
-rw-r--r--cmd/podman/tree.go2
-rw-r--r--cmd/podman/utils.go16
-rw-r--r--cmd/podman/varlink/io.podman.varlink9
-rw-r--r--completions/bash/podman3
-rwxr-xr-xcontrib/cirrus/build_vm_images.sh39
-rwxr-xr-xcontrib/cirrus/check_image.sh10
-rw-r--r--contrib/cirrus/lib.sh27
-rwxr-xr-xcontrib/cirrus/notice_branch_failure.sh (renamed from contrib/cirrus/notice_master_failure.sh)4
-rw-r--r--contrib/cirrus/packer/fedora_base-setup.sh3
-rw-r--r--contrib/cirrus/packer/fedora_setup.sh3
-rw-r--r--contrib/cirrus/packer/ubuntu_setup.sh3
-rwxr-xr-xcontrib/cirrus/setup_environment.sh6
-rw-r--r--contrib/imgprune/Dockerfile7
-rw-r--r--contrib/imgprune/README.md11
-rwxr-xr-xcontrib/imgprune/entrypoint.sh67
-rw-r--r--contrib/imgts/Dockerfile4
-rwxr-xr-xcontrib/imgts/entrypoint.sh47
-rw-r--r--contrib/imgts/lib_entrypoint.sh44
-rw-r--r--dependencies/analyses/README.md88
-rwxr-xr-xdependencies/analyses/dependency-tree.sh17
-rwxr-xr-xdependencies/analyses/go-archive-analysis.sh12
-rwxr-xr-xdependencies/analyses/nm-symbols-analysis.sh9
-rw-r--r--docs/podman-build.1.md14
-rw-r--r--docs/podman-cp.1.md2
-rw-r--r--docs/podman-create.1.md60
-rw-r--r--docs/podman-generate-kube.1.md2
-rw-r--r--docs/podman-generate-systemd.1.md2
-rw-r--r--docs/podman-image-sign.1.md6
-rw-r--r--docs/podman-load.1.md2
-rw-r--r--docs/podman-mount.1.md2
-rw-r--r--docs/podman-play-kube.1.md2
-rw-r--r--docs/podman-pod-create.1.md2
-rw-r--r--docs/podman-pod-stats.1.md2
-rw-r--r--docs/podman-run.1.md63
-rw-r--r--docs/podman-stats.1.md2
-rw-r--r--docs/podman-system-df.1.md2
-rw-r--r--docs/podman-volume-inspect.1.md2
-rw-r--r--go.mod2
-rw-r--r--install.md2
-rw-r--r--libpod/container.go15
-rw-r--r--libpod/container_api.go11
-rw-r--r--libpod/container_graph.go2
-rw-r--r--libpod/container_inspect.go1049
-rw-r--r--libpod/container_internal.go28
-rw-r--r--libpod/events/filters.go5
-rw-r--r--libpod/events/nullout.go3
-rw-r--r--libpod/healthcheck.go23
-rw-r--r--libpod/healthcheck_linux.go2
-rw-r--r--libpod/image/image.go30
-rw-r--r--libpod/image/pull.go10
-rw-r--r--libpod/kube.go3
-rw-r--r--libpod/logs/log.go5
-rw-r--r--libpod/networking_linux.go18
-rw-r--r--libpod/oci.go4
-rw-r--r--libpod/oci_linux.go28
-rw-r--r--libpod/options.go15
-rw-r--r--libpod/runtime.go76
-rw-r--r--libpod/runtime_ctr.go10
-rw-r--r--pkg/adapter/checkpoint_restore.go29
-rw-r--r--pkg/adapter/containers.go4
-rw-r--r--pkg/adapter/containers_remote.go1
-rw-r--r--pkg/adapter/pods.go7
-rw-r--r--pkg/adapter/runtime.go3
-rw-r--r--pkg/adapter/terminal_linux.go6
-rw-r--r--pkg/apparmor/apparmor_linux.go13
-rw-r--r--pkg/apparmor/apparmor_linux_test.go17
-rw-r--r--pkg/apparmor/apparmor_unsupported.go5
-rw-r--r--pkg/cgroups/blkio.go2
-rw-r--r--pkg/cgroups/cgroups.go49
-rw-r--r--pkg/cgroups/cpu.go2
-rw-r--r--pkg/cgroups/cpuset.go3
-rw-r--r--pkg/cgroups/memory.go3
-rw-r--r--pkg/cgroups/pids.go3
-rw-r--r--pkg/namespaces/namespaces.go57
-rw-r--r--pkg/netns/netns_linux.go17
-rw-r--r--pkg/rootless/rootless_linux.go24
-rw-r--r--pkg/spec/config_linux.go41
-rw-r--r--pkg/spec/createconfig.go19
-rw-r--r--pkg/spec/spec.go127
-rw-r--r--pkg/spec/storage.go7
-rw-r--r--pkg/util/utils_linux.go43
-rw-r--r--pkg/util/utils_unsupported.go12
-rw-r--r--pkg/varlinkapi/containers.go78
-rw-r--r--pkg/varlinkapi/transfers.go5
-rw-r--r--pkg/varlinkapi/util.go2
-rw-r--r--rootless.md2
-rw-r--r--test/README.md23
-rw-r--r--test/e2e/common_test.go9
-rw-r--r--test/e2e/healthcheck_run_test.go10
-rw-r--r--test/e2e/play_kube_test.go123
-rw-r--r--test/e2e/pod_rm_test.go17
-rw-r--r--test/e2e/run_device_test.go9
-rw-r--r--test/e2e/run_dns_test.go18
-rw-r--r--test/e2e/run_ns_test.go9
-rw-r--r--test/e2e/run_test.go43
-rw-r--r--vendor/modules.txt2
114 files changed, 2543 insertions, 740 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index dac41dc5f..70c3cb3da 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -13,6 +13,8 @@ env:
####
#### Global variables used for all tasks
####
+ # Name of the ultimate destination branch for this build
+ DEST_BRANCH: "master"
# Overrides default location (/tmp/cirrus) for repo clone
GOPATH: "/var/tmp/go"
GOSRC: "/var/tmp/go/src/github.com/containers/libpod"
@@ -29,9 +31,9 @@ env:
####
#### Cache-image names to test with
###
- FEDORA_CACHE_IMAGE_NAME: "fedora-30-libpod-5081463649730560"
- PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-5081463649730560"
- UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-5081463649730560"
+ FEDORA_CACHE_IMAGE_NAME: "fedora-30-libpod-5925244995371008"
+ PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-5925244995371008"
+ UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-5925244995371008"
####
#### Variables for composing new cache-images (used in PR testing) from
@@ -118,7 +120,7 @@ gating_task:
pipefail_enabledscript: 'if /bin/false | /bin/true; then echo "pipefail fault" && exit 72; fi'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
# This task runs `make vendor` followed by ./hack/tree_status.sh to check
@@ -150,7 +152,7 @@ vendor_task:
- 'cd ${GOSRC} && ./hack/tree_status.sh |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh |& ${TIMESTAMP}'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh |& ${TIMESTAMP}'
# This task runs `make varlink_api_generate` followed by ./hack/tree_status.sh to check
@@ -182,7 +184,7 @@ varlink_api_task:
- 'cd ${GOSRC} && ./hack/tree_status.sh |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
build_each_commit_task:
@@ -193,7 +195,7 @@ build_each_commit_task:
- "varlink_api"
# $CIRRUS_BASE_BRANCH is only set when testing a PR
- only_if: $CIRRUS_BRANCH != 'master' &&
+ only_if: $CIRRUS_BRANCH != $DEST_BRANCH &&
$CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*'
gce_instance:
@@ -213,9 +215,10 @@ build_each_commit_task:
- 'make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
-build_without_cgo:
+
+build_without_cgo_task:
depends_on:
- "gating"
@@ -223,7 +226,7 @@ build_without_cgo:
- "varlink_api"
# $CIRRUS_BASE_BRANCH is only set when testing a PR
- only_if: $CIRRUS_BRANCH != 'master' &&
+ only_if: $CIRRUS_BRANCH != $DEST_BRANCH &&
$CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*'
gce_instance:
@@ -242,7 +245,7 @@ build_without_cgo:
- 'make build-no-cgo'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
# Update metadata on VM images referenced by this repository state
@@ -260,7 +263,7 @@ meta_task:
cpu: 1
memory: 1
- env:
+ env: &meta_env_vars
# Space-separated list of images used by this repository state
IMGNAMES: >-
${FEDORA_CACHE_IMAGE_NAME}
@@ -276,6 +279,31 @@ meta_task:
timeout_in: 10m
+ # Cirrus-CI ignores entrypoint defined in image
+ script: '/usr/local/bin/entrypoint.sh |& ${TIMESTAMP}'
+
+
+# Remove old and disused images based on labels set by meta_task
+image_prune_task:
+
+ # Do not run this frequently
+ only_if: $CIRRUS_BRANCH == 'master'
+
+ depends_on:
+ - "meta"
+
+ container:
+ image: "quay.io/libpod/imgprune:latest" # see contrib/imgprune
+ cpu: 1
+ memory: 1
+
+ env:
+ <<: *meta_env_vars
+ GCPJSON: ENCRYPTED[4c11d8e09c904c30fc70eecb95c73dec0ddf19976f9b981a0f80f3f6599e8f990bcef93c253ac0277f200850d98528e7]
+ GCPNAME: ENCRYPTED[7f54557ba6e5a437f11283a53e71baec9ca546f48a9835538cc54d297f79968eb1337d4596a1025b14f9d1c5723fbd29]
+
+ timeout_in: 10m
+
script: '/usr/local/bin/entrypoint.sh |& ${TIMESTAMP}'
@@ -315,7 +343,7 @@ testing_task:
$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
always: &standardlogs
ginkgo_node_logs_script: '$SCRIPT_BASE/logcollector.sh ginkgo'
@@ -350,7 +378,7 @@ special_testing_rootless_task:
system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
always:
<<: *standardlogs
@@ -376,7 +404,7 @@ special_testing_in_podman_task:
integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
always:
<<: *standardlogs
@@ -402,14 +430,14 @@ special_testing_cross_task:
cache_release_archive_script: '$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}'
on_failure:
- failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
# Test building of new cache-images for future PR testing, in this PR.
test_build_cache_images_task:
only_if: >-
- $CIRRUS_BRANCH != 'master' &&
+ $CIRRUS_BRANCH != $DEST_BRANCH &&
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' &&
$CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*'
@@ -444,7 +472,7 @@ test_build_cache_images_task:
verify_test_built_images_task:
only_if: >-
- $CIRRUS_BRANCH != 'master' &&
+ $CIRRUS_BRANCH != $DEST_BRANCH &&
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' &&
$CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*'
@@ -471,21 +499,25 @@ verify_test_built_images_task:
environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}'
check_image_script: '$SCRIPT_BASE/check_image.sh'
integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}'
+ system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}'
always:
<<: *standardlogs
-
-# Post message to IRC if everything passed
+# Post message to IRC if everything passed PR testing
success_task:
- only_if: $CIRRUS_BRANCH != 'master'
+ # This task is a required-pass in github settings,
+ # it blocks PRs from merging if a depends_on task fails
+ only_if: $CIRRUS_BRANCH != $DEST_BRANCH
- depends_on: # ignores any dependent task conditions
+ # ignores any dependent task conditions, include everything except 'release'
+ depends_on: &alltasks
- "gating"
- "vendor"
- "varlink_api"
- "build_each_commit"
+ - "build_without_cgo"
- "meta"
- "testing"
- "special_testing_rootless"
@@ -493,7 +525,6 @@ success_task:
- "special_testing_cross"
- "test_build_cache_images"
- "verify_test_built_images"
- - "build_without_cgo"
env:
CIRRUS_WORKING_DIR: "/usr/src/libpod"
@@ -514,8 +545,7 @@ release_task:
# allow_failures: $CI == "true"
# skip_notifications: $CI == "true"
- depends_on:
- - "success"
+ depends_on: *alltasks
gce_instance:
image_name: "${IMAGE_BUILDER_CACHE_IMAGE_NAME}"
@@ -529,3 +559,6 @@ release_task:
GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f]
uncache_release_archives_script: '$SCRIPT_BASE/uncache_release_archives.sh |& ${TIMESTAMP}'
+
+ on_failure:
+ failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh'
diff --git a/API.md b/API.md
index cfe050343..febd094df 100755
--- a/API.md
+++ b/API.md
@@ -1894,6 +1894,8 @@ pod [?bool](#?bool)
quiet [?bool](#?bool)
+size [?bool](#?bool)
+
sort [?string](#?string)
sync [?bool](#?bool)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 59b0a88da..07b2b3584 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -376,7 +376,7 @@ author hold special privileges on the github repository. Others can be used by
will cause Cirrus CI to ***NOT*** execute tests for the PR or after merge. This
is useful in only one instance: Your changes are absolutely not exercised by
any test. For example, documentation changes. ***IMPORTANT NOTE*** **Other
- automation may interpret the lack of test results as "PASSED" and unintentionall
+ automation may interpret the lack of test results as "PASSED" and unintentional
merge a PR. Consider also using `/hold` in a comment, to add additional
protection.**
diff --git a/Makefile b/Makefile
index 140b2e149..e155d31d4 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,7 @@ SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers
ETCDIR ?= /etc
TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d
SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system
+BUILDFLAGS ?=
BUILDTAGS ?= \
$(shell hack/apparmor_tag.sh) \
$(shell hack/btrfs_installed_tag.sh) \
@@ -126,8 +127,8 @@ help:
.gopathok:
ifeq ("$(wildcard $(GOPKGDIR))","")
mkdir -p "$(GOPKGBASEDIR)"
- ln -sf "$(CURDIR)" "$(GOPKGBASEDIR)"
- ln -sf "$(CURDIR)/vendor/github.com/varlink" "$(FIRST_GOPATH)/src/github.com/varlink"
+ ln -sfnT "$(CURDIR)" "$(GOPKGDIR)"
+ ln -sfnT "$(CURDIR)/vendor/github.com/varlink" "$(FIRST_GOPATH)/src/github.com/varlink"
endif
touch $@
@@ -147,10 +148,10 @@ test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -o $@ $(PROJECT)/test/goecho
podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman
- $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman
+ $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman
podman-remote: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote environment
- $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman
+ $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman
podman-remote-darwin: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote OSX environment
CGO_ENABLED=0 GOOS=darwin $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@ $(PROJECT)/cmd/podman
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 50f3d9a7b..1e9092bd6 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -130,6 +130,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"Drop capabilities from the container",
)
createFlags.String(
+ "cgroupns", "host",
+ "cgroup namespace to use",
+ )
+ createFlags.String(
"cgroup-parent", "",
"Optional parent cgroup for the container",
)
@@ -221,6 +225,9 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"env", "e", []string{},
"Set environment variables in container",
)
+ createFlags.Bool(
+ "env-host", false, "Use all current host environment variables in container",
+ )
createFlags.StringSlice(
"env-file", []string{},
"Read in a file of environment variables",
@@ -241,23 +248,23 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"help", false, "",
)
createFlags.String(
- "healthcheck-command", "",
+ "health-cmd", "",
"set a healthcheck command for the container ('none' disables the existing healthcheck)",
)
createFlags.String(
- "healthcheck-interval", cliconfig.DefaultHealthCheckInterval,
+ "health-interval", cliconfig.DefaultHealthCheckInterval,
"set an interval for the healthchecks (a value of disable results in no automatic timer setup)",
)
createFlags.Uint(
- "healthcheck-retries", cliconfig.DefaultHealthCheckRetries,
+ "health-retries", cliconfig.DefaultHealthCheckRetries,
"the number of retries allowed before a healthcheck is considered to be unhealthy",
)
createFlags.String(
- "healthcheck-start-period", cliconfig.DefaultHealthCheckStartPeriod,
+ "health-start-period", cliconfig.DefaultHealthCheckStartPeriod,
"the initialization time needed for a container to bootstrap",
)
createFlags.String(
- "healthcheck-timeout", cliconfig.DefaultHealthCheckTimeout,
+ "health-timeout", cliconfig.DefaultHealthCheckTimeout,
"the maximum time allowed to complete the healthcheck before an interval is considered failed",
)
createFlags.StringP(
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 93141a800..3c24729c5 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
@@ -40,7 +41,7 @@ func init() {
getCreateFlags(&createCommand.PodmanCommand)
flags := createCommand.Flags()
flags.SetInterspersed(false)
-
+ flags.SetNormalizeFunc(aliasFlags)
}
func createCmd(c *cliconfig.CreateValues) error {
@@ -77,6 +78,16 @@ func createInit(c *cliconfig.PodmanCommand) error {
logrus.Warn("setting security options with --privileged has no effect")
}
+ var setNet string
+ if c.IsSet("network") {
+ setNet = c.String("network")
+ } else if c.IsSet("net") {
+ setNet = c.String("net")
+ }
+ if (c.IsSet("dns") || c.IsSet("dns-opt") || c.IsSet("dns-search")) && (setNet == "none" || strings.HasPrefix(setNet, "container:")) {
+ return errors.Errorf("conflicting options: dns and the network mode.")
+ }
+
// Docker-compatibility: the "-h" flag for run/create is reserved for
// the hostname (see https://github.com/containers/libpod/issues/1367).
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index df597c868..12d89764c 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -6,9 +6,7 @@ import (
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/pkg/adapter"
- cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -148,19 +146,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
inspectError = errors.Wrapf(err, "error looking up container %q", input)
break
}
- libpodInspectData, err := ctr.Inspect(size)
+ data, err = ctr.Inspect(size)
if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
- break
- }
- artifact, err := getArtifact(ctr)
- if inspectError != nil {
- inspectError = err
- break
- }
- data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
- if err != nil {
- inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID())
+ inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID())
break
}
case inspectTypeImage:
@@ -188,19 +176,9 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
break
}
} else {
- libpodInspectData, err := ctr.Inspect(size)
- if err != nil {
- inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
- break
- }
- artifact, err := getArtifact(ctr)
+ data, err = ctr.Inspect(size)
if err != nil {
- inspectError = err
- break
- }
- data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
- if err != nil {
- inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
+ inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID())
break
}
}
@@ -211,15 +189,3 @@ func iterateInput(ctx context.Context, size bool, args []string, runtime *adapte
}
return inspectedItems, inspectError
}
-
-func getArtifact(ctr *adapter.Container) (*cc.CreateConfig, error) {
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err != nil {
- return nil, err
- }
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- return &createArtifact, nil
-}
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index a525857de..fd8da53fb 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -552,9 +552,6 @@ func generatePodPsOutput(pods []*adapter.Pod, opts podPsOptions) error {
switch opts.Format {
case formats.JSONString:
- if err != nil {
- return errors.Wrapf(err, "unable to create JSON for output")
- }
out = formats.JSONStructArray{Output: podPsToGeneric([]podPsTemplateParams{}, psOutput)}
default:
psOutput, err := getPodTemplateOutput(psOutput, opts)
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 26cc55e5f..9fad0ea65 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -319,6 +319,9 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error {
}
pss, err := runtime.Ps(c, opts)
+ if err != nil {
+ return err
+ }
// Here and down
if opts.Sort != "" {
pss, err = sortPsOutput(opts.Sort, pss)
@@ -376,8 +379,8 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error {
size = units.HumanSizeWithPrecision(0, 0)
} else {
size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")"
- fmt.Fprintf(w, "\t%s", size)
}
+ fmt.Fprintf(w, "\t%s", size)
}
} else {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 76ab3d944..4836c99dc 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -34,6 +34,7 @@ func init() {
runCommand.SetUsageTemplate(UsageTemplate())
flags := runCommand.Flags()
flags.SetInterspersed(false)
+ flags.SetNormalizeFunc(aliasFlags)
flags.Bool("sig-proxy", true, "Proxy received signals to the process")
getCreateFlags(&runCommand.PodmanCommand)
markFlagHiddenForRemoteClient("authfile", flags)
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index df4583be6..3c68a29b4 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -305,7 +305,7 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime)
}
return func(c *libpod.Container) bool {
ec, exited, err := c.ExitCode()
- if ec == int32(exitCode) && err == nil && exited == true {
+ if ec == int32(exitCode) && err == nil && exited {
return true
}
return false
@@ -611,7 +611,7 @@ func getNamespaceInfo(path string) (string, error) {
// getStrFromSquareBrackets gets the string inside [] from a string
func getStrFromSquareBrackets(cmd string) string {
- reg, err := regexp.Compile(".*\\[|\\].*")
+ reg, err := regexp.Compile(`.*\[|\].*`)
if err != nil {
return ""
}
diff --git a/cmd/podman/shared/container_inspect.go b/cmd/podman/shared/container_inspect.go
deleted file mode 100644
index a8094466e..000000000
--- a/cmd/podman/shared/container_inspect.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package shared
-
-import (
- "github.com/containers/libpod/libpod"
- cc "github.com/containers/libpod/pkg/spec"
- "github.com/docker/go-connections/nat"
- "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// InspectContainer holds all inspect data for a container.
-// The format of individual components is fixed so the overall structure, when
-// JSON encoded, matches the output of `docker inspect`.
-// It combines Libpod-source inspect data with Podman-specific inspect data.
-type InspectContainer struct {
- *libpod.InspectContainerData
- HostConfig *InspectContainerHostConfig `json:"HostConfig"`
-}
-
-// InspectContainerHostConfig holds Container configuration that is not specific
-// to Libpod. This information is (mostly) stored by Podman as an artifact.
-// This struct is matched to the output of `docker inspect`.
-type InspectContainerHostConfig struct {
- ContainerIDFile string `json:"ContainerIDFile"`
- LogConfig *InspectLogConfig `json:"LogConfig"` //TODO
- NetworkMode string `json:"NetworkMode"`
- PortBindings nat.PortMap `json:"PortBindings"` //TODO
- AutoRemove bool `json:"AutoRemove"`
- CapAdd []string `json:"CapAdd"`
- CapDrop []string `json:"CapDrop"`
- DNS []string `json:"DNS"`
- DNSOptions []string `json:"DNSOptions"`
- DNSSearch []string `json:"DNSSearch"`
- ExtraHosts []string `json:"ExtraHosts"`
- GroupAdd []uint32 `json:"GroupAdd"`
- IpcMode string `json:"IpcMode"`
- Cgroup string `json:"Cgroup"`
- OomScoreAdj *int `json:"OomScoreAdj"`
- PidMode string `json:"PidMode"`
- Privileged bool `json:"Privileged"`
- PublishAllPorts bool `json:"PublishAllPorts"` //TODO
- ReadOnlyRootfs bool `json:"ReadonlyRootfs"`
- ReadOnlyTmpfs bool `json:"ReadonlyTmpfs"`
- SecurityOpt []string `json:"SecurityOpt"`
- UTSMode string `json:"UTSMode"`
- UsernsMode string `json:"UsernsMode"`
- ShmSize int64 `json:"ShmSize"`
- Runtime string `json:"Runtime"`
- ConsoleSize *specs.Box `json:"ConsoleSize"`
- CPUShares *uint64 `json:"CpuShares"`
- Memory int64 `json:"Memory"`
- NanoCPUs int `json:"NanoCpus"`
- CgroupParent string `json:"CgroupParent"`
- BlkioWeight *uint16 `json:"BlkioWeight"`
- BlkioWeightDevice []specs.LinuxWeightDevice `json:"BlkioWeightDevice"`
- BlkioDeviceReadBps []specs.LinuxThrottleDevice `json:"BlkioDeviceReadBps"`
- BlkioDeviceWriteBps []specs.LinuxThrottleDevice `json:"BlkioDeviceWriteBps"`
- BlkioDeviceReadIOps []specs.LinuxThrottleDevice `json:"BlkioDeviceReadIOps"`
- BlkioDeviceWriteIOps []specs.LinuxThrottleDevice `json:"BlkioDeviceWriteIOps"`
- CPUPeriod *uint64 `json:"CpuPeriod"`
- CPUQuota *int64 `json:"CpuQuota"`
- CPURealtimePeriod *uint64 `json:"CpuRealtimePeriod"`
- CPURealtimeRuntime *int64 `json:"CpuRealtimeRuntime"`
- CPUSetCPUs string `json:"CpuSetCpus"`
- CPUSetMems string `json:"CpuSetMems"`
- Devices []specs.LinuxDevice `json:"Devices"`
- DiskQuota int `json:"DiskQuota"` //check type, TODO
- KernelMemory *int64 `json:"KernelMemory"`
- MemoryReservation *int64 `json:"MemoryReservation"`
- MemorySwap *int64 `json:"MemorySwap"`
- MemorySwappiness *uint64 `json:"MemorySwappiness"`
- OomKillDisable *bool `json:"OomKillDisable"`
- PidsLimit *int64 `json:"PidsLimit"`
- Ulimits []string `json:"Ulimits"`
- CPUCount int `json:"CpuCount"`
- CPUPercent int `json:"CpuPercent"`
- IOMaximumIOps int `json:"IOMaximumIOps"` //check type, TODO
- IOMaximumBandwidth int `json:"IOMaximumBandwidth"` //check type, TODO
- Tmpfs []string `json:"Tmpfs"`
-}
-
-// InspectLogConfig holds information about a container's configured log driver
-// and is presently unused. It is retained for Docker compatibility.
-type InspectLogConfig struct {
- Type string `json:"Type"`
- Config map[string]string `json:"Config"` //idk type, TODO
-}
-
-// GetCtrInspectInfo inspects a container, combining Libpod inspect information
-// with other information not stored in Libpod and returning a struct that, when
-// formatted for JSON output, is compatible with `docker inspect`.
-func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *libpod.InspectContainerData, createArtifact *cc.CreateConfig) (*InspectContainer, error) {
- spec := config.Spec
-
- cpus, mems, period, quota, realtimePeriod, realtimeRuntime, shares := getCPUInfo(spec)
- blkioWeight, blkioWeightDevice, blkioReadBps, blkioWriteBps, blkioReadIOPS, blkioeWriteIOPS := getBLKIOInfo(spec)
- memKernel, memReservation, memSwap, memSwappiness, memDisableOOMKiller := getMemoryInfo(spec)
- pidsLimit := getPidsInfo(spec)
- cgroup := getCgroup(spec)
- logConfig := InspectLogConfig{
- config.LogDriver,
- make(map[string]string),
- }
-
- data := &InspectContainer{
- ctrInspectData,
- &InspectContainerHostConfig{
- ConsoleSize: spec.Process.ConsoleSize,
- OomScoreAdj: spec.Process.OOMScoreAdj,
- CPUShares: shares,
- BlkioWeight: blkioWeight,
- BlkioWeightDevice: blkioWeightDevice,
- BlkioDeviceReadBps: blkioReadBps,
- BlkioDeviceWriteBps: blkioWriteBps,
- BlkioDeviceReadIOps: blkioReadIOPS,
- BlkioDeviceWriteIOps: blkioeWriteIOPS,
- CPUPeriod: period,
- CPUQuota: quota,
- CPURealtimePeriod: realtimePeriod,
- CPURealtimeRuntime: realtimeRuntime,
- CPUSetCPUs: cpus,
- CPUSetMems: mems,
- Devices: spec.Linux.Devices,
- KernelMemory: memKernel,
- LogConfig: &logConfig,
- MemoryReservation: memReservation,
- MemorySwap: memSwap,
- MemorySwappiness: memSwappiness,
- OomKillDisable: memDisableOOMKiller,
- PidsLimit: pidsLimit,
- Privileged: config.Privileged,
- ReadOnlyRootfs: spec.Root.Readonly,
- ReadOnlyTmpfs: createArtifact.ReadOnlyTmpfs,
- Runtime: config.OCIRuntime,
- NetworkMode: string(createArtifact.NetMode),
- IpcMode: string(createArtifact.IpcMode),
- Cgroup: cgroup,
- UTSMode: string(createArtifact.UtsMode),
- UsernsMode: string(createArtifact.UsernsMode),
- GroupAdd: spec.Process.User.AdditionalGids,
- ContainerIDFile: createArtifact.CidFile,
- AutoRemove: createArtifact.Rm,
- CapAdd: createArtifact.CapAdd,
- CapDrop: createArtifact.CapDrop,
- DNS: createArtifact.DNSServers,
- DNSOptions: createArtifact.DNSOpt,
- DNSSearch: createArtifact.DNSSearch,
- PidMode: string(createArtifact.PidMode),
- CgroupParent: createArtifact.CgroupParent,
- ShmSize: createArtifact.Resources.ShmSize,
- Memory: createArtifact.Resources.Memory,
- Ulimits: createArtifact.Resources.Ulimit,
- SecurityOpt: createArtifact.SecurityOpts,
- Tmpfs: createArtifact.Tmpfs,
- },
- }
- return data, nil
-}
-
-func getCPUInfo(spec *specs.Spec) (string, string, *uint64, *int64, *uint64, *int64, *uint64) {
- if spec.Linux.Resources == nil {
- return "", "", nil, nil, nil, nil, nil
- }
- cpu := spec.Linux.Resources.CPU
- if cpu == nil {
- return "", "", nil, nil, nil, nil, nil
- }
- return cpu.Cpus, cpu.Mems, cpu.Period, cpu.Quota, cpu.RealtimePeriod, cpu.RealtimeRuntime, cpu.Shares
-}
-
-func getBLKIOInfo(spec *specs.Spec) (*uint16, []specs.LinuxWeightDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice, []specs.LinuxThrottleDevice) {
- if spec.Linux.Resources == nil {
- return nil, nil, nil, nil, nil, nil
- }
- blkio := spec.Linux.Resources.BlockIO
- if blkio == nil {
- return nil, nil, nil, nil, nil, nil
- }
- return blkio.Weight, blkio.WeightDevice, blkio.ThrottleReadBpsDevice, blkio.ThrottleWriteBpsDevice, blkio.ThrottleReadIOPSDevice, blkio.ThrottleWriteIOPSDevice
-}
-
-func getMemoryInfo(spec *specs.Spec) (*int64, *int64, *int64, *uint64, *bool) {
- if spec.Linux.Resources == nil {
- return nil, nil, nil, nil, nil
- }
- memory := spec.Linux.Resources.Memory
- if memory == nil {
- return nil, nil, nil, nil, nil
- }
- return memory.Kernel, memory.Reservation, memory.Swap, memory.Swappiness, memory.DisableOOMKiller
-}
-
-func getPidsInfo(spec *specs.Spec) *int64 {
- if spec.Linux.Resources == nil {
- return nil
- }
- pids := spec.Linux.Resources.Pids
- if pids == nil {
- return nil
- }
- return &pids.Limit
-}
-
-func getCgroup(spec *specs.Spec) string {
- cgroup := "host"
- for _, ns := range spec.Linux.Namespaces {
- if ns.Type == specs.CgroupNamespace && ns.Path != "" {
- cgroup = "container"
- }
- }
- return cgroup
-}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index f401d3cf5..b14ce431d 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -26,7 +26,6 @@ import (
"github.com/docker/docker/pkg/signal"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
- "github.com/google/shlex"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
@@ -93,9 +92,8 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
imageName = newImage.ID()
}
- var healthCheckCommandInput string
// if the user disabled the healthcheck with "none", we skip adding it
- healthCheckCommandInput = c.String("healthcheck-command")
+ healthCheckCommandInput := c.String("healthcheck-command")
// the user didnt disable the healthcheck but did pass in a healthcheck command
// now we need to make a healthcheck from the commandline input
@@ -117,6 +115,30 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0])
}
+
+ if healthCheck != nil {
+ hcCommand := healthCheck.Test
+ if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" {
+ // disable health check
+ healthCheck = nil
+ } else {
+ // apply defaults if image doesn't override them
+ if healthCheck.Interval == 0 {
+ healthCheck.Interval = 30 * time.Second
+ }
+ if healthCheck.Timeout == 0 {
+ healthCheck.Timeout = 30 * time.Second
+ }
+ /* Docker default is 0s, so the following would be a no-op
+ if healthCheck.StartPeriod == 0 {
+ healthCheck.StartPeriod = 0 * time.Second
+ }
+ */
+ if healthCheck.Retries == 0 {
+ healthCheck.Retries = 3
+ }
+ }
+ }
}
}
}
@@ -378,11 +400,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
namespaceNet = c.String("net")
}
namespaces = map[string]string{
- "pid": c.String("pid"),
- "net": namespaceNet,
- "ipc": c.String("ipc"),
- "user": c.String("userns"),
- "uts": c.String("uts"),
+ "cgroup": c.String("cgroupns"),
+ "pid": c.String("pid"),
+ "net": namespaceNet,
+ "ipc": c.String("ipc"),
+ "user": c.String("userns"),
+ "uts": c.String("uts"),
}
originalPodName := c.String("pod")
@@ -440,6 +463,11 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
return nil, errors.Errorf("--uts %q is not valid", namespaces["uts"])
}
+ cgroupMode := ns.CgroupMode(namespaces["cgroup"])
+ if !cgroupMode.Valid() {
+ return nil, errors.Errorf("--cgroup %q is not valid", namespaces["cgroup"])
+ }
+
ipcMode := ns.IpcMode(namespaces["ipc"])
if !cc.Valid(string(ipcMode), ipcMode) {
return nil, errors.Errorf("--ipc %q is not valid", ipcMode)
@@ -483,6 +511,16 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
// ENVIRONMENT VARIABLES
env := EnvVariablesFromData(data)
+ if c.Bool("env-host") {
+ for _, e := range os.Environ() {
+ pair := strings.SplitN(e, "=", 2)
+ if _, ok := env[pair[0]]; !ok {
+ if len(pair) > 1 {
+ env[pair[0]] = pair[1]
+ }
+ }
+ }
+ }
if err := parse.ReadKVStrings(env, c.StringSlice("env-file"), c.StringArray("env")); err != nil {
return nil, errors.Wrapf(err, "unable to process environment variables")
}
@@ -619,6 +657,8 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
ImageVolumeType: c.String("image-volume"),
CapAdd: c.StringSlice("cap-add"),
CapDrop: c.StringSlice("cap-drop"),
+ CidFile: c.String("cidfile"),
+ Cgroupns: c.String("cgroupns"),
CgroupParent: c.String("cgroup-parent"),
Command: command,
Detach: c.Bool("detach"),
@@ -654,6 +694,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
NetMode: netMode,
UtsMode: utsMode,
PidMode: pidMode,
+ CgroupMode: cgroupMode,
Pod: podName,
Privileged: c.Bool("privileged"),
Publish: c.StringSlice("publish"),
@@ -734,14 +775,6 @@ func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateC
if err != nil {
return nil, err
}
-
- createConfigJSON, err := json.Marshal(createConfig)
- if err != nil {
- return nil, err
- }
- if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
- return nil, err
- }
return ctr, nil
}
@@ -779,9 +812,12 @@ func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig
return nil, errors.New("Must define a healthcheck command for all healthchecks")
}
- cmd, err := shlex.Split(inCommand)
+ // first try to parse option value as JSON array of strings...
+ cmd := []string{}
+ err := json.Unmarshal([]byte(inCommand), &cmd)
if err != nil {
- return nil, errors.Wrap(err, "failed to parse healthcheck command")
+ // ...otherwise pass it to "/bin/sh -c" inside the container
+ cmd = []string{"CMD-SHELL", inCommand}
}
hc := manifest.Schema2HealthConfig{
Test: cmd,
diff --git a/cmd/podman/shared/create_cli.go b/cmd/podman/shared/create_cli.go
index 4bfef8b62..08a40b206 100644
--- a/cmd/podman/shared/create_cli.go
+++ b/cmd/podman/shared/create_cli.go
@@ -133,7 +133,7 @@ func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, e
if config.Resources.KernelMemory > 0 && config.Resources.KernelMemory < linuxMinMemory {
return warnings, fmt.Errorf("minimum kernel memory limit allowed is 4MB")
}
- if config.Resources.DisableOomKiller == true && !sysInfo.OomKillDisable {
+ if config.Resources.DisableOomKiller && !sysInfo.OomKillDisable {
// only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
// warning the caller if they already wanted the feature to be off
warnings = addWarning(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index eecd1604c..4062ac48a 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -370,6 +370,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["blkio-weight-device"] = newCRStringSlice(c, "blkio-weight-device")
m["cap-add"] = newCRStringSlice(c, "cap-add")
m["cap-drop"] = newCRStringSlice(c, "cap-drop")
+ m["cgroupns"] = newCRString(c, "cgroupns")
m["cgroup-parent"] = newCRString(c, "cgroup-parent")
m["cidfile"] = newCRString(c, "cidfile")
m["conmon-pidfile"] = newCRString(c, "conmon-pidfile")
@@ -393,16 +394,17 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["dns-search"] = newCRStringSlice(c, "dns-search")
m["entrypoint"] = newCRString(c, "entrypoint")
m["env"] = newCRStringArray(c, "env")
+ m["env-host"] = newCRBool(c, "env-host")
m["env-file"] = newCRStringSlice(c, "env-file")
m["expose"] = newCRStringSlice(c, "expose")
m["gidmap"] = newCRStringSlice(c, "gidmap")
m["group-add"] = newCRStringSlice(c, "group-add")
m["help"] = newCRBool(c, "help")
- m["healthcheck-command"] = newCRString(c, "healthcheck-command")
- m["healthcheck-interval"] = newCRString(c, "healthcheck-interval")
- m["healthcheck-retries"] = newCRUint(c, "healthcheck-retries")
- m["healthcheck-start-period"] = newCRString(c, "healthcheck-start-period")
- m["healthcheck-timeout"] = newCRString(c, "healthcheck-timeout")
+ m["healthcheck-command"] = newCRString(c, "health-cmd")
+ m["healthcheck-interval"] = newCRString(c, "health-interval")
+ m["healthcheck-retries"] = newCRUint(c, "health-retries")
+ m["healthcheck-start-period"] = newCRString(c, "health-start-period")
+ m["healthcheck-timeout"] = newCRString(c, "health-timeout")
m["hostname"] = newCRString(c, "hostname")
m["http-proxy"] = newCRBool(c, "http-proxy")
m["image-volume"] = newCRString(c, "image-volume")
diff --git a/cmd/podman/shared/parse/parse.go b/cmd/podman/shared/parse/parse.go
index 7bc2652cb..a77002235 100644
--- a/cmd/podman/shared/parse/parse.go
+++ b/cmd/podman/shared/parse/parse.go
@@ -112,9 +112,22 @@ func parseEnv(env map[string]string, line string) error {
if len(data) > 1 {
env[name] = data[1]
} else {
- // if only a pass-through variable is given, clean it up.
- val, _ := os.LookupEnv(name)
- env[name] = val
+ if strings.HasSuffix(name, "*") {
+ name = strings.TrimSuffix(name, "*")
+ for _, e := range os.Environ() {
+ part := strings.SplitN(e, "=", 2)
+ if len(part) < 2 {
+ continue
+ }
+ if strings.HasPrefix(part[0], name) {
+ env[part[0]] = part[1]
+ }
+ }
+ } else {
+ // if only a pass-through variable is given, clean it up.
+ val, _ := os.LookupEnv(name)
+ env[name] = val
+ }
}
return nil
}
diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go
index a1ec20b37..05e30f95f 100644
--- a/cmd/podman/stats.go
+++ b/cmd/podman/stats.go
@@ -101,9 +101,8 @@ func statsCmd(c *cliconfig.StatsValues) error {
}
var ctrs []*libpod.Container
- var containerFunc func() ([]*libpod.Container, error)
- containerFunc = runtime.GetRunningContainers
+ containerFunc := runtime.GetRunningContainers
if len(c.InputArgs) > 0 {
containerFunc = func() ([]*libpod.Container, error) { return runtime.GetContainersByList(c.InputArgs) }
} else if latest {
diff --git a/cmd/podman/system_df.go b/cmd/podman/system_df.go
index 85554bf05..5b5655dc9 100644
--- a/cmd/podman/system_df.go
+++ b/cmd/podman/system_df.go
@@ -546,7 +546,7 @@ func imagesVerboseOutput(ctx context.Context, metaData dfMetaData) error {
"Created": "CREATED",
"Size": "SIZE",
"SharedSize": "SHARED SIZE",
- "UniqueSize": "UNQUE SIZE",
+ "UniqueSize": "UNIQUE SIZE",
"Containers": "CONTAINERS",
}
imagesVerboseDiskUsage, err := getImageVerboseDiskUsage(ctx, metaData.images, metaData.imagesUsedbyCtrMap)
diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go
index c13dffd6e..904a0d375 100644
--- a/cmd/podman/tree.go
+++ b/cmd/podman/tree.go
@@ -107,7 +107,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr
if !ok {
return fmt.Errorf("lookup error: layerid %s, not found", layerID)
}
- fmt.Printf(prefix)
+ fmt.Print(prefix)
//initialize intend with middleItem to reduce middleItem checks.
intend := middleItem
diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go
index 0790f673a..c0ddaba4e 100644
--- a/cmd/podman/utils.go
+++ b/cmd/podman/utils.go
@@ -47,3 +47,19 @@ func markFlagHidden(flags *pflag.FlagSet, flag string) {
logrus.Errorf("unable to mark flag '%s' as hidden: %q", flag, err)
}
}
+
+func aliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ switch name {
+ case "healthcheck-command":
+ name = "health-cmd"
+ case "healthcheck-interval":
+ name = "health-interval"
+ case "healthcheck-retries":
+ name = "health-retries"
+ case "healthcheck-start-period":
+ name = "health-start-period"
+ case "healthcheck-timeout":
+ name = "health-timeout"
+ }
+ return pflag.NormalizedName(name)
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 9410b9459..0bf236b77 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -144,10 +144,11 @@ type PsOpts (
last: ?int,
latest: ?bool,
noTrunc: ?bool,
- pod: ?bool,
- quiet: ?bool,
- sort: ?string,
- sync: ?bool
+ pod: ?bool,
+ quiet: ?bool,
+ size: ?bool,
+ sort: ?string,
+ sync: ?bool
)
type PsContainer (
diff --git a/completions/bash/podman b/completions/bash/podman
index 619c9e1e6..969f1054e 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1742,6 +1742,7 @@ _podman_container_run() {
--dns-search
--entrypoint
--env -e
+ --env-host
--env-file
--expose
--gidmap
@@ -1813,10 +1814,10 @@ _podman_container_run() {
--health-interval
--health-retries
--health-timeout
+ --health-start-period
"
boolean_options="$boolean_options
--detach -d
- --no-healthcheck
--rm
--sig-proxy=false
"
diff --git a/contrib/cirrus/build_vm_images.sh b/contrib/cirrus/build_vm_images.sh
index f5d53a92e..74b10158c 100755
--- a/contrib/cirrus/build_vm_images.sh
+++ b/contrib/cirrus/build_vm_images.sh
@@ -3,7 +3,8 @@
set -e
source $(dirname $0)/lib.sh
-ENV_VARS='PACKER_BUILDS BUILT_IMAGE_SUFFIX UBUNTU_BASE_IMAGE FEDORA_BASE_IMAGE PRIOR_FEDORA_BASE_IMAGE SERVICE_ACCOUNT GCE_SSH_USERNAME GCP_PROJECT_ID PACKER_VER SCRIPT_BASE PACKER_BASE'
+BASE_IMAGE_VARS='FEDORA_BASE_IMAGE PRIOR_FEDORA_BASE_IMAGE UBUNTU_BASE_IMAGE'
+ENV_VARS="PACKER_BUILDS BUILT_IMAGE_SUFFIX $BASE_IMAGE_VARS SERVICE_ACCOUNT GCE_SSH_USERNAME GCP_PROJECT_ID PACKER_VER SCRIPT_BASE PACKER_BASE CIRRUS_BUILD_ID CIRRUS_CHANGE_IN_REPO"
req_env_var $ENV_VARS
# Must also be made available through make, into packer process
export $ENV_VARS
@@ -24,6 +25,20 @@ then
fi
cd "$GOSRC/$PACKER_BASE"
+# Add/update labels on base-images used in this build to prevent premature deletion
+ARGS="
+"
+for base_image_var in $BASE_IMAGE_VARS
+do
+ # See entrypoint.sh in contrib/imgts and contrib/imgprune
+ # These updates can take a while, run them in the background, check later
+ gcloud compute images update "$image" \
+ --update-labels=last-used=$(date +%s) \
+ --update-labels=build-id=$CIRRUS_BUILD_ID \
+ --update-labels=repo-ref=$CIRRUS_CHANGE_IN_REPO \
+ --update-labels=project=$GCP_PROJECT_ID \
+ ${!base_image_var} &
+done
make libpod_images \
PACKER_BUILDS=$PACKER_BUILDS \
@@ -33,9 +48,31 @@ make libpod_images \
PACKER_BASE=$PACKER_BASE \
BUILT_IMAGE_SUFFIX=$BUILT_IMAGE_SUFFIX
+# Separate PR-produced images from those produced on master.
+if [[ "${CIRRUS_BRANCH:-}" == "master" ]]
+then
+ POST_MERGE_BUCKET_SUFFIX="-master"
+else
+ POST_MERGE_BUCKET_SUFFIX=""
+fi
+
# When successful, upload manifest of produced images using a filename unique
# to this build.
URI="gs://packer-import${POST_MERGE_BUCKET_SUFFIX}/manifest${BUILT_IMAGE_SUFFIX}.json"
gsutil cp packer-manifest.json "$URI"
+# Ensure any background 'gcloud compute images update' processes finish
+set +e # need 'wait' exit code to avoid race
+while [[ -n "$(jobs)" ]]
+do
+ wait -n
+ RET=$?
+ if [[ "$RET" -eq "127" ]] || \ # Avoid TOCTOU race w/ jobs + wait
+ [[ "$RET" -eq "0" ]]
+ then
+ continue
+ fi
+ die $RET "Required base-image metadata update failed"
+done
+
echo "Finished. A JSON manifest of produced images is available at $URI"
diff --git a/contrib/cirrus/check_image.sh b/contrib/cirrus/check_image.sh
index 948039234..690a38119 100755
--- a/contrib/cirrus/check_image.sh
+++ b/contrib/cirrus/check_image.sh
@@ -16,7 +16,9 @@ MIN_MEM_MB=2000
read JUNK TOTAL USED MEM_FREE JUNK <<<$(free -tm | tail -1)
item_test 'Minimum available memory' $MEM_FREE -ge $MIN_MEM_MB || let "RET+=1"
-item_test "podman command NOT found on path" -z "$(type -P podman)" || let "RET+=1"
+# We're testing a custom-built podman; make sure there isn't a distro-provided
+# binary anywhere; that could potentially taint our results.
+item_test "remove_packaged_podman_files() did it's job" -z "$(type -P podman)" || let "RET+=1"
MIN_ZIP_VER='3.0'
VER_RE='.+([[:digit:]]+\.[[:digit:]]+).+'
@@ -34,4 +36,10 @@ do
"$(systemctl list-unit-files --no-legend $REQ_UNIT)" = "$REQ_UNIT enabled" || let "RET+=1"
done
+# Exits zero if any unit matching pattern is running
+UNIT_STATUS=$(systemctl is-active $EVIL_UNITS; echo $?)
+item_test "No interfering background units are active:" \
+ "$UNIT_STATUS" -ne "0" || let "RET+=1"
+
+echo "Total failed tests: $RET"
exit $RET
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index ea0f9e326..a9da3f4ce 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -45,6 +45,7 @@ fi
# Defaults when not running under CI
export CI="${CI:-false}"
CIRRUS_CI="${CIRRUS_CI:-false}"
+DEST_BRANCH="${DEST_BRANCH:-master}"
CONTINUOUS_INTEGRATION="${CONTINUOUS_INTEGRATION:-false}"
CIRRUS_REPO_NAME=${CIRRUS_REPO_NAME:-libpod}
CIRRUS_BASE_SHA=${CIRRUS_BASE_SHA:-unknown$(date +%s)} # difficult to reliably discover
@@ -77,6 +78,9 @@ ROOTLESS_ENV_RE='(CIRRUS_.+)|(ROOTLESS_.+)|(.+_IMAGE.*)|(.+_BASE)|(.*DIRPATH)|(.
# Unsafe env. vars for display
SECRET_ENV_RE='(IRCID)|(ACCOUNT)|(^GC[EP]..+)|(SSH)'
+# Names of systemd units which should never be running
+EVIL_UNITS="cron crond atd apt-daily-upgrade apt-daily fstrim motd-news systemd-tmpfiles-clean"
+
SPECIALMODE="${SPECIALMODE:-none}"
TEST_REMOTE_CLIENT="${TEST_REMOTE_CLIENT:-false}"
export CONTAINER_RUNTIME=${CONTAINER_RUNTIME:-podman}
@@ -323,8 +327,15 @@ install_test_configs(){
/etc/containers/registries.conf
}
+# Remove all files (except conmon, for now) provided by the distro version of podman.
+# Except conmon, for now as it's expected to eventually be packaged separately.
+# All VM cache-images used for testing include the distro podman because (1) it's
+# required for podman-in-podman testing and (2) it somewhat simplifies the task
+# of pulling in necessary prerequisites packages as the set can change over time.
+# For general CI testing however, calling this function makes sure the system
+# can only run the compiled source version.
remove_packaged_podman_files(){
- show_and_store_warning "Removing packaged podman files to prevent conflicts with source build and testing."
+ echo "Removing packaged podman files to prevent conflicts with source build and testing."
req_env_var OS_RELEASE_ID
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]
then
@@ -342,6 +353,20 @@ remove_packaged_podman_files(){
done
}
+systemd_banish(){
+ echo "Disabling periodic services that could destabilize testing:"
+ set +e # Not all of these exist on every platform
+ for unit in $EVIL_UNITS
+ do
+ ooe.sh sudo systemctl stop $unit
+ ooe.sh sudo systemctl disable $unit
+ ooe.sh sudo systemctl disable $unit.timer
+ ooe.sh sudo systemctl mask $unit
+ ooe.sh sudo systemctl mask $unit.timer
+ done
+ set -e
+}
+
_finalize(){
set +e # Don't fail at the very end
set +e # make errors non-fatal
diff --git a/contrib/cirrus/notice_master_failure.sh b/contrib/cirrus/notice_branch_failure.sh
index 1fc15cdf9..f030c12e5 100755
--- a/contrib/cirrus/notice_master_failure.sh
+++ b/contrib/cirrus/notice_branch_failure.sh
@@ -9,10 +9,10 @@ ETX="$(echo -n -e '\x03')"
RED="${ETX}4"
NOR="$(echo -n -e '\x0f')"
-if [[ "$CIRRUS_BRANCH" =~ "master" ]]
+if [[ "$CIRRUS_BRANCH" = "$DEST_BRANCH" ]]
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."
+ ircmsg "${RED}[Action Recommended]: ${NOR}Post-merge testing on ${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
diff --git a/contrib/cirrus/packer/fedora_base-setup.sh b/contrib/cirrus/packer/fedora_base-setup.sh
index a425b2b57..788a54c34 100644
--- a/contrib/cirrus/packer/fedora_base-setup.sh
+++ b/contrib/cirrus/packer/fedora_base-setup.sh
@@ -27,6 +27,9 @@ ooe.sh systemctl enable rngd
echo "Setting cloud-init service to start after google-network-daemon.service"
cp -v $GOSRC/$PACKER_BASE/cloud-init/fedora/cloud-init.service /etc/systemd/system/
+# Ensure there are no disruptive periodic services enabled by default in image
+systemd_banish
+
rh_finalize
echo "SUCCESS!"
diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh
index eb95db907..1e25a1a3c 100644
--- a/contrib/cirrus/packer/fedora_setup.sh
+++ b/contrib/cirrus/packer/fedora_setup.sh
@@ -76,6 +76,9 @@ ooe.sh sudo dnf install -y \
xz \
zip
+# Ensure there are no disruptive periodic services enabled by default in image
+systemd_banish
+
sudo /tmp/libpod/hack/install_catatonit.sh
rh_finalize
diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh
index 6209f2f89..dba191ad2 100644
--- a/contrib/cirrus/packer/ubuntu_setup.sh
+++ b/contrib/cirrus/packer/ubuntu_setup.sh
@@ -100,6 +100,9 @@ ooe.sh sudo update-grub
sudo /tmp/libpod/hack/install_catatonit.sh
ooe.sh sudo make -C /tmp/libpod install.libseccomp.sudo
+# Ensure there are no disruptive periodic services enabled by default in image
+systemd_banish
+
ubuntu_finalize
echo "SUCCESS!"
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index f312e593a..e49bb98fe 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -52,7 +52,9 @@ install_test_configs
make install.tools
case "$SPECIALMODE" in
- none) ;; # Do the normal thing
+ none)
+ remove_packaged_podman_files # we're building from source
+ ;;
rootless)
# Only do this once, even if ROOTLESS_USER (somehow) changes
if ! grep -q 'ROOTLESS_USER' /etc/environment
@@ -65,9 +67,9 @@ case "$SPECIALMODE" in
tee -a /etc/environment) && eval "$X" && echo "$X"
setup_rootless
fi
+ remove_packaged_podman_files
;;
in_podman) # Assumed to be Fedora
- dnf install -y podman
$SCRIPT_BASE/setup_container_environment.sh
;;
windows) ;& # for podman-remote building only
diff --git a/contrib/imgprune/Dockerfile b/contrib/imgprune/Dockerfile
new file mode 100644
index 000000000..26329e828
--- /dev/null
+++ b/contrib/imgprune/Dockerfile
@@ -0,0 +1,7 @@
+FROM libpod/imgts:latest
+
+RUN yum -y update && \
+ yum clean all
+
+COPY /contrib/imgprune/entrypoint.sh /usr/local/bin/entrypoint.sh
+RUN chmod 755 /usr/local/bin/entrypoint.sh
diff --git a/contrib/imgprune/README.md b/contrib/imgprune/README.md
new file mode 100644
index 000000000..48abc2028
--- /dev/null
+++ b/contrib/imgprune/README.md
@@ -0,0 +1,11 @@
+![PODMAN logo](../../logo/podman-logo-source.svg)
+
+A container image for maintaining the collection of
+VM images used by CI/CD on this project and several others.
+Acts upon metadata maintained by the imgts container.
+
+Example build (from repository root):
+
+```bash
+sudo podman build -t $IMAGE_NAME -f contrib/imgprune/Dockerfile .
+```
diff --git a/contrib/imgprune/entrypoint.sh b/contrib/imgprune/entrypoint.sh
new file mode 100755
index 000000000..a4b77523b
--- /dev/null
+++ b/contrib/imgprune/entrypoint.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+set -e
+
+source /usr/local/bin/lib_entrypoint.sh
+
+req_env_var GCPJSON GCPNAME GCPPROJECT IMGNAMES
+
+gcloud_init
+
+# For safety's sake + limit nr background processes
+PRUNE_LIMIT=10
+THEFUTURE=$(date --date='+1 hour' +%s)
+TOO_OLD='90 days ago'
+THRESHOLD=$(date --date="$TOO_OLD" +%s)
+# Format Ref: https://cloud.google.com/sdk/gcloud/reference/topic/formats
+FORMAT='value[quote](name,selfLink,creationTimestamp,labels)'
+PROJRE="/v1/projects/$GCPPROJECT/global/"
+BASE_IMAGE_RE='cloud-base'
+RECENTLY=$(date --date='30 days ago' --iso-8601=date)
+EXCLUDE="$IMGNAMES $IMAGE_BUILDER_CACHE_IMAGE_NAME" # whitespace separated values
+# Filter Ref: https://cloud.google.com/sdk/gcloud/reference/topic/filters
+FILTER="selfLink~$PROJRE AND creationTimestamp<$RECENTLY AND NOT name=($EXCLUDE)"
+TODELETE=$(mktemp -p '' todelete.XXXXXX)
+
+echo "Searching images for pruning candidates older than $TOO_OLD ($THRESHOLD):"
+$GCLOUD compute images list --format="$FORMAT" --filter="$FILTER" | \
+ while read name selfLink creationTimestamp labels
+ do
+ created_ymd=$(date --date=$creationTimestamp --iso-8601=date)
+ last_used=$(egrep --only-matching --max-count=1 'last-used=[[:digit:]]+' <<< $labels || true)
+ markmsgpfx="Marking $name (created $created_ymd) for deletion"
+ if [[ -z "$last_used" ]]
+ then # image pre-dates addition of tracking labels
+ echo "$markmsgpfx: Missing 'last-used' metadata, labels: '$labels'"
+ echo "$name" >> $TODELETE
+ continue
+ fi
+
+ last_used_timestamp=$(date --date=@$(cut -d= -f2 <<< $last_used || true) +%s || true)
+ last_used_ymd=$(date --date=@$last_used_timestamp --iso-8601=date)
+ if [[ -z "$last_used_timestamp" ]] || [[ "$last_used_timestamp" -ge "$THEFUTURE" ]]
+ then
+ echo "$markmsgpfx: Missing or invalid last-used timestamp: '$last_used_timestamp'"
+ echo "$name" >> $TODELETE
+ continue
+ fi
+
+ if [[ "$last_used_timestamp" -le "$THRESHOLD" ]]
+ then
+ echo "$markmsgpfx: Used over $TOO_OLD on $last_used_ymd"
+ echo "$name" >> $TODELETE
+ continue
+ fi
+
+ echo "NOT $markmsgpfx: last used on $last_used_ymd)"
+ done
+
+echo "Pruning up to $PRUNE_LIMIT images that were marked for deletion:"
+for image_name in $(tail -$PRUNE_LIMIT $TODELETE | sort --random-sort)
+do
+ # This can take quite some time (minutes), run in parallel disconnected from terminal
+ echo "TODO: Would have: $GCLOUD compute images delete $image_name &"
+ sleep "$[1+RANDOM/1000]s" & # Simlate background operation
+done
+
+wait || true # Nothing to delete: No background jobs
diff --git a/contrib/imgts/Dockerfile b/contrib/imgts/Dockerfile
index 0746eca4c..deaadb899 100644
--- a/contrib/imgts/Dockerfile
+++ b/contrib/imgts/Dockerfile
@@ -7,14 +7,14 @@ RUN yum -y update && \
yum -y install google-cloud-sdk && \
yum clean all
-COPY /contrib/imgts/entrypoint.sh /usr/local/bin/entrypoint.sh
ENV GCPJSON="__unknown__" \
GCPNAME="__unknown__" \
GCPPROJECT="__unknown__" \
IMGNAMES="__unknown__" \
- TIMESTAMP="__unknown__" \
BUILDID="__unknown__" \
REPOREF="__unknown__"
+
+COPY ["/contrib/imgts/entrypoint.sh", "/contrib/imgts/lib_entrypoint.sh", "/usr/local/bin/"]
RUN chmod 755 /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
diff --git a/contrib/imgts/entrypoint.sh b/contrib/imgts/entrypoint.sh
index 610e1f3b6..9c653eda0 100755
--- a/contrib/imgts/entrypoint.sh
+++ b/contrib/imgts/entrypoint.sh
@@ -2,45 +2,22 @@
set -e
-RED="\e[1;36;41m"
-YEL="\e[1;33;44m"
-NOR="\e[0m"
+source /usr/local/bin/lib_entrypoint.sh
-die() {
- echo -e "$2" >&2
- exit "$1"
-}
+req_env_var GCPJSON GCPNAME GCPPROJECT IMGNAMES BUILDID REPOREF
-SENTINEL="__unknown__" # default set in dockerfile
+gcloud_init
-[[ "$GCPJSON" != "$SENTINEL" ]] || \
- die 1 "Must specify service account JSON in \$GCPJSON"
-[[ "$GCPNAME" != "$SENTINEL" ]] || \
- die 2 "Must specify service account name in \$GCPNAME"
-[[ "$GCPPROJECT" != "$SENTINEL" ]] || \
- die 4 "Must specify GCP Project ID in \$GCPPROJECT"
-[[ -n "$GCPPROJECT" ]] || \
- die 5 "Must specify non-empty GCP Project ID in \$GCPPROJECT"
-[[ "$IMGNAMES" != "$SENTINEL" ]] || \
- die 6 "Must specify space separated list of GCE image names in \$IMGNAMES"
-[[ "$BUILDID" != "$SENTINEL" ]] || \
- die 7 "Must specify the number of current build in \$BUILDID"
-[[ "$REPOREF" != "$SENTINEL" ]] || \
- die 8 "Must specify a PR number or Branch name in \$REPOREF"
+ARGS="
+ --update-labels=last-used=$(date +%s)
+ --update-labels=build-id=$BUILDID
+ --update-labels=repo-ref=$REPOREF
+ --update-labels=project=$GCPPROJECT
+"
-ARGS="--update-labels=last-used=$(date +%s)"
-# optional
-[[ -z "$BUILDID" ]] || ARGS="$ARGS --update-labels=build-id=$BUILDID"
-[[ -z "$REPOREF" ]] || ARGS="$ARGS --update-labels=repo-ref=$REPOREF"
-[[ -z "$GCPPROJECT" ]] || ARGS="$ARGS --update-labels=project=$GCPPROJECT"
-
-gcloud config set account "$GCPNAME"
-gcloud config set project "$GCPPROJECT"
-echo "$GCPJSON" > /tmp/gcp.json
-gcloud auth activate-service-account --key-file=/tmp/gcp.json || rm /tmp/gcp.json
for image in $IMGNAMES
do
- gcloud compute images update "$image" $ARGS &
+ $GCLOUD compute images update "$image" $ARGS &
done
-set +e # Actual update failures are only warnings
-wait || die 0 "${RED}WARNING:$NOR ${YEL}Failed to update labels on one or more images:$NOR '$IMGNAMES'"
+
+wait || echo "Warning: No \$IMGNAMES were specified."
diff --git a/contrib/imgts/lib_entrypoint.sh b/contrib/imgts/lib_entrypoint.sh
new file mode 100644
index 000000000..7b76c823f
--- /dev/null
+++ b/contrib/imgts/lib_entrypoint.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+set -e
+
+RED="\e[1;36;41m"
+YEL="\e[1;33;44m"
+NOR="\e[0m"
+SENTINEL="__unknown__" # default set in dockerfile
+# Disable all input prompts
+# https://cloud.google.com/sdk/docs/scripting-gcloud
+GCLOUD="gcloud --quiet"
+
+die() {
+ EXIT=$1
+ PFX=$2
+ shift 2
+ MSG="$@"
+ echo -e "${RED}${PFX}:${NOR} ${YEL}$MSG${NOR}"
+ [[ "$EXIT" -eq "0" ]] || exit "$EXIT"
+}
+
+# Pass in a list of one or more envariable names; exit non-zero with
+# helpful error message if any value is empty
+req_env_var() {
+ for i; do
+ if [[ -z "${!i}" ]]
+ then
+ die 1 FATAL entrypoint.sh requires \$$i to be non-empty.
+ elif [[ "${!i}" == "$SENTINEL" ]]
+ then
+ die 2 FATAL entrypoint.sh requires \$$i to be explicitly set.
+ fi
+ done
+}
+
+gcloud_init() {
+ set +xe
+ TMPF=$(mktemp -p '' .$(uuidgen)XXXX)
+ trap "rm -f $TMPF" EXIT
+ echo "$GCPJSON" > $TMPF && \
+ $GCLOUD auth activate-service-account --project "$GCPPROJECT" --key-file=$TMPF || \
+ die 5 FATAL auth
+ rm -f $TMPF
+}
diff --git a/dependencies/analyses/README.md b/dependencies/analyses/README.md
new file mode 100644
index 000000000..a440a0ebd
--- /dev/null
+++ b/dependencies/analyses/README.md
@@ -0,0 +1,88 @@
+# A set of scripts and instructions that help to analyze and debloat go-lang dependencies
+
+Note that all scripts mentioned below follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) on purpose.
+The scripts are meant to be used in combination to aid in understanding the packages' dependencies and how they contribute to the size of the compiled binary.
+
+## Size of packages
+
+To analyze the size of all go packages used during the build process, pass the `-work -a` build flags to `go build`.
+The `-a` flag forces go to rebuild all packages even if they are already up-to-date (e.g., in the build cache), while the `-work` flag instructs go to print the temporary work directory used for compiling the packages.
+The path to the temporary work directory of `go-build` must be passed to `go-archive-analysis.sh` by setting it as an environment variable.
+The analysis script will then read and parse the build data and print a sorted table of the package size in bytes followed by the package name.
+
+Running such an analysis on libpod may look as follows:
+
+```
+# 1) Build the podman binary with `-work -a`.
+[libpod]$ BUILDFLAGS="-work -a" make podman
+[...]
+WORK=/tmp/go-build794287815
+
+# 2) Set the work directory as an environment variable and call the analysis script
+[libpod]$ WORK=/tmp/go-build794287815 ./dependencies/analyses/go-archive-analysis.sh | head -n10
+17M github.com/containers/libpod/cmd/podman/cliconfig
+13M github.com/containers/libpod/vendor/github.com/DataDog/zstd
+10M github.com/containers/libpod/vendor/k8s.io/api/core/v1
+3.7M net/http
+3.7M github.com/containers/libpod/libpod
+3.2M runtime
+2.7M github.com/containers/libpod/vendor/github.com/gogo/protobuf/proto
+2.5M github.com/containers/libpod/vendor/k8s.io/apimachinery/pkg/apis/meta/v1
+2.3M github.com/containers/libpod/vendor/github.com/vishvananda/netlink
+2.1M github.com/containers/libpod/cmd/podman/varlink
+```
+
+The output of the `go-archive-analysis.sh` script is a sorted table with the size in bytes followed by the package.
+The size denotes the size of the compiled package (i.e., the `.a` file).
+
+
+## Size of symbols in binary
+
+Once the binary is compiled, we can run another set of analyses on it.
+The `nm-symbols-analysis.sh` is a wrapper around `go tool nm` and prints a table with the size in bytes followed by the symbol's name.
+To avoid information overload, the scripts prints only symbols from the text/code segment.
+
+Running such an analysis on libpod may look as follows:
+
+```
+# 1) Compile the binary
+[libpod]$ make podman
+[...]
+
+# 2) Run the script with the binary as an argument
+[libpod]$ ./dependencies/analyses/nm-symbols-analysis.sh ./bin/podman | grep "containers/libpod/libpod" | head -n10
+299 github.com/containers/libpod/libpod.(*BoltState).AddContainer
+658 github.com/containers/libpod/libpod.(*BoltState).AddContainerToPod
+2120 github.com/containers/libpod/libpod.(*BoltState).AddPod
+3773 github.com/containers/libpod/libpod.(*BoltState).AddPod.func1
+965 github.com/containers/libpod/libpod.(*BoltState).AddVolume
+1651 github.com/containers/libpod/libpod.(*BoltState).AddVolume.func1
+558 github.com/containers/libpod/libpod.(*BoltState).AllContainers
+282 github.com/containers/libpod/libpod.(*BoltState).AllContainers.func1
+1121 github.com/containers/libpod/libpod.(*BoltState).AllContainers.func1.1
+558 github.com/containers/libpod/libpod.(*BoltState).AllPods
+```
+
+Running the script can help identify sources of bloat and reveal potential candidates (e.g., entire packages, types, or function) for refactoring.
+
+
+## Dependency Tree
+
+Use the `dependency-tree.sh` script to figure out which package includes which packages.
+The output of the script has the format `package: dependency_1, dependency_2, ...`.
+Each line is followed by a blank line to make it easier to read.
+The script generates two files:
+
+ - `direct-tree.txt` - listing direct dependencies
+ - `transitive-tree.txt` - listing direct and transitive dependencies
+
+Running such a dependency-tree analysis may look as follows:
+
+
+```
+[libpod]$ ./dependencies/analyses/dependency-tree.sh github.com/containers/libpod
+[libpod]$ grep "^github.com/containers/libpod/pkg/registries" direct-tree.txt
+github.com/containers/libpod/pkg/registries: github.com/containers/libpod/vendor/github.com/containers/image/pkg/sysregistriesv2, github.com/containers/libpod/vendor/github.com/containers/image/types, github.com/containers/libpod/pkg/rootless, github.com/containers/libpod/vendor/github.com/docker/distribution/reference, github.com/containers/libpod/vendor/github.com/pkg/errors, os, path/filepath, strings
+```
+
+As shown above, the script's output can then be used to query for specific packages (e.g, with `grep`).
diff --git a/dependencies/analyses/dependency-tree.sh b/dependencies/analyses/dependency-tree.sh
new file mode 100755
index 000000000..84085a50d
--- /dev/null
+++ b/dependencies/analyses/dependency-tree.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/bash
+
+if test "$#" -ne 1; then
+ echo "invalid arguments: usage: $0 path to package"
+ exit 1
+fi
+
+go list $1/... \
+ | xargs -d '\n' go list -f '{{ .ImportPath }}: {{ join .Imports ", " }}' \
+ | awk '{ printf "%s\n\n", $0 }' \
+ > direct-tree.tmp.$$ && mv -f direct-tree.tmp.$$ direct-tree.txt
+
+
+go list $1/... \
+ | xargs -d '\n' go list -f '{{ .ImportPath }}: {{ join .Deps ", " }}' \
+ | awk '{ printf "%s\n\n", $0 }' \
+ > transitive-tree.tmp.$$ && mv -f transitive-tree.tmp.$$ transitive-tree.txt
diff --git a/dependencies/analyses/go-archive-analysis.sh b/dependencies/analyses/go-archive-analysis.sh
new file mode 100755
index 000000000..f10145dad
--- /dev/null
+++ b/dependencies/analyses/go-archive-analysis.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/bash
+
+if [ -z "$WORK" ]
+then
+ echo "WORK environment variable must be set"
+ exit 1
+fi
+
+grep --no-filename packagefile $WORK/**/importcfg \
+ | awk '{ split($2, data, "="); printf "%s ", data[1]; system("du -sh " data[2]) }' \
+ | awk '{ printf "%s %s\n", $2, $1 }' \
+ | sort -u | sort -rh
diff --git a/dependencies/analyses/nm-symbols-analysis.sh b/dependencies/analyses/nm-symbols-analysis.sh
new file mode 100755
index 000000000..361b746e4
--- /dev/null
+++ b/dependencies/analyses/nm-symbols-analysis.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/bash
+
+if test "$#" -ne 1; then
+ echo "invalid arguments: usage: $0 path/to/binary"
+ exit 1
+fi
+
+go tool nm -size "$1" \
+ | awk 'NF==4 && $3=="t" {printf "%s\t\t%s\n", $2, $4}'
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index 120573235..c4667070d 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -339,7 +339,7 @@ environment variable. `export BUILDAH_RUNTIME=/usr/local/bin/runc`
**--runtime-flag**=*flag*
-Adds global flags for the container rutime. To list the supported flags, please
+Adds global flags for the container runtime. To list the supported flags, please
consult the manpages of the selected container runtime (`runc` is the default
runtime, the manpage to consult is `runc(8)`).
@@ -394,7 +394,7 @@ Require HTTPS and verify certificates when talking to container registries (defa
Specifies resource limits to apply to processes launched when processing `RUN` instructions.
This option can be specified multiple times. Recognized resource types
include:
- "core": maximimum core dump size (ulimit -c)
+ "core": maximum core dump size (ulimit -c)
"cpu": maximum CPU time (ulimit -t)
"data": maximum size of a process's data segment (ulimit -d)
"fsize": maximum size of new files (ulimit -f)
@@ -422,7 +422,7 @@ process.
**--userns-uid-map**=*mapping*
Directly specifies a UID mapping which should be used to set ownership, at the
-filesytem level, on the working container's contents.
+filesystem level, on the working container's contents.
Commands run when handling `RUN` instructions will default to being run in
their own user namespaces, configured using the UID and GID maps.
@@ -443,7 +443,7 @@ use the same numeric values as the GID map.
**--userns-gid-map**=*mapping*
Directly specifies a GID mapping which should be used to set ownership, at the
-filesytem level, on the working container's contents.
+filesystem level, on the working container's contents.
Commands run when handling `RUN` instructions will default to being run in
their own user namespaces, configured using the UID and GID maps.
@@ -464,7 +464,7 @@ use the same numeric values as the UID map.
**--userns-uid-map-user**=*user*
Specifies that a UID mapping which should be used to set ownership, at the
-filesytem level, on the working container's contents, can be found in entries
+filesystem level, on the working container's contents, can be found in entries
in the `/etc/subuid` file which correspond to the specified user.
Commands run when handling `RUN` instructions will default to being run in
their own user namespaces, configured using the UID and GID maps.
@@ -475,7 +475,7 @@ suitable user name to use as the default setting for this option.
**--userns-gid-map-group**=*group*
Specifies that a GID mapping which should be used to set ownership, at the
-filesytem level, on the working container's contents, can be found in entries
+filesystem level, on the working container's contents, can be found in entries
in the `/etc/subgid` file which correspond to the specified group.
Commands run when handling `RUN` instructions will default to being run in
their own user namespaces, configured using the UID and GID maps.
@@ -565,7 +565,7 @@ Use `df <source-dir>` to determine the source mount and then use
`findmnt -o TARGET,PROPAGATION <source-mount-dir>` to determine propagation
properties of source mount, if `findmnt` utility is not available, the source mount point
can be determined by looking at the mount entry in `/proc/self/mountinfo`. Look
-at `optional fields` and see if any propagaion properties are specified.
+at `optional fields` and see if any propagation properties are specified.
`shared:X` means the mount is `shared`, `master:X` means the mount is `slave` and if
nothing is there that means the mount is `private`.
diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md
index bc9f17520..178a05018 100644
--- a/docs/podman-cp.1.md
+++ b/docs/podman-cp.1.md
@@ -7,7 +7,7 @@ podman\-cp - Copy files/folders between a container and the local filesystem
**podman cp** [*options*] [*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 container's filesystem to the local machine or the reverse, from the local filesystem to the container.
If - is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT.
The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory.
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 9cf3e038d..4008b64e6 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -63,6 +63,14 @@ Add Linux capabilities
Drop Linux capabilities
+**--cgroupns**=*mode*
+
+Set the cgroup namespace mode for the container, by default **host** is used.
+ **host**: use the host's cgroup namespace inside the container.
+ **container:<NAME|ID>**: join the namespace of the specified container.
+ **private**: create a new cgroup namespace.
+ **ns:<PATH>**: join the namespace at the specified path.
+
**--cgroup-parent**=*path*
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
@@ -206,7 +214,7 @@ Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:
**--dns**=*dns*
-Set custom DNS servers
+Set custom DNS servers. Invalid if using **--dns** and **--network** that is set to 'none' or 'container:<name|id>'.
This option can be used to override the DNS
configuration passed to the container. Typically this is necessary when the
@@ -218,11 +226,11 @@ The **/etc/resolv.conf** file in the image will be used without changes.
**--dns-option**=*option*
-Set custom DNS options
+Set custom DNS options. Invalid if using **--dns-option** and **--network** that is set to 'none' or 'container:<name|id>'.
**--dns-search**=*domain*
-Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
+Set custom DNS search domains. Invalid if using **--dns-search** and **--network** that is set to 'none' or 'container:<name|id>'. (Use --dns-search=. if you don't wish to set the search domain)
**--entrypoint**=*"command"* | *'["command", "arg1", ...]'*
@@ -245,13 +253,17 @@ You need to specify multi option commands in the form of a json string.
Set environment variables
-This option allows you to specify arbitrary
-environment variables that are available for the process that will be launched
-inside of the container.
+This option allows you to specify arbitrary environment variables that are available for the process that will be launched inside of the container. If you specify a environment variable without a value, podman will check the host environment for a value or set the environment to "". If you specify a environment variable ending in --*--, podman will search the host environment for variables starting with the prefix and add them to the container. If you want to add an environment variable with a ***** following it, then you need to set a value.
+
+See **Environment** note below for precedence.
+
+**--env-host**=*true|false*
+
+Use host environment inside of the container. See **Environment** note below for precedence.
**--env-file**=*file*
-Read in a line delimited file of environment variables
+Read in a line delimited file of environment variables. See **Environment** note below for precedence.
**--expose**=*port*
@@ -268,26 +280,29 @@ The following example maps uids 0-2000 in the container to the uids 30000-31999
Add additional groups to run as
-**--healthcheck-command**=*command*
+**--health-cmd**=*"command"* | *'["command", "arg1", ...]'*
Set or alter a healthcheck command for a container. The command is a command to be executed inside your
container that determines your container health. The command is required for other healthcheck options
to be applied. A value of `none` disables existing healthchecks.
-**--healthcheck-interval**=*interval*
+Multiple options can be passed in the form of a JSON array; otherwise, the command will be interpreted
+as an argument to `/bin/sh -c`.
+
+**--health-interval**=*interval*
Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s")
-**--healthcheck-retries**=*retries*
+**--health-retries**=*retries*
The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`.
-**--healthcheck-start-period**=*period*
+**--health-start-period**=*period*
The initialization time needed for a container to bootstrap. The value can be expressed in time format like
`2m3s`. The default value is `0s`
-**--healthcheck-timeout**=*timeout*
+**--health-timeout**=*timeout*
The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
value can be expressed in a time format such as `1m22s`. The default value is `30s`.
@@ -312,7 +327,7 @@ those. This option is only needed when the host system must use a proxy but
the container should not use any proxy. Proxy environment variables specified
for the container in any other way will override the values that would have
been passed thru from the host. (Other ways to specify the proxy for the
-container include passing the values with the `--env` flag, or hardcoding the
+container include passing the values with the `--env` flag, or hard coding the
proxy environment at container build time.)
For example, to disable passing these environment variables from host to
@@ -484,7 +499,7 @@ This works for both background and foreground containers.
**--network**, **--net**="*bridge*"
-Set the Network mode for the container
+Set the Network mode for the container. Invalid if using **--dns**, **--dns-option**, or **--dns-search** with **--network** that is set to 'none' or 'container:<name|id>'.
'bridge': create a network stack on the default bridge
'none': no networking
'container:<name|id>': reuse another container's network stack
@@ -630,7 +645,7 @@ Security Options
"seccomp=unconfined" : Turn off seccomp confinement for the container
"seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter
-Note: Labelling can be disabled for all containers by setting label=false in the **libpod.conf** (`/etc/containers/libpod.conf`) file.
+Note: Labeling can be disabled for all containers by setting label=false in the **libpod.conf** (`/etc/containers/libpod.conf`) file.
**--shm-size**=*size*
@@ -723,6 +738,8 @@ The following example maps uids 0-2000 in the container to the uids 30000-31999
Ulimit options
+You can pass `host` to copy the current configuration from the host.
+
**--user**, **-u**=*user*
Sets the username or UID used and optionally the groupname or GID for the specified command.
@@ -901,6 +918,19 @@ The fuse-overlay package provides a userspace overlay storage driver, otherwise
the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
required for VPN, without it containers need to be run with the --net=host flag.
+## ENVIRONMENT
+
+Environment variables within containers can be set using multiple different options: This section describes the precedence.
+
+Precedence Order:
+ **--env-host** : Host environment of the process executing podman is added.
+
+ Container image : Any environment variables specified in the container image.
+
+ **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry.
+
+ **--env** : Any environment variables specified will override previous settings.
+
## FILES
**/etc/subuid**
diff --git a/docs/podman-generate-kube.1.md b/docs/podman-generate-kube.1.md
index 76baad83a..93f746664 100644
--- a/docs/podman-generate-kube.1.md
+++ b/docs/podman-generate-kube.1.md
@@ -147,4 +147,4 @@ status:
podman(1), podman-container(1), podman-pod(1), podman-play-kube(1)
## HISTORY
-Decemeber 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
+December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/podman-generate-systemd.1.md b/docs/podman-generate-systemd.1.md
index 64e68a69a..ea72fdfae 100644
--- a/docs/podman-generate-systemd.1.md
+++ b/docs/podman-generate-systemd.1.md
@@ -23,7 +23,7 @@ Use the name of the container for the start, stop, and description in the unit f
Override the default stop timeout for the container with the given value.
**--restart-policy**=*policy*
-Set the SystemD restart policy. The restart-policy must be one of: "no", "on-success", "on-failure", "on-abnormal",
+Set the systemd restart policy. The restart-policy must be one of: "no", "on-success", "on-failure", "on-abnormal",
"on-watchdog", "on-abort", or "always". The default policy is *on-failure*.
## Examples
diff --git a/docs/podman-image-sign.1.md b/docs/podman-image-sign.1.md
index c425fcf2e..61df3b3bd 100644
--- a/docs/podman-image-sign.1.md
+++ b/docs/podman-image-sign.1.md
@@ -7,7 +7,7 @@ podman-image-sign - Create a signature for an image
**podman image sign** [*options*] *image* [*image* ...]
## DESCRIPTION
-**podmain image sign** will create a local signature for one or more local images that have
+**podman image sign** will create a local signature for one or more local images that have
been pulled from a registry. The signature will be written to a directory
derived from the registry configuration files in /etc/containers/registries.d. By default, the signature will be written into /var/lib/containers/sigstore directory.
@@ -39,8 +39,8 @@ docker:
privateregistry.example.com:
sigstore: file:///var/lib/containers/sigstore
-When signing an image preceeded with the registry name 'privateregistry.example.com',
-the signature will be written into subdirectories of
+When signing an image preceded with the registry name 'privateregistry.example.com',
+the signature will be written into sub-directories of
/var/lib/containers/sigstore/privateregistry.example.com. The use of 'sigstore' also means
the signature will be 'read' from that same location on a pull-related function.
diff --git a/docs/podman-load.1.md b/docs/podman-load.1.md
index 0a47c18c1..6643538ce 100644
--- a/docs/podman-load.1.md
+++ b/docs/podman-load.1.md
@@ -8,7 +8,7 @@ podman\-load - Load an image from a container image archive into container stora
## DESCRIPTION
**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.
+You can also specify a name for the image if the archive does not contain a named reference, of if you want an additional 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.
diff --git a/docs/podman-mount.1.md b/docs/podman-mount.1.md
index 6b8eb77d9..2722f460c 100644
--- a/docs/podman-mount.1.md
+++ b/docs/podman-mount.1.md
@@ -1,7 +1,7 @@
% podman-mount(1)
## NAME
-podman\-mount - Mount the specifed working containers' root filesystem
+podman\-mount - Mount the specified working containers' root filesystem
## SYNOPSIS
**podman mount** [*container* ...]
diff --git a/docs/podman-play-kube.1.md b/docs/podman-play-kube.1.md
index f9a867b44..2fae09199 100644
--- a/docs/podman-play-kube.1.md
+++ b/docs/podman-play-kube.1.md
@@ -62,4 +62,4 @@ $ podman play kube demo.yml
podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1)
## HISTORY
-Decemeber 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
+December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/podman-pod-create.1.md b/docs/podman-pod-create.1.md
index 009a93019..cd1de6401 100644
--- a/docs/podman-pod-create.1.md
+++ b/docs/podman-pod-create.1.md
@@ -64,7 +64,7 @@ NOTE: This cannot be modified once the pod is created.
**--share**=*namespace*
-A comma deliminated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts.
+A comma delimited list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts.
The operator can identify a pod in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
diff --git a/docs/podman-pod-stats.1.md b/docs/podman-pod-stats.1.md
index d081c91cb..12fc83cff 100644
--- a/docs/podman-pod-stats.1.md
+++ b/docs/podman-pod-stats.1.md
@@ -47,7 +47,7 @@ Valid placeholders for the Go template are listed below:
| .BlockIO | Block IO |
| .PIDS | Number of PIDs |
-When using a GO template, you may preceed the format with `table` to print headers.
+When using a GO template, you may precede the format with `table` to print headers.
## EXAMPLE
```
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 4889e5755..6d95d6045 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -77,6 +77,14 @@ Add Linux capabilities
Drop Linux capabilities
+**--cgroupns**=*mode*
+
+Set the cgroup namespace mode for the container, by default **host** is used.
+ **host**: use the host's cgroup namespace inside the container.
+ **container:<NAME|ID>**: join the namespace of the specified container.
+ **private**: create a new cgroup namespace.
+ **ns:<PATH>**: join the namespace at the specified path.
+
**--cgroup-parent**=*cgroup*
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
@@ -212,7 +220,7 @@ Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:
**--dns**=*dns*
-Set custom DNS servers
+Set custom DNS servers. Invalid if using **--dns** with **--network** that is set to 'none' or 'container:<name|id>'.
This option can be used to override the DNS
configuration passed to the container. Typically this is necessary when the
@@ -224,11 +232,11 @@ The **/etc/resolv.conf** file in the image will be used without changes.
**--dns-option**=*option*
-Set custom DNS options
+Set custom DNS options. Invalid if using **--dns-option** with **--network** that is set to 'none' or 'container:<name|id>'.
**--dns-search**=*domain*
-Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
+Set custom DNS search domains. Invalid if using **--dns-search** and **--network** that is set to 'none' or 'container:<name|id>'. (Use --dns-search=. if you don't wish to set the search domain)
**--entrypoint**=*"command"* | *'["command", "arg1", ...]'*
@@ -252,13 +260,17 @@ You need to specify multi option commands in the form of a json string.
Set environment variables
-This option allows you to specify arbitrary
-environment variables that are available for the process that will be launched
-inside of the container.
+This option allows you to specify arbitrary environment variables that are available for the process that will be launched inside of the container. If you specify a environment variable without a value, podman will check the host environment for a value or set the environment to "". If you specify a environment variable ending in --*--, podman will search the host environment for variables starting with the prefix and add them to the container. If you want to add an environment variable with a ***** following it, then you need to set a value.
+
+See **Environment** note below for precedence.
+
+**--env-host**=*true|false*
+
+Use host environment inside of the container. See **Environment** note below for precedence.
**--env-file**=*file*
-Read in a line delimited file of environment variables
+Read in a line delimited file of environment variables. See **Environment** note below for precedence.
**--expose**=*port*
@@ -275,26 +287,29 @@ The example maps gids 0-2000 in the container to the gids 30000-31999 on the hos
Add additional groups to run as
-**--healthcheck-command**=*command*
+**--health-cmd**=*"command"* | *'["command", "arg1", ...]'*
Set or alter a healthcheck command for a container. The command is a command to be executed inside your
container that determines your container health. The command is required for other healthcheck options
to be applied. A value of `none` disables existing healthchecks.
-**--healthcheck-interval**=*interval*
+Multiple options can be passed in the form of a JSON array; otherwise, the command will be interpreted
+as an argument to `/bin/sh -c`.
+
+**--health-interval**=*interval*
Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s")
-**--healthcheck-retries**=*retries*
+**--health-retries**=*retries*
The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`.
-**--healthcheck-start-period**=*period*
+**--health-start-period**=*period*
The initialization time needed for a container to bootstrap. The value can be expressed in time format like
`2m3s`. The default value is `0s`
-**--healthcheck-timeout**=*timeout*
+**--health-timeout**=*timeout*
The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
value can be expressed in a time format such as `1m22s`. The default value is `30s`.
@@ -319,7 +334,7 @@ those. This option is only needed when the host system must use a proxy but
the container should not use any proxy. Proxy environment variables specified
for the container in any other way will override the values that would have
been passed thru from the host. (Other ways to specify the proxy for the
-container include passing the values with the `--env` flag, or hardcoding the
+container include passing the values with the `--env` flag, or hard coding the
proxy environment at container build time.)
For example, to disable passing these environment variables from host to
@@ -495,9 +510,9 @@ to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-**--network**, **--net**=*mode*
+**--network**, **--net**=*node*
-Set the Network mode for the container:
+Set the Network mode for the container. Invalid if using **--dns**, **--dns-option**, or **--dns-search** with **--network** that is set to 'none' or 'container:<name|id>'.
- `bridge`: create a network stack on the default bridge
- `none`: no networking
- `container:<name|id>`: reuse another container's network stack
@@ -651,7 +666,7 @@ Security Options
- `seccomp=unconfined` : Turn off seccomp confinement for the container
- `seccomp=profile.json` : White listed syscalls seccomp Json file to be used as a seccomp filter
-Note: Labelling can be disabled for all containers by setting label=false in the **libpod.conf** (`/etc/containers/libpod.conf`) file.
+Note: Labeling can be disabled for all containers by setting label=false in the **libpod.conf** (`/etc/containers/libpod.conf`) file.
**--shm-size**=*size*
@@ -759,6 +774,8 @@ The example maps uids 0-2000 in the container to the uids 30000-31999 on the hos
Ulimit options
+You can pass `host` to copy the current configuration from the host.
+
**--user**, **-u**=*user*
Sets the username or UID used and optionally the groupname or GID for the specified command.
@@ -1185,6 +1202,20 @@ The fuse-overlay package provides a userspace overlay storage driver, otherwise
the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
required for VPN, without it containers need to be run with the --net=host flag.
+## ENVIRONMENT
+
+Environment variables within containers can be set using multiple different options: This section describes the precedence.
+
+Precedence Order:
+
+ **--env-host** : Host environment of the process executing podman is added.
+
+ Container image : Any environment variables specified in the container image.
+
+ **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry.
+
+ **--env** : Any environment variables specified will override previous settings.
+
## FILES
**/etc/subuid**
diff --git a/docs/podman-stats.1.md b/docs/podman-stats.1.md
index b71d435fa..c1a87f210 100644
--- a/docs/podman-stats.1.md
+++ b/docs/podman-stats.1.md
@@ -48,7 +48,7 @@ Valid placeholders for the Go template are listed below:
| .BlockIO | Block IO |
| .PIDS | Number of PIDs |
-When using a GO template, you may preceed the format with `table` to print headers.
+When using a GO template, you may precede the format with `table` to print headers.
## EXAMPLE
diff --git a/docs/podman-system-df.1.md b/docs/podman-system-df.1.md
index 425796f50..d0b1755ee 100644
--- a/docs/podman-system-df.1.md
+++ b/docs/podman-system-df.1.md
@@ -28,7 +28,7 @@ Local Volumes 1 1 22B 0B (0%)
$ podman system df -v
Images space usage:
-REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNQUE SIZE CONTAINERS
+REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
docker.io/library/alpine latest 5cb3aa00f899 2 weeks ago 5.79MB 0B 5.79MB 5
Containers space usage:
diff --git a/docs/podman-volume-inspect.1.md b/docs/podman-volume-inspect.1.md
index 4900e2feb..b00c821bb 100644
--- a/docs/podman-volume-inspect.1.md
+++ b/docs/podman-volume-inspect.1.md
@@ -8,7 +8,7 @@ podman\-volume\-inspect - Inspect one or more volumes
## DESCRIPTION
-Display detailed information on one or more volumes. The output can be formated using
+Display detailed information on one or more volumes. The output can be formatted using
the **--format** flag and a Go template. To get detailed information about all the
existing volumes, use the **--all** flag.
diff --git a/go.mod b/go.mod
index 6fb743847..b6c400f18 100644
--- a/go.mod
+++ b/go.mod
@@ -101,7 +101,7 @@ require (
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.4.0 // indirect
github.com/stretchr/testify v1.3.0
- github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
+ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5 // indirect
diff --git a/install.md b/install.md
index 368cdd386..0706a68c1 100644
--- a/install.md
+++ b/install.md
@@ -158,7 +158,7 @@ After that enable user namespaces:
sudo sysctl kernel.unprivileged_userns_clone=1
```
-To enable the user namespaces permanenty:
+To enable the user namespaces permanently:
```
echo 'kernel.unprivileged_userns_clone=1' > /etc/sysctl.d/userns.conf
diff --git a/libpod/container.go b/libpod/container.go
index a9b512de9..b71c0b2be 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -639,10 +639,7 @@ func (c *Container) HostsAdd() []string {
// trigger some OCI hooks.
func (c *Container) UserVolumes() []string {
volumes := make([]string, 0, len(c.config.UserVolumes))
- for _, vol := range c.config.UserVolumes {
- volumes = append(volumes, vol)
- }
-
+ volumes = append(volumes, c.config.UserVolumes...)
return volumes
}
@@ -650,10 +647,7 @@ func (c *Container) UserVolumes() []string {
// This is not added to the spec, but is instead used during image commit.
func (c *Container) Entrypoint() []string {
entrypoint := make([]string, 0, len(c.config.Entrypoint))
- for _, str := range c.config.Entrypoint {
- entrypoint = append(entrypoint, str)
- }
-
+ entrypoint = append(entrypoint, c.config.Entrypoint...)
return entrypoint
}
@@ -661,10 +655,7 @@ func (c *Container) Entrypoint() []string {
// This is not added to the spec, but is instead used during image commit
func (c *Container) Command() []string {
command := make([]string, 0, len(c.config.Command))
- for _, str := range c.config.Command {
- command = append(command, str)
- }
-
+ command = append(command, c.config.Command...)
return command
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index c6e478846..6f530f75f 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -277,7 +277,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
break
}
}
- if found == true {
+ if found {
sessionID = stringid.GenerateNonCryptoID()
}
}
@@ -305,6 +305,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
if err != nil {
if exited {
// If the runtime exited, propagate the error we got from the process.
+ // We need to remove PID files to ensure no memory leaks
+ if err2 := os.Remove(pidFile); err2 != nil {
+ logrus.Errorf("Error removing exit file for container %s exec session %s: %v", c.ID(), sessionID, err2)
+ }
+
return err
}
return errors.Wrapf(err, "timed out waiting for runtime to create pidfile for exec session in container %s", c.ID())
@@ -312,6 +317,10 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
// Pidfile exists, read it
contents, err := ioutil.ReadFile(pidFile)
+ // We need to remove PID files to ensure no memory leaks
+ if err2 := os.Remove(pidFile); err2 != nil {
+ logrus.Errorf("Error removing exit file for container %s exec session %s: %v", c.ID(), sessionID, err2)
+ }
if err != nil {
// We don't know the PID of the exec session
// However, it may still be alive
diff --git a/libpod/container_graph.go b/libpod/container_graph.go
index 50dbdfbe4..5aa51bc2f 100644
--- a/libpod/container_graph.go
+++ b/libpod/container_graph.go
@@ -264,6 +264,4 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
for _, successor := range node.dependedOn {
startNode(ctx, successor, ctrErrored, ctrErrors, ctrsVisited, restart)
}
-
- return
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 2de78254c..c4d2af66e 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -1,16 +1,88 @@
package libpod
import (
+ "fmt"
"strings"
"time"
"github.com/containers/image/manifest"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/driver"
+ "github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/opencontainers/runtime-tools/validate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "github.com/syndtr/gocapability/capability"
+)
+
+const (
+ // InspectAnnotationCIDFile is used by Inspect to determine if a
+ // container ID file was created for the container.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationCIDFile = "io.podman.annotations.cid-file"
+ // InspectAnnotationAutoremove is used by Inspect to determine if a
+ // container will be automatically removed on exit.
+ // If an annotation with this key is found in the OCI spec and is one of
+ // the two supported boolean values (InspectResponseTrue and
+ // InspectResponseFalse) it will be used in the output of Inspect().
+ InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
+ // InspectAnnotationVolumesFrom is used by Inspect to identify
+ // containers whose volumes are are being used by this container.
+ // It is expected to be a comma-separated list of container names and/or
+ // IDs.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationVolumesFrom = "io.podman.annotations.volumes-from"
+ // InspectAnnotationPrivileged is used by Inspect to identify containers
+ // which are privileged (IE, running with elevated privileges).
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationPrivileged = "io.podman.annotations.privileged"
+ // InspectAnnotationPublishAll is used by Inspect to identify containers
+ // which have all the ports from their image published.
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationPublishAll = "io.podman.annotations.publish-all"
+ // InspectAnnotationInit is used by Inspect to identify containers that
+ // mount an init binary in.
+ // It is expected to be a boolean, populated by one of
+ // InspectResponseTrue or InspectResponseFalse.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationInit = "io.podman.annotations.init"
+ // InspectAnnotationLabel is used by Inspect to identify containers with
+ // special SELinux-related settings. It is used to populate the output
+ // of the SecurityOpt setting.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationLabel = "io.podman.annotations.label"
+ // InspectAnnotationSeccomp is used by Inspect to identify containers
+ // with special Seccomp-related settings. It is used to populate the
+ // output of the SecurityOpt setting in Inspect.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationSeccomp = "io.podman.annotations.seccomp"
+ // InspectAnnotationApparmor is used by Inspect to identify containers
+ // with special Apparmor-related settings. It is used to populate the
+ // output of the SecurityOpt setting.
+ // If an annotation with this key is found in the OCI spec, it will be
+ // used in the output of Inspect().
+ InspectAnnotationApparmor = "io.podman.annotations.apparmor"
+
+ // InspectResponseTrue is a boolean True response for an inspect
+ // annotation.
+ InspectResponseTrue = "TRUE"
+ // InspectResponseFalse is a boolean False response for an inspect
+ // annotation.
+ InspectResponseFalse = "FALSE"
)
// InspectContainerData provides a detailed record of a container's configuration
@@ -19,41 +91,42 @@ import (
// compatible with `docker inspect` JSON, but additional fields have been added
// as required to share information not in the original output.
type InspectContainerData struct {
- ID string `json:"Id"`
- Created time.Time `json:"Created"`
- Path string `json:"Path"`
- Args []string `json:"Args"`
- State *InspectContainerState `json:"State"`
- ImageID string `json:"Image"`
- ImageName string `json:"ImageName"`
- Rootfs string `json:"Rootfs"`
- ResolvConfPath string `json:"ResolvConfPath"`
- HostnamePath string `json:"HostnamePath"`
- HostsPath string `json:"HostsPath"`
- StaticDir string `json:"StaticDir"`
- OCIConfigPath string `json:"OCIConfigPath,omitempty"`
- OCIRuntime string `json:"OCIRuntime,omitempty"`
- LogPath string `json:"LogPath"`
- ConmonPidFile string `json:"ConmonPidFile"`
- Name string `json:"Name"`
- RestartCount int32 `json:"RestartCount"`
- Driver string `json:"Driver"`
- MountLabel string `json:"MountLabel"`
- ProcessLabel string `json:"ProcessLabel"`
- AppArmorProfile string `json:"AppArmorProfile"`
- EffectiveCaps []string `json:"EffectiveCaps"`
- BoundingCaps []string `json:"BoundingCaps"`
- ExecIDs []string `json:"ExecIDs"`
- GraphDriver *driver.Data `json:"GraphDriver"`
- SizeRw int64 `json:"SizeRw,omitempty"`
- SizeRootFs int64 `json:"SizeRootFs,omitempty"`
- Mounts []InspectMount `json:"Mounts"`
- Dependencies []string `json:"Dependencies"`
- NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
- ExitCommand []string `json:"ExitCommand"`
- Namespace string `json:"Namespace"`
- IsInfra bool `json:"IsInfra"`
- Config *InspectContainerConfig `json:"Config"`
+ ID string `json:"Id"`
+ Created time.Time `json:"Created"`
+ Path string `json:"Path"`
+ Args []string `json:"Args"`
+ State *InspectContainerState `json:"State"`
+ ImageID string `json:"Image"`
+ ImageName string `json:"ImageName"`
+ Rootfs string `json:"Rootfs"`
+ ResolvConfPath string `json:"ResolvConfPath"`
+ HostnamePath string `json:"HostnamePath"`
+ HostsPath string `json:"HostsPath"`
+ StaticDir string `json:"StaticDir"`
+ OCIConfigPath string `json:"OCIConfigPath,omitempty"`
+ OCIRuntime string `json:"OCIRuntime,omitempty"`
+ LogPath string `json:"LogPath"`
+ ConmonPidFile string `json:"ConmonPidFile"`
+ Name string `json:"Name"`
+ RestartCount int32 `json:"RestartCount"`
+ Driver string `json:"Driver"`
+ MountLabel string `json:"MountLabel"`
+ ProcessLabel string `json:"ProcessLabel"`
+ AppArmorProfile string `json:"AppArmorProfile"`
+ EffectiveCaps []string `json:"EffectiveCaps"`
+ BoundingCaps []string `json:"BoundingCaps"`
+ ExecIDs []string `json:"ExecIDs"`
+ GraphDriver *driver.Data `json:"GraphDriver"`
+ SizeRw int64 `json:"SizeRw,omitempty"`
+ SizeRootFs int64 `json:"SizeRootFs,omitempty"`
+ Mounts []InspectMount `json:"Mounts"`
+ Dependencies []string `json:"Dependencies"`
+ NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
+ ExitCommand []string `json:"ExitCommand"`
+ Namespace string `json:"Namespace"`
+ IsInfra bool `json:"IsInfra"`
+ Config *InspectContainerConfig `json:"Config"`
+ HostConfig *InspectContainerHostConfig `json:"HostConfig"`
}
// InspectContainerConfig holds further data about how a container was initially
@@ -102,6 +175,353 @@ type InspectContainerConfig struct {
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
}
+// InspectContainerHostConfig holds information used when the container was
+// created.
+// It's very much a Docker-specific struct, retained (mostly) as-is for
+// compatibility. We fill individual fields as best as we can, inferring as much
+// as possible from the spec and container config.
+// Some things cannot be inferred. These will be populated by spec annotations
+// (if available).
+// Field names are fixed for compatibility and cannot be changed.
+// As such, silence lint warnings about them.
+//nolint
+type InspectContainerHostConfig struct {
+ // Binds contains an array of user-added mounts.
+ // Both volume mounts and named volumes are included.
+ // Tmpfs mounts are NOT included.
+ // In 'docker inspect' this is separated into 'Binds' and 'Mounts' based
+ // on how a mount was added. We do not make this distinction and do not
+ // include a Mounts field in inspect.
+ // Format: <src>:<destination>[:<comma-separated options>]
+ Binds []string `json:"Binds"`
+ // ContainerIDFile is a file created during container creation to hold
+ // the ID of the created container.
+ // This is not handled within libpod and is stored in an annotation.
+ ContainerIDFile string `json:"ContainerIDFile"`
+ // LogConfig contains information on the container's logging backend
+ LogConfig *InspectLogConfig `json:"LogConfig"`
+ // NetworkMode is the configuration of the container's network
+ // namespace.
+ // Populated as follows:
+ // default - A network namespace is being created and configured via CNI
+ // none - A network namespace is being created, not configured via CNI
+ // host - No network namespace created
+ // container:<id> - Using another container's network namespace
+ // ns:<path> - A path to a network namespace has been specified
+ NetworkMode string `json:"NetworkMode"`
+ // PortBindings contains the container's port bindings.
+ // It is formatted as map[string][]InspectHostPort.
+ // The string key here is formatted as <integer port number>/<protocol>
+ // and represents the container port. A single container port may be
+ // bound to multiple host ports (on different IPs).
+ PortBindings map[string][]InspectHostPort `json:"PortBindings"`
+ // RestartPolicy contains the container's restart policy.
+ RestartPolicy *InspectRestartPolicy `json:"RestartPolicy"`
+ // AutoRemove is whether the container will be automatically removed on
+ // exiting.
+ // It is not handled directly within libpod and is stored in an
+ // annotation.
+ AutoRemove bool `json:"AutoRemove"`
+ // VolumeDriver is presently unused and is retained for Docker
+ // compatibility.
+ VolumeDriver string `json:"VolumeDriver"`
+ // VolumesFrom is a list of containers which this container uses volumes
+ // from. This is not handled directly within libpod and is stored in an
+ // annotation.
+ // It is formatted as an array of container names and IDs.
+ VolumesFrom []string `json:"VolumesFrom"`
+ // CapAdd is a list of capabilities added to the container.
+ // It is not directly stored by Libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapAdd []string `json:"CapAdd"`
+ // CapDrop is a list of capabilities removed from the container.
+ // It is not directly stored by libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapDrop []string `json:"CapDrop"`
+ // Dns is a list of DNS nameservers that will be added to the
+ // container's resolv.conf
+ Dns []string `json:"Dns"`
+ // DnsOptions is a list of DNS options that will be set in the
+ // container's resolv.conf
+ DnsOptions []string `json:"DnsOptions"`
+ // DnsSearch is a list of DNS search domains that will be set in the
+ // container's resolv.conf
+ DnsSearch []string `json:"DnsSearch"`
+ // ExtraHosts contains hosts that will be aded to the container's
+ // /etc/hosts.
+ ExtraHosts []string `json:"ExtraHosts"`
+ // GroupAdd contains groups that the user inside the container will be
+ // added to.
+ GroupAdd []string `json:"GroupAdd"`
+ // IpcMode represents the configuration of the container's IPC
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, an IPC namespace will be created
+ // host - No IPC namespace created
+ // container:<id> - Using another container's IPC namespace
+ // ns:<path> - A path to an IPC namespace has been specified
+ IpcMode string `json:"IpcMode"`
+ // Cgroup contains the container's cgroup. It is presently not
+ // populated.
+ // TODO.
+ Cgroup string `json:"Cgroup"`
+ // Links is unused, and provided purely for Docker compatibility.
+ Links []string `json:"Links"`
+ // OOMScoreAdj is an adjustment that will be made to the container's OOM
+ // score.
+ OomScoreAdj int `json:"OomScoreAdj"`
+ // PidMode represents the configuration of the container's PID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a PID namespace will be created
+ // host - No PID namespace created
+ // container:<id> - Using another container's PID namespace
+ // ns:<path> - A path to a PID namespace has been specified
+ PidMode string `json:"PidMode"`
+ // Privileged indicates whether the container is running with elevated
+ // privileges.
+ // This has a very specific meaning in the Docker sense, so it's very
+ // difficult to decode from the spec and config, and so is stored as an
+ // annotation.
+ Privileged bool `json:"Privileged"`
+ // PublishAllPorts indicates whether image ports are being published.
+ // This is not directly stored in libpod and is saved as an annotation.
+ PublishAllPorts bool `json:"PublishAllPorts"`
+ // ReadonlyRootfs is whether the container will be mounted read-only.
+ ReadonlyRootfs bool `json:"ReadonlyRootfs"`
+ // SecurityOpt is a list of security-related options that are set in the
+ // container.
+ SecurityOpt []string `json:"SecurityOpt"`
+ // Tmpfs is a list of tmpfs filesystems that will be mounted into the
+ // container.
+ // It is a map of destination path to options for the mount.
+ Tmpfs map[string]string `json:"Tmpfs"`
+ // UTSMode represents the configuration of the container's UID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a UTS namespace will be created
+ // host - no UTS namespace created
+ // container:<id> - Using another container's UTS namespace
+ // ns:<path> - A path to a UTS namespace has been specified
+ UTSMode string `json:"UTSMode"`
+ // UsernsMode represents the configuration of the container's user
+ // namespace.
+ // When running rootless, a user namespace is created outside of libpod
+ // to allow some privileged operations. This will not be reflected here.
+ // Populated as follows:
+ // "" (empty string) - No user namespace will be created
+ // private - The container will be run in a user namespace
+ // container:<id> - Using another container's user namespace
+ // ns:<path> - A path to a user namespace has been specified
+ // TODO Rootless has an additional 'keep-id' option, presently not
+ // reflected here.
+ UsernsMode string `json:"UsernsMode"`
+ // ShmSize is the size of the container's SHM device.
+ ShmSize int64 `json:"ShmSize"`
+ // Runtime is provided purely for Docker compatibility.
+ // It is set unconditionally to "oci" as Podman does not presently
+ // support non-OCI runtimes.
+ Runtime string `json:"Runtime"`
+ // ConsoleSize is an array of 2 integers showing the size of the
+ // container's console.
+ // It is only set if the container is creating a terminal.
+ // TODO.
+ ConsoleSize []uint `json:"ConsoleSize"`
+ // Isolation is presently unused and provided solely for Docker
+ // compatibility.
+ Isolation string `json:"Isolation"`
+ // CpuShares indicates the CPU resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning CPU time
+ // versus other CGroups.
+ CpuShares uint64 `json:"CpuShares"`
+ // Memory indicates the memory resources allocated to the container.
+ // This is the limit (in bytes) of RAM the container may use.
+ Memory int64 `json:"Memory"`
+ // NanoCpus indicates number of CPUs allocated to the container.
+ // It is an integer where one full CPU is indicated by 1000000000 (one
+ // billion).
+ // Thus, 2.5 CPUs (fractional portions of CPUs are allowed) would be
+ // 2500000000 (2.5 billion).
+ // In 'docker inspect' this is set exclusively of two further options in
+ // the output (CpuPeriod and CpuQuota) which are both used to implement
+ // this functionality.
+ // We can't distinguish here, so if CpuQuota is set to the default of
+ // 100000, we will set both CpuQuota, CpuPeriod, and NanoCpus. If
+ // CpuQuota is not the default, we will not set NanoCpus.
+ NanoCpus int64 `json:"NanoCpus"`
+ // CgroupParent is the CGroup parent of the container.
+ // Only set if not default.
+ CgroupParent string `json:"CgroupParent"`
+ // BlkioWeight indicates the I/O resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning I/O time
+ // versus other CGroups.
+ BlkioWeight uint16 `json:"BlkioWeight"`
+ // BlkioWeightDevice is an array of I/O resource priorities for
+ // individual device nodes.
+ // Unfortunately, the spec only stores the device's Major/Minor numbers
+ // and not the path, which is used here.
+ // Fortunately, the kernel provides an interface for retrieving the path
+ // of a given node by major:minor at /sys/dev/. However, the exact path
+ // in use may not be what was used in the original CLI invocation -
+ // though it is guaranteed that the device node will be the same, and
+ // using the given path will be functionally identical.
+ BlkioWeightDevice []InspectBlkioWeightDevice `json:"BlkioWeightDevice"`
+ // BlkioDeviceReadBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets read rate cap in bytes per second for device
+ // nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadBps"`
+ // BlkioDeviceWriteBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // this specifically sets write rate cap in bytes per second for device
+ // nodes.
+ // as with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteBps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteBps"`
+ // BlkioDeviceReadIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the read rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadIOps"`
+ // BlkioDeviceWriteIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the write rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteIOps"`
+ // CpuPeriod is the length of a CPU period in microseconds.
+ // It relates directly to CpuQuota.
+ CpuPeriod uint64 `json:"CpuPeriod"`
+ // CpuPeriod is the amount of time (in microseconds) that a container
+ // can use the CPU in every CpuPeriod.
+ CpuQuota int64 `json:"CpuQuota"`
+ // CpuRealtimePeriod is the length of time (in microseconds) of the CPU
+ // realtime period. If set to 0, no time will be allocated to realtime
+ // tasks.
+ CpuRealtimePeriod uint64 `json:"CpuRealtimePeriod"`
+ // CpuRealtimeRuntime is the length of time (in microseconds) allocated
+ // for realtime tasks within every CpuRealtimePeriod.
+ CpuRealtimeRuntime int64 `json:"CpuRealtimeRuntime"`
+ // CpusetCpus is the is the set of CPUs that the container will execute
+ // on. Formatted as `0-3` or `0,2`. Default (if unset) is all CPUs.
+ CpusetCpus string `json:"CpusetCpus"`
+ // CpusetMems is the set of memory nodes the container will use.
+ // Formatted as `0-3` or `0,2`. Default (if unset) is all memory nodes.
+ CpusetMems string `json:"CpusetMems"`
+ // Devices is a list of device nodes that will be added to the
+ // container.
+ // These are stored in the OCI spec only as type, major, minor while we
+ // display the host path. We convert this with /sys/dev, but we cannot
+ // guarantee that the host path will be identical - only that the actual
+ // device will be.
+ Devices []InspectDevice `json:"Devices"`
+ // DiskQuota is the maximum amount of disk space the container may use
+ // (in bytes).
+ // Presently not populated.
+ // TODO.
+ DiskQuota uint64 `json:"DiskQuota"`
+ // KernelMemory is the maximum amount of memory the kernel will devote
+ // to the container.
+ KernelMemory int64 `json:"KernelMemory"`
+ // MemoryReservation is the reservation (soft limit) of memory available
+ // to the container. Soft limits are warnings only and can be exceeded.
+ MemoryReservation int64 `json:"MemoryReservation"`
+ // MemorySwap is the total limit for all memory available to the
+ // container, including swap. 0 indicates that there is no limit to the
+ // amount of memory available.
+ MemorySwap int64 `json:"MemorySwap"`
+ // MemorySwappiness is the willingness of the kernel to page container
+ // memory to swap. It is an integer from 0 to 100, with low numbers
+ // being more likely to be put into swap.
+ // -1, the default, will not set swappiness and use the system defaults.
+ MemorySwappiness int64 `json:"MemorySwappiness"`
+ // OomKillDisable indicates whether the kernel OOM killer is disabled
+ // for the container.
+ OomKillDisable bool `json:"OomKillDisable"`
+ // Init indicates whether the container has an init mounted into it.
+ Init bool `json:"Init,omitempty"`
+ // PidsLimit is the maximum number of PIDs what may be created within
+ // the container. 0, the default, indicates no limit.
+ PidsLimit int64 `json:"PidsLimit"`
+ // Ulimits is a set of ulimits that will be set within the container.
+ Ulimits []InspectUlimit `json:"Ulimits"`
+ // CpuCount is Windows-only and not presently implemented.
+ CpuCount uint64 `json:"CpuCount"`
+ // CpuPercent is Windows-only and not presently implemented.
+ CpuPercent uint64 `json:"CpuPercent"`
+ // IOMaximumIOps is Windows-only and not presently implemented.
+ IOMaximumIOps uint64 `json:"IOMaximumIOps"`
+ // IOMaximumBandwidth is Windows-only and not presently implemented.
+ IOMaximumBandwidth uint64 `json:"IOMaximumBandwidth"`
+}
+
+// InspectLogConfig holds information about a container's configured log driver
+// and is presently unused. It is retained for Docker compatibility.
+type InspectLogConfig struct {
+ Type string `json:"Type"`
+ Config map[string]string `json:"Config"` //idk type, TODO
+}
+
+// InspectRestartPolicy holds information about the container's restart policy.
+type InspectRestartPolicy struct {
+ // Name contains the container's restart policy.
+ // Allowable values are "no" or "" (take no action),
+ // "on-failure" (restart on non-zero exit code, with an optional max
+ // retry count), and "always" (always restart on container stop, unless
+ // explicitly requested by API).
+ // Note that this is NOT actually a name of any sort - the poor naming
+ // is for Docker compatibility.
+ Name string `json:"Name"`
+ // MaximumRetryCount is the maximum number of retries allowed if the
+ // "on-failure" restart policy is in use. Not used if "on-failure" is
+ // not set.
+ MaximumRetryCount uint `json:"MaximumRetryCount"`
+}
+
+// InspectBlkioWeightDevice holds information about the relative weight
+// of an individual device node. Weights are used in the I/O scheduler to give
+// relative priority to some accesses.
+type InspectBlkioWeightDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Weight is the relative weight the scheduler will use when scheduling
+ // I/O.
+ Weight uint16 `json:"Weight"`
+}
+
+// InspectBlkioThrottleDevice holds information about a speed cap for a device
+// node. This cap applies to a specific operation (read, write, etc) on the given
+// node.
+type InspectBlkioThrottleDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Rate is the maximum rate. It is in either bytes per second or iops
+ // per second, determined by where it is used - documentation will
+ // indicate which is appropriate.
+ Rate uint64 `json:"Rate"`
+}
+
+// InspectUlimit is a ulimit that will be applied to the container.
+type InspectUlimit struct {
+ // Name is the name (type) of the ulimit.
+ Name string `json:"Name"`
+ // Soft is the soft limit that will be applied.
+ Soft uint64 `json:"Soft"`
+ // Hard is the hard limit that will be applied.
+ Hard uint64 `json:"Hard"`
+}
+
// InspectMount provides a record of a single mount in a container. It contains
// fields for both named and normal volumes. Only user-specified volumes will be
// included, and tmpfs volumes are not included even if the user specified them.
@@ -131,6 +551,29 @@ type InspectMount struct {
Propagation string `json:"Propagation"`
}
+// InspectDevice is a single device that will be mounted into the container.
+type InspectDevice struct {
+ // PathOnHost is the path of the device on the host.
+ PathOnHost string `json:"PathOnHost"`
+ // PathInContainer is the path of the device within the container.
+ PathInContainer string `json:"PathInContainer"`
+ // CgroupPermissions is the permissions of the mounted device.
+ // Presently not populated.
+ // TODO.
+ CgroupPermissions string `json:"CgroupPermissions"`
+}
+
+// InspectHostPort provides information on a port on the host that a container's
+// port is bound to.
+type InspectHostPort struct {
+ // IP on the host we are bound to. "" if not specified (binding to all
+ // IPs).
+ HostIP string `json:"HostIp"`
+ // Port on the host we are bound to. No special formatting - just an
+ // integer stuffed into a string.
+ HostPort string `json:"HostPort"`
+}
+
// InspectContainerState provides a detailed record of a container's current
// state. It is returned as part of InspectContainerData.
// As with InspectContainerData, many portions of this struct are matched to
@@ -206,7 +649,7 @@ func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*InspectContainerData, error) {
config := c.config
runtimeInfo := c.state
- stateSpec, err := c.specFromState()
+ ctrSpec, err := c.specFromState()
if err != nil {
return nil, err
}
@@ -244,7 +687,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
}
}
- mounts, err := c.getInspectMounts(stateSpec)
+ namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
+ inspectMounts, err := c.getInspectMounts(ctrSpec, namedVolumes, mounts)
if err != nil {
return nil, err
}
@@ -255,7 +699,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
Path: path,
Args: args,
State: &InspectContainerState{
- OciVersion: stateSpec.Version,
+ OciVersion: ctrSpec.Version,
Status: runtimeInfo.State.String(),
Running: runtimeInfo.State == define.ContainerStateRunning,
Paused: runtimeInfo.State == define.ContainerStatePaused,
@@ -285,12 +729,12 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
Driver: driverData.Name,
MountLabel: config.MountLabel,
ProcessLabel: config.ProcessLabel,
- EffectiveCaps: stateSpec.Process.Capabilities.Effective,
- BoundingCaps: stateSpec.Process.Capabilities.Bounding,
- AppArmorProfile: stateSpec.Process.ApparmorProfile,
+ EffectiveCaps: ctrSpec.Process.Capabilities.Effective,
+ BoundingCaps: ctrSpec.Process.Capabilities.Bounding,
+ AppArmorProfile: ctrSpec.Process.ApparmorProfile,
ExecIDs: execIDs,
GraphDriver: driverData,
- Mounts: mounts,
+ Mounts: inspectMounts,
Dependencies: c.Dependencies(),
NetworkSettings: &InspectNetworkSettings{
Bridge: "", // TODO
@@ -338,12 +782,18 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get information on the container's network namespace (if present)
data = c.getContainerNetworkInfo(data)
- inspectConfig, err := c.generateInspectContainerConfig(stateSpec)
+ inspectConfig, err := c.generateInspectContainerConfig(ctrSpec)
if err != nil {
return nil, err
}
data.Config = inspectConfig
+ hostConfig, err := c.generateInspectContainerHostConfig(ctrSpec, namedVolumes, mounts)
+ if err != nil {
+ return nil, err
+ }
+ data.HostConfig = hostConfig
+
if size {
rootFsSize, err := c.rootFsSize()
if err != nil {
@@ -362,7 +812,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get inspect-formatted mounts list.
// Only includes user-specified mounts. Only includes bind mounts and named
// volumes, not tmpfs volumes.
-func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]InspectMount, error) {
+func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]InspectMount, error) {
inspectMounts := []InspectMount{}
// No mounts, return early
@@ -370,7 +820,6 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]InspectMount, error)
return inspectMounts, nil
}
- namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
for _, volume := range namedVolumes {
mountStruct := InspectMount{}
mountStruct.Type = "volume"
@@ -454,9 +903,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectCon
if spec.Process != nil {
ctrConfig.Tty = spec.Process.Terminal
ctrConfig.Env = []string{}
- for _, val := range spec.Process.Env {
- ctrConfig.Env = append(ctrConfig.Env, val)
- }
+ ctrConfig.Env = append(ctrConfig.Env, spec.Process.Env...)
ctrConfig.WorkingDir = spec.Process.Cwd
}
@@ -466,9 +913,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectCon
// Leave empty is not explicitly overwritten by user
if len(c.config.Command) != 0 {
ctrConfig.Cmd = []string{}
- for _, val := range c.config.Command {
- ctrConfig.Cmd = append(ctrConfig.Cmd, val)
- }
+ ctrConfig.Cmd = append(ctrConfig.Cmd, c.config.Command...)
}
// Leave empty if not explicitly overwritten by user
@@ -497,3 +942,505 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectCon
return ctrConfig, nil
}
+
+// Generate the InspectContainerHostConfig struct for the HostConfig field of
+// Inspect.
+func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) (*InspectContainerHostConfig, error) {
+ hostConfig := new(InspectContainerHostConfig)
+
+ logConfig := new(InspectLogConfig)
+ logConfig.Type = c.config.LogDriver
+ hostConfig.LogConfig = logConfig
+
+ restartPolicy := new(InspectRestartPolicy)
+ restartPolicy.Name = c.config.RestartPolicy
+ restartPolicy.MaximumRetryCount = c.config.RestartRetries
+ hostConfig.RestartPolicy = restartPolicy
+
+ hostConfig.Dns = make([]string, 0, len(c.config.DNSServer))
+ for _, dns := range c.config.DNSServer {
+ hostConfig.Dns = append(hostConfig.Dns, dns.String())
+ }
+
+ hostConfig.DnsOptions = make([]string, 0, len(c.config.DNSOption))
+ for _, opt := range c.config.DNSOption {
+ hostConfig.DnsOptions = append(hostConfig.DnsOptions, opt)
+ }
+
+ hostConfig.DnsSearch = make([]string, 0, len(c.config.DNSSearch))
+ for _, search := range c.config.DNSSearch {
+ hostConfig.DnsSearch = append(hostConfig.DnsSearch, search)
+ }
+
+ hostConfig.ExtraHosts = make([]string, 0, len(c.config.HostAdd))
+ for _, host := range c.config.HostAdd {
+ hostConfig.ExtraHosts = append(hostConfig.ExtraHosts, host)
+ }
+
+ hostConfig.GroupAdd = make([]string, 0, len(c.config.Groups))
+ for _, group := range c.config.Groups {
+ hostConfig.GroupAdd = append(hostConfig.GroupAdd, group)
+ }
+
+ hostConfig.SecurityOpt = []string{}
+ if ctrSpec.Process != nil {
+ if ctrSpec.Process.OOMScoreAdj != nil {
+ hostConfig.OomScoreAdj = *ctrSpec.Process.OOMScoreAdj
+ }
+ if ctrSpec.Process.NoNewPrivileges {
+ hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, "no-new-privileges")
+ }
+ }
+
+ hostConfig.ReadonlyRootfs = ctrSpec.Root.Readonly
+ hostConfig.ShmSize = c.config.ShmSize
+ hostConfig.Runtime = "oci"
+
+ // This is very expensive to initialize.
+ // So we don't want to initialize it unless we absolutely have to - IE,
+ // there are things that require a major:minor to path translation.
+ var deviceNodes map[string]string
+
+ // Annotations
+ if ctrSpec.Annotations != nil {
+ hostConfig.ContainerIDFile = ctrSpec.Annotations[InspectAnnotationCIDFile]
+ if ctrSpec.Annotations[InspectAnnotationAutoremove] == InspectResponseTrue {
+ hostConfig.AutoRemove = true
+ }
+ if ctrs, ok := ctrSpec.Annotations[InspectAnnotationVolumesFrom]; ok {
+ hostConfig.VolumesFrom = strings.Split(ctrs, ",")
+ }
+ if ctrSpec.Annotations[InspectAnnotationPrivileged] == InspectResponseTrue {
+ hostConfig.Privileged = true
+ }
+ if ctrSpec.Annotations[InspectAnnotationInit] == InspectResponseTrue {
+ hostConfig.Init = true
+ }
+ if label, ok := ctrSpec.Annotations[InspectAnnotationLabel]; ok {
+ hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("label=%s", label))
+ }
+ if seccomp, ok := ctrSpec.Annotations[InspectAnnotationSeccomp]; ok {
+ hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
+ }
+ if apparmor, ok := ctrSpec.Annotations[InspectAnnotationApparmor]; ok {
+ hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
+ }
+ }
+
+ // Resource limits
+ if ctrSpec.Linux != nil {
+ if ctrSpec.Linux.Resources != nil {
+ if ctrSpec.Linux.Resources.CPU != nil {
+ if ctrSpec.Linux.Resources.CPU.Shares != nil {
+ hostConfig.CpuShares = *ctrSpec.Linux.Resources.CPU.Shares
+ }
+ if ctrSpec.Linux.Resources.CPU.Period != nil {
+ hostConfig.CpuPeriod = *ctrSpec.Linux.Resources.CPU.Period
+ }
+ if ctrSpec.Linux.Resources.CPU.Quota != nil {
+ hostConfig.CpuQuota = *ctrSpec.Linux.Resources.CPU.Quota
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimePeriod != nil {
+ hostConfig.CpuRealtimePeriod = *ctrSpec.Linux.Resources.CPU.RealtimePeriod
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimeRuntime != nil {
+ hostConfig.CpuRealtimeRuntime = *ctrSpec.Linux.Resources.CPU.RealtimeRuntime
+ }
+ hostConfig.CpusetCpus = ctrSpec.Linux.Resources.CPU.Cpus
+ hostConfig.CpusetMems = ctrSpec.Linux.Resources.CPU.Mems
+ }
+ if ctrSpec.Linux.Resources.Memory != nil {
+ if ctrSpec.Linux.Resources.Memory.Limit != nil {
+ hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
+ }
+ if ctrSpec.Linux.Resources.Memory.Kernel != nil {
+ hostConfig.KernelMemory = *ctrSpec.Linux.Resources.Memory.Kernel
+ }
+ if ctrSpec.Linux.Resources.Memory.Reservation != nil {
+ hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
+ }
+ if ctrSpec.Linux.Resources.Memory.Swap != nil {
+ hostConfig.MemorySwap = *ctrSpec.Linux.Resources.Memory.Swap
+ }
+ if ctrSpec.Linux.Resources.Memory.Swappiness != nil {
+ hostConfig.MemorySwappiness = int64(*ctrSpec.Linux.Resources.Memory.Swappiness)
+ } else {
+ // Swappiness has a default of -1
+ hostConfig.MemorySwappiness = -1
+ }
+ if ctrSpec.Linux.Resources.Memory.DisableOOMKiller != nil {
+ hostConfig.OomKillDisable = *ctrSpec.Linux.Resources.Memory.DisableOOMKiller
+ }
+ }
+ if ctrSpec.Linux.Resources.Pids != nil {
+ hostConfig.PidsLimit = ctrSpec.Linux.Resources.Pids.Limit
+ }
+ if ctrSpec.Linux.Resources.BlockIO != nil {
+ if ctrSpec.Linux.Resources.BlockIO.Weight != nil {
+ hostConfig.BlkioWeight = *ctrSpec.Linux.Resources.BlockIO.Weight
+ }
+ hostConfig.BlkioWeightDevice = []InspectBlkioWeightDevice{}
+ for _, dev := range ctrSpec.Linux.Resources.BlockIO.WeightDevice {
+ key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
+ // TODO: how do we handle LeafWeight vs
+ // Weight? For now, ignore anything
+ // without Weight set.
+ if dev.Weight == nil {
+ logrus.Warnf("Ignoring weight device %s as it lacks a weight", key)
+ continue
+ }
+ if deviceNodes == nil {
+ nodes, err := util.FindDeviceNodes()
+ if err != nil {
+ return nil, err
+ }
+ deviceNodes = nodes
+ }
+ path, ok := deviceNodes[key]
+ if !ok {
+ logrus.Warnf("Could not locate weight device %s in system devices", key)
+ continue
+ }
+ weightDev := InspectBlkioWeightDevice{}
+ weightDev.Path = path
+ weightDev.Weight = *dev.Weight
+ hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
+ }
+
+ handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]InspectBlkioThrottleDevice, error) {
+ out := []InspectBlkioThrottleDevice{}
+ for _, dev := range devs {
+ key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
+ if deviceNodes == nil {
+ nodes, err := util.FindDeviceNodes()
+ if err != nil {
+ return nil, err
+ }
+ deviceNodes = nodes
+ }
+ path, ok := deviceNodes[key]
+ if !ok {
+ logrus.Warnf("Could not locate throttle device %s in system devices", key)
+ continue
+ }
+ throttleDev := InspectBlkioThrottleDevice{}
+ throttleDev.Path = path
+ throttleDev.Rate = dev.Rate
+ out = append(out, throttleDev)
+ }
+ return out, nil
+ }
+
+ readBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceReadBps = readBps
+
+ writeBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceWriteBps = writeBps
+
+ readIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceReadIOps = readIops
+
+ writeIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
+ if err != nil {
+ return nil, err
+ }
+ hostConfig.BlkioDeviceWriteIOps = writeIops
+ }
+ }
+ }
+
+ // NanoCPUs.
+ // This is only calculated if CpuPeriod == 100000.
+ // It is given in nanoseconds, versus the microseconds used elsewhere -
+ // so multiply by 10000 (not sure why, but 1000 is off by 10).
+ if hostConfig.CpuPeriod == 100000 {
+ hostConfig.NanoCpus = 10000 * hostConfig.CpuQuota
+ }
+
+ // Bind mounts, formatted as src:dst.
+ // We'll be appending some options that aren't necessarily in the
+ // original command line... but no helping that from inside libpod.
+ binds := []string{}
+ tmpfs := make(map[string]string)
+ for _, namedVol := range namedVolumes {
+ if len(namedVol.Options) > 0 {
+ binds = append(binds, fmt.Sprintf("%s:%s:%s", namedVol.Name, namedVol.Dest, strings.Join(namedVol.Options, ",")))
+ } else {
+ binds = append(binds, fmt.Sprintf("%s:%s", namedVol.Name, namedVol.Dest))
+ }
+ }
+ for _, mount := range mounts {
+ if mount.Type == "tmpfs" {
+ tmpfs[mount.Destination] = strings.Join(mount.Options, ",")
+ } else {
+ // TODO - maybe we should parse for empty source/destination
+ // here. Would be confusing if we print just a bare colon.
+ if len(mount.Options) > 0 {
+ binds = append(binds, fmt.Sprintf("%s:%s:%s", mount.Source, mount.Destination, strings.Join(mount.Options, ",")))
+ } else {
+ binds = append(binds, fmt.Sprintf("%s:%s", mount.Source, mount.Destination))
+ }
+ }
+ }
+ hostConfig.Binds = binds
+ hostConfig.Tmpfs = tmpfs
+
+ // Network mode parsing.
+ networkMode := ""
+ if c.config.CreateNetNS {
+ networkMode = "default"
+ } else if c.config.NetNsCtr != "" {
+ networkMode = fmt.Sprintf("container:%s", c.config.NetNsCtr)
+ } else {
+ // Find the spec's network namespace.
+ // If there is none, it's host networking.
+ // If there is one and it has a path, it's "ns:".
+ foundNetNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.NetworkNamespace {
+ foundNetNS = true
+ if ns.Path != "" {
+ networkMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ networkMode = "none"
+ }
+ break
+ }
+ }
+ if !foundNetNS {
+ networkMode = "host"
+ }
+ }
+ hostConfig.NetworkMode = networkMode
+
+ // Port bindings.
+ // Only populate if we're using CNI to configure the network.
+ portBindings := make(map[string][]InspectHostPort)
+ if c.config.CreateNetNS {
+ for _, port := range c.config.PortMappings {
+ key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
+ hostPorts := portBindings[key]
+ if hostPorts == nil {
+ hostPorts = []InspectHostPort{}
+ }
+ hostPorts = append(hostPorts, InspectHostPort{
+ HostIP: port.HostIP,
+ HostPort: fmt.Sprintf("%d", port.HostPort),
+ })
+ portBindings[key] = hostPorts
+ }
+ }
+ hostConfig.PortBindings = portBindings
+
+ // Cap add and cap drop.
+ // We need a default set of capabilities to compare against.
+ // The OCI generate package has one, and is commonly used, so we'll
+ // use it.
+ // Problem: there are 5 sets of capabilities.
+ // Use the bounding set for this computation, it's the most encompassing
+ // (but still not perfect).
+ capAdd := []string{}
+ capDrop := []string{}
+ // No point in continuing if we got a spec without a Process block...
+ if ctrSpec.Process != nil {
+ // Max an O(1) lookup table for default bounding caps.
+ boundingCaps := make(map[string]bool)
+ g, err := generate.New("linux")
+ if err != nil {
+ return nil, err
+ }
+ if !hostConfig.Privileged {
+ for _, cap := range g.Config.Process.Capabilities.Bounding {
+ boundingCaps[cap] = true
+ }
+ } else {
+ // If we are privileged, use all caps.
+ for _, cap := range capability.List() {
+ if g.HostSpecific && cap > validate.LastCap() {
+ continue
+ }
+ boundingCaps[fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))] = true
+ }
+ }
+ // Iterate through spec caps.
+ // If it's not in default bounding caps, it was added.
+ // If it is, delete from the default set. Whatever remains after
+ // we finish are the dropped caps.
+ for _, cap := range ctrSpec.Process.Capabilities.Bounding {
+ if _, ok := boundingCaps[cap]; ok {
+ delete(boundingCaps, cap)
+ } else {
+ capAdd = append(capAdd, cap)
+ }
+ }
+ for cap := range boundingCaps {
+ capDrop = append(capDrop, cap)
+ }
+ }
+ hostConfig.CapAdd = capAdd
+ hostConfig.CapDrop = capDrop
+
+ // IPC Namespace mode
+ ipcMode := ""
+ if c.config.IPCNsCtr != "" {
+ ipcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
+ } else {
+ // Locate the spec's IPC namespace.
+ // If there is none, it's ipc=host.
+ // If there is one and it has a path, it's "ns:".
+ // If no path, it's default - the empty string.
+ foundIPCNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.IPCNamespace {
+ foundIPCNS = true
+ if ns.Path != "" {
+ ipcMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundIPCNS {
+ ipcMode = "host"
+ }
+ }
+ hostConfig.IpcMode = ipcMode
+
+ // CGroup parent
+ // Need to check if it's the default, and not print if so.
+ defaultCgroupParent := ""
+ switch c.runtime.config.CgroupManager {
+ case CgroupfsCgroupsManager:
+ defaultCgroupParent = CgroupfsDefaultCgroupParent
+ case SystemdCgroupsManager:
+ defaultCgroupParent = SystemdDefaultCgroupParent
+ }
+ if c.config.CgroupParent != defaultCgroupParent {
+ hostConfig.CgroupParent = c.config.CgroupParent
+ }
+
+ // PID namespace mode
+ pidMode := ""
+ if c.config.PIDNsCtr != "" {
+ pidMode = fmt.Sprintf("container:%s", c.config.PIDNsCtr)
+ } else {
+ // Locate the spec's PID namespace.
+ // If there is none, it's pid=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's default - the empty string.
+ foundPIDNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.PIDNamespace {
+ foundPIDNS = true
+ if ns.Path != "" {
+ pidMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundPIDNS {
+ pidMode = "host"
+ }
+ }
+ hostConfig.PidMode = pidMode
+
+ // UTS namespace mode
+ utsMode := ""
+ if c.config.UTSNsCtr != "" {
+ utsMode = fmt.Sprintf("container:%s", c.config.UTSNsCtr)
+ } else {
+ // Locate the spec's UTS namespace.
+ // If there is none, it's uts=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's default - the empty string.
+ foundUTSNS := false
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.UTSNamespace {
+ foundUTSNS = true
+ if ns.Path != "" {
+ utsMode = fmt.Sprintf("ns:%s", ns.Path)
+ }
+ break
+ }
+ }
+ if !foundUTSNS {
+ utsMode = "host"
+ }
+ }
+ hostConfig.UTSMode = utsMode
+
+ // User namespace mode
+ usernsMode := ""
+ if c.config.UserNsCtr != "" {
+ usernsMode = fmt.Sprintf("container:%s", c.config.UserNsCtr)
+ } else {
+ // Locate the spec's user namespace.
+ // If there is none, it's default - the empty string.
+ // If there is one, it's "private" if no path, or "ns:" if
+ // there's a path.
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.UserNamespace {
+ if ns.Path != "" {
+ usernsMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ usernsMode = "private"
+ }
+ }
+ }
+ }
+ hostConfig.UsernsMode = usernsMode
+
+ // Devices
+ // Do not include if privileged - assumed that all devices will be
+ // included.
+ hostConfig.Devices = []InspectDevice{}
+ if ctrSpec.Linux != nil && !hostConfig.Privileged {
+ for _, dev := range ctrSpec.Linux.Devices {
+ key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
+ if deviceNodes == nil {
+ nodes, err := util.FindDeviceNodes()
+ if err != nil {
+ return nil, err
+ }
+ deviceNodes = nodes
+ }
+ path, ok := deviceNodes[key]
+ if !ok {
+ logrus.Warnf("Could not locate device %s on host", key)
+ continue
+ }
+ newDev := InspectDevice{}
+ newDev.PathOnHost = path
+ newDev.PathInContainer = dev.Path
+ hostConfig.Devices = append(hostConfig.Devices, newDev)
+ }
+ }
+
+ // Ulimits
+ hostConfig.Ulimits = []InspectUlimit{}
+ if ctrSpec.Process != nil {
+ for _, limit := range ctrSpec.Process.Rlimits {
+ newLimit := InspectUlimit{}
+ newLimit.Name = limit.Type
+ newLimit.Soft = limit.Soft
+ newLimit.Hard = limit.Hard
+ hostConfig.Ulimits = append(hostConfig.Ulimits, newLimit)
+ }
+ }
+
+ // Terminal size
+ // We can't actually get this for now...
+ // So default to something sane.
+ // TODO: Populate this.
+ hostConfig.ConsoleSize = []uint{0, 0}
+
+ return hostConfig, nil
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index c409da96a..47b425c0a 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -815,34 +815,6 @@ func (c *Container) checkDependenciesRunning() ([]string, error) {
return notRunning, nil
}
-// Check if a container's dependencies are running
-// Returns a []string containing the IDs of dependencies that are not running
-// Assumes depencies are already locked, and will be passed in
-// Accepts a map[string]*Container containing, at a minimum, the locked
-// dependency containers
-// (This must be a map from container ID to container)
-func (c *Container) checkDependenciesRunningLocked(depCtrs map[string]*Container) ([]string, error) {
- deps := c.Dependencies()
- notRunning := []string{}
-
- for _, dep := range deps {
- depCtr, ok := depCtrs[dep]
- if !ok {
- return nil, errors.Wrapf(define.ErrNoSuchCtr, "container %s depends on container %s but it is not on containers passed to checkDependenciesRunning", c.ID(), dep)
- }
-
- if err := c.syncContainer(); err != nil {
- return nil, err
- }
-
- if depCtr.state.State != define.ContainerStateRunning {
- notRunning = append(notRunning, dep)
- }
- }
-
- return notRunning, nil
-}
-
func (c *Container) completeNetworkSetup() error {
netDisabled, err := c.NetworkDisabled()
if err != nil {
diff --git a/libpod/events/filters.go b/libpod/events/filters.go
index 9a64082d1..b3c5eda6e 100644
--- a/libpod/events/filters.go
+++ b/libpod/events/filters.go
@@ -1,7 +1,6 @@
package events
import (
- "fmt"
"strings"
"time"
@@ -23,7 +22,7 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
}, nil
case "EVENT", "STATUS":
return func(e *Event) bool {
- return fmt.Sprintf("%s", e.Status) == filterValue
+ return string(e.Status) == filterValue
}, nil
case "IMAGE":
return func(e *Event) bool {
@@ -54,7 +53,7 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
}, nil
case "TYPE":
return func(e *Event) bool {
- return fmt.Sprintf("%s", e.Type) == filterValue
+ return string(e.Type) == filterValue
}, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
diff --git a/libpod/events/nullout.go b/libpod/events/nullout.go
index 7d811a9c7..b11afcf80 100644
--- a/libpod/events/nullout.go
+++ b/libpod/events/nullout.go
@@ -17,7 +17,6 @@ func (e EventToNull) Read(options ReadOptions) error {
// NewNullEventer returns a new null eventer. You should only do this for
// the purposes on internal libpod testing.
func NewNullEventer() Eventer {
- var e Eventer
- e = EventToNull{}
+ e := EventToNull{}
return e
}
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index f4ea6c694..8ed2b12e1 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -107,16 +107,25 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) {
capture bytes.Buffer
inStartPeriod bool
)
- hcStatus, err := checkHealthCheckCanBeRun(c)
- if err != nil {
- return hcStatus, err
- }
hcCommand := c.HealthCheckConfig().Test
- if len(hcCommand) > 0 && hcCommand[0] == "CMD-SHELL" {
- newCommand = []string{"sh", "-c", strings.Join(hcCommand[1:], " ")}
- } else {
+ if len(hcCommand) < 1 {
+ return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID())
+ }
+ switch hcCommand[0] {
+ case "", "NONE":
+ return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID())
+ case "CMD":
+ newCommand = hcCommand[1:]
+ case "CMD-SHELL":
+ // TODO: SHELL command from image not available in Container - use Docker default
+ newCommand = []string{"/bin/sh", "-c", strings.Join(hcCommand[1:], " ")}
+ default:
+ // command supplied on command line - pass as-is
newCommand = hcCommand
}
+ if len(newCommand) < 1 || newCommand[0] == "" {
+ return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID())
+ }
captureBuffer := bufio.NewWriter(&capture)
hcw := hcWriteCloser{
captureBuffer,
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 53fb271d1..dca72430d 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -62,7 +62,7 @@ func (c *Container) createTimer() error {
if rootless.IsRootless() {
cmd = append(cmd, "--user")
}
- cmd = append(cmd, "--unit", fmt.Sprintf("%s", c.ID()), fmt.Sprintf("--on-unit-inactive=%s", c.HealthCheckConfig().Interval.String()), "--timer-property=AccuracySec=1s", podman, "healthcheck", "run", c.ID())
+ cmd = append(cmd, "--unit", c.ID(), fmt.Sprintf("--on-unit-inactive=%s", c.HealthCheckConfig().Interval.String()), "--timer-property=AccuracySec=1s", podman, "healthcheck", "run", c.ID())
conn, err := getConnection()
if err != nil {
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 6509134ac..f9879b85b 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -461,7 +461,11 @@ func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.Sys
if err != nil {
return "", err
}
- defer newImg.Close()
+ defer func() {
+ if err := newImg.Close(); err != nil {
+ logrus.Errorf("failed to close image: %q", err)
+ }
+ }()
imageDigest := newImg.ConfigInfo().Digest
if err = imageDigest.Validate(); err != nil {
return "", errors.Wrapf(err, "error getting config info")
@@ -513,7 +517,7 @@ func (i *Image) TagImage(tag string) error {
if err := i.reloadImage(); err != nil {
return err
}
- defer i.newImageEvent(events.Tag)
+ i.newImageEvent(events.Tag)
return nil
}
@@ -538,7 +542,7 @@ func (i *Image) UntagImage(tag string) error {
if err := i.reloadImage(); err != nil {
return err
}
- defer i.newImageEvent(events.Untag)
+ i.newImageEvent(events.Untag)
return nil
}
@@ -574,7 +578,11 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
if err != nil {
return err
}
- defer policyContext.Destroy()
+ defer func() {
+ if err := policyContext.Destroy(); err != nil {
+ logrus.Errorf("failed to destroy policy context: %q", err)
+ }
+ }()
// Look up the source image, expecting it to be in local storage
src, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID())
@@ -588,7 +596,7 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
if err != nil {
return errors.Wrapf(err, "Error copying image to the remote destination")
}
- defer i.newImageEvent(events.Push)
+ i.newImageEvent(events.Push)
return nil
}
@@ -984,11 +992,15 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
if err != nil {
return nil, err
}
- defer policyContext.Destroy()
+ defer func() {
+ if err := policyContext.Destroy(); err != nil {
+ logrus.Errorf("failed to destroy policy context: %q", err)
+ }
+ }()
copyOptions := getCopyOptions(sc, writer, nil, nil, signingOptions, "", nil)
dest, err := is.Transport.ParseStoreReference(ir.store, reference)
if err != nil {
- errors.Wrapf(err, "error getting image reference for %q", reference)
+ return nil, errors.Wrapf(err, "error getting image reference for %q", reference)
}
_, err = cp.Image(ctx, policyContext, dest, src, copyOptions)
if err != nil {
@@ -996,7 +1008,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
}
newImage, err := ir.NewFromLocal(reference)
if err == nil {
- defer newImage.newImageEvent(events.Import)
+ newImage.newImageEvent(events.Import)
}
return newImage, err
}
@@ -1339,7 +1351,7 @@ 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 {
return errors.Wrapf(err, "unable to save %q", source)
}
- defer i.newImageEvent(events.Save)
+ i.newImageEvent(events.Save)
return nil
}
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index ce8a19fbc..2f1d1e912 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -249,7 +249,11 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
if err != nil {
return nil, err
}
- defer policyContext.Destroy()
+ defer func() {
+ if err := policyContext.Destroy(); err != nil {
+ logrus.Errorf("failed to destroy policy context: %q", err)
+ }
+ }()
systemRegistriesConfPath := registries.SystemRegistriesConfPath()
@@ -263,7 +267,7 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
copyOptions.SourceCtx.SystemRegistriesConfPath = systemRegistriesConfPath // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place.
// Print the following statement only when pulling from a docker or atomic registry
if writer != nil && (imageInfo.srcRef.Transport().Name() == DockerTransport || imageInfo.srcRef.Transport().Name() == AtomicTransport) {
- if _, err := io.WriteString(writer, fmt.Sprintf("Trying to pull %s...", imageInfo.image)); err != nil {
+ if _, err := io.WriteString(writer, fmt.Sprintf("Trying to pull %s...\n", imageInfo.image)); err != nil {
return nil, err
}
}
@@ -304,7 +308,7 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
return nil, pullErrors
}
if len(images) > 0 {
- defer ir.newImageEvent(events.Pull, images[0])
+ ir.newImageEvent(events.Pull, images[0])
}
return images, nil
}
diff --git a/libpod/kube.go b/libpod/kube.go
index 409937010..b114cda72 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -1,7 +1,6 @@
package libpod
import (
- "fmt"
"math/rand"
"os"
"strconv"
@@ -179,7 +178,7 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
labels["app"] = removeUnderscores(podName)
om := v12.ObjectMeta{
// The name of the pod is container_name-libpod
- Name: fmt.Sprintf("%s", removeUnderscores(podName)),
+ Name: removeUnderscores(podName),
Labels: labels,
// CreationTimestamp seems to be required, so adding it; in doing so, the timestamp
// will reflect time this is run (not container create time) because the conversion
diff --git a/libpod/logs/log.go b/libpod/logs/log.go
index 488291cfe..0b1703567 100644
--- a/libpod/logs/log.go
+++ b/libpod/logs/log.go
@@ -156,8 +156,5 @@ func NewLogLine(line string) (*LogLine, error) {
// Partial returns a bool if the log line is a partial log type
func (l *LogLine) Partial() bool {
- if l.ParseLogType == PartialLogType {
- return true
- }
- return false
+ return l.ParseLogType == PartialLogType
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 987c1fc5b..bef3f7739 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -5,7 +5,6 @@ package libpod
import (
"crypto/rand"
"fmt"
- "github.com/containers/libpod/pkg/errorhandling"
"net"
"os"
"os/exec"
@@ -17,6 +16,7 @@ import (
cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/firewall"
"github.com/containers/libpod/pkg/netns"
"github.com/containers/libpod/pkg/rootless"
@@ -151,8 +151,8 @@ func checkSlirpFlags(path string) (bool, bool, error) {
// Configure the network namespace for a rootless container
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
- defer ctr.rootlessSlirpSyncR.Close()
- defer ctr.rootlessSlirpSyncW.Close()
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW)
path := r.config.NetworkCmdPath
@@ -201,7 +201,11 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if err := cmd.Start(); err != nil {
return errors.Wrapf(err, "failed to start slirp4netns process")
}
- defer cmd.Process.Release()
+ defer func() {
+ if err := cmd.Process.Release(); err != nil {
+ logrus.Errorf("unable to release comman process: %q", err)
+ }
+ }()
b := make([]byte, 16)
for {
@@ -268,7 +272,11 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if err != nil {
return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
}
- defer conn.Close()
+ defer func() {
+ if err := conn.Close(); err != nil {
+ logrus.Errorf("unable to close connection: %q", err)
+ }
+ }()
hostIP := i.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
diff --git a/libpod/oci.go b/libpod/oci.go
index 6aad79cdf..566cbd821 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -273,7 +273,9 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro
}
return errors.Wrapf(err, "error getting container %s state. stderr/out: %s", ctr.ID(), out)
}
- defer cmd.Wait()
+ defer func() {
+ _ = cmd.Wait()
+ }()
if err := errPipe.Close(); err != nil {
return err
diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go
index ca13d5517..1182457f4 100644
--- a/libpod/oci_linux.go
+++ b/libpod/oci_linux.go
@@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
+ "strconv"
"strings"
"syscall"
"time"
@@ -124,7 +125,11 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor
if err = unix.Unshare(unix.CLONE_NEWNS); err != nil {
return err
}
- defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS)
+ defer func() {
+ if err := unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS); err != nil {
+ logrus.Errorf("unable to clone new namespace: %q", err)
+ }
+ }()
// don't spread our mounts around. We are setting only /sys to be slave
// so that the cleanup process is still able to umount the storage and the
@@ -376,7 +381,9 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
errorhandling.CloseQuiet(childPipe)
return err
}
- defer cmd.Wait()
+ defer func() {
+ _ = cmd.Wait()
+ }()
// We don't need childPipe on the parent side
if err := childPipe.Close(); err != nil {
@@ -455,8 +462,21 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
return errors.Wrapf(define.ErrInternal, "container create failed")
}
ctr.state.PID = ss.si.Pid
- if cmd.Process != nil {
- ctr.state.ConmonPID = cmd.Process.Pid
+ // Let's try reading the Conmon pid at the same time.
+ if ctr.config.ConmonPidFile != "" {
+ contents, err := ioutil.ReadFile(ctr.config.ConmonPidFile)
+ if err != nil {
+ logrus.Warnf("Error reading Conmon pidfile for container %s: %v", ctr.ID(), err)
+ } else {
+ // Convert it to an int
+ conmonPID, err := strconv.Atoi(string(contents))
+ if err != nil {
+ logrus.Warnf("Error decoding Conmon PID %q for container %s: %v", string(contents), ctr.ID(), err)
+ } else {
+ ctr.state.ConmonPID = conmonPID
+ logrus.Infof("Got Conmon PID as %d", conmonPID)
+ }
+ }
}
case <-time.After(ContainerCreateTimeout):
return errors.Wrapf(define.ErrInternal, "container creation timeout")
diff --git a/libpod/options.go b/libpod/options.go
index 4f8bb42df..8d41764a9 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1152,10 +1152,7 @@ func WithUserVolumes(volumes []string) CtrCreateOption {
}
ctr.config.UserVolumes = make([]string, 0, len(volumes))
- for _, vol := range volumes {
- ctr.config.UserVolumes = append(ctr.config.UserVolumes, vol)
- }
-
+ ctr.config.UserVolumes = append(ctr.config.UserVolumes, volumes...)
return nil
}
}
@@ -1172,10 +1169,7 @@ func WithEntrypoint(entrypoint []string) CtrCreateOption {
}
ctr.config.Entrypoint = make([]string, 0, len(entrypoint))
- for _, str := range entrypoint {
- ctr.config.Entrypoint = append(ctr.config.Entrypoint, str)
- }
-
+ ctr.config.Entrypoint = append(ctr.config.Entrypoint, entrypoint...)
return nil
}
}
@@ -1192,10 +1186,7 @@ func WithCommand(command []string) CtrCreateOption {
}
ctr.config.Command = make([]string, 0, len(command))
- for _, str := range command {
- ctr.config.Command = append(ctr.config.Command, str)
- }
-
+ ctr.config.Command = append(ctr.config.Command, command...)
return nil
}
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 9196547a2..08c6cb588 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -854,39 +854,20 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
} else if runtime.noStore {
logrus.Debug("No store required. Not opening container store.")
} else {
- store, err = storage.GetStore(runtime.config.StorageConfig)
- if err != nil {
+ if err := runtime.configureStore(); err != nil {
return err
}
- err = nil
-
- defer func() {
- if err != nil && store != nil {
- // Don't forcibly shut down
- // We could be opening a store in use by another libpod
- _, err2 := store.Shutdown(false)
- if err2 != nil {
- logrus.Errorf("Error removing store for partially-created runtime: %s", err2)
- }
- }
- }()
}
-
- runtime.store = store
- is.Transport.SetStore(store)
-
- // Set up image runtime and store in runtime
- ir := image.NewImageRuntimeFromStore(runtime.store)
-
- runtime.imageRuntime = ir
-
- // Setting signaturepolicypath
- ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath
-
- // Set logfile path for events
- ir.EventsLogFilePath = runtime.config.EventsLogFilePath
- // Set logger type
- ir.EventsLogger = runtime.config.EventsLogger
+ defer func() {
+ if err != nil && store != nil {
+ // Don't forcibly shut down
+ // We could be opening a store in use by another libpod
+ _, err2 := store.Shutdown(false)
+ if err2 != nil {
+ logrus.Errorf("Error removing store for partially-created runtime: %s", err2)
+ }
+ }
+ }()
// Setup the eventer
eventer, err := runtime.newEventer()
@@ -894,7 +875,9 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
return err
}
runtime.eventer = eventer
- ir.Eventer = eventer
+ if runtime.imageRuntime != nil {
+ runtime.imageRuntime.Eventer = eventer
+ }
// Set up a storage service for creating container root filesystems from
// images
@@ -1125,6 +1108,13 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
// If we need to refresh the state, do it now - things are guaranteed to
// be set up by now.
if doRefresh {
+ // Ensure we have a store before refresh occurs
+ if runtime.store == nil {
+ if err := runtime.configureStore(); err != nil {
+ return err
+ }
+ }
+
if err2 := runtime.refresh(runtimeAliveFile); err2 != nil {
return err2
}
@@ -1330,7 +1320,29 @@ func (r *Runtime) generateName() (string, error) {
// The code should never reach here.
}
-// ImageRuntime returns the imageruntime for image resolution
+// Configure store and image runtime
+func (r *Runtime) configureStore() error {
+ store, err := storage.GetStore(r.config.StorageConfig)
+ if err != nil {
+ return err
+ }
+
+ r.store = store
+ is.Transport.SetStore(store)
+
+ ir := image.NewImageRuntimeFromStore(r.store)
+ ir.SignaturePolicyPath = r.config.SignaturePolicyPath
+ ir.EventsLogFilePath = r.config.EventsLogFilePath
+ ir.EventsLogger = r.config.EventsLogger
+
+ r.imageRuntime = ir
+
+ return nil
+}
+
+// ImageRuntime returns the imageruntime for image operations.
+// If WithNoStore() was used, no image runtime will be available, and this
+// function will return nil.
func (r *Runtime) ImageRuntime() *image.Runtime {
return r.imageRuntime
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 760a07daf..4b3aeaa37 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -431,10 +431,12 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
// If we're removing the pod, the container will be evicted
// from the state elsewhere
if !removePod {
- if cleanupErr == nil {
- cleanupErr = err
- } else {
- logrus.Errorf("removing container from pod: %v", err)
+ if err := r.state.RemoveContainerFromPod(pod, c); err != nil {
+ if cleanupErr == nil {
+ cleanupErr = err
+ } else {
+ logrus.Errorf("removing container from pod: %v", err)
+ }
}
}
} else {
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go
index 1514a3414..533e9e3a2 100644
--- a/pkg/adapter/checkpoint_restore.go
+++ b/pkg/adapter/checkpoint_restore.go
@@ -4,16 +4,19 @@ package adapter
import (
"context"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/storage/pkg/archive"
jsoniter "github.com/json-iterator/go"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
+ "github.com/sirupsen/logrus"
)
// Prefixing the checkpoint/restore related functions with 'cr'
@@ -25,7 +28,7 @@ func crImportFromJSON(filePath string, v interface{}) error {
if err != nil {
return errors.Wrapf(err, "Failed to open container definition %s for restore", filePath)
}
- defer jsonFile.Close()
+ defer errorhandling.CloseQuiet(jsonFile)
content, err := ioutil.ReadAll(jsonFile)
if err != nil {
@@ -48,7 +51,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
if err != nil {
return nil, errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
}
- defer archiveFile.Close()
+ defer errorhandling.CloseQuiet(archiveFile)
options := &archive.TarOptions{
// Here we only need the files config.dump and spec.dump
ExcludePatterns: []string{
@@ -63,15 +66,19 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
if err != nil {
return nil, err
}
- defer os.RemoveAll(dir)
+ defer func() {
+ if err := os.RemoveAll(dir); err != nil {
+ logrus.Errorf("could not recursively remove %s: %q", dir, err)
+ }
+ }()
err = archive.Untar(archiveFile, dir, options)
if err != nil {
return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
}
// Load spec.dump from temporary directory
- spec := new(spec.Spec)
- if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), spec); err != nil {
+ dumpSpec := new(spec.Spec)
+ if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil {
return nil, err
}
@@ -113,7 +120,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
}
// Now create a new container from the just loaded information
- container, err := runtime.RestoreContainer(ctx, spec, config)
+ container, err := runtime.RestoreContainer(ctx, dumpSpec, config)
if err != nil {
return nil, err
}
@@ -128,7 +135,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri
return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
}
- if newName == false {
+ if !newName {
// Only check ID for a restore with the same name.
// Using -n to request a new name for the restored container, will also create a new ID
if containerConfig.ID != ctrID {
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index e0245d2ac..7e2384e18 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -213,8 +213,8 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa
c := c
pool.Add(shared.Job{
- c.ID(),
- func() error {
+ ID: c.ID(),
+ Fn: func() error {
err := r.RemoveContainer(ctx, c, cli.Force, cli.Volumes)
if err != nil {
logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error())
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 5e8df1b62..fc23381a4 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -493,6 +493,7 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share
NoTrunc: &c.NoTrunct,
Pod: &c.Pod,
Quiet: &c.Quiet,
+ Size: &c.Size,
Sort: &c.Sort,
Sync: &c.Sync,
}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index a28e1ab4b..2ca4f228f 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -70,8 +70,9 @@ func (r *LocalRuntime) PrunePods(ctx context.Context, cli *cliconfig.PodPruneVal
for _, p := range pods {
p := p
- pool.Add(shared.Job{p.ID(),
- func() error {
+ pool.Add(shared.Job{
+ ID: p.ID(),
+ Fn: func() error {
err := r.Runtime.RemovePod(ctx, p, cli.Force, cli.Force)
if err != nil {
logrus.Debugf("Failed to remove pod %s: %s", p.ID(), err.Error())
@@ -675,7 +676,7 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
if imageData != nil && imageData.Config != nil {
containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...)
}
- if len(containerConfig.Command) != 0 {
+ if len(containerYAML.Command) != 0 {
containerConfig.Command = append(containerConfig.Command, containerYAML.Command...)
} else if imageData != nil && imageData.Config != nil {
containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...)
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 8ef88f36b..e65f07898 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -359,9 +359,6 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
if eventsError != nil {
return eventsError
}
- if err != nil {
- return errors.Wrapf(err, "unable to tail the events log")
- }
w := bufio.NewWriter(os.Stdout)
for event := range eventChannel {
if len(c.Format) > 0 {
diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go
index be7dc0cb6..e3255ecb6 100644
--- a/pkg/adapter/terminal_linux.go
+++ b/pkg/adapter/terminal_linux.go
@@ -39,7 +39,11 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
return err
}
- defer restoreTerminal(oldTermState)
+ defer func() {
+ if err := restoreTerminal(oldTermState); err != nil {
+ logrus.Errorf("unable to restore terminal: %q", err)
+ }
+ }()
}
streams := new(libpod.AttachStreams)
diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go
index 0d01f41e9..479600408 100644
--- a/pkg/apparmor/apparmor_linux.go
+++ b/pkg/apparmor/apparmor_linux.go
@@ -4,6 +4,7 @@ package apparmor
import (
"bufio"
+ "bytes"
"fmt"
"io"
"os"
@@ -104,6 +105,18 @@ func InstallDefault(name string) error {
return cmd.Wait()
}
+// DefaultContent returns the default profile content as byte slice. The
+// profile is named as the provided `name`. The function errors if the profile
+// generation fails.
+func DefaultContent(name string) ([]byte, error) {
+ p := profileData{Name: name}
+ var bytes bytes.Buffer
+ if err := p.generateDefault(&bytes); err != nil {
+ return nil, err
+ }
+ return bytes.Bytes(), nil
+}
+
// IsLoaded checks if a profile with the given name has been loaded into the
// kernel.
func IsLoaded(name string) (bool, error) {
diff --git a/pkg/apparmor/apparmor_linux_test.go b/pkg/apparmor/apparmor_linux_test.go
index ac3260723..e94293d87 100644
--- a/pkg/apparmor/apparmor_linux_test.go
+++ b/pkg/apparmor/apparmor_linux_test.go
@@ -78,10 +78,12 @@ Copyright 2009-2012 Canonical Ltd.
}
}
-func TestInstallDefault(t *testing.T) {
- profile := "libpod-default-testing"
- aapath := "/sys/kernel/security/apparmor/"
+const (
+ aapath = "/sys/kernel/security/apparmor/"
+ profile = "libpod-default-testing"
+)
+func TestInstallDefault(t *testing.T) {
if _, err := os.Stat(aapath); err != nil {
t.Skip("AppArmor isn't available in this environment")
}
@@ -127,3 +129,12 @@ func TestInstallDefault(t *testing.T) {
}
checkLoaded(false)
}
+
+func TestDefaultContent(t *testing.T) {
+ if _, err := os.Stat(aapath); err != nil {
+ t.Skip("AppArmor isn't available in this environment")
+ }
+ if err := DefaultContent(profile); err != nil {
+ t.Fatalf("Couldn't retrieve default AppArmor profile content '%s': %v", profile, err)
+ }
+}
diff --git a/pkg/apparmor/apparmor_unsupported.go b/pkg/apparmor/apparmor_unsupported.go
index b2b4de5f5..13469f1b6 100644
--- a/pkg/apparmor/apparmor_unsupported.go
+++ b/pkg/apparmor/apparmor_unsupported.go
@@ -24,3 +24,8 @@ func CheckProfileAndLoadDefault(name string) (string, error) {
}
return "", ErrApparmorUnsupported
}
+
+// DefaultContent dummy.
+func DefaultContent(name string) ([]byte, error) {
+ return nil, nil
+}
diff --git a/pkg/cgroups/blkio.go b/pkg/cgroups/blkio.go
index 9c2a811d9..bacd4eb93 100644
--- a/pkg/cgroups/blkio.go
+++ b/pkg/cgroups/blkio.go
@@ -37,7 +37,7 @@ func (c *blkioHandler) Create(ctr *CgroupControl) (bool, error) {
// Destroy the cgroup
func (c *blkioHandler) Destroy(ctr *CgroupControl) error {
- return os.Remove(ctr.getCgroupv1Path(Blkio))
+ return rmDirRecursively(ctr.getCgroupv1Path(Blkio))
}
// Stat fills a metrics structure with usage stats for the controller
diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go
index 1dad45d7f..fda19bff8 100644
--- a/pkg/cgroups/cgroups.go
+++ b/pkg/cgroups/cgroups.go
@@ -187,8 +187,12 @@ func createCgroupv2Path(path string) (Err error) {
}()
}
}
- if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), resByte, 0755); err != nil {
- return errors.Wrapf(err, "write %s", filepath.Join(current, "cgroup.subtree_control"))
+ // We enable the controllers for all the path components except the last one. It is not allowed to add
+ // PIDs if there are already enabled controllers.
+ if i < len(elements[3:])-1 {
+ if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), resByte, 0755); err != nil {
+ return errors.Wrapf(err, "write %s", filepath.Join(current, "cgroup.subtree_control"))
+ }
}
}
return nil
@@ -328,6 +332,13 @@ func Load(path string) (*CgroupControl, error) {
systemd: false,
}
if !cgroup2 {
+ controllers, err := getAvailableControllers(handlers, false)
+ if err != nil {
+ return nil, err
+ }
+ control.additionalControllers = controllers
+ }
+ if !cgroup2 {
for name := range handlers {
p := control.getCgroupv1Path(name)
if _, err := os.Stat(p); err != nil {
@@ -355,11 +366,40 @@ func (c *CgroupControl) Delete() error {
return c.DeleteByPath(c.path)
}
+// rmDirRecursively delete recursively a cgroup directory.
+// It differs from os.RemoveAll as it doesn't attempt to unlink files.
+// On cgroupfs we are allowed only to rmdir empty directories.
+func rmDirRecursively(path string) error {
+ if err := os.Remove(path); err == nil || os.IsNotExist(err) {
+ return nil
+ }
+ entries, err := ioutil.ReadDir(path)
+ if err != nil {
+ return errors.Wrapf(err, "read %s", path)
+ }
+ for _, i := range entries {
+ if i.IsDir() {
+ if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil {
+ return err
+ }
+ }
+ }
+ if os.Remove(path); err != nil {
+ if !os.IsNotExist(err) {
+ return errors.Wrapf(err, "remove %s", path)
+ }
+ }
+ return nil
+}
+
// DeleteByPath deletes the specified cgroup path
func (c *CgroupControl) DeleteByPath(path string) error {
if c.systemd {
return systemdDestroy(path)
}
+ if c.cgroup2 {
+ return rmDirRecursively(filepath.Join(cgroupRoot, c.path))
+ }
var lastError error
for _, h := range handlers {
if err := h.Destroy(c); err != nil {
@@ -368,8 +408,11 @@ func (c *CgroupControl) DeleteByPath(path string) error {
}
for _, ctr := range c.additionalControllers {
+ if ctr.symlink {
+ continue
+ }
p := c.getCgroupv1Path(ctr.name)
- if err := os.Remove(p); err != nil {
+ if err := rmDirRecursively(p); err != nil {
lastError = errors.Wrapf(err, "remove %s", p)
}
}
diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go
index 1c8610cc4..03677f1ef 100644
--- a/pkg/cgroups/cpu.go
+++ b/pkg/cgroups/cpu.go
@@ -68,7 +68,7 @@ func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) {
// Destroy the cgroup
func (c *cpuHandler) Destroy(ctr *CgroupControl) error {
- return os.Remove(ctr.getCgroupv1Path(CPU))
+ return rmDirRecursively(ctr.getCgroupv1Path(CPU))
}
// Stat fills a metrics structure with usage stats for the controller
diff --git a/pkg/cgroups/cpuset.go b/pkg/cgroups/cpuset.go
index 25d2f7f76..46d0484f2 100644
--- a/pkg/cgroups/cpuset.go
+++ b/pkg/cgroups/cpuset.go
@@ -3,7 +3,6 @@ package cgroups
import (
"fmt"
"io/ioutil"
- "os"
"path/filepath"
"strings"
@@ -77,7 +76,7 @@ func (c *cpusetHandler) Create(ctr *CgroupControl) (bool, error) {
// Destroy the cgroup
func (c *cpusetHandler) Destroy(ctr *CgroupControl) error {
- return os.Remove(ctr.getCgroupv1Path(CPUset))
+ return rmDirRecursively(ctr.getCgroupv1Path(CPUset))
}
// Stat fills a metrics structure with usage stats for the controller
diff --git a/pkg/cgroups/memory.go b/pkg/cgroups/memory.go
index 80e88d17c..b3991f7e3 100644
--- a/pkg/cgroups/memory.go
+++ b/pkg/cgroups/memory.go
@@ -2,7 +2,6 @@ package cgroups
import (
"fmt"
- "os"
"path/filepath"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -33,7 +32,7 @@ func (c *memHandler) Create(ctr *CgroupControl) (bool, error) {
// Destroy the cgroup
func (c *memHandler) Destroy(ctr *CgroupControl) error {
- return os.Remove(ctr.getCgroupv1Path(Memory))
+ return rmDirRecursively(ctr.getCgroupv1Path(Memory))
}
// Stat fills a metrics structure with usage stats for the controller
diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go
index ffbde100d..65b9b5b34 100644
--- a/pkg/cgroups/pids.go
+++ b/pkg/cgroups/pids.go
@@ -3,7 +3,6 @@ package cgroups
import (
"fmt"
"io/ioutil"
- "os"
"path/filepath"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -40,7 +39,7 @@ func (c *pidHandler) Create(ctr *CgroupControl) (bool, error) {
// Destroy the cgroup
func (c *pidHandler) Destroy(ctr *CgroupControl) error {
- return os.Remove(ctr.getCgroupv1Path(Pids))
+ return rmDirRecursively(ctr.getCgroupv1Path(Pids))
}
// Stat fills a metrics structure with usage stats for the controller
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index ec9276344..7ed95bd0f 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -4,6 +4,63 @@ import (
"strings"
)
+// CgroupMode represents cgroup mode in the container.
+type CgroupMode string
+
+// IsHost indicates whether the container uses the host's cgroup.
+func (n CgroupMode) IsHost() bool {
+ return n == "host"
+}
+
+// IsNS indicates a cgroup namespace passed in by path (ns:<path>)
+func (n CgroupMode) IsNS() bool {
+ return strings.HasPrefix(string(n), "ns:")
+}
+
+// NS gets the path associated with a ns:<path> cgroup ns
+func (n CgroupMode) NS() string {
+ parts := strings.SplitN(string(n), ":", 2)
+ if len(parts) > 1 {
+ return parts[1]
+ }
+ return ""
+}
+
+// IsContainer indicates whether the container uses a new cgroup namespace.
+func (n CgroupMode) IsContainer() bool {
+ parts := strings.SplitN(string(n), ":", 2)
+ return len(parts) > 1 && parts[0] == "container"
+}
+
+// Container returns the name of the container whose cgroup namespace is going to be used.
+func (n CgroupMode) Container() string {
+ parts := strings.SplitN(string(n), ":", 2)
+ if len(parts) > 1 {
+ return parts[1]
+ }
+ return ""
+}
+
+// IsPrivate indicates whether the container uses the a private cgroup.
+func (n CgroupMode) IsPrivate() bool {
+ return n == "private"
+}
+
+// Valid indicates whether the Cgroup namespace is valid.
+func (n CgroupMode) Valid() bool {
+ parts := strings.Split(string(n), ":")
+ switch mode := parts[0]; mode {
+ case "", "host", "private", "ns":
+ case "container":
+ if len(parts) != 2 || parts[1] == "" {
+ return false
+ }
+ default:
+ return false
+ }
+ return true
+}
+
// UsernsMode represents userns mode in the container.
type UsernsMode string
diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go
index 4a515c72a..1d6fb873c 100644
--- a/pkg/netns/netns_linux.go
+++ b/pkg/netns/netns_linux.go
@@ -28,6 +28,7 @@ import (
"sync"
"github.com/containernetworking/plugins/pkg/ns"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -90,7 +91,9 @@ func NewNS() (ns.NetNS, error) {
// Ensure the mount point is cleaned up on errors; if the namespace
// was successfully mounted this will have no effect because the file
// is in-use
- defer os.RemoveAll(nsPath)
+ defer func() {
+ _ = os.RemoveAll(nsPath)
+ }()
var wg sync.WaitGroup
wg.Add(1)
@@ -109,7 +112,11 @@ func NewNS() (ns.NetNS, error) {
if err != nil {
return
}
- defer origNS.Close()
+ defer func() {
+ if err := origNS.Close(); err != nil {
+ logrus.Errorf("unable to close namespace: %q", err)
+ }
+ }()
// create a new netns on the current thread
err = unix.Unshare(unix.CLONE_NEWNET)
@@ -118,7 +125,11 @@ func NewNS() (ns.NetNS, error) {
}
// Put this thread back to the orig ns, since it might get reused (pre go1.10)
- defer origNS.Set()
+ defer func() {
+ if err := origNS.Set(); err != nil {
+ logrus.Errorf("unable to set namespace: %q", err)
+ }
+ }()
// bind mount the netns from the current thread (from /proc) onto the
// mount point. This causes the namespace to persist, even when there
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index d7c2de81d..99a0eb729 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -220,7 +220,11 @@ func EnableLinger() (string, error) {
conn, err := dbus.SystemBus()
if err == nil {
- defer conn.Close()
+ defer func() {
+ if err := conn.Close(); err != nil {
+ logrus.Errorf("unable to close dbus connection: %q", err)
+ }
+ }()
}
lingerEnabled := false
@@ -310,13 +314,21 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) {
if err != nil {
return false, -1, err
}
- defer userNS.Close()
+ defer func() {
+ if err := userNS.Close(); err != nil {
+ logrus.Errorf("unable to close namespace: %q", err)
+ }
+ }()
mountNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/mnt", pid))
if err != nil {
return false, -1, err
}
- defer userNS.Close()
+ defer func() {
+ if err := mountNS.Close(); err != nil {
+ logrus.Errorf("unable to close namespace: %q", err)
+ }
+ }()
fd, err := getUserNSFirstChild(userNS.Fd())
if err != nil {
@@ -364,7 +376,11 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
defer errorhandling.CloseQuiet(r)
defer errorhandling.CloseQuiet(w)
- defer w.Write([]byte("0"))
+ defer func() {
+ if _, err := w.Write([]byte("0")); err != nil {
+ logrus.Errorf("failed to write byte 0: %q", err)
+ }
+ }()
pidC := C.reexec_in_user_namespace(C.int(r.Fd()), cPausePid, cFileToRead, fileOutputFD)
pid := int(pidC)
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index 9f6a4a058..a84e9a72f 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -118,8 +119,44 @@ func (c *CreateConfig) addPrivilegedDevices(g *generate.Generator) error {
return err
}
g.ClearLinuxDevices()
- for _, d := range hostDevices {
- g.AddDevice(Device(d))
+
+ if rootless.IsRootless() {
+ mounts := make(map[string]interface{})
+ for _, m := range g.Mounts() {
+ mounts[m.Destination] = true
+ }
+ newMounts := []spec.Mount{}
+ for _, d := range hostDevices {
+ devMnt := spec.Mount{
+ Destination: d.Path,
+ Type: TypeBind,
+ Source: d.Path,
+ Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"},
+ }
+ if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") {
+ continue
+ }
+ if _, found := mounts[d.Path]; found {
+ continue
+ }
+ st, err := os.Stat(d.Path)
+ if err != nil {
+ if err == unix.EPERM {
+ continue
+ }
+ return errors.Wrapf(err, "stat %s", d.Path)
+ }
+ // Skip devices that the user has not access to.
+ if st.Mode()&0007 == 0 {
+ continue
+ }
+ newMounts = append(newMounts, devMnt)
+ }
+ g.Config.Mounts = append(newMounts, g.Config.Mounts...)
+ } else {
+ for _, d := range hostDevices {
+ g.AddDevice(Device(d))
+ }
}
// Add resources device - need to clear the existing one first.
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 0042ed401..1fb1f829b 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -63,6 +63,7 @@ type CreateConfig struct {
CapDrop []string // cap-drop
CidFile string
ConmonPidFile string
+ Cgroupns string
CgroupParent string // cgroup-parent
Command []string
Detach bool // detach
@@ -101,6 +102,7 @@ type CreateConfig struct {
NetworkAlias []string //network-alias
PidMode namespaces.PidMode //pid
Pod string //pod
+ CgroupMode namespaces.CgroupMode //cgroup
PortBindings nat.PortMap
Privileged bool //privileged
Publish []string //publish
@@ -268,6 +270,23 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
+ if c.CgroupMode.IsNS() {
+ ns := c.CgroupMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.CgroupMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
+ }
+ options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
+ }
+
if c.PidMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
if err != nil {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 5cc021bf5..824c99025 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -80,23 +80,41 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.AddLinuxMaskedPaths("/sys/kernel")
}
}
+ gid5Available := true
if isRootless {
nGids, err := getAvailableGids()
if err != nil {
return nil, err
}
- if nGids < 5 {
- // If we have no GID mappings, the gid=5 default option would fail, so drop it.
- g.RemoveMount("/dev/pts")
- devPts := spec.Mount{
- Destination: "/dev/pts",
- Type: "devpts",
- Source: "devpts",
- Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
+ gid5Available = nGids >= 5
+ }
+ // When using a different user namespace, check that the GID 5 is mapped inside
+ // the container.
+ if gid5Available && len(config.IDMappings.GIDMap) > 0 {
+ mappingFound := false
+ for _, r := range config.IDMappings.GIDMap {
+ if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
+ mappingFound = true
+ break
}
- g.AddMount(devPts)
}
+ if !mappingFound {
+ gid5Available = false
+ }
+
+ }
+ if !gid5Available {
+ // If we have no GID mappings, the gid=5 default option would fail, so drop it.
+ g.RemoveMount("/dev/pts")
+ devPts := spec.Mount{
+ Destination: "/dev/pts",
+ Type: "devpts",
+ Source: "devpts",
+ Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
+ }
+ g.AddMount(devPts)
}
+
if inUserNS && config.IpcMode.IsHost() {
g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{
@@ -246,10 +264,8 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// If privileged, we need to add all the host devices to the
// spec. We do not add the user provided ones because we are
// already adding them all.
- if !rootless.IsRootless() {
- if err := config.AddPrivilegedDevices(&g); err != nil {
- return nil, err
- }
+ if err := config.AddPrivilegedDevices(&g); err != nil {
+ return nil, err
}
} else {
for _, devicePath := range config.Devices {
@@ -307,6 +323,10 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if err := addIpcNS(config, &g); err != nil {
return nil, err
}
+
+ if err := addCgroupNS(config, &g); err != nil {
+ return nil, err
+ }
configSpec := g.Config
// HANDLE CAPABILITIES
@@ -400,6 +420,62 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
+ // Add annotations
+ if configSpec.Annotations == nil {
+ configSpec.Annotations = make(map[string]string)
+ }
+
+ if config.CidFile != "" {
+ configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
+ }
+
+ if config.Rm {
+ configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
+ }
+
+ if len(config.VolumesFrom) > 0 {
+ configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
+ }
+
+ if config.Privileged {
+ configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
+ }
+
+ if config.PublishAll {
+ configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
+ }
+
+ if config.Init {
+ configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
+ } else {
+ configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
+ }
+
+ for _, opt := range config.SecurityOpts {
+ // Split on both : and =
+ splitOpt := strings.Split(opt, "=")
+ if len(splitOpt) == 1 {
+ splitOpt = strings.Split(opt, ":")
+ }
+ if len(splitOpt) < 2 {
+ continue
+ }
+ switch splitOpt[0] {
+ case "label":
+ configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
+ case "seccomp":
+ configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
+ case "apparmor":
+ configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
+ }
+ }
+
return configSpec, nil
}
@@ -548,6 +624,23 @@ func addIpcNS(config *CreateConfig, g *generate.Generator) error {
return nil
}
+func addCgroupNS(config *CreateConfig, g *generate.Generator) error {
+ cgroupMode := config.CgroupMode
+ if cgroupMode.IsNS() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
+ }
+ if cgroupMode.IsHost() {
+ return g.RemoveLinuxNamespace(spec.CgroupNamespace)
+ }
+ if cgroupMode.IsPrivate() {
+ return g.AddOrReplaceLinuxNamespace(spec.CgroupNamespace, "")
+ }
+ if cgroupMode.IsContainer() {
+ logrus.Debug("Using container cgroup mode")
+ }
+ return nil
+}
+
func addRlimits(config *CreateConfig, g *generate.Generator) error {
var (
kernelMax uint64 = 1048576
@@ -557,6 +650,14 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
)
for _, u := range config.Resources.Ulimit {
+ if u == "host" {
+ if len(config.Resources.Ulimit) != 1 {
+ return errors.New("ulimit can use host only once")
+ }
+ g.Config.Process.Rlimits = nil
+ break
+ }
+
ul, err := units.ParseUlimit(u)
if err != nil {
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index ed767f5ba..88f1f6dc1 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -211,6 +211,13 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
}
mount.Options = opts
}
+ if mount.Type == TypeBind {
+ absSrc, err := filepath.Abs(mount.Source)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
+ }
+ mount.Source = absSrc
+ }
finalMounts = append(finalMounts, mount)
}
finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes))
diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go
index 47fa1031f..318bd2b1b 100644
--- a/pkg/util/utils_linux.go
+++ b/pkg/util/utils_linux.go
@@ -1,7 +1,14 @@
package util
import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "syscall"
+
"github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// GetContainerPidInformationDescriptors returns a string slice of all supported
@@ -9,3 +16,39 @@ import (
func GetContainerPidInformationDescriptors() ([]string, error) {
return psgo.ListDescriptors(), nil
}
+
+// FindDeviceNodes parses /dev/ into a set of major:minor -> path, where
+// [major:minor] is the device's major and minor numbers formatted as, for
+// example, 2:0 and path is the path to the device node.
+// Symlinks to nodes are ignored.
+func FindDeviceNodes() (map[string]string, error) {
+ nodes := make(map[string]string)
+ err := filepath.Walk("/dev", func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ logrus.Warnf("Error descending into path %s: %v", path, err)
+ return filepath.SkipDir
+ }
+
+ // If we aren't a device node, do nothing.
+ if info.Mode()&(os.ModeDevice|os.ModeCharDevice) == 0 {
+ return nil
+ }
+
+ // We are a device node. Get major/minor.
+ sysstat, ok := info.Sys().(*syscall.Stat_t)
+ if !ok {
+ return errors.Errorf("Could not convert stat output for use")
+ }
+ major := uint64(sysstat.Rdev / 256)
+ minor := uint64(sysstat.Rdev % 256)
+
+ nodes[fmt.Sprintf("%d:%d", major, minor)] = path
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return nodes, nil
+}
diff --git a/pkg/util/utils_unsupported.go b/pkg/util/utils_unsupported.go
new file mode 100644
index 000000000..62805d7c8
--- /dev/null
+++ b/pkg/util/utils_unsupported.go
@@ -0,0 +1,12 @@
+// +build darwin windows
+
+package util
+
+import (
+ "github.com/pkg/errors"
+)
+
+// FindDeviceNodes is not implemented anywhere except Linux.
+func FindDeviceNodes() (map[string]string, error) {
+ return nil, errors.Errorf("not supported on non-Linux OSes")
+}
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 6855a7231..6f6909fac 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -19,7 +19,6 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/adapter/shortcuts"
- cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
)
@@ -66,32 +65,34 @@ func (i *LibpodAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error {
for _, ctr := range psContainerOutputs {
container := iopodman.PsContainer{
- Id: ctr.ID,
- Image: ctr.Image,
- Command: ctr.Command,
- Created: ctr.Created,
- Ports: ctr.Ports,
- Names: ctr.Names,
- IsInfra: ctr.IsInfra,
- Status: ctr.Status,
- State: ctr.State.String(),
- PidNum: int64(ctr.Pid),
- RootFsSize: ctr.Size.RootFsSize,
- RwSize: ctr.Size.RwSize,
- Pod: ctr.Pod,
- CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano),
- ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano),
- StartedAt: ctr.StartedAt.Format(time.RFC3339Nano),
- Labels: ctr.Labels,
- NsPid: ctr.PID,
- Cgroup: ctr.Cgroup,
- Ipc: ctr.Cgroup,
- Mnt: ctr.MNT,
- Net: ctr.NET,
- PidNs: ctr.PIDNS,
- User: ctr.User,
- Uts: ctr.UTS,
- Mounts: ctr.Mounts,
+ Id: ctr.ID,
+ Image: ctr.Image,
+ Command: ctr.Command,
+ Created: ctr.Created,
+ Ports: ctr.Ports,
+ Names: ctr.Names,
+ IsInfra: ctr.IsInfra,
+ Status: ctr.Status,
+ State: ctr.State.String(),
+ PidNum: int64(ctr.Pid),
+ Pod: ctr.Pod,
+ CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano),
+ ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano),
+ StartedAt: ctr.StartedAt.Format(time.RFC3339Nano),
+ Labels: ctr.Labels,
+ NsPid: ctr.PID,
+ Cgroup: ctr.Cgroup,
+ Ipc: ctr.Cgroup,
+ Mnt: ctr.MNT,
+ Net: ctr.NET,
+ PidNs: ctr.PIDNS,
+ User: ctr.User,
+ Uts: ctr.UTS,
+ Mounts: ctr.Mounts,
+ }
+ if ctr.Size != nil {
+ container.RootFsSize = ctr.Size.RootFsSize
+ container.RwSize = ctr.Size.RwSize
}
containers = append(containers, container)
}
@@ -170,16 +171,7 @@ func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) err
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
- inspectInfo, err := ctr.Inspect(true)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
- artifact, err := getArtifact(ctr)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
-
- data, err := shared.GetCtrInspectInfo(ctr.Config(), inspectInfo, artifact)
+ data, err := ctr.Inspect(true)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -587,18 +579,6 @@ func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, kee
return call.ReplyContainerRestore(ctr.ID())
}
-func getArtifact(ctr *libpod.Container) (*cc.CreateConfig, error) {
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err != nil {
- return nil, err
- }
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- return &createArtifact, nil
-}
-
// ContainerConfig returns just the container.config struct
func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go
index 24a91a86f..31d26c3aa 100644
--- a/pkg/varlinkapi/transfers.go
+++ b/pkg/varlinkapi/transfers.go
@@ -26,11 +26,6 @@ func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int
defer outputFile.Close()
if err = call.ReplySendFile(outputFile.Name()); err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
-
- // FIXME return parameter
- if err = call.ReplySendFile("FIXME_file_handle"); err != nil {
// If an error occurs while sending the reply, return the error
return err
}
diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go
index e8f74e6aa..d3a41f7ab 100644
--- a/pkg/varlinkapi/util.go
+++ b/pkg/varlinkapi/util.go
@@ -191,7 +191,7 @@ func makePsOpts(inOpts iopodman.PsOpts) shared.PsOptions {
Latest: derefBool(inOpts.Latest),
NoTrunc: derefBool(inOpts.NoTrunc),
Pod: derefBool(inOpts.Pod),
- Size: true,
+ Size: derefBool(inOpts.Size),
Sort: derefString(inOpts.Sort),
Namespace: true,
Sync: derefBool(inOpts.Sync),
diff --git a/rootless.md b/rootless.md
index bdbc1becc..c5033881b 100644
--- a/rootless.md
+++ b/rootless.md
@@ -2,7 +2,7 @@
The following list categorizes the known issues and irregularities with running Podman as a non-root user. Although currently functional, there is still a number of work items that are under consideration to be added. These proposed changes are in varying degrees of design and development.
-Contributors are more than welcomed to help with this work. If you decide to carve off a piece and work on it, please create an issue in [GitHub](https://github.com/containers/libpod/issues), and assign it to yourself. If you find other unexpected behaviour with rootless Podman and feel it’s warranted, please feel free to update this document.
+Contributors are more than welcomed to help with this work. If you decide to carve off a piece and work on it, please create an issue in [GitHub](https://github.com/containers/libpod/issues), and assign it to yourself. If you find other unexpected behavior with rootless Podman and feel it’s warranted, please feel free to update this document.
* Podman can not create containers that bind to ports < 1024.
* The kernel does not allow processes without CAP_NET_BIND_SERVICE to bind to low ports.
diff --git a/test/README.md b/test/README.md
index 4e61a0774..9bea679dc 100644
--- a/test/README.md
+++ b/test/README.md
@@ -110,19 +110,30 @@ make shell
This will run a container and give you a shell and you can follow the instructions above.
-# System test
+# System tests
System tests are used for testing the *podman* CLI in the context of a complete system. It
requires that *podman*, all dependencies, and configurations are in place. The intention of
system testing is to match as closely as possible with real-world user/developer use-cases
and environments. The orchestration of the environments and tests is left to external
tooling.
-* `PodmanTestSystem`: System test *struct* as a composite of `PodmanTest`. It will not add any
-options to the command by default. When you run system test, you can set GLOBALOPTIONS,
-PODMAN_SUBCMD_OPTIONS or PODMAN_BINARY in ENV to run the test suite for different test matrices.
+System tests use Bash Automated Testing System (`bats`) as a testing framework.
+Install it via your package manager or get latest stable version
+[directly from the repository](https://github.com/bats-core/bats-core), e.g.:
-## Run system test
-You can run the test with following command:
+```
+mkdir -p ~/tools/bats
+git clone --single-branch --branch v1.1.0 https://github.com/bats-core/bats-core.git ~/tools/bats
+```
+
+Make sure that `bats` binary (`bin/bats` in the repository) is in your `PATH`, if not - add it:
+
+```
+PATH=$PATH:~/tools/bats/bin
+```
+
+## Running system tests
+When `bats` is installed and is in your `PATH`, you can run the test suite with following command:
```
make localsystem
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 21afc4b84..ef1c85518 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -12,7 +12,6 @@ import (
"testing"
"time"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/rootless"
@@ -322,7 +321,7 @@ func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
}
// InspectContainer returns a container's inspect data in JSON format
-func (p *PodmanTestIntegration) InspectContainer(name string) []shared.InspectContainer {
+func (p *PodmanTestIntegration) InspectContainer(name string) []libpod.InspectContainerData {
cmd := []string{"inspect", name}
session := p.Podman(cmd)
session.WaitWithDefaultTimeout()
@@ -375,7 +374,7 @@ func (p *PodmanTestIntegration) RunNginxWithHealthCheck(name string) (*PodmanSes
if name != "" {
podmanArgs = append(podmanArgs, "--name", name)
}
- podmanArgs = append(podmanArgs, "-dt", "-P", "--healthcheck-command", "CMD-SHELL curl http://localhost/", nginx)
+ podmanArgs = append(podmanArgs, "-dt", "-P", "--health-cmd", "curl http://localhost/", nginx)
session := p.Podman(podmanArgs)
session.WaitWithDefaultTimeout()
return session, session.OutputToString()
@@ -481,8 +480,8 @@ func (p *PodmanTestIntegration) PullImage(image string) error {
// InspectContainerToJSON takes the session output of an inspect
// container and returns json
-func (s *PodmanSessionIntegration) InspectContainerToJSON() []shared.InspectContainer {
- var i []shared.InspectContainer
+func (s *PodmanSessionIntegration) InspectContainerToJSON() []libpod.InspectContainerData {
+ var i []libpod.InspectContainerData
err := json.Unmarshal(s.Out.Contents(), &i)
Expect(err).To(BeNil())
return i
diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go
index 125002bf9..dafc8a837 100644
--- a/test/e2e/healthcheck_run_test.go
+++ b/test/e2e/healthcheck_run_test.go
@@ -95,7 +95,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck should be starting", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--healthcheck-retries", "2", "--healthcheck-command", "\"CMD-SHELL ls /foo || exit 1\"", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
inspect := podmanTest.InspectContainer("hc")
@@ -103,7 +103,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck failed checks in start-period should not change status", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--healthcheck-start-period", "2m", "--healthcheck-retries", "2", "--healthcheck-command", "\"CMD-SHELL ls /foo || exit 1\"", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-start-period", "2m", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -124,7 +124,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck failed checks must reach retries before unhealthy ", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--healthcheck-retries", "2", "--healthcheck-command", "\"CMD-SHELL ls /foo || exit 1\"", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -145,7 +145,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck good check results in healthy even in start-period", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--healthcheck-start-period", "2m", "--healthcheck-retries", "2", "--healthcheck-command", "\"CMD-SHELL\" \"ls\" \"||\" \"exit\" \"1\"", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-start-period", "2m", "--health-retries", "2", "--health-cmd", "ls || exit 1", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -158,7 +158,7 @@ var _ = Describe("Podman healthcheck run", func() {
})
It("podman healthcheck single healthy result changes failed to healthy", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--healthcheck-retries", "2", "--healthcheck-command", "\"CMD-SHELL\" \"ls\" \"/foo\" \"||\" \"exit\" \"1\"", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
new file mode 100644
index 000000000..a6f59a3da
--- /dev/null
+++ b/test/e2e/play_kube_test.go
@@ -0,0 +1,123 @@
+// +build !remoteclient
+
+package integration
+
+import (
+ "os"
+ "path/filepath"
+ "text/template"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var yamlTemplate = `
+apiVersion: v1
+kind: Pod
+metadata:
+ creationTimestamp: "2019-07-17T14:44:08Z"
+ labels:
+ app: {{ .Name }}
+ name: {{ .Name }}
+spec:
+ containers:
+{{ with .Containers }}
+ {{ range . }}
+ - command:
+ - {{ .Cmd }}
+ env:
+ - name: PATH
+ value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ - name: TERM
+ value: xterm
+ - name: HOSTNAME
+ - name: container
+ value: podman
+ image: {{ .Image }}
+ name: {{ .Name }}
+ resources: {}
+ securityContext:
+ allowPrivilegeEscalation: true
+ capabilities: {}
+ privileged: false
+ readOnlyRootFilesystem: false
+ workingDir: /
+ {{ end }}
+{{ end }}
+status: {}
+`
+
+type Pod struct {
+ Name string
+ Containers []Container
+}
+
+type Container struct {
+ Cmd string
+ Image string
+ Name string
+}
+
+func generateKubeYaml(ctrs []Container, fileName string) error {
+ f, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ testPod := Pod{"test", ctrs}
+
+ t, err := template.New("pod").Parse(yamlTemplate)
+ if err != nil {
+ return err
+ }
+
+ if err := t.Execute(f, testPod); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+var _ = Describe("Podman generate kube", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.SeedImages()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+ })
+
+ It("podman play kube test correct command", func() {
+ ctrName := "testCtr"
+ ctrCmd := "top"
+ testContainer := Container{ctrCmd, ALPINE, ctrName}
+ tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml")
+
+ err := generateKubeYaml([]Container{testContainer}, tempFile)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", tempFile})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(ctrCmd))
+ })
+})
diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go
index 0d3f47f30..f0689f152 100644
--- a/test/e2e/pod_rm_test.go
+++ b/test/e2e/pod_rm_test.go
@@ -3,6 +3,8 @@ package integration
import (
"fmt"
"os"
+ "path/filepath"
+ "strings"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
@@ -40,6 +42,21 @@ var _ = Describe("Podman pod rm", func() {
result := podmanTest.Podman([]string{"pod", "rm", podid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
+
+ // Also check that we don't leak cgroups
+ err := filepath.Walk("/sys/fs/cgroup", func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if !info.IsDir() {
+ Expect(err).To(BeNil())
+ }
+ if strings.Contains(info.Name(), podid) {
+ return fmt.Errorf("leaking cgroup path %s", path)
+ }
+ return nil
+ })
+ Expect(err).To(BeNil())
})
It("podman pod rm latest pod", func() {
diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go
index 2e537a9f9..cf7ce9cdf 100644
--- a/test/e2e/run_device_test.go
+++ b/test/e2e/run_device_test.go
@@ -85,4 +85,13 @@ var _ = Describe("Podman run device", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
+
+ It("podman run device host device with --privileged", func() {
+ if _, err := os.Stat("/dev/kvm"); err != nil {
+ Skip("/dev/kvm not available")
+ }
+ session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "/dev/kvm"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
index f1196ff38..081fab3fd 100644
--- a/test/e2e/run_dns_test.go
+++ b/test/e2e/run_dns_test.go
@@ -94,4 +94,22 @@ var _ = Describe("Podman run dns", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOutputContains("foobar")).To(BeTrue())
})
+
+ It("podman run mutually excludes --dns* and --network", func() {
+ session := podmanTest.Podman([]string{"run", "--dns=1.2.3.4", "--network", "container:ALPINE", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--dns-opt=1.2.3.4", "--network", "container:ALPINE", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--dns-search=foobar.com", "--network", "none", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--dns=1.2.3.4", "--network", "host", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To((Equal(0)))
+ })
})
diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go
index 6ba0d1aba..e3e86fc66 100644
--- a/test/e2e/run_ns_test.go
+++ b/test/e2e/run_ns_test.go
@@ -51,6 +51,15 @@ var _ = Describe("Podman run ns", func() {
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+ It("podman run --cgroup private test", func() {
+ session := podmanTest.Podman([]string{"run", "--cgroupns=private", fedoraMinimal, "cat", "/proc/self/cgroup"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ output := session.OutputToString()
+ Expect(output).ToNot(ContainSubstring("slice"))
+ })
+
It("podman run ipcns test", func() {
setup := SystemExec("ls", []string{"--inode", "-d", "/dev/shm"})
Expect(setup.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 3fc628589..e35c84f5b 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -8,7 +8,9 @@ import (
"net"
"os"
"path/filepath"
+ "strconv"
"strings"
+ "syscall"
"time"
. "github.com/containers/libpod/test/utils"
@@ -224,6 +226,22 @@ var _ = Describe("Podman run", func() {
Expect(match).Should(BeTrue())
})
+ It("podman run --host-env environment test", func() {
+ os.Setenv("FOO", "BAR")
+ session := podmanTest.Podman([]string{"run", "--rm", "--env-host", ALPINE, "printenv", "FOO"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("BAR")
+ Expect(match).Should(BeTrue())
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--env", "FOO=BAR1", "--env-host", ALPINE, "printenv", "FOO"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ = session.GrepString("BAR1")
+ Expect(match).Should(BeTrue())
+ os.Unsetenv("FOO")
+ })
+
It("podman run limits test", func() {
SkipIfRootless()
session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "rtprio=99", "--cap-add=sys_nice", fedoraMinimal, "cat", "/proc/self/sched"})
@@ -250,6 +268,25 @@ var _ = Describe("Podman run", func() {
Expect(session.OutputToString()).To(ContainSubstring("100"))
})
+ It("podman run limits host test", func() {
+ SkipIfRemote()
+
+ var l syscall.Rlimit
+
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
+ Expect(err).To(BeNil())
+
+ session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "host", fedoraMinimal, "ulimit", "-Hn"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ ulimitCtrStr := strings.TrimSpace(session.OutputToString())
+ ulimitCtr, err := strconv.ParseUint(ulimitCtrStr, 10, 0)
+ Expect(err).To(BeNil())
+
+ Expect(ulimitCtr).Should(BeNumerically(">=", l.Max))
+ })
+
It("podman run with cidfile", func() {
session := podmanTest.Podman([]string{"run", "--cidfile", tempdir + "cidfile", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
@@ -713,21 +750,21 @@ USER mail`
})
It("podman run with bad healthcheck retries", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--healthcheck-command", "foo", "--healthcheck-retries", "0", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-retries", "0", ALPINE, "top"})
session.Wait()
Expect(session.ExitCode()).ToNot(Equal(0))
Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-retries must be greater than 0"))
})
It("podman run with bad healthcheck timeout", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--healthcheck-command", "foo", "--healthcheck-timeout", "0s", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-timeout", "0s", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(Equal(0))
Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-timeout must be at least 1 second"))
})
It("podman run with bad healthcheck start-period", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--healthcheck-command", "foo", "--healthcheck-start-period", "-1s", ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-start-period", "-1s", ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(Equal(0))
Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-start-period must be 0 seconds or greater"))
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ad2f69976..462ba1408 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -355,8 +355,8 @@ github.com/opencontainers/runc/libcontainer/system
github.com/opencontainers/runtime-spec/specs-go
# github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/runtime-tools/generate
-github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/validate
+github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/error