aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml13
-rw-r--r--.gitignore3
-rw-r--r--Makefile21
-rw-r--r--cmd/podman/import.go8
-rw-r--r--cmd/podman/shared/create.go15
-rw-r--r--cmd/podman/shared/parse/parse.go10
-rw-r--r--commands.md4
-rwxr-xr-xcontrib/cirrus/build_vm_images.sh15
-rwxr-xr-xcontrib/cirrus/check_image.sh32
-rw-r--r--contrib/cirrus/lib.sh13
-rwxr-xr-xcontrib/cirrus/setup_environment.sh9
-rw-r--r--docs/links/podman-container-cp.11
-rw-r--r--docs/links/podman-container-init.11
-rw-r--r--docs/links/podman-help.11
-rw-r--r--docs/podman-create.1.md2
-rw-r--r--docs/podman-remote.1.md141
-rwxr-xr-xdocs/podman-remote.sh11
-rw-r--r--docs/podman-run.1.md5
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/container_internal_linux.go5
-rw-r--r--libpod/image/image.go5
-rw-r--r--libpod/options.go1
-rw-r--r--pkg/adapter/pods.go22
-rw-r--r--pkg/hooks/hooks.go24
-rw-r--r--pkg/hooks/monitor.go49
-rw-r--r--pkg/hooks/monitor_test.go25
-rw-r--r--pkg/hooks/read.go11
-rw-r--r--pkg/namespaces/namespaces.go29
-rw-r--r--pkg/spec/createconfig.go24
-rw-r--r--pkg/spec/spec.go4
-rw-r--r--test/e2e/common_test.go5
-rw-r--r--test/e2e/run_userns_test.go15
32 files changed, 389 insertions, 139 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index cd6bac265..faa0a531c 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -23,7 +23,6 @@ env:
CIRRUS_SHELL: "/bin/bash"
# Save a little typing (path relative to $CIRRUS_WORKING_DIR)
SCRIPT_BASE: "./contrib/cirrus"
- CIRRUS_CLONE_DEPTH: 50
# Command to prefix output lines with timing information
# (can't do inline awk script, Cirrus-CI or YAML mangles quoting)
TIMESTAMP: "awk --file ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/timestamp.awk"
@@ -40,11 +39,6 @@ env:
#### base-images (pre-existing in GCE)
####
BUILT_IMAGE_SUFFIX: "-${CIRRUS_REPO_NAME}-${CIRRUS_BUILD_ID}"
- # Git commits to use while building dependencies into cache-images
- FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554"
- CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
- CONMON_COMMIT: "6f3572558b97bc60dd8f8c7f0807748e6ce2c440"
- CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a"
# Special image w/ nested-libvirt + tools for creating new cache and base images
IMAGE_BUILDER_CACHE_IMAGE_NAME: "image-builder-image-1541772081"
@@ -211,7 +205,7 @@ build_each_commit_task:
build_each_commit_script:
# set -x by default, no need to spew contents of lib.sh
- 'source $SCRIPT_BASE/lib.sh &> /dev/null'
- - 'git fetch --depth $CIRRUS_CLONE_DEPTH origin $DEST_BRANCH |& ${TIMESTAMP}'
+ - 'git fetch --depth 50 origin $DEST_BRANCH |& ${TIMESTAMP}'
- 'make build-all-new-commits GIT_BASE_BRANCH=origin/$DEST_BRANCH |& ${TIMESTAMP}'
on_failure:
@@ -274,7 +268,6 @@ meta_task:
GCPJSON: ENCRYPTED[950d9c64ad78f7b1f0c7e499b42dc058d2b23aa67e38b315e68f557f2aba0bf83068d4734f7b1e1bdd22deabe99629df]
GCPNAME: ENCRYPTED[b05d469a0dba8cb479cb00cc7c1f6747c91d17622fba260a986b976aa6c817d4077eacffd4613d6d5f23afc4084fab1d]
GCPPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f]
- CIRRUS_CLONE_DEPTH: 1 # source not used
timeout_in: 10m
@@ -540,6 +533,9 @@ success_task:
release_task:
+ # Never do this when building images
+ only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*'
+
# TODO: Uncomment both to not affect pass/fail status of entire job?
# allow_failures: $CI == "true"
# skip_notifications: $CI == "true"
@@ -552,7 +548,6 @@ release_task:
timeout_in: 30m
env:
- CIRRUS_CLONE_DEPTH: 1 # source is not used, only Makefile
GCPJSON: ENCRYPTED[789d8f7e9a5972ce350fd8e60f1032ccbf4a35c3938b604774b711aad280e12c21faf10e25af1e0ba33597ffb9e39e46]
GCPNAME: ENCRYPTED[417d50488a4bd197bcc925ba6574de5823b97e68db1a17e3a5fde4bcf26576987345e75f8d9ea1c15a156b4612c072a1]
GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f]
diff --git a/.gitignore b/.gitignore
index b26674172..4f1100d8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,10 @@
/.artifacts/
/_output/
+/brew
/conmon/conmon.o
/docs/*.[158]
/docs/*.[158].gz
+/docs/remote
*.o
*.orig
/pause/pause.o
@@ -19,3 +21,4 @@ __pycache__
.gopathok
test/e2e/e2e.coverprofile
/podman*zip
+podman*.tar.gz
diff --git a/Makefile b/Makefile
index 2ebcb6605..476e4f054 100644
--- a/Makefile
+++ b/Makefile
@@ -75,11 +75,13 @@ LIBSECCOMP_COMMIT := release-2.3
GINKGOTIMEOUT ?= -timeout=90m
RELEASE_VERSION ?= $(shell git fetch --tags && git describe HEAD 2> /dev/null)
+RELEASE_NUMBER ?= $(shell echo $(RELEASE_VERSION) | sed 's/-.*//')
RELEASE_DIST ?= $(shell ( source /etc/os-release; echo $$ID ))
RELEASE_DIST_VER ?= $(shell ( source /etc/os-release; echo $$VERSION_ID | cut -d '.' -f 1))
RELEASE_ARCH ?= $(shell go env GOARCH 2> /dev/null)
RELEASE_BASENAME := $(shell basename $(PROJECT))
+
# If GOPATH not specified, use one in the local directory
ifeq ($(GOPATH),)
export GOPATH := $(CURDIR)/_output
@@ -143,7 +145,6 @@ gofmt: ## Verify the source code gofmt
find . -name '*.go' ! -path './vendor/*' -exec gofmt -s -w {} \+
git diff --exit-code
-
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
@@ -175,8 +176,10 @@ clean: ## Clean artifacts
.gopathok \
_output \
podman*.zip \
+ podman*.tar.gz \
bin \
build \
+ docs/remote \
test/checkseccomp/checkseccomp \
test/goecho/goecho \
test/testdata/redis-image \
@@ -292,6 +295,22 @@ $(MANPAGES): %: %.md .gopathok
docs: $(MANPAGES) ## Generate documentation
+install-podman-remote-docs: docs
+ @(cd docs; ./podman-remote.sh ./remote)
+
+
+brew-pkg: install-podman-remote-docs podman-remote-darwin
+ @mkdir -p ./brew
+ @cp ./bin/podman-remote-darwin ./brew/podman
+ @cp -r ./docs/remote ./brew/docs/
+ @cp docs/podman-remote.1 ./brew/docs/podman.1
+ @sed -i 's/podman\\*-remote/podman/g' ./brew/docs/podman.1
+ @sed -i 's/Podman\\*-remote/Podman\ for\ Mac/g' ./brew/docs/podman.1
+ @sed -i 's/podman\.conf/podman\-remote\.conf/g' ./brew/docs/podman.1
+ @sed -i 's/A\ remote\ CLI\ for\ Podman\:\ //g' ./brew/docs/podman.1
+ tar -czvf podman-${RELEASE_NUMBER}.tar.gz ./brew
+ @rm -rf ./brew
+
docker-docs: docs
(cd docs; ./dckrman.sh *.1)
diff --git a/cmd/podman/import.go b/cmd/podman/import.go
index 70ea167cb..d49792f27 100644
--- a/cmd/podman/import.go
+++ b/cmd/podman/import.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/pkg/adapter"
+ multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -69,8 +70,11 @@ func importCmd(c *cliconfig.ImportValues) error {
return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]")
}
- if err := parse.ValidateFileName(source); err != nil {
- return err
+ errFileName := parse.ValidateFileName(source)
+ errURL := parse.ValidURL(source)
+
+ if errFileName != nil && errURL != nil {
+ return multierror.Append(errFileName, errURL)
}
quiet := c.Quiet
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 815e2d304..9578eb17d 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -282,13 +282,26 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[
if err != nil {
return namespaces, err
}
+ hasUserns := false
+ if podInfraID != "" {
+ podCtr, err := runtime.GetContainer(podInfraID)
+ if err != nil {
+ return namespaces, err
+ }
+ mappings, err := podCtr.IDMappings()
+ if err != nil {
+ return namespaces, err
+ }
+ hasUserns = len(mappings.UIDMap) > 0
+ }
+
if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) {
namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID)
}
if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) {
namespaces["net"] = fmt.Sprintf("container:%s", podInfraID)
}
- if (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) {
+ if hasUserns && (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) {
namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
}
if (namespaces["ipc"] == cc.Pod) || (!c.IsSet("ipc") && pod.SharesIPC()) {
diff --git a/cmd/podman/shared/parse/parse.go b/cmd/podman/shared/parse/parse.go
index a77002235..9fbc92fc3 100644
--- a/cmd/podman/shared/parse/parse.go
+++ b/cmd/podman/shared/parse/parse.go
@@ -7,6 +7,7 @@ import (
"bufio"
"fmt"
"net"
+ "net/url"
"os"
"regexp"
"strings"
@@ -162,3 +163,12 @@ func ValidateFileName(filename string) error {
}
return nil
}
+
+// ValidURL checks a string urlStr is a url or not
+func ValidURL(urlStr string) error {
+ _, err := url.ParseRequestURI(urlStr)
+ if err != nil {
+ return errors.Wrapf(err, "invalid url path: %q", urlStr)
+ }
+ return nil
+}
diff --git a/commands.md b/commands.md
index d3ceca9dc..1b48d7862 100644
--- a/commands.md
+++ b/commands.md
@@ -44,7 +44,7 @@
| [podman-logout(1)](/docs/podman-logout.1.md) | Logout of a container registry |
| [podman-logs(1)](/docs/podman-logs.1.md) | Display the logs of a container |
| [podman-mount(1)](/docs/podman-mount.1.md) | Mount a working container's root filesystem |
-| [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers | [![...](/docs/play.png)](https://asciinema.org/a/141292) |
+| [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers | [![...](/docs/play.png)](https://podman.io/asciinema/podman/pause_unpause/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_pause_unpause.sh) |
| [podman-play(1)](/docs/podman-play.1.md) | Play pods and containers based on a structured input file |
| [podman-pod(1)](/docs/podman-pod.1.md) | Simple management tool for groups of containers, called pods |
| [podman-pod-create(1)](/docs/podman-pod-create.1.md) | Create a new pod |
@@ -76,7 +76,7 @@
| [podman-tag(1)](/docs/podman-tag.1.md) | Add an additional name to a local image | [![...](/docs/play.png)](https://asciinema.org/a/133803) |
| [podman-top(1)](/docs/podman-top.1.md) | Display the running processes of a container |
| [podman-umount(1)](/docs/podman-umount.1.md) | Unmount a working container's root filesystem |
-| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers | [![...](/docs/play.png)](https://asciinema.org/a/141292) |
+| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers | [![...](/docs/play.png)](https://podman.io/asciinema/podman/pause_unpause/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_pause_unpause.sh) |
| [podman-unshare(1)](/docs/podman-unshare.1.md) | Run a command inside of a modified user namespace. |
| [podman-varlink(1)](/docs/podman-varlink.1.md) | Run the varlink backend |
| [podman-version(1)](/docs/podman-version.1.md) | Display the version information |
diff --git a/contrib/cirrus/build_vm_images.sh b/contrib/cirrus/build_vm_images.sh
index 74b10158c..dd5182c37 100755
--- a/contrib/cirrus/build_vm_images.sh
+++ b/contrib/cirrus/build_vm_images.sh
@@ -32,7 +32,7 @@ for base_image_var in $BASE_IMAGE_VARS
do
# See entrypoint.sh in contrib/imgts and contrib/imgprune
# These updates can take a while, run them in the background, check later
- gcloud compute images update "$image" \
+ gcloud compute images update \
--update-labels=last-used=$(date +%s) \
--update-labels=build-id=$CIRRUS_BUILD_ID \
--update-labels=repo-ref=$CIRRUS_CHANGE_IN_REPO \
@@ -62,17 +62,6 @@ URI="gs://packer-import${POST_MERGE_BUCKET_SUFFIX}/manifest${BUILT_IMAGE_SUFFIX}
gsutil cp packer-manifest.json "$URI"
# Ensure any background 'gcloud compute images update' processes finish
-set +e # need 'wait' exit code to avoid race
-while [[ -n "$(jobs)" ]]
-do
- wait -n
- RET=$?
- if [[ "$RET" -eq "127" ]] || \ # Avoid TOCTOU race w/ jobs + wait
- [[ "$RET" -eq "0" ]]
- then
- continue
- fi
- die $RET "Required base-image metadata update failed"
-done
+wait # CentOS has no -n option :(
echo "Finished. A JSON manifest of produced images is available at $URI"
diff --git a/contrib/cirrus/check_image.sh b/contrib/cirrus/check_image.sh
index 690a38119..22ed1ddc4 100755
--- a/contrib/cirrus/check_image.sh
+++ b/contrib/cirrus/check_image.sh
@@ -4,26 +4,26 @@ set -eo pipefail
source $(dirname $0)/lib.sh
-RET=0
+NFAILS=0
echo "Validating VM image"
MIN_SLASH_GIGS=50
read SLASH_DEVICE SLASH_FSTYPE SLASH_SIZE JUNK <<<$(findmnt --df --first-only --noheadings / | cut -d '.' -f 1)
SLASH_SIZE_GIGS=$(echo "$SLASH_SIZE" | sed -r -e 's/G|g//')
-item_test "Minimum available disk space" $SLASH_SIZE_GIGS -gt $MIN_SLASH_GIGS || let "RET+=1"
+item_test "Minimum available disk space" $SLASH_SIZE_GIGS -gt $MIN_SLASH_GIGS || let "NFAILS+=1"
MIN_MEM_MB=2000
read JUNK TOTAL USED MEM_FREE JUNK <<<$(free -tm | tail -1)
-item_test 'Minimum available memory' $MEM_FREE -ge $MIN_MEM_MB || let "RET+=1"
+item_test 'Minimum available memory' $MEM_FREE -ge $MIN_MEM_MB || let "NFAILS+=1"
# We're testing a custom-built podman; make sure there isn't a distro-provided
# binary anywhere; that could potentially taint our results.
-item_test "remove_packaged_podman_files() did it's job" -z "$(type -P podman)" || let "RET+=1"
+item_test "remove_packaged_podman_files() did it's job" -z "$(type -P podman)" || let "NFAILS+=1"
MIN_ZIP_VER='3.0'
VER_RE='.+([[:digit:]]+\.[[:digit:]]+).+'
ACTUAL_VER=$(zip --version 2>&1 | egrep -m 1 "Zip$VER_RE" | sed -r -e "s/$VER_RE/\\1/")
-item_test "minimum zip version" "$MIN_ZIP_VER" = $(echo -e "$MIN_ZIP_VER\n$ACTUAL_VER" | sort -V | head -1) || let "RET+=1"
+item_test "minimum zip version" "$MIN_ZIP_VER" = $(echo -e "$MIN_ZIP_VER\n$ACTUAL_VER" | sort -V | head -1) || let "NFAILS+=1"
for REQ_UNIT in google-accounts-daemon.service \
google-clock-skew-daemon.service \
@@ -33,13 +33,21 @@ for REQ_UNIT in google-accounts-daemon.service \
google-startup-scripts.service
do
item_test "required $REQ_UNIT enabled" \
- "$(systemctl list-unit-files --no-legend $REQ_UNIT)" = "$REQ_UNIT enabled" || let "RET+=1"
+ "$(systemctl list-unit-files --no-legend $REQ_UNIT)" = "$REQ_UNIT enabled" || let "NFAILS+=1"
done
-# Exits zero if any unit matching pattern is running
-UNIT_STATUS=$(systemctl is-active $EVIL_UNITS; echo $?)
-item_test "No interfering background units are active:" \
- "$UNIT_STATUS" -ne "0" || let "RET+=1"
+for evil_unit in $EVIL_UNITS
+do
+ # Exits zero if any unit matching pattern is running
+ unit_status=$(systemctl is-active $evil_unit &> /dev/null; echo $?)
+ item_test "No $evil_unit unit is present or active:" "$unit_status" -ne "0" || let "NFAILS+=1"
+done
+
+if [[ "$OS_RELEASE_ID" == "ubuntu" ]] && [[ -x "/usr/lib/cri-o-runc/sbin/runc" ]]
+then
+ SAMESAME=$(diff --brief /usr/lib/cri-o-runc/sbin/runc /usr/bin/runc &> /dev/null; echo $?)
+ item_test "On ubuntu /usr/bin/runc is /usr/lib/cri-o-runc/sbin/runc" "$SAMESAME" -eq "0" || let "NFAILS+=1"
+fi
-echo "Total failed tests: $RET"
-exit $RET
+echo "Total failed tests: $NFAILS"
+exit $NFAILS
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index a9da3f4ce..737ca3c01 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -358,11 +358,14 @@ systemd_banish(){
set +e # Not all of these exist on every platform
for unit in $EVIL_UNITS
do
- ooe.sh sudo systemctl stop $unit
- ooe.sh sudo systemctl disable $unit
- ooe.sh sudo systemctl disable $unit.timer
- ooe.sh sudo systemctl mask $unit
- ooe.sh sudo systemctl mask $unit.timer
+ echo "Banishing $unit (ignoring errors)"
+ (
+ sudo systemctl stop $unit
+ sudo systemctl disable $unit
+ sudo systemctl disable $unit.timer
+ sudo systemctl mask $unit
+ sudo systemctl mask $unit.timer
+ ) &> /dev/null
done
set -e
}
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index e49bb98fe..2230684ac 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -34,7 +34,14 @@ done
# (see docs).
cd "${GOSRC}/"
case "${OS_REL_VER}" in
- ubuntu-18) ;;
+ ubuntu-18)
+ CRIO_RUNC_PATH="/usr/lib/cri-o-runc/sbin/runc"
+ if dpkg -L cri-o-runc | grep -m 1 -q "$CRIO_RUNC_PATH"
+ then
+ echo "Linking $CRIO_RUNC_PATH to /usr/bin/runc for ease of testing."
+ ln -f "$CRIO_RUNC_PATH" "/usr/bin/runc"
+ fi
+ ;;
fedora-30) ;;
fedora-29) ;;
centos-7) # Current VM is an image-builder-image no local podman/testing
diff --git a/docs/links/podman-container-cp.1 b/docs/links/podman-container-cp.1
new file mode 100644
index 000000000..6ad859c84
--- /dev/null
+++ b/docs/links/podman-container-cp.1
@@ -0,0 +1 @@
+.so man1/podman-cp.1
diff --git a/docs/links/podman-container-init.1 b/docs/links/podman-container-init.1
new file mode 100644
index 000000000..3a8bee249
--- /dev/null
+++ b/docs/links/podman-container-init.1
@@ -0,0 +1 @@
+.so man1/podman-init.1
diff --git a/docs/links/podman-help.1 b/docs/links/podman-help.1
new file mode 100644
index 000000000..6b7954b0d
--- /dev/null
+++ b/docs/links/podman-help.1
@@ -0,0 +1 @@
+.so man1/podman.1
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 4008b64e6..d796c2586 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -751,6 +751,7 @@ Without this argument the command will be run as root in the container.
**--userns**=*host*
**--userns**=*keep-id*
+**--userns**=container:container
**--userns**=*ns:my_namespace*
Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled.
@@ -758,6 +759,7 @@ Set the user namespace mode for the container. It defaults to the **PODMAN_USER
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
- `keep-id`: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
- `ns`: run the container in the given existing user namespace.
+- `container`: join the user namespace of the specified container.
This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
diff --git a/docs/podman-remote.1.md b/docs/podman-remote.1.md
new file mode 100644
index 000000000..84042a842
--- /dev/null
+++ b/docs/podman-remote.1.md
@@ -0,0 +1,141 @@
+% podman-remote(1)
+
+## NAME
+podman-remote - A remote CLI for Podman: A Simple management tool for pods, containers and images.
+
+## SYNOPSIS
+**podman-remote** [*options*] *command*
+
+## DESCRIPTION
+Podman (Pod Manager) is a fully featured container engine that is a simple daemonless tool.
+Podman provides a Docker-CLI comparable command line that eases the transition from other
+container engines and allows the management of pods, containers and images. Simply put: `alias docker=podman`.
+Most Podman commands can be run as a regular user, without requiring additional
+privileges.
+
+Podman uses Buildah(1) internally to create container images. Both tools share image
+(not container) storage, hence each can use or manipulate images (but not containers)
+created by the other.
+
+Podman-remote provides a local client interacting with a Podman backend node through a varlink ssh connection. In this context, a Podman node is a Linux system with Podman installed on it and the varlink service activated. Credentials for this session can be passed in using flags, enviroment variables, or in `podman-remote.conf`
+
+**podman [GLOBAL OPTIONS]**
+
+## GLOBAL OPTIONS
+
+**--connection**=*name*
+
+Remote connection name
+
+**--help**, **-h**
+
+Print usage statement
+
+**--log-level**=*level*
+
+Log messages above specified level: debug, info, warn, error (default), fatal or panic
+
+**--remote-config-path**=*path*
+
+Alternate path for configuration file
+
+**--remote-host**=*ip*
+
+Remote host IP
+
+**--syslog**
+
+Output logging information to syslog as well as the console
+
+**--username**=*string*
+
+Username on the remote host (defaults to current username)
+
+**--version**
+
+Print the version
+
+## Exit Status
+
+The exit code from `podman` gives information about why the container
+failed to run or why it exited. When `podman` commands exit with a non-zero code,
+the exit codes follow the `chroot` standard, see below:
+
+**_125_** if the error is with podman **_itself_**
+
+ $ podman run --foo busybox; echo $?
+ Error: unknown flag: --foo
+ 125
+
+**_126_** if executing a **_contained command_** and the **_command_** cannot be invoked
+
+ $ podman run busybox /etc; echo $?
+ Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error
+ 126
+
+**_127_** if executing a **_contained command_** and the **_command_** cannot be found
+ $ podman run busybox foo; echo $?
+ Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error
+ 127
+
+**_Exit code_** of **_contained command_** otherwise
+
+ $ podman run busybox /bin/sh -c 'exit 3'
+ # 3
+
+
+## COMMANDS
+
+| Command | Description |
+| ------------------------------------------------ | --------------------------------------------------------------------------- |
+| [podman-attach(1)](podman-attach.1.md) | Attach to a running container. |
+| [podman-build(1)](podman-build.1.md) | Build a container image using a Dockerfile. |
+| [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. |
+| [podman-container(1)](podman-container.1.md) | Manage containers. |
+| [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. |
+| [podman-create(1)](podman-create.1.md) | Create a new container. |
+| [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. |
+| [podman-events(1)](podman-events.1.md) | Monitor Podman events |
+| [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. |
+| [podman-generate(1)](podman-generate.1.md) | Generate structured data based for a containers and pods. |
+| [podman-healthcheck(1)](podman-healthcheck.1.md) | Manage healthchecks for containers |
+| [podman-history(1)](podman-history.1.md) | Show the history of an image. |
+| [podman-image(1)](podman-image.1.md) | Manage images. |
+| [podman-images(1)](podman-images.1.md) | List images in local storage. |
+| [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
+| [podman-info(1)](podman-info.1.md) | Displays Podman related system information. |
+| [podman-init(1)](podman-init.1.md) | Initialize a container |
+| [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
+| [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
+| [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. |
+| [podman-logs(1)](podman-logs.1.md) | Display the logs of a container. |
+| [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. |
+| [podman-pod(1)](podman-pod.1.md) | Management tool for groups of containers, called pods. |
+| [podman-port(1)](podman-port.1.md) | List port mappings for a container. |
+| [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. |
+| [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. |
+| [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. |
+| [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. |
+| [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. |
+| [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. |
+| [podman-run(1)](podman-run.1.md) | Run a command in a new container. |
+| [podman-save(1)](podman-save.1.md) | Save an image to a container archive. |
+| [podman-start(1)](podman-start.1.md) | Start one or more containers. |
+| [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
+| [podman-system(1)](podman-system.1.md) | Manage podman. |
+| [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
+| [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
+| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
+| [podman-version(1)](podman-version.1.md) | Display the Podman version information. |
+| [podman-volume(1)](podman-volume.1.md) | Manage Volumes. |
+
+## FILES
+
+**podman-remote.conf** (`~/.config/containers/podman-remote.conf`)
+
+ The podman-remote.conf file is the default configuration file for the podman
+ remote client. It is in the TOML format. It is primarily used to keep track
+ of the user's remote connections.
+
+## SEE ALSO
+`podman-remote.conf(5)`
diff --git a/docs/podman-remote.sh b/docs/podman-remote.sh
new file mode 100755
index 000000000..db3bb6d50
--- /dev/null
+++ b/docs/podman-remote.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+BREWDIR=$1
+mkdir -p $BREWDIR
+docs() {
+[ -z $1 ] || type="-$1"
+for i in $(podman-remote $1 --help | sed -n '/^Available Commands:/,/^Flags:/p'| sed -e '1d;$d' -e '/^$/d' | awk '{print $1}'); do install podman$type-$i.1 $BREWDIR 2>/dev/null || install links/podman$type-$i.1 $BREWDIR; done
+}
+docs
+
+for cmd in 'container image pod volume'; do docs $cmd; done
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index d6c7ae055..f5f44fad4 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -646,6 +646,9 @@ If specified, the first argument refers to an exploded container on the file sys
This is useful to run a container without requiring any image management, the rootfs
of the container is assumed to be managed externally.
+Note: On `SELinux` systems, the rootfs needs the correct label, which is by default
+`unconfined_u:object_r:container_file_t`.
+
**--security-opt**=*option*
Security Options
@@ -785,6 +788,7 @@ Without this argument the command will be run as root in the container.
**--userns**=host
**--userns**=keep-id
+**--userns**=container:container
**--userns**=ns:my_namespace
Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled.
@@ -792,6 +796,7 @@ Set the user namespace mode for the container. It defaults to the **PODMAN_USER
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
- `keep-id`: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
- `ns`: run the container in the given existing user namespace.
+- `container`: join the user namespace of the specified container.
This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 36732685b..83ee5640e 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1474,10 +1474,6 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten
} else {
manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"precreate", "poststop"})
if err != nil {
- if os.IsNotExist(err) {
- logrus.Warnf("Requested OCI hooks directory %q does not exist", c.runtime.config.HooksDir)
- return nil, nil
- }
return nil, err
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 6e775cd28..afcf51a11 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -318,6 +318,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if err := c.addNamespaceContainer(&g, UserNS, c.config.UserNsCtr, spec.UserNamespace); err != nil {
return nil, err
}
+ if len(g.Config.Linux.UIDMappings) == 0 {
+ // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
+ g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
+ g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
+ }
}
if c.config.UTSNsCtr != "" {
if err := c.addNamespaceContainer(&g, UTSNS, c.config.UTSNsCtr, spec.UTSNamespace); err != nil {
diff --git a/libpod/image/image.go b/libpod/image/image.go
index db50e3dbd..068491f28 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -1298,7 +1298,10 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
if err != nil {
return "", err
}
- return ociv1Img.History[0].Comment, nil
+ if len(ociv1Img.History) > 0 {
+ return ociv1Img.History[0].Comment, nil
+ }
+ return "", nil
}
// Save writes a container image to the filesystem
diff --git a/libpod/options.go b/libpod/options.go
index 8d41764a9..81d3aa64f 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -847,6 +847,7 @@ func WithUserNSFrom(nsCtr *Container) CtrCreateOption {
}
ctr.config.UserNsCtr = nsCtr.ID()
+ ctr.config.IDMappings = nsCtr.config.IDMappings
return nil
}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 5960fac60..b9d7fcd9b 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -492,14 +492,28 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa
if err != nil {
return nil, err
}
+ hasUserns := false
+ if podInfraID != "" {
+ podCtr, err := r.GetContainer(podInfraID)
+ if err != nil {
+ return nil, err
+ }
+ mappings, err := podCtr.IDMappings()
+ if err != nil {
+ return nil, err
+ }
+ hasUserns = len(mappings.UIDMap) > 0
+ }
namespaces := map[string]string{
// Disabled during code review per mheon
//"pid": fmt.Sprintf("container:%s", podInfraID),
- "net": fmt.Sprintf("container:%s", podInfraID),
- "user": fmt.Sprintf("container:%s", podInfraID),
- "ipc": fmt.Sprintf("container:%s", podInfraID),
- "uts": fmt.Sprintf("container:%s", podInfraID),
+ "net": fmt.Sprintf("container:%s", podInfraID),
+ "ipc": fmt.Sprintf("container:%s", podInfraID),
+ "uts": fmt.Sprintf("container:%s", podInfraID),
+ }
+ if hasUserns {
+ namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
}
if !c.Quiet {
writer = os.Stderr
diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go
index 5ed028b95..b962ffa5c 100644
--- a/pkg/hooks/hooks.go
+++ b/pkg/hooks/hooks.go
@@ -4,7 +4,6 @@ package hooks
import (
"context"
"fmt"
- "path/filepath"
"sort"
"strings"
"sync"
@@ -138,26 +137,3 @@ func (m *Manager) Hooks(config *rspec.Spec, annotations map[string]string, hasBi
return extensionStageHooks, nil
}
-
-// remove remove a hook by name.
-func (m *Manager) remove(hook string) (ok bool) {
- m.lock.Lock()
- defer m.lock.Unlock()
- _, ok = m.hooks[hook]
- if ok {
- delete(m.hooks, hook)
- }
- return ok
-}
-
-// add adds a hook by path
-func (m *Manager) add(path string) (err error) {
- m.lock.Lock()
- defer m.lock.Unlock()
- hook, err := Read(path, m.extensionStages)
- if err != nil {
- return err
- }
- m.hooks[filepath.Base(path)] = hook
- return nil
-}
diff --git a/pkg/hooks/monitor.go b/pkg/hooks/monitor.go
index febe3483f..c50b321f2 100644
--- a/pkg/hooks/monitor.go
+++ b/pkg/hooks/monitor.go
@@ -2,9 +2,8 @@ package hooks
import (
"context"
- "os"
- "path/filepath"
+ current "github.com/containers/libpod/pkg/hooks/1.0.0"
"github.com/fsnotify/fsnotify"
"github.com/sirupsen/logrus"
)
@@ -49,47 +48,11 @@ func (m *Manager) Monitor(ctx context.Context, sync chan<- error) {
for {
select {
case event := <-watcher.Events:
- filename := filepath.Base(event.Name)
- if len(m.directories) <= 1 {
- if event.Op&fsnotify.Remove == fsnotify.Remove {
- ok := m.remove(filename)
- if ok {
- logrus.Debugf("removed hook %s", event.Name)
- }
- } else if event.Op&fsnotify.Create == fsnotify.Create || event.Op&fsnotify.Write == fsnotify.Write {
- err = m.add(event.Name)
- if err == nil {
- logrus.Debugf("added hook %s", event.Name)
- } else if err != ErrNoJSONSuffix {
- logrus.Errorf("failed to add hook %s: %v", event.Name, err)
- }
- }
- } else if event.Op&fsnotify.Create == fsnotify.Create || event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Remove == fsnotify.Remove {
- err = nil
- found := false
- for i := len(m.directories) - 1; i >= 0; i-- {
- path := filepath.Join(m.directories[i], filename)
- err = m.add(path)
- if err == nil {
- found = true
- logrus.Debugf("(re)added hook %s (triggered activity on %s)", path, event.Name)
- break
- } else if err == ErrNoJSONSuffix {
- found = true
- break // this is not going to change for fallback directories
- } else if os.IsNotExist(err) {
- continue // move on to the next fallback directory
- } else {
- found = true
- logrus.Errorf("failed to (re)add hook %s (triggered by activity on %s): %v", path, event.Name, err)
- break
- }
- }
- if (found || event.Op&fsnotify.Remove == fsnotify.Remove) && err != nil {
- ok := m.remove(filename)
- if ok {
- logrus.Debugf("removed hook %s (triggered by activity on %s)", filename, event.Name)
- }
+ m.hooks = make(map[string]*current.Hook)
+ for _, dir := range m.directories {
+ err = ReadDir(dir, m.extensionStages, m.hooks)
+ if err != nil {
+ logrus.Errorf("failed loading hooks for %s: %v", event.Name, err)
}
}
case <-ctx.Done():
diff --git a/pkg/hooks/monitor_test.go b/pkg/hooks/monitor_test.go
index 31d7f9e39..dc67eaf83 100644
--- a/pkg/hooks/monitor_test.go
+++ b/pkg/hooks/monitor_test.go
@@ -226,7 +226,28 @@ func TestMonitorTwoDirGood(t *testing.T) {
assert.Equal(t, primaryInjected, config.Hooks) // masked by primary
})
- t.Run("bad-primary-addition", func(t *testing.T) {
+ primaryPath2 := filepath.Join(primaryDir, "0a.json") //0a because it will be before a.json alphabetically
+
+ t.Run("bad-primary-new-addition", func(t *testing.T) {
+ err = ioutil.WriteFile(primaryPath2, []byte("{\"version\": \"-1\"}"), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ time.Sleep(100 * time.Millisecond) // wait for monitor to notice
+
+ config := &rspec.Spec{}
+ fmt.Println("expected: ", config.Hooks)
+ expected := primaryInjected // 0a.json is bad, a.json is still good
+ _, err = manager.Hooks(config, map[string]string{}, false)
+ fmt.Println("actual: ", config.Hooks)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, expected, config.Hooks)
+ })
+
+ t.Run("bad-primary-same-addition", func(t *testing.T) {
err = ioutil.WriteFile(primaryPath, []byte("{\"version\": \"-1\"}"), 0644)
if err != nil {
t.Fatal(err)
@@ -235,7 +256,7 @@ func TestMonitorTwoDirGood(t *testing.T) {
time.Sleep(100 * time.Millisecond) // wait for monitor to notice
config := &rspec.Spec{}
- expected := config.Hooks
+ expected := fallbackInjected
_, err = manager.Hooks(config, map[string]string{}, false)
if err != nil {
t.Fatal(err)
diff --git a/pkg/hooks/read.go b/pkg/hooks/read.go
index d3995a0be..560ff1899 100644
--- a/pkg/hooks/read.go
+++ b/pkg/hooks/read.go
@@ -67,7 +67,7 @@ func ReadDir(path string, extensionStages []string, hooks map[string]*current.Ho
if err != nil {
return err
}
-
+ res := err
for _, file := range files {
filePath := filepath.Join(path, file.Name())
hook, err := Read(filePath, extensionStages)
@@ -80,12 +80,17 @@ func ReadDir(path string, extensionStages []string, hooks map[string]*current.Ho
continue
}
}
- return err
+ if res == nil {
+ res = err
+ } else {
+ res = errors.Wrapf(res, "%v", err)
+ }
+ continue
}
hooks[file.Name()] = hook
logrus.Debugf("added hook %s", filePath)
}
- return nil
+ return res
}
func init() {
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index 7ed95bd0f..35298796f 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -76,27 +76,50 @@ func (n UsernsMode) IsKeepID() bool {
// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
- return !(n.IsHost())
+ return !(n.IsHost() || n.IsContainer())
}
// Valid indicates whether the userns is valid.
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", "host", "keep-id":
+ case "", "host", "keep-id", "ns":
+ case "container":
+ if len(parts) != 2 || parts[1] == "" {
+ return false
+ }
default:
return false
}
return true
}
+// IsNS indicates a userns namespace passed in by path (ns:<path>)
+func (n UsernsMode) IsNS() bool {
+ return strings.HasPrefix(string(n), "ns:")
+}
+
+// NS gets the path associated with a ns:<path> userns ns
+func (n UsernsMode) NS() string {
+ parts := strings.SplitN(string(n), ":", 2)
+ if len(parts) > 1 {
+ return parts[1]
+ }
+ return ""
+}
+
// IsContainer indicates whether container uses a container userns.
func (n UsernsMode) IsContainer() bool {
- return false
+ parts := strings.SplitN(string(n), ":", 2)
+ return len(parts) > 1 && parts[0] == "container"
}
// Container is the id of the container which network this container is connected to.
func (n UsernsMode) Container() string {
+ parts := strings.SplitN(string(n), ":", 2)
+ if len(parts) > 1 {
+ return parts[1]
+ }
return ""
}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 1fb1f829b..214a3c5ed 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -266,7 +266,8 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
- postConfigureNetNS := c.NetMode.IsSlirp4netns() || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ postConfigureNetNS := c.NetMode.IsSlirp4netns() || (hasUserns && !c.UsernsMode.IsHost())
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
@@ -287,6 +288,26 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
}
+ if c.UsernsMode.IsNS() {
+ ns := c.UsernsMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined user namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ } else if c.UsernsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
+ }
+ options = append(options, libpod.WithUserNSFrom(connectedCtr))
+ } else {
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ }
+
if c.PidMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
if err != nil {
@@ -379,7 +400,6 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
}
options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
options = append(options, libpod.WithGroups(c.GroupAdd))
- options = append(options, libpod.WithIDMappings(*c.IDMappings))
if c.Rootfs != "" {
options = append(options, libpod.WithRootFS(c.Rootfs))
}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 824c99025..15c8c77fa 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -46,7 +46,8 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
canMountSys := true
isRootless := rootless.IsRootless()
- inUserNS := isRootless || (len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0) && !config.UsernsMode.IsHost()
+ hasUserns := config.UsernsMode.IsContainer() || config.UsernsMode.IsNS() || len(config.IDMappings.UIDMap) > 0 || len(config.IDMappings.GIDMap) > 0
+ inUserNS := isRootless || (hasUserns && !config.UsernsMode.IsHost())
if inUserNS && config.NetMode.IsHost() {
canMountSys = false
@@ -554,7 +555,6 @@ func addUserNS(config *CreateConfig, g *generate.Generator) error {
if err := g.AddOrReplaceLinuxNamespace(spec.UserNamespace, NS(string(config.UsernsMode))); err != nil {
return err
}
-
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index ef1c85518..7e14f9e06 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -214,11 +214,6 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
cgroupManager = os.Getenv("CGROUP_MANAGER")
}
- // Ubuntu doesn't use systemd cgroups
- if host.Distribution == "ubuntu" {
- cgroupManager = "cgroupfs"
- }
-
ociRuntime := os.Getenv("OCI_RUNTIME")
if ociRuntime == "" {
var err error
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index be3f7df49..e873f5abe 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -85,4 +85,19 @@ var _ = Describe("Podman UserNS support", func() {
ok, _ := session.GrepString(uid)
Expect(ok).To(BeTrue())
})
+
+ It("podman --userns=container:CTR", func() {
+ ctrName := "userns-ctr"
+ session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:1:4998", "--name", ctrName, "alpine", "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // runc has an issue and we also need to join the IPC namespace.
+ session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--ipc=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ ok, _ := session.GrepString("4998")
+ Expect(ok).To(BeTrue())
+ })
})