summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml8
-rw-r--r--.gitignore2
-rw-r--r--Makefile9
-rw-r--r--README.md34
-rw-r--r--cmd/podman/common/create.go9
-rw-r--r--cmd/podman/common/create_opts.go1
-rw-r--r--cmd/podman/common/specgen.go4
-rw-r--r--cmd/podman/common/volumes.go95
-rw-r--r--cmd/podman/images/pull.go1
-rw-r--r--cmd/podman/images/push.go1
-rw-r--r--cmd/podman/registry/config.go2
-rw-r--r--cmd/podman/system/renumber.go2
-rw-r--r--cni/README.md4
-rw-r--r--completions/bash/podman5
-rw-r--r--contrib/systemd/system/podman.service7
-rw-r--r--contrib/systemd/system/podman.socket2
l---------contrib/systemd/user1
-rw-r--r--contrib/systemd/user/podman.service17
-rw-r--r--contrib/systemd/user/podman.socket10
-rw-r--r--docs/source/markdown/libpod.conf.5.md114
-rw-r--r--docs/source/markdown/podman-attach.1.md4
-rw-r--r--docs/source/markdown/podman-create.1.md97
-rw-r--r--docs/source/markdown/podman-events.1.md4
-rw-r--r--docs/source/markdown/podman-pull.1.md6
-rw-r--r--docs/source/markdown/podman-push.1.md6
-rw-r--r--docs/source/markdown/podman-run.1.md66
-rw-r--r--docs/source/markdown/podman-system-migrate.1.md2
-rw-r--r--docs/source/markdown/podman-system-renumber.1.md6
-rw-r--r--docs/source/markdown/podman.1.md16
-rw-r--r--docs/tutorials/rootless_tutorial.md4
-rw-r--r--go.mod6
-rw-r--r--go.sum8
-rwxr-xr-xhack/check_root.sh5
-rw-r--r--libpod/container.go17
-rw-r--r--libpod/container_exec.go4
-rw-r--r--libpod/container_inspect.go8
-rw-r--r--libpod/container_internal.go6
-rw-r--r--libpod/container_internal_linux.go26
-rw-r--r--libpod/container_internal_unsupported.go4
-rw-r--r--libpod/container_validate.go19
-rw-r--r--libpod/define/config.go2
-rw-r--r--libpod/define/container_inspect.go2
-rw-r--r--libpod/events/journal_linux.go7
-rw-r--r--libpod/events/logfile.go8
-rw-r--r--libpod/healthcheck.go5
-rw-r--r--libpod/options.go43
-rw-r--r--libpod/runtime.go4
-rw-r--r--pkg/api/handlers/compat/events.go154
-rw-r--r--pkg/api/server/register_generate.go4
-rw-r--r--pkg/bindings/test/system_test.go29
-rw-r--r--pkg/domain/infra/abi/play.go11
-rw-r--r--pkg/network/config.go4
-rw-r--r--pkg/specgen/generate/container_create.go17
-rw-r--r--pkg/specgen/specgen.go14
-rw-r--r--pkg/util/mountOpts.go4
-rw-r--r--test/e2e/attach_test.go5
-rw-r--r--test/e2e/build_test.go5
-rw-r--r--test/e2e/common_test.go19
-rw-r--r--test/e2e/config/containers.conf2
-rw-r--r--test/e2e/containers_conf_test.go11
-rw-r--r--test/e2e/create_test.go43
-rw-r--r--test/e2e/events_test.go4
-rw-r--r--test/e2e/generate_kube_test.go4
-rw-r--r--test/e2e/libpod_suite_remote_test.go2
-rw-r--r--test/e2e/libpod_suite_test.go20
-rw-r--r--test/e2e/load_test.go4
-rw-r--r--test/e2e/play_kube_test.go82
-rw-r--r--test/e2e/pull_test.go6
-rw-r--r--test/e2e/run_device_test.go12
-rw-r--r--test/e2e/run_passwd_test.go2
-rw-r--r--test/e2e/run_privileged_test.go2
-rw-r--r--test/e2e/run_test.go70
-rw-r--r--test/e2e/run_volume_test.go89
-rw-r--r--test/e2e/search_test.go6
-rw-r--r--test/system/015-help.bats21
-rw-r--r--test/system/helpers.bash10
-rw-r--r--troubleshooting.md70
-rw-r--r--vendor/github.com/containers/common/pkg/config/config.go24
-rw-r--r--vendor/github.com/containers/common/pkg/config/default.go9
-rw-r--r--vendor/github.com/containers/common/version/version.go2
-rw-r--r--vendor/github.com/containers/storage/VERSION2
-rw-r--r--vendor/github.com/containers/storage/drivers/devmapper/deviceset.go2
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/archive.go4
-rw-r--r--vendor/github.com/containers/storage/pkg/unshare/unshare.go27
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/generate.go77
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go14
-rw-r--r--vendor/modules.txt6
87 files changed, 1096 insertions, 510 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 3187de34c..9a5e0472f 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -61,7 +61,7 @@ env:
SPECIALMODE: "none" # don't do anything special
TEST_REMOTE_CLIENT: 'false' # don't test remote client by default
ADD_SECOND_PARTITION: 'false' # will certainly fail inside containers
- MOD_LIBPOD_CONF: 'true' # Update libpod.conf runtime if required by OS environment
+ MOD_CONTAINERS_CONF: 'true' # Update containers.conf runtime if required by OS environment
####
#### Credentials and other secret-sauces, decrypted at runtime when authorized.
@@ -286,7 +286,7 @@ build_each_commit_task:
memory: "8Gb"
env:
- MOD_LIBPOD_CONF: 'false'
+ MOD_CONTAINERS_CONF: 'false'
timeout_in: 30m
@@ -318,7 +318,7 @@ build_without_cgo_task:
memory: "8Gb"
env:
- MOD_LIBPOD_CONF: 'false'
+ MOD_CONTAINERS_CONF: 'false'
timeout_in: 30m
@@ -512,7 +512,7 @@ special_testing_in_podman_task:
env:
ADD_SECOND_PARTITION: 'true'
- MOD_LIBPOD_CONF: 'false' # Use existing/native setup
+ MOD_CONTAINERS_CONF: 'false' # Use existing/native setup
SPECIALMODE: 'in_podman' # See docs
timeout_in: 60m
diff --git a/.gitignore b/.gitignore
index f0fdf4dc8..434a12759 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,8 @@
/conmon/
contrib/spec/podman.spec
*.coverprofile
+coverprofile
+/.coverage
/docs/*.[158]
/docs/*.[158].gz
/docs/build/
diff --git a/Makefile b/Makefile
index 1df9bf5f1..983ab46be 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@ export GO111MODULE=off
export GOPROXY=https://proxy.golang.org
GO ?= go
+COVERAGE_PATH ?= .coverage
DESTDIR ?=
EPOCH_TEST_COMMIT ?= $(shell git merge-base $${DEST_BRANCH:-master} HEAD)
HEAD ?= HEAD
@@ -263,6 +264,7 @@ clean: ## Clean artifacts
libpod/container_easyjson.go \
libpod/pod_easyjson.go \
docs/build
+ make -C docs clean
.PHONY: libpodimage
libpodimage: ## Build the libpod image
@@ -306,14 +308,21 @@ testunit: libpodimage ## Run unittest on the built image
.PHONY: localunit
localunit: test/goecho/goecho varlink_generate
+ hack/check_root.sh make localunit
+ rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH}
ginkgo \
-r \
$(TESTFLAGS) \
--skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings,hack \
--cover \
--covermode atomic \
+ --coverprofile coverprofile \
+ --outputdir ${COVERAGE_PATH} \
--tags "$(BUILDTAGS)" \
--succinct
+ $(GO) tool cover -html=${COVERAGE_PATH}/coverprofile -o ${COVERAGE_PATH}/coverage.html
+ $(GO) tool cover -func=${COVERAGE_PATH}/coverprofile > ${COVERAGE_PATH}/functions
+ cat ${COVERAGE_PATH}/functions | sed -n 's/\(total:\).*\([0-9][0-9].[0-9]\)/\1 \2/p'
.PHONY: ginkgo
ginkgo:
diff --git a/README.md b/README.md
index 82e2d3af9..21b30fdbd 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
![PODMAN logo](logo/podman-logo-source.svg)
-# Library and tool for running OCI-based containers in Pods
+# Podman: A tool for managing OCI containers and pods
-Libpod provides a library for applications looking to use the Container Pod concept,
-popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes.
+Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers.
+Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes.
* [Latest Version: 2.0.2](https://github.com/containers/libpod/releases/latest)
* Latest Remote client for Windows
@@ -15,26 +15,24 @@ popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`
## Overview and scope
-At a high level, the scope of libpod and Podman is the following:
+At a high level, the scope of Podman and libpod is the following:
-* Support multiple image formats including the OCI and Docker image formats.
-* Support for multiple means to download images including trust & image verification.
-* Container image management (managing image layers, overlay filesystems, etc).
-* Full management of container lifecycle.
-* Support for pods to manage groups of containers together.
+* Support for multiple container image formats, including OCI and Docker images.
+* Full management of those images, including pulling from various sources (including trust and verification), creating (built via Containerfile or Dockerfile or committed from a container), and pushing to registries and other storage backends.
+* Full management of container lifecycle, including creation (both from an image and from an exploded root filesystem), running, checkpointing and restoring (via CRIU), and removal.
+* Support for pods, groups of containers that share resources and are managed together.
* Resource isolation of containers and pods.
-* Support for a Docker-compatible CLI interface through Podman.
+* Support for a Docker-compatible CLI interface.
* Support for a REST API providing both a Docker-compatible interface and an improved interface exposing advanced Podman functionality.
-* Integration with CRI-O to share containers and backend code.
+* In the future, integration with [CRI-O](https://github.com/cri-o/cri-o) to share containers and backend code.
Podman presently only supports running containers on Linux. However, we are building a remote client which can run on Windows and OS X and manage Podman containers on a Linux system via the REST API using SSH tunneling.
## Roadmap
-1. Complete the Podman REST API and Podman v2, which will be able to connect to remote Podman instances via this API
-1. Integrate libpod into CRI-O to replace its existing container management backend
-1. Further work on the podman pod command
-1. Further improvements on rootless containers
+1. Further improvements to the REST API, with a focus on bugfixes and implementing missing functionality
+1. Integrate libpod into [CRI-O](https://github.com/cri-o/cri-o) to replace its existing container management backend
+1. Improvements on rootless containers, with a focus on improving the user experience and exposing presently-unavailable features when possible
## Communications
@@ -67,10 +65,10 @@ A little configuration by an administrator is required before rootless Podman ca
## Out of scope
-* Specializing in signing and pushing images to various storage backends.
+* Specialized signing and pushing of images to various storage backends.
See [Skopeo](https://github.com/containers/skopeo/) for those tasks.
-* Container runtimes daemons for working with the Kubernetes CRI interface.
- [CRI-O](https://github.com/cri-o/cri-o) specializes in that.
+* Support for the Kubernetes CRI interface for container management.
+ The [CRI-O](https://github.com/cri-o/cri-o) daemon specializes in that.
* Supporting `docker-compose`. We believe that Kubernetes is the defacto
standard for composing Pods and for orchestrating containers, making
Kubernetes YAML a defacto standard file format. Hence, Podman allows the
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index a26bbf718..46f78cdba 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -155,6 +155,10 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"device-write-iops", []string{},
"Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
)
+ createFlags.Bool(
+ "disable-content-trust", false,
+ "This is a Docker specific option and is a NOOP",
+ )
createFlags.String("entrypoint", "",
"Overwrite the default ENTRYPOINT of the image",
)
@@ -459,6 +463,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"tz", containerConfig.TZ(),
"Set timezone in container",
)
+ createFlags.StringVar(
+ &cf.Umask,
+ "umask", containerConfig.Umask(),
+ "Set umask in container",
+ )
createFlags.StringSliceVar(
&cf.UIDMap,
"uidmap", []string{},
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index a544846aa..2bea8b0b4 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -93,6 +93,7 @@ type ContainerCLIOpts struct {
TmpFS []string
TTY bool
Timezone string
+ Umask string
UIDMap []string
Ulimit []string
User string
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 08099da4b..731085731 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -531,12 +531,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
// Only add read-only tmpfs mounts in case that we are read-only and the
// read-only tmpfs flag has been set.
- mounts, volumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly)
+ mounts, volumes, overlayVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly)
if err != nil {
return err
}
s.Mounts = mounts
s.Volumes = volumes
+ s.OverlayVolumes = overlayVolumes
for _, dev := range c.Devices {
s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev})
@@ -612,6 +613,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Remove = c.Rm
s.StopTimeout = &c.StopTimeout
s.Timezone = c.Timezone
+ s.Umask = c.Umask
return nil
}
diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go
index b201786f0..47a6f3c35 100644
--- a/cmd/podman/common/volumes.go
+++ b/cmd/podman/common/volumes.go
@@ -34,43 +34,43 @@ var (
// Does not handle image volumes, init, and --volumes-from flags.
// Can also add tmpfs mounts from read-only tmpfs.
// TODO: handle options parsing/processing via containers/storage/pkg/mount
-func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, error) {
+func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) {
// Get mounts from the --mounts flag.
unifiedMounts, unifiedVolumes, err := getMounts(mountFlag)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
// Next --volumes flag.
- volumeMounts, volumeVolumes, err := getVolumeMounts(volumeFlag)
+ volumeMounts, volumeVolumes, overlayVolumes, err := getVolumeMounts(volumeFlag)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
// Next --tmpfs flag.
tmpfsMounts, err := getTmpfsMounts(tmpfsFlag)
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
// Unify mounts from --mount, --volume, --tmpfs.
// Start with --volume.
for dest, mount := range volumeMounts {
if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedMounts[dest] = mount
}
for dest, volume := range volumeVolumes {
if _, ok := unifiedVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedVolumes[dest] = volume
}
// Now --tmpfs
for dest, tmpfs := range tmpfsMounts {
if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedMounts[dest] = tmpfs
}
@@ -101,15 +101,29 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
}
}
- // Check for conflicts between named volumes and mounts
+ // Check for conflicts between named volumes, overlay volumes, and mounts
for dest := range unifiedMounts {
if _, ok := unifiedVolumes[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ }
+ if _, ok := overlayVolumes[dest]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
for dest := range unifiedVolumes {
if _, ok := unifiedMounts[dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ }
+ if _, ok := overlayVolumes[dest]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ }
+ }
+ for dest := range overlayVolumes {
+ if _, ok := unifiedMounts[dest]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
+ }
+ if _, ok := unifiedVolumes[dest]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
@@ -119,7 +133,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
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)
+ return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
}
mount.Source = absSrc
}
@@ -129,8 +143,12 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
for _, volume := range unifiedVolumes {
finalVolumes = append(finalVolumes, volume)
}
+ finalOverlayVolume := make([]*specgen.OverlayVolume, 0)
+ for _, volume := range overlayVolumes {
+ finalOverlayVolume = append(finalOverlayVolume, volume)
+ }
- return finalMounts, finalVolumes, nil
+ return finalMounts, finalVolumes, finalOverlayVolume, nil
}
// getMounts takes user-provided input from the --mount flag and creates OCI
@@ -465,9 +483,10 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
return newVolume, nil
}
-func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) {
+func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.OverlayVolume, error) {
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*specgen.NamedVolume)
+ overlayVolumes := make(map[string]*specgen.OverlayVolume)
volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
@@ -481,7 +500,7 @@ func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*sp
splitVol := strings.Split(vol, ":")
if len(splitVol) > 3 {
- return nil, nil, errors.Wrapf(volumeFormatErr, vol)
+ return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol)
}
src = splitVol[0]
@@ -496,34 +515,54 @@ func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*sp
}
if len(splitVol) > 2 {
if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
}
// Do not check source dir for anonymous volumes
if len(splitVol) > 1 {
if err := parse.ValidateVolumeHostDir(src); err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
cleanDest := filepath.Clean(dest)
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
// This is not a named volume
- newMount := spec.Mount{
- Destination: cleanDest,
- Type: string(TypeBind),
- Source: src,
- Options: options,
+ overlayFlag := false
+ for _, o := range options {
+ if o == "O" {
+ overlayFlag = true
+ if len(options) > 1 {
+ return nil, nil, nil, errors.New("can't use 'O' with other options")
+ }
+ }
}
- if _, ok := mounts[newMount.Destination]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
+ if overlayFlag {
+ // This is a overlay volume
+ newOverlayVol := new(specgen.OverlayVolume)
+ newOverlayVol.Destination = cleanDest
+ newOverlayVol.Source = src
+ if _, ok := overlayVolumes[newOverlayVol.Destination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination)
+ }
+ overlayVolumes[newOverlayVol.Destination] = newOverlayVol
+ } else {
+ newMount := spec.Mount{
+ Destination: cleanDest,
+ Type: string(TypeBind),
+ Source: src,
+ Options: options,
+ }
+ if _, ok := mounts[newMount.Destination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
+ }
+ mounts[newMount.Destination] = newMount
}
- mounts[newMount.Destination] = newMount
} else {
// This is a named volume
newNamedVol := new(specgen.NamedVolume)
@@ -532,7 +571,7 @@ func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*sp
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
- return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
}
volumes[newNamedVol.Dest] = newNamedVol
}
@@ -540,7 +579,7 @@ func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*sp
logrus.Debugf("User mount %s:%s options %v", src, dest, options)
}
- return mounts, volumes, nil
+ return mounts, volumes, overlayVolumes, nil
}
// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index 83bb186df..c10a351d8 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -82,6 +82,7 @@ func pullFlags(flags *pflag.FlagSet) {
flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images")
flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images")
+ flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP")
flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index 4eeed13d4..480b5e0f0 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -79,6 +79,7 @@ func pushFlags(flags *pflag.FlagSet) {
flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&pushOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
+ flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP")
flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)")
flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images")
flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go
index 75e67b35d..a7e368115 100644
--- a/cmd/podman/registry/config.go
+++ b/cmd/podman/registry/config.go
@@ -70,7 +70,7 @@ func newPodmanConfig() {
// setXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
// containers/image uses XDG_RUNTIME_DIR to locate the auth file, XDG_CONFIG_HOME is
-// use for the libpod.conf configuration file.
+// use for the containers.conf configuration file.
func setXdgDirs() error {
if !rootless.IsRootless() {
return nil
diff --git a/cmd/podman/system/renumber.go b/cmd/podman/system/renumber.go
index 82cf65d8f..7cec5b986 100644
--- a/cmd/podman/system/renumber.go
+++ b/cmd/podman/system/renumber.go
@@ -18,7 +18,7 @@ var (
podman system renumber
Migrate lock numbers to handle a change in maximum number of locks.
- Mandatory after the number of locks in libpod.conf is changed.
+ Mandatory after the number of locks in containers.conf is changed.
`
renumberCommand = &cobra.Command{
diff --git a/cni/README.md b/cni/README.md
index 2683df714..bc37df1eb 100644
--- a/cni/README.md
+++ b/cni/README.md
@@ -5,7 +5,7 @@ directory just contains an example configuration that can be used as the
basis for your own configuration.
To use this configuration, place it in `/etc/cni/net.d` (or the directory
-specified by `cni_config_dir` in your `libpod.conf`).
+specified by `cni_config_dir` in your `containers.conf`).
For example a basic network configuration can be achieved with:
@@ -14,4 +14,4 @@ sudo mkdir -p /etc/cni/net.d
curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | sudo tee /etc/cni/net.d/87-podman-bridge.conf
```
-Dependent upon your CNI configuration, you will need to install as a minimum the `port` and `bridge` [CNI plugins](https://github.com/containernetworking/plugins) into `/opt/cni/bin` (or the directory specified by `cni_plugin_dir` in libpod.conf). Please refer to the [CNI](https://github.com/containernetworking) project page in GitHub for more information.
+Dependent upon your CNI configuration, you will need to install as a minimum the `port` and `bridge` [CNI plugins](https://github.com/containernetworking/plugins) into `/opt/cni/bin` (or the directory specified by `cni_plugin_dir` in containers.conf). Please refer to the [CNI](https://github.com/containernetworking) project page in GitHub for more information.
diff --git a/completions/bash/podman b/completions/bash/podman
index 458090ac4..eb727ef63 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2119,12 +2119,13 @@ _podman_container_run() {
--shm-size
--stop-signal
--stop-timeout
- --tmpfs
- --tz
--subgidname
--subuidname
--sysctl
--systemd
+ --tmpfs
+ --tz
+ --umask
--uidmap
--ulimit
--user -u
diff --git a/contrib/systemd/system/podman.service b/contrib/systemd/system/podman.service
index eaa2ec437..4a63735a3 100644
--- a/contrib/systemd/system/podman.service
+++ b/contrib/systemd/system/podman.service
@@ -2,15 +2,12 @@
Description=Podman API Service
Requires=podman.socket
After=podman.socket
-Documentation=man:podman-api(1)
+Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0
[Service]
-Type=oneshot
-Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf
+Type=simple
ExecStart=/usr/bin/podman system service
-TimeoutStopSec=30
-KillMode=process
[Install]
WantedBy=multi-user.target
diff --git a/contrib/systemd/system/podman.socket b/contrib/systemd/system/podman.socket
index 8b22e31e4..397058ee4 100644
--- a/contrib/systemd/system/podman.socket
+++ b/contrib/systemd/system/podman.socket
@@ -1,6 +1,6 @@
[Unit]
Description=Podman API Socket
-Documentation=man:podman-api(1)
+Documentation=man:podman-system-service(1)
[Socket]
ListenStream=%t/podman/podman.sock
diff --git a/contrib/systemd/user b/contrib/systemd/user
new file mode 120000
index 000000000..3f3d9896b
--- /dev/null
+++ b/contrib/systemd/user
@@ -0,0 +1 @@
+./system/ \ No newline at end of file
diff --git a/contrib/systemd/user/podman.service b/contrib/systemd/user/podman.service
deleted file mode 100644
index eaa2ec437..000000000
--- a/contrib/systemd/user/podman.service
+++ /dev/null
@@ -1,17 +0,0 @@
-[Unit]
-Description=Podman API Service
-Requires=podman.socket
-After=podman.socket
-Documentation=man:podman-api(1)
-StartLimitIntervalSec=0
-
-[Service]
-Type=oneshot
-Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf
-ExecStart=/usr/bin/podman system service
-TimeoutStopSec=30
-KillMode=process
-
-[Install]
-WantedBy=multi-user.target
-Also=podman.socket
diff --git a/contrib/systemd/user/podman.socket b/contrib/systemd/user/podman.socket
deleted file mode 100644
index 8b22e31e4..000000000
--- a/contrib/systemd/user/podman.socket
+++ /dev/null
@@ -1,10 +0,0 @@
-[Unit]
-Description=Podman API Socket
-Documentation=man:podman-api(1)
-
-[Socket]
-ListenStream=%t/podman/podman.sock
-SocketMode=0660
-
-[Install]
-WantedBy=sockets.target
diff --git a/docs/source/markdown/libpod.conf.5.md b/docs/source/markdown/libpod.conf.5.md
deleted file mode 100644
index ca45bccf6..000000000
--- a/docs/source/markdown/libpod.conf.5.md
+++ /dev/null
@@ -1,114 +0,0 @@
-% libpod.conf(5)
-
-## NAME
-libpod.conf - libpod configuration file
-
-## DESCRIPTION
-The libpod.conf file is the default configuration file for all tools using
-libpod to manage containers.
-
-## OPTIONS
-
-**image_default_transport**=""
- Default transport method for pulling and pushing images
-
-**runtime**=""
- Default OCI runtime to use if nothing is specified in **runtimes**
-
-**runtimes**
- For each OCI runtime, specify a list of paths to look for. The first one found is used. If the paths are empty or no valid path was found, then the `$PATH` environment variable will be used as the fallback.
-
-**conmon_path**=""
- Paths to search for the conmon container manager binary. If the paths are empty or no valid path was found, then the `$PATH` environment variable will be used as the fallback.
-
-**conmon_env_vars**=""
- Environment variables to pass into Conmon
-
-**cgroup_manager**=""
- Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs"
-
-**lock_type**=""
- Specify the locking mechanism to use; valid values are "shm" and "file". Change the default only if you are sure of what you are doing, in general "file" is useful only on platforms where cgo is not available for using the faster "shm" lock type. You may need to run "podman system renumber" after you change the lock type.
-
-**init_path**=""
- Path to the container-init binary, which forwards signals and reaps processes within containers. Note that the container-init binary will only be used when the `--init` for podman-create and podman-run is set.
-
-**hooks_dir**=["*path*", ...]
-
- Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated.
-
- Paths listed later in the array have higher precedence (`oci-hooks(5)` discusses directory precedence).
-
- For the annotation conditions, libpod uses any annotations set in the generated OCI configuration.
-
- For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered.
-
- Podman and libpod currently support an additional `precreate` state which is called before the runtime's `create` operation. Unlike the other stages, which receive the container state on their standard input, `precreate` hooks receive the proposed runtime configuration on their standard input. They may alter that configuration as they see fit, and write the altered form to their standard output.
-
- **WARNING**: the `precreate` hook lets you do powerful things, such as adding additional mounts to the runtime configuration. That power also makes it easy to break things. Before reporting libpod errors, try running your container with `precreate` hooks disabled to see if the problem is due to one of your hooks.
-
-**static_dir**=""
- Directory for persistent libpod files (database, etc)
- By default this will be configured relative to where containers/storage
- stores containers
-
-**tmp_dir**=""
- Directory for temporary files
- Must be a tmpfs (wiped after reboot)
-
-**max_log_size**=""
- Maximum size of log files (in bytes)
-
-**no_pivot_root**=""
- Whether to use chroot instead of pivot_root in the runtime
-
-**cni_config_dir**=""
- Directory containing CNI plugin configuration files
-
-**cni_plugin_dir**=""
- Directories where CNI plugin binaries may be located
-
-**infra_image** = ""
- Infra (pause) container image name for pod infra containers. When running a pod, we
- start a `pause` process in a container to hold open the namespaces associated with the
- pod. This container and process, basically sleep/pause for the lifetime of the pod.
-
-**infra_command**=""
- Command to run the infra container
-
-**namespace**=""
- Default libpod namespace. If libpod is joined to a namespace, it will see only containers and pods
- that were created in the same namespace, and will create new containers and pods in that namespace.
- The default namespace is "", which corresponds to no namespace. When no namespace is set, all
- containers and pods are visible.
-
-**label**="true|false"
- Indicates whether the containers should use label separation by default.
- Can be overridden via `--security-opt label=...` on the CLI.
-
-**num_locks**=""
- Number of locks available for containers and pods. Each created container or pod consumes one lock.
- The default number available is 2048.
- If this is changed, a lock renumbering must be performed, using the `podman system renumber` command.
-
-**volume_path**=""
- Directory where named volumes will be created in using the default volume driver.
- By default this will be configured relative to where containers/storage stores containers.
-
-**network_cmd_path**=""
- Path to the command binary to use for setting up a network. It is currently only used for setting up
- a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable.
-
-**events_logger**=""
- Default method to use when logging events. Valid values are "file", "journald", and "none".
-
-**detach_keys**=""
- Keys sequence used for detaching a container
-
-## FILES
- `/usr/share/containers/libpod.conf`, default libpod configuration path
-
- `/etc/containers/libpod.conf`, override libpod configuration path
-
-## HISTORY
-Apr 2018, Originally compiled by Nathan Williams <nath.e.will@gmail.com>
diff --git a/docs/source/markdown/podman-attach.1.md b/docs/source/markdown/podman-attach.1.md
index 1ac2e49a9..cb3ffa92e 100644
--- a/docs/source/markdown/podman-attach.1.md
+++ b/docs/source/markdown/podman-attach.1.md
@@ -15,7 +15,7 @@ or name, either to view its ongoing output or to control it interactively.
You can detach from the container (and leave it running) using a configurable key sequence. The default
sequence is `ctrl-p,ctrl-q`.
Configure the keys sequence using the **--detach-keys** option, or specifying
-it in the **libpod.conf** file: see **libpod.conf(5)** for more information.
+it in the **containers.conf** file: see **containers.conf(5)** for more information.
## OPTIONS
**--detach-keys**=*sequence*
@@ -55,4 +55,4 @@ $ podman attach 1234
$ podman attach --no-stdin foobar
```
## SEE ALSO
-podman(1), podman-exec(1), podman-run(1)
+podman(1), podman-exec(1), podman-run(1), containers.conf(5)
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 4466e6616..b4456225e 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -190,7 +190,7 @@ detached container with **podman attach**.
When attached in the tty mode, you can detach from the container (and leave it
running) using a configurable key sequence. The default sequence is `ctrl-p,ctrl-q`.
Configure the keys sequence using the **--detach-keys** option, or specifying
-it in the **libpod.conf** file: see **libpod.conf(5)** for more information.
+it in the **containers.conf** file: see **containers.conf(5)** for more information.
**--detach-keys**=*sequence*
@@ -234,6 +234,12 @@ Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sd
Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--dns**=*dns*
Set custom DNS servers. Invalid if using **--dns** and **--network** that is set to 'none' or 'container:<name|id>'.
@@ -741,7 +747,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: Labeling 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 **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
**--shm-size**=*size*
@@ -811,7 +817,7 @@ Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
-$ podman run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
+$ podman create -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
This command mounts a `tmpfs` at `/tmp` within the container. The supported mount
options are the same as the Linux default `mount` flags. If you do not specify
@@ -833,6 +839,10 @@ standard input.
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
+**--umask**=*umask*
+
+Set the umask inside the container. Defaults to `0022`.
+
**--uidmap**=*container_uid:host_uid:amount*
UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags.
@@ -887,15 +897,20 @@ Set the UTS mode for the container
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman
-container. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
+container. Similarly, `-v SOURCE-VOLUME:/CONTAINER-DIR` will mount the volume
+in the host to the container. If no such named volume exists, Podman will
+create one. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
-* [rw|ro]
-* [z|Z]
-* [`[r]shared`|`[r]slave`|`[r]private`]
-* [`[r]bind`]
-* [`noexec`|`exec`]
-* [`nodev`|`dev`]
-* [`nosuid`|`suid`]
+The _options_ is a comma delimited list and can be:
+
+* **rw**|**ro**
+* **z**|**Z**
+* [**r**]**shared**|[**r**]**slave**|[**r**]**private**
+* [**r**]**bind**
+* [**no**]**exec**
+* [**no**]**dev**
+* [**no**]**suid**
+* [**O**]
The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The volume
will be mounted into the container at this directory.
@@ -908,18 +923,22 @@ the container is removed via the `--rm` flag or `podman rm --volumes`.
If a volume source is specified, it must be a path on the host or the name of a
named volume. Host paths are allowed to be absolute or relative; relative paths
are resolved relative to the directory Podman is run in. Any source that does
-not begin with a `.` or `/` it will be treated as the name of a named volume.
+not begin with a `.` or `/` will be treated as the name of a named volume.
If a volume with that name does not exist, it will be created. Volumes created
-with names are not anonymous and are not removed by `--rm` and
-`podman rm --volumes`.
+with names are not anonymous. They are not removed by the `--rm` option and the
+`podman rm --volumes` command.
You can specify multiple **-v** options to mount one or more volumes into a
container.
-You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
+ `Write Protected Volume Mounts`
+
+You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
read-write mode, respectively. By default, the volumes are mounted read-write.
See examples.
+ `Labeling Volume Mounts`
+
Labeling systems like SELinux require that proper labels are placed on volume
content mounted into a container. Without a label, the security system might
prevent the processes running inside the container from using the content. By
@@ -933,6 +952,37 @@ content label. Shared volume labels allow all containers to read/write content.
The `Z` option tells Podman to label the content with a private unshared label.
Only the current container can use a private volume.
+ `Overlay Volume Mounts`
+
+ The `:O` flag tells Podman to mount the directory from the host as a
+temporary storage using the `overlay file system`. The container processes
+can modify content within the mountpoint which is stored in the
+container storage in a separate directory. In overlay terms, the source
+directory will be the lower, and the container storage directory will be the
+upper. Modifications to the mount point are destroyed when the container
+finishes executing, similar to a tmpfs mount point being unmounted.
+
+ Subsequent executions of the container will see the original source directory
+content, any changes from previous container executions no longer exists.
+
+ One use case of the overlay mount is sharing the package cache from the
+host into the container to allow speeding up builds.
+
+ Note:
+
+ - The `O` flag conflicts with other options listed above.
+Content mounted into the container is labeled with the private label.
+ On SELinux systems, labels in the source directory must be readable
+by the container label. Usually containers can read/execute `container_share_t`
+and can read/write `container_file_t`. If you can not change the labels on a
+source volume, SELinux container separation must be disabled for the container
+to work.
+ - The source directory mounted into the container with an overlay mount
+should not be modified, it can cause unexpected failures. It is recommended
+that you do not modify the directory until the container finishes running.
+
+ `Mounts propagation`
+
By default bind mounted volumes are `private`. That means any mounts done
inside container will not be visible on host and vice versa. One can change
this behavior by specifying a volume mount propagation property. Making a
@@ -949,7 +999,7 @@ where source dir is mounted on) has to have right propagation properties. For
shared volumes, source mount point has to be shared. And for slave volumes,
source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
-If you want to recursively mount a volume and all of it's submounts into a
+If you want to recursively mount a volume and all of its submounts into a
container, then you can use the `rbind` option. By default the bind option is
used, and submounts of the source directory will not be mounted into the
container.
@@ -1080,14 +1130,13 @@ required for VPN, without it containers need to be run with the --network=host f
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.
+Precedence order (later entries override earlier entries):
- **--env** : Any environment variables specified will override previous settings.
+- **--env-host** : Host environment of the process executing Podman is added.
+- **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details.
+- 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.
Create containers and set the environment ending with a __*__ and a *****
@@ -1110,7 +1159,7 @@ b
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
## SEE ALSO
-subgid(5), subuid(5), libpod.conf(5), systemd.unit(5), setsebool(8), slirp4netns(1), fuse-overlayfs(1)
+**subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1).
## HISTORY
October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md
index abfc6e9c1..0d91cdf17 100644
--- a/docs/source/markdown/podman-events.1.md
+++ b/docs/source/markdown/podman-events.1.md
@@ -10,7 +10,7 @@ podman\-events - Monitor Podman events
Monitor and print events that occur in Podman. Each event will include a timestamp,
a type, a status, name (if applicable), and image (if applicable). The default logging
-mechanism is *journald*. This can be changed in libpod.conf by changing the `events_logger`
+mechanism is *journald*. This can be changed in containers.conf by changing the `events_logger`
value to `file`. Only `file` and `journald` are accepted. A `none` logger is also
available but this logging mechanism completely disables events; nothing will be reported by
`podman events`.
@@ -150,7 +150,7 @@ $ podman events --format json
```
## SEE ALSO
-podman(1)
+podman(1), containers.conf(5)
## HISTORY
March 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md
index 5d941219a..201b10aa6 100644
--- a/docs/source/markdown/podman-pull.1.md
+++ b/docs/source/markdown/podman-pull.1.md
@@ -73,6 +73,12 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--override-os**=*OS*
Use OS instead of the running OS for choosing images
diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md
index f029c8db1..fffd76801 100644
--- a/docs/source/markdown/podman-push.1.md
+++ b/docs/source/markdown/podman-push.1.md
@@ -71,6 +71,12 @@ Note: This flag can only be set when using the **dir** transport
After copying the image, write the digest of the resulting image to the file. (Not available for remote commands)
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--format**, **-f**=*format*
Manifest Type (oci, v2s1, or v2s2) to use when pushing an image to a directory using the 'dir:' transport (default is manifest type of source)
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index de1d8aff6..303d025b0 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -208,7 +208,7 @@ Specify the key sequence for detaching a container; _sequence_ is a comma-delimi
in which each item can be a single character from the [a-Z] range,
or **ctrl**-_value_, where _value_ is one of: **a-z** or **@^[,_**.
-This option can also be set in **libpod.conf**(5) file.
+This option can also be set in **containers.conf**(5) file.
Specifying "" will disable this feature. The default is **ctrl-p,ctrl-q**.
@@ -247,6 +247,12 @@ Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/d
Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**).
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--dns**=*ipaddr*
Set custom DNS servers. Invalid if using **--dns** with **--network** that is set to **none** or **container:**_id_.
@@ -759,7 +765,7 @@ Security Options
- **seccomp=unconfined**: Turn off seccomp confinement for the container
- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter
-Note: Labeling can be disabled for all containers by setting **label=false** in the **libpod.conf**(5) file.
+Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
**--shm-size**=_number_[_unit_]
@@ -874,6 +880,10 @@ standard input.
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
+**--umask**=*umask*
+
+Set the umask inside the container. Defaults to `0022`.
+
**--uidmap**=*container_uid*:*host_uid*:*amount*
Run the container in a new user namespace using the supplied mapping. This option conflicts
@@ -936,6 +946,7 @@ The _options_ is a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
* [**no**]**exec**
* [**no**]**dev**
* [**no**]**suid**
+* [**O**]
The _container-dir_ must be an absolute path.
@@ -947,7 +958,7 @@ the container is removed via the **--rm** flag or **podman rm --volumes**.
If a volume source is specified, it must be a path on the host or the name of a
named volume. Host paths are allowed to be absolute or relative; relative paths
are resolved relative to the directory Podman is run in. Any source that does
-not begin with a **.** or **/** it will be treated as the name of a named volume.
+not begin with a **.** or **/** will be treated as the name of a named volume.
If a volume with that name does not exist, it will be created. Volumes created
with names are not anonymous and are not removed by **--rm** and
**podman rm --volumes**.
@@ -958,6 +969,8 @@ container.
You can add **:ro** or **:rw** option to mount a volume in read-only or
read-write mode, respectively. By default, the volumes are mounted read-write.
+ `Labeling Volume Mounts`
+
Labeling systems like SELinux require that proper labels are placed on volume
content mounted into a container. Without a label, the security system might
prevent the processes running inside the container from using the content. By
@@ -969,9 +982,41 @@ objects on the shared volumes. The **z** option tells Podman that two containers
share the volume content. As a result, Podman labels the content with a shared
content label. Shared volume labels allow all containers to read/write content.
The **Z** option tells Podman to label the content with a private unshared label.
+
+ `Overlay Volume Mounts`
+
+ The `:O` flag tells Podman to mount the directory from the host as a
+temporary storage using the `overlay file system`. The container processes
+can modify content within the mountpoint which is stored in the
+container storage in a separate directory. In overlay terms, the source
+directory will be the lower, and the container storage directory will be the
+upper. Modifications to the mount point are destroyed when the container
+finishes executing, similar to a tmpfs mount point being unmounted.
+
+ Subsequent executions of the container will see the original source directory
+content, any changes from previous container executions no longer exists.
+
+ One use case of the overlay mount is sharing the package cache from the
+host into the container to allow speeding up builds.
+
+ Note:
+
+ - The `O` flag conflicts with other options listed above.
+Content mounted into the container is labeled with the private label.
+ On SELinux systems, labels in the source directory must be readable
+by the container label. Usually containers can read/execute `container_share_t`
+and can read/write `container_file_t`. If you can not change the labels on a
+source volume, SELinux container separation must be disabled for the container
+to work.
+ - The source directory mounted into the container with an overlay mount
+should not be modified, it can cause unexpected failures. It is recommended
+that you do not modify the directory until the container finishes running.
+
Only the current container can use a private volume.
-By default bind mounted volumes are **private**. That means any mounts done
+ `Mounts propagation`
+
+By default bind mounted volumes are `private`. That means any mounts done
inside container will not be visible on host and vice versa. One can change
this behavior by specifying a volume mount propagation property. Making a
volume shared mounts done under that volume inside container will be
@@ -1220,14 +1265,16 @@ $ podman run -p 8080:80 -d -i -t fedora/httpd
To mount a host directory as a container volume, specify the absolute path to
the directory and the absolute path for the container directory separated by a
-colon. If the source is a named volume maintained by Podman, it's recommended to
-use it's name rather than the path to the volume. Otherwise the volume will be
+colon. If the source is a named volume maintained by Podman, it is recommended to
+use its name rather than the path to the volume. Otherwise the volume will be
considered as an orphan and wiped if you execute **podman volume prune**:
```
$ podman run -v /var/db:/data1 -i -t fedora bash
$ podman run -v data:/data2 -i -t fedora bash
+
+$ podman run -v /var/cache/dnf:/var/cache/dnf:O -ti fedora dnf -y update
```
Using **--mount** flags to mount a host directory as a container folder, specify
@@ -1362,9 +1409,10 @@ required for VPN, without it containers need to be run with the **--network=host
## ENVIRONMENT
Environment variables within containers can be set using multiple different options,
-in the following order of precedence:
+in the following order of precedence (later entries override earlier entries):
- **--env-host**: Host environment of the process executing Podman is added.
+- **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details.
- 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.
@@ -1389,7 +1437,7 @@ b
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
## SEE ALSO
-**subgid**(5), **subuid**(5), **libpod.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1).
+**subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1).
## HISTORY
September 2018, updated by Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
@@ -1398,8 +1446,6 @@ October 2017, converted from Docker documentation to Podman by Dan Walsh for Pod
November 2015, updated by Sally O'Malley <somalley@redhat.com>
-July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
-
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2014, Originally compiled by William Henry <whenry@redhat.com> based on docker.com source material and internal work.
diff --git a/docs/source/markdown/podman-system-migrate.1.md b/docs/source/markdown/podman-system-migrate.1.md
index baabfd14b..29c0ef94b 100644
--- a/docs/source/markdown/podman-system-migrate.1.md
+++ b/docs/source/markdown/podman-system-migrate.1.md
@@ -33,7 +33,7 @@ This can be used after a system upgrade which changes the default OCI runtime to
There are no guarantees that the containers will continue to work under the new runtime, as some runtimes support differing options and configurations.
## SEE ALSO
-`podman(1)`, `libpod.conf(5)`, `usermod(8)`
+`podman(1)`, `containers.conf(5)`, `usermod(8)`
## HISTORY
April 2019, Originally compiled by Giuseppe Scrivano (gscrivan at redhat dot com)
diff --git a/docs/source/markdown/podman-system-renumber.1.md b/docs/source/markdown/podman-system-renumber.1.md
index 071eefe29..51c085606 100644
--- a/docs/source/markdown/podman-system-renumber.1.md
+++ b/docs/source/markdown/podman-system-renumber.1.md
@@ -9,9 +9,9 @@ podman\-system\-renumber - Migrate lock numbers to handle a change in maximum nu
## DESCRIPTION
**podman system renumber** renumbers locks used by containers and pods.
-Each Podman container and pod is allocated a lock at creation time, up to a maximum number controlled by the **num_locks** parameter in **libpod.conf**.
+Each Podman container and pod is allocated a lock at creation time, up to a maximum number controlled by the **num_locks** parameter in **containers.conf**.
-When all available locks are exhausted, no further containers and pods can be created until some existing containers and pods are removed. This can be avoided by increasing the number of locks available via modifying **libpod.conf** and subsequently running **podman system renumber** to prepare the new locks (and reallocate lock numbers to fit the new struct).
+When all available locks are exhausted, no further containers and pods can be created until some existing containers and pods are removed. This can be avoided by increasing the number of locks available via modifying **containers.conf** and subsequently running **podman system renumber** to prepare the new locks (and reallocate lock numbers to fit the new struct).
**podman system renumber** must be called after any changes to **num_locks** - failure to do so will result in errors starting Podman as the number of locks available conflicts with the configured number of locks.
@@ -20,7 +20,7 @@ When all available locks are exhausted, no further containers and pods can be cr
If possible, avoid calling **podman system renumber** while there are other Podman processes running.
## SEE ALSO
-`podman(1)`, `libpod.conf(5)`
+`podman(1)`, `containers.conf(5)`
## HISTORY
February 2019, Originally compiled by Matt Heon (mheon at redhat dot com)
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index c45c10243..776ee7a67 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -23,7 +23,7 @@ created by the other.
**--cgroup-manager**=*manager*
-CGroup manager to use for container cgroups. Supported values are cgroupfs or systemd. Default is systemd unless overridden in the libpod.conf file.
+The CGroup manager to use for container cgroups. Supported values are cgroupfs or systemd. Default is systemd unless overridden in the containers.conf file.
Note: Setting this flag can cause certain commands to break when called on containers previously created by the other CGroup manager type.
Note: CGroup manager is not supported in rootless mode when using CGroups Version V1.
@@ -32,7 +32,7 @@ Note: CGroup manager is not supported in rootless mode when using CGroups Versio
Path of the configuration directory for CNI networks. (Default: `/etc/cni/net.d`)
**--conmon**
-Path of the conmon binary (Default path is configured in `libpod.conf`)
+Path of the conmon binary (Default path is configured in `containers.conf`)
**--events-backend**=*type*
@@ -94,7 +94,7 @@ Default state dir configured in `/etc/containers/storage.conf`.
**--runtime**=*value*
-Name of the OCI runtime as specified in libpod.conf or absolute path to the OCI compatible binary used to run containers.
+Name of the OCI runtime as specified in containers.conf or absolute path to the OCI compatible binary used to run containers.
**--storage-driver**=*value*
@@ -217,11 +217,13 @@ the exit codes follow the `chroot` standard, see below:
## FILES
-**libpod.conf** (`/usr/share/containers/libpod.conf`)
+**containers.conf** (`/usr/share/containers/containers.conf`)
- libpod.conf is the configuration file for all tools using libpod to manage containers, when run as root. Administrators can override the defaults file by creating `/etc/containers/libpod.conf`. When Podman runs in rootless mode, the file `$HOME/.config/containers/libpod.conf` is created and replaces some fields in the system configuration file.
+ Podman has builtin defaults for command line options. These defaults can be overridden using the containers.conf configuration files.
- Podman uses builtin defaults if no libpod.conf file is found.
+Distributions ship the `/usr/share/containers/containers.conf` file with their default settings. Administrators can override fields in this file by creating the `/etc/containers/containers.conf` file. Users can further modify defaults by creating the `$HOME/.config/containers/containers.conf` file. Podman merges its builtin defaults with the specified fields from these files, if they exist. Fields specified in the users file override the administrator's file, which overrides the distribution's file, which override the built-in defaults.
+
+Podman uses builtin defaults if no containers.conf file is found.
**mounts.conf** (`/usr/share/containers/mounts.conf`)
@@ -280,7 +282,7 @@ The Network File System (NFS) and other distributed file systems (for example: L
For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/libpod/blob/master/troubleshooting.md).
## SEE ALSO
-`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `buildah(1)`, `libpod.conf(5)`, `oci-hooks(5)`, `containers-policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)`
+`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `buildah(1)`, `containers.conf(5)`, `oci-hooks(5)`, `containers-policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)`
## HISTORY
Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md
index 440e12062..821c07647 100644
--- a/docs/tutorials/rootless_tutorial.md
+++ b/docs/tutorials/rootless_tutorial.md
@@ -13,7 +13,7 @@ The alternative OCI runtime support for cgroup V2 can be turned on at the comma
```
sudo podman --runtime /usr/bin/crun
```
-or by changing the value for the "Default OCI runtime" in the libpod.conf file either at the system level or at the [user level](#user-configuration-files) from `runtime = "runc"` to `runtime = "crun"`.
+or by changing the value for the "Default OCI runtime" in the containers.conf file either at the system level or at the [user level](#user-configuration-files) from `runtime = "runc"` to `runtime = "crun"`.
## Administrator Actions
@@ -106,7 +106,7 @@ Once the Administrator has completed the setup on the machine and then the confi
### User Configuration Files
-The Podman configuration files for root reside in `/usr/share/containers` with overrides in `/etc/containers`. In the rootless environment they reside in `${XDG_CONFIG_HOME}/containers` (usually `~/.config/containers`) and are owned by each individual user. The main files are `libpod.conf` and `storage.conf` and the user can modify these files as they wish.
+The Podman configuration files for root reside in `/usr/share/containers` with overrides in `/etc/containers`. In the rootless environment they reside in `${XDG_CONFIG_HOME}/containers` (usually `~/.config/containers`) and are owned by each individual user. The main files are `containers.conf` and `storage.conf` and the user can modify these files as they wish.
The default authorization file used by the `podman login` and `podman logout` commands reside in `${XDG_RUNTIME_DIR}/containers/auth.json`.
diff --git a/go.mod b/go.mod
index bf9718dea..e165b92f4 100644
--- a/go.mod
+++ b/go.mod
@@ -11,11 +11,11 @@ require (
github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921
github.com/containernetworking/plugins v0.8.6
github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d
- github.com/containers/common v0.16.0
+ github.com/containers/common v0.17.0
github.com/containers/conmon v2.0.19+incompatible
github.com/containers/image/v5 v5.5.1
github.com/containers/psgo v1.5.1
- github.com/containers/storage v1.21.1
+ github.com/containers/storage v1.21.2
github.com/coreos/go-systemd/v22 v22.1.0
github.com/cri-o/ocicni v0.2.0
github.com/cyphar/filepath-securejoin v0.2.2
@@ -41,7 +41,7 @@ require (
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/runc v1.0.0-rc91.0.20200708210054-ce54a9d4d79b
github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2
- github.com/opencontainers/runtime-tools v0.9.0
+ github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa
github.com/opencontainers/selinux v1.6.0
github.com/opentracing/opentracing-go v1.2.0
github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index 7f95292ac..ec04d7073 100644
--- a/go.sum
+++ b/go.sum
@@ -73,8 +73,8 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV
github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d h1:HgJJn1UBFjM464NpEmgLwVje5vSF/fBYAdLLoww9HgU=
github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d/go.mod h1:HUAiD1mCGPFPcIuk5zls1LElLhXo7Q3hWDwheojjyAs=
github.com/containers/common v0.15.2/go.mod h1:rhpXuGLTEKsk/xX/x0iKGHjRadMHpBd2ZiNDugwXPEM=
-github.com/containers/common v0.16.0 h1:zAxDJ2tA2wBEjXwV/+ddC8s1f3MfqulH3waSjKxlX3o=
-github.com/containers/common v0.16.0/go.mod h1:siKqOA03Bhh7Ss2m7fCsbVvbjwaNqrI+gZtC9FUp+DI=
+github.com/containers/common v0.17.0 h1:3wkcSLw92UYPEhPSHeG1szyIe1qFgi9Jtj18l/tTii0=
+github.com/containers/common v0.17.0/go.mod h1:bG22Wvr0iyWJ8UvIkL5dA5OEZFDqAJx1MejmWZb51HE=
github.com/containers/conmon v2.0.19+incompatible h1:1bDVRvHy2MUNTUT/SW6LlHsJHQBTSwXvnKNdcB/a1vQ=
github.com/containers/conmon v2.0.19+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.5.1 h1:h1FCOXH6Ux9/p/E4rndsQOC4yAdRU0msRTfLVeQ7FDQ=
@@ -88,6 +88,8 @@ github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzP
github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc=
github.com/containers/storage v1.21.1 h1:FGA2c7+0Bn8ndrlrj+HHmKeVjFD3yVhvYa0gijsrg1M=
github.com/containers/storage v1.21.1/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw=
+github.com/containers/storage v1.21.2 h1:bf9IqA+g6ClBviqVG5lVCp5tTH9lvWwjYws7mVYSti0=
+github.com/containers/storage v1.21.2/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
@@ -342,6 +344,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.m
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa h1:iyj+fFHVBn0xOalz9UChYzSU1K0HJ+d75b4YqShBRhI=
+github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
diff --git a/hack/check_root.sh b/hack/check_root.sh
new file mode 100755
index 000000000..203eae9d3
--- /dev/null
+++ b/hack/check_root.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+if ! [ $(id -u) = 0 ]; then
+ echo "Please run as root! '$@' requires root privileges."
+ exit 1
+fi
diff --git a/libpod/container.go b/libpod/container.go
index 1ca38ae7e..8a69df685 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -262,6 +262,8 @@ type ContainerConfig struct {
Mounts []string `json:"mounts,omitempty"`
// NamedVolumes lists the named volumes to mount into the container.
NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
+ // OverlayVolumes lists the overlay volumes to mount into the container.
+ OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
// Security Config
@@ -435,6 +437,9 @@ type ContainerConfig struct {
// Timezone is the timezone inside the container.
// Local means it has the same timezone as the host machine
Timezone string `json:"timezone,omitempty"`
+
+ // Umask is the umask inside the container.
+ Umask string `json:"umask,omitempty"`
}
// ContainerNamedVolume is a named volume that will be mounted into the
@@ -449,6 +454,15 @@ type ContainerNamedVolume struct {
Options []string `json:"options,omitempty"`
}
+// ContainerOverlayVolume is a overlay volume that will be mounted into the
+// container. Each volume is a libpod Volume present in the state.
+type ContainerOverlayVolume struct {
+ // Destination is the absolute path where the mount will be placed in the container.
+ Dest string `json:"dest"`
+ // Source specifies the source path of the mount.
+ Source string `json:"source,omitempty"`
+}
+
// Config accessors
// Unlocked
@@ -1265,5 +1279,8 @@ func (c *Container) AutoRemove() bool {
func (c *Container) Timezone() string {
return c.config.Timezone
+}
+func (c *Container) Umask() string {
+ return c.config.Umask
}
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index bd04ee9b9..a16aea06d 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -729,10 +729,6 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
return -1, err
}
- if exitCode != 0 {
- return exitCode, errors.Wrapf(define.ErrOCIRuntime, "exec session exited with non-zero exit code %d", exitCode)
- }
-
return exitCode, nil
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 680776dba..a0d223c8c 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -325,6 +325,14 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Timezone = c.config.Timezone
+ // Pad Umask to 4 characters
+ if len(c.config.Umask) < 4 {
+ pad := strings.Repeat("0", 4-len(c.config.Umask))
+ ctrConfig.Umask = pad + c.config.Umask
+ } else {
+ ctrConfig.Umask = c.config.Umask
+ }
+
return ctrConfig
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index a79b9e5a8..b2e23b3a8 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1588,6 +1588,12 @@ func (c *Container) cleanupStorage() error {
}
}
+ if err := c.cleanupOverlayMounts(); err != nil {
+ // If the container can't remove content report the error
+ logrus.Errorf("Failed to cleanup overlay mounts for %s: %v", c.ID(), err)
+ cleanupErr = err
+ }
+
if c.config.Rootfs != "" {
return cleanupErr
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 255505416..edea62a0d 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -20,6 +20,7 @@ import (
cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containers/buildah/pkg/overlay"
"github.com/containers/buildah/pkg/secrets"
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/config"
@@ -319,6 +320,19 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
+ // Add overlay volumes
+ for _, overlayVol := range c.config.OverlayVolumes {
+ contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID())
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir)
+ }
+ overlayMount, err := overlay.Mount(contentDir, overlayVol.Source, overlayVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
+ if err != nil {
+ return nil, errors.Wrapf(err, "creating overlay failed %q", overlayVol.Source)
+ }
+ g.AddMount(overlayMount)
+ }
+
hasHomeSet := false
for _, s := range c.config.Spec.Process.Env {
if strings.HasPrefix(s, "HOME=") {
@@ -341,6 +355,14 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.SetProcessGID(uint32(execUser.Gid))
}
+ if c.config.Umask != "" {
+ decVal, err := strconv.ParseUint(c.config.Umask, 8, 32)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Invalid Umask Value")
+ }
+ g.SetProcessUmask(uint32(decVal))
+ }
+
// Add addition groups if c.config.GroupAdd is not empty
if len(c.config.Groups) > 0 {
gids, err := lookup.GetContainerGroups(c.config.Groups, c.state.Mountpoint, overrides)
@@ -1642,3 +1664,7 @@ func (c *Container) copyTimezoneFile(zonePath string) (string, error) {
}
return localtimeCopy, err
}
+
+func (c *Container) cleanupOverlayMounts() error {
+ return overlay.CleanupContent(c.config.StaticDir)
+}
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index e6d94104c..b58a5116b 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -46,6 +46,10 @@ func (c *Container) getOCICgroupPath() (string, error) {
return "", define.ErrNotImplemented
}
+func (c *Container) cleanupOverlayMounts() error {
+ return nil
+}
+
func (c *Container) getUserOverrides() *lookup.Overrides {
return nil
}
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index c02833359..666ad0aca 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -99,5 +99,24 @@ func (c *Container) validate() error {
return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if using image's /etc/hosts")
}
+ // Check named volume and overlay volumes destination conflits
+ destinations := make(map[string]bool)
+ for _, vol := range c.config.NamedVolumes {
+ // Don't check if they already exist.
+ // If they don't we will automatically create them.
+ if _, ok := destinations[vol.Dest]; ok {
+ return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest)
+ }
+ destinations[vol.Dest] = true
+ }
+ for _, vol := range c.config.OverlayVolumes {
+ // Don't check if they already exist.
+ // If they don't we will automatically create them.
+ if _, ok := destinations[vol.Dest]; ok {
+ return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest)
+ }
+ destinations[vol.Dest] = true
+ }
+
return nil
}
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 64b24d9e2..6c426f2ec 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -20,6 +20,8 @@ var (
NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
// RegexError is thrown in presence of an invalid container/pod name.
RegexError = errors.Wrapf(ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
+ // UmaskRegex is a regular expression to validate Umask.
+ UmaskRegex = regexp.MustCompile(`^[0-7]{1,4}$`)
)
const (
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index fbd9da3e7..a08cb3de6 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -61,6 +61,8 @@ type InspectContainerConfig struct {
// systemd mode, the container configuration is customized to optimize
// running systemd in the container.
SystemdMode bool `json:"SystemdMode,omitempty"`
+ // Umask is the umask inside the container.
+ Umask string `json:"Umask,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index d341ca7b5..7c2a3e0f2 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -90,6 +90,13 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
return err
}
for {
+ select {
+ case <-ctx.Done():
+ // the consumer has cancelled
+ return nil
+ default:
+ // fallthrough
+ }
if _, err := j.Next(); err != nil {
return err
}
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index 28d0dc07e..b70102450 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -63,6 +63,14 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
}
}()
for line := range t.Lines {
+ select {
+ case <-ctx.Done():
+ // the consumer has cancelled
+ return nil
+ default:
+ // fallthrough
+ }
+
event, err := newEventFromJSONString(line.Text)
if err != nil {
return err
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index b04742974..4818f8dc4 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -92,7 +92,7 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) {
hcResult := define.HealthCheckSuccess
config := new(ExecConfig)
config.Command = newCommand
- _, hcErr := c.Exec(config, streams, nil)
+ exitCode, hcErr := c.Exec(config, streams, nil)
if hcErr != nil {
errCause := errors.Cause(hcErr)
hcResult = define.HealthCheckFailure
@@ -104,6 +104,9 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) {
} else {
returnCode = 125
}
+ } else if exitCode != 0 {
+ hcResult = define.HealthCheckFailure
+ returnCode = 1
}
timeEnd := time.Now()
if c.HealthCheckConfig().StartPeriod > 0 {
diff --git a/libpod/options.go b/libpod/options.go
index 32748a3c1..41b0d7212 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1380,17 +1380,7 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
return define.ErrCtrFinalized
}
- destinations := make(map[string]bool)
-
for _, vol := range volumes {
- // Don't check if they already exist.
- // If they don't we will automatically create them.
-
- if _, ok := destinations[vol.Dest]; ok {
- return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest)
- }
- destinations[vol.Dest] = true
-
mountOpts, err := util.ProcessOptions(vol.Options, false, "")
if err != nil {
return errors.Wrapf(err, "error processing options for named volume %q mounted at %q", vol.Name, vol.Dest)
@@ -1407,6 +1397,25 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
}
}
+// WithOverlayVolumes adds the given overlay volumes to the container.
+func WithOverlayVolumes(volumes []*ContainerOverlayVolume) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ for _, vol := range volumes {
+
+ ctr.config.OverlayVolumes = append(ctr.config.OverlayVolumes, &ContainerOverlayVolume{
+ Dest: vol.Dest,
+ Source: vol.Source,
+ })
+ }
+
+ return nil
+ }
+}
+
// WithHealthCheck adds the healthcheck to the container config
func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption {
return func(ctr *Container) error {
@@ -1598,6 +1607,20 @@ func WithTimezone(path string) CtrCreateOption {
}
}
+// WithUmask sets the umask in the container
+func WithUmask(umask string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ if !define.UmaskRegex.MatchString(umask) {
+ return errors.Wrapf(define.ErrInvalidArg, "Invalid umask string %s", umask)
+ }
+ ctr.config.Umask = umask
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodName sets the name of the pod.
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 0075c0e13..96243c808 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -84,7 +84,7 @@ type Runtime struct {
// SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
// containers/image uses XDG_RUNTIME_DIR to locate the auth file, XDG_CONFIG_HOME is
-// use for the libpod.conf configuration file.
+// use for the containers.conf configuration file.
func SetXdgDirs() error {
if !rootless.IsRootless() {
return nil
@@ -577,7 +577,7 @@ func (r *Runtime) Shutdown(force bool) error {
}
var lastError error
- // If no store was requested, it can bew nil and there is no need to
+ // If no store was requested, it can be nil and there is no need to
// attempt to shut it down
if r.store != nil {
if _, err := r.store.Shutdown(force); err != nil {
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 5acc94153..9d5cb5045 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -1,9 +1,10 @@
package compat
import (
- "context"
+ "encoding/json"
"fmt"
"net/http"
+ "sync"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/libpod/events"
@@ -15,77 +16,132 @@ import (
"github.com/sirupsen/logrus"
)
+// filtersFromRequests extracts the "filters" parameter from the specified
+// http.Request. The paramater can either be a `map[string][]string` as done
+// in new versions of Docker and libpod, or a `map[string]map[string]bool` as
+// done in older versions of Docker. We have to do a bit of Yoga to support
+// both - just as Docker does as well.
+//
+// Please refer to https://github.com/containers/podman/issues/6899 for some
+// background.
+func filtersFromRequest(r *http.Request) ([]string, error) {
+ var (
+ compatFilters map[string]map[string]bool
+ filters map[string][]string
+ libpodFilters []string
+ )
+ raw := []byte(r.Form.Get("filters"))
+
+ // Backwards compat with older versions of Docker.
+ if err := json.Unmarshal(raw, &compatFilters); err == nil {
+ for filterKey, filterMap := range compatFilters {
+ for filterValue, toAdd := range filterMap {
+ if toAdd {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
+ }
+ }
+ }
+ return libpodFilters, nil
+ }
+
+ if err := json.Unmarshal(raw, &filters); err != nil {
+ return nil, err
+ }
+
+ for filterKey, filterSlice := range filters {
+ for _, filterValue := range filterSlice {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
+ }
+ }
+
+ return libpodFilters, nil
+}
+
+// NOTE: this endpoint serves both the docker-compatible one and the new libpod
+// one.
func GetEvents(w http.ResponseWriter, r *http.Request) {
var (
- fromStart bool
- eventsError error
- decoder = r.Context().Value("decoder").(*schema.Decoder)
- runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ fromStart bool
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ json = jsoniter.ConfigCompatibleWithStandardLibrary // FIXME: this should happen on the package level
)
+ // NOTE: the "filters" parameter is extracted separately for backwards
+ // compat via `fitlerFromRequest()`.
query := struct {
- Since string `schema:"since"`
- Until string `schema:"until"`
- Filters map[string][]string `schema:"filters"`
- Stream bool `schema:"stream"`
+ Since string `schema:"since"`
+ Until string `schema:"until"`
+ Stream bool `schema:"stream"`
}{
Stream: true,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
- }
-
- var libpodFilters = []string{}
- if _, found := r.URL.Query()["filters"]; found {
- for k, v := range query.Filters {
- libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
- }
+ return
}
if len(query.Since) > 0 || len(query.Until) > 0 {
fromStart = true
}
- eventCtx, eventCancel := context.WithCancel(r.Context())
- eventChannel := make(chan *events.Event)
- go func() {
- readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until}
- eventsError = runtime.Events(eventCtx, readOpts)
- }()
- if eventsError != nil {
- utils.InternalServerError(w, eventsError)
- eventCancel()
- close(eventChannel)
+ libpodFilters, err := filtersFromRequest(r)
+ if err != nil {
+ utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- // If client disappears we need to stop listening for events
- go func(done <-chan struct{}) {
- <-done
- eventCancel()
- if _, ok := <-eventChannel; ok {
- close(eventChannel)
+ eventChannel := make(chan *events.Event)
+ errorChannel := make(chan error)
+
+ // Start reading events.
+ go func() {
+ readOpts := events.ReadOptions{
+ FromStart: fromStart,
+ Stream: query.Stream,
+ Filters: libpodFilters,
+ EventChannel: eventChannel,
+ Since: query.Since,
+ Until: query.Until,
}
- }(r.Context().Done())
+ errorChannel <- runtime.Events(r.Context(), readOpts)
+ }()
- // Headers need to be written out before turning Writer() over to json encoder
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
- }
+ var coder *jsoniter.Encoder
+ var writeHeader sync.Once
- json := jsoniter.ConfigCompatibleWithStandardLibrary
- coder := json.NewEncoder(w)
- coder.SetEscapeHTML(true)
+ for stream := true; stream; stream = query.Stream {
+ select {
+ case err := <-errorChannel:
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ case evt := <-eventChannel:
+ writeHeader.Do(func() {
+ // Use a sync.Once so that we write the header
+ // only once.
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
+ coder = json.NewEncoder(w)
+ coder.SetEscapeHTML(true)
+ })
- for event := range eventChannel {
- e := entities.ConvertToEntitiesEvent(*event)
- if err := coder.Encode(e); err != nil {
- logrus.Errorf("unable to write json: %q", err)
- }
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
+ if evt == nil {
+ continue
+ }
+
+ e := entities.ConvertToEntitiesEvent(*evt)
+ if err := coder.Encode(e); err != nil {
+ logrus.Errorf("unable to write json: %q", err)
+ }
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
}
+
}
}
diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go
index 82f1dc680..a1ab3f727 100644
--- a/pkg/api/server/register_generate.go
+++ b/pkg/api/server/register_generate.go
@@ -13,8 +13,8 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// tags:
// - containers
// - pods
- // summary: Play a Kubernetes YAML file.
- // description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
+ // summary: Generate a Kubernetes YAML file.
+ // description: Generate Kubernetes YAML based on a pod or container.
// parameters:
// - in: path
// name: name:.*
diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go
index 93141400b..430184f4a 100644
--- a/pkg/bindings/test/system_test.go
+++ b/pkg/bindings/test/system_test.go
@@ -1,6 +1,7 @@
package test_bindings
import (
+ "sync"
"time"
"github.com/containers/libpod/v2/pkg/bindings"
@@ -38,22 +39,28 @@ var _ = Describe("Podman system", func() {
})
It("podman events", func() {
- eChan := make(chan entities.Event, 1)
- var messages []entities.Event
- cancelChan := make(chan bool, 1)
+ var name = "top"
+ _, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ filters := make(map[string][]string)
+ filters["container"] = []string{name}
+
+ binChan := make(chan entities.Event)
+ done := sync.Mutex{}
+ done.Lock()
+ eventCounter := 0
go func() {
- for e := range eChan {
- messages = append(messages, e)
+ defer done.Unlock()
+ for range binChan {
+ eventCounter++
}
}()
- go func() {
- system.Events(bt.conn, eChan, cancelChan, nil, nil, nil, bindings.PFalse)
- }()
- _, err := bt.RunTopContainer(nil, nil, nil)
+ err = system.Events(bt.conn, binChan, nil, nil, nil, filters, bindings.PFalse)
Expect(err).To(BeNil())
- cancelChan <- true
- Expect(len(messages)).To(BeNumerically("==", 5))
+ done.Lock()
+ Expect(eventCounter).To(BeNumerically(">", 0))
})
It("podman system prune - pod,container stopped", func() {
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index f82da2c95..888811958 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -453,11 +453,16 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.Command = []string{}
if imageData != nil && imageData.Config != nil {
- containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...)
+ containerConfig.Command = imageData.Config.Entrypoint
}
if len(containerYAML.Command) != 0 {
- containerConfig.Command = append(containerConfig.Command, containerYAML.Command...)
- } else if imageData != nil && imageData.Config != nil {
+ containerConfig.Command = containerYAML.Command
+ }
+ // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes
+ if len(containerYAML.Args) != 0 {
+ containerConfig.Command = append(containerConfig.Command, containerYAML.Args...)
+ } else if len(containerYAML.Command) == 0 {
+ // Add the Cmd from the image config only if containerYAML.Command and containerYAML.Args are empty
containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...)
}
if imageData != nil && len(containerConfig.Command) == 0 {
diff --git a/pkg/network/config.go b/pkg/network/config.go
index e5c981419..a504e0ad0 100644
--- a/pkg/network/config.go
+++ b/pkg/network/config.go
@@ -6,8 +6,8 @@ import (
"net"
)
-// TODO once the libpod.conf file stuff is worked out, this should be modified
-// to honor defines in the libpod.conf as well as overrides?
+// TODO once the containers.conf file stuff is worked out, this should be modified
+// to honor defines in the containers.conf as well as overrides?
const (
// CNIConfigDir is the path where CNI config files exist
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index c1ceac69e..934d5fbac 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -145,6 +145,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Timezone != "" {
options = append(options, libpod.WithTimezone(s.Timezone))
}
+ if s.Umask != "" {
+ options = append(options, libpod.WithUmask(s.Umask))
+ }
useSystemd := false
switch s.Systemd {
@@ -201,6 +204,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
for _, volume := range volumes {
destinations = append(destinations, volume.Dest)
}
+ for _, overlayVolume := range s.OverlayVolumes {
+ destinations = append(destinations, overlayVolume.Destination)
+ }
options = append(options, libpod.WithUserVolumes(destinations))
if len(volumes) != 0 {
@@ -215,6 +221,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
options = append(options, libpod.WithNamedVolumes(vols))
}
+ if len(s.OverlayVolumes) != 0 {
+ var vols []*libpod.ContainerOverlayVolume
+ for _, v := range s.OverlayVolumes {
+ vols = append(vols, &libpod.ContainerOverlayVolume{
+ Dest: v.Destination,
+ Source: v.Source,
+ })
+ }
+ options = append(options, libpod.WithOverlayVolumes(vols))
+ }
+
if s.Command != nil {
options = append(options, libpod.WithCommand(s.Command))
}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index a346a9742..84a6c36a0 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -198,6 +198,9 @@ type ContainerStorageConfig struct {
// there are conflicts.
// Optional.
Volumes []*NamedVolume `json:"volumes,omitempty"`
+ // Overlay volumes are named volumes that will be added to the container.
+ // Optional.
+ OverlayVolumes []*OverlayVolume `json:"overlay_volumes,omitempty"`
// Devices are devices that will be added to the container.
// Optional.
Devices []spec.LinuxDevice `json:"devices,omitempty"`
@@ -284,6 +287,8 @@ type ContainerSecurityConfig struct {
// ReadOnlyFilesystem indicates that everything will be mounted
// as read-only
ReadOnlyFilesystem bool `json:"read_only_filesystem,omittempty"`
+ // Umask is the umask the init process of the container will be run with.
+ Umask string `json:"umask,omitempty"`
}
// ContainerCgroupConfig contains configuration information about a container's
@@ -443,6 +448,15 @@ type NamedVolume struct {
Options []string
}
+// OverlayVolume holds information about a overlay volume that will be mounted into
+// the container.
+type OverlayVolume struct {
+ // Destination is the absolute path where the mount will be placed in the container.
+ Destination string `json:"destination"`
+ // Source specifies the source path of the mount.
+ Source string `json:"source,omitempty"`
+}
+
// PortMapping is one or more ports that will be mapped into the container.
type PortMapping struct {
// HostIP is the IP that we will bind to on the host.
diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go
index 416e60728..eab2657e3 100644
--- a/pkg/util/mountOpts.go
+++ b/pkg/util/mountOpts.go
@@ -33,6 +33,10 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
// Some options have parameters - size, mode
splitOpt := strings.SplitN(opt, "=", 2)
switch splitOpt[0] {
+ case "O":
+ if len(options) > 1 {
+ return nil, errors.Wrapf(ErrDupeMntOption, "'O' option can not be used with other options")
+ }
case "exec", "noexec":
if foundExec {
return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'noexec' and 'exec' can be used")
diff --git a/test/e2e/attach_test.go b/test/e2e/attach_test.go
index 9fd1466aa..827b7568d 100644
--- a/test/e2e/attach_test.go
+++ b/test/e2e/attach_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -42,6 +40,7 @@ var _ = Describe("Podman attach", func() {
})
It("podman attach to non-running container", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"create", "--name", "test1", "-d", "-i", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -52,6 +51,7 @@ var _ = Describe("Podman attach", func() {
})
It("podman container attach to non-running container", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"container", "create", "--name", "test1", "-d", "-i", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -87,6 +87,7 @@ var _ = Describe("Podman attach", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
})
It("podman attach to the latest container", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", ALPINE, "/bin/sh", "-c", "while true; do echo test1; sleep 1; done"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index 9c8078f16..c72d2243a 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -40,6 +38,7 @@ var _ = Describe("Podman build", func() {
// Let's first do the most simple build possible to make sure stuff is
// happy and then clean up after ourselves to make sure that works too.
It("podman build and remove basic alpine", func() {
+ SkipIfRemote()
session := podmanTest.PodmanNoCache([]string{"build", "build/basicalpine"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -69,6 +68,7 @@ var _ = Describe("Podman build", func() {
// Check that builds with different values for the squash options
// create the appropriate number of layers, then clean up after.
It("podman build basic alpine with squash", func() {
+ SkipIfRemote()
session := podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-a", "-t", "test-squash-a:latest", "build/squash"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -154,6 +154,7 @@ var _ = Describe("Podman build", func() {
})
It("podman build basic alpine and print id to external file", func() {
+ SkipIfRemote()
// Switch to temp dir and restore it afterwards
cwd, err := os.Getwd()
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index aa0e9635a..f475a927c 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -14,6 +14,7 @@ import (
"time"
"github.com/containers/libpod/v2/libpod/define"
+ "github.com/containers/libpod/v2/pkg/cgroups"
"github.com/containers/libpod/v2/pkg/inspect"
"github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
@@ -599,3 +600,21 @@ func SkipIfNotFedora() {
func isRootless() bool {
return os.Geteuid() != 0
}
+
+func SkipIfCgroupV1() {
+ cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
+ Expect(err).To(BeNil())
+
+ if !cgroupsv2 {
+ Skip("Skip on systems with cgroup V1 systems")
+ }
+}
+
+func SkipIfCgroupV2() {
+ cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
+ Expect(err).To(BeNil())
+
+ if cgroupsv2 {
+ Skip("Skip on systems with cgroup V2 systems")
+ }
+}
diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf
index 0a07676c4..5f852468d 100644
--- a/test/e2e/config/containers.conf
+++ b/test/e2e/config/containers.conf
@@ -50,3 +50,5 @@ dns_servers=[ "1.2.3.4", ]
dns_options=[ "debug", ]
tz = "Pacific/Honolulu"
+
+umask = "0002"
diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go
index 23d8dd197..aebbca855 100644
--- a/test/e2e/containers_conf_test.go
+++ b/test/e2e/containers_conf_test.go
@@ -218,6 +218,17 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("HST"))
+ })
+ It("podman run containers.conf umask", func() {
+ //containers.conf umask set to 0002
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0002"))
})
+
})
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index f21f17d39..09b4f5911 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
@@ -499,4 +500,46 @@ var _ = Describe("Podman create", func() {
Expect(data[0].Config.Timezone).To(Equal("local"))
})
+ It("podman create --umask", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+
+ session := podmanTest.Podman([]string{"create", "--name", "default", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect := podmanTest.Podman([]string{"inspect", "default"})
+ inspect.WaitWithDefaultTimeout()
+ data := inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "0002", "--name", "umask", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "umask"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0002"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "0077", "--name", "fedora", fedoraMinimal})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "fedora"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0077"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "22", "--name", "umask-short", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "umask-short"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "9999", "--name", "bad", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask"))
+ })
})
diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go
index 93c51098f..9b527b88e 100644
--- a/test/e2e/events_test.go
+++ b/test/e2e/events_test.go
@@ -136,6 +136,7 @@ var _ = Describe("Podman events", func() {
Expect(ec).To(Equal(0))
test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"})
test.WaitWithDefaultTimeout()
+ Expect(test.ExitCode()).To(BeZero())
jsonArr := test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero()))
eventsMap := make(map[string]string)
@@ -143,10 +144,10 @@ var _ = Describe("Podman events", func() {
Expect(err).To(BeNil())
_, exist := eventsMap["Status"]
Expect(exist).To(BeTrue())
- Expect(test.ExitCode()).To(BeZero())
test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"})
test.WaitWithDefaultTimeout()
+ Expect(test.ExitCode()).To(BeZero())
jsonArr = test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero()))
eventsMap = make(map[string]string)
@@ -154,6 +155,5 @@ var _ = Describe("Podman events", func() {
Expect(err).To(BeNil())
_, exist = eventsMap["Status"]
Expect(exist).To(BeTrue())
- Expect(test.ExitCode()).To(BeZero())
})
})
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index 987e4779c..7a3bfdf61 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -254,8 +252,6 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman generate with user and reimport kube on pod", func() {
- // This test fails on ubuntu due to https://github.com/seccomp/containers-golang/pull/27
- SkipIfNotFedora()
podName := "toppod"
_, rc, _ := podmanTest.CreatePod(podName)
Expect(rc).To(Equal(0))
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 13f4e1aef..263139064 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -214,5 +214,3 @@ func (p *PodmanTestIntegration) DelayForService() error {
func populateCache(podman *PodmanTestIntegration) {}
func removeCache() {}
-func SkipIfCgroupV1() {}
-func SkipIfCgroupV2() {}
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 29ad01363..49e4e53cd 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -9,32 +9,12 @@ import (
"path/filepath"
"strings"
- "github.com/containers/libpod/v2/pkg/cgroups"
. "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
)
func SkipIfRemote() {
}
-func SkipIfCgroupV1() {
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
- if !cgroupsv2 {
- Skip("Skip on systems with cgroup V1 systems")
- }
-}
-
-func SkipIfCgroupV2() {
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
- if cgroupsv2 {
- Skip("Skip on systems with cgroup V2 systems")
- }
-}
-
func SkipIfRootless() {
if os.Geteuid() != 0 {
Skip("This function is not enabled for rootless podman")
diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go
index df2613334..879291c36 100644
--- a/test/e2e/load_test.go
+++ b/test/e2e/load_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -125,6 +123,7 @@ var _ = Describe("Podman load", func() {
})
It("podman load directory", func() {
+ SkipIfRemote()
outdir := filepath.Join(podmanTest.TempDir, "alpine")
save := podmanTest.PodmanNoCache([]string{"save", "--format", "oci-dir", "-o", outdir, ALPINE})
@@ -228,6 +227,7 @@ var _ = Describe("Podman load", func() {
})
It("podman load localhost registry from dir", func() {
+ SkipIfRemote()
outfile := filepath.Join(podmanTest.TempDir, "load")
setup := podmanTest.PodmanNoCache([]string{"tag", BB, "hello:world"})
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 23604f47d..4b68f6232 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"text/template"
. "github.com/containers/libpod/v2/test/utils"
@@ -50,6 +51,10 @@ spec:
{{ range .Cmd }}
- {{.}}
{{ end }}
+ args:
+ {{ range .Arg }}
+ - {{.}}
+ {{ end }}
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
@@ -129,6 +134,10 @@ spec:
{{ range .Cmd }}
- {{.}}
{{ end }}
+ args:
+ {{ range .Arg }}
+ - {{.}}
+ {{ end }}
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
@@ -171,6 +180,7 @@ spec:
var (
defaultCtrName = "testCtr"
defaultCtrCmd = []string{"top"}
+ defaultCtrArg = []string{"-d", "1.5"}
defaultCtrImage = ALPINE
defaultPodName = "testPod"
defaultDeploymentName = "testDeployment"
@@ -322,6 +332,7 @@ type Ctr struct {
Name string
Image string
Cmd []string
+ Arg []string
SecurityContext bool
Caps bool
CapAdd []string
@@ -332,7 +343,7 @@ type Ctr struct {
// getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
// and the configured options
func getCtr(options ...ctrOption) *Ctr {
- c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil, ""}
+ c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, defaultCtrArg, true, false, nil, nil, ""}
for _, option := range options {
option(&c)
}
@@ -347,6 +358,12 @@ func withCmd(cmd []string) ctrOption {
}
}
+func withArg(arg []string) ctrOption {
+ return func(c *Ctr) {
+ c.Arg = arg
+ }
+}
+
func withImage(img string) ctrOption {
return func(c *Ctr) {
c.Image = img
@@ -438,14 +455,50 @@ var _ = Describe("Podman generate kube", func() {
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ // Use the defined command to override the image's command
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
+ })
+
+ It("podman play kube test correct command with only set command in yaml file", func() {
+ pod := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg(nil))))
+ err := generatePodKubeYaml(pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ // Use the defined command to override the image's command, and don't set the args
+ // so the full command in result should not contains the image's command
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`))
+ })
+
+ It("podman play kube test correct command with only set args in yaml file", func() {
+ pod := getPod(withCtr(getCtr(withImage(redis), withCmd(nil), withArg([]string{"echo", "hello"}))))
+ err := generatePodKubeYaml(pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ // this image's ENTRYPOINT is called `docker-entrypoint.sh`
+ // so result should be `docker-entrypoint.sh + withArg(...)`
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[docker-entrypoint.sh echo hello]`))
})
It("podman play kube test correct output", func() {
- p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}))))
+ p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg([]string{"world"}))))
err := generatePodKubeYaml(p, kubeYaml)
Expect(err).To(BeNil())
@@ -457,12 +510,12 @@ var _ = Describe("Podman generate kube", func() {
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(p)})
logs.WaitWithDefaultTimeout()
Expect(logs.ExitCode()).To(Equal(0))
- Expect(logs.OutputToString()).To(ContainSubstring("hello"))
+ Expect(logs.OutputToString()).To(ContainSubstring("hello world"))
inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring("hello"))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello world]`))
})
It("podman play kube test hostname", func() {
@@ -498,7 +551,7 @@ var _ = Describe("Podman generate kube", func() {
It("podman play kube cap add", func() {
capAdd := "CAP_SYS_ADMIN"
- ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}))
+ ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}), withArg(nil))
pod := getPod(withCtr(ctr))
err := generatePodKubeYaml(pod, kubeYaml)
@@ -556,7 +609,7 @@ var _ = Describe("Podman generate kube", func() {
}
ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName
- ctr := getCtr(withCmd([]string{"pwd"}))
+ ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil))
pod := getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile)))
err = generatePodKubeYaml(pod, kubeYaml)
@@ -582,7 +635,7 @@ var _ = Describe("Podman generate kube", func() {
}
defer os.Remove(jsonFile)
- ctr := getCtr(withCmd([]string{"pwd"}))
+ ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil))
pod := getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile)))
err = generatePodKubeYaml(pod, kubeYaml)
@@ -734,10 +787,12 @@ spec:
Expect(kube.ExitCode()).To(Equal(0))
podNames := getPodNamesInDeployment(deployment)
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0])})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ // yaml's command shuold override the image's Entrypoint
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
})
It("podman play kube deployment more than 1 replica test correct command", func() {
@@ -752,11 +807,12 @@ spec:
Expect(kube.ExitCode()).To(Equal(0))
podNames := getPodNamesInDeployment(deployment)
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
for i = 0; i < numReplicas; i++ {
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i])})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
}
})
})
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index eec2877e8..665cd1b55 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -237,6 +235,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from docker-archive", func() {
+ SkipIfRemote()
podmanTest.RestoreArtifact(ALPINE)
tarfn := filepath.Join(podmanTest.TempDir, "alp.tar")
session := podmanTest.PodmanNoCache([]string{"save", "-o", tarfn, "alpine"})
@@ -255,6 +254,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from oci-archive", func() {
+ SkipIfRemote()
podmanTest.RestoreArtifact(ALPINE)
tarfn := filepath.Join(podmanTest.TempDir, "oci-alp.tar")
session := podmanTest.PodmanNoCache([]string{"save", "--format", "oci-archive", "-o", tarfn, "alpine"})
@@ -273,6 +273,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from local directory", func() {
+ SkipIfRemote()
podmanTest.RestoreArtifact(ALPINE)
dirpath := filepath.Join(podmanTest.TempDir, "alpine")
os.MkdirAll(dirpath, os.ModePerm)
@@ -297,6 +298,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from local OCI directory", func() {
+ SkipIfRemote()
podmanTest.RestoreArtifact(ALPINE)
dirpath := filepath.Join(podmanTest.TempDir, "alpine")
os.MkdirAll(dirpath, os.ModePerm)
diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go
index a5e1e0269..b1a4c9cb2 100644
--- a/test/e2e/run_device_test.go
+++ b/test/e2e/run_device_test.go
@@ -75,11 +75,17 @@ var _ = Describe("Podman run device", func() {
It("podman run device host device and container device parameter are directories", func() {
SkipIfRootless()
- SystemExec("mkdir", []string{"/dev/foodevdir"})
- SystemExec("mknod", []string{"/dev/foodevdir/null", "c", "1", "3"})
- session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/foodevdir:/dev/bar", ALPINE, "ls", "/dev/bar/null"})
+ Expect(os.MkdirAll("/dev/foodevdir", os.ModePerm)).To(BeNil())
+ defer os.RemoveAll("/dev/foodevdir")
+
+ mknod := SystemExec("mknod", []string{"/dev/foodevdir/null", "c", "1", "3"})
+ mknod.WaitWithDefaultTimeout()
+ Expect(mknod.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/foodevdir:/dev/bar", ALPINE, "stat", "-c%t:%T", "/dev/bar/null"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("1:3"))
})
It("podman run device host device with --privileged", func() {
diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go
index 3152f166b..40385e3ad 100644
--- a/test/e2e/run_passwd_test.go
+++ b/test/e2e/run_passwd_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go
index 03fb71656..8efbe0690 100644
--- a/test/e2e/run_privileged_test.go
+++ b/test/e2e/run_privileged_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 9d48f1540..4f4eb1028 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -160,10 +158,10 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container with --init", func() {
- session := podmanTest.Podman([]string{"run", "--init", ALPINE, "ls"})
+ session := podmanTest.Podman([]string{"run", "--name", "test", "--init", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- result := podmanTest.Podman([]string{"inspect", "-l"})
+ result := podmanTest.Podman([]string{"inspect", "test"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
conData := result.InspectContainerToJSON()
@@ -172,10 +170,10 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container with --init and --init-path", func() {
- session := podmanTest.Podman([]string{"run", "--init", "--init-path", "/usr/libexec/podman/catatonit", ALPINE, "ls"})
+ session := podmanTest.Podman([]string{"run", "--name", "test", "--init", "--init-path", "/usr/libexec/podman/catatonit", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- result := podmanTest.Podman([]string{"inspect", "-l"})
+ result := podmanTest.Podman([]string{"inspect", "test"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
conData := result.InspectContainerToJSON()
@@ -184,10 +182,10 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container without --init", func() {
- session := podmanTest.Podman([]string{"run", ALPINE, "ls"})
+ session := podmanTest.Podman([]string{"run", "--name", "test", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- result := podmanTest.Podman([]string{"inspect", "-l"})
+ result := podmanTest.Podman([]string{"inspect", "test"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
conData := result.InspectContainerToJSON()
@@ -259,11 +257,14 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
+ })
+ It("podman run user capabilities test with image", func() {
+ SkipIfRemote()
dockerfile := `FROM busybox
USER bin`
podmanTest.BuildImage(dockerfile, "test", "false")
- session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", "test", "grep", "CapBnd", "/proc/self/status"})
+ session := podmanTest.Podman([]string{"run", "--rm", "--user", "bin", "test", "grep", "CapBnd", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
@@ -510,6 +511,7 @@ USER bin`
})
It("podman run with secrets", func() {
+ SkipIfRemote()
containersDir := filepath.Join(podmanTest.TempDir, "containers")
err := os.MkdirAll(containersDir, 0755)
Expect(err).To(BeNil())
@@ -674,6 +676,7 @@ USER bin`
})
It("podman run with built-in volume image", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"run", "--rm", redis, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -820,16 +823,22 @@ USER mail`
})
It("podman run --rm should work", func() {
- session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "ls"})
+ session := podmanTest.Podman([]string{"run", "--name", "test", "--rm", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"wait", "test"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(ExitWithError())
numContainers := podmanTest.NumberOfContainers()
Expect(numContainers).To(Equal(0))
})
It("podman run --rm failed container should delete itself", func() {
- session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "foo"})
+ session := podmanTest.Podman([]string{"run", "--name", "test", "--rm", ALPINE, "foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(ExitWithError())
+ session = podmanTest.Podman([]string{"wait", "test"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
@@ -841,6 +850,10 @@ USER mail`
session := podmanTest.Podman([]string{"run", ALPINE, "foo"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
+ // If remote we could have a race condition
+ session = podmanTest.Podman([]string{"wait", "test"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(ExitWithError())
numContainers := podmanTest.NumberOfContainers()
Expect(numContainers).To(Equal(1))
@@ -881,7 +894,7 @@ USER mail`
})
It("podman run with restart-policy always restarts containers", func() {
-
+ SkipIfRemote()
testDir := filepath.Join(podmanTest.RunRoot, "restart-test")
err := os.MkdirAll(testDir, 0755)
Expect(err).To(BeNil())
@@ -995,14 +1008,12 @@ USER mail`
})
It("podman run should fail with nonexist authfile", func() {
- SkipIfRemote()
session := podmanTest.Podman([]string{"run", "--authfile", "/tmp/nonexist", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
It("podman run --device-cgroup-rule", func() {
- SkipIfRemote()
SkipIfRootless()
deviceCgroupRule := "c 42:* rwm"
session := podmanTest.Podman([]string{"run", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"})
@@ -1081,4 +1092,35 @@ USER mail`
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(limit))
})
+
+ It("podman run umask", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+
+ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "0002", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0002"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "0077", "--rm", fedoraMinimal, "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0077"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "22", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "9999", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask"))
+ })
})
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 92988ff87..2b806ac2b 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -450,4 +451,92 @@ VOLUME /test/`
Expect(data[0].Mounts[0].Source).To(Equal("/tmp"))
Expect(data[0].Mounts[0].Destination).To(Equal("/test"))
})
+
+ It("podman run with overlay volume flag", func() {
+ if os.Getenv("container") != "" {
+ Skip("Overlay mounts not supported when running in a container")
+ }
+ if rootless.IsRootless() {
+ if _, err := exec.LookPath("fuse_overlay"); err != nil {
+ Skip("Fuse-Overlayfs required for rootless overlay mount test")
+ }
+ }
+ mountPath := filepath.Join(podmanTest.TempDir, "secrets")
+ os.Mkdir(mountPath, 0755)
+ testFile := filepath.Join(mountPath, "test1")
+ f, err := os.Create(testFile)
+ f.Close()
+
+ // Make sure host directory gets mounted in to container as overlay
+ session := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ found, matches := session.GrepString("/run/test")
+ Expect(found).Should(BeTrue())
+ Expect(matches[0]).To(ContainSubstring("overlay"))
+
+ // Make sure host files show up in the container
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "ls", "/run/test/test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // Make sure modifications in container do not show up on host
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "touch", "/run/test/container"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ _, err = os.Stat(filepath.Join(mountPath, "container"))
+ Expect(err).To(Not(BeNil()))
+
+ // Make sure modifications in container disappear when container is stopped
+ session = podmanTest.Podman([]string{"create", "-d", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"start", "-l"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "-l", "touch", "/run/test/container"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"stop", "-l"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"start", "-l"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("overlay volume conflicts with named volume and mounts", func() {
+ mountPath := filepath.Join(podmanTest.TempDir, "secrets")
+ os.Mkdir(mountPath, 0755)
+ testFile := filepath.Join(mountPath, "test1")
+ f, err := os.Create(testFile)
+ Expect(err).To(BeNil())
+ f.Close()
+ mountSrc := filepath.Join(podmanTest.TempDir, "vol-test1")
+ err = os.MkdirAll(mountSrc, 0755)
+ Expect(err).To(BeNil())
+ mountDest := "/run/test"
+ volName := "myvol"
+
+ session := podmanTest.Podman([]string{"volume", "create", volName})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // overlay and named volume destinations conflict
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "-v", fmt.Sprintf("%s:%s", volName, mountDest), ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ // overlay and bind mount destinations conflict
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=bind,src=%s,target=%s", mountSrc, mountDest), ALPINE})
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ // overlay and tmpfs mount destinations conflict
+ session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=tmpfs,target=%s", mountDest), ALPINE})
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
})
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index aeab3ac56..65e981bbd 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -229,6 +227,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() {
+ SkipIfRemote()
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -269,6 +268,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if force secure is true", func() {
+ SkipIfRemote()
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -307,6 +307,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
+ SkipIfRemote()
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -345,6 +346,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if one registry is not listed as insecure", func() {
+ SkipIfRemote()
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index 3d05b44fe..76d29d22c 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -78,7 +78,8 @@ function check_help() {
if ! expr "$usage" : '.*[A-Z]' >/dev/null; then
if [ "$cmd" != "help" ]; then
dprint "$command_string invalid-arg"
- run_podman 125 "$@" $cmd invalid-arg
+ run_podman '?' "$@" $cmd invalid-arg
+ is "$status" 125 "'$command_string invalid-arg' - exit status"
is "$output" "Error: .* takes no arguments" \
"'$command_string' with extra (invalid) arguments"
fi
@@ -104,7 +105,8 @@ function check_help() {
# The </dev/null protects us from 'podman login' which will
# try to read username/password from stdin.
dprint "$command_string (without required args)"
- run_podman 125 "$@" $cmd </dev/null
+ run_podman '?' "$@" $cmd </dev/null
+ is "$status" 125 "'$command_string' with no arguments - exit status"
is "$output" "Error:.* \(require\|specif\|must\|provide\|need\|choose\|accepts\)" \
"'$command_string' without required arg"
@@ -126,7 +128,8 @@ function check_help() {
local rhs=$(sed -e 's/^[^A-Z]\+[A-Z]/X/' -e 's/ | /-or-/g' <<<"$usage")
local n_args=$(wc -w <<<"$rhs")
- run_podman 125 "$@" $cmd $(seq --format='x%g' 0 $n_args)
+ run_podman '?' "$@" $cmd $(seq --format='x%g' 0 $n_args)
+ is "$status" 125 "'$command_string' with >$n_args arguments - exit status"
is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \
"'$command_string' with >$n_args arguments"
@@ -140,13 +143,17 @@ function check_help() {
# Any command that takes subcommands, must throw error if called
# without one.
dprint "podman $@"
- run_podman 125 "$@"
- is "$output" "Error: missing command .*$@ COMMAND"
+ run_podman '?' "$@"
+ is "$status" 125 "'podman $*' without any subcommand - exit status"
+ is "$output" "Error: missing command .*$@ COMMAND" \
+ "'podman $*' without any subcommand - expected error message"
# Assume that 'NoSuchCommand' is not a command
dprint "podman $@ NoSuchCommand"
- run_podman 125 "$@" NoSuchCommand
- is "$output" "Error: unrecognized command .*$@ NoSuchCommand"
+ run_podman '?' "$@" NoSuchCommand
+ is "$status" 125 "'podman $* NoSuchCommand' - exit status"
+ is "$output" "Error: unrecognized command .*$@ NoSuchCommand" \
+ "'podman $* NoSuchCommand' - expected error message"
# This can happen if the output of --help changes, such as between
# the old command parser and cobra.
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 4239ef876..abca91739 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -404,7 +404,13 @@ function find_exec_pid_files() {
#
# This obviously screws us up when we look at output results.
#
-# This function removes the warning from $output and $lines
+# This function removes the warning from $output and $lines. We don't
+# do a full string match because there's another variant of that message:
+#
+# WARNING: Creating device "/dev/null" with same type, major and minor as existing "/dev/foodevdir/null".
+#
+# (We should never again see that precise error ever again, but we could
+# see variants of it).
#
function remove_same_dev_warning() {
# No input arguments. We operate in-place on $output and $lines
@@ -412,7 +418,7 @@ function remove_same_dev_warning() {
local i=0
local -a new_lines=()
while [[ $i -lt ${#lines[@]} ]]; do
- if expr "${lines[$i]}" : 'WARNING: .* same type, major.* multiple' >/dev/null; then
+ if expr "${lines[$i]}" : 'WARNING: .* same type, major' >/dev/null; then
:
else
new_lines+=("${lines[$i]}")
diff --git a/troubleshooting.md b/troubleshooting.md
index 8175073cd..4c452404c 100644
--- a/troubleshooting.md
+++ b/troubleshooting.md
@@ -102,41 +102,7 @@ communicate with a registry and not use tls verification.
* I.e. `podman push --tls-verify=false alpine docker://localhost:5000/myalpine:latest`
---
-### 5) Rootless: could not get runtime - database configuration mismatch
-
-In Podman release 0.11.1, a default path for rootless containers was changed,
-potentially causing rootless Podman to be unable to function. The new default
-path is not a problem for new installations, but existing installations will
-need to work around it with the following fix.
-
-#### Symptom
-
-```console
-$ podman info
-could not get runtime: database run root /run/user/1000/run does not match our run root /run/user/1000: database configuration mismatch
-```
-
-#### Solution
-
-This problem has been fixed in Podman release 0.12.1 and it is recommended
-to upgrade to that version. If that is not possible use the following procedure.
-
-To work around the new default path, we can manually set the path Podman is
-expecting in a configuration file.
-
-First, we need to make a new local configuration file for rootless Podman.
-* `mkdir -p ~/.config/containers`
-* `cp /usr/share/containers/libpod.conf ~/.config/containers`
-
-Next, edit the new local configuration file
-(`~/.config/containers/libpod.conf`) with your favorite editor. Comment out the
-line starting with `cgroup_manager` by adding a `#` character at the beginning
-of the line, and change the path in the line starting with `tmp_dir` to point to
-the first path in the error message Podman gave (in this case,
-`/run/user/1000/tmp`).
-
----
-### 6) rootless containers cannot ping hosts
+### 5) rootless containers cannot ping hosts
When using the ping command from a non-root container, the command may
fail because of a lack of privileges.
@@ -164,7 +130,7 @@ To make the change persistent, you'll need to add a file in
`/etc/sysctl.d` that contains `net.ipv4.ping_group_range=0 $MAX_UID`.
---
-### 7) Build hangs when the Dockerfile contains the useradd command
+### 6) Build hangs when the Dockerfile contains the useradd command
When the Dockerfile contains a command like `RUN useradd -u 99999000 -g users newuser` the build can hang.
@@ -176,7 +142,7 @@ If you are using a useradd command within a Dockerfile with a large UID/GID, it
If the entry in the Dockerfile looked like: RUN useradd -u 99999000 -g users newuser then add the `--no-log-init` parameter to change it to: `RUN useradd --no-log-init -u 99999000 -g users newuser`. This option tells useradd to stop creating the lastlog file.
-### 8) Permission denied when running Podman commands
+### 7) Permission denied when running Podman commands
When rootless Podman attempts to execute a container on a non exec home directory a permission error will be raised.
@@ -206,7 +172,7 @@ cat ~/.config/containers/storage.conf
mount_program = "/bin/fuse-overlayfs"
```
-### 9) Permission denied when running systemd within a Podman container
+### 8) Permission denied when running systemd within a Podman container
When running systemd as PID 1 inside of a container on an SELinux
separated machine, it needs to write to the cgroup file system.
@@ -231,7 +197,7 @@ Only do this on systems running older versions of Podman.
`setsebool -P container_manage_cgroup true`
-### 10) Newuidmap missing when running rootless Podman commands
+### 9) Newuidmap missing when running rootless Podman commands
Rootless Podman requires the newuidmap and newgidmap programs to be installed.
@@ -249,7 +215,7 @@ cannot find newuidmap: exec: "newuidmap": executable file not found in $PATH
Install a version of shadow-utils that includes these executables. Note that for RHEL and CentOS 7, at least the 7.7 release must be installed for support to be available.
-### 11) rootless setup user: invalid argument
+### 10) rootless setup user: invalid argument
Rootless Podman requires the user running it to have a range of UIDs listed in /etc/subuid and /etc/subgid.
@@ -298,7 +264,7 @@ grep johndoe /etc/subuid /etc/subgid
/etc/subgid:johndoe:200000:1001
```
-### 12) Changing the location of the Graphroot leads to permission denied
+### 11) Changing the location of the Graphroot leads to permission denied
When I change the graphroot storage location in storage.conf, the next time I
run Podman I get an error like:
@@ -337,7 +303,7 @@ tells SELinux to apply the labels to the actual content.
Now all new content created in these directories will automatically be created
with the correct label.
-### 13) Anonymous image pull fails with 'invalid username/password'
+### 12) Anonymous image pull fails with 'invalid username/password'
Pulling an anonymous image that doesn't require authentication can result in an
`invalid username/password` error.
@@ -363,7 +329,7 @@ are established locally and then the password is updated later in the container
Depending upon which container tool was used to establish the credentials, use `podman logout`
or `docker logout` to remove the credentials from the authentication file.
-### 14) Running Podman inside a container causes container crashes and inconsistent states
+### 13) Running Podman inside a container causes container crashes and inconsistent states
Running Podman in a container and forwarding some, but not all, of the required host directories can cause inconsistent container behavior.
@@ -381,7 +347,7 @@ This can cause Podman to reset container states and lose track of running contai
For running containers on the host from inside a container, we also recommend the [Podman remote client](remote_client.md), which only requires a single socket to be mounted into the container.
-### 15) Rootless 'podman build' fails EPERM on NFS:
+### 14) Rootless 'podman build' fails EPERM on NFS:
NFS enforces file creation on different UIDs on the server side and does not understand user namespace, which rootless Podman requires.
When a container root process like YUM attempts to create a file owned by a different UID, NFS Server denies the creation.
@@ -398,10 +364,10 @@ error creating build container: Error committing the finished image: error addin
Choose one of the following:
* Setup containers/storage in a different directory, not on an NFS share.
* Create a directory on a local file system.
- * Edit `~/.config/containers/libpod.conf` and point the `volume_path` option to that local directory.
+ * Edit `~/.config/containers/containers.conf` and point the `volume_path` option to that local directory. (Copy /usr/share/containers/containers.conf if ~/.config/containers/containers.conf does not exist)
* Otherwise just run Podman as root, via `sudo podman`
-### 16) Rootless 'podman build' fails when using OverlayFS:
+### 15) Rootless 'podman build' fails when using OverlayFS:
The Overlay file system (OverlayFS) requires the ability to call the `mknod` command when creating whiteout files
when extracting an image. However, a rootless user does not have the privileges to use `mknod` in this capacity.
@@ -431,7 +397,7 @@ Choose one of the following:
* Install the fuse-overlayfs package for your Linux Distribution.
* Add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in your `~/.config/containers/storage.conf` file.
-### 17) RHEL 7 and CentOS 7 based `init` images don't work with cgroup v2
+### 16) RHEL 7 and CentOS 7 based `init` images don't work with cgroup v2
The systemd version shipped in RHEL 7 and CentOS 7 doesn't have support for cgroup v2. Support for cgroup V2 requires version 230 of systemd or newer, which
was never shipped or supported on RHEL 7 or CentOS 7.
@@ -459,7 +425,7 @@ On Fedora you can do:
* update the image to use an updated version of systemd.
-### 18) rootless containers exit once the user session exits
+### 17) rootless containers exit once the user session exits
You need to set lingering mode through loginctl to prevent user processes to be killed once
the user session completed.
@@ -477,7 +443,7 @@ or as root if your user has not enough privileges.
* sudo loginctl enable-linger $UID
-### 19) `podman run` fails with "bpf create: permission denied error"
+### 18) `podman run` fails with "bpf create: permission denied error"
The Kernel Lockdown patches deny eBPF programs when Secure Boot is enabled in the BIOS. [Matthew Garrett's post](https://mjg59.dreamwidth.org/50577.html) describes the relationship between Lockdown and Secure Boot and [Jan-Philip Gehrcke's](https://gehrcke.de/2019/09/running-an-ebpf-program-may-require-lifting-the-kernel-lockdown/) connects this with eBPF. [RH bug 1768125](https://bugzilla.redhat.com/show_bug.cgi?id=1768125) contains some additional details.
@@ -518,7 +484,7 @@ $ podman unshare cat /proc/self/uid_map
Reference [subuid](http://man7.org/linux/man-pages/man5/subuid.5.html) and [subgid](http://man7.org/linux/man-pages/man5/subgid.5.html) man pages for more detail.
-### 21) Passed-in device can't be accessed in rootless container
+### 20) Passed-in device can't be accessed in rootless container
As a non-root user you have group access rights to a device that you want to
pass into a rootless container with `--device=...`.
@@ -534,7 +500,7 @@ the non-root user has. If you use the `crun` runtime, 0.10.4 or newer,
then you can enable a workaround by adding `--annotation io.crun.keep_original_groups=1`
to the `podman` command line.
-### 22) A rootless container running in detached mode is closed at logout
+### 21) A rootless container running in detached mode is closed at logout
When running a container with a command like `podman run --detach httpd` as
a rootless user, the container is closed upon logout and is not kept running.
@@ -554,7 +520,7 @@ To later revert the linger functionality, use `loginctl disable-linger`.
LOGINCTL(1), SYSTEMD(1)
-### 23) Containers default detach keys conflict with shell history navigation
+### 22) Containers default detach keys conflict with shell history navigation
Podman defaults to `ctrl-p,ctrl-q` to detach from a running containers. The
bash and zsh shells default to ctrl-p for the displaying of the previous
diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go
index b3278bb28..80c478505 100644
--- a/vendor/github.com/containers/common/pkg/config/config.go
+++ b/vendor/github.com/containers/common/pkg/config/config.go
@@ -622,9 +622,17 @@ func (c *ContainersConfig) Validate() error {
// execution checks. It returns an `error` on validation failure, otherwise
// `nil`.
func (c *NetworkConfig) Validate() error {
- if c.NetworkConfigDir != _cniConfigDir {
- err := isDirectory(c.NetworkConfigDir)
+ expectedConfigDir := _cniConfigDir
+ if unshare.IsRootless() {
+ home, err := unshare.HomeDir()
if err != nil {
+ return err
+ }
+ expectedConfigDir = filepath.Join(home, _cniConfigDirRootless)
+ }
+ if c.NetworkConfigDir != expectedConfigDir {
+ err := isDirectory(c.NetworkConfigDir)
+ if err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "invalid network_config_dir: %s", c.NetworkConfigDir)
}
}
@@ -874,6 +882,10 @@ func Default() (*Config, error) {
if config != nil || configErr != nil {
return config, configErr
}
+ return defConfig()
+}
+
+func defConfig() (*Config, error) {
config, configErr = NewConfig("")
return config, configErr
}
@@ -969,13 +981,11 @@ func (c *Config) Write() error {
// the cached containers.conf files.
func Reload() (*Config, error) {
configMutex.Lock()
- configErr = nil
- config = nil
- configMutex.Unlock()
- return Default()
+ defer configMutex.Unlock()
+ return defConfig()
}
-func (c *Config) ActiveDestination() (string, string, error){
+func (c *Config) ActiveDestination() (string, string, error) {
if uri, found := os.LookupEnv("CONTAINER_HOST"); found {
var ident string
if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found {
diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go
index 12cf1b421..57b703f53 100644
--- a/vendor/github.com/containers/common/pkg/config/default.go
+++ b/vendor/github.com/containers/common/pkg/config/default.go
@@ -92,8 +92,10 @@ const (
// InstallPrefix is the prefix where podman will be installed.
// It can be overridden at build time.
_installPrefix = "/usr"
- // _cniConfigDir is the directory where cni plugins are found
+ // _cniConfigDir is the directory where cni configuration is found
_cniConfigDir = "/etc/cni/net.d/"
+ // _cniConfigDirRootless is the directory where cni plugins are found
+ _cniConfigDirRootless = ".config/cni/net.d/"
// CgroupfsCgroupsManager represents cgroupfs native cgroup manager
CgroupfsCgroupsManager = "cgroupfs"
// DefaultApparmorProfile specifies the default apparmor profile for the container.
@@ -138,6 +140,8 @@ func DefaultConfig() (*Config, error) {
netns := "bridge"
+ cniConfig := _cniConfigDir
+
defaultEngineConfig.SignaturePolicyPath = DefaultSignaturePolicyPath
if unshare.IsRootless() {
home, err := unshare.HomeDir()
@@ -152,6 +156,7 @@ func DefaultConfig() (*Config, error) {
}
}
netns = "slirp4netns"
+ cniConfig = filepath.Join(home, _cniConfigDirRootless)
}
cgroupNS := "host"
@@ -198,7 +203,7 @@ func DefaultConfig() (*Config, error) {
},
Network: NetworkConfig{
DefaultNetwork: "podman",
- NetworkConfigDir: _cniConfigDir,
+ NetworkConfigDir: cniConfig,
CNIPluginDirs: cniBinDir,
},
Engine: *defaultEngineConfig,
diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go
index b75bbe971..2913fe974 100644
--- a/vendor/github.com/containers/common/version/version.go
+++ b/vendor/github.com/containers/common/version/version.go
@@ -1,4 +1,4 @@
package version
// Version is the version of the build.
-const Version = "0.16.0"
+const Version = "0.17.0"
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index 284497740..0369d0b1e 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.21.1
+1.21.2
diff --git a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go
index b7a95bfc2..cba3d05ea 100644
--- a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go
+++ b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go
@@ -1749,7 +1749,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
// - Managed by container storage
// - The target of this device is at major <maj> and minor <min>
// - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
- devices.devicePrefix = fmt.Sprintf("container-%d:%d-%d", major(st.Dev), minor(st.Dev), st.Ino)
+ devices.devicePrefix = fmt.Sprintf("container-%d:%d-%d", major(uint64(st.Dev)), minor(uint64(st.Dev)), st.Ino)
logrus.Debugf("devmapper: Generated prefix: %s", devices.devicePrefix)
// Check for the existence of the thin-pool device
diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go
index 863465456..78744e0f3 100644
--- a/vendor/github.com/containers/storage/pkg/archive/archive.go
+++ b/vendor/github.com/containers/storage/pkg/archive/archive.go
@@ -393,13 +393,15 @@ func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
// ReadSecurityXattrToTarHeader reads security.capability, security,image
// xattrs from filesystem to a tar header
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
+ if hdr.Xattrs == nil {
+ hdr.Xattrs = make(map[string]string)
+ }
for _, xattr := range []string{"security.capability", "security.ima"} {
capability, err := system.Lgetxattr(path, xattr)
if err != nil && err != system.EOPNOTSUPP && err != system.ErrNotSupportedPlatform {
return errors.Wrapf(err, "failed to read %q attribute from %q", xattr, path)
}
if capability != nil {
- hdr.Xattrs = make(map[string]string)
hdr.Xattrs[xattr] = string(capability)
}
}
diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare.go b/vendor/github.com/containers/storage/pkg/unshare/unshare.go
index 1eff82e8e..a08fb674d 100644
--- a/vendor/github.com/containers/storage/pkg/unshare/unshare.go
+++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.go
@@ -4,19 +4,30 @@ import (
"fmt"
"os"
"os/user"
+ "sync"
"github.com/pkg/errors"
)
+var (
+ homeDirOnce sync.Once
+ homeDirErr error
+ homeDir string
+)
+
// HomeDir returns the home directory for the current user.
func HomeDir() (string, error) {
- home := os.Getenv("HOME")
- if home == "" {
- usr, err := user.LookupId(fmt.Sprintf("%d", GetRootlessUID()))
- if err != nil {
- return "", errors.Wrapf(err, "unable to resolve HOME directory")
+ homeDirOnce.Do(func() {
+ home := os.Getenv("HOME")
+ if home == "" {
+ usr, err := user.LookupId(fmt.Sprintf("%d", GetRootlessUID()))
+ if err != nil {
+ homeDir, homeDirErr = "", errors.Wrapf(err, "unable to resolve HOME directory")
+ return
+ }
+ homeDir, homeDirErr = usr.HomeDir, nil
}
- home = usr.HomeDir
- }
- return home, nil
+ homeDir, homeDirErr = home, nil
+ })
+ return homeDir, homeDirErr
}
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
index 6d3268902..c757c20e0 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
@@ -29,6 +29,9 @@ var (
type Generator struct {
Config *rspec.Spec
HostSpecific bool
+ // This is used to keep a cache of the ENVs added to improve
+ // performance when adding a huge number of ENV variables
+ envMap map[string]int
}
// ExportOptions have toggles for exporting only certain parts of the specification
@@ -236,7 +239,12 @@ func New(os string) (generator Generator, err error) {
}
}
- return Generator{Config: &config}, nil
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
+ return Generator{Config: &config, envMap: envCache}, nil
}
// NewFromSpec creates a configuration Generator from a given
@@ -246,8 +254,14 @@ func New(os string) (generator Generator, err error) {
//
// generator := Generator{Config: config}
func NewFromSpec(config *rspec.Spec) Generator {
+ envCache := map[string]int{}
+ if config != nil && config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: config,
+ envMap: envCache,
}
}
@@ -273,11 +287,27 @@ func NewFromTemplate(r io.Reader) (Generator, error) {
if err := json.NewDecoder(r).Decode(&config); err != nil {
return Generator{}, err
}
+
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: &config,
+ envMap: envCache,
}, nil
}
+// createEnvCacheMap creates a hash map with the ENV variables given by the config
+func createEnvCacheMap(env []string) map[string]int {
+ envMap := make(map[string]int, len(env))
+ for i, val := range env {
+ envMap[val] = i
+ }
+ return envMap
+}
+
// SetSpec sets the configuration in the Generator g.
//
// Deprecated: Replace with:
@@ -414,6 +444,12 @@ func (g *Generator) SetProcessUsername(username string) {
g.Config.Process.User.Username = username
}
+// SetProcessUmask sets g.Config.Process.User.Umask.
+func (g *Generator) SetProcessUmask(umask uint32) {
+ g.initConfigProcess()
+ g.Config.Process.User.Umask = umask
+}
+
// SetProcessGID sets g.Config.Process.User.GID.
func (g *Generator) SetProcessGID(gid uint32) {
g.initConfigProcess()
@@ -456,21 +492,44 @@ func (g *Generator) ClearProcessEnv() {
return
}
g.Config.Process.Env = []string{}
+ // Clear out the env cache map as well
+ g.envMap = map[string]int{}
}
// AddProcessEnv adds name=value into g.Config.Process.Env, or replaces an
// existing entry with the given name.
func (g *Generator) AddProcessEnv(name, value string) {
+ if name == "" {
+ return
+ }
+
g.initConfigProcess()
+ g.addEnv(fmt.Sprintf("%s=%s", name, value), name)
+}
- env := fmt.Sprintf("%s=%s", name, value)
- for idx := range g.Config.Process.Env {
- if strings.HasPrefix(g.Config.Process.Env[idx], name+"=") {
- g.Config.Process.Env[idx] = env
- return
- }
+// AddMultipleProcessEnv adds multiple name=value into g.Config.Process.Env, or replaces
+// existing entries with the given name.
+func (g *Generator) AddMultipleProcessEnv(envs []string) {
+ g.initConfigProcess()
+
+ for _, val := range envs {
+ split := strings.SplitN(val, "=", 2)
+ g.addEnv(val, split[0])
+ }
+}
+
+// addEnv looks through adds ENV to the Process and checks envMap for
+// any duplicates
+// This is called by both AddMultipleProcessEnv and AddProcessEnv
+func (g *Generator) addEnv(env, key string) {
+ if idx, ok := g.envMap[key]; ok {
+ // The ENV exists in the cache, so change its value in g.Config.Process.Env
+ g.Config.Process.Env[idx] = env
+ } else {
+ // else the env doesn't exist, so add it and add it's index to g.envMap
+ g.Config.Process.Env = append(g.Config.Process.Env, env)
+ g.envMap[key] = len(g.Config.Process.Env) - 1
}
- g.Config.Process.Env = append(g.Config.Process.Env, env)
}
// AddProcessRlimits adds rlimit into g.Config.Process.Rlimits.
@@ -1443,7 +1502,7 @@ func (g *Generator) AddDevice(device rspec.LinuxDevice) {
return
}
if dev.Type == device.Type && dev.Major == device.Major && dev.Minor == device.Minor {
- fmt.Fprintln(os.Stderr, "WARNING: The same type, major and minor should not be used for multiple devices.")
+ fmt.Fprintf(os.Stderr, "WARNING: Creating device %q with same type, major and minor as existing %q.\n", device.Path, dev.Path)
}
}
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
index 5fee5a3b2..8a8dc3970 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
@@ -566,6 +566,20 @@ func DefaultProfile(rs *specs.Spec) *rspec.LinuxSeccomp {
},
}...)
/* Flags parameter of the clone syscall is the 2nd on s390 */
+ syscalls = append(syscalls, []rspec.LinuxSyscall{
+ {
+ Names: []string{"clone"},
+ Action: rspec.ActAllow,
+ Args: []rspec.LinuxSeccompArg{
+ {
+ Index: 1,
+ Value: 2080505856,
+ ValueTwo: 0,
+ Op: rspec.OpMaskedEqual,
+ },
+ },
+ },
+ }...)
}
return &rspec.LinuxSeccomp{
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 913cb71eb..1d97f810a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -84,7 +84,7 @@ github.com/containers/buildah/pkg/secrets
github.com/containers/buildah/pkg/supplemented
github.com/containers/buildah/pkg/umask
github.com/containers/buildah/util
-# github.com/containers/common v0.16.0
+# github.com/containers/common v0.17.0
github.com/containers/common/pkg/apparmor
github.com/containers/common/pkg/auth
github.com/containers/common/pkg/capabilities
@@ -155,7 +155,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.21.1
+# github.com/containers/storage v1.21.2
github.com/containers/storage
github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs
@@ -421,7 +421,7 @@ github.com/opencontainers/runc/libcontainer/user
github.com/opencontainers/runc/libcontainer/utils
# github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2
github.com/opencontainers/runtime-spec/specs-go
-# github.com/opencontainers/runtime-tools v0.9.0
+# github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa
github.com/opencontainers/runtime-tools/error
github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/generate