diff options
63 files changed, 862 insertions, 567 deletions
@@ -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 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/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/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 3786054a7..90d28b7ac 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -20,6 +20,13 @@ die_unknown() { } msg "************************************************************" +msg "FIXME: force-install catatonit 0.17.0 until CI images are updated" +msg "************************************************************" +# FIXME: this is just a temporary workaround to force-install +# catatonit 0.17.0. Please remove once the images are updated. +./hack/install_catatonit.sh --force + +msg "************************************************************" msg "Setting up runtime environment" msg "************************************************************" show_env_vars 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-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..811d16880 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -1047,6 +1047,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..3d908444b 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -1117,6 +1117,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 @@ -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 @@ -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= @@ -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/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_inspect.go b/libpod/container_inspect.go index 277c3b960..0dae810de 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -300,8 +300,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/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 8ce435efd..314a74427 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" @@ -400,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) @@ -429,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 { @@ -437,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") } @@ -937,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. @@ -1124,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, netAddress.IPNet.IP.String()) } } 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, netAddress.IPNet.IP.String()) } } } diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index 67ea31c1c..cc1b3cfdc 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -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..e007d0b92 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -794,6 +794,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") } @@ -1101,6 +1104,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/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/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/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index da82c9745..3a300daaf 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 } 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..e04c7a38a 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -516,6 +516,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ WithPrevious: options.WithPrevious, Compression: options.Compression, PrintStats: options.PrintStats, + FileLocks: options.FileLocks, } if options.All { 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/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/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/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/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/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..916cf41ae 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 |