summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--cmd/podman/common/create.go8
-rw-r--r--cmd/podman/common/create_opts.go1
-rw-r--r--cmd/podman/common/specgen.go2
-rw-r--r--contrib/cirrus/lib.sh8
-rwxr-xr-xcontrib/cirrus/runner.sh19
-rw-r--r--docs/source/markdown/podman-create.1.md27
-rw-r--r--docs/source/markdown/podman-run.1.md26
-rwxr-xr-xhack/get_ci_vm.sh316
-rwxr-xr-xhack/swagger-check106
-rw-r--r--libpod/boltdb_state_internal.go2
-rw-r--r--libpod/container_exec.go27
-rw-r--r--libpod/container_validate.go6
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/in_memory_state.go4
-rw-r--r--libpod/networking_linux.go10
-rw-r--r--libpod/oci_conmon_exec_linux.go124
-rw-r--r--libpod/oci_conmon_linux.go120
-rw-r--r--pkg/api/handlers/compat/containers_start.go2
-rw-r--r--pkg/api/server/register_containers.go6
-rw-r--r--pkg/api/server/register_play.go2
-rw-r--r--pkg/api/server/register_pods.go2
-rw-r--r--pkg/bindings/containers/types.go1
-rw-r--r--pkg/bindings/containers/types_start_options.go16
-rw-r--r--pkg/domain/infra/abi/containers.go14
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/specgen/generate/container_create.go11
-rw-r--r--pkg/specgen/generate/namespaces.go10
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--test/e2e/exec_test.go14
-rw-r--r--test/e2e/run_test.go25
-rw-r--r--test/system/500-networking.bats15
33 files changed, 493 insertions, 450 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 074f2f4e0..b77464bae 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -30,7 +30,7 @@ env:
PRIOR_UBUNTU_NAME: "ubuntu-2004"
# Google-cloud VM Images
- IMAGE_SUFFIX: "c5961289315909632"
+ IMAGE_SUFFIX: "c5501386583441408"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index dda709ecd..220a30a10 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -576,6 +576,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
`If a container with the same name exists, replace it`,
)
+ requiresFlagName := "requires"
+ createFlags.StringSliceVar(
+ &cf.Requires,
+ requiresFlagName, []string{},
+ "Add one or more requirement containers that must be started before this container will start",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers)
+
restartFlagName := "restart"
createFlags.StringVar(
&cf.Restart,
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index a296ef4f1..e14918fe1 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -93,6 +93,7 @@ type ContainerCLIOpts struct {
ReadOnlyTmpFS bool
Restart string
Replace bool
+ Requires []string
Rm bool
RootFS bool
Secrets []string
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index d1b67d963..363a8f5f9 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -486,6 +486,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.ReadOnlyFilesystem = c.ReadOnly
s.ConmonPidFile = c.ConmonPIDFile
+ s.DependencyContainers = c.Requires
+
// TODO
// outside of specgen and oci though
// defaults to true, check spec/storage
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 451a267b3..2cd28e34a 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -13,8 +13,12 @@ set -a
_waserrexit=0
if [[ "$SHELLOPTS" =~ errexit ]]; then _waserrexit=1; fi
set +e # Assumed in F33 for setting global vars
-source /etc/profile
-source /etc/environment
+if [[ -r "/etc/automation_environment" ]]; then
+ source /etc/automation_environment
+else # prior to automation library v2.0, this was necessary
+ source /etc/profile
+ source /etc/environment
+fi
if [[ -r "/etc/ci_environment" ]]; then source /etc/ci_environment; fi
USER="$(whoami)"
HOME="$(getent passwd $USER | cut -d : -f 6)"
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index f52e107cc..8d3a6b987 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -269,13 +269,18 @@ function _run_release() {
}
logformatter() {
- # Use similar format as human-friendly task name from .cirrus.yml
- # shellcheck disable=SC2154
- output_name="$TEST_FLAVOR-$PODBIN_NAME-$DISTRO_NV-$PRIV_NAME-$TEST_ENVIRON"
- # Requires stdin and stderr combined!
- cat - \
- |& awk --file "${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/timestamp.awk" \
- |& "${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/logformatter" "$output_name"
+ if [[ "$CI" == "true" ]]; then
+ # Use similar format as human-friendly task name from .cirrus.yml
+ # shellcheck disable=SC2154
+ output_name="$TEST_FLAVOR-$PODBIN_NAME-$DISTRO_NV-$PRIV_NAME-$TEST_ENVIRON"
+ # Requires stdin and stderr combined!
+ cat - \
+ |& awk --file "${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/timestamp.awk" \
+ |& "${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/logformatter" "$output_name"
+ else
+ # Assume script is run by a human, they want output immediatly
+ cat -
+ fi
}
# Handle local|remote integration|system testing in a uniform way
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 927b3df33..fcd5f3e3f 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -781,6 +781,12 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r
If another container with the same name already exists, replace and remove it. The default is **false**.
+#### **\-\-requires**=**container**
+
+Specify one or more requirements.
+A requirement is a dependency container that will be started before this container.
+Containers can be specified by name or ID, with multiple containers being separated by commas.
+
#### **\-\-restart**=*policy*
Restart policy to follow when containers exit.
@@ -1250,6 +1256,25 @@ $ podman create --tz=Asia/Shanghai alpine date
$ podman create --tz=US/Eastern alpine date
```
+### Adding dependency containers
+
+Podman will make sure the first container, container1, is running before the second container (container2) is started.
+
+```
+$ podman create --name container1 -t -i fedora bash
+$ podman create --name container2 --requires container1 -t -i fedora bash
+$ podman start --attach container2
+```
+
+Multiple containers can be required.
+
+```
+$ podman create --name container1 -t -i fedora bash
+$ podman create --name container2 -t -i fedora bash
+$ podman create --name container3 --requires container1,container2 -t -i fedora bash
+$ podman start --attach container3
+```
+
### Rootless Containers
Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
@@ -1297,7 +1322,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
-**podman**(1), **podman-secret**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1),
+**podman**(1), **podman-secret**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start*(1), **podman-kill**(1), **podman-stop**(1),
**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**.
## HISTORY
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 4c096ecfe..3fad9bf64 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -825,6 +825,12 @@ If container is running in **\-\-read-only** mode, then mount a read-write tmpfs
If another container with the same name already exists, replace and remove it. The default is **false**.
+#### **\-\-requires**=**container**
+
+Specify one or more requirements.
+A requirement is a dependency container that will be started before this container.
+Containers can be specified by name or ID, with multiple containers being separated by commas.
+
#### **\-\-restart**=*policy*
Restart policy to follow when containers exit.
@@ -1612,6 +1618,24 @@ $ podman run --tz=Asia/Shanghai alpine date
$ podman run --tz=US/Eastern alpine date
```
+### Adding dependency containers
+
+The first container, container1, is not started initially, but must be running before container2 will start.
+The `podman run` command will start the container automatically before starting container2.
+
+```
+$ podman create --name container1 -t -i fedora bash
+$ podman run --name container2 --requires container1 -t -i fedora bash
+```
+
+Multiple containers can be required.
+
+```
+$ podman create --name container1 -t -i fedora bash
+$ podman create --name container2 -t -i fedora bash
+$ podman run --name container3 --requires container1,container2 -t -i fedora bash
+```
+
### Rootless Containers
Podman runs as a non root user on most systems. This feature requires that a new enough version of **shadow-utils**
@@ -1657,7 +1681,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
-**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1),
+**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start**(1), **podman-kill**(1), **podman-stop**(1),
**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**.
## HISTORY
diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh
index 4f6c42a06..1a4804857 100755
--- a/hack/get_ci_vm.sh
+++ b/hack/get_ci_vm.sh
@@ -3,270 +3,68 @@
#
# For help and usage information, simply execute the script w/o any arguments.
#
-# This script is intended to be run by podman developers who need to debug
-# problems specifically related to Cirrus-CI automated testing. However,
-# because it's only loosely coupled to the `.cirrus.yml` configuration, it must
-# orchestrate VMs in GCP directly. This means users need to have
-# pre-authorization (access) to manipulate google-cloud resources. Additionally,
-# there are no guarantees it will remain in-sync with other automation-related
-# scripts. Therefore it may not always function for everybody in every
-# future scenario without updates/modifications/tweaks.
-#
-# When successful, you will end up connected to a GCP VM with with a clone of
-# the upstream podman repository 'master' branch, using a remote named 'origin'.
-# If you want to customize this behavior, you will want to use a "hook" script.
-# Please use this example carefully, since git setups vary by person, you
-# will probably need to make local edits.
-#
-# https://gist.github.com/cevich/626a0790c0b476d5cd2a5a76fbdae0a1
+# This script is intended to be run by Red Hat podman developers who need
+# to debug problems specifically related to Cirrus-CI automated testing.
+# It requires that you have been granted prior access to create VMs in
+# google-cloud. For non-Red Hat contributors, VMs are available as-needed,
+# with supervision upon request.
set -e
-RED="\e[1;31m"
-YEL="\e[1;32m"
-NOR="\e[0m"
-USAGE_WARNING="
-${YEL}WARNING: This will not work without podman,${NOR}
- ${YEL}and prior authorization to use the libpod GCP project.${NOR}
-"
-# These values come from .cirrus.yml gce_instance clause
-ZONE="${ZONE:-us-central1-a}"
-CPUS="2"
-MEMORY="4Gb"
-DISK="200"
-PROJECT="libpod-218412"
-GOSRC="/var/tmp/go/src/github.com/containers/podman"
-GIT_REPO="https://github.com/containers/podman.git"
-
-# Container image with necessary runtime elements
-GCLOUD_IMAGE="${GCLOUD_IMAGE:-docker.io/google/cloud-sdk:alpine}"
-GCLOUD_CFGDIR=".config/gcloud"
-
-SCRIPT_FILENAME=$(basename ${BASH_SOURCE[0]})
-HOOK_FILENAME="hook_${SCRIPT_FILENAME}"
+SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}")
+SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH")
+REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../")
-# Shared tmp directory between container and us
-TMPDIR=$(mktemp -d --tmpdir ${SCRIPT_FILENAME}_tmpdir_XXXXXX)
-
-show_usage() {
- echo -e "\n${RED}ERROR: $1${NOR}"
- echo -e "${YEL}Usage: $SCRIPT_FILENAME <image_name>${NOR}"
- echo ""
- if [[ -r ".cirrus.yml" ]]
- then
- echo -e "${YEL}Some possible image_name values (from .cirrus.yml):${NOR}"
- image_hints
- echo ""
- echo -e "${YEL}Optional:${NOR} If a $HOME/$GCLOUD_CFGDIR/$HOOK_FILENAME executable exists during"
- echo "VM creation, it will be executed remotely after cloning"
- echo "$GIT_REPO. The"
- echo "current local working branch name and commit ID, will be provided as"
- echo "it's arguments."
+# Help detect if we were called by get_ci_vm container
+GET_CI_VM="${GET_CI_VM:-0}"
+in_get_ci_vm() {
+ if ((GET_CI_VM==0)); then
+ echo "Error: $1 is not intended for use in this context"
+ exit 2
fi
- exit 1
-}
-
-LIBPODROOT=$(realpath "$(dirname ${BASH_SOURCE[0]})/../")
-# else: Assume $PWD is the root of the libpod repository
-[[ "$LIBPODROOT" != "/" ]] || \
- show_usage "Must execute script from within clone of containers/podman repo."
-
-[[ "$UID" -ne 0 ]] || \
- show_usage "Must execute script as a regular (non-root) user."
-
-[[ "${LIBPODROOT#$HOME}" != "$LIBPODROOT" ]] || \
- show_usage "Clone of containers/podman must be a subdirectory of \$HOME ($HOME)"
-
-# Disable SELinux labeling to allow read-only mounting of repository files
-PGCLOUD="podman run -it --rm --security-opt label=disable -v $TMPDIR:$TMPDIR -v $HOME/.config/gcloud:/root/.config/gcloud -v $HOME/.config/gcloud/ssh:/root/.ssh -v $LIBPODROOT:$LIBPODROOT:ro $GCLOUD_IMAGE gcloud --configuration=libpod --project=$PROJECT"
-SCP_CMD="$PGCLOUD compute scp"
-
-showrun() {
- echo '+ '$(printf " %q" "$@") > /dev/stderr
- echo ""
- "$@"
-}
-
-cleanup() {
- RET=$?
- set +e
- wait
-
- # set GCLOUD_DEBUG to leave tmpdir behind for postmortem
- # shellcheck disable=SC2154
- test -z "$GCLOUD_DEBUG" && rm -rf $TMPDIR
-
- # Not always called from an exit handler, but should always exit when called
- exit $RET
-}
-trap cleanup EXIT
-
-delvm() {
- echo -e "\n"
- echo -e "\n${YEL}Offering to Delete $VMNAME${NOR}"
- echo -e "${RED}(Deletion might take a minute or two)${NOR}"
- echo -e "${YEL}Note: It's safe to answer N, then re-run script again later.${NOR}"
- showrun $CLEANUP_CMD # prompts for Yes/No
- cleanup
-}
-
-get_env_vars() {
- # Deal with both YAML and embedded shell-like substitutions in values
- # if substitution fails, fall back to printing naked env. var as-is.
- python3 -c '
-import sys,yaml,re
-env=yaml.load(open(".cirrus.yml"), Loader=yaml.SafeLoader)["env"]
-dollar_env_var=re.compile(r"\$(\w+)")
-dollarcurly_env_var=re.compile(r"\$\{(\w+)\}")
-class ReIterKey(dict):
- def __missing__(self, key):
- # Cirrus-CI provides some runtime-only env. vars. Avoid
- # breaking this hack-script if/when any are present in YAML
- return "${0}".format(key)
-rep=r"{\1}" # Convert env vars markup to -> str.format_map(re_iter_key) markup
-out=ReIterKey()
-for k,v in env.items():
- if "ENCRYPTED" not in str(v) and bool(v):
- out[k]=dollar_env_var.sub(rep, dollarcurly_env_var.sub(rep, str(v)))
-for k,v in out.items():
- sys.stdout.write("{0}=\"{1}\"\n".format(k, str(v).format_map(out)))
- '
-}
-
-image_hints() {
- get_env_vars | fgrep '_CACHE_IMAGE_NAME' | awk -F "=" '{print $2}'
}
-unset VM_IMAGE_NAME
-unset VMNAME
-unset CREATE_CMD
-unset SSH_CMD
-unset CLEANUP_CMD
-declare -xa ENVS
-parse_args(){
- local arg
- echo -e "$USAGE_WARNING"
-
- if [[ "$USER" =~ "root" ]]
- then
- show_usage "This script must be run as a regular user."
- fi
-
- [[ "$#" -eq 1 ]] || \
- show_usage "Must specify a VM Image name to use, and the test flavor."
-
- VM_IMAGE_NAME="$1"
-
- # Word-splitting is desirable in this case.
- # Values are used literally (with '=') as args to future `env` command.
- # get_env_vars() will take care of properly quoting it's output.
- # shellcheck disable=SC2207,SC2191
- ENVS=(
- $(get_env_vars)
- VM_IMAGE_NAME="$VM_IMAGE_NAME"
- UPSTREAM_REMOTE="upstream"
- )
-
- VMNAME="${VMNAME:-${USER}-${VM_IMAGE_NAME}}"
-
- CREATE_CMD="$PGCLOUD compute instances create --zone=$ZONE --image=${VM_IMAGE_NAME} --custom-cpu=$CPUS --custom-memory=$MEMORY --boot-disk-size=$DISK --labels=in-use-by=$USER $VMNAME"
-
- SSH_CMD="$PGCLOUD compute ssh root@$VMNAME"
-
- CLEANUP_CMD="$PGCLOUD compute instances delete --zone $ZONE --delete-disks=all $VMNAME"
-}
-
-# Returns true if user has run an 'init' and has a valid token for
-# the specific project-id and named-configuration arguments in $PGCLOUD.
-function has_valid_credentials() {
- if $PGCLOUD info |& grep -Eq 'Account:.*None'; then
- return 1
- fi
-
- # It's possible for 'gcloud info' to list expired credentials,
- # e.g. 'ERROR: ... invalid grant: Bad Request'
- if $PGCLOUD auth print-access-token |& grep -q 'ERROR'; then
- return 1
- fi
-
- return 0
-}
-
-##### main
-
-[[ "${LIBPODROOT%%${LIBPODROOT##$HOME}}" == "$HOME" ]] || \
- show_usage "Repo clone must be sub-dir of $HOME"
-
-cd "$LIBPODROOT"
-
-parse_args "$@"
-mkdir -p $TMPDIR/.ssh
-mkdir -p {$HOME,$TMPDIR}/.config/gcloud/ssh
-chmod 700 {$HOME,$TMPDIR}/.config/gcloud/ssh $TMPDIR/.ssh
-
-echo -e "\n${YEL}Pulling gcloud image...${NOR}"
-podman pull $GCLOUD_IMAGE
-
-if ! has_valid_credentials
-then
- echo -e "\n${YEL}WARNING: Can't find gcloud configuration for libpod, running init.${NOR}"
- echo -e " ${RED}Please choose \"#1: Re-initialize\" and \"login\" if asked.${NOR}"
- showrun $PGCLOUD init --project=$PROJECT --console-only --skip-diagnostics
-
- # Verify it worked (account name == someone@example.com)
- $PGCLOUD info > $TMPDIR/gcloud-info-after-init
- if egrep -q "Account:.*None" $TMPDIR/gcloud-info-after-init
- then
- echo -e "${RED}ERROR: Could not initialize libpod configuration in gcloud.${NOR}"
- exit 5
- fi
-
- # If this is the only config, make it the default to avoid
- # persistent warnings from gcloud about there being no default.
- [[ -r "$HOME/.config/gcloud/configurations/config_default" ]] || \
- ln "$HOME/.config/gcloud/configurations/config_libpod" \
- "$HOME/.config/gcloud/configurations/config_default"
+# get_ci_vm APIv1 container entrypoint calls into this script
+# to obtain required repo. specific configuration options.
+if [[ "$1" == "--config" ]]; then
+ in_get_ci_vm "$1"
+ cat <<EOF
+DESTDIR="/var/tmp/go/src/github.com/containers/podman"
+UPSTREAM_REPO="https://github.com/containers/podman.git"
+CI_ENVFILE="/etc/ci_environment"
+GCLOUD_PROJECT="libpod-218412"
+GCLOUD_IMGPROJECT="libpod-218412"
+GCLOUD_CFG="libpod"
+GCLOUD_ZONE="${GCLOUD_ZONE:-us-central1-a}"
+GCLOUD_CPUS="2"
+GCLOUD_MEMORY="4Gb"
+GCLOUD_DISK="200"
+EOF
+elif [[ "$1" == "--setup" ]]; then
+ in_get_ci_vm "$1"
+ # get_ci_vm container entrypoint calls us with this option on the
+ # Cirrus-CI environment instance, to perform repo.-specific setup.
+ cd $REPO_DIRPATH
+ echo "+ Loading ./contrib/cirrus/lib.sh" > /dev/stderr
+ source ./contrib/cirrus/lib.sh
+ echo "+ Mimicking .cirrus.yml clone_script and build_task" > /dev/stderr
+ make install.tools
+ make vendor
+ make podman
+ make podman-remote
+ echo "+ Running environment setup" > /dev/stderr
+ ./contrib/cirrus/setup_environment.sh
+else
+ # Create and access VM for specified Cirrus-CI task
+ mkdir -p $HOME/.config/gcloud/ssh
+ podman run -it --rm \
+ --tz=local \
+ -e NAME="$USER" \
+ -e SRCDIR=/src \
+ -e GCLOUD_ZONE="$GCLOUD_ZONE" \
+ -e DEBUG="${DEBUG:-0}" \
+ -v $REPO_DIRPATH:/src:O \
+ -v $HOME/.config/gcloud:/root/.config/gcloud:z \
+ -v $HOME/.config/gcloud/ssh:/root/.ssh:z \
+ quay.io/libpod/get_ci_vm:latest "$@"
fi
-
-trap delvm EXIT # Allow deleting VM if CTRL-C during create
-echo -e "\n${YEL}Trying to creating a VM named $VMNAME${NOR}\n${YEL}in GCE region/zone $ZONE${NOR}"
-echo -e "For faster terminal access, export ZONE='<something-closer>'"
-echo -e 'Zone-list at: https://cloud.google.com/compute/docs/regions-zones/\n'
-if showrun $CREATE_CMD; then # Freshly created VM needs initial setup
-
- echo -e "\n${YEL}Waiting up to 30s for ssh port to open${NOR}"
- ATTEMPTS=10
- trap "exit 1" INT
- while ((ATTEMPTS)) && ! $SSH_CMD --command "true"; do
- let "ATTEMPTS--"
- echo -e "${RED}Nope, not yet.${NOR}"
- sleep 3s
- done
- trap - INT
- if ! ((ATTEMPTS)); then
- echo -e "\n${RED}Failed${NOR}"
- exit 7
- fi
- echo -e "${YEL}Got it. Cloning upstream repository as a starting point.${NOR}"
-
- showrun $SSH_CMD -- "mkdir -p $GOSRC"
- showrun $SSH_CMD -- "git clone --progress $GIT_REPO $GOSRC"
-
- if [[ -x "$HOME/$GCLOUD_CFGDIR/$HOOK_FILENAME" ]]; then
- echo -e "\n${YEL}Copying hook to VM and executing (ignoring errors).${NOR}"
- $PGCLOUD compute scp "/root/$GCLOUD_CFGDIR/$HOOK_FILENAME" root@$VMNAME:.
- if ! showrun $SSH_CMD -- "cd $GOSRC && bash /root/$HOOK_FILENAME $(git branch --show-current) $(git rev-parse HEAD)"; then
- echo "-e ${RED}Hook exited: $?${NOR}"
- fi
- fi
-fi
-
-echo -e "\n${YEL}Generating connection script for $VMNAME.${NOR}"
-echo -e "Note: Script can be re-used in another terminal if needed."
-echo -e "${RED}(option to delete VM presented upon exiting).${NOR}"
-# TODO: This is fairly fragile, specifically the quoting for the remote command.
-echo '#!/bin/bash' > $TMPDIR/ssh
-echo "$SSH_CMD -- -t 'cd $GOSRC && exec env ${ENVS[*]} bash -il'" >> $TMPDIR/ssh
-chmod +x $TMPDIR/ssh
-
-showrun $TMPDIR/ssh
diff --git a/hack/swagger-check b/hack/swagger-check
index d20318305..646cbcb84 100755
--- a/hack/swagger-check
+++ b/hack/swagger-check
@@ -59,11 +59,6 @@ says 'libpod'.
OPTIONS:
- --pedantic Compare operation names (the last part of swagger comment).
- There are far too many of these inconsistencies to allow us
- to enable this by default, but it still might be a useful
- check in some circumstances.
-
-v, --verbose show verbose progress indicators
-n, --dry-run make no actual changes
@@ -75,7 +70,6 @@ END_USAGE
}
# Command-line options. Note that this operates directly on @ARGV !
-our $pedantic;
our $debug = 0;
our $force = 0;
our $verbose = 0;
@@ -83,8 +77,6 @@ our $NOT = ''; # print "blahing the blah$NOT\n" if $debug
sub handle_opts {
use Getopt::Long;
GetOptions(
- 'pedantic' => \$pedantic,
-
'debug!' => \$debug,
'dry-run|n!' => sub { $NOT = ' [NOT]' },
'force' => \$force,
@@ -225,26 +217,14 @@ sub handle_handle {
my $tag = ($endpoint =~ /(libpod)/ ? $1 : 'compat');
#
- # Determine the OPERATION. *** NOTE: This is mostly useless! ***
- # In an ideal world the swagger comment would match actual function call;
- # in reality there are over thirty mismatches. Use --pedantic to see.
+ # Determine the OPERATION. Done in a helper function because there
+ # are a lot of complicated special cases.
#
- my $operation = '';
- if ($line =~ /(generic|handlers|compat)\.(\w+)/) {
- $operation = lcfirst $2;
- if ($endpoint =~ m!/libpod/! && $operation !~ /^libpod/) {
- $operation = 'libpod' . ucfirst $operation;
- }
- }
- elsif ($line =~ /(libpod)\.(\w+)/) {
- $operation = "$1$2";
- }
+ my $operation = operation_name($method, $endpoint);
# Special case: the following endpoints all get a custom tag
if ($endpoint =~ m!/(pods|manifests)/!) {
$tag = $1;
- $operation =~ s/^libpod//;
- $operation = lcfirst $operation;
}
# Special case: anything related to 'events' gets a system tag
@@ -252,11 +232,6 @@ sub handle_handle {
$tag = 'system';
}
- # Special case: /changes is libpod even though it says compat
- if ($endpoint =~ m!/changes!) {
- $tag = 'libpod';
- }
-
state $previous_path; # Previous path name, to avoid dups
#
@@ -269,13 +244,6 @@ sub handle_handle {
my $actual = $actual[0];
- # By default, don't compare the operation: there are far too many
- # mismatches here.
- if (! $pedantic) {
- $actual =~ s/\s+\S+\s*$//;
- $expect =~ s/\s+\S+\s*$//;
- }
-
# (Ignore whitespace discrepancies)
(my $a_trimmed = $actual) =~ s/\s+/ /g;
@@ -314,4 +282,72 @@ sub handle_handle {
return;
}
+
+####################
+# operation_name # Given a method + endpoint, return the swagger operation
+####################
+sub operation_name {
+ my ($method, $endpoint) = @_;
+
+ # /libpod/foo/bar -> (libpod, foo, bar)
+ my @endpoints = grep { /\S/ } split '/', $endpoint;
+
+ # /libpod endpoints -> add 'Libpod' to end, e.g. PodStatsLibpod
+ my $Libpod = '';
+ my $main = shift(@endpoints);
+ if ($main eq 'libpod') {
+ $Libpod = ucfirst($main);
+ $main = shift(@endpoints);
+ }
+ $main =~ s/s$//; # e.g. Volumes -> Volume
+
+ # Next path component is an optional action:
+ # GET /containers/json -> ContainerList
+ # DELETE /libpod/containers/{name} -> ContainerDelete
+ # GET /libpod/containers/{name}/logs -> ContainerLogsLibpod
+ my $action = shift(@endpoints) || 'list';
+ $action = 'list' if $action eq 'json';
+ $action = 'delete' if $method eq 'DELETE';
+
+ # Anything with {id}, {name}, {name:..} may have a following component
+ if ($action =~ m!\{.*\}!) {
+ $action = shift(@endpoints) || 'inspect';
+ $action = 'inspect' if $action eq 'json';
+ }
+
+ # All sorts of special cases
+ if ($action eq 'df') {
+ $action = 'dataUsage';
+ }
+ # Grrrrrr, this one is annoying: some operations get an extra 'All'
+ elsif ($action =~ /^(delete|get|stats)$/ && $endpoint !~ /\{/) {
+ $action .= "All";
+ $main .= 's' if $main eq 'container';
+ }
+ # No real way to used MixedCase in an endpoint, so we have to hack it here
+ elsif ($action eq 'showmounted') {
+ $action = 'showMounted';
+ }
+ # Ping is a special endpoint, and even if /libpod/_ping, no 'Libpod'
+ elsif ($main eq '_ping') {
+ $main = 'system';
+ $action = 'ping';
+ $Libpod = '';
+ }
+ # Top-level compat endpoints
+ elsif ($main =~ /^(build|commit)$/) {
+ $main = 'image';
+ $action = $1;
+ }
+ # Top-level system endpoints
+ elsif ($main =~ /^(auth|event|info|version)$/) {
+ $main = 'system';
+ $action = $1;
+ $action .= 's' if $action eq 'event';
+ }
+
+ return "\u${main}\u${action}$Libpod";
+}
+
+
1;
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index d4994334f..f63876c14 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -919,7 +919,7 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
return err
}
if len(deps) != 0 {
- return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
+ return errors.Wrapf(define.ErrDepExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
}
if err := ctrBucket.DeleteBucket(ctrID); err != nil {
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index bb43287d9..8d8ed14aa 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -696,6 +696,24 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er
return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it is not running", c.ID(), session.ID())
}
+ // The exec session may have exited since we last updated.
+ // Needed to prevent race conditions around short-running exec sessions.
+ running, err := c.ociRuntime.ExecUpdateStatus(c, session.ID())
+ if err != nil {
+ return err
+ }
+ if !running {
+ session.State = define.ExecStateStopped
+
+ if err := c.save(); err != nil {
+ logrus.Errorf("Error saving state of container %s: %v", c.ID(), err)
+ }
+
+ return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it has stopped", c.ID(), session.ID())
+ }
+
+ // Make sure the exec session is still running.
+
return c.ociRuntime.ExecAttachResize(c, sessionID, newSize)
}
@@ -720,8 +738,13 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
logrus.Debugf("Sending resize events to exec session %s", sessionID)
for resizeRequest := range resize {
if err := c.ExecResize(sessionID, resizeRequest); err != nil {
- // Assume the exec session went down.
- logrus.Warnf("Error resizing exec session %s: %v", sessionID, err)
+ if errors.Cause(err) == define.ErrExecSessionStateInvalid {
+ // The exec session stopped
+ // before we could resize.
+ logrus.Infof("Missed resize on exec session %s, already stopped", sessionID)
+ } else {
+ logrus.Warnf("Error resizing exec session %s: %v", sessionID, err)
+ }
return
}
}
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index 245121a91..aae96ae85 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -126,5 +126,11 @@ func (c *Container) validate() error {
}
}
+ // If User in the OCI spec is set, require that c.config.User is set for
+ // security reasons (a lot of our code relies on c.config.User).
+ if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
+ return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly")
+ }
+
return nil
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 2e85454b2..e19ac6a27 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -31,6 +31,10 @@ var (
// not exist.
ErrNoSuchExecSession = errors.New("no such exec session")
+ // ErrDepExists indicates that the current object has dependencies and
+ // cannot be removed before them.
+ ErrDepExists = errors.New("dependency exists")
+
// ErrNoAliases indicates that the container does not have any network
// aliases.
ErrNoAliases = errors.New("no aliases for container")
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 3875878ed..df45f8e73 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -391,7 +391,7 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
deps, ok := s.ctrDepends[ctr.ID()]
if ok && len(deps) != 0 {
depsStr := strings.Join(deps, ", ")
- return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
+ return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
// Ensure we don't have active exec sessions
@@ -1497,7 +1497,7 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
deps, ok := s.ctrDepends[ctr.ID()]
if ok && len(deps) != 0 {
depsStr := strings.Join(deps, ", ")
- return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
+ return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
// Ensure we don't have active exec sessions
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 157c85431..3c4014c73 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -411,6 +411,16 @@ func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
}
}
+ // The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put
+ // /usr/sbin in $PATH for rootless users. This will break rootless cni completely.
+ // We might break existing users and we cannot expect everyone to change their $PATH so
+ // lets add /usr/sbin to $PATH ourselves.
+ path = os.Getenv("PATH")
+ if !strings.Contains(path, "/usr/sbin") {
+ path = path + ":/usr/sbin"
+ os.Setenv("PATH", path)
+ }
+
rootlessCNINS = &rootlessCNI{
ns: ns,
dir: cniDir,
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index 173edba2b..b43316951 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -2,18 +2,23 @@ package libpod
import (
"fmt"
+ "io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
+ "strings"
"syscall"
"time"
+ "github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/containers/podman/v3/pkg/lookup"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
@@ -654,3 +659,122 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
}
}
}
+
+// prepareProcessExec returns the path of the process.json used in runc exec -p
+// caller is responsible to close the returned *os.File if needed.
+func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) {
+ f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-")
+ if err != nil {
+ return nil, err
+ }
+ pspec := new(spec.Process)
+ if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil {
+ return nil, err
+ }
+ pspec.SelinuxLabel = c.config.ProcessLabel
+ pspec.Args = options.Cmd
+
+ // We need to default this to false else it will inherit terminal as true
+ // from the container.
+ pspec.Terminal = false
+ if options.Terminal {
+ pspec.Terminal = true
+ }
+ if len(env) > 0 {
+ pspec.Env = append(pspec.Env, env...)
+ }
+
+ if options.Cwd != "" {
+ pspec.Cwd = options.Cwd
+ }
+
+ var addGroups []string
+ var sgids []uint32
+
+ // if the user is empty, we should inherit the user that the container is currently running with
+ user := options.User
+ if user == "" {
+ logrus.Debugf("Set user to %s", c.config.User)
+ user = c.config.User
+ addGroups = c.config.Groups
+ }
+
+ overrides := c.getUserOverrides()
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(addGroups) > 0 {
+ sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, overrides)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error looking up supplemental groups for container %s exec session %s", c.ID(), sessionID)
+ }
+ }
+
+ // If user was set, look it up in the container to get a UID to use on
+ // the host
+ if user != "" || len(sgids) > 0 {
+ if user != "" {
+ for _, sgid := range execUser.Sgids {
+ sgids = append(sgids, uint32(sgid))
+ }
+ }
+ processUser := spec.User{
+ UID: uint32(execUser.Uid),
+ GID: uint32(execUser.Gid),
+ AdditionalGids: sgids,
+ }
+
+ pspec.User = processUser
+ }
+
+ ctrSpec, err := c.specFromState()
+ if err != nil {
+ return nil, err
+ }
+
+ allCaps, err := capabilities.BoundingSet()
+ if err != nil {
+ return nil, err
+ }
+ if options.Privileged {
+ pspec.Capabilities.Bounding = allCaps
+ } else {
+ pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
+ }
+ if execUser.Uid == 0 {
+ pspec.Capabilities.Effective = pspec.Capabilities.Bounding
+ pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
+ pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
+ pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
+ } else {
+ if user == c.config.User {
+ pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective
+ }
+ }
+
+ hasHomeSet := false
+ for _, s := range pspec.Env {
+ if strings.HasPrefix(s, "HOME=") {
+ hasHomeSet = true
+ break
+ }
+ }
+ if !hasHomeSet {
+ pspec.Env = append(pspec.Env, fmt.Sprintf("HOME=%s", execUser.Home))
+ }
+
+ processJSON, err := json.Marshal(pspec)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
+ return nil, err
+ }
+ return f, nil
+}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 1c7089e5d..f26ca67ce 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -22,7 +22,6 @@ import (
"text/template"
"time"
- "github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
conmonConfig "github.com/containers/conmon/runner/config"
"github.com/containers/podman/v3/libpod/define"
@@ -30,7 +29,6 @@ import (
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
"github.com/containers/podman/v3/pkg/errorhandling"
- "github.com/containers/podman/v3/pkg/lookup"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
@@ -1195,124 +1193,6 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
return nil
}
-// prepareProcessExec returns the path of the process.json used in runc exec -p
-// caller is responsible to close the returned *os.File if needed.
-func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) {
- f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-")
- if err != nil {
- return nil, err
- }
- pspec := new(spec.Process)
- if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil {
- return nil, err
- }
- pspec.SelinuxLabel = c.config.ProcessLabel
- pspec.Args = options.Cmd
-
- // We need to default this to false else it will inherit terminal as true
- // from the container.
- pspec.Terminal = false
- if options.Terminal {
- pspec.Terminal = true
- }
- if len(env) > 0 {
- pspec.Env = append(pspec.Env, env...)
- }
-
- if options.Cwd != "" {
- pspec.Cwd = options.Cwd
- }
-
- var addGroups []string
- var sgids []uint32
-
- // if the user is empty, we should inherit the user that the container is currently running with
- user := options.User
- if user == "" {
- user = c.config.User
- addGroups = c.config.Groups
- }
-
- overrides := c.getUserOverrides()
- execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
- if err != nil {
- return nil, err
- }
-
- if len(addGroups) > 0 {
- sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, overrides)
- if err != nil {
- return nil, errors.Wrapf(err, "error looking up supplemental groups for container %s exec session %s", c.ID(), sessionID)
- }
- }
-
- // If user was set, look it up in the container to get a UID to use on
- // the host
- if user != "" || len(sgids) > 0 {
- if user != "" {
- for _, sgid := range execUser.Sgids {
- sgids = append(sgids, uint32(sgid))
- }
- }
- processUser := spec.User{
- UID: uint32(execUser.Uid),
- GID: uint32(execUser.Gid),
- AdditionalGids: sgids,
- }
-
- pspec.User = processUser
- }
-
- ctrSpec, err := c.specFromState()
- if err != nil {
- return nil, err
- }
-
- allCaps, err := capabilities.BoundingSet()
- if err != nil {
- return nil, err
- }
- if options.Privileged {
- pspec.Capabilities.Bounding = allCaps
- } else {
- pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
- }
- if execUser.Uid == 0 {
- pspec.Capabilities.Effective = pspec.Capabilities.Bounding
- pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
- pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
- pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
- } else {
- if user == c.config.User {
- pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective
- }
- }
-
- hasHomeSet := false
- for _, s := range pspec.Env {
- if strings.HasPrefix(s, "HOME=") {
- hasHomeSet = true
- break
- }
- }
- if !hasHomeSet {
- pspec.Env = append(pspec.Env, fmt.Sprintf("HOME=%s", execUser.Home))
- }
-
- processJSON, err := json.Marshal(pspec)
- if err != nil {
- return nil, err
- }
-
- if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
- return nil, err
- }
- return f, nil
-}
-
// configureConmonEnv gets the environment values to add to conmon's exec struct
// TODO this may want to be less hardcoded/more configurable in the future
func (r *ConmonOCIRuntime) configureConmonEnv(ctr *Container, runtimeDir string) ([]string, []*os.File) {
diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go
index 391aa752d..f1ed1b2b8 100644
--- a/pkg/api/handlers/compat/containers_start.go
+++ b/pkg/api/handlers/compat/containers_start.go
@@ -42,7 +42,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
- if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil {
+ if err := con.Start(r.Context(), true); err != nil {
utils.InternalServerError(w, err)
return
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 3bf3e4e11..c2bb44c8f 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -774,7 +774,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/prune"), s.APIHandler(compat.PruneContainers)).Methods(http.MethodPost)
- // swagger:operation GET /libpod/containers/showmounted libpod ShowMountedContainersLibpod
+ // swagger:operation GET /libpod/containers/showmounted libpod ContainerShowMountedLibpod
// ---
// tags:
// - containers
@@ -1468,8 +1468,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost)
- // swagger:operation GET /containers/{name}/changes libpod ContainerChangesLibpod
- // swagger:operation GET /libpod/containers/{name}/changes compat ContainerChanges
+ // swagger:operation GET /containers/{name}/changes compat ContainerChanges
+ // swagger:operation GET /libpod/containers/{name}/changes libpod ContainerChangesLibpod
// ---
// tags:
// - containers
diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go
index b9d3349be..d21029db5 100644
--- a/pkg/api/server/register_play.go
+++ b/pkg/api/server/register_play.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
- // swagger:operation POST /libpod/play/kube libpod KubePlayLibpod
+ // swagger:operation POST /libpod/play/kube libpod PlayKubeLibpod
// ---
// tags:
// - containers
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 226cbce11..5944b98fd 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -305,7 +305,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/pods/{name}/top"), s.APIHandler(libpod.PodTop)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/pods/stats pods PodStatsLibpod
+ // swagger:operation GET /libpod/pods/stats pods PodStatsAllLibpod
// ---
// tags:
// - pods
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index f63e35bf1..0d22c32f8 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -154,6 +154,7 @@ type RestartOptions struct {
// StartOptions are optional options for starting containers
type StartOptions struct {
DetachKeys *string
+ Recursive *bool
}
//go:generate go run ../generator/generator.go StatsOptions
diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go
index f8ba29623..d419c755c 100644
--- a/pkg/bindings/containers/types_start_options.go
+++ b/pkg/bindings/containers/types_start_options.go
@@ -35,3 +35,19 @@ func (o *StartOptions) GetDetachKeys() string {
}
return *o.DetachKeys
}
+
+// WithRecursive
+func (o *StartOptions) WithRecursive(value bool) *StartOptions {
+ v := &value
+ o.Recursive = v
+ return o
+}
+
+// GetRecursive
+func (o *StartOptions) GetRecursive() bool {
+ var recursive bool
+ if o.Recursive == nil {
+ return recursive
+ }
+ return *o.Recursive
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 24261e5ed..6f8845f10 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -585,7 +585,7 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string,
}
// If the container is in a pod, also set to recursively start dependencies
- err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "")
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false)
if err != nil && errors.Cause(err) != define.ErrDetach {
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
}
@@ -708,7 +708,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
ctrRunning := ctrState == define.ContainerStateRunning
if options.Attach {
- err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "")
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning)
if errors.Cause(err) == define.ErrDetach {
// User manually detached
// Exit cleanly immediately
@@ -784,7 +784,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
RawInput: rawInput,
ExitCode: 125,
}
- if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
+ if err := ctr.Start(ctx, true); err != nil {
// if lastError != nil {
// fmt.Fprintln(os.Stderr, lastError)
// }
@@ -845,10 +845,6 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
}
- var joinPod bool
- if len(ctr.PodID()) > 0 {
- joinPod = true
- }
report := entities.ContainerRunReport{Id: ctr.ID()}
if logrus.GetLevel() == logrus.DebugLevel {
@@ -859,7 +855,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
if opts.Detach {
// if the container was created as part of a pod, also start its dependencies, if any.
- if err := ctr.Start(ctx, joinPod); err != nil {
+ if err := ctr.Start(ctx, true); err != nil {
// This means the command did not exist
report.ExitCode = define.ExitCode(err)
return &report, err
@@ -869,7 +865,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
}
// if the container was created as part of a pod, also start its dependencies, if any.
- if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil {
+ if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true); err != nil {
// We've manually detached from the container
// Do not perform cleanup, or wait for container exit code
// Just exit immediately
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 7a0c2907c..ab71f8f6f 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -39,7 +39,7 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo
// StartAttachCtr starts and (if required) attaches to a container
// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
// error. we may need to just lint disable this one.
-func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
+func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint-interfacer
resize := make(chan define.TerminalSize)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
@@ -88,7 +88,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
return ctr.Attach(streams, detachKeys, resize)
}
- attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
+ attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, true)
if err != nil {
return err
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index a0f65f11f..4545d266b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -629,7 +629,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
if opts.Detach {
// Detach and return early
- err := containers.Start(ic.ClientCtx, con.ID, nil)
+ err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true))
if err != nil {
report.ExitCode = define.ExitCode(err)
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 1d724ffb0..ef9975021 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -364,6 +364,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if len(s.Secrets) != 0 {
options = append(options, libpod.WithSecrets(s.Secrets))
}
+ if len(s.DependencyContainers) > 0 {
+ deps := make([]*libpod.Container, 0, len(s.DependencyContainers))
+ for _, ctr := range s.DependencyContainers {
+ depCtr, err := rt.LookupContainer(ctr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr)
+ }
+ deps = append(deps, depCtr)
+ }
+ options = append(options, libpod.WithDependencyCtrs(deps))
+ }
return options, nil
}
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index 845dfdad7..b52e8d100 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -157,6 +157,16 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
case specgen.KeepID:
if rootless.IsRootless() {
toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
+
+ // If user is not overridden, set user in the container
+ // to user running Podman.
+ if s.User == "" {
+ _, uid, gid, err := util.GetKeepIDMapping()
+ if err != nil {
+ return nil, err
+ }
+ toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid)))
+ }
} else {
// keep-id as root doesn't need a user namespace
s.UserNS.NSMode = specgen.Host
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index c10dc5ef5..28111f96d 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -160,10 +160,17 @@ type ContainerBasicConfig struct {
// to 0, 1, 2) that will be passed to the executed process. The total FDs
// passed will be 3 + PreserveFDs.
// set tags as `json:"-"` for not supported remote
+ // Optional.
PreserveFDs uint `json:"-"`
// Timezone is the timezone inside the container.
// Local means it has the same timezone as the host machine
+ // Optional.
Timezone string `json:"timezone,omitempty"`
+ // DependencyContainers is an array of containers this container
+ // depends on. Dependency containers must be started before this
+ // container. Dependencies can be specified by name or full/partial ID.
+ // Optional.
+ DependencyContainers []string `json:"dependencyContainers,omitempty"`
}
// ContainerStorageConfig contains information on the storage configuration of a
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index df86eab15..e6f63a391 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -119,6 +119,19 @@ var _ = Describe("Podman exec", func() {
Expect(session.ExitCode()).To(Equal(100))
})
+ It("podman exec in keep-id container drops privileges", func() {
+ SkipIfNotRootless("This function is not enabled for rootful podman")
+ ctrName := "testctr1"
+ testCtr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "--userns=keep-id", ALPINE, "top"})
+ testCtr.WaitWithDefaultTimeout()
+ Expect(testCtr.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"exec", ctrName, "grep", "CapEff", "/proc/self/status"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
+ })
+
It("podman exec --privileged", func() {
session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
@@ -143,7 +156,6 @@ var _ = Describe("Podman exec", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
-
})
It("podman exec --privileged", func() {
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 23930b4f7..cefe00655 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1588,4 +1588,29 @@ WORKDIR /madethis`, BB)
Expect(session.OutputToString()).To(ContainSubstring("mysecret"))
})
+
+ It("podman run --requires", func() {
+ depName := "ctr1"
+ depContainer := podmanTest.Podman([]string{"create", "--name", depName, ALPINE, "top"})
+ depContainer.WaitWithDefaultTimeout()
+ Expect(depContainer.ExitCode()).To(Equal(0))
+
+ mainName := "ctr2"
+ mainContainer := podmanTest.Podman([]string{"run", "--name", mainName, "--requires", depName, "-d", ALPINE, "top"})
+ mainContainer.WaitWithDefaultTimeout()
+ Expect(mainContainer.ExitCode()).To(Equal(0))
+
+ stop := podmanTest.Podman([]string{"stop", "--all"})
+ stop.WaitWithDefaultTimeout()
+ Expect(stop.ExitCode()).To(Equal(0))
+
+ start := podmanTest.Podman([]string{"start", mainName})
+ start.WaitWithDefaultTimeout()
+ Expect(start.ExitCode()).To(Equal(0))
+
+ running := podmanTest.Podman([]string{"ps", "-q"})
+ running.WaitWithDefaultTimeout()
+ Expect(running.ExitCode()).To(Equal(0))
+ Expect(len(running.OutputToStringArray())).To(Equal(2))
+ })
})
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index 804dd46b1..cda054b15 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -209,4 +209,19 @@ load helpers
run_podman rm -f $cid
}
+@test "podman rootless cni adds /usr/sbin to PATH" {
+ is_rootless || skip "only meaningful for rootless"
+
+ local mynetname=testnet-$(random_string 10)
+ run_podman network create $mynetname
+
+ # Test that rootless cni adds /usr/sbin to $PATH
+ # iptables is located under /usr/sbin and is needed for the CNI plugins.
+ # Debian doesn't add /usr/sbin to $PATH for rootless users so we have to add it.
+ PATH=/usr/local/bin:/usr/bin run_podman run --rm --network $mynetname $IMAGE ip addr
+ is "$output" ".*eth0.*" "Interface eth0 not found in ip addr output"
+
+ run_podman network rm -f $mynetname
+}
+
# vim: filetype=sh