summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml211
-rw-r--r--.pre-commit-config.yaml1
-rw-r--r--Makefile3
-rw-r--r--cmd/podman/play/kube.go6
-rw-r--r--contrib/cirrus/lib.sh34
-rwxr-xr-xcontrib/cirrus/pr-should-include-tests27
-rwxr-xr-xcontrib/cirrus/runner.sh19
-rwxr-xr-xcontrib/cirrus/setup_environment.sh29
-rw-r--r--docs/source/markdown/podman-play-kube.1.md39
-rwxr-xr-xhack/get_ci_vm.sh2
-rw-r--r--pkg/bindings/play/types.go2
-rw-r--r--pkg/bindings/play/types_kube_options.go15
-rw-r--r--pkg/domain/entities/play.go2
-rw-r--r--pkg/domain/infra/abi/play.go77
-rw-r--r--pkg/domain/infra/tunnel/play.go2
-rw-r--r--pkg/specgen/generate/kube/kube.go61
-rw-r--r--test/e2e/play_kube_test.go150
17 files changed, 454 insertions, 226 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 958c6b0c7..324fd32f6 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -29,7 +29,7 @@ env:
UBUNTU_NAME: "ubuntu-2110"
# Google-cloud VM Images
- IMAGE_SUFFIX: "c4955393725038592"
+ IMAGE_SUFFIX: "c6211193021923328"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
@@ -51,6 +51,11 @@ env:
VM_IMAGE_NAME: # One of the "Google-cloud VM Images" (above)
CTR_FQIN: # One of the "Container FQIN's" (above)
+ # Curl-command prefix for downloading task artifacts, simply add the
+ # the url-encoded task name, artifact name, and path as a suffix.
+ ARTCURL: >-
+ curl --fail --location -O
+ --url https://api.cirrus-ci.com/v1/artifact/build/${CIRRUS_BUILD_ID}
# Default timeout for each task
timeout_in: 60m
@@ -96,7 +101,6 @@ ext_svc_check_task:
_gc='git config --file /root/.gitconfig'
$_gc user.email "TMcTestFace@example.com"
$_gc user.name "Testy McTestface"
- make install.tools
setup_script: &setup '$GOSRC/$SCRIPT_BASE/setup_environment.sh'
main_script: &main '/usr/bin/time --verbose --output="$STATS_LOGFILE" $GOSRC/$SCRIPT_BASE/runner.sh'
@@ -123,14 +127,12 @@ automation_task:
always: *runner_stats
-# N/B: This task is critical. It builds all binaries and release archives
-# for the project, using all primary OS platforms and versions. Assuming
-# the builds are successful, a cache is stored of the entire `$GOPATH`
-# contents. For all subsequent tasks, the _BUILD_CACHE_HANDLE value
-# is used as a key to reuse this cache, saving both time and money.
-# The only exceptions are tasks which only run inside a container, they
-# will not have access the cache and therefore must rely on cloning the
-# repository.
+# N/B: This task is critical. It builds all binaries for all supported
+# OS platforms and versions. On success, the contents of the repository
+# are preserved as an artifact. This saves most subsequent tasks about
+# 3 minutes of otherwise duplicative effort. It also ensures that the
+# exact same binaries used throughout CI testing, are available for
+# future consumption|inspection by the final 'artifacts' task.
build_task:
alias: 'build'
name: 'Build for $DISTRO_NV'
@@ -151,37 +153,27 @@ build_task:
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
# ID for re-use of build output
- _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- env: &priorfedora_envvars
DISTRO_NV: ${PRIOR_FEDORA_NAME}
VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN}
- _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- env: &ubuntu_envvars
DISTRO_NV: ${UBUNTU_NAME}
VM_IMAGE_NAME: ${UBUNTU_CACHE_IMAGE_NAME}
CTR_FQIN: ${UBUNTU_CONTAINER_FQIN}
- _BUILD_CACHE_HANDLE: ${UBUNTU_NAME}-build-${CIRRUS_BUILD_ID}
env:
TEST_FLAVOR: build
- # Ref: https://cirrus-ci.org/guide/writing-tasks/#cache-instruction
- gopath_cache: &gopath_cache
- folder: *gopath # Required hard-coded path, no variables.
- fingerprint_script: echo "$_BUILD_CACHE_HANDLE"
- # Cheat: Clone here when cache is empty, guaranteeing consistency.
- populate_script: *full_clone
- # A normal clone would invalidate useful cache
- clone_script: &noop mkdir -p $CIRRUS_WORKING_DIR
+ clone_script: *full_clone
setup_script: *setup
main_script: *main
- always: &binary_artifacts
- <<: *runner_stats
- gosrc_artifacts:
- path: ./* # Grab everything in top-level $GOSRC
- type: application/octet-stream
- binary_artifacts:
- path: ./bin/*
- type: application/octet-stream
+ # Cirrus-CI is very slow uploading one file at time, and the repo contains
+ # thousands of files. Speed this up by archiving into tarball first.
+ repo_prep_script: &repo_prep >-
+ tar cjf /tmp/repo.tbz -C $GOSRC . && mv /tmp/repo.tbz $GOSRC/
+ repo_artifacts: &repo_artifacts
+ path: ./repo.tbz
+ type: application/octet-stream
+ always: *runner_stats
# Confirm the result of building on at least one platform appears sane.
@@ -208,10 +200,12 @@ validate_task:
env:
<<: *stdenvars
TEST_FLAVOR: validate
- gopath_cache: &ro_gopath_cache
- <<: *gopath_cache
- reupload_on_changes: false
- clone_script: *noop
+ # N/B: This script depends on ${DISTRO_NV} being defined for the task.
+ clone_script: &get_gosrc |
+ cd /tmp
+ echo "$ARTCURL/Build%20for%20${DISTRO_NV}/repo/repo.tbz"
+ time $ARTCURL/Build%20for%20${DISTRO_NV}/repo/repo.tbz
+ time tar xjf /tmp/repo.tbz -C $GOSRC
setup_script: *setup
main_script: *main
always: *runner_stats
@@ -231,8 +225,7 @@ bindings_task:
env:
<<: *stdenvars
TEST_FLAVOR: bindings
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: &logs_artifacts
@@ -260,19 +253,18 @@ swagger_task:
env:
<<: *stdenvars
TEST_FLAVOR: swagger
- # TODO: Due to podman 3.0 activity (including new images), avoid
- # disturbing the status-quo just to incorporate this one new
- # container image. Uncomment line below when CI activities normalize.
- #CTR_FQIN: 'quay.io/libpod/gcsupld:${IMAGE_SUFFIX}'
- CTR_FQIN: 'quay.io/libpod/gcsupld:c4813063494828032'
+ CTR_FQIN: 'quay.io/libpod/gcsupld:${IMAGE_SUFFIX}'
GCPJSON: ENCRYPTED[927dc01e755eaddb4242b0845cf86c9098d1e3dffac38c70aefb1487fd8b4fe6dd6ae627b3bffafaba70e2c63172664e]
GCPNAME: ENCRYPTED[c145e9c16b6fb88d476944a454bf4c1ccc84bb4ecaca73bdd28bdacef0dfa7959ebc8171a27b2e4064d66093b2cdba49]
GCPPROJECT: 'libpod-218412'
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
- always: *binary_artifacts
+ always:
+ <<: *runner_stats
+ swagger_artifacts:
+ path: ./swagger.yaml
+ type: text/plain
# Check that all included go modules from other sources match
@@ -291,7 +283,7 @@ consistency_task:
TEST_FLAVOR: consistency
TEST_ENVIRON: container
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
- clone_script: *full_clone # build-cache not available to container tasks
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *runner_stats
@@ -322,11 +314,14 @@ alt_build_task:
ALT_NAME: 'Test build RPM'
- env:
ALT_NAME: 'Alt Arch. Cross'
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ # This task cannot make use of the shared repo.tbz artifact.
+ clone_script: *full_clone
setup_script: *setup
main_script: *main
- always: *binary_artifacts
+ # Produce a new repo.tbz artifact for consumption by 'artifacts' task.
+ repo_prep_script: *repo_prep
+ repo_artifacts: *repo_artifacts
+ always: *runner_stats
# Confirm building the remote client, natively on a Mac OS-X VM.
@@ -348,10 +343,14 @@ osx_alt_build_task:
- brew install go-md2man
- go version
build_amd64_script:
- - make podman-remote-release-darwin_amd64.zip
+ - make podman-remote-release-darwin_amd64.zip GOARCH=amd64
build_arm64_script:
- make podman-remote-release-darwin_arm64.zip GOARCH=arm64
- always: *binary_artifacts
+ # This task cannot make use of the shared repo.tbz artifact and must
+ # produce a new repo.tbz artifact for consumption by 'artifacts' task.
+ repo_prep_script: *repo_prep
+ repo_artifacts: *repo_artifacts
+ always: *runner_stats
# Verify podman is compatible with the docker python-module.
@@ -367,8 +366,7 @@ docker-py_test_task:
<<: *stdenvars
TEST_FLAVOR: docker-py
TEST_ENVIRON: container
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *runner_stats
@@ -395,8 +393,7 @@ unit_test_task:
gce_instance: *standardvm
env:
TEST_FLAVOR: unit
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -416,8 +413,7 @@ apiv2_test_task:
env:
<<: *stdenvars
TEST_FLAVOR: apiv2
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -431,8 +427,6 @@ compose_test_task:
depends_on:
- validate
gce_instance: *standardvm
- env:
- <<: *stdenvars
matrix:
- env:
TEST_FLAVOR: compose
@@ -446,8 +440,9 @@ compose_test_task:
- env:
TEST_FLAVOR: compose_v2
PRIV_NAME: rootless
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ env:
+ <<: *stdenvars
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -469,8 +464,7 @@ local_integration_test_task: &local_integration_test_task
timeout_in: 90m
env:
TEST_FLAVOR: int
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: &int_logs_artifacts
@@ -516,8 +510,7 @@ container_integration_test_task:
env:
TEST_FLAVOR: int
TEST_ENVIRON: container
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *int_logs_artifacts
@@ -537,8 +530,7 @@ rootless_integration_test_task:
env:
TEST_FLAVOR: int
PRIV_NAME: rootless
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *int_logs_artifacts
@@ -559,8 +551,7 @@ local_system_test_task: &local_system_test_task
gce_instance: *standardvm
env:
TEST_FLAVOR: sys
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -619,8 +610,7 @@ buildah_bud_test_task:
PODBIN_NAME: remote
gce_instance: *standardvm
timeout_in: 45m
- clone_script: *noop
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *int_logs_artifacts
@@ -638,8 +628,7 @@ rootless_system_test_task:
env:
TEST_FLAVOR: sys
PRIV_NAME: rootless
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -661,8 +650,7 @@ rootless_gitlab_test_task:
<<: *ubuntu_envvars
TEST_FLAVOR: 'gitlab'
PRIV_NAME: rootless
- clone_script: *noop # Comes from cache
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always:
@@ -694,8 +682,7 @@ upgrade_test_task:
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
# ID for re-use of build output
_BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- clone_script: *noop
- gopath_cache: *ro_gopath_cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
always: *logs_artifacts
@@ -715,11 +702,6 @@ image_build_task: &image-build
image_name: build-push-${IMAGE_SUFFIX}
# More muscle required for parallel multi-arch build
type: "n2-standard-4"
- env:
- PODMAN_USERNAME: ENCRYPTED[b9f0f2550029dd2196e086d9dd6c2d1fec7e328630b15990d9bb610f9fcccb5baab8b64a8c3e72b0c1d0f5917cf65aa1]
- PODMAN_PASSWORD: ENCRYPTED[e3444f6072853f0c8db7f964ead5e2204116af485469fa0de367f26b9316b460fd842a9882f552b9e9a83bbaf650d8b4]
- CONTAINERS_USERNAME: ENCRYPTED[54a372d5f22f424173c114c6fb25c3214956cad323d5b285c7393a71041884ce96471d0ff733774e5dab9fa5a3c8795c]
- CONTAINERS_PASSWORD: ENCRYPTED[4ecc3fb534935095a99fb1f2e320ac6bc87f3e7e186746e41cbcc4b5f5379a014b9fc8cc90e1f3d5abdbaf31580a4ab9]
matrix:
- env:
CTXDIR: contrib/podmanimage/upstream
@@ -729,6 +711,12 @@ image_build_task: &image-build
CTXDIR: contrib/podmanimage/stable
- env:
CTXDIR: contrib/hello
+ env:
+ PODMAN_USERNAME: ENCRYPTED[b9f0f2550029dd2196e086d9dd6c2d1fec7e328630b15990d9bb610f9fcccb5baab8b64a8c3e72b0c1d0f5917cf65aa1]
+ PODMAN_PASSWORD: ENCRYPTED[e3444f6072853f0c8db7f964ead5e2204116af485469fa0de367f26b9316b460fd842a9882f552b9e9a83bbaf650d8b4]
+ CONTAINERS_USERNAME: ENCRYPTED[54a372d5f22f424173c114c6fb25c3214956cad323d5b285c7393a71041884ce96471d0ff733774e5dab9fa5a3c8795c]
+ CONTAINERS_PASSWORD: ENCRYPTED[4ecc3fb534935095a99fb1f2e320ac6bc87f3e7e186746e41cbcc4b5f5379a014b9fc8cc90e1f3d5abdbaf31580a4ab9]
+ clone_script: &noop mkdir -p $CIRRUS_WORKING_DIR
script:
- set -a; source /etc/automation_environment; set +a
- main.sh $CIRRUS_REPO_CLONE_URL $CTXDIR
@@ -828,42 +816,41 @@ artifacts_task:
env:
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
TEST_ENVIRON: container
- CURL: "curl --fail --location -O https://api.cirrus-ci.com/v1/artifact/build/${CIRRUS_BUILD_ID}"
# In order to keep the download URL and Cirrus-CI artifact.zip contents
# simple, nothing should exist in $CIRRUS_WORKING_DIR except for artifacts.
clone_script: *noop
- script:
- # Assume the latest Fedora release build is most useful
- - $CURL/Build%20for%20$FEDORA_NAME/binary/bin/podman
- - $CURL/Build%20for%20$FEDORA_NAME/binary/bin/podman-remote
- - $CURL/Build%20for%20$FEDORA_NAME/binary/bin/rootlessport
- - chmod +x podman* rootlessport
- # Architecture in filename & can't use wildcards in a URL
+ fedora_binaries_script:
+ - mkdir -p /tmp/fed
+ - cd /tmp/fed
+ - $ARTCURL/Build%20for%20${FEDORA_NAME}/repo/repo.tbz
+ - tar xjf repo.tbz
+ - cp ./bin/* $CIRRUS_WORKING_DIR/
+ alt_binaries_script:
- mkdir -p /tmp/alt
- cd /tmp/alt
- - $CURL/Alt%20Arch.%20Cross/gosrc.zip
- - unzip gosrc.zip
- - cd $CIRRUS_WORKING_DIR
- - mv /tmp/alt/*.tar.gz ./
- # Windows MSI filename has version number
+ - $ARTCURL/Alt%20Arch.%20Cross/repo/repo.tbz
+ - tar xjf repo.tbz
+ - mv ./*.tar.gz $CIRRUS_WORKING_DIR/
+ win_binaries_script:
- mkdir -p /tmp/win
- cd /tmp/win
- - $CURL/Windows%20Cross/gosrc.zip
- - unzip gosrc.zip
- - cd $CIRRUS_WORKING_DIR
- - mv /tmp/win/podman-remote*.zip /tmp/win/*.msi ./
- # OSX
- - $CURL/OSX%20Cross/gosrc/podman-remote-release-darwin_amd64.zip
- - $CURL/OSX%20Cross/gosrc/podman-remote-release-darwin_arm64.zip
- # Always show contents to assist in debugging
+ - $ARTCURL/Windows%20Cross/repo/repo.tbz
+ - tar xjf repo.tbz
+ - mv ./podman-remote*.zip ./*.msi $CIRRUS_WORKING_DIR/
+ osx_binaries_script:
+ - mkdir -p /tmp/osx
+ - cd /tmp/osx
+ - $ARTCURL/OSX%20Cross/repo/repo.tbz
+ - tar xjf repo.tbz
+ - mv ./podman-remote-release-darwin_*.zip $CIRRUS_WORKING_DIR/
always:
- contents_script: ls -1 $CIRRUS_WORKING_DIR
- # Produce downloadable files and an automatic zip-file accessible
- # by a consistent URL, based on contents of $CIRRUS_WORKING_DIR
- # Ref: https://cirrus-ci.org/guide/writing-tasks/#latest-build-artifacts
- binary_artifacts:
- path: ./*
- type: application/octet-stream
+ contents_script: ls -la $CIRRUS_WORKING_DIR
+ # Produce downloadable files and an automatic zip-file accessible
+ # by a consistent URL, based on contents of $CIRRUS_WORKING_DIR
+ # Ref: https://cirrus-ci.org/guide/writing-tasks/#latest-build-artifacts
+ binary_artifacts:
+ path: ./*
+ type: application/octet-stream
# When a new tag is pushed, confirm that the code and commits
@@ -878,11 +865,9 @@ release_task:
env:
<<: *stdenvars
TEST_FLAVOR: release
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
- always: *binary_artifacts
# When preparing to release a new version, this task may be manually
@@ -905,8 +890,6 @@ release_test_task:
env:
<<: *stdenvars
TEST_FLAVOR: release
- gopath_cache: *ro_gopath_cache
- clone_script: *noop # Comes from cache
+ clone_script: *get_gosrc
setup_script: *setup
main_script: *main
- always: *binary_artifacts
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3416a93ee..ca023d18a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,3 +17,4 @@ repos:
- id: check-byte-order-marker
- id: check-executables-have-shebangs
- id: check-merge-conflict
+ - id: check-yaml
diff --git a/Makefile b/Makefile
index 51b3d6d96..6ea207bca 100644
--- a/Makefile
+++ b/Makefile
@@ -134,7 +134,8 @@ ifeq ($(GOBIN),)
GOBIN := $(FIRST_GOPATH)/bin
endif
-export PATH := $(PATH):$(GOBIN):$(CURDIR)/hack
+# This must never include the 'hack' directory
+export PATH := $(PATH):$(GOBIN)
GOMD2MAN ?= $(shell command -v go-md2man || echo './test/tools/build/go-md2man')
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index 3be7396ce..5fe059139 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -98,6 +98,12 @@ func init() {
)
_ = kubeCmd.RegisterFlagCompletionFunc(logOptFlagName, common.AutocompleteLogOpt)
+ usernsFlagName := "userns"
+ flags.StringVar(&kubeOptions.Userns, usernsFlagName, os.Getenv("PODMAN_USERNS"),
+ "User namespace to use",
+ )
+ _ = kubeCmd.RegisterFlagCompletionFunc(usernsFlagName, common.AutocompleteUserNamespace)
+
flags.BoolVar(&kubeOptions.NoHosts, "no-hosts", false, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 46b245a45..5d3e43c50 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -36,11 +36,6 @@ fi
# Managed by setup_environment.sh; holds task-specific definitions.
if [[ -r "/etc/ci_environment" ]]; then source /etc/ci_environment; fi
-OS_RELEASE_ID="$(source /etc/os-release; echo $ID)"
-# GCE image-name compatible string representation of distribution _major_ version
-OS_RELEASE_VER="$(source /etc/os-release; echo $VERSION_ID | tr -d '.')"
-# Combined to ease some usage
-OS_REL_VER="${OS_RELEASE_ID}-${OS_RELEASE_VER}"
# This is normally set from .cirrus.yml but default is necessary when
# running under hack/get_ci_vm.sh since it cannot infer the value.
DISTRO_NV="${DISTRO_NV:-$OS_REL_VER}"
@@ -96,7 +91,7 @@ EPOCH_TEST_COMMIT="$CIRRUS_BASE_SHA"
# testing operations on all platforms and versions. This is necessary
# to avoid needlessly passing through global/system values across
# contexts, such as host->container or root->rootless user
-PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)|(NETWORK_BACKEND)'
+PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)|(NETWORK_BACKEND)|(DEST_BRANCH)'
# Unsafe env. vars for display
SECRET_ENV_RE='(ACCOUNT)|(GC[EP]..+)|(SSH)|(PASSWORD)|(TOKEN)'
@@ -182,30 +177,21 @@ setup_rootless() {
cat $HOME/.ssh/*.pub /home/$ROOTLESS_USER/.ssh/*.pub >> $HOME/.ssh/authorized_keys
cat $HOME/.ssh/*.pub /home/$ROOTLESS_USER/.ssh/*.pub >> /home/$ROOTLESS_USER/.ssh/authorized_keys
- msg "Ensure the ssh daemon is up and running within 5 minutes"
- systemctl start sshd
- lilto systemctl is-active sshd
-
msg "Configure ssh file permissions"
chmod -R 700 "$HOME/.ssh"
chmod -R 700 "/home/$ROOTLESS_USER/.ssh"
chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh"
+ # N/B: We're clobbering the known_hosts here on purpose. There should
+ # never be any non-localhost connections made from tests (using strict-mode).
+ # If there are, it's either a security problem or a broken test, both of which
+ # we want to lead to test failures.
msg " setup known_hosts for $USER"
- ssh -q root@localhost \
- -o UserKnownHostsFile=/root/.ssh/known_hosts \
- -o UpdateHostKeys=yes \
- -o StrictHostKeyChecking=no \
- -o CheckHostIP=no \
- true
-
+ ssh-keyscan localhost > /root/.ssh/known_hosts
msg " setup known_hosts for $ROOTLESS_USER"
- su $ROOTLESS_USER -c "ssh -q $ROOTLESS_USER@localhost \
- -o UserKnownHostsFile=/home/$ROOTLESS_USER/.ssh/known_hosts \
- -o UpdateHostKeys=yes \
- -o StrictHostKeyChecking=no \
- -o CheckHostIP=no \
- true"
+ # Maintain access-permission consistency with all other .ssh files.
+ install -Z -m 700 -o $ROOTLESS_USER -g $ROOTLESS_USER \
+ /root/.ssh/known_hosts /home/$ROOTLESS_USER/.ssh/known_hosts
}
install_test_configs() {
@@ -270,6 +256,8 @@ remove_packaged_podman_files() {
done
done
+ # OS_RELEASE_ID is defined by automation-library
+ # shellcheck disable=SC2154
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]
then
LISTING_CMD="dpkg-query -L podman"
diff --git a/contrib/cirrus/pr-should-include-tests b/contrib/cirrus/pr-should-include-tests
index 0d39047a6..57ca39d9b 100755
--- a/contrib/cirrus/pr-should-include-tests
+++ b/contrib/cirrus/pr-should-include-tests
@@ -30,19 +30,20 @@ fi
# Nothing changed under test subdirectory.
#
# This is OK if the only files being touched are "safe" ones.
-filtered_changes=$(git diff --name-only $base $head |
- fgrep -vx .cirrus.yml |
- fgrep -vx .gitignore |
- fgrep -vx Makefile |
- fgrep -vx go.mod |
- fgrep -vx go.sum |
- egrep -v '^[^/]+\.md$' |
- egrep -v '^.github' |
- egrep -v '^contrib/' |
- egrep -v '^docs/' |
- egrep -v '^hack/' |
- egrep -v '^nix/' |
- egrep -v '^vendor/' |
+filtered_changes=$(git diff --name-only $base $head |
+ fgrep -vx .cirrus.yml |
+ fgrep -vx .pre-commit-config.yaml |
+ fgrep -vx .gitignore |
+ fgrep -vx Makefile |
+ fgrep -vx go.mod |
+ fgrep -vx go.sum |
+ egrep -v '^[^/]+\.md$' |
+ egrep -v '^.github' |
+ egrep -v '^contrib/' |
+ egrep -v '^docs/' |
+ egrep -v '^hack/' |
+ egrep -v '^nix/' |
+ egrep -v '^vendor/' |
egrep -v '^version/')
if [[ -z "$filtered_changes" ]]; then
exit 0
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index c4a714691..1ec18c861 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -250,16 +250,23 @@ function _run_altbuild() {
case "$ALT_NAME" in
*Each*)
git fetch origin
- # The check-size script, introduced 2022-03-22 in #13518,
+ # The make-and-check-size script, introduced 2022-03-22 in #13518,
# runs 'make' (the original purpose of this check) against
# each commit, then checks image sizes to make sure that
# none have grown beyond a given limit. That of course
- # requires a baseline, which is why we use '^' to start
- # with the *parent* commit of this PR, not the first commit.
+ # requires a baseline, so our first step is to build the
+ # branch point of the PR.
+ local context_dir savedhead pr_base
context_dir=$(mktemp -d --tmpdir make-size-check.XXXXXXX)
- make build-all-new-commits \
- GIT_BASE_BRANCH=origin/"${DEST_BRANCH}^" \
- MAKE="hack/make-and-check-size $context_dir"
+ savedhead=$(git rev-parse HEAD)
+ # Push to PR base. First run of the script will write size files
+ pr_base=$(git merge-base --fork-point origin/$DEST_BRANCH)
+ git checkout $pr_base
+ hack/make-and-check-size $context_dir
+ # pop back to PR, and run incremental makes. Subsequent script
+ # invocations will compare against original size.
+ git checkout $savedhead
+ git rebase $pr_base -x "hack/make-and-check-size $context_dir"
rm -rf $context_dir
;;
*Windows*)
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index e3eb46783..f31cd6eeb 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -25,7 +25,7 @@ msg "************************************************************"
show_env_vars
req_env_vars USER HOME GOSRC SCRIPT_BASE TEST_FLAVOR TEST_ENVIRON \
- PODBIN_NAME PRIV_NAME DISTRO_NV
+ PODBIN_NAME PRIV_NAME DISTRO_NV DEST_BRANCH
# Verify basic dependencies
for depbin in go rsync unzip sha256sum curl make python3 git
@@ -121,6 +121,9 @@ case "$OS_RELEASE_ID" in
# CNI networking available. Upgrading from one to the other is
# not supported at this time. Support execution of the upgrade
# tests in F36 and later, by disabling Netavark and enabling CNI.
+ #
+ # OS_RELEASE_VER is defined by automation-library
+ # shellcheck disable=SC2154
if [[ "$OS_RELEASE_VER" -ge 36 ]] && \
[[ "$TEST_FLAVOR" != "upgrade_test" ]];
then
@@ -217,6 +220,7 @@ case "$TEST_FLAVOR" in
validate)
dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm
# For some reason, this is also needed for validation
+ make install.tools
make .install.pre-commit
;;
automation) ;;
@@ -226,10 +230,12 @@ case "$TEST_FLAVOR" in
if [[ "$ALT_NAME" =~ RPM ]]; then
bigto dnf install -y glibc-minimal-langpack go-rpm-macros rpkg rpm-build shadow-utils-subid-devel
fi
+ make install.tools
;;
docker-py)
remove_packaged_podman_files
- make && make install PREFIX=/usr ETCDIR=/etc
+ make install.tools
+ make install PREFIX=/usr ETCDIR=/etc
msg "Installing previously downloaded/cached packages"
dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm
@@ -239,13 +245,17 @@ case "$TEST_FLAVOR" in
pip install --requirement $GOSRC/test/python/requirements.txt
;;
build) make clean ;;
- unit) ;;
+ unit)
+ make install.tools
+ ;;
compose_v2)
+ make install.tools
dnf -y remove docker-compose
curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
;& # Continue with next item
apiv2)
+ make install.tools
msg "Installing previously downloaded/cached packages"
dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm
virtualenv .venv/requests
@@ -254,6 +264,7 @@ case "$TEST_FLAVOR" in
pip install --requirement $GOSRC/test/apiv2/python/requirements.txt
;& # continue with next item
compose)
+ make install.tools
rpm -ivh $PACKAGE_DOWNLOAD_DIR/podman-docker*
;& # continue with next item
int) ;&
@@ -262,6 +273,7 @@ case "$TEST_FLAVOR" in
bud) ;&
bindings) ;&
endpoint)
+ make install.tools
# Use existing host bits when testing is to happen inside a container
# since this script will run again in that environment.
# shellcheck disable=SC2154
@@ -270,11 +282,11 @@ case "$TEST_FLAVOR" in
die "Refusing to config. host-test in container";
fi
remove_packaged_podman_files
- make && make install PREFIX=/usr ETCDIR=/etc
+ make install PREFIX=/usr ETCDIR=/etc
elif [[ "$TEST_ENVIRON" == "container" ]]; then
if ((CONTAINER)); then
remove_packaged_podman_files
- make && make install PREFIX=/usr ETCDIR=/etc
+ make install PREFIX=/usr ETCDIR=/etc
fi
else
die "Invalid value for \$TEST_ENVIRON=$TEST_ENVIRON"
@@ -291,7 +303,7 @@ case "$TEST_FLAVOR" in
# Ref: https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27270#note_499585550
remove_packaged_podman_files
- make && make install PREFIX=/usr ETCDIR=/etc
+ make install PREFIX=/usr ETCDIR=/etc
msg "Installing docker and containerd"
# N/B: Tests check/expect `docker info` output, and this `!= podman info`
@@ -324,7 +336,10 @@ case "$TEST_FLAVOR" in
docker.io/gitlab/gitlab-runner-helper:x86_64-latest-pwsh
;;
swagger) ;& # use next item
- consistency) make clean ;;
+ consistency)
+ make clean
+ make install.tools
+ ;;
release) ;;
*) die_unknown TEST_FLAVOR
esac
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index 8ed71b734..5c4bdc8c4 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -243,6 +243,45 @@ Require HTTPS and verify certificates when contacting registries (default: true)
then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified,
TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf.
+#### **--userns**=*mode*
+
+Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **--uidmap** and **--gidmap** options.
+
+Rootless user --userns=Key mappings:
+
+Key | Host User | Container User
+----------|---------------|---------------------
+"" |$UID |0 (Default User account mapped to root user in container.)
+keep-id |$UID |$UID (Map user account to same UID within container.)
+auto |$UID | nil (Host User UID is not mapped into container.)
+nomap |$UID | nil (Host User UID is not mapped into container.)
+
+Valid _mode_ values are:
+
+**auto**[:_OPTIONS,..._]: automatically create a unique user namespace.
+
+The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files.
+
+Example: `containers:2147483647:2147483648`.
+
+Podman allocates unique ranges of UIDs and GIDs from the `containers` subordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode
+
+ Valid `auto` options:
+
+ - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace.
+ - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
+ - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace.
+
+**container:**_id_: join the user namespace of the specified container.
+
+**host**: create a new namespace for the container.
+
+**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.
+
+**nomap**: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is ignored for containers created by the root user.
+
+**ns:**_namespace_: run the pod in the given existing user namespace.
+
## EXAMPLES
Recreate the pod and containers as described in a file called `demo.yml`
diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh
index ef7069a81..6632a0178 100755
--- a/hack/get_ci_vm.sh
+++ b/hack/get_ci_vm.sh
@@ -61,7 +61,7 @@ else
-e NAME="$USER" \
-e SRCDIR=/src \
-e GCLOUD_ZONE="$GCLOUD_ZONE" \
- -e DEBUG="${DEBUG:-0}" \
+ -e A_DEBUG="${A_DEBUG:-0}" \
-v $REPO_DIRPATH:/src:O \
-v $HOME/.config/gcloud:/root/.config/gcloud:z \
-v $HOME/.config/gcloud/ssh:/root/.ssh:z \
diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go
index dbff4304b..5aaa87b8c 100644
--- a/pkg/bindings/play/types.go
+++ b/pkg/bindings/play/types.go
@@ -43,4 +43,6 @@ type KubeOptions struct {
LogOptions *[]string
// Start - don't start the pod if false
Start *bool
+ // Userns - define the user namespace to use.
+ Userns *string
}
diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go
index d7a452ea2..54c9a8e74 100644
--- a/pkg/bindings/play/types_kube_options.go
+++ b/pkg/bindings/play/types_kube_options.go
@@ -272,3 +272,18 @@ func (o *KubeOptions) GetStart() bool {
}
return *o.Start
}
+
+// WithUserns set field Userns to given value
+func (o *KubeOptions) WithUserns(value string) *KubeOptions {
+ o.Userns = &value
+ return o
+}
+
+// GetUserns returns value of field Userns
+func (o *KubeOptions) GetUserns() string {
+ if o.Userns == nil {
+ var z string
+ return z
+ }
+ return *o.Userns
+}
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
index c9dc3f08c..bf7c33f2b 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -54,6 +54,8 @@ type PlayKubeOptions struct {
LogOptions []string
// Start - don't start the pod if false
Start types.OptionalBool
+ // Userns - define the user namespace to use.
+ Userns string
}
// PlayKubePod represents a single pod and associated containers created by play kube
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index b3ded7db6..019361694 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -222,6 +222,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
podOpt.Net.NetworkOptions = netOpts
}
+ if options.Userns == "" {
+ options.Userns = "host"
+ }
+
+ // Validate the userns modes supported.
+ podOpt.Userns, err = specgen.ParseUserNamespace(options.Userns)
+ if err != nil {
+ return nil, err
+ }
+
// FIXME This is very hard to support properly with a good ux
if len(options.StaticIPs) > *ipIndex {
if !podOpt.Net.Network.IsBridge() {
@@ -352,6 +362,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
infraImage := util.DefaultContainerConfig().Engine.InfraImage
infraOptions := entities.NewInfraContainerCreateOptions()
infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
+ infraOptions.UserNS = options.Userns
podSpec.PodSpecGen.InfraImage = infraImage
podSpec.PodSpecGen.NoInfra = false
podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
@@ -412,22 +423,24 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
specgenOpts := kube.CtrSpecGenOptions{
- Annotations: annotations,
- Container: initCtr,
- Image: pulledImage,
- Volumes: volumes,
- PodID: pod.ID(),
- PodName: podName,
- PodInfraID: podInfraID,
- ConfigMaps: configMaps,
- SeccompPaths: seccompPaths,
- RestartPolicy: ctrRestartPolicy,
- NetNSIsHost: p.NetNS.IsHost(),
- SecretsManager: secretsManager,
- LogDriver: options.LogDriver,
- LogOptions: options.LogOptions,
- Labels: labels,
- InitContainerType: define.AlwaysInitContainer,
+ Annotations: annotations,
+ ConfigMaps: configMaps,
+ Container: initCtr,
+ Image: pulledImage,
+ InitContainerType: define.AlwaysInitContainer,
+ Labels: labels,
+ LogDriver: options.LogDriver,
+ LogOptions: options.LogOptions,
+ NetNSIsHost: p.NetNS.IsHost(),
+ PodID: pod.ID(),
+ PodInfraID: podInfraID,
+ PodName: podName,
+ PodSecurityContext: podYAML.Spec.SecurityContext,
+ RestartPolicy: ctrRestartPolicy,
+ SeccompPaths: seccompPaths,
+ SecretsManager: secretsManager,
+ UserNSIsHost: p.Userns.IsHost(),
+ Volumes: volumes,
}
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
if err != nil {
@@ -460,21 +473,23 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
specgenOpts := kube.CtrSpecGenOptions{
- Annotations: annotations,
- Container: container,
- Image: pulledImage,
- Volumes: volumes,
- PodID: pod.ID(),
- PodName: podName,
- PodInfraID: podInfraID,
- ConfigMaps: configMaps,
- SeccompPaths: seccompPaths,
- RestartPolicy: ctrRestartPolicy,
- NetNSIsHost: p.NetNS.IsHost(),
- SecretsManager: secretsManager,
- LogDriver: options.LogDriver,
- LogOptions: options.LogOptions,
- Labels: labels,
+ Annotations: annotations,
+ ConfigMaps: configMaps,
+ Container: container,
+ Image: pulledImage,
+ Labels: labels,
+ LogDriver: options.LogDriver,
+ LogOptions: options.LogOptions,
+ NetNSIsHost: p.NetNS.IsHost(),
+ PodID: pod.ID(),
+ PodInfraID: podInfraID,
+ PodName: podName,
+ PodSecurityContext: podYAML.Spec.SecurityContext,
+ RestartPolicy: ctrRestartPolicy,
+ SeccompPaths: seccompPaths,
+ SecretsManager: secretsManager,
+ UserNSIsHost: p.Userns.IsHost(),
+ Volumes: volumes,
}
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
if err != nil {
diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go
index d9637254a..d731a1d6c 100644
--- a/pkg/domain/infra/tunnel/play.go
+++ b/pkg/domain/infra/tunnel/play.go
@@ -20,7 +20,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en
if opts.Annotations != nil {
options.WithAnnotations(opts.Annotations)
}
- options.WithNoHosts(opts.NoHosts)
+ options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
}
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index d56b50fd5..e4c149abf 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -120,6 +120,8 @@ type CtrSpecGenOptions struct {
RestartPolicy string
// NetNSIsHost tells the container to use the host netns
NetNSIsHost bool
+ // UserNSIsHost tells the container to use the host userns
+ UserNSIsHost bool
// SecretManager to access the secrets
SecretsManager *secrets.SecretsManager
// LogDriver which should be used for the container
@@ -133,6 +135,8 @@ type CtrSpecGenOptions struct {
// InitContainerType sets what type the init container is
// Note: When playing a kube yaml, the inti container type will be set to "always" only
InitContainerType string
+ // PodSecurityContext is the security context specified for the pod
+ PodSecurityContext *v1.PodSecurityContext
}
func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) {
@@ -188,7 +192,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
s.InitContainerType = opts.InitContainerType
- setupSecurityContext(s, opts.Container)
+ setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext)
err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy)
if err != nil {
return nil, errors.Wrap(err, "Failed to configure livenessProbe")
@@ -387,8 +391,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
if opts.NetNSIsHost {
s.NetNS.NSMode = specgen.Host
}
- // Always set the userns to host since k8s doesn't have support for userns yet
- s.UserNS.NSMode = specgen.Host
+ if opts.UserNSIsHost {
+ s.UserNS.NSMode = specgen.Host
+ }
// Add labels that come from kube
if len(s.Labels) == 0 {
@@ -531,22 +536,30 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32,
return &hc, nil
}
-func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container) {
- if containerYAML.SecurityContext == nil {
- return
+func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.SecurityContext, podSecurityContext *v1.PodSecurityContext) {
+ if securityContext == nil {
+ securityContext = &v1.SecurityContext{}
+ }
+ if podSecurityContext == nil {
+ podSecurityContext = &v1.PodSecurityContext{}
}
- if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
- s.ReadOnlyFilesystem = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
+
+ if securityContext.ReadOnlyRootFilesystem != nil {
+ s.ReadOnlyFilesystem = *securityContext.ReadOnlyRootFilesystem
}
- if containerYAML.SecurityContext.Privileged != nil {
- s.Privileged = *containerYAML.SecurityContext.Privileged
+ if securityContext.Privileged != nil {
+ s.Privileged = *securityContext.Privileged
}
- if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
- s.NoNewPrivileges = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
+ if securityContext.AllowPrivilegeEscalation != nil {
+ s.NoNewPrivileges = !*securityContext.AllowPrivilegeEscalation
}
- if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil {
+ seopt := securityContext.SELinuxOptions
+ if seopt == nil {
+ seopt = podSecurityContext.SELinuxOptions
+ }
+ if seopt != nil {
if seopt.User != "" {
s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("user:%s", seopt.User))
}
@@ -560,7 +573,7 @@ func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container)
s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("level:%s", seopt.Level))
}
}
- if caps := containerYAML.SecurityContext.Capabilities; caps != nil {
+ if caps := securityContext.Capabilities; caps != nil {
for _, capability := range caps.Add {
s.CapAdd = append(s.CapAdd, string(capability))
}
@@ -568,14 +581,26 @@ func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container)
s.CapDrop = append(s.CapDrop, string(capability))
}
}
- if containerYAML.SecurityContext.RunAsUser != nil {
- s.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
+ runAsUser := securityContext.RunAsUser
+ if runAsUser == nil {
+ runAsUser = podSecurityContext.RunAsUser
}
- if containerYAML.SecurityContext.RunAsGroup != nil {
+ if runAsUser != nil {
+ s.User = fmt.Sprintf("%d", *runAsUser)
+ }
+
+ runAsGroup := securityContext.RunAsGroup
+ if runAsGroup == nil {
+ runAsGroup = podSecurityContext.RunAsGroup
+ }
+ if runAsGroup != nil {
if s.User == "" {
s.User = "0"
}
- s.User = fmt.Sprintf("%s:%d", s.User, *containerYAML.SecurityContext.RunAsGroup)
+ s.User = fmt.Sprintf("%s:%d", s.User, *runAsGroup)
+ }
+ for _, group := range podSecurityContext.SupplementalGroups {
+ s.Groups = append(s.Groups, fmt.Sprintf("%d", group))
}
}
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index fb01dc1e9..216c3357c 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -8,6 +8,7 @@ import (
"net"
"net/url"
"os"
+ "os/user"
"path/filepath"
"strconv"
"strings"
@@ -327,6 +328,11 @@ spec:
name: {{ .Name }}
{{ end }}
{{ end }}
+{{ if .SecurityContext }}
+ securityContext:
+ {{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
+ {{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
+{{ end }}
containers:
{{ with .Ctrs }}
{{ range . }}
@@ -393,6 +399,8 @@ spec:
{{- end }}
{{ if .SecurityContext }}
securityContext:
+ {{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
+ {{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
allowPrivilegeEscalation: true
{{ if .Caps }}
capabilities:
@@ -758,16 +766,19 @@ func withPVCAnnotations(k, v string) pvcOption {
// Pod describes the options a kube yaml can be configured at pod level
type Pod struct {
- Name string
- RestartPolicy string
- Hostname string
- HostNetwork bool
- HostAliases []HostAlias
- Ctrs []*Ctr
- InitCtrs []*Ctr
- Volumes []*Volume
- Labels map[string]string
- Annotations map[string]string
+ Name string
+ RestartPolicy string
+ Hostname string
+ HostNetwork bool
+ HostAliases []HostAlias
+ Ctrs []*Ctr
+ InitCtrs []*Ctr
+ Volumes []*Volume
+ Labels map[string]string
+ Annotations map[string]string
+ SecurityContext bool
+ RunAsUser string
+ RunAsGroup string
}
type HostAlias struct {
@@ -802,6 +813,24 @@ func getPod(options ...podOption) *Pod {
type podOption func(*Pod)
+func withPodSecurityContext(sc bool) podOption {
+ return func(p *Pod) {
+ p.SecurityContext = sc
+ }
+}
+
+func withPodRunAsUser(runAsUser string) podOption {
+ return func(p *Pod) {
+ p.RunAsUser = runAsUser
+ }
+}
+
+func withPodRunAsGroup(runAsGroup string) podOption {
+ return func(p *Pod) {
+ p.RunAsGroup = runAsGroup
+ }
+}
+
func withPodName(name string) podOption {
return func(pod *Pod) {
pod.Name = name
@@ -949,6 +978,8 @@ type Ctr struct {
Env []Env
EnvFrom []EnvFrom
InitCtrType string
+ RunAsUser string
+ RunAsGroup string
}
// getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
@@ -1042,6 +1073,18 @@ func withSecurityContext(sc bool) ctrOption {
}
}
+func withRunAsUser(runAsUser string) ctrOption {
+ return func(c *Ctr) {
+ c.RunAsUser = runAsUser
+ }
+}
+
+func withRunAsGroup(runAsGroup string) ctrOption {
+ return func(c *Ctr) {
+ c.RunAsGroup = runAsGroup
+ }
+}
+
func withCapAdd(caps []string) ctrOption {
return func(c *Ctr) {
c.CapAdd = caps
@@ -1105,8 +1148,12 @@ func withEnvFrom(name, from string, optional bool) ctrOption {
}
}
+func makeCtrNameInPod(pod *Pod, containerName string) string {
+ return fmt.Sprintf("%s-%s", pod.Name, containerName)
+}
+
func getCtrNameInPod(pod *Pod) string {
- return fmt.Sprintf("%s-%s", pod.Name, defaultCtrName)
+ return makeCtrNameInPod(pod, defaultCtrName)
}
type HostPath struct {
@@ -3222,6 +3269,38 @@ invalid kube kind
Expect(ls.OutputToStringArray()).To(HaveLen(1))
})
+ It("podman play kube RunAsUser", func() {
+ ctr1Name := "ctr1"
+ ctr2Name := "ctr2"
+ ctr1 := getCtr(withName(ctr1Name), withSecurityContext(true), withRunAsUser("101"), withRunAsGroup("102"))
+ ctr2 := getCtr(withName(ctr2Name), withSecurityContext(true))
+
+ pod := getPod(
+ withCtr(ctr1),
+ withCtr(ctr2),
+ withPodSecurityContext(true),
+ withPodRunAsUser("103"),
+ withPodRunAsGroup("104"),
+ )
+
+ err := generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ cmd := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ cmd.WaitWithDefaultTimeout()
+ Expect(cmd).Should(Exit(0))
+
+ // we expect the user:group as configured for the container
+ inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "'{{.Config.User}}'", makeCtrNameInPod(pod, ctr1Name)})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.OutputToString()).To(Equal("'101:102'"))
+
+ // we expect the user:group as configured for the pod
+ inspect = podmanTest.Podman([]string{"container", "inspect", "--format", "'{{.Config.User}}'", makeCtrNameInPod(pod, ctr2Name)})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.OutputToString()).To(Equal("'103:104'"))
+ })
+
Describe("verify environment variables", func() {
var maxLength int
BeforeEach(func() {
@@ -3555,6 +3634,55 @@ ENV OPENJ9_JAVA_OPTIONS=%q
inspect.WaitWithDefaultTimeout()
Expect(start).Should(Exit(0))
Expect((inspect.InspectContainerToJSON()[0]).HostConfig.LogConfig.Tag).To(Equal("{{.ImageName}}"))
+ })
+
+ // Check that --userns=auto creates a user namespace
+ It("podman play kube --userns=auto", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+ name := u.Name
+ if name == "root" {
+ name = "containers"
+ }
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ initialUsernsConfig, err := ioutil.ReadFile("/proc/self/uid_map")
+ Expect(err).To(BeNil())
+ if os.Geteuid() != 0 {
+ unshare := podmanTest.Podman([]string{"unshare", "cat", "/proc/self/uid_map"})
+ unshare.WaitWithDefaultTimeout()
+ Expect(unshare).Should(Exit(0))
+ initialUsernsConfig = unshare.Out.Contents()
+ }
+
+ pod := getPod()
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+
+ usernsInCtr := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/proc/self/uid_map"})
+ usernsInCtr.WaitWithDefaultTimeout()
+ Expect(usernsInCtr).Should(Exit(0))
+ // the conversion to string is needed for better error messages
+ Expect(string(usernsInCtr.Out.Contents())).To(Equal(string(initialUsernsConfig)))
+
+ // PodmanNoCache is a workaround for https://github.com/containers/storage/issues/1232
+ kube = podmanTest.PodmanNoCache([]string{"play", "kube", "--replace", "--userns=auto", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+ usernsInCtr = podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/proc/self/uid_map"})
+ usernsInCtr.WaitWithDefaultTimeout()
+ Expect(usernsInCtr).Should(Exit(0))
+ Expect(string(usernsInCtr.Out.Contents())).To(Not(Equal(string(initialUsernsConfig))))
})
})