summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml48
-rw-r--r--Makefile25
-rw-r--r--cmd/podman/common/create.go14
-rw-r--r--cmd/podman/common/create_opts.go2
-rw-r--r--cmd/podman/containers/checkpoint.go1
-rw-r--r--cmd/podman/containers/restore.go9
-rw-r--r--cmd/podman/pods/logs.go31
-rw-r--r--cmd/podman/root.go14
-rw-r--r--contrib/cirrus/add_second_partition.sh63
-rwxr-xr-xcontrib/cirrus/runner.sh3
-rwxr-xr-xcontrib/cirrus/setup_environment.sh24
-rw-r--r--contrib/fedora-minimal/Dockerfile1
-rw-r--r--contrib/fedora-minimal/README.md4
-rw-r--r--contrib/spec/podman.spec.in2
-rw-r--r--docs/source/markdown/podman-container-checkpoint.1.md8
-rw-r--r--docs/source/markdown/podman-container-inspect.1.md22
-rw-r--r--docs/source/markdown/podman-container-restore.1.md9
-rw-r--r--docs/source/markdown/podman-create.1.md22
-rw-r--r--docs/source/markdown/podman-run.1.md22
-rw-r--r--go.mod8
-rw-r--r--go.sum17
-rwxr-xr-xhack/bats12
-rwxr-xr-xhack/install_catatonit.sh33
-rwxr-xr-xhack/xref-helpmsgs-manpages7
-rw-r--r--libpod/container_api.go3
-rw-r--r--libpod/container_config.go7
-rw-r--r--libpod/container_inspect.go4
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/container_internal_linux.go22
-rw-r--r--libpod/define/container_inspect.go11
-rw-r--r--libpod/network/cni/cni_conversion.go15
-rw-r--r--libpod/network/cni/cni_types.go15
-rw-r--r--libpod/network/cni/config_test.go13
-rw-r--r--libpod/network/cni/network.go7
-rw-r--r--libpod/network/cni/run.go8
-rw-r--r--libpod/network/cni/run_test.go66
-rw-r--r--libpod/network/netavark/run_test.go48
-rw-r--r--libpod/network/types/network.go20
-rw-r--r--libpod/networking_linux.go100
-rw-r--r--libpod/networking_linux_test.go217
-rw-r--r--libpod/networking_machine.go121
-rw-r--r--libpod/networking_slirp4netns.go10
-rw-r--r--libpod/oci_conmon_linux.go21
-rw-r--r--libpod/options.go14
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--pause/pause.c69
-rw-r--r--pkg/api/handlers/compat/containers_create.go2
-rw-r--r--pkg/api/handlers/compat/exec.go4
-rw-r--r--pkg/api/handlers/libpod/containers.go147
-rw-r--r--pkg/api/handlers/types.go2
-rw-r--r--pkg/bindings/containers/checkpoint.go49
-rw-r--r--pkg/bindings/containers/types.go11
-rw-r--r--pkg/bindings/containers/types_checkpoint_options.go60
-rw-r--r--pkg/bindings/containers/types_restore_options.go60
-rw-r--r--pkg/bindings/test/common_test.go2
-rw-r--r--pkg/checkpoint/checkpoint_restore.go13
-rw-r--r--pkg/domain/entities/containers.go4
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/domain/infra/abi/containers.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go40
-rw-r--r--pkg/machine/ignition.go2
-rw-r--r--pkg/machine/qemu/machine.go5
-rw-r--r--pkg/machine/qemu/options_darwin_arm64.go2
-rw-r--r--pkg/rootless/rootless_linux.c309
-rw-r--r--pkg/specgen/generate/container.go12
-rw-r--r--pkg/specgen/generate/container_create.go63
-rw-r--r--pkg/specgen/generate/oci.go2
-rw-r--r--pkg/specgen/generate/pod_create.go13
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--pkg/specgenutil/specgen.go2
-rw-r--r--pkg/specgenutil/util.go54
-rw-r--r--pkg/specgenutil/volumes.go2
-rw-r--r--pkg/util/filters.go25
-rw-r--r--pkg/util/mountOpts.go1
-rw-r--r--test/apiv2/40-pods.at4
-rw-r--r--test/e2e/checkpoint_test.go130
-rw-r--r--test/e2e/config.go2
-rw-r--r--test/e2e/images_test.go2
-rw-r--r--test/e2e/pod_create_test.go4
-rw-r--r--test/e2e/run_test.go2
-rw-r--r--test/e2e/volume_ls_test.go11
-rw-r--r--test/system/030-run.bats22
-rw-r--r--test/system/250-systemd.bats8
-rw-r--r--test/system/520-checkpoint.bats175
-rw-r--r--test/system/700-play.bats2
-rw-r--r--utils/utils.go12
-rw-r--r--utils/utils_supported.go9
-rw-r--r--vendor/github.com/containers/common/libimage/manifests/manifests.go3
-rw-r--r--vendor/github.com/containers/common/pkg/config/config.go33
-rw-r--r--vendor/github.com/containers/common/pkg/parse/parse.go1
-rw-r--r--vendor/modules.txt8
91 files changed, 1621 insertions, 904 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 091b37627..961104e96 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -24,12 +24,12 @@ env:
####
#### Cache-image names to test with (double-quotes around names are critical)
####
- FEDORA_NAME: "fedora-34"
- PRIOR_FEDORA_NAME: "fedora-33"
+ FEDORA_NAME: "fedora-35"
+ PRIOR_FEDORA_NAME: "fedora-34"
UBUNTU_NAME: "ubuntu-2110"
# Google-cloud VM Images
- IMAGE_SUFFIX: "c4955591916388352"
+ IMAGE_SUFFIX: "c6226133906620416"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
@@ -148,11 +148,11 @@ build_task:
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: &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}
@@ -230,12 +230,18 @@ bindings_task:
clone_script: *noop # Comes from cache
setup_script: *setup
main_script: *main
- always: &html_artifacts
+ always: &logs_artifacts
<<: *runner_stats
# Required for `contrib/cirrus/logformatter` to work properly
html_artifacts:
path: ./*.html
type: text/html
+ package_versions_script: '$SCRIPT_BASE/logcollector.sh packages'
+ df_script: '$SCRIPT_BASE/logcollector.sh df'
+ audit_log_script: '$SCRIPT_BASE/logcollector.sh audit'
+ journal_script: '$SCRIPT_BASE/logcollector.sh journal'
+ podman_system_info_script: '$SCRIPT_BASE/logcollector.sh podman'
+ time_script: '$SCRIPT_BASE/logcollector.sh time'
# Build the "libpod" API documentation `swagger.yaml` and
@@ -402,7 +408,7 @@ unit_test_task:
- validate
matrix:
- env: *stdenvars
- #- env: *priorfedora_envvars
+ - env: *priorfedora_envvars
- env: *ubuntu_envvars
# Special-case: Rootless on latest Fedora (standard) VM
- name: "Rootless unit on $DISTRO_NV"
@@ -416,7 +422,7 @@ unit_test_task:
gopath_cache: *ro_gopath_cache
setup_script: *setup
main_script: *main
- always: *runner_stats
+ always: *logs_artifacts
apiv2_test_task:
@@ -437,14 +443,8 @@ apiv2_test_task:
gopath_cache: *ro_gopath_cache
setup_script: *setup
main_script: *main
- always: &logs_artifacts
- <<: *html_artifacts
- package_versions_script: '$SCRIPT_BASE/logcollector.sh packages'
- df_script: '$SCRIPT_BASE/logcollector.sh df'
- audit_log_script: '$SCRIPT_BASE/logcollector.sh audit'
- journal_script: '$SCRIPT_BASE/logcollector.sh journal'
- podman_system_info_script: '$SCRIPT_BASE/logcollector.sh podman'
- time_script: '$SCRIPT_BASE/logcollector.sh time'
+ always: *logs_artifacts
+
compose_test_task:
name: "compose test on $DISTRO_NV ($PRIV_NAME)"
@@ -522,11 +522,11 @@ container_integration_test_task:
_BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
- # - env:
- # DISTRO_NV: ${PRIOR_FEDORA_NAME}
- # _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- # VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
- # CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN}
+ - env:
+ DISTRO_NV: ${PRIOR_FEDORA_NAME}
+ _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
+ VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
+ CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN}
gce_instance: *standardvm
timeout_in: 90m
env:
diff --git a/Makefile b/Makefile
index 47f30e5fa..ceecda274 100644
--- a/Makefile
+++ b/Makefile
@@ -186,10 +186,6 @@ ifdef HOMEBREW_PREFIX
endif
endif
-# For building pause/pause.c
-GCC ?= gcc
-PAUSE_CFLAGS = -Os -static -Wall -Werror -DVERSION=v$(RELEASE_VERSION)
-
###
### Primary entry-point targets
###
@@ -201,7 +197,7 @@ default: all
all: binaries docs
.PHONY: binaries
-binaries: podman podman-remote rootlessport pause
+binaries: podman podman-remote rootlessport ## Build podman, podman-remote and rootlessport binaries
# Extract text following double-# for targets, as their description for
# the `help` target. Otherwise These simple-substitutions are resolved
@@ -379,12 +375,6 @@ bin/rootlessport: .gopathok $(SOURCES) go.mod go.sum
.PHONY: rootlessport
rootlessport: bin/rootlessport
-bin/pause: pause/pause.c
- $(GCC) $(PAUSE_CFLAGS) pause/pause.c -o bin/pause
-
-.PHONY: pause
-pause: bin/pause
-
###
### Secondary binary-build targets
###
@@ -744,7 +734,7 @@ install.remote-nobuild:
install.remote: podman-remote install.remote-nobuild
.PHONY: install.bin-nobuild
-install.bin-nobuild: install.pause
+install.bin-nobuild:
install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR)
install ${SELINUXOPT} -m 755 bin/podman $(DESTDIR)$(BINDIR)/podman
test -z "${SELINUXOPT}" || chcon --verbose --reference=$(DESTDIR)$(BINDIR)/podman bin/podman
@@ -798,10 +788,8 @@ install.docker-docs-nobuild:
.PHONY: install.docker-docs
install.docker-docs: docker-docs install.docker-docs-nobuild
-.PHONY: install.pause
-install.pause: pause
- install ${SELINUXOPT} -m 755 -d $(DESTDIR)$(LIBEXECPODMAN)/pause
- install ${SELINUXOPT} -m 755 bin/pause $(DESTDIR)$(LIBEXECPODMAN)/pause/pause
+.PHONY: install.docker-full
+install.docker-full: install.docker install.docker-docs
.PHONY: install.systemd
ifneq (,$(findstring systemd,$(BUILDTAGS)))
@@ -832,9 +820,6 @@ else
install.systemd:
endif
-.PHONY: install.pause
-install.pause: pause
-
.PHONY: install.tools
install.tools: .install.goimports .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint .install.bats ## Install needed tools
@@ -908,7 +893,7 @@ uninstall:
.PHONY: clean-binaries
clean-binaries: ## Remove platform/architecture specific binary files
rm -rf \
- bin \
+ bin
.PHONY: clean
clean: clean-binaries ## Clean all make artifacts
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 4598e535d..d73fa653f 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -201,6 +201,20 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone)
+ unsetenvFlagName := "unsetenv"
+ createFlags.StringArrayVar(
+ &cf.UnsetEnv,
+ unsetenvFlagName, []string{},
+ "Unset environment default variables in container",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(unsetenvFlagName, completion.AutocompleteNone)
+
+ createFlags.BoolVar(
+ &cf.UnsetEnvAll,
+ "unsetenv-all", false,
+ "Unset all default environment variables in container",
+ )
+
if !registry.IsRemote() {
createFlags.BoolVar(
&cf.EnvHost,
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index 6283eb28e..aacdfd274 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -297,6 +297,8 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
Systemd: "true", // podman default
TmpFS: parsedTmp,
TTY: cc.Config.Tty,
+ UnsetEnv: cc.UnsetEnv,
+ UnsetEnvAll: cc.UnsetEnvAll,
User: cc.Config.User,
UserNS: string(cc.HostConfig.UsernsMode),
UTS: string(cc.HostConfig.UTSMode),
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index d92bc3e5e..e8dd25978 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -55,6 +55,7 @@ func init() {
flags.BoolVarP(&checkpointOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
flags.BoolVarP(&checkpointOptions.LeaveRunning, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk")
flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
+ flags.BoolVar(&checkpointOptions.FileLocks, "file-locks", false, "Checkpoint a container with file locks")
flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers")
exportFlagName := "export"
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index 217adc887..cf0ad5f80 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -54,6 +53,7 @@ func init() {
flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers")
flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections")
+ flags.BoolVar(&restoreOptions.FileLocks, "file-locks", false, "Restore a container with file locks")
importFlagName := "import"
flags.StringVarP(&restoreOptions.Import, importFlagName, "i", "", "Restore from exported checkpoint archive (tar.gz)")
@@ -120,12 +120,7 @@ func restore(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- if len(inputPorts) > 0 {
- restoreOptions.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts)
- if err != nil {
- return err
- }
- }
+ restoreOptions.PublishPorts = inputPorts
argLen := len(args)
if restoreOptions.Import != "" {
diff --git a/cmd/podman/pods/logs.go b/cmd/podman/pods/logs.go
index fe5205669..a79c0430f 100644
--- a/cmd/podman/pods/logs.go
+++ b/cmd/podman/pods/logs.go
@@ -27,11 +27,11 @@ type logsOptionsWrapper struct {
var (
logsPodOptions logsOptionsWrapper
logsPodDescription = `Displays logs for pod with one or more containers.`
- logsPodCommand = &cobra.Command{
+ podLogsCommand = &cobra.Command{
Use: "logs [options] POD",
Short: "Fetch logs for pod with one or more containers",
Long: logsPodDescription,
- // We dont want users to invoke latest and pod togather
+ // We dont want users to invoke latest and pod together
Args: func(cmd *cobra.Command, args []string) error {
switch {
case registry.IsRemote() && logsPodOptions.Latest:
@@ -53,35 +53,16 @@ var (
podman pod logs --follow=true --since 10m podID
podman pod logs mywebserver`,
}
-
- containerLogsCommand = &cobra.Command{
- Use: logsPodCommand.Use,
- Short: logsPodCommand.Short,
- Long: logsPodCommand.Long,
- Args: logsPodCommand.Args,
- RunE: logsPodCommand.RunE,
- ValidArgsFunction: logsPodCommand.ValidArgsFunction,
- Example: `podman pod logs podId
- podman pod logs -c ctrname podName
- podman pod logs --tail 2 mywebserver
- podman pod logs --follow=true --since 10m podID`,
- }
)
func init() {
+ // pod logs
registry.Commands = append(registry.Commands, registry.CliCommand{
- Command: logsPodCommand,
- })
- logsFlags(logsPodCommand)
- validate.AddLatestFlag(logsPodCommand, &logsPodOptions.Latest)
-
- // container logs
- registry.Commands = append(registry.Commands, registry.CliCommand{
- Command: containerLogsCommand,
+ Command: podLogsCommand,
Parent: podCmd,
})
- logsFlags(containerLogsCommand)
- validate.AddLatestFlag(containerLogsCommand, &logsPodOptions.Latest)
+ logsFlags(podLogsCommand)
+ validate.AddLatestFlag(podLogsCommand, &logsPodOptions.Latest)
}
func logsFlags(cmd *cobra.Command) {
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 418a70e1e..9e4c8d24d 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -163,20 +163,6 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
return err
}
- for _, env := range cfg.Engine.Env {
- splitEnv := strings.SplitN(env, "=", 2)
- if len(splitEnv) != 2 {
- return fmt.Errorf("invalid environment variable for engine %s, valid configuration is KEY=value pair", env)
- }
- // skip if the env is already defined
- if _, ok := os.LookupEnv(splitEnv[0]); ok {
- logrus.Debugf("environment variable %s is already defined, skip the settings from containers.conf", splitEnv[0])
- continue
- }
- if err := os.Setenv(splitEnv[0], splitEnv[1]); err != nil {
- return err
- }
- }
// Hard code TMPDIR functions to use /var/tmp, if user did not override
if _, ok := os.LookupEnv("TMPDIR"); !ok {
if tmpdir, err := cfg.ImageCopyTmpDir(); err != nil {
diff --git a/contrib/cirrus/add_second_partition.sh b/contrib/cirrus/add_second_partition.sh
deleted file mode 100644
index 322dd2512..000000000
--- a/contrib/cirrus/add_second_partition.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-
-# N/B: This script could mega f*!@up your disks if run by mistake.
-# it is left without the execute-bit on purpose!
-
-set -eo pipefail
-
-# shellcheck source=./lib.sh
-source $(dirname $0)/lib.sh
-
-# $SLASH_DEVICE is the disk device to be f*xtuP
-SLASH_DEVICE="/dev/sda" # Always the case on GCP
-
-# The unallocated space results from the difference in disk-size between VM Image
-# and runtime request.
-NEW_PART_START="50%"
-NEW_PART_END="100%"
-
-
-if [[ ! -r "/root" ]] || [[ -r "/root/second_partition_ready" ]]
-then
- warn "Ignoring attempted execution of $(basename $0)"
- exit 0
-fi
-
-[[ -x "$(type -P parted)" ]] || \
- die "The parted command is required."
-
-[[ ! -b ${SLASH_DEVICE}2 ]] || \
- die "Found unexpected block device ${SLASH_DEVICE}2"
-
-PPRINTCMD="parted --script ${SLASH_DEVICE} print"
-FINDMNTCMD="findmnt --source=${SLASH_DEVICE}1 --mountpoint=/ --canonicalize --evaluate --first-only --noheadings"
-TMPF=$(mktemp -p '' $(basename $0)_XXXX)
-trap "rm -f $TMPF" EXIT
-
-if $FINDMNTCMD | tee $TMPF | egrep -q "^/\s+${SLASH_DEVICE}1"
-then
- msg "Repartitioning original partition table:"
- $PPRINTCMD
-else
- die "Unexpected output from '$FINDMNTCMD': $(<$TMPF)"
-fi
-
-echo "Adding partition offset within unpartitioned space."
-parted --script --align optimal /dev/sda unit % mkpart primary "" "" "$NEW_PART_START" "$NEW_PART_END"
-
-msg "New partition table:"
-$PPRINTCMD
-
-msg "Growing ${SLASH_DEVICE}1 meet start of ${SLASH_DEVICE}2"
-growpart ${SLASH_DEVICE} 1
-
-FSTYPE=$(findmnt --first-only --noheadings --output FSTYPE ${SLASH_DEVICE}1)
-echo "Expanding $FSTYPE filesystem on ${SLASH_DEVICE}1"
-case $FSTYPE in
- ext*) resize2fs ${SLASH_DEVICE}1 ;;
- *) die "Script $(basename $0) doesn't know how to resize a $FSTYPE filesystem." ;;
-esac
-
-# Must happen last - signals completion to other tooling
-msg "Recording newly available disk partition device into /root/second_partition_ready"
-echo "${SLASH_DEVICE}2" > /root/second_partition_ready
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 8ef2a6e64..4c27dfa4b 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -84,7 +84,8 @@ function _run_bindings() {
# Subshell needed so logformatter will write output in cwd; if it runs in
# the subdir, .cirrus.yml will not find the html'ized log
- (cd pkg/bindings/test && ginkgo -trace -noColor -debug -r) |& logformatter
+ (cd pkg/bindings/test && \
+ ginkgo -progress -trace -noColor -debug -timeout 30m -r -v) |& logformatter
}
function _run_docker-py() {
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 3786054a7..8f535c7e7 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -119,12 +119,6 @@ case "$OS_RELEASE_ID" in
ubuntu) ;;
fedora)
if ((CONTAINER==0)); then
- msg "Configuring / Expanding host storage."
- # VM is setup to allow flexibility in testing alternate storage.
- # For general use, simply make use of all available space.
- bash "$SCRIPT_BASE/add_second_partition.sh"
- $SCRIPT_BASE/logcollector.sh df
-
# All SELinux distros need this for systemd-in-a-container
msg "Enabling container_manage_cgroup"
setsebool container_manage_cgroup true
@@ -164,6 +158,18 @@ case "$TEST_ENVIRON" in
# affected/related tests are sensitive to this variable.
warn "Disabling usernamespace integration testing"
echo "SKIP_USERNS=1" >> /etc/ci_environment
+
+ # In F35 the hard-coded default
+ # (from containers-common-1-32.fc35.noarch) is 'journald' despite
+ # the upstream repository having this line commented-out.
+ # Containerized integration tests cannot run with 'journald'
+ # as there is no daemon/process there to receive them.
+ cconf="/usr/share/containers/containers.conf"
+ note="- commented-out by setup_environment.sh"
+ if grep -Eq '^log_driver.+journald' "$cconf"; then
+ warn "Patching out $cconf journald log_driver"
+ sed -r -i -e "s/^log_driver(.*)/# log_driver\1 $note/" "$cconf"
+ fi
fi
;;
*) die_unknown TEST_ENVIRON
@@ -212,10 +218,8 @@ case "$TEST_FLAVOR" in
remove_packaged_podman_files
make install PREFIX=/usr ETCDIR=/etc
- # TODO: Don't install stuff at test runtime! Do this from
- # cache_images/fedora_packaging.sh in containers/automation_images
- # and STRONGLY prefer installing RPMs vs pip packages in venv
- dnf install -y python3-virtualenv python3-pytest4
+ msg "Installing previously downloaded/cached packages"
+ dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm
virtualenv venv
source venv/bin/activate
pip install --upgrade pip
diff --git a/contrib/fedora-minimal/Dockerfile b/contrib/fedora-minimal/Dockerfile
deleted file mode 100644
index a051b3204..000000000
--- a/contrib/fedora-minimal/Dockerfile
+++ /dev/null
@@ -1 +0,0 @@
-FROM registry.fedoraproject.org/fedora-minimal:latest
diff --git a/contrib/fedora-minimal/README.md b/contrib/fedora-minimal/README.md
deleted file mode 100644
index 52bf94b53..000000000
--- a/contrib/fedora-minimal/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-This dockerfile exists so that the container image can be "mirrored"
-onto quay.io automatically, so automated testing can be more resilient.
-
-https://quay.io/repository/libpod/fedora-minimal?tab=builds
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 474add1af..cb041df6c 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -58,6 +58,7 @@ BuildRequires: libselinux-devel
BuildRequires: pkgconfig
BuildRequires: make
BuildRequires: systemd-devel
+Requires: catatonit >= 0.1.7
Requires: containers-common
Requires: conmon
Requires: containernetworking-plugins >= 0.6.0-3
@@ -529,7 +530,6 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath}
%{_usr}/lib/tmpfiles.d/podman.conf
%dir %{_libexecdir}/%{name}
%{_libexecdir}/%{name}/rootlessport
-%{_libexecdir}/%{name}/pause/pause
%if 0%{?with_devel}
%files -n libpod-devel -f devel.file-list
diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md
index 1faa40a94..200920ca9 100644
--- a/docs/source/markdown/podman-container-checkpoint.1.md
+++ b/docs/source/markdown/podman-container-checkpoint.1.md
@@ -110,6 +110,14 @@ restore. Defaults to not checkpointing *containers* with established TCP
connections.\
The default is **false**.
+#### **--file-locks**
+
+Checkpoint a *container* with file locks. If an application running in the container
+is using file locks, this OPTION is required during checkpoint and restore. Otherwise
+checkpointing *containers* with file locks is expected to fail. If file locks are not
+used, this option is ignored.\
+The default is **false**.
+
#### **--with-previous**
Check out the *container* with previous criu image files in pre-dump. It only works on `runc 1.0-rc3` or `higher`.\
diff --git a/docs/source/markdown/podman-container-inspect.1.md b/docs/source/markdown/podman-container-inspect.1.md
index 54b3cb2ae..dfed294fc 100644
--- a/docs/source/markdown/podman-container-inspect.1.md
+++ b/docs/source/markdown/podman-container-inspect.1.md
@@ -133,28 +133,6 @@ $ podman container inspect foobar
"Ports": {},
"SandboxKey": ""
},
- "ExitCommand": [
- "/usr/bin/podman",
- "--root",
- "/home/dwalsh/.local/share/containers/storage",
- "--runroot",
- "/run/user/3267/containers",
- "--log-level",
- "warning",
- "--cgroup-manager",
- "systemd",
- "--tmpdir",
- "/run/user/3267/libpod/tmp",
- "--runtime",
- "crun",
- "--storage-driver",
- "overlay",
- "--events-backend",
- "journald",
- "container",
- "cleanup",
- "99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6"
- ],
"Namespace": "",
"IsInfra": false,
"Config": {
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index 962770ea0..10477fc77 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -81,6 +81,7 @@ to import a checkpointed *container* from another host.\
Import a pre-checkpoint tar.gz file which was exported by Podman. This option
must be used with **-i** or **--import**. It only works on `runc 1.0-rc3` or `higher`.
+*IMPORTANT: This OPTION is not supported on the remote client.*
#### **--name**, **-n**=*name*
@@ -142,6 +143,14 @@ option is ignored. Defaults to not restoring *containers* with established TCP
connections.\
The default is **false**.
+#### **--file-locks**
+
+Restore a *container* with file locks. This option is required to
+restore file locks from a checkpoint image. If the checkpoint image
+does not contain file locks, this option is ignored. Defaults to not
+restoring file locks.\
+The default is **false**.
+
## EXAMPLE
Restores the container "mywebserver".
```
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index b0d7b8f12..b58fd1e18 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -606,7 +606,9 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· ro, readonly: true or false (default).
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+
+ · idmap: true or false (default). If specified, create an idmapped mount to the target user namespace in the container.
Options specific to image:
@@ -622,7 +624,9 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. relabel: shared, private.
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ · idmap: true or false (default). If specified, create an idmapped mount to the target user namespace in the container.
+
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
Options specific to tmpfs:
@@ -636,7 +640,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· notmpcopyup: Disable copying files from the image to the tmpfs.
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
Options specific to devpts:
@@ -1047,6 +1051,18 @@ Remote connections use local containers.conf for defaults
Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
+#### **--unsetenv**=*env*
+
+Unset default environment variables for the container. Default environment
+variables include variables provided natively by Podman, environment variables
+configured by the image, and environment variables from containers.conf.
+
+#### **--unsetenv-all**=*true|false*
+
+Unset all default environment variables for the container. Default environment
+variables include variables provided natively by Podman, environment variables
+configured by the image, and environment variables from containers.conf.
+
#### **--uidmap**=*container_uid*:*from_uid*:*amount*
Run the container in a new user namespace using the supplied mapping. This
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 0fdd47a78..90c456544 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -633,7 +633,9 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· ro, readonly: true or false (default).
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+
+ · idmap: true or false (default). If specified, create an idmapped mount to the target user namespace in the container.
Options specific to image:
@@ -649,7 +651,9 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. relabel: shared, private.
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ · idmap: true or false (default). If specified, create an idmapped mount to the target user namespace in the container.
+
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
Options specific to tmpfs:
@@ -663,7 +667,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· notmpcopyup: Disable copying files from the image to the tmpfs.
- . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
Options specific to devpts:
@@ -1117,6 +1121,18 @@ Remote connections use local containers.conf for defaults
Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
+#### **--unsetenv**=*env*
+
+Unset default environment variables for the container. Default environment
+variables include variables provided natively by Podman, environment variables
+configured by the image, and environment variables from containers.conf.
+
+#### **--unsetenv-all**=*true|false*
+
+Unset all default environment variables for the container. Default environment
+variables include variables provided natively by Podman, environment variables
+configured by the image, and environment variables from containers.conf.
+
#### **--uidmap**=*container_uid*:*from_uid*:*amount*
Run the container in a new user namespace using the supplied mapping. This
diff --git a/go.mod b/go.mod
index 05ad8990b..93c9dfba3 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.23.1
- github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358
+ github.com/containers/common v0.46.1-0.20211115170340-7ae7bd1c3f8e
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.16.1
github.com/containers/ocicrypt v1.1.2
@@ -24,7 +24,7 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f
github.com/docker/distribution v2.7.1+incompatible
- github.com/docker/docker v20.10.10+incompatible
+ github.com/docker/docker v20.10.11+incompatible
github.com/docker/go-connections v0.4.0
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc
github.com/docker/go-units v0.4.0
@@ -69,8 +69,8 @@ require (
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
- k8s.io/api v0.22.3
- k8s.io/apimachinery v0.22.3
+ k8s.io/api v0.22.4
+ k8s.io/apimachinery v0.22.4
)
replace github.com/onsi/gomega => github.com/onsi/gomega v1.16.0
diff --git a/go.sum b/go.sum
index f3a7dcffd..b303c2221 100644
--- a/go.sum
+++ b/go.sum
@@ -258,8 +258,8 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB
github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY=
github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ=
github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo=
-github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358 h1:dK2AgGBdWspdQNw28Wc4peY25QeyYV4H9ViQaFaQ9XQ=
-github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358/go.mod h1:bu8gizEkgAz6gXHvUw2cMtI5ErxB+fn/hv49RWk5N1A=
+github.com/containers/common v0.46.1-0.20211115170340-7ae7bd1c3f8e h1:YSuo3zGivcgQhRV1TOJ6zW3VjyjoU7BJMRyh71v/Zdc=
+github.com/containers/common v0.46.1-0.20211115170340-7ae7bd1c3f8e/go.mod h1:bu8gizEkgAz6gXHvUw2cMtI5ErxB+fn/hv49RWk5N1A=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4=
@@ -336,8 +336,9 @@ github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BU
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM=
github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo=
+github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
@@ -1494,13 +1495,13 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
-k8s.io/api v0.22.3 h1:wOoES2GoSkUsdped2RB4zYypPqWtvprGoKCENTOOjP4=
-k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs=
+k8s.io/api v0.22.4 h1:UvyHW0ezB2oIgHAxlYoo6UJQObYXU7awuNarwoHEOjw=
+k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
-k8s.io/apimachinery v0.22.3 h1:mrvBG5CZnEfwgpVqWcrRKvdsYECTrhAR6cApAgdsflk=
-k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+k8s.io/apimachinery v0.22.4 h1:9uwcvPpukBw/Ri0EUmWz+49cnFtaoiyEhQTK+xOe7Ck=
+k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0=
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
@@ -1527,7 +1528,7 @@ k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
-k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/hack/bats b/hack/bats
index 7cc3b9bde..cd81a9c73 100755
--- a/hack/bats
+++ b/hack/bats
@@ -93,19 +93,25 @@ done
rc=0
+# As of 2021-11 podman has a bunch of external helper binaries
+if [[ -z "$CONTAINERS_HELPER_BINARY_DIR" ]]; then
+ export CONTAINERS_HELPER_BINARY_DIR=$(pwd)/bin
+fi
+
# Root
if [ -z "$ROOTLESS_ONLY" ]; then
echo "# bats ${bats_filter[@]} $TESTS"
sudo --preserve-env=PODMAN \
--preserve-env=PODMAN_TEST_DEBUG \
--preserve-env=OCI_RUNTIME \
+ --preserve-env=CONTAINERS_HELPER_BINARY_DIR \
bats "${bats_opts[@]}" "${bats_filter[@]}" $TESTS
rc=$?
fi
-# Rootless
-echo "--------------------------------------------------"
-if [ -z "$ROOT_ONLY" ]; then
+# Rootless. (Only if we're not already root)
+if [[ -z "$ROOT_ONLY" && "$(id -u)" != 0 ]]; then
+ echo "--------------------------------------------------"
echo "\$ bats ${bats_filter[@]} $TESTS"
bats "${bats_opts[@]}" "${bats_filter[@]}" $TESTS
rc=$((rc | $?))
diff --git a/hack/install_catatonit.sh b/hack/install_catatonit.sh
index 0a02b75ab..a35e349f5 100755
--- a/hack/install_catatonit.sh
+++ b/hack/install_catatonit.sh
@@ -4,22 +4,23 @@ CATATONIT_PATH="${BASE_PATH}/catatonit"
CATATONIT_VERSION="v0.1.7"
set -e
-if [ -f $CATATONIT_PATH ]; then
+if [ -f $CATATONIT_PATH ] && [ -z "$1" ]; then
echo "skipping ... catatonit is already installed"
-else
- echo "installing catatonit to $CATATONIT_PATH"
- buildDir=$(mktemp -d)
- git clone https://github.com/openSUSE/catatonit.git $buildDir
+ exit 0
+fi
- pushd $buildDir
- echo `pwd`
- git reset --hard ${CATATONIT_VERSION}
- autoreconf -fi
- ./configure
- make
- install ${SELINUXOPT} -d -m 755 $BASE_PATH
- install ${SELINUXOPT} -m 755 catatonit $CATATONIT_PATH
- popd
+echo "installing catatonit to $CATATONIT_PATH"
+buildDir=$(mktemp -d)
+git clone https://github.com/openSUSE/catatonit.git $buildDir
- rm -rf $buildDir
-fi
+pushd $buildDir
+echo `pwd`
+git reset --hard ${CATATONIT_VERSION}
+autoreconf -fi
+./configure
+make
+install ${SELINUXOPT} -d -m 755 $BASE_PATH
+install ${SELINUXOPT} -m 755 catatonit $CATATONIT_PATH
+popd
+
+rm -rf $buildDir
diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages
index 6a2d627bb..a447f4da1 100755
--- a/hack/xref-helpmsgs-manpages
+++ b/hack/xref-helpmsgs-manpages
@@ -242,6 +242,13 @@ sub podman_help {
if ($line =~ /^\s{1,4}(\S+)\s/) {
my $subcommand = $1;
print "> podman @_ $subcommand\n" if $debug;
+
+ # check that the same subcommand is not listed twice (#12356)
+ if (exists $help{$subcommand}) {
+ warn "$ME: 'podman @_ help' lists '$subcommand' twice\n";
+ ++$Errs;
+ }
+
$help{$subcommand} = podman_help(@_, $subcommand)
unless $subcommand eq 'help'; # 'help' not in man
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index a41bb03df..7ae9f497c 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -798,6 +798,9 @@ type ContainerCheckpointOptions struct {
// how much time each component in the stack requires to
// checkpoint a container.
PrintStats bool
+ // FileLocks tells the API to checkpoint/restore a container
+ // with file-locks
+ FileLocks bool
}
// Checkpoint checkpoints a container
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 412be835f..57f5b92ac 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -364,13 +364,6 @@ type ContainerMiscConfig struct {
PostConfigureNetNS bool `json:"postConfigureNetNS"`
// OCIRuntime used to create the container
OCIRuntime string `json:"runtime,omitempty"`
- // ExitCommand is the container's exit command.
- // This Command will be executed when the container exits by Conmon.
- // It is usually used to invoke post-run cleanup - for example, in
- // Podman, it invokes `podman container cleanup`, which in turn calls
- // Libpod's Cleanup() API to unmount the container and clean up its
- // network.
- ExitCommand []string `json:"exitCommand,omitempty"`
// IsInfra is a bool indicating whether this container is an infra container used for
// sharing kernel namespaces in a pod
IsInfra bool `json:"pause"`
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 277c3b960..76a08ce30 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -119,7 +119,6 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
},
Image: config.RootfsImageID,
ImageName: config.RootfsImageName,
- ExitCommand: config.ExitCommand,
Namespace: config.Namespace,
Rootfs: config.Rootfs,
Pod: config.Pod,
@@ -300,8 +299,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.User = c.config.User
if spec.Process != nil {
ctrConfig.Tty = spec.Process.Terminal
- ctrConfig.Env = []string{}
- ctrConfig.Env = append(ctrConfig.Env, spec.Process.Env...)
+ ctrConfig.Env = append([]string{}, spec.Process.Env...)
ctrConfig.WorkingDir = spec.Process.Cwd
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 4bf15be86..871c6787a 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1050,8 +1050,8 @@ func (c *Container) cniHosts() string {
var hosts string
for _, status := range c.getNetworkStatus() {
for _, netInt := range status.Interfaces {
- for _, netAddress := range netInt.Networks {
- hosts += fmt.Sprintf("%s\t%s %s\n", netAddress.Subnet.IP.String(), c.Hostname(), c.config.Name)
+ for _, netAddress := range netInt.Subnets {
+ hosts += fmt.Sprintf("%s\t%s %s\n", netAddress.IPNet.IP.String(), c.Hostname(), c.config.Name)
}
}
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 1a4508448..364b77f29 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -709,18 +709,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.AddAnnotation(annotations.ContainerManager, annotations.ContainerManagerLibpod)
}
- // Only add container environment variable if not already present
- foundContainerEnv := false
- for _, env := range g.Config.Process.Env {
- if strings.HasPrefix(env, "container=") {
- foundContainerEnv = true
- break
- }
- }
- if !foundContainerEnv {
- g.AddProcessEnv("container", "libpod")
- }
-
cgroupPath, err := c.getOCICgroupPath()
if err != nil {
return nil, err
@@ -1353,8 +1341,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
perNetOpts.StaticMAC = netInt.MacAddress
}
if !options.IgnoreStaticIP {
- for _, netAddress := range netInt.Networks {
- perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP)
+ for _, netAddress := range netInt.Subnets {
+ perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.IPNet.IP)
}
}
// Normally interfaces have a length of 1, only for some special cni configs we could get more.
@@ -1955,9 +1943,9 @@ func (c *Container) generateResolvConf() (string, error) {
netStatus := c.getNetworkStatus()
for _, status := range netStatus {
for _, netInt := range status.Interfaces {
- for _, netAddress := range netInt.Networks {
+ for _, netAddress := range netInt.Subnets {
// Note: only using To16() does not work since it also returns a valid ip for ipv4
- if netAddress.Subnet.IP.To4() == nil && netAddress.Subnet.IP.To16() != nil {
+ if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil {
ipv6 = true
}
}
@@ -2163,7 +2151,7 @@ func (c *Container) getHosts() string {
if depCtr != nil {
for _, status := range depCtr.getNetworkStatus() {
for _, netInt := range status.Interfaces {
- for _, netAddress := range netInt.Networks {
+ for _, netAddress := range netInt.Subnets {
if netAddress.Gateway != nil {
hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String())
}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 7decb18a8..8e07cff81 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -542,6 +542,12 @@ type InspectContainerHostConfig struct {
CgroupConf map[string]string `json:"CgroupConf"`
}
+// Address represents an IP address.
+type Address struct {
+ Addr string
+ PrefixLength int
+}
+
// InspectBasicNetworkConfig holds basic configuration information (e.g. IP
// addresses, MAC address, subnet masks, etc) that are common for all networks
// (both additional and main).
@@ -556,7 +562,7 @@ type InspectBasicNetworkConfig struct {
IPPrefixLen int `json:"IPPrefixLen"`
// SecondaryIPAddresses is a list of extra IP Addresses that the
// container has been assigned in this network.
- SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty"`
+ SecondaryIPAddresses []Address `json:"SecondaryIPAddresses,omitempty"`
// IPv6Gateway is the IPv6 gateway this network will use.
IPv6Gateway string `json:"IPv6Gateway"`
// GlobalIPv6Address is the global-scope IPv6 Address for this network.
@@ -565,7 +571,7 @@ type InspectBasicNetworkConfig struct {
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
// SecondaryIPv6Addresses is a list of extra IPv6 Addresses that the
// container has been assigned in this network.
- SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty"`
+ SecondaryIPv6Addresses []Address `json:"SecondaryIPv6Addresses,omitempty"`
// MacAddress is the MAC address for the interface in this network.
MacAddress string `json:"MacAddress"`
// AdditionalMacAddresses is a set of additional MAC Addresses beyond
@@ -654,7 +660,6 @@ type InspectContainerData struct {
Mounts []InspectMount `json:"Mounts"`
Dependencies []string `json:"Dependencies"`
NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
- ExitCommand []string `json:"ExitCommand"`
Namespace string `json:"Namespace"`
IsInfra bool `json:"IsInfra"`
Config *InspectContainerConfig `json:"Config"`
diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go
index 70d259b60..788165b5e 100644
--- a/libpod/network/cni/cni_conversion.go
+++ b/libpod/network/cni/cni_conversion.go
@@ -295,10 +295,6 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
// Note: in the future we might like to allow for dynamic domain names
plugins = append(plugins, newDNSNamePlugin(defaultPodmanDomainName))
}
- // Add the podman-machine CNI plugin if we are in a machine
- if n.isMachine {
- plugins = append(plugins, newPodmanMachinePlugin())
- }
case types.MacVLANNetworkDriver:
plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf))
@@ -369,3 +365,14 @@ func convertSpecgenPortsToCNIPorts(ports []types.PortMapping) ([]cniPortMapEntry
}
return cniPorts, nil
}
+
+func removeMachinePlugin(conf *libcni.NetworkConfigList) *libcni.NetworkConfigList {
+ plugins := make([]*libcni.NetworkConfig, 0, len(conf.Plugins))
+ for _, net := range conf.Plugins {
+ if net.Network.Type != "podman-machine" {
+ plugins = append(plugins, net)
+ }
+ }
+ conf.Plugins = plugins
+ return conf
+}
diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go
index c70cb92b6..e5eb777de 100644
--- a/libpod/network/cni/cni_types.go
+++ b/libpod/network/cni/cni_types.go
@@ -110,12 +110,6 @@ type dnsNameConfig struct {
Capabilities map[string]bool `json:"capabilities"`
}
-// podmanMachineConfig enables port handling on the host OS
-type podmanMachineConfig struct {
- PluginType string `json:"type"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
// ncList describes a generic map
type ncList map[string]interface{}
@@ -285,12 +279,3 @@ func newVLANPlugin(pluginType, device, mode string, mtu int, ipam ipamConfig) VL
}
return m
}
-
-func newPodmanMachinePlugin() podmanMachineConfig {
- caps := make(map[string]bool, 1)
- caps["portMappings"] = true
- return podmanMachineConfig{
- PluginType: "podman-machine",
- Capabilities: caps,
- }
-}
diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go
index 0dfc6173c..c2e5fc985 100644
--- a/libpod/network/cni/config_test.go
+++ b/libpod/network/cni/config_test.go
@@ -965,19 +965,6 @@ var _ = Describe("Config", func() {
Expect(logString).To(ContainSubstring("dnsname and internal networks are incompatible"))
})
- It("create config with podman machine plugin", func() {
- libpodNet, err := getNetworkInterface(cniConfDir, true)
- Expect(err).To(BeNil())
-
- network := types.Network{}
- network1, err := libpodNet.NetworkCreate(network)
- Expect(err).To(BeNil())
- Expect(network1.Driver).To(Equal("bridge"))
- path := filepath.Join(cniConfDir, network1.Name+".conflist")
- Expect(path).To(BeARegularFile())
- grepInFile(path, `"type": "podman-machine",`)
- })
-
It("network inspect partial ID", func() {
network := types.Network{Name: "net4"}
network1, err := libpodNet.NetworkCreate(network)
diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go
index 3e9cdaa47..41e3e414e 100644
--- a/libpod/network/cni/network.go
+++ b/libpod/network/cni/network.go
@@ -150,6 +150,13 @@ func (n *cniNetwork) loadNetworks() error {
continue
}
+ // podman < v4.0 used the podman-machine cni plugin for podman machine port forwarding
+ // since this is now build into podman we no longer use the plugin
+ // old configs may still contain it so we just remove it here
+ if n.isMachine {
+ conf = removeMachinePlugin(conf)
+ }
+
if _, err := n.cniConf.ValidateNetworkList(context.Background(), conf); err != nil {
logrus.Warnf("Error validating CNI config file %s: %v", file, err)
continue
diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go
index 667ed3ab1..d0ff49b73 100644
--- a/libpod/network/cni/run.go
+++ b/libpod/network/cni/run.go
@@ -135,8 +135,8 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
cniInt := cniResult.Interfaces[*ip.Interface]
netInt, ok := interfaces[cniInt.Name]
if ok {
- netInt.Networks = append(netInt.Networks, types.NetAddress{
- Subnet: types.IPNet{IPNet: ip.Address},
+ netInt.Subnets = append(netInt.Subnets, types.NetAddress{
+ IPNet: types.IPNet{IPNet: ip.Address},
Gateway: ip.Gateway,
})
interfaces[cniInt.Name] = netInt
@@ -147,8 +147,8 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
}
interfaces[cniInt.Name] = types.NetInterface{
MacAddress: types.HardwareAddr(mac),
- Networks: []types.NetAddress{{
- Subnet: types.IPNet{IPNet: ip.Address},
+ Subnets: []types.NetAddress{{
+ IPNet: types.IPNet{IPNet: ip.Address},
Gateway: ip.Gateway,
}},
}
diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go
index 6c54f82ef..f6cc2d412 100644
--- a/libpod/network/cni/run_test.go
+++ b/libpod/network/cni/run_test.go
@@ -133,8 +133,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
Expect(res[defNet].DNSServerIPs).To(BeEmpty())
@@ -170,8 +170,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP).To(Equal(ip))
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP).To(Equal(ip))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
Expect(res[defNet].DNSServerIPs).To(BeEmpty())
@@ -209,8 +209,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
Expect(res[defNet].DNSServerIPs).To(BeEmpty())
@@ -263,8 +263,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- containerIP := res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ containerIP := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()
Expect(containerIP).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
@@ -324,8 +324,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
for _, proto := range []string{"tcp", "udp"} {
@@ -386,8 +386,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveKey(netName1))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
- Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
- ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1))
+ ipInt1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP
Expect(ipInt1).ToNot(BeEmpty())
macInt1 := res[netName1].Interfaces[intName1].MacAddress
Expect(macInt1).To(HaveLen(6))
@@ -436,8 +436,8 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveKey(netName2))
Expect(res[netName2].Interfaces).To(HaveKey(intName2))
- Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1))
- ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ Expect(res[netName2].Interfaces[intName2].Subnets).To(HaveLen(1))
+ ipInt2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP
Expect(ipInt2).ToNot(BeEmpty())
macInt2 := res[netName2].Interfaces[intName2].MacAddress
Expect(macInt2).To(HaveLen(6))
@@ -576,16 +576,16 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveKey(netName1))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
- Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
- ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1))
+ ipInt1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP
Expect(ipInt1.String()).To(ContainSubstring("192.168.0."))
macInt1 := res[netName1].Interfaces[intName1].MacAddress
Expect(macInt1).To(HaveLen(6))
Expect(res).To(HaveKey(netName2))
Expect(res[netName2].Interfaces).To(HaveKey(intName2))
- Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1))
- ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ Expect(res[netName2].Interfaces[intName2].Subnets).To(HaveLen(1))
+ ipInt2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP
Expect(ipInt2.String()).To(ContainSubstring("192.168.1."))
macInt2 := res[netName2].Interfaces[intName2].MacAddress
Expect(macInt2).To(HaveLen(6))
@@ -701,13 +701,13 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName))
Expect(res[netName].Interfaces).To(HaveKey(interfaceName))
- Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2))
- Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String()))
- Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(subnet1.Mask))
- Expect(res[netName].Interfaces[interfaceName].Networks[0].Gateway).To(Equal(net.ParseIP("192.168.0.1")))
- Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String()))
- Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(subnet2.Mask))
- Expect(res[netName].Interfaces[interfaceName].Networks[1].Gateway).To(Equal(net.ParseIP("fd41:0a75:2ca0:48a9::1")))
+ Expect(res[netName].Interfaces[interfaceName].Subnets).To(HaveLen(2))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.IP.String()).To(Equal(ip1.String()))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.Mask).To(Equal(subnet1.Mask))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[0].Gateway).To(Equal(net.ParseIP("192.168.0.1")))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.IP.String()).To(Equal(ip2.String()))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.Mask).To(Equal(subnet2.Mask))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[1].Gateway).To(Equal(net.ParseIP("fd41:0a75:2ca0:48a9::1")))
Expect(res[netName].Interfaces[interfaceName].MacAddress).To(Equal(types.HardwareAddr(mac)))
// default network has no dns
Expect(res[netName].DNSServerIPs).To(BeEmpty())
@@ -799,9 +799,9 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName))
Expect(res[netName].Interfaces).To(HaveKey(intName))
- Expect(res[netName].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[netName].Interfaces[intName].Networks[0].Subnet.IP.String()).To(Equal(ip))
- Expect(res[netName].Interfaces[intName].Networks[0].Subnet.Mask).To(Equal(net.CIDRMask(24, 32)))
+ Expect(res[netName].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[netName].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(Equal(ip))
+ Expect(res[netName].Interfaces[intName].Subnets[0].IPNet.Mask).To(Equal(net.CIDRMask(24, 32)))
// check in the container namespace if the settings are applied
err = netNSContainer.Do(func(_ ns.NetNS) error {
@@ -902,11 +902,11 @@ var _ = Describe("run CNI", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName))
Expect(res[netName].Interfaces).To(HaveKey(interfaceName))
- Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2))
- Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String()))
- Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(mask1))
- Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String()))
- Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(mask2))
+ Expect(res[netName].Interfaces[interfaceName].Subnets).To(HaveLen(2))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.IP.String()).To(Equal(ip1.String()))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.Mask).To(Equal(mask1))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.IP.String()).To(Equal(ip2.String()))
+ Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.Mask).To(Equal(mask2))
// dualstack network dns
Expect(res[netName].DNSServerIPs).To(HaveLen(2))
Expect(res[netName].DNSSearchDomains).To(HaveLen(1))
diff --git a/libpod/network/netavark/run_test.go b/libpod/network/netavark/run_test.go
index 3279203cc..67dc51c10 100644
--- a/libpod/network/netavark/run_test.go
+++ b/libpod/network/netavark/run_test.go
@@ -131,10 +131,10 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- ip := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ ip := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP
Expect(ip.String()).To(ContainSubstring("10.88.0."))
- gw := res[defNet].Interfaces[intName].Networks[0].Gateway
+ gw := res[defNet].Interfaces[intName].Subnets[0].Gateway
util.NormalizeIP(&gw)
Expect(gw.String()).To(Equal("10.88.0.1"))
macAddress := res[defNet].Interfaces[intName].MacAddress
@@ -222,8 +222,8 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- ip1 := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ ip1 := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP
Expect(ip1.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
@@ -246,8 +246,8 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- ip2 := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ ip2 := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP
Expect(ip2.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
Expect(ip1.Equal(ip2)).To(BeFalse(), "IP1 %s should not be equal to IP2 %s", ip1.String(), ip2.String())
@@ -286,14 +286,14 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName))
Expect(res[netName].Interfaces).To(HaveKey(intName))
- Expect(res[netName].Interfaces[intName].Networks).To(HaveLen(2))
- ip1 := res[netName].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(res[netName].Interfaces[intName].Subnets).To(HaveLen(2))
+ ip1 := res[netName].Interfaces[intName].Subnets[0].IPNet.IP
Expect(ip1.String()).To(ContainSubstring("10.0.0."))
- gw1 := res[netName].Interfaces[intName].Networks[0].Gateway
+ gw1 := res[netName].Interfaces[intName].Subnets[0].Gateway
Expect(gw1.String()).To(Equal("10.0.0.1"))
- ip2 := res[netName].Interfaces[intName].Networks[1].Subnet.IP
+ ip2 := res[netName].Interfaces[intName].Subnets[1].IPNet.IP
Expect(ip2.String()).To(ContainSubstring("fd10:88:a::"))
- gw2 := res[netName].Interfaces[intName].Networks[0].Gateway
+ gw2 := res[netName].Interfaces[intName].Subnets[0].Gateway
Expect(gw2.String()).To(Equal("fd10:88:a::1"))
Expect(res[netName].Interfaces[intName].MacAddress).To(HaveLen(6))
@@ -380,14 +380,14 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveKey(netName2))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
Expect(res[netName2].Interfaces).To(HaveKey(intName2))
- Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
- ip1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1))
+ ip1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP
Expect(ip1.String()).To(ContainSubstring("10.0.0."))
- gw1 := res[netName1].Interfaces[intName1].Networks[0].Gateway
+ gw1 := res[netName1].Interfaces[intName1].Subnets[0].Gateway
Expect(gw1.String()).To(Equal("10.0.0.1"))
- ip2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ ip2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP
Expect(ip2.String()).To(ContainSubstring("10.1.0."))
- gw2 := res[netName2].Interfaces[intName2].Networks[0].Gateway
+ gw2 := res[netName2].Interfaces[intName2].Subnets[0].Gateway
Expect(gw2.String()).To(Equal("10.1.0.1"))
mac1 := res[netName1].Interfaces[intName1].MacAddress
Expect(mac1).To(HaveLen(6))
@@ -481,8 +481,8 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
Expect(res[defNet].DNSServerIPs).To(BeEmpty())
@@ -535,8 +535,8 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- containerIP := res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ containerIP := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()
Expect(containerIP).To(ContainSubstring("10.88.0."))
Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
// default network has no dns
@@ -593,10 +593,10 @@ var _ = Describe("run netavark", func() {
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(defNet))
Expect(res[defNet].Interfaces).To(HaveKey(intName))
- Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1))
- ip := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1))
+ ip := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP
Expect(ip.String()).To(ContainSubstring("10.88.0."))
- gw := res[defNet].Interfaces[intName].Networks[0].Gateway
+ gw := res[defNet].Interfaces[intName].Subnets[0].Gateway
Expect(gw.String()).To(Equal("10.88.0.1"))
macAddress := res[defNet].Interfaces[intName].MacAddress
Expect(macAddress).To(HaveLen(6))
diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go
index ba5e018fd..105641e70 100644
--- a/libpod/network/types/network.go
+++ b/libpod/network/types/network.go
@@ -38,11 +38,11 @@ type Network struct {
ID string `json:"id"`
// Driver for this Network, e.g. bridge, macvlan...
Driver string `json:"driver"`
- // InterfaceName is the network interface name on the host.
+ // NetworkInterface is the network interface name on the host.
NetworkInterface string `json:"network_interface,omitempty"`
// Created contains the timestamp when this network was created.
Created time.Time `json:"created,omitempty"`
- // Subnets to use.
+ // Subnets to use for this network.
Subnets []Subnet `json:"subnets,omitempty"`
// IPv6Enabled if set to true an ipv6 subnet should be created for this net.
IPv6Enabled bool `json:"ipv6_enabled"`
@@ -177,24 +177,24 @@ type StatusBlock struct {
// NetInterface contains the settings for a given network interface.
type NetInterface struct {
- // Networks list of assigned subnets with their gateway.
- Networks []NetAddress `json:"networks,omitempty"`
+ // Subnets list of assigned subnets with their gateway.
+ Subnets []NetAddress `json:"subnets,omitempty"`
// MacAddress for this Interface.
MacAddress HardwareAddr `json:"mac_address"`
}
-// NetAddress contains the subnet and gateway.
+// NetAddress contains the ip address, subnet and gateway.
type NetAddress struct {
- // Subnet of this NetAddress. Note that the subnet contains the
- // actual ip of the net interface and not the network address.
- Subnet IPNet `json:"subnet"`
- // Gateway for the Subnet. This can be nil if there is no gateway, e.g. internal network.
+ // IPNet of this NetAddress. Note that this is a subnet but it has to contain the
+ // actual ip of the network interface and not the network address.
+ IPNet IPNet `json:"ipnet"`
+ // Gateway for the network. This can be empty if there is no gateway, e.g. internal network.
Gateway net.IP `json:"gateway,omitempty"`
}
// PerNetworkOptions are options which should be set on a per network basis.
type PerNetworkOptions struct {
- // StaticIPv4 for this container. Optional.
+ // StaticIPs for this container. Optional.
StaticIPs []net.IP `json:"static_ips,omitempty"`
// Aliases contains a list of names which the dns server should resolve
// to this container. Should only be set when DNSEnabled is true on the Network.
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index fc91155fa..7d1214183 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -4,6 +4,7 @@ package libpod
import (
"crypto/rand"
+ "crypto/sha1"
"fmt"
"io/ioutil"
"net"
@@ -87,12 +88,28 @@ func (c *Container) GetNetworkAliases(netName string) ([]string, error) {
return aliases, nil
}
+// convertPortMappings will remove the HostIP part from the ports when running inside podman machine.
+// This is need because a HostIP of 127.0.0.1 would now allow the gvproxy forwarder to reach to open ports.
+// For machine the HostIP must only be used by gvproxy and never in the VM.
+func (c *Container) convertPortMappings() []types.PortMapping {
+ if !c.runtime.config.Engine.MachineEnabled || len(c.config.PortMappings) == 0 {
+ return c.config.PortMappings
+ }
+ // if we run in a machine VM we have to ignore the host IP part
+ newPorts := make([]types.PortMapping, 0, len(c.config.PortMappings))
+ for _, port := range c.config.PortMappings {
+ port.HostIP = ""
+ newPorts = append(newPorts, port)
+ }
+ return newPorts
+}
+
func (c *Container) getNetworkOptions() (types.NetworkOptions, error) {
opts := types.NetworkOptions{
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- opts.PortMappings = c.config.PortMappings
+ opts.PortMappings = c.convertPortMappings()
networks, _, err := c.networks()
if err != nil {
return opts, err
@@ -384,10 +401,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return nil, nil
}
var rootlessNetNS *RootlessNetNS
- runDir, err := util.GetRuntimeDir()
- if err != nil {
- return nil, err
- }
+ runDir := r.config.Engine.TmpDir
lfile := filepath.Join(runDir, "rootless-netns.lock")
lock, err := lockfile.GetLockfile(lfile)
@@ -413,7 +427,15 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
if err != nil {
return nil, err
}
- path := filepath.Join(nsDir, rootlessNetNsName)
+
+ // create a hash from the static dir
+ // the cleanup will check if there are running containers
+ // if you run a several libpod instances with different root/runroot directories this check will fail
+ // we want one netns for each libpod static dir so we use the hash to prevent name collisions
+ hash := sha1.Sum([]byte(r.config.Engine.StaticDir))
+ netnsName := fmt.Sprintf("%s-%x", rootlessNetNsName, hash[:10])
+
+ path := filepath.Join(nsDir, netnsName)
ns, err := ns.GetNS(path)
if err != nil {
if !new {
@@ -421,8 +443,8 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return nil, errors.Wrap(err, "error getting rootless network namespace")
}
// create a new namespace
- logrus.Debug("creating rootless network namespace")
- ns, err = netns.NewNSWithName(rootlessNetNsName)
+ logrus.Debugf("creating rootless network namespace with name %q", netnsName)
+ ns, err = netns.NewNSWithName(netnsName)
if err != nil {
return nil, errors.Wrap(err, "error creating rootless network namespace")
}
@@ -591,32 +613,9 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return rootlessNetNS, nil
}
-// setPrimaryMachineIP is used for podman-machine and it sets
-// and environment variable with the IP address of the podman-machine
-// host.
-func setPrimaryMachineIP() error {
- // no connection is actually made here
- conn, err := net.Dial("udp", "8.8.8.8:80")
- if err != nil {
- return err
- }
- defer func() {
- if err := conn.Close(); err != nil {
- logrus.Error(err)
- }
- }()
- addr := conn.LocalAddr().(*net.UDPAddr)
- return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String())
-}
-
// setUpNetwork will set up the the networks, on error it will also tear down the cni
// networks. If rootless it will join/create the rootless network namespace.
func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string]types.StatusBlock, error) {
- if r.config.MachineEnabled() {
- if err := setPrimaryMachineIP(); err != nil {
- return nil, err
- }
- }
rootlessNetNS, err := r.GetRootlessNetNs(true)
if err != nil {
return nil, err
@@ -650,7 +649,18 @@ func getCNIPodName(c *Container) string {
}
// Create and configure a new network namespace for a container
-func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]types.StatusBlock, error) {
+func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[string]types.StatusBlock, rerr error) {
+ if err := r.exposeMachinePorts(ctr.config.PortMappings); err != nil {
+ return nil, err
+ }
+ defer func() {
+ // make sure to unexpose the gvproxy ports when an error happens
+ if rerr != nil {
+ if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
+ logrus.Errorf("failed to free gvproxy machine ports: %v", err)
+ }
+ }
+ }()
if ctr.config.NetMode.IsSlirp4netns() {
return nil, r.setupSlirp4netns(ctr, ctrNS)
}
@@ -836,6 +846,10 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
// Tear down a network namespace, undoing all state associated with it.
func (r *Runtime) teardownNetNS(ctr *Container) error {
+ if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
+ // do not return an error otherwise we would prevent network cleanup
+ logrus.Errorf("failed to free gvproxy machine ports: %v", err)
+ }
if err := r.teardownCNI(ctr); err != nil {
return err
}
@@ -929,8 +943,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu
Aliases: aliases[network],
StaticMAC: netInt.MacAddress,
}
- for _, netAddress := range netInt.Networks {
- perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP)
+ for _, netAddress := range netInt.Subnets {
+ perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.IPNet.IP)
}
// Normally interfaces have a length of 1, only for some special cni configs we could get more.
// For now just use the first interface to get the ips this should be good enough for most cases.
@@ -1116,25 +1130,25 @@ func (c *Container) setupNetworkDescriptions(networks []string) error {
func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) {
config := define.InspectBasicNetworkConfig{}
for _, netInt := range result.Interfaces {
- for _, netAddress := range netInt.Networks {
- size, _ := netAddress.Subnet.Mask.Size()
- if netAddress.Subnet.IP.To4() != nil {
+ for _, netAddress := range netInt.Subnets {
+ size, _ := netAddress.IPNet.Mask.Size()
+ if netAddress.IPNet.IP.To4() != nil {
//ipv4
if config.IPAddress == "" {
- config.IPAddress = netAddress.Subnet.IP.String()
+ config.IPAddress = netAddress.IPNet.IP.String()
config.IPPrefixLen = size
config.Gateway = netAddress.Gateway.String()
} else {
- config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, netAddress.Subnet.IP.String())
+ config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, define.Address{Addr: netAddress.IPNet.IP.String(), PrefixLength: size})
}
} else {
//ipv6
if config.GlobalIPv6Address == "" {
- config.GlobalIPv6Address = netAddress.Subnet.IP.String()
+ config.GlobalIPv6Address = netAddress.IPNet.IP.String()
config.GlobalIPv6PrefixLen = size
config.IPv6Gateway = netAddress.Gateway.String()
} else {
- config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, netAddress.Subnet.IP.String())
+ config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, define.Address{Addr: netAddress.IPNet.IP.String(), PrefixLength: size})
}
}
}
@@ -1206,7 +1220,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- opts.PortMappings = c.config.PortMappings
+ opts.PortMappings = c.convertPortMappings()
eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
if !exists {
return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
@@ -1298,7 +1312,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- opts.PortMappings = c.config.PortMappings
+ opts.PortMappings = c.convertPortMappings()
eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
if !exists {
return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
diff --git a/libpod/networking_linux_test.go b/libpod/networking_linux_test.go
index 06bf05723..d925b69f7 100644
--- a/libpod/networking_linux_test.go
+++ b/libpod/networking_linux_test.go
@@ -2,10 +2,14 @@ package libpod
import (
"fmt"
+ "net"
+ "reflect"
"testing"
- "github.com/containers/podman/v3/libpod/network/types"
"github.com/stretchr/testify/assert"
+
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
)
func Test_ocicniPortsToNetTypesPorts(t *testing.T) {
@@ -234,6 +238,217 @@ func Test_ocicniPortsToNetTypesPorts(t *testing.T) {
}
}
+func Test_resultToBasicNetworkConfig(t *testing.T) {
+ testCases := []struct {
+ description string
+ expectError bool
+ inputResult types.StatusBlock
+ expectedNetworkConfig define.InspectBasicNetworkConfig
+ }{
+ {
+ description: "single secondary IPv4 address is shown as define.Address",
+ inputResult: types.StatusBlock{
+ Interfaces: map[string]types.NetInterface{
+ "eth1": {
+ Subnets: []types.NetAddress{
+ {
+ Gateway: net.ParseIP("172.26.0.1"),
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("172.26.0.2"),
+ Mask: net.CIDRMask(20, 32),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("172.26.0.3"),
+ Mask: net.CIDRMask(10, 32),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expectedNetworkConfig: define.InspectBasicNetworkConfig{
+ IPAddress: "172.26.0.2",
+ IPPrefixLen: 20,
+ Gateway: "172.26.0.1",
+ SecondaryIPAddresses: []define.Address{
+ {
+ Addr: "172.26.0.3",
+ PrefixLength: 10,
+ },
+ },
+ },
+ },
+ {
+ description: "multiple secondary IPv4 addresses are shown as define.Address",
+ inputResult: types.StatusBlock{
+ Interfaces: map[string]types.NetInterface{
+ "eth1": {
+ Subnets: []types.NetAddress{
+ {
+ Gateway: net.ParseIP("172.26.0.1"),
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("172.26.0.2"),
+ Mask: net.CIDRMask(20, 32),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("172.26.0.3"),
+ Mask: net.CIDRMask(10, 32),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("172.26.0.4"),
+ Mask: net.CIDRMask(24, 32),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expectedNetworkConfig: define.InspectBasicNetworkConfig{
+ IPAddress: "172.26.0.2",
+ IPPrefixLen: 20,
+ Gateway: "172.26.0.1",
+ SecondaryIPAddresses: []define.Address{
+ {
+ Addr: "172.26.0.3",
+ PrefixLength: 10,
+ },
+ {
+ Addr: "172.26.0.4",
+ PrefixLength: 24,
+ },
+ },
+ },
+ },
+ {
+ description: "single secondary IPv6 address is shown as define.Address",
+ inputResult: types.StatusBlock{
+ Interfaces: map[string]types.NetInterface{
+ "eth1": {
+ Subnets: []types.NetAddress{
+ {
+ Gateway: net.ParseIP("ff02::fb"),
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("ff02::fc"),
+ Mask: net.CIDRMask(20, 128),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("ff02::fd"),
+ Mask: net.CIDRMask(10, 128),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expectedNetworkConfig: define.InspectBasicNetworkConfig{
+ GlobalIPv6Address: "ff02::fc",
+ GlobalIPv6PrefixLen: 20,
+ IPv6Gateway: "ff02::fb",
+ SecondaryIPv6Addresses: []define.Address{
+ {
+ Addr: "ff02::fd",
+ PrefixLength: 10,
+ },
+ },
+ },
+ },
+ {
+ description: "multiple secondary IPv6 addresses are shown as define.Address",
+ inputResult: types.StatusBlock{
+ Interfaces: map[string]types.NetInterface{
+ "eth1": {
+ Subnets: []types.NetAddress{
+ {
+ Gateway: net.ParseIP("ff02::fb"),
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("ff02::fc"),
+ Mask: net.CIDRMask(20, 128),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("ff02::fd"),
+ Mask: net.CIDRMask(10, 128),
+ },
+ },
+ },
+ {
+ IPNet: types.IPNet{
+ IPNet: net.IPNet{
+ IP: net.ParseIP("ff02::fe"),
+ Mask: net.CIDRMask(24, 128),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expectedNetworkConfig: define.InspectBasicNetworkConfig{
+ GlobalIPv6Address: "ff02::fc",
+ GlobalIPv6PrefixLen: 20,
+ IPv6Gateway: "ff02::fb",
+ SecondaryIPv6Addresses: []define.Address{
+ {
+ Addr: "ff02::fd",
+ PrefixLength: 10,
+ },
+ {
+ Addr: "ff02::fe",
+ PrefixLength: 24,
+ },
+ },
+ },
+ },
+ }
+
+ for _, tcl := range testCases {
+ tc := tcl
+ t.Run(tc.description, func(t *testing.T) {
+ t.Parallel()
+ actualNetworkConfig, err := resultToBasicNetworkConfig(tc.inputResult)
+
+ if tc.expectError && err == nil {
+ t.Fatalf("Expected error didn't happen")
+ }
+
+ if !tc.expectError && err != nil {
+ t.Fatalf("Unexpected error happened: %v", err)
+ }
+
+ if !reflect.DeepEqual(tc.expectedNetworkConfig, actualNetworkConfig) {
+ t.Fatalf(
+ "Expected networkConfig %+v didn't match actual value %+v", tc.expectedNetworkConfig, actualNetworkConfig)
+ }
+ })
+ }
+}
+
func benchmarkOCICNIPortsToNetTypesPorts(b *testing.B, ports []types.OCICNIPortMapping) {
for n := 0; n < b.N; n++ {
ocicniPortsToNetTypesPorts(ports)
diff --git a/libpod/networking_machine.go b/libpod/networking_machine.go
new file mode 100644
index 000000000..7cb2a00f7
--- /dev/null
+++ b/libpod/networking_machine.go
@@ -0,0 +1,121 @@
+package libpod
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/sirupsen/logrus"
+)
+
+const machineGvproxyEndpoint = "gateway.containers.internal"
+
+// machineExpose is the struct for the gvproxy port forwarding api send via json
+type machineExpose struct {
+ // Local is the local address on the vm host, format is ip:port
+ Local string `json:"local"`
+ // Remote is used to specify the vm ip:port
+ Remote string `json:"remote,omitempty"`
+ // Protocol to forward, tcp or udp
+ Protocol string `json:"protocol"`
+}
+
+func requestMachinePorts(expose bool, ports []types.PortMapping) error {
+ url := "http://" + machineGvproxyEndpoint + "/services/forwarder/"
+ if expose {
+ url = url + "expose"
+ } else {
+ url = url + "unexpose"
+ }
+ ctx := context.Background()
+ client := &http.Client{}
+ buf := new(bytes.Buffer)
+ for num, port := range ports {
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ for i := uint16(0); i < port.Range; i++ {
+ machinePort := machineExpose{
+ Local: net.JoinHostPort(port.HostIP, strconv.FormatInt(int64(port.HostPort+i), 10)),
+ Protocol: protocol,
+ }
+ if expose {
+ // only set the remote port the ip will be automatically be set by gvproxy
+ machinePort.Remote = ":" + strconv.FormatInt(int64(port.HostPort+i), 10)
+ }
+
+ // post request
+ if err := json.NewEncoder(buf).Encode(machinePort); err != nil {
+ if expose {
+ // in case of an error make sure to unexpose the other ports
+ if cerr := requestMachinePorts(false, ports[:num]); cerr != nil {
+ logrus.Errorf("failed to free gvproxy machine ports: %v", cerr)
+ }
+ }
+ return err
+ }
+ if err := makeMachineRequest(ctx, client, url, buf); err != nil {
+ if expose {
+ // in case of an error make sure to unexpose the other ports
+ if cerr := requestMachinePorts(false, ports[:num]); cerr != nil {
+ logrus.Errorf("failed to free gvproxy machine ports: %v", cerr)
+ }
+ }
+ return err
+ }
+ buf.Reset()
+ }
+ }
+ }
+ return nil
+}
+
+func makeMachineRequest(ctx context.Context, client *http.Client, url string, buf io.Reader) error {
+ //var buf io.ReadWriter
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, buf)
+ if err != nil {
+ return err
+ }
+ req.Header.Add("Accept", "application/json")
+ req.Header.Add("Content-Type", "application/json")
+ resp, err := client.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return annotateGvproxyResponseError(resp.Body)
+ }
+ return nil
+}
+
+func annotateGvproxyResponseError(r io.Reader) error {
+ b, err := ioutil.ReadAll(r)
+ if err == nil && len(b) > 0 {
+ return fmt.Errorf("something went wrong with the request: %q", string(b))
+ }
+ return errors.New("something went wrong with the request, could not read response")
+}
+
+// exposeMachinePorts exposes the ports for podman machine via gvproxy
+func (r *Runtime) exposeMachinePorts(ports []types.PortMapping) error {
+ if !r.config.Engine.MachineEnabled {
+ return nil
+ }
+ return requestMachinePorts(true, ports)
+}
+
+// unexposeMachinePorts closes the ports for podman machine via gvproxy
+func (r *Runtime) unexposeMachinePorts(ports []types.PortMapping) error {
+ if !r.config.Engine.MachineEnabled {
+ return nil
+ }
+ return requestMachinePorts(false, ports)
+}
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index 674075e23..cc1b3cfdc 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -509,7 +509,7 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin
childIP := getRootlessPortChildIP(ctr, netStatus)
cfg := rootlessport.Config{
- Mappings: ctr.config.PortMappings,
+ Mappings: ctr.convertPortMappings(),
NetNSPath: netnsPath,
ExitFD: 3,
ReadyFD: 4,
@@ -594,7 +594,7 @@ func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd
// for each port we want to add we need to open a connection to the slirp4netns control socket
// and send the add_hostfwd command.
- for _, i := range ctr.config.PortMappings {
+ for _, i := range ctr.convertPortMappings() {
conn, err := net.Dial("unix", apiSocket)
if err != nil {
return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
@@ -660,12 +660,12 @@ func getRootlessPortChildIP(c *Container, netStatus map[string]types.StatusBlock
var ipv6 net.IP
for _, status := range netStatus {
for _, netInt := range status.Interfaces {
- for _, netAddress := range netInt.Networks {
- ipv4 := netAddress.Subnet.IP.To4()
+ for _, netAddress := range netInt.Subnets {
+ ipv4 := netAddress.IPNet.IP.To4()
if ipv4 != nil {
return ipv4.String()
}
- ipv6 = netAddress.Subnet.IP
+ ipv6 = netAddress.IPNet.IP
}
}
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 533a0d78b..bcf45ec8d 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -30,6 +30,7 @@ import (
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/rootless"
+ "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
"github.com/containers/storage/pkg/homedir"
@@ -794,6 +795,9 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
if options.TCPEstablished {
args = append(args, "--tcp-established")
}
+ if options.FileLocks {
+ args = append(args, "--file-locks")
+ }
if !options.PreCheckPoint && options.KeepRunning {
args = append(args, "--leave-running")
}
@@ -1071,11 +1075,15 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
args = append(args, "--no-pivot")
}
- if len(ctr.config.ExitCommand) > 0 {
- args = append(args, "--exit-command", ctr.config.ExitCommand[0])
- for _, arg := range ctr.config.ExitCommand[1:] {
- args = append(args, []string{"--exit-command-arg", arg}...)
- }
+ exitCommand, err := specgenutil.CreateExitCommandArgs(ctr.runtime.storageConfig, ctr.runtime.config, logrus.IsLevelEnabled(logrus.DebugLevel), ctr.AutoRemove(), false)
+ if err != nil {
+ return 0, err
+ }
+ exitCommand = append(exitCommand, ctr.config.ID)
+
+ args = append(args, "--exit-command", exitCommand[0])
+ for _, arg := range exitCommand[1:] {
+ args = append(args, []string{"--exit-command-arg", arg}...)
}
// Pass down the LISTEN_* environment (see #10443).
@@ -1101,6 +1109,9 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if restoreOptions.TCPEstablished {
args = append(args, "--runtime-opt", "--tcp-established")
}
+ if restoreOptions.FileLocks {
+ args = append(args, "--runtime-opt", "--file-locks")
+ }
if restoreOptions.Pod != "" {
mountLabel := ctr.config.MountLabel
processLabel := ctr.config.ProcessLabel
diff --git a/libpod/options.go b/libpod/options.go
index 0cc4c784c..3f0f9fbe0 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -835,20 +835,6 @@ func WithIDMappings(idmappings storage.IDMappingOptions) CtrCreateOption {
}
}
-// WithExitCommand sets the ExitCommand for the container, appending on the ctr.ID() to the end
-func WithExitCommand(exitCommand []string) CtrCreateOption {
- return func(ctr *Container) error {
- if ctr.valid {
- return define.ErrCtrFinalized
- }
-
- ctr.config.ExitCommand = exitCommand
- ctr.config.ExitCommand = append(ctr.config.ExitCommand, ctr.ID())
-
- return nil
- }
-}
-
// WithUTSNSFromPod indicates the the container should join the UTS namespace of
// its pod
func WithUTSNSFromPod(p *Pod) CtrCreateOption {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 114bf9315..05f22c1fe 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -186,8 +186,6 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
// If the ID is empty a new name for the restored container was requested
if ctr.config.ID == "" {
ctr.config.ID = stringid.GenerateNonCryptoID()
- // Fixup ExitCommand with new ID
- ctr.config.ExitCommand[len(ctr.config.ExitCommand)-1] = ctr.config.ID
}
// Reset the log path to point to the default
ctr.config.LogPath = ""
diff --git a/pause/pause.c b/pause/pause.c
deleted file mode 100644
index 1e363bd7a..000000000
--- a/pause/pause.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Copyright 2016 The Kubernetes Authors.
-Copyright 2021 The Podman Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#define STRINGIFY(x) #x
-#define VERSION_STRING(x) STRINGIFY(x)
-
-#ifndef VERSION
-#define VERSION HEAD
-#endif
-
-static void sigdown(int signo) {
- psignal(signo, "Shutting down, got signal");
- exit(0);
-}
-
-static void sigreap(int signo) {
- while (waitpid(-1, NULL, WNOHANG) > 0)
- ;
-}
-
-int main(int argc, char **argv) {
- int i;
- for (i = 1; i < argc; ++i) {
- if (!strcasecmp(argv[i], "-v")) {
- printf("pause.c %s\n", VERSION_STRING(VERSION));
- return 0;
- }
- }
-
- if (getpid() != 1)
- /* Not an error because pause sees use outside of infra containers. */
- fprintf(stderr, "Warning: pause should be the first process\n");
-
- if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
- return 1;
- if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
- return 2;
- if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
- .sa_flags = SA_NOCLDSTOP},
- NULL) < 0)
- return 3;
-
- for (;;)
- pause();
- fprintf(stderr, "Error: infinite loop terminated\n");
- return 42;
-}
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 1e175d664..d5abb6e44 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -55,7 +55,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
newImage, resolvedName, err := runtime.LibimageRuntime().LookupImage(body.Config.Image, nil)
if err != nil {
if errors.Cause(err) == storage.ErrImageUnknown {
- utils.Error(w, "No such image", http.StatusNotFound, err)
+ utils.Error(w, "No such image", http.StatusNotFound, errors.Wrap(err, "No such image"))
return
}
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index ea61a1013..76f720bf2 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -12,7 +12,7 @@ import (
"github.com/containers/podman/v3/pkg/api/handlers/utils"
"github.com/containers/podman/v3/pkg/api/server/idle"
api "github.com/containers/podman/v3/pkg/api/types"
- "github.com/containers/podman/v3/pkg/specgen/generate"
+ "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -65,7 +65,7 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Automatically log to syslog if the server has log-level=debug set
- exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), true, true)
+ exitCommandArgs, err := specgenutil.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), true, true)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 3aeebc334..d5da22a91 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -1,9 +1,11 @@
package libpod
import (
+ "fmt"
"io/ioutil"
"net/http"
"os"
+ "strings"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
@@ -206,7 +208,9 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
}
func Checkpoint(w http.ResponseWriter, r *http.Request) {
- var targetFile string
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Keep bool `schema:"keep"`
@@ -215,6 +219,8 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) {
Export bool `schema:"export"`
IgnoreRootFS bool `schema:"ignoreRootFS"`
PrintStats bool `schema:"printStats"`
+ PreCheckpoint bool `schema:"preCheckpoint"`
+ WithPrevious bool `schema:"withPrevious"`
}{
// override any golang type defaults
}
@@ -224,66 +230,70 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
+
name := utils.GetName(r)
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
+ if _, err := runtime.LookupContainer(name); err != nil {
utils.ContainerNotFound(w, name, err)
return
}
+ names := []string{name}
+
+ options := entities.CheckpointOptions{
+ Keep: query.Keep,
+ LeaveRunning: query.LeaveRunning,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootFS: query.IgnoreRootFS,
+ PrintStats: query.PrintStats,
+ PreCheckPoint: query.PreCheckpoint,
+ WithPrevious: query.WithPrevious,
+ }
+
if query.Export {
- tmpFile, err := ioutil.TempFile("", "checkpoint")
+ f, err := ioutil.TempFile("", "checkpoint")
if err != nil {
utils.InternalServerError(w, err)
return
}
- defer os.Remove(tmpFile.Name())
- if err := tmpFile.Close(); err != nil {
+ defer os.Remove(f.Name())
+ if err := f.Close(); err != nil {
utils.InternalServerError(w, err)
return
}
- targetFile = tmpFile.Name()
- }
- options := libpod.ContainerCheckpointOptions{
- Keep: query.Keep,
- KeepRunning: query.LeaveRunning,
- TCPEstablished: query.TCPEstablished,
- IgnoreRootfs: query.IgnoreRootFS,
- PrintStats: query.PrintStats,
- }
- if query.Export {
- options.TargetFile = targetFile
+ options.Export = f.Name()
}
- criuStatistics, runtimeCheckpointDuration, err := ctr.Checkpoint(r.Context(), options)
+
+ reports, err := containerEngine.ContainerCheckpoint(r.Context(), names, options)
if err != nil {
utils.InternalServerError(w, err)
return
}
- if query.Export {
- f, err := os.Open(targetFile)
- if err != nil {
- utils.InternalServerError(w, err)
+
+ if !query.Export {
+ if len(reports) != 1 {
+ utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports)))
return
}
- defer f.Close()
- utils.WriteResponse(w, http.StatusOK, f)
+ if reports[0].Err != nil {
+ utils.InternalServerError(w, reports[0].Err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, reports[0])
+ return
+ }
+
+ f, err := os.Open(options.Export)
+ if err != nil {
+ utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(
- w,
- http.StatusOK,
- entities.CheckpointReport{
- Id: ctr.ID(),
- RuntimeDuration: runtimeCheckpointDuration,
- CRIUStatistics: criuStatistics,
- },
- )
+ defer f.Close()
+ utils.WriteResponse(w, http.StatusOK, f)
}
func Restore(w http.ResponseWriter, r *http.Request) {
- var (
- targetFile string
- )
+ runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Keep bool `schema:"keep"`
@@ -295,6 +305,7 @@ func Restore(w http.ResponseWriter, r *http.Request) {
IgnoreStaticIP bool `schema:"ignoreStaticIP"`
IgnoreStaticMAC bool `schema:"ignoreStaticMAC"`
PrintStats bool `schema:"printStats"`
+ PublishPorts string `schema:"publishPorts"`
}{
// override any golang type defaults
}
@@ -303,53 +314,55 @@ func Restore(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
- name := utils.GetName(r)
- runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
- utils.ContainerNotFound(w, name, err)
- return
+
+ options := entities.RestoreOptions{
+ Name: query.Name,
+ Keep: query.Keep,
+ TCPEstablished: query.TCPEstablished,
+ IgnoreRootFS: query.IgnoreRootFS,
+ IgnoreVolumes: query.IgnoreVolumes,
+ IgnoreStaticIP: query.IgnoreStaticIP,
+ IgnoreStaticMAC: query.IgnoreStaticMAC,
+ PrintStats: query.PrintStats,
+ PublishPorts: strings.Fields(query.PublishPorts),
}
+
+ var names []string
if query.Import {
t, err := ioutil.TempFile("", "restore")
if err != nil {
utils.InternalServerError(w, err)
return
}
- defer t.Close()
+ defer os.Remove(t.Name())
if err := compat.SaveFromBody(t, r); err != nil {
utils.InternalServerError(w, err)
return
}
- targetFile = t.Name()
+ options.Import = t.Name()
+ } else {
+ name := utils.GetName(r)
+ if _, err := runtime.LookupContainer(name); err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ names = []string{name}
}
- options := libpod.ContainerCheckpointOptions{
- Keep: query.Keep,
- TCPEstablished: query.TCPEstablished,
- IgnoreRootfs: query.IgnoreRootFS,
- IgnoreStaticIP: query.IgnoreStaticIP,
- IgnoreStaticMAC: query.IgnoreStaticMAC,
- PrintStats: query.PrintStats,
- }
- if query.Import {
- options.TargetFile = targetFile
- options.Name = query.Name
- }
- criuStatistics, runtimeRestoreDuration, err := ctr.Restore(r.Context(), options)
+ reports, err := containerEngine.ContainerRestore(r.Context(), names, options)
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(
- w,
- http.StatusOK,
- entities.RestoreReport{
- Id: ctr.ID(),
- RuntimeDuration: runtimeRestoreDuration,
- CRIUStatistics: criuStatistics,
- },
- )
+ if len(reports) != 1 {
+ utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports)))
+ return
+ }
+ if reports[0].Err != nil {
+ utils.InternalServerError(w, reports[0].Err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, reports[0])
}
func InitContainer(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 35120a1a5..f850db3d8 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -116,6 +116,8 @@ type CreateContainerConfig struct {
dockerContainer.Config // desired container configuration
HostConfig dockerContainer.HostConfig // host dependent configuration for container
NetworkingConfig dockerNetwork.NetworkingConfig // network configuration for container
+ UnsetEnv []string // unset specified default environment variables
+ UnsetEnvAll bool // unset all default environment variables
}
// swagger:model IDResponse
diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go
index 2ad2c6931..7b4ec093d 100644
--- a/pkg/bindings/containers/checkpoint.go
+++ b/pkg/bindings/containers/checkpoint.go
@@ -2,7 +2,9 @@ package containers
import (
"context"
+ "io"
"net/http"
+ "os"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/domain/entities"
@@ -23,13 +25,34 @@ func Checkpoint(ctx context.Context, nameOrID string, options *CheckpointOptions
if err != nil {
return nil, err
}
+
+ // "export" is a bool for the server so override it in the parameters
+ // if set.
+ export := false
+ if options.Export != nil && *options.Export != "" {
+ export = true
+ params.Set("export", "true")
+ }
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID)
if err != nil {
return nil, err
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ if !export {
+ return &report, response.Process(&report)
+ }
+
+ f, err := os.OpenFile(*options.Export, os.O_RDWR|os.O_CREATE, 0600)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ if _, err := io.Copy(f, response.Body); err != nil {
+ return nil, err
+ }
+
+ return &entities.CheckpointReport{}, nil
}
// Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All
@@ -47,12 +70,26 @@ func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*en
if err != nil {
return nil, err
}
- // The import key is a reserved golang term
- params.Del("ImportArchive")
- if i := options.GetImportAchive(); options.Changed("ImportArchive") {
- params.Set("import", i)
+
+ for _, p := range options.PublishPorts {
+ params.Add("publishPorts", p)
+ }
+
+ params.Del("ImportArchive") // The import key is a reserved golang term
+
+ // Open the to-be-imported archive if needed.
+ var r io.Reader
+ if i := options.GetImportAchive(); i != "" {
+ params.Set("import", "true")
+ r, err = os.Open(i)
+ if err != nil {
+ return nil, err
+ }
+ // Hard-code the name since it will be ignored in any case.
+ nameOrID = "import"
}
- response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID)
+
+ response, err := conn.DoRequest(ctx, r, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index 3a7d5a4c7..81a53a549 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -50,12 +50,17 @@ type CheckpointOptions struct {
Keep *bool
LeaveRunning *bool
TCPEstablished *bool
+ PrintStats *bool
+ PreCheckpoint *bool
+ WithPrevious *bool
+ FileLocks *bool
}
//go:generate go run ../generator/generator.go RestoreOptions
// RestoreOptions are optional options for restoring containers
type RestoreOptions struct {
IgnoreRootfs *bool
+ IgnoreVolumes *bool
IgnoreStaticIP *bool
IgnoreStaticMAC *bool
ImportAchive *string
@@ -63,6 +68,9 @@ type RestoreOptions struct {
Name *string
TCPEstablished *bool
Pod *string
+ PrintStats *bool
+ PublishPorts []string
+ FileLocks *bool
}
//go:generate go run ../generator/generator.go CreateOptions
@@ -86,7 +94,8 @@ type ExecInspectOptions struct{}
//go:generate go run ../generator/generator.go ExecStartOptions
// ExecStartOptions are optional options for starting
// exec sessions
-type ExecStartOptions struct{}
+type ExecStartOptions struct {
+}
//go:generate go run ../generator/generator.go HealthCheckOptions
// HealthCheckOptions are optional options for checking
diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go
index 7b28c4045..391748d76 100644
--- a/pkg/bindings/containers/types_checkpoint_options.go
+++ b/pkg/bindings/containers/types_checkpoint_options.go
@@ -91,3 +91,63 @@ func (o *CheckpointOptions) GetTCPEstablished() bool {
}
return *o.TCPEstablished
}
+
+// WithPrintStats set field PrintStats to given value
+func (o *CheckpointOptions) WithPrintStats(value bool) *CheckpointOptions {
+ o.PrintStats = &value
+ return o
+}
+
+// GetPrintStats returns value of field PrintStats
+func (o *CheckpointOptions) GetPrintStats() bool {
+ if o.PrintStats == nil {
+ var z bool
+ return z
+ }
+ return *o.PrintStats
+}
+
+// WithPreCheckpoint set field PreCheckpoint to given value
+func (o *CheckpointOptions) WithPreCheckpoint(value bool) *CheckpointOptions {
+ o.PreCheckpoint = &value
+ return o
+}
+
+// GetPreCheckpoint returns value of field PreCheckpoint
+func (o *CheckpointOptions) GetPreCheckpoint() bool {
+ if o.PreCheckpoint == nil {
+ var z bool
+ return z
+ }
+ return *o.PreCheckpoint
+}
+
+// WithWithPrevious set field WithPrevious to given value
+func (o *CheckpointOptions) WithWithPrevious(value bool) *CheckpointOptions {
+ o.WithPrevious = &value
+ return o
+}
+
+// GetWithPrevious returns value of field WithPrevious
+func (o *CheckpointOptions) GetWithPrevious() bool {
+ if o.WithPrevious == nil {
+ var z bool
+ return z
+ }
+ return *o.WithPrevious
+}
+
+// WithFileLocks set field FileLocks to given value
+func (o *CheckpointOptions) WithFileLocks(value bool) *CheckpointOptions {
+ o.FileLocks = &value
+ return o
+}
+
+// GetFileLocks returns value of field FileLocks
+func (o *CheckpointOptions) GetFileLocks() bool {
+ if o.FileLocks == nil {
+ var z bool
+ return z
+ }
+ return *o.FileLocks
+}
diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go
index 6eea108f4..7af2bba32 100644
--- a/pkg/bindings/containers/types_restore_options.go
+++ b/pkg/bindings/containers/types_restore_options.go
@@ -32,6 +32,21 @@ func (o *RestoreOptions) GetIgnoreRootfs() bool {
return *o.IgnoreRootfs
}
+// WithIgnoreVolumes set field IgnoreVolumes to given value
+func (o *RestoreOptions) WithIgnoreVolumes(value bool) *RestoreOptions {
+ o.IgnoreVolumes = &value
+ return o
+}
+
+// GetIgnoreVolumes returns value of field IgnoreVolumes
+func (o *RestoreOptions) GetIgnoreVolumes() bool {
+ if o.IgnoreVolumes == nil {
+ var z bool
+ return z
+ }
+ return *o.IgnoreVolumes
+}
+
// WithIgnoreStaticIP set field IgnoreStaticIP to given value
func (o *RestoreOptions) WithIgnoreStaticIP(value bool) *RestoreOptions {
o.IgnoreStaticIP = &value
@@ -136,3 +151,48 @@ func (o *RestoreOptions) GetPod() string {
}
return *o.Pod
}
+
+// WithPrintStats set field PrintStats to given value
+func (o *RestoreOptions) WithPrintStats(value bool) *RestoreOptions {
+ o.PrintStats = &value
+ return o
+}
+
+// GetPrintStats returns value of field PrintStats
+func (o *RestoreOptions) GetPrintStats() bool {
+ if o.PrintStats == nil {
+ var z bool
+ return z
+ }
+ return *o.PrintStats
+}
+
+// WithPublishPorts set field PublishPorts to given value
+func (o *RestoreOptions) WithPublishPorts(value []string) *RestoreOptions {
+ o.PublishPorts = value
+ return o
+}
+
+// GetPublishPorts returns value of field PublishPorts
+func (o *RestoreOptions) GetPublishPorts() []string {
+ if o.PublishPorts == nil {
+ var z []string
+ return z
+ }
+ return o.PublishPorts
+}
+
+// WithFileLocks set field FileLocks to given value
+func (o *RestoreOptions) WithFileLocks(value bool) *RestoreOptions {
+ o.FileLocks = &value
+ return o
+}
+
+// GetFileLocks returns value of field FileLocks
+func (o *RestoreOptions) GetFileLocks() bool {
+ if o.FileLocks == nil {
+ var z bool
+ return z
+ }
+ return *o.FileLocks
+}
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index d996595bf..233666a48 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -151,7 +151,7 @@ func createTempDirInTempDir() (string, error) {
}
func (b *bindingTest) startAPIService() *gexec.Session {
- cmd := []string{"--log-level=debug", "--events-backend=file", "system", "service", "--timeout=0", b.sock}
+ cmd := []string{"--log-level=debug", "system", "service", "--timeout=0", b.sock}
session := b.runPodman(cmd)
sock := strings.TrimPrefix(b.sock, "unix://")
diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index da82c9745..85fe6a77e 100644
--- a/pkg/checkpoint/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen/generate"
+ "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/containers/storage/pkg/archive"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -195,7 +196,12 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
}
if len(restoreOptions.PublishPorts) > 0 {
- ports, err := generate.ParsePortMapping(restoreOptions.PublishPorts, nil)
+ pubPorts, err := specgenutil.CreatePortBindings(restoreOptions.PublishPorts)
+ if err != nil {
+ return nil, err
+ }
+
+ ports, err := generate.ParsePortMapping(pubPorts, nil)
if err != nil {
return nil, err
}
@@ -233,11 +239,6 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
}
}
- // Check if the ExitCommand points to the correct container ID
- if containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1] != containerConfig.ID {
- return nil, errors.Errorf("'ExitCommandID' uses ID %s instead of container ID %s", containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1], containerConfig.ID)
- }
-
containers = append(containers, container)
return containers, nil
}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 8b7cd62d9..1677c067f 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -191,6 +191,7 @@ type CheckpointOptions struct {
WithPrevious bool
Compression archive.Compression
PrintStats bool
+ FileLocks bool
}
type CheckpointReport struct {
@@ -212,9 +213,10 @@ type RestoreOptions struct {
Name string
TCPEstablished bool
ImportPrevious string
- PublishPorts []nettypes.PortMapping
+ PublishPorts []string
Pod string
PrintStats bool
+ FileLocks bool
}
type RestoreReport struct {
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 70d2be1e6..b255785c2 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -248,6 +248,8 @@ type ContainerCreateOptions struct {
TTY bool
Timezone string
Umask string
+ UnsetEnv []string
+ UnsetEnvAll bool
UIDMap []string
Ulimit []string
User string
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 69c628669..631eb3a43 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -29,6 +29,7 @@ import (
"github.com/containers/podman/v3/pkg/signal"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
+ "github.com/containers/podman/v3/pkg/specgenutil"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
@@ -516,6 +517,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
WithPrevious: options.WithPrevious,
Compression: options.Compression,
PrintStats: options.PrintStats,
+ FileLocks: options.FileLocks,
}
if options.All {
@@ -656,7 +658,7 @@ func makeExecConfig(options entities.ExecOptions, rt *libpod.Runtime) (*libpod.E
return nil, errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command")
}
// TODO: Add some ability to toggle syslog
- exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, false, false, true)
+ exitCommandArgs, err := specgenutil.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, true)
if err != nil {
return nil, errors.Wrapf(err, "error constructing exit command for exec session")
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 5b5a1912c..2127f8749 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -302,6 +302,17 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string,
}
func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) {
+ options := new(containers.CheckpointOptions)
+ options.WithFileLocks(opts.FileLocks)
+ options.WithIgnoreRootfs(opts.IgnoreRootFS)
+ options.WithKeep(opts.Keep)
+ options.WithExport(opts.Export)
+ options.WithTCPEstablished(opts.TCPEstablished)
+ options.WithPrintStats(opts.PrintStats)
+ options.WithPreCheckpoint(opts.PreCheckPoint)
+ options.WithLeaveRunning(opts.LeaveRunning)
+ options.WithWithPrevious(opts.WithPrevious)
+
var (
err error
ctrs = []entities.ListContainer{}
@@ -325,19 +336,41 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
}
}
reports := make([]*entities.CheckpointReport, 0, len(ctrs))
- options := new(containers.CheckpointOptions).WithExport(opts.Export).WithIgnoreRootfs(opts.IgnoreRootFS).WithKeep(opts.Keep)
- options.WithLeaveRunning(opts.LeaveRunning).WithTCPEstablished(opts.TCPEstablished)
for _, c := range ctrs {
report, err := containers.Checkpoint(ic.ClientCtx, c.ID, options)
if err != nil {
reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err})
+ } else {
+ reports = append(reports, report)
}
- reports = append(reports, report)
}
return reports, nil
}
func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) {
+ if opts.ImportPrevious != "" {
+ return nil, fmt.Errorf("--import-previous is not supported on the remote client")
+ }
+
+ options := new(containers.RestoreOptions)
+ options.WithFileLocks(opts.FileLocks)
+ options.WithIgnoreRootfs(opts.IgnoreRootFS)
+ options.WithIgnoreVolumes(opts.IgnoreVolumes)
+ options.WithIgnoreStaticIP(opts.IgnoreStaticIP)
+ options.WithIgnoreStaticMAC(opts.IgnoreStaticMAC)
+ options.WithKeep(opts.Keep)
+ options.WithName(opts.Name)
+ options.WithTCPEstablished(opts.TCPEstablished)
+ options.WithPod(opts.Pod)
+ options.WithPrintStats(opts.PrintStats)
+ options.WithPublishPorts(opts.PublishPorts)
+
+ if opts.Import != "" {
+ options.WithImportAchive(opts.Import)
+ report, err := containers.Restore(ic.ClientCtx, "", options)
+ return []*entities.RestoreReport{report}, err
+ }
+
var (
err error
ctrs = []entities.ListContainer{}
@@ -360,7 +393,6 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
}
}
reports := make([]*entities.RestoreReport, 0, len(ctrs))
- options := new(containers.RestoreOptions)
for _, c := range ctrs {
report, err := containers.Restore(ic.ClientCtx, c.ID, options)
if err != nil {
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index 42d729458..e19940b22 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -81,7 +81,7 @@ func NewIgnitionFile(ign DynamicIgnition) error {
// so a listening host knows it can being interacting with it
ready := `[Unit]
Requires=dev-virtio\\x2dports-%s.device
-After=remove-moby.service
+After=remove-moby.service sshd.socket sshd.service
OnFailure=emergency.target
OnFailureJobMode=isolate
[Service]
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index a7174aac3..57c32bf74 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -664,9 +664,6 @@ func (v *MachineVM) startHostNetworking() error {
return err
}
- // Listen on all at port 7777 for setting up and tearing
- // down forwarding
- listenSocket := "tcp://0.0.0.0:7777"
qemuSocket, pidFile, err := v.getSocketandPid()
if err != nil {
return err
@@ -676,7 +673,7 @@ func (v *MachineVM) startHostNetworking() error {
files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
attr.Files = files
cmd := []string{binary}
- cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...)
+ cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...)
// Add the ssh port
cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...)
if logrus.GetLevel() == logrus.DebugLevel {
diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go
index 43cd3d69d..727a275d2 100644
--- a/pkg/machine/qemu/options_darwin_arm64.go
+++ b/pkg/machine/qemu/options_darwin_arm64.go
@@ -24,7 +24,7 @@ func (v *MachineVM) addArchOptions() []string {
func (v *MachineVM) prepare() error {
ovmfDir := getOvmfDir(v.ImagePath, v.Name)
- cmd := []string{"dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir}
+ cmd := []string{"/bin/dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir}
return exec.Command(cmd[0], cmd[1:]...).Run()
}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index 6ce4b1e29..e71d5d999 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -19,6 +19,33 @@
#include <sys/select.h>
#include <stdio.h>
+#define cleanup_free __attribute__ ((cleanup (cleanup_freep)))
+#define cleanup_close __attribute__ ((cleanup (cleanup_closep)))
+#define cleanup_dir __attribute__ ((cleanup (cleanup_dirp)))
+
+static inline void
+cleanup_freep (void *p)
+{
+ void **pp = (void **) p;
+ free (*pp);
+}
+
+static inline void
+cleanup_closep (void *p)
+{
+ int *pp = p;
+ if (*pp >= 0)
+ TEMP_FAILURE_RETRY (close (*pp));
+}
+
+static inline void
+cleanup_dirp (DIR **p)
+{
+ DIR *dir = *p;
+ if (dir)
+ closedir (dir);
+}
+
int rename_noreplace (int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
{
int ret;
@@ -106,6 +133,11 @@ do_pause ()
for (i = 0; sig[i]; i++)
sigaction (sig[i], &act, NULL);
+ /* Attempt to execv catatonit to keep the pause process alive. */
+ execl ("/usr/libexec/podman/catatonit", "catatonit", "-P", NULL);
+ execl ("/usr/bin/catatonit", "catatonit", "-P", NULL);
+ /* and if the catatonit executable could not be found, fallback here... */
+
prctl (PR_SET_NAME, "podman pause", NULL, NULL, NULL);
while (1)
pause ();
@@ -114,8 +146,8 @@ do_pause ()
static char **
get_cmd_line_args ()
{
- int fd;
- char *buffer;
+ cleanup_free char *buffer = NULL;
+ cleanup_close int fd = -1;
size_t allocated;
size_t used = 0;
int ret;
@@ -134,10 +166,7 @@ get_cmd_line_args ()
{
ret = TEMP_FAILURE_RETRY (read (fd, buffer + used, allocated - used));
if (ret < 0)
- {
- free (buffer);
- return NULL;
- }
+ return NULL;
if (ret == 0)
break;
@@ -148,30 +177,21 @@ get_cmd_line_args ()
allocated += 512;
char *tmp = realloc (buffer, allocated);
if (tmp == NULL)
- {
- free (buffer);
- return NULL;
- }
+ return NULL;
buffer = tmp;
}
}
- close (fd);
for (i = 0; i < used; i++)
if (buffer[i] == '\0')
argc++;
if (argc == 0)
- {
- free (buffer);
- return NULL;
- }
+ return NULL;
argv = malloc (sizeof (char *) * (argc + 1));
if (argv == NULL)
- {
- free (buffer);
- return NULL;
- }
+ return NULL;
+
argc = 0;
argv[argc++] = buffer;
@@ -181,15 +201,19 @@ get_cmd_line_args ()
argv[argc] = NULL;
+ /* Move ownership. */
+ buffer = NULL;
+
return argv;
}
static bool
can_use_shortcut ()
{
- int argc;
- char **argv;
+ cleanup_free char **argv = NULL;
+ cleanup_free char *argv0 = NULL;
bool ret = true;
+ int argc;
#ifdef DISABLE_JOIN_SHORTCUT
return false;
@@ -199,12 +223,10 @@ can_use_shortcut ()
if (argv == NULL)
return false;
+ argv0 = argv[0];
+
if (strstr (argv[0], "podman") == NULL)
- {
- free (argv[0]);
- free (argv);
- return false;
- }
+ return false;
for (argc = 0; argv[argc]; argc++)
{
@@ -229,11 +251,25 @@ can_use_shortcut ()
}
}
- free (argv[0]);
- free (argv);
return ret;
}
+static int
+open_namespace (int pid_to_join, const char *ns_file)
+{
+ char ns_path[PATH_MAX];
+ int ret;
+
+ ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file);
+ if (ret == PATH_MAX)
+ {
+ fprintf (stderr, "internal error: namespace path too long\n");
+ return -1;
+ }
+
+ return open (ns_path, O_CLOEXEC | O_RDONLY);
+}
+
int
is_fd_inherited(int fd)
{
@@ -250,8 +286,7 @@ static void __attribute__((constructor)) init()
const char *listen_pid;
const char *listen_fds;
const char *listen_fdnames;
-
- DIR *d;
+ cleanup_dir DIR *d = NULL;
pause = getenv ("_PODMAN_PAUSE");
if (pause && pause[0])
@@ -299,7 +334,6 @@ static void __attribute__((constructor)) init()
FD_SET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE]));
}
- closedir (d);
}
listen_pid = getenv("LISTEN_PID");
@@ -317,7 +351,7 @@ static void __attribute__((constructor)) init()
if (saved_systemd_listen_pid == NULL
|| saved_systemd_listen_fds == NULL)
{
- fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno));
+ fprintf (stderr, "save socket listen environments error: %m\n");
_exit (EXIT_FAILURE);
}
}
@@ -327,73 +361,70 @@ static void __attribute__((constructor)) init()
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ())
{
- int r;
- int fd;
+ cleanup_free char *cwd = NULL;
+ cleanup_close int userns_fd = -1;
+ cleanup_close int mntns_fd = -1;
+ cleanup_close int fd = -1;
long pid;
char buf[12];
uid_t uid;
gid_t gid;
char path[PATH_MAX];
const char *const suffix = "/libpod/tmp/pause.pid";
- char *cwd = getcwd (NULL, 0);
char uid_fmt[16];
char gid_fmt[16];
size_t len;
+ int r;
+ cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
- fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
+ fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
len = snprintf (path, PATH_MAX, "%s%s", xdg_runtime_dir, suffix);
if (len >= PATH_MAX)
{
- fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %s", strerror (ENAMETOOLONG));
+ errno = ENAMETOOLONG;
+ fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %m");
exit (EXIT_FAILURE);
}
fd = open (path, O_RDONLY);
if (fd < 0)
- {
- free (cwd);
- return;
- }
+ return;
r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof (buf) - 1));
- close (fd);
+
if (r < 0)
- {
- free (cwd);
- return;
- }
+ return;
buf[r] = '\0';
pid = strtol (buf, NULL, 10);
if (pid == LONG_MAX)
- {
- free (cwd);
- return;
- }
+ return;
uid = geteuid ();
gid = getegid ();
- sprintf (path, "/proc/%ld/ns/user", pid);
- fd = open (path, O_RDONLY);
- if (fd < 0 || setns (fd, 0) < 0)
- {
- free (cwd);
- return;
- }
- close (fd);
+ userns_fd = open_namespace (pid, "user");
+ if (userns_fd < 0)
+ return;
- /* Errors here cannot be ignored as we already joined a ns. */
- sprintf (path, "/proc/%ld/ns/mnt", pid);
- fd = open (path, O_RDONLY);
- if (fd < 0)
+ mntns_fd = open_namespace (pid, "mnt");
+ if (mntns_fd < 0)
+ return;
+
+ if (setns (userns_fd, 0) < 0)
+ return;
+
+ /* The user namespace was joined, after this point errors are
+ not recoverable anymore. */
+
+ if (setns (mntns_fd, 0) < 0)
{
- fprintf (stderr, "cannot open %s: %s", path, strerror (errno));
+ fprintf (stderr, "cannot join mount namespace for %ld: %m", pid);
exit (EXIT_FAILURE);
}
@@ -404,33 +435,24 @@ static void __attribute__((constructor)) init()
setenv ("_CONTAINERS_ROOTLESS_UID", uid_fmt, 1);
setenv ("_CONTAINERS_ROOTLESS_GID", gid_fmt, 1);
- r = setns (fd, 0);
- if (r < 0)
- {
- fprintf (stderr, "cannot join mount namespace for %ld: %s", pid, strerror (errno));
- exit (EXIT_FAILURE);
- }
- close (fd);
-
if (syscall_setresgid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresgid: %m\n");
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresuid: %m\n");
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
- fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
+ fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
_exit (EXIT_FAILURE);
}
- free (cwd);
rootless_uid_init = uid;
rootless_gid_init = gid;
}
@@ -529,7 +551,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
fd = mkstemp (tmp_file_path);
if (fd < 0)
{
- fprintf (stderr, "error creating temporary file: %s\n", strerror (errno));
+ fprintf (stderr, "error creating temporary file: %m\n");
kill (pid, SIGKILL);
_exit (EXIT_FAILURE);
}
@@ -537,7 +559,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
r = TEMP_FAILURE_RETRY (write (fd, pid_str, strlen (pid_str)));
if (r < 0)
{
- fprintf (stderr, "cannot write to file descriptor: %s\n", strerror (errno));
+ fprintf (stderr, "cannot write to file descriptor: %m\n");
kill (pid, SIGKILL);
_exit (EXIT_FAILURE);
}
@@ -555,7 +577,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
r = TEMP_FAILURE_RETRY (write (p[1], "0", 1));
if (r < 0)
{
- fprintf (stderr, "cannot write to pipe: %s\n", strerror (errno));
+ fprintf (stderr, "cannot write to pipe: %m\n");
_exit (EXIT_FAILURE);
}
close (p[1]);
@@ -590,22 +612,6 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
}
}
-static int
-open_namespace (int pid_to_join, const char *ns_file)
-{
- char ns_path[PATH_MAX];
- int ret;
-
- ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file);
- if (ret == PATH_MAX)
- {
- fprintf (stderr, "internal error: namespace path too long\n");
- return -1;
- }
-
- return open (ns_path, O_CLOEXEC | O_RDONLY);
-}
-
static void
join_namespace_or_die (const char *name, int ns_fd)
{
@@ -619,18 +625,20 @@ join_namespace_or_die (const char *name, int ns_fd)
int
reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
{
+ cleanup_close int userns_fd = -1;
+ cleanup_close int mntns_fd = -1;
+ cleanup_free char *cwd = NULL;
char uid[16];
char gid[16];
- char **argv;
+ cleanup_free char *argv0 = NULL;
+ cleanup_free char **argv = NULL;
int pid;
- int mnt_ns = -1;
- int user_ns = -1;
- char *cwd = getcwd (NULL, 0);
sigset_t sigset, oldsigset;
+ cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
- fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
+ fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
@@ -640,32 +648,27 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
argv = get_cmd_line_args ();
if (argv == NULL)
{
- fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
+ fprintf (stderr, "cannot read argv: %m\n");
_exit (EXIT_FAILURE);
}
- user_ns = open_namespace (pid_to_join, "user");
- if (user_ns < 0)
- return user_ns;
- mnt_ns = open_namespace (pid_to_join, "mnt");
- if (mnt_ns < 0)
- {
- close (user_ns);
- return mnt_ns;
- }
+ argv0 = argv[0];
+
+ userns_fd = open_namespace (pid_to_join, "user");
+ if (userns_fd < 0)
+ return userns_fd;
+ mntns_fd = open_namespace (pid_to_join, "mnt");
+ if (mntns_fd < 0)
+ return mntns_fd;
pid = fork ();
if (pid < 0)
- fprintf (stderr, "cannot fork: %s\n", strerror (errno));
+ fprintf (stderr, "cannot fork: %m\n");
if (pid)
{
int f;
- /* We passed down these fds, close them. */
- close (user_ns);
- close (mnt_ns);
-
for (f = 3; f <= open_files_max_fd; f++)
if (is_fd_inherited (f))
close (f);
@@ -681,22 +684,22 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
if (sigfillset (&sigset) < 0)
{
- fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno));
+ fprintf (stderr, "cannot fill sigset: %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGCHLD) < 0)
{
- fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno));
+ fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGTERM) < 0)
{
- fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno));
+ fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n");
_exit (EXIT_FAILURE);
}
if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0)
{
- fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
+ fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
@@ -717,33 +720,30 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0)
{
- fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %s\n", strerror (errno));
+ fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %m\n");
_exit (EXIT_FAILURE);
}
- join_namespace_or_die ("user", user_ns);
- join_namespace_or_die ("mnt", mnt_ns);
- close (user_ns);
- close (mnt_ns);
+ join_namespace_or_die ("user", userns_fd);
+ join_namespace_or_die ("mnt", mntns_fd);
if (syscall_setresgid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresgid: %m\n");
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresuid: %m\n");
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
- fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
+ fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
_exit (EXIT_FAILURE);
}
- free (cwd);
if (pause_pid_file_path && pause_pid_file_path[0] != '\0')
{
@@ -752,7 +752,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
}
if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0)
{
- fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
+ fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
@@ -784,7 +784,7 @@ static int
copy_file_to_fd (const char *file_to_read, int outfd)
{
char buf[512];
- int fd;
+ cleanup_close int fd = -1;
fd = open (file_to_read, O_RDONLY);
if (fd < 0)
@@ -796,10 +796,7 @@ copy_file_to_fd (const char *file_to_read, int outfd)
r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf));
if (r < 0)
- {
- close (fd);
- return r;
- }
+ return r;
if (r == 0)
break;
@@ -808,43 +805,40 @@ copy_file_to_fd (const char *file_to_read, int outfd)
{
w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t));
if (w < 0)
- {
- close (fd);
- return w;
- }
+ return w;
t += w;
}
}
- close (fd);
return 0;
}
int
reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_read, int outputfd)
{
+ cleanup_free char **argv = NULL;
+ cleanup_free char *argv0 = NULL;
+ cleanup_free char *cwd = NULL;
+ sigset_t sigset, oldsigset;
int ret;
pid_t pid;
char b;
- char **argv;
char uid[16];
char gid[16];
- char *cwd = getcwd (NULL, 0);
- sigset_t sigset, oldsigset;
+ cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
- fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
+ fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
-
sprintf (uid, "%d", geteuid ());
sprintf (gid, "%d", getegid ());
pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL);
if (pid < 0)
{
- fprintf (stderr, "cannot clone: %s\n", strerror (errno));
+ fprintf (stderr, "cannot clone: %m\n");
check_proc_sys_userns_file (_max_user_namespaces);
check_proc_sys_userns_file (_unprivileged_user_namespaces);
}
@@ -872,32 +866,34 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
if (sigfillset (&sigset) < 0)
{
- fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno));
+ fprintf (stderr, "cannot fill sigset: %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGCHLD) < 0)
{
- fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno));
+ fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGTERM) < 0)
{
- fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno));
+ fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n");
_exit (EXIT_FAILURE);
}
if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0)
{
- fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
+ fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
argv = get_cmd_line_args ();
if (argv == NULL)
{
- fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
+ fprintf (stderr, "cannot read argv: %m\n");
_exit (EXIT_FAILURE);
}
+ argv0 = argv[0];
+
if (do_socket_activation)
{
char s[32];
@@ -916,7 +912,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
ret = TEMP_FAILURE_RETRY (read (ready, &b, 1));
if (ret < 0)
{
- fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno));
+ fprintf (stderr, "cannot read from sync pipe: %m\n");
_exit (EXIT_FAILURE);
}
if (ret != 1 || b != '0')
@@ -924,25 +920,24 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
if (syscall_setresgid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresgid: %m\n");
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
- fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
+ fprintf (stderr, "cannot setresuid: %m\n");
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
- fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
+ fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
- free (cwd);
if (pause_pid_file_path && pause_pid_file_path[0] != '\0')
{
@@ -956,14 +951,14 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
ret = TEMP_FAILURE_RETRY (write (ready, "0", 1));
if (ret < 0)
{
- fprintf (stderr, "cannot write to ready pipe: %s\n", strerror (errno));
- _exit (EXIT_FAILURE);
+ fprintf (stderr, "cannot write to ready pipe: %m\n");
+ _exit (EXIT_FAILURE);
}
close (ready);
if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0)
{
- fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
+ fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 002b4ace3..40a18a6ac 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -88,9 +88,6 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
if err != nil {
return nil, errors.Wrap(err, "error parsing fields in containers.conf")
}
- if defaultEnvs["container"] == "" {
- defaultEnvs["container"] = "podman"
- }
var envs map[string]string
// Image Environment defaults
@@ -101,9 +98,16 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
if err != nil {
return nil, errors.Wrap(err, "Env fields from image failed to parse")
}
- defaultEnvs = envLib.Join(defaultEnvs, envs)
+ defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs))
+ }
+
+ for _, e := range s.UnsetEnv {
+ delete(defaultEnvs, e)
}
+ if s.UnsetEnvAll {
+ defaultEnvs = make(map[string]string)
+ }
// First transform the os env into a map. We need it for the labels later in
// any case.
osEnv, err := envLib.ParseSlice(os.Environ())
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index f90fef9e8..df5d2e8ff 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -3,17 +3,14 @@ package generate
import (
"context"
"fmt"
- "os"
"path/filepath"
"strings"
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
"github.com/containers/common/libimage"
- "github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
- "github.com/containers/storage/types"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@@ -163,15 +160,6 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
}
options = append(options, opts...)
- var exitCommandArgs []string
-
- exitCommandArgs, err = CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false)
- if err != nil {
- return nil, nil, nil, err
- }
-
- options = append(options, libpod.WithExitCommand(exitCommandArgs))
-
if len(s.Aliases) > 0 {
options = append(options, libpod.WithNetworkAliases(s.Aliases))
}
@@ -500,54 +488,3 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
}
return options, nil
}
-
-func CreateExitCommandArgs(storageConfig types.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) {
- // We need a cleanup process for containers in the current model.
- // But we can't assume that the caller is Podman - it could be another
- // user of the API.
- // As such, provide a way to specify a path to Podman, so we can
- // still invoke a cleanup process.
-
- podmanPath, err := os.Executable()
- if err != nil {
- return nil, err
- }
-
- command := []string{podmanPath,
- "--root", storageConfig.GraphRoot,
- "--runroot", storageConfig.RunRoot,
- "--log-level", logrus.GetLevel().String(),
- "--cgroup-manager", config.Engine.CgroupManager,
- "--tmpdir", config.Engine.TmpDir,
- "--cni-config-dir", config.Network.NetworkConfigDir,
- }
- if config.Engine.OCIRuntime != "" {
- command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
- }
- if storageConfig.GraphDriverName != "" {
- command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
- }
- for _, opt := range storageConfig.GraphDriverOptions {
- command = append(command, []string{"--storage-opt", opt}...)
- }
- if config.Engine.EventsLogger != "" {
- command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
- }
-
- if syslog {
- command = append(command, "--syslog")
- }
- command = append(command, []string{"container", "cleanup"}...)
-
- if rm {
- command = append(command, "--rm")
- }
-
- // This has to be absolutely last, to ensure that the exec session ID
- // will be added after it by Libpod.
- if exec {
- command = append(command, "--exec")
- }
-
- return command, nil
-}
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index beccd9fc2..1b022b912 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -298,7 +298,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
for key, val := range s.Annotations {
g.AddAnnotation(key, val)
}
- g.AddProcessEnv("container", "podman")
g.Config.Linux.Resources = s.ResourceLimits
// Devices
@@ -332,6 +331,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g)
+ g.ClearProcessEnv()
for name, val := range s.Env {
g.AddProcessEnv(name, val)
}
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index bfd81739a..72dd249e7 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -29,19 +29,16 @@ func buildPauseImage(rt *libpod.Runtime, rtConfig *config.Config) (string, error
return imageName, nil
}
- // NOTE: Having the pause binary in its own directory keeps the door
- // open for replacing the image building with using an overlay root FS.
- // The latter turned out to be complex and error prone (see #11956) but
- // we may be able to come up with a proper solution at a later point in
- // time.
- pausePath, err := rtConfig.FindHelperBinary("pause/pause", false)
+ // Also look into the path as some distributions install catatonit in
+ // /usr/bin.
+ catatonitPath, err := rtConfig.FindHelperBinary("catatonit", true)
if err != nil {
return "", fmt.Errorf("finding pause binary: %w", err)
}
buildContent := fmt.Sprintf(`FROM scratch
-COPY %s /pause
-ENTRYPOINT ["/pause"]`, pausePath)
+COPY %s /catatonit
+ENTRYPOINT ["/catatonit", "-P"]`, catatonitPath)
tmpF, err := ioutil.TempFile("", "pause.containerfile")
if err != nil {
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 310695c3b..0e257ad4c 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -194,6 +194,13 @@ type ContainerBasicConfig struct {
// The execution domain system allows Linux to provide limited support
// for binaries compiled under other UNIX-like operating systems.
Personality *spec.LinuxPersonality `json:"personality,omitempty"`
+ // UnsetEnv unsets the specified default environment variables from the image or from buildin or containers.conf
+ // Optional.
+ UnsetEnv []string `json:"unsetenv,omitempty"`
+ // UnsetEnvAll unsetall default environment variables from the image or from buildin or containers.conf
+ // UnsetEnvAll unsets all default environment variables from the image or from buildin
+ // Optional.
+ UnsetEnvAll bool `json:"unsetenvall,omitempty"`
}
// ContainerStorageConfig contains information on the storage configuration of a
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 91f5165f2..c110b9e97 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -711,6 +711,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.Umask = c.Umask
s.PidFile = c.PidFile
s.Volatile = c.Rm
+ s.UnsetEnv = c.UnsetEnv
+ s.UnsetEnvAll = c.UnsetEnvAll
// Initcontainers
s.InitContainerType = c.InitContainerType
diff --git a/pkg/specgenutil/util.go b/pkg/specgenutil/util.go
index 15676d086..b47082b7f 100644
--- a/pkg/specgenutil/util.go
+++ b/pkg/specgenutil/util.go
@@ -3,10 +3,13 @@ package specgenutil
import (
"io/ioutil"
"net"
+ "os"
"strconv"
"strings"
+ "github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/network/types"
+ storageTypes "github.com/containers/storage/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -272,3 +275,54 @@ func parseAndValidatePort(port string) (uint16, error) {
}
return uint16(num), nil
}
+
+func CreateExitCommandArgs(storageConfig storageTypes.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) {
+ // We need a cleanup process for containers in the current model.
+ // But we can't assume that the caller is Podman - it could be another
+ // user of the API.
+ // As such, provide a way to specify a path to Podman, so we can
+ // still invoke a cleanup process.
+
+ podmanPath, err := os.Executable()
+ if err != nil {
+ return nil, err
+ }
+
+ command := []string{podmanPath,
+ "--root", storageConfig.GraphRoot,
+ "--runroot", storageConfig.RunRoot,
+ "--log-level", logrus.GetLevel().String(),
+ "--cgroup-manager", config.Engine.CgroupManager,
+ "--tmpdir", config.Engine.TmpDir,
+ "--cni-config-dir", config.Network.NetworkConfigDir,
+ }
+ if config.Engine.OCIRuntime != "" {
+ command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
+ }
+ if storageConfig.GraphDriverName != "" {
+ command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
+ }
+ for _, opt := range storageConfig.GraphDriverOptions {
+ command = append(command, []string{"--storage-opt", opt}...)
+ }
+ if config.Engine.EventsLogger != "" {
+ command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
+ }
+
+ if syslog {
+ command = append(command, "--syslog")
+ }
+ command = append(command, []string{"container", "cleanup"}...)
+
+ if rm {
+ command = append(command, "--rm")
+ }
+
+ // This has to be absolutely last, to ensure that the exec session ID
+ // will be added after it by Libpod.
+ if exec {
+ command = append(command, "--exec")
+ }
+
+ return command, nil
+}
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 184bfadf8..8ff770f9c 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -355,6 +355,8 @@ func getBindMount(args []string) (spec.Mount, error) {
newMount.Options = append(newMount.Options, "U")
}
setOwnership = true
+ case "idmap":
+ newMount.Options = append(newMount.Options, "idmap")
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
diff --git a/pkg/util/filters.go b/pkg/util/filters.go
index e252c1ddf..5af868873 100644
--- a/pkg/util/filters.go
+++ b/pkg/util/filters.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "regexp"
"strings"
"time"
@@ -94,6 +95,28 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) {
return &filterMap, nil
}
+func wildCardToRegexp(pattern string) string {
+ var result strings.Builder
+ for i, literal := range strings.Split(pattern, "*") {
+ // Replace * with .*
+ if i > 0 {
+ result.WriteString(".*")
+ }
+ // Quote any regular expression meta characters in the
+ // literal text.
+ result.WriteString(regexp.QuoteMeta(literal))
+ }
+ return result.String()
+}
+
+func matchPattern(pattern string, value string) bool {
+ if strings.Contains(pattern, "*") {
+ result, _ := regexp.MatchString(wildCardToRegexp(pattern), value)
+ return result
+ }
+ return false
+}
+
// MatchLabelFilters matches labels and returns true if they are valid
func MatchLabelFilters(filterValues []string, labels map[string]string) bool {
outer:
@@ -106,7 +129,7 @@ outer:
filterValue = ""
}
for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
+ if ((labelKey == filterKey) || matchPattern(filterKey, labelKey)) && (filterValue == "" || labelValue == filterValue) {
continue outer
}
}
diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go
index f13dc94ec..959763dba 100644
--- a/pkg/util/mountOpts.go
+++ b/pkg/util/mountOpts.go
@@ -33,6 +33,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
// Some options have parameters - size, mode
splitOpt := strings.SplitN(opt, "=", 2)
switch splitOpt[0] {
+ case "idmap":
case "O":
if len(options) > 1 {
return nil, errors.Wrapf(ErrDupeMntOption, "'O' option can not be used with other options")
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index f45e85f61..0a5201213 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -110,11 +110,11 @@ t GET libpod/pods/fakename/top 404 \
.cause="no such pod"
t GET libpod/pods/foo/top 200 \
- .Processes[0][-1]="/pause" \
+ .Processes[0][-1]="/catatonit -P" \
.Titles[-1]="COMMAND"
t GET libpod/pods/foo/top?ps_args=args,pid 200 \
- .Processes[0][0]="/pause" \
+ .Processes[0][0]="/catatonit -P" \
.Processes[0][1]="1" \
.Titles[0]="COMMAND" \
.Titles[1]="PID" \
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 6b294802d..4963b04fc 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -35,7 +35,6 @@ var _ = Describe("Podman checkpoint", func() {
)
BeforeEach(func() {
- SkipIfRemote("checkpoint not supported in remote mode")
SkipIfRootless("checkpoint not supported in rootless mode")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
@@ -183,7 +182,7 @@ var _ = Describe("Podman checkpoint", func() {
session2.WaitWithDefaultTimeout()
Expect(session2).Should(Exit(0))
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l"})
+ result := podmanTest.Podman([]string{"container", "checkpoint", "second"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -195,7 +194,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeTrue())
Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse())
- result = podmanTest.Podman([]string{"container", "restore", "-l"})
+ result = podmanTest.Podman([]string{"container", "restore", "second"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -258,7 +257,7 @@ var _ = Describe("Podman checkpoint", func() {
Fail("Container failed to get ready")
}
- IP := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IP := podmanTest.Podman([]string{"inspect", cid, "--format={{.NetworkSettings.IPAddress}}"})
IP.WaitWithDefaultTimeout()
Expect(IP).Should(Exit(0))
@@ -267,7 +266,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(err).To(BeNil())
// This should fail as the container has established TCP connections
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l"})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(125))
@@ -275,7 +274,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Now it should work thanks to "--tcp-established"
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "--tcp-established"})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "--tcp-established"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -283,7 +282,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
// Restore should fail as the checkpoint image contains established TCP connections
- result = podmanTest.Podman([]string{"container", "restore", "-l"})
+ result = podmanTest.Podman([]string{"container", "restore", cid})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(125))
@@ -291,7 +290,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
// Now it should work thanks to "--tcp-established"
- result = podmanTest.Podman([]string{"container", "restore", "-l", "--tcp-established"})
+ result = podmanTest.Podman([]string{"container", "restore", cid, "--tcp-established"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -350,11 +349,11 @@ var _ = Describe("Podman checkpoint", func() {
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- IPBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IPBefore := podmanTest.Podman([]string{"inspect", "test_name", "--format={{.NetworkSettings.IPAddress}}"})
IPBefore.WaitWithDefaultTimeout()
Expect(IPBefore).Should(Exit(0))
- MACBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
+ MACBefore := podmanTest.Podman([]string{"inspect", "test_name", "--format={{.NetworkSettings.MacAddress}}"})
MACBefore.WaitWithDefaultTimeout()
Expect(MACBefore).Should(Exit(0))
@@ -368,11 +367,11 @@ var _ = Describe("Podman checkpoint", func() {
result = podmanTest.Podman([]string{"container", "restore", "test_name"})
result.WaitWithDefaultTimeout()
- IPAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IPAfter := podmanTest.Podman([]string{"inspect", "test_name", "--format={{.NetworkSettings.IPAddress}}"})
IPAfter.WaitWithDefaultTimeout()
Expect(IPAfter).Should(Exit(0))
- MACAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
+ MACAfter := podmanTest.Podman([]string{"inspect", "test_name", "--format={{.NetworkSettings.MacAddress}}"})
MACAfter.WaitWithDefaultTimeout()
Expect(MACAfter).Should(Exit(0))
@@ -403,7 +402,7 @@ var _ = Describe("Podman checkpoint", func() {
cid := session.OutputToString()
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -455,7 +454,7 @@ var _ = Describe("Podman checkpoint", func() {
fileName := "/tmp/checkpoint-" + cid + ".tar"
// Checkpoint with the default algorithm
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -473,7 +472,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Checkpoint with the zstd algorithm
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "--compress", "zstd"})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName, "--compress", "zstd"})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -491,7 +490,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Checkpoint with the none algorithm
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "none"})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName, "-c", "none"})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -509,7 +508,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Checkpoint with the gzip algorithm
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "gzip"})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName, "-c", "gzip"})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -527,7 +526,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Checkpoint with the non-existing algorithm
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "non-existing"})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName, "-c", "non-existing"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(125))
@@ -555,15 +554,15 @@ var _ = Describe("Podman checkpoint", func() {
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
// Change the container's root file-system
- result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
+ result := podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
- result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "rm /etc/motd"})
+ result = podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "rm /etc/motd"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
- result = podmanTest.Podman([]string{"diff", "-l"})
+ result = podmanTest.Podman([]string{"diff", cid})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring("C /etc"))
@@ -572,7 +571,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(len(result.OutputToStringArray())).To(Equal(3))
// Checkpoint the container
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -589,12 +588,12 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Verify the changes to the container's root file-system
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring("test" + cid + "test"))
- result = podmanTest.Podman([]string{"diff", "-l"})
+ result = podmanTest.Podman([]string{"diff", cid})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring("C /etc"))
@@ -616,12 +615,12 @@ var _ = Describe("Podman checkpoint", func() {
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
// Change the container's root file-system
- result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
+ result := podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
// Checkpoint the container
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -638,7 +637,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Verify the changes to the container's root file-system
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(1))
Expect(result.ErrorToString()).To(ContainSubstring("cat: can't open '/test.output': No such file or directory"))
@@ -657,12 +656,12 @@ var _ = Describe("Podman checkpoint", func() {
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
// Change the container's root file-system
- result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
+ result := podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo test" + cid + "test > /test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
// Checkpoint the container
- result = podmanTest.Podman([]string{"container", "checkpoint", "--ignore-rootfs", "-l", "-e", fileName})
+ result = podmanTest.Podman([]string{"container", "checkpoint", "--ignore-rootfs", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -679,7 +678,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Verify the changes to the container's root file-system
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(1))
Expect(result.ErrorToString()).To(ContainSubstring("cat: can't open '/test.output': No such file or directory"))
@@ -699,7 +698,7 @@ var _ = Describe("Podman checkpoint", func() {
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
// Checkpoint the container
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -716,11 +715,11 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Exec in the container
- result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo " + cid + " > /test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring(cid))
@@ -738,7 +737,7 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
// Checkpoint the container - this should fail as it was started with --rm
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l"})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid})
result.WaitWithDefaultTimeout()
Expect(result).To(ExitWithError())
Expect(result.ErrorToString()).To(ContainSubstring("cannot checkpoint containers that have been started with '--rm'"))
@@ -746,7 +745,7 @@ var _ = Describe("Podman checkpoint", func() {
// Checkpointing with --export should still work
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -796,21 +795,21 @@ var _ = Describe("Podman checkpoint", func() {
// Add file in volume0
result := podmanTest.Podman([]string{
- "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume0/test.output",
+ "exec", cid, "/bin/sh", "-c", "echo " + cid + " > /volume0/test.output",
})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
// Add file in volume1
result = podmanTest.Podman([]string{
- "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume1/test.output",
+ "exec", cid, "/bin/sh", "-c", "echo " + cid + " > /volume1/test.output",
})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
// Add file in volume2
result = podmanTest.Podman([]string{
- "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume2/test.output",
+ "exec", cid, "/bin/sh", "-c", "echo " + cid + " > /volume2/test.output",
})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
@@ -818,7 +817,7 @@ var _ = Describe("Podman checkpoint", func() {
checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz"
// Checkpoint the container
- result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", checkpointFileName})
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", checkpointFileName})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
@@ -846,19 +845,19 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
// Validate volume0 content
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume0/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/volume0/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring(cid))
// Validate volume1 content
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume1/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/volume1/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring(cid))
// Validate volume2 content
- result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume2/test.output"})
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/volume2/test.output"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToString()).To(ContainSubstring(cid))
@@ -900,6 +899,7 @@ var _ = Describe("Podman checkpoint", func() {
})
It("podman checkpoint container with --pre-checkpoint and export (migration)", func() {
+ SkipIfRemote("--import-previous is not yet supported on the remote client")
if !strings.Contains(podmanTest.OCIRuntime, "runc") {
Skip("Test only works on runc 1.0-rc3 or higher.")
}
@@ -962,7 +962,7 @@ var _ = Describe("Podman checkpoint", func() {
conn.Close()
// Checkpoint the container
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -984,6 +984,7 @@ var _ = Describe("Podman checkpoint", func() {
// Open a network connection to the redis server via initial port mapping
// This should fail
conn, err = net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", randomPort), time.Duration(3)*time.Second)
+ Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("connection refused"))
// Open a network connection to the redis server via new port mapping
fmt.Fprintf(os.Stderr, "Trying to reconnect to redis server at localhost:%d", newRandomPort)
@@ -1023,7 +1024,7 @@ var _ = Describe("Podman checkpoint", func() {
Skip("CRIU is missing or too old.")
}
if !crutils.CRRuntimeSupportsPodCheckpointRestore(podmanTest.OCIRuntime) {
- Skip("runtime does not support pod restore")
+ Skip("runtime does not support pod restore: " + podmanTest.OCIRuntime)
}
// Create a pod
session := podmanTest.Podman([]string{
@@ -1170,7 +1171,7 @@ var _ = Describe("Podman checkpoint", func() {
cid := session.OutputToString()
fileName := "/tmp/checkpoint-" + cid + ".tar.gz"
- result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName})
+ result := podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", fileName})
result.WaitWithDefaultTimeout()
// As the container has been started with '--rm' it will be completely
@@ -1212,7 +1213,7 @@ var _ = Describe("Podman checkpoint", func() {
result := podmanTest.Podman([]string{
"container",
"checkpoint",
- "-l", "-e",
+ cid, "-e",
fileName,
})
result.WaitWithDefaultTimeout()
@@ -1339,4 +1340,41 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
})
+ It("podman checkpoint and restore container with --file-locks", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "runc") {
+ // TODO: Enable test for crun when this feature has been released
+ // https://github.com/containers/crun/pull/783
+ Skip("FIXME: requires crun >= 1.4")
+ }
+ localRunString := getRunString([]string{"--name", "test_name", ALPINE, "flock", "test.lock", "sleep", "100"})
+ session := podmanTest.Podman(localRunString)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ // Checkpoint is expected to fail without --file-locks
+ result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(125))
+ Expect(result.ErrorToString()).To(ContainSubstring("criu failed"))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+ // Checkpoint is expected to succeed with --file-locks
+ result = podmanTest.Podman([]string{"container", "checkpoint", "--file-locks", "test_name"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ result = podmanTest.Podman([]string{"container", "restore", "--file-locks", "test_name"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"rm", "-t", "0", "-f", "test_name"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
})
diff --git a/test/e2e/config.go b/test/e2e/config.go
index 2552595ad..9c810575b 100644
--- a/test/e2e/config.go
+++ b/test/e2e/config.go
@@ -2,7 +2,7 @@ package integration
var (
redis = "quay.io/libpod/redis:alpine"
- fedoraMinimal = "quay.io/libpod/fedora-minimal:latest"
+ fedoraMinimal = "registry.fedoraproject.org/fedora-minimal:34"
ALPINE = "quay.io/libpod/alpine:latest"
ALPINELISTTAG = "quay.io/libpod/alpine:3.10.2"
ALPINELISTDIGEST = "quay.io/libpod/alpine@sha256:fa93b01658e3a5a1686dc3ae55f170d8de487006fb53a28efcd12ab0710a2e5f"
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index 56af64f04..b07e287ac 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -144,7 +144,7 @@ var _ = Describe("Podman images", func() {
result := podmanTest.Podman([]string{"images", "-q", "-f", "reference=quay.io*"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
- Expect(len(result.OutputToStringArray())).To(Equal(8))
+ Expect(len(result.OutputToStringArray())).To(Equal(7))
retalpine := podmanTest.Podman([]string{"images", "-f", "reference=a*pine"})
retalpine.WaitWithDefaultTimeout()
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index 12aeffd1b..f5a2caad7 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -369,13 +369,13 @@ var _ = Describe("Podman pod create", func() {
check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID})
check1.WaitWithDefaultTimeout()
Expect(check1).Should(Exit(0))
- Expect(check1.OutputToString()).To(Equal("/pause"))
+ Expect(check1.OutputToString()).To(Equal("/catatonit -P"))
// check the Path and Args
check2 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Path}}:{{.Args}}", data.Containers[0].ID})
check2.WaitWithDefaultTimeout()
Expect(check2).Should(Exit(0))
- Expect(check2.OutputToString()).To(Equal("/pause:[/pause]"))
+ Expect(check2.OutputToString()).To(Equal("/catatonit:[-P]"))
})
It("podman create pod with --infra-command", func() {
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index d6d729d3a..05cb986c6 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1516,7 +1516,7 @@ USER mail`, BB)
})
It("podman run --privileged and --group-add", func() {
- groupName := "kvm"
+ groupName := "mail"
session := podmanTest.Podman([]string{"run", "-t", "-i", "--group-add", groupName, "--privileged", fedoraMinimal, "groups"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go
index 0dd1a2b7c..6c4b22fa5 100644
--- a/test/e2e/volume_ls_test.go
+++ b/test/e2e/volume_ls_test.go
@@ -45,6 +45,17 @@ var _ = Describe("Podman volume ls", func() {
Expect(len(session.OutputToStringArray())).To(Equal(2))
})
+ It("podman ls volume filter with a key pattern", func() {
+ session := podmanTest.Podman([]string{"volume", "create", "--label", "helloworld=world", "myvol2"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=hello*"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(len(session.OutputToStringArray())).To(Equal(2))
+ })
+
It("podman ls volume with JSON format", func() {
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 2c8d08b99..ba21cd21d 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -736,4 +736,26 @@ EOF
is "$output" "$random_1" "output matches STDIN"
}
+@test "podman run defaultenv" {
+ run_podman run --rm $IMAGE printenv
+ is "$output" ".*TERM=xterm" "output matches TERM"
+ is "$output" ".*container=podman" "output matches container=podman"
+
+ run_podman run --unsetenv=TERM --rm $IMAGE printenv
+ is "$output" ".*container=podman" "output matches container=podman"
+ run grep TERM <<<$output
+ is "$output" "" "unwanted TERM environment variable despite --unsetenv=TERM"
+
+ run_podman run --unsetenv-all --rm $IMAGE /bin/printenv
+ run grep TERM <<<$output
+ is "$output" "" "unwanted TERM environment variable despite --unsetenv-all"
+ run grep container <<<$output
+ is "$output" "" "unwanted container environment variable despite --unsetenv-all"
+ run grep PATH <<<$output
+ is "$output" "" "unwanted PATH environment variable despite --unsetenv-all"
+
+ run_podman run --unsetenv-all --env TERM=abc --rm $IMAGE /bin/printenv
+ is "$output" ".*TERM=abc" "missing TERM environment variable despite TERM being set on commandline"
+}
+
# vim: filetype=sh
diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats
index 1c778a5e3..e997ab6f9 100644
--- a/test/system/250-systemd.bats
+++ b/test/system/250-systemd.bats
@@ -174,10 +174,14 @@ function check_listen_env() {
if is_remote; then
is "$output" "$stdenv" "LISTEN Environment did not pass: $context"
else
- is "$output" "$stdenv
+ out=$(for o in $output; do echo $o; done| sort)
+ std=$(echo "$stdenv
LISTEN_PID=1
LISTEN_FDS=1
-LISTEN_FDNAMES=listen_fdnames" "LISTEN Environment passed: $context"
+LISTEN_FDNAMES=listen_fdnames" | sort)
+ echo "<$out>"
+ echo "<$std>"
+ is "$out" "$std" "LISTEN Environment passed: $context"
fi
}
diff --git a/test/system/520-checkpoint.bats b/test/system/520-checkpoint.bats
new file mode 100644
index 000000000..723a20cc4
--- /dev/null
+++ b/test/system/520-checkpoint.bats
@@ -0,0 +1,175 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# test podman checkpoint. Similar in many ways to our pause tests.
+#
+
+load helpers
+
+CHECKED_ROOTLESS=
+function setup() {
+ # FIXME: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1857257
+ # TL;DR they keep fixing it then breaking it again. There's a test we
+ # could run to see if it's fixed, but it's way too complicated. Since
+ # integration tests also skip checkpoint tests on Ubuntu, do the same here.
+ if grep -qiw ubuntu /etc/os-release; then
+ skip "FIXME: checkpointing broken in Ubuntu 2004, 2104, 2110, ..."
+ fi
+
+ # None of these tests work rootless....
+ if is_rootless; then
+ # ...however, is that a genuine cast-in-stone limitation, or one
+ # that can some day be fixed? If one day some PR removes that
+ # restriction, fail loudly here, so the developer can enable tests.
+ if [[ -n "$CHECKED_ROOTLESS" ]]; then
+ run_podman '?' container checkpoint -l
+ is "$output" "Error: checkpointing a container requires root" \
+ "Confirming that rootless checkpoint doesn't work. If that changed, please reexamine this test file!"
+ CHECKED_ROOTLESS=y
+ fi
+ skip "checkpoint does not work rootless"
+ fi
+
+ basic_setup
+}
+
+function teardown() {
+ run_podman '?' volume rm myvol
+
+ basic_teardown
+}
+
+@test "podman checkpoint - basic test" {
+ run_podman run -d $IMAGE sh -c 'while :;do cat /proc/uptime; sleep 0.1;done'
+ local cid="$output"
+
+ # Wait for container to start emitting output
+ wait_for_output '[1-9]\+' $cid
+
+ # Checkpoint, and confirm via inspect
+ run_podman container checkpoint $cid
+ is "$output" "$cid" "podman container checkpoint"
+
+ run_podman container inspect \
+ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cid
+ is "$output" "exited:false:false:true" "State. Status:Running:Pause:Checkpointed"
+
+ # Plan A was to do something similar to 080-pause.bats: sleep for long
+ # enough to cause a gap in the timestamps in the log. But checkpoint
+ # doesn't seem to work like that: upon restore, even if we sleep a long
+ # time, the newly-started container seems to pick back up close to
+ # where it left off. (Maybe it's something about /proc/uptime?)
+ # Anyway, scratch Plan A. Plan B is simply to make sure that the
+ # restarted container spits something out.
+ run_podman logs $cid
+ local nlines_before="${#lines[*]}"
+
+ # Restart immediately and confirm state
+ run_podman container restore $cid
+ is "$output" "$cid" "podman container restore"
+
+ # Note that upon restore, .Checkpointed reverts to false (#12117)
+ run_podman container inspect \
+ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cid
+ is "$output" "running:true:false:false" \
+ "State. Status:Running:Pause:Checkpointed"
+
+ # Pause briefly to let restarted container emit some output
+ sleep 0.3
+
+ # Get full logs, and make sure something changed
+ run_podman logs $cid
+ local nlines_after="${#lines[*]}"
+ if [[ $nlines_after -eq $nlines_before ]]; then
+ die "Container failed to output new lines after first restore"
+ fi
+
+ # Same thing again: test for https://github.com/containers/crun/issues/756
+ # in which, after second checkpoint/restore, we lose logs
+ run_podman container checkpoint $cid
+ run_podman container logs $cid
+ nlines_before="${#lines[*]}"
+ run_podman container restore $cid
+
+ # Give container time to write new output; then confirm that something
+ # was emitted
+ sleep 0.3
+ run_podman container logs $cid
+ nlines_after="${#lines[*]}"
+ if [[ $nlines_after -eq $nlines_before ]]; then
+ die "stdout went away after second restore (crun issue 756)"
+ fi
+
+ run_podman rm -t 0 -f $cid
+}
+
+
+@test "podman checkpoint --export, with volumes" {
+ skip_if_remote "Test uses --root/--runroot, which are N/A over remote"
+
+ # Create a root in tempdir. We will run a container here.
+ local p_root=${PODMAN_TMPDIR}/testroot/root
+ local p_runroot=${PODMAN_TMPDIR}/testroot/runroot
+ mkdir -p $p_root $p_runroot
+
+ # To avoid network pull, copy $IMAGE straight to temp root
+ local p_opts="--root $p_root --runroot $p_runroot --events-backend file"
+ run_podman save -o $PODMAN_TMPDIR/image.tar $IMAGE
+ run_podman $p_opts load -i $PODMAN_TMPDIR/image.tar
+
+ # Create a volume, find unused network port, and create a webserv container
+ run_podman $p_opts volume create myvol
+ local cname=c_$(random_string 10)
+ local host_port=$(random_free_port)
+ local server=http://127.0.0.1:$host_port
+
+ run_podman $p_opts run -d --name $cname --volume myvol:/myvol \
+ -p $host_port:80 \
+ -w /myvol \
+ $IMAGE sh -c "/bin/busybox-extras httpd -p 80;echo $cname >cname;echo READY;while :;do cat /proc/uptime >mydate.tmp;mv -f mydate.tmp mydate;sleep 0.1;done"
+ local cid="$output"
+ _PODMAN_TEST_OPTS="$p_opts" wait_for_ready $cid
+
+ # Confirm that container responds
+ run curl --max-time 3 -s $server/cname
+ is "$output" "$cname" "curl $server/cname"
+ run curl --max-time 3 -s $server/mydate
+ local date_oldroot="$output"
+
+ # Checkpoint...
+ run_podman $p_opts container checkpoint \
+ --ignore-rootfs \
+ --export=$PODMAN_TMPDIR/$cname.tar.gz \
+ $cname
+
+ # ...confirm that port is now closed
+ run curl --max-time 1 -s $server/mydate
+ is "$status" "7" "cannot connect to port $host_port while container is down"
+
+ # ...now restore it to our regular root
+ run_podman container restore --import=$PODMAN_TMPDIR/$cname.tar.gz
+ is "$output" "$cid"
+
+ # Inspect (on regular root). Note that, unlike the basic test above,
+ # .State.Checkpointed here is *false*.
+ run_podman container inspect \
+ --format '{{.State.Status}}:{{.State.Running}}:{{.State.Paused}}:{{.State.Checkpointed}}' $cname
+ is "$output" "running:true:false:false" "State. Status:Running:Pause:Checkpointed"
+
+ # Pause a moment to let the restarted container update the timestamp file
+ sleep .3
+ run curl --max-time 3 -s $server/mydate
+ local date_newroot="$output"
+ if [[ $date_newroot = $date_oldroot ]]; then
+ die "Restored container did not update the timestamp file"
+ fi
+
+ run_podman exec $cid cat /myvol/cname
+ is "$output" "$cname" "volume transferred fine"
+
+ run_podman rm -t 0 -f $cid
+ run_podman volume rm -f myvol
+}
+
+# FIXME: test --leave-running
+
+# vim: filetype=sh
diff --git a/test/system/700-play.bats b/test/system/700-play.bats
index c3e5e9354..b77d41920 100644
--- a/test/system/700-play.bats
+++ b/test/system/700-play.bats
@@ -11,7 +11,7 @@ function teardown() {
run_podman rm -t 0 -f -a
run_podman image list --format '{{.ID}} {{.Repository}}'
while read id name; do
- if [[ "$name" =~ /pause ]]; then
+ if [[ "$name" =~ /podman-pause ]]; then
run_podman rmi $id
fi
done <<<"$output"
diff --git a/utils/utils.go b/utils/utils.go
index 109ae088b..f2e7beef9 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math/rand"
"os"
"os/exec"
"strconv"
@@ -203,7 +204,16 @@ func moveProcessToScope(pidPath, slice, scope string) error {
// MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to
// a separate scope.
func MovePauseProcessToScope(pausePidPath string) {
- err := moveProcessToScope(pausePidPath, "user.slice", "podman-pause.scope")
+ var err error
+
+ for i := 0; i < 3; i++ {
+ r := rand.Int()
+ err = moveProcessToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%d.scope", r))
+ if err == nil {
+ return
+ }
+ }
+
if err != nil {
unified, err2 := cgroups.IsCgroup2UnifiedMode()
if err2 != nil {
diff --git a/utils/utils_supported.go b/utils/utils_supported.go
index 1404e3194..0f0c9a9ba 100644
--- a/utils/utils_supported.go
+++ b/utils/utils_supported.go
@@ -44,15 +44,6 @@ func RunUnderSystemdScope(pid int, slice string, unitName string) error {
ch := make(chan string)
_, err = conn.StartTransientUnit(unitName, "replace", properties, ch)
if err != nil {
- // On errors check if the cgroup already exists, if it does move the process there
- if props, err := conn.GetUnitTypeProperties(unitName, "Scope"); err == nil {
- if cgroup, ok := props["ControlGroup"].(string); ok && cgroup != "" {
- if err := moveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err == nil {
- return nil
- }
- // On errors return the original error message we got from StartTransientUnit.
- }
- }
return err
}
diff --git a/vendor/github.com/containers/common/libimage/manifests/manifests.go b/vendor/github.com/containers/common/libimage/manifests/manifests.go
index 8d1abfba9..45223cc2f 100644
--- a/vendor/github.com/containers/common/libimage/manifests/manifests.go
+++ b/vendor/github.com/containers/common/libimage/manifests/manifests.go
@@ -353,9 +353,12 @@ func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.Imag
}
if instanceInfo.OS == "" {
instanceInfo.OS = config.OS
+ instanceInfo.OSVersion = config.OSVersion
+ instanceInfo.OSFeatures = config.OSFeatures
}
if instanceInfo.Architecture == "" {
instanceInfo.Architecture = config.Architecture
+ instanceInfo.Variant = config.Variant
}
}
manifestBytes, manifestType, err := src.GetManifest(ctx, instanceInfo.instanceDigest)
diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go
index 2eda0290a..1a5370a39 100644
--- a/vendor/github.com/containers/common/pkg/config/config.go
+++ b/vendor/github.com/containers/common/pkg/config/config.go
@@ -563,6 +563,10 @@ func NewConfig(userConfigPath string) (*Config, error) {
return nil, err
}
+ if err := config.setupEnv(); err != nil {
+ return nil, err
+ }
+
return config, nil
}
@@ -1146,7 +1150,14 @@ func (c *Config) ActiveDestination() (uri, identity string, err error) {
// FindHelperBinary will search the given binary name in the configured directories.
// If searchPATH is set to true it will also search in $PATH.
func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) {
- for _, path := range c.Engine.HelperBinariesDir {
+ dir_list := c.Engine.HelperBinariesDir
+
+ // If set, search this directory first. This is used in testing.
+ if dir, found := os.LookupEnv("CONTAINERS_HELPER_BINARY_DIR"); found {
+ dir_list = append([]string{dir}, dir_list...)
+ }
+
+ for _, path := range dir_list {
fullpath := filepath.Join(path, name)
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
@@ -1180,3 +1191,23 @@ func (c *Config) ImageCopyTmpDir() (string, error) {
return "", errors.Errorf("invalid image_copy_tmp_dir value %q (relative paths are not accepted)", c.Engine.ImageCopyTmpDir)
}
+
+// setupEnv sets the environment variables for the engine
+func (c *Config) setupEnv() error {
+ for _, env := range c.Engine.Env {
+ splitEnv := strings.SplitN(env, "=", 2)
+ if len(splitEnv) != 2 {
+ logrus.Warnf("invalid environment variable for engine %s, valid configuration is KEY=value pair", env)
+ continue
+ }
+ // skip if the env is already defined
+ if _, ok := os.LookupEnv(splitEnv[0]); ok {
+ logrus.Debugf("environment variable %s is already defined, skip the settings from containers.conf", splitEnv[0])
+ continue
+ }
+ if err := os.Setenv(splitEnv[0], splitEnv[1]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/common/pkg/parse/parse.go b/vendor/github.com/containers/common/pkg/parse/parse.go
index 02e670c50..fda129c83 100644
--- a/vendor/github.com/containers/common/pkg/parse/parse.go
+++ b/vendor/github.com/containers/common/pkg/parse/parse.go
@@ -66,6 +66,7 @@ func ValidateVolumeOpts(options []string) ([]string, error) {
// are intended to be always safe to use, even not on OS
// X).
continue
+ case "idmap":
default:
return nil, errors.Errorf("invalid option type %q", opt)
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 9b6bd34bf..047c228e2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -97,7 +97,7 @@ github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/util
-# github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358
+# github.com/containers/common v0.46.1-0.20211115170340-7ae7bd1c3f8e
github.com/containers/common/libimage
github.com/containers/common/libimage/manifests
github.com/containers/common/pkg/apparmor
@@ -288,7 +288,7 @@ github.com/docker/distribution/registry/client/auth/challenge
github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
-# github.com/docker/docker v20.10.10+incompatible
+# github.com/docker/docker v20.10.11+incompatible
github.com/docker/docker/api
github.com/docker/docker/api/types
github.com/docker/docker/api/types/blkiodev
@@ -802,10 +802,10 @@ gopkg.in/tomb.v1
gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gopkg.in/yaml.v3
-# k8s.io/api v0.22.3
+# k8s.io/api v0.22.4
k8s.io/api/apps/v1
k8s.io/api/core/v1
-# k8s.io/apimachinery v0.22.3
+# k8s.io/apimachinery v0.22.4
k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/apis/meta/v1
k8s.io/apimachinery/pkg/conversion