diff options
41 files changed, 345 insertions, 453 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index d26c1ec11..fe09ea988 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -123,6 +123,9 @@ gating_task: timeout_in: 20m + networking_script: # Don't bother going further if something is down + - 'while read host port; do nc -zv -w 13 $host $port || exit 1; done < ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/required_host_ports.txt' + gate_script: # N/B: entrypoint.sh resets $GOSRC (same as make clean) - '/usr/local/bin/entrypoint.sh install.tools |& ${TIMESTAMP}' diff --git a/.copr/Makefile b/.copr/Makefile index 05d9eb592..71142920b 100644 --- a/.copr/Makefile +++ b/.copr/Makefile @@ -16,4 +16,4 @@ build_binary: clean: rm -fr rpms - rm -fr cri-o + rm -fr conmon diff --git a/.copr/prepare.sh b/.copr/prepare.sh index 1ad29da36..a40e2aadb 100644 --- a/.copr/prepare.sh +++ b/.copr/prepare.sh @@ -28,5 +28,5 @@ fi mkdir build/ git archive --prefix "libpod-${COMMIT_SHORT}/" --format "tar.gz" HEAD -o "build/libpod-${COMMIT_SHORT}.tar.gz" -git clone https://github.com/kubernetes-incubator/cri-o -cd cri-o && git checkout 4cd5a7c60349be0678d9f1b0657683324c1a2726 && git archive --prefix "crio/" --format "tar.gz" HEAD -o "../build/crio.tar.gz" +git clone https://github.com/containers/conmon +cd conmon && git checkout f02c053eb37010fc76d1e2966de7f2cb9f969ef2 && git archive --prefix "conmon/" --format "tar.gz" HEAD -o "../build/conmon.tar.gz" diff --git a/Dockerfile b/Dockerfile index 767e64570..f3afd5e25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,13 +56,13 @@ RUN set -x \ && rm -rf "$GOPATH" # Install conmon -ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466 +ENV CONMON_COMMIT f02c053eb37010fc76d1e2966de7f2cb9f969ef2 RUN set -x \ && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ - && cd "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ + && git clone https://github.com/containers/conmon.git "$GOPATH/src/github.com/containers/conmon.git" \ + && cd "$GOPATH/src/github.com/containers/conmon.git" \ && git fetch origin --tags \ - && git checkout -q "$CRIO_COMMIT" \ + && git checkout -q "$CONMON_COMMIT" \ && make \ && install -D -m 755 bin/conmon /usr/libexec/podman/conmon \ && rm -rf "$GOPATH" diff --git a/Dockerfile.centos b/Dockerfile.centos index 605dc9df4..47f7182b6 100644 --- a/Dockerfile.centos +++ b/Dockerfile.centos @@ -64,15 +64,14 @@ RUN set -x \ && install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ # Install conmon -ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466 +ENV CONMON_COMMIT f02c053eb37010fc76d1e2966de7f2cb9f969ef2 RUN set -x \ && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ - && cd "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ + && git clone https://github.com/containers/conmon.git "$GOPATH/src/github.com/containers/conmon.git" \ + && cd "$GOPATH/src/github.com/containers/conmon.git" \ && git fetch origin --tags \ - && git checkout -q "$CRIO_COMMIT" \ + && git checkout -q "$CONMON_COMMIT" \ && make \ - && make bin/conmon \ && install -D -m 755 bin/conmon /usr/libexec/podman/conmon \ && rm -rf "$GOPATH" diff --git a/Dockerfile.fedora b/Dockerfile.fedora index d4bcc11ea..290fe3f82 100644 --- a/Dockerfile.fedora +++ b/Dockerfile.fedora @@ -68,15 +68,14 @@ RUN set -x \ && install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ # Install conmon -ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466 +ENV CONMON_COMMIT f02c053eb37010fc76d1e2966de7f2cb9f969ef2 RUN set -x \ && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ - && cd "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ + && git clone https://github.com/containers/conmon.git "$GOPATH/src/github.com/containers/conmon.git" \ + && cd "$GOPATH/src/github.com/containers/conmon.git" \ && git fetch origin --tags \ - && git checkout -q "$CRIO_COMMIT" \ + && git checkout -q "$CONMON_COMMIT" \ && make \ - && make bin/conmon \ && install -D -m 755 bin/conmon /usr/libexec/podman/conmon \ && rm -rf "$GOPATH" @@ -1,6 +1,6 @@ GO ?= go DESTDIR ?= / -EPOCH_TEST_COMMIT ?= a9fc570dd844bf1ebd1f106f1b8091882b4a2b29 +EPOCH_TEST_COMMIT ?= 8161802f7df857e0850f842261079c83290f9891 HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD @@ -259,14 +259,17 @@ changelog: ## Generate changelog $(shell cat $(TMPFILE) >> changelog.txt) $(shell rm $(TMPFILE)) -install: .gopathok install.bin install.man install.cni install.systemd ## Install binaries to system locations +install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations + +install.remote: + install ${SELINUXOPT} -d -m 755 $(BINDIR) + install ${SELINUXOPT} -m 755 bin/podman-remote $(BINDIR)/podman-remote + test -z "${SELINUXOPT}" || chcon --verbose --reference=$(BINDIR)/podman bin/podman-remote install.bin: install ${SELINUXOPT} -d -m 755 $(BINDIR) install ${SELINUXOPT} -m 755 bin/podman $(BINDIR)/podman - install ${SELINUXOPT} -m 755 bin/podman-remote $(BINDIR)/podman-remote test -z "${SELINUXOPT}" || chcon --verbose --reference=$(BINDIR)/podman bin/podman - test -z "${SELINUXOPT}" || chcon --verbose --reference=$(BINDIR)/podman bin/podman-remote install.man: docs install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1 @@ -7,6 +7,7 @@ approvers: - umohnani8 - giuseppe - vrothberg + - jwhonce reviewers: - mheon - baude @@ -16,3 +17,4 @@ reviewers: - umohnani8 - giuseppe - vrothberg + - jwhonce @@ -5,7 +5,7 @@ Libpod provides a library for applications looking to use the Container Pod concept, popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes. -* [Latest Version: 1.2.0](https://github.com/containers/libpod/releases/latest) +* [Latest Version: 1.3.1](https://github.com/containers/libpod/releases/latest) * [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master) ## Overview and scope @@ -44,7 +44,7 @@ Any recent Podman release should be able to run rootless without any additional * Specializing in signing and pushing images to various storage backends. See [Skopeo](https://github.com/containers/skopeo/) for those tasks. * Container runtimes daemons for working with the Kubernetes CRI interface. - [CRI-O](https://github.com/kubernetes-sigs/cri-o) specializes in that. + [CRI-O](https://github.com/cri-o/cri-o) specializes in that. * Supporting `docker-compose`. We believe that Kubernetes is the defacto standard for composing Pods and for orchestrating containers, making Kubernetes YAML a defacto standard file format. Hence, Podman allows the @@ -67,7 +67,7 @@ The plan is to use OCI projects and best of breed libraries for different aspect - Storage: Container and image storage is managed by [containers/storage](https://github.com/containers/storage) - Networking: Networking support through use of [CNI](https://github.com/containernetworking/cni) - Builds: Builds are supported via [Buildah](https://github.com/containers/buildah). -- Conmon: [Conmon](https://github.com/kubernetes-sigs/cri-o) is a tool for monitoring OCI runtimes. It is part of the CRI-O package +- Conmon: [Conmon](https://github.com/containers/conmon) is a tool for monitoring OCI runtimes. ## Podman Information for Developers diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b41714d45..5eb85d0bc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,7 @@ ### Features - The `podman cp` command can now read input redirected to `STDIN`, and output to `STDOUT` instead of a file, using `-` instead of an argument. - The Podman remote client now displays version information from both the client and server in `podman version` +- The `podman unshare` command has been added, allowing easy entry into the user namespace set up by rootless Podman (allowing the removal of files created by rootless Podman, among other things) ### Bugfixes - Fixed a bug where Podman containers with the `--rm` flag were removing created volumes when they were automatically removed ([#3071](https://github.com/containers/libpod/issues/3071)) @@ -22,7 +23,7 @@ ## 1.3.0 ### Features -- Podman now supports container restart policies! The `--restart-policy` flag on `podman create` and `podman run` allows containers to be restarted after they exit. Please note that Podman cannot restart containers after a system reboot - for that, see our next feature +- Podman now supports container restart policies! The `--restart` flag on `podman create` and `podman run` allows containers to be restarted after they exit. Please note that Podman cannot restart containers after a system reboot - for that, see our next feature - Podman `podman generate systemd` command was added to generate systemd unit files for managing Podman containers - The `podman runlabel` command now allows a `$GLOBAL_OPTS` variable, which will be populated by global options passed to the `podman runlabel` command, allowing custom storage configurations to be passed into containers run with `runlabel` ([#2399](https://github.com/containers/libpod/issues/2399)) - The `podman play kube` command now allows `File` and `FileOrCreate` volumes diff --git a/changelog.txt b/changelog.txt index c72117d7f..ec0a62f26 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,48 @@ +- Changelog for v1.3.1 (2019-05-16) + * More release notes + * Add unshare to podman + * Release notes for 1.3.1 + * Kill os.Exit() in tests, replace with asserts + * Minor capitalization fix in Readme + * Add debug mode to Ginkgo, collect debug logs in Cirrus + * set default event logger based on build tags + * Add VarlinkCall.RequiresUpgrade() type and method + * Ensure that start() in StartAndAttach() is locked + * When removing pods, free their locks + * network: raise a clearer error when using CNI + * Fix libpod.conf option ordering + * split remote tests from distro tests + * varlink: fix usage message, URI is now optional + * Update containerd/cgroups to 4994991857f9b0ae + * healthcheck benign error + * Add `systemd` build tag + * podman: fix events help string + * When removing a pod with CGroupfs, set pids limit to 0 + * Add fix for an issue breaking our CI + * Use standard remove functions for removing pod ctrs + * implement cp reads tar file from stdin/stdout + * Add information when running podman version on client + * add varlink bridge + * Add negative command-line test + * Preserve errors returned by removing pods + * Improve robustness of pod removal + * enable integration tests for remote-client + * fix podman-remote ps --ns + * podman-run|create man updates + * Update installation instructions + * remote-podman checkpoint and restore add to container submenu + * Remove tests for deprecated podman-refresh command + * When refreshing after a reboot, force lock allocation + * Do not remove volumes when --rm removes a container + * add unit tests for generate systemd + * Bump gitvalidation epoch + * Bump to v1.3.1-dev + * Upgrade to latest criu and selinux-policy + * Only run checkpoint/restore tests on Fedora >= 29 + * Fix API.md + * Cirrus: Add missing task dependencies + * Cirrus: Add check for make varlink_api_generate + - Changelog for v1.3.0 (2019-05-06) * Update release notes for 1.3.0 release * Bump to Buildah v1.8.2 diff --git a/cmd/podman/info.go b/cmd/podman/info.go index a6fce7fcb..823303354 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -10,6 +10,7 @@ import ( "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/version" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -60,7 +61,16 @@ func infoCmd(c *cliconfig.InfoValues) error { if err != nil { return errors.Wrapf(err, "error getting info") } + if runtime.Remote { + endpoint, err := runtime.RemoteEndpoint() + if err != nil { + logrus.Errorf("Failed to obtain server connection: %s", err.Error()) + } else { + remoteClientInfo["Connection"] = endpoint.Connection + remoteClientInfo["Connection Type"] = endpoint.Type.String() + } + remoteClientInfo["RemoteAPI Version"] = version.RemoteAPIVersion remoteClientInfo["Podman Version"] = version.Version remoteClientInfo["OS Arch"] = fmt.Sprintf("%s/%s", rt.GOOS, rt.GOARCH) diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index b533dc056..b8d77602d 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -107,7 +107,11 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, if c.Flags().Changed("cgroup-manager") { options = append(options, libpod.WithCgroupManager(c.GlobalFlags.CGroupManager)) } else { - if rootless.IsRootless() { + unified, err := util.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if rootless.IsRootless() && !unified { options = append(options, libpod.WithCgroupManager("cgroupfs")) } } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index c426817de..e87b88992 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -152,7 +152,7 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error { return err } if !c.Quiet { - fmt.Printf("command: %s\n", strings.Join(cmd, " ")) + fmt.Printf("command: %s\n", strings.Join(append([]string{os.Args[0]}, cmd[1:]...), " ")) if c.Display { return nil } diff --git a/cmd/podman/shared/create_cli.go b/cmd/podman/shared/create_cli.go index f731e8db5..7f158b09a 100644 --- a/cmd/podman/shared/create_cli.go +++ b/cmd/podman/shared/create_cli.go @@ -7,6 +7,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared/parse" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/sysinfo" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -76,6 +77,12 @@ func addWarning(warnings []string, msg string) []string { func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) { warnings := []string{} + + cgroup2, err := util.IsCgroup2UnifiedMode() + if err != nil || cgroup2 { + return warnings, err + } + sysInfo := sysinfo.New(true) // memory subsystem checks and adjustments diff --git a/cmd/podman/version.go b/cmd/podman/version.go index 439a1cca6..52a518db8 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -70,7 +70,7 @@ func versionCmd(c *cliconfig.VersionValues) error { if remote { fmt.Fprintf(w, "\nService:\n") - runtime, err := adapter.GetRuntime(getContext(), nil) + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index f422fe935..a285b133b 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -283,16 +283,16 @@ install_buildah() { ooe.sh sudo make install } -# Requires $GOPATH and $CRIO_COMMIT to be set +# Requires $GOPATH and $CONMON_COMMIT to be set install_conmon(){ - echo "Installing conmon from commit $CRIO_COMMIT" - req_env_var GOPATH CRIO_COMMIT - DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" + echo "Installing conmon from commit $CONMON_COMMIT" + req_env_var GOPATH CONMON_COMMIT + DEST="$GOPATH/src/github.com/containers/conmon.git" rm -rf "$DEST" - ooe.sh git clone https://github.com/kubernetes-sigs/cri-o.git "$DEST" + ooe.sh git clone https://github.com/containers/conmon.git "$DEST" cd "$DEST" ooe.sh git fetch origin --tags - ooe.sh git checkout -q "$CRIO_COMMIT" + ooe.sh git checkout -q "$CONMON_COMMIT" ooe.sh make sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon } diff --git a/contrib/cirrus/required_host_ports.txt b/contrib/cirrus/required_host_ports.txt new file mode 100644 index 000000000..9248e497a --- /dev/null +++ b/contrib/cirrus/required_host_ports.txt @@ -0,0 +1,4 @@ +github.com 22 +docker.io 443 +quay.io 443 +registry.fedoraproject.org 443 diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 29c786ca6..985dbbc74 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -33,19 +33,19 @@ %global shortcommit0 %(c=%{commit0}; echo ${c:0:8}) # People want conmon packaged with the copr rpm -%global import_path_conmon github.com/kubernetes-sigs/cri-o +%global import_path_conmon github.com/containers/conmon %global git_conmon https://%{import_path_conmon} -%global commit_conmon 4cd5a7c60349be0678d9f1b0657683324c1a2726 +%global commit_conmon f02c053eb37010fc76d1e2966de7f2cb9f969ef2 %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) Name: podman -Version: 1.3.1 +Version: 1.3.2 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 URL: %{git_podman} Source0: %{git0}/archive/%{commit0}/%{repo}-%{shortcommit0}.tar.gz -Source1: crio.tar.gz +Source1: conmon.tar.gz # e.g. el6 has ppc64 arch without gcc-go, so EA tag is required #ExclusiveArch: %%{?go_arches:%%{go_arches}}%%{!?go_arches:%%{ix86} x86_64 aarch64 %%{arm}} ExclusiveArch: aarch64 %{arm} ppc64le s390x x86_64 @@ -371,24 +371,23 @@ GOPATH=$GOPATH go generate ./cmd/podman/varlink/... BUILDTAGS=$BUILDTAGS make binaries docs # build conmon -pushd crio +pushd conmon mkdir _output pushd _output -mkdir -p src/%{provider}.%{provider_tld}/{kubernetes-sigs,opencontainers} +mkdir -p src/%{provider}.%{provider_tld}/{containers,opencontainers} ln -s $(dirs +1 -l) src/%{import_path_conmon} popd -ln -s vendor src -export GOPATH=$(pwd)/_output:$(pwd):%{gopath} export BUILDTAGS="selinux seccomp $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh) containers_image_ostree_stub" -BUILDTAGS=$BUILDTAGS make -C conmon +BUILDTAGS=$BUILDTAGS make popd %install install -dp %{buildroot}%{_unitdir} PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \ install.bin \ + install.remote \ install.man \ install.cni \ install.systemd \ @@ -402,7 +401,7 @@ install -p -m 644 %{repo}.conf %{buildroot}%{_datadir}/containers # install conmon install -dp %{buildroot}%{_libexecdir}/%{name} -install -p -m 755 crio/bin/conmon %{buildroot}%{_libexecdir}/%{name} +install -p -m 755 conmon/bin/conmon %{buildroot}%{_libexecdir}/%{name} # source codes for building projects %if 0%{?with_devel} diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md index 2f0b3f303..cb08f0eb0 100644 --- a/docs/libpod.conf.5.md +++ b/docs/libpod.conf.5.md @@ -34,7 +34,7 @@ libpod to manage containers. Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated. - Paths listed later in the array higher precedence (`oci-hooks(5)` discusses directory precedence). + Paths listed later in the array have higher precedence (`oci-hooks(5)` discusses directory precedence). For the annotation conditions, libpod uses any annotations set in the generated OCI configuration. diff --git a/install.md b/install.md index 82dd4c36a..ae74acdf8 100644 --- a/install.md +++ b/install.md @@ -91,7 +91,6 @@ Fedora, CentOS, RHEL, and related distributions: sudo yum install -y \ atomic-registries \ btrfs-progs-devel \ - conmon \ containernetworking-cni \ device-mapper-devel \ git \ @@ -188,13 +187,12 @@ export PATH=$GOPATH/bin:$PATH #### conmon The latest version of `conmon` is expected to be installed on the system. Conmon is used to monitor OCI Runtimes. -To build from source, use the following (if not already executed above, run `export GOPATH=~/go && mkdir -p $GOPATH`): +To build from source, use the following: ```bash -git clone https://github.com/cri-o/cri-o $GOPATH/src/github.com/cri-o/cri-o -cd $GOPATH/src/github.com/cri-o/cri-o -mkdir bin -make bin/conmon +git clone https://github.com/containers/conmon +cd conmon +make sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon ``` diff --git a/libpod/container_api.go b/libpod/container_api.go index 06a31da11..eff5bfe5f 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -289,8 +289,8 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir chWait := make(chan error) go func() { chWait <- execCmd.Wait() + close(chWait) }() - defer close(chWait) pidFile := c.execPidPath(sessionID) // 60 second seems a reasonable time to wait diff --git a/libpod/container_commit.go b/libpod/container_commit.go index ae04f67bb..739fcd80e 100644 --- a/libpod/container_commit.go +++ b/libpod/container_commit.go @@ -99,7 +99,7 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai // Should we store the ENV we actually want in the spec separately? if c.config.Spec.Process != nil { for _, e := range c.config.Spec.Process.Env { - splitEnv := strings.Split(e, "=") + splitEnv := strings.SplitN(e, "=", 2) importBuilder.SetEnv(splitEnv[0], splitEnv[1]) } } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 2450bd6b1..b8a916de3 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -19,6 +19,7 @@ import ( "github.com/containers/libpod/pkg/firewall" "github.com/containers/libpod/pkg/inspect" "github.com/containers/libpod/pkg/netns" + "github.com/containers/libpod/pkg/rootless" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -100,6 +101,9 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re // Create and configure a new network namespace for a container func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) { + if rootless.IsRootless() { + return nil, nil, errors.New("cannot configure a new network namespace in rootless mode, only --network=slirp4netns is supported") + } ctrNS, err := netns.NewNS() if err != nil { return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) diff --git a/libpod/util.go b/libpod/util.go index 7e2dff21a..3a15f9e39 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -90,11 +90,7 @@ func MountExists(specMounts []spec.Mount, dest string) bool { // WaitForFile waits until a file has been created or the given timeout has occurred func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, error) { - done := make(chan struct{}) - chControl := make(chan struct{}) - var inotifyEvents chan fsnotify.Event - var timer chan struct{} watcher, err := fsnotify.NewWatcher() if err == nil { if err := watcher.Add(filepath.Dir(path)); err == nil { @@ -102,51 +98,36 @@ func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, e } defer watcher.Close() } - if inotifyEvents == nil { - // If for any reason we fail to create the inotify - // watcher, fallback to polling the file - timer = make(chan struct{}) - go func() { - select { - case <-chControl: - close(timer) - return - default: - time.Sleep(25 * time.Millisecond) - timer <- struct{}{} - } - }() - } - go func() { - for { - select { - case <-chControl: - return - case <-timer: - _, err := os.Stat(path) - if err == nil { - close(done) - return - } - case <-inotifyEvents: - _, err := os.Stat(path) - if err == nil { - close(done) - return - } + timeoutChan := time.After(timeout) + + for { + select { + case e := <-chWait: + return true, e + case <-inotifyEvents: + _, err := os.Stat(path) + if err == nil { + return false, nil + } + if !os.IsNotExist(err) { + return false, errors.Wrapf(err, "checking file %s", path) + } + case <-time.After(25 * time.Millisecond): + // Check periodically for the file existence. It is needed + // if the inotify watcher could not have been created. It is + // also useful when using inotify as if for any reasons we missed + // a notification, we won't hang the process. + _, err := os.Stat(path) + if err == nil { + return false, nil + } + if !os.IsNotExist(err) { + return false, errors.Wrapf(err, "checking file %s", path) } + case <-timeoutChan: + return false, errors.Wrapf(ErrInternal, "timed out waiting for file %s", path) } - }() - - select { - case e := <-chWait: - return true, e - case <-done: - return false, nil - case <-time.After(timeout): - close(chControl) - return false, errors.Wrapf(ErrInternal, "timed out waiting for file %s", path) } } diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index f672a92a6..01914834f 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -10,44 +10,56 @@ import ( "github.com/varlink/go/varlink" ) -type VarlinkConnectionInfo struct { - RemoteUserName string - RemoteHost string - VarlinkAddress string -} - -// Connect provides a varlink connection -func (r RemoteRuntime) Connect() (*varlink.Connection, error) { - var ( - err error - connection *varlink.Connection - ) +var remoteEndpoint *Endpoint - logLevel := r.cmd.LogLevel +func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { + if remoteEndpoint == nil { + remoteEndpoint = &Endpoint{Unknown, ""} + } else { + return remoteEndpoint, nil + } // I'm leaving this here for now as a document of the birdge format. It can be removed later once the bridge // function is more flushed out. - //bridge := `ssh -T root@192.168.122.1 "/usr/bin/varlink -A '/usr/bin/podman varlink \$VARLINK_ADDRESS' bridge"` + // bridge := `ssh -T root@192.168.122.1 "/usr/bin/varlink -A '/usr/bin/podman varlink \$VARLINK_ADDRESS' bridge"` if len(r.cmd.RemoteHost) > 0 { // The user has provided a remote host endpoint if len(r.cmd.RemoteUserName) < 1 { return nil, errors.New("you must provide a username when providing a remote host name") } - bridge := fmt.Sprintf(`ssh -T %s@%s /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, r.cmd.RemoteUserName, r.cmd.RemoteHost, logLevel) - connection, err = varlink.NewBridge(bridge) + remoteEndpoint.Type = BridgeConnection + remoteEndpoint.Connection = fmt.Sprintf( + `ssh -T %s@%s /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, + r.cmd.RemoteUserName, r.cmd.RemoteHost, r.cmd.LogLevel) + } else if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" { - connection, err = varlink.NewBridge(bridge) + remoteEndpoint.Type = BridgeConnection + remoteEndpoint.Connection = bridge } else { address := os.Getenv("PODMAN_VARLINK_ADDRESS") if address == "" { address = DefaultAddress } - connection, err = varlink.NewConnection(address) + remoteEndpoint.Type = DirectConnection + remoteEndpoint.Connection = address } + return +} + +// Connect provides a varlink connection +func (r RemoteRuntime) Connect() (*varlink.Connection, error) { + ep, err := r.RemoteEndpoint() if err != nil { return nil, err } - return connection, nil + + switch ep.Type { + case DirectConnection: + return varlink.NewConnection(ep.Connection) + case BridgeConnection: + return varlink.NewBridge(ep.Connection) + } + return nil, errors.New(fmt.Sprintf("Unable to determine type of varlink connection: %s", ep.Connection)) } // RefreshConnection is used to replace the current r.Conn after things like diff --git a/pkg/adapter/client_config.go b/pkg/adapter/client_config.go index d165ef1cc..3559b16e3 100644 --- a/pkg/adapter/client_config.go +++ b/pkg/adapter/client_config.go @@ -2,3 +2,35 @@ package adapter // DefaultAddress is the default address of the varlink socket const DefaultAddress = "unix:/run/podman/io.podman" + +// EndpointType declares the type of server connection +type EndpointType int + +// Enum of connection types +const ( + Unknown = iota - 1 // Unknown connection type + BridgeConnection // BridgeConnection proxy connection via ssh + DirectConnection // DirectConnection socket connection to server +) + +// String prints ASCII string for EndpointType +func (e EndpointType) String() string { + // declare an array of strings + // ... operator counts how many + // items in the array (7) + names := [...]string{ + "BridgeConnection", + "DirectConnection", + } + + if e < BridgeConnection || e > DirectConnection { + return "Unknown" + } + return names[e] +} + +// Endpoint type and connection string to use +type Endpoint struct { + Type EndpointType + Connection string +} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 21613c425..37ee1b737 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -398,3 +398,8 @@ func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*libpod.Pod, error) func (r *LocalRuntime) GetVersion() (libpod.Version, error) { return libpod.GetVersion() } + +// RemoteEndpoint resolve interface requirement +func (r *LocalRuntime) RemoteEndpoint() (*Endpoint, error) { + return nil, errors.New("RemoteEndpoint() not implemented for local connection") +} diff --git a/pkg/hooks/0.1.0/hook.go b/pkg/hooks/0.1.0/hook.go index 49d833aa8..ba68b0f10 100644 --- a/pkg/hooks/0.1.0/hook.go +++ b/pkg/hooks/0.1.0/hook.go @@ -19,7 +19,7 @@ type Hook struct { Hook *string `json:"hook"` Arguments []string `json:"arguments,omitempty"` - // https://github.com/kubernetes-sigs/cri-o/pull/1235 + // https://github.com/cri-o/cri-o/pull/1235 Stages []string `json:"stages"` Stage []string `json:"stage"` diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 20c649f9a..df303db6d 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -7,6 +7,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" pmount "github.com/containers/storage/pkg/mount" "github.com/docker/docker/oci/caps" "github.com/docker/go-units" @@ -267,7 +268,9 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM // SECURITY OPTS g.SetProcessNoNewPrivileges(config.NoNewPrivs) - g.SetProcessApparmorProfile(config.ApparmorProfile) + if !config.Privileged { + g.SetProcessApparmorProfile(config.ApparmorProfile) + } blockAccessToKernelFilesystems(config, &g) @@ -347,10 +350,13 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM } if rootless.IsRootless() { - if addedResources { - return nil, errors.New("invalid configuration, cannot set resources with rootless containers") + cgroup2, err := util.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if addedResources && !cgroup2 { + return nil, errors.New("invalid configuration, cannot set resources with rootless containers not using cgroups v2 unified mode") } - configSpec.Linux.Resources = &spec.LinuxResources{} } // Make sure that the bind mounts keep options like nosuid, noexec, nodev. diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index af5e67fc1..8b98658c2 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -11,9 +11,33 @@ import ( "github.com/pkg/errors" "os" "path/filepath" + "sync" "syscall" ) +const ( + _cgroup2SuperMagic = 0x63677270 +) + +var ( + isUnifiedOnce sync.Once + isUnified bool + isUnifiedErr error +) + +// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 unified mode. +func IsCgroup2UnifiedMode() (bool, error) { + isUnifiedOnce.Do(func() { + var st syscall.Statfs_t + if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil { + isUnified, isUnifiedErr = false, err + } else { + isUnified, isUnifiedErr = st.Type == _cgroup2SuperMagic, nil + } + }) + return isUnified, isUnifiedErr +} + // GetRootlessRuntimeDir returns the runtime directory when running as non root func GetRootlessRuntimeDir() (string, error) { var rootlessRuntimeDirError error diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index 1e9ccea90..b33733da9 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -10,3 +10,8 @@ import ( func GetRootlessRuntimeDir() (string, error) { return "", errors.New("this function is not implemented for windows") } + +// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 unified mode. +func IsCgroup2UnifiedMode() (bool, error) { + return false, errors.New("this function is not implemented for windows") +} diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index 3ece4887e..bf20ac999 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -194,4 +194,24 @@ var _ = Describe("Podman commit", func() { Expect(r.ExitCode()).To(Equal(0)) }) + It("podman commit container check env variables", func() { + s := podmanTest.Podman([]string{"run", "--name", "test1", "-e", "TEST=1=1-01=9.01", "-it", "alpine", "true"}) + s.WaitWithDefaultTimeout() + Expect(s.ExitCode()).To(Equal(0)) + + c := podmanTest.Podman([]string{"commit", "test1", "newimage"}) + c.WaitWithDefaultTimeout() + Expect(c.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "newimage"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + image := inspect.InspectImageJSON() + + envMap := make(map[string]bool) + for _, v := range image[0].Config.Env { + envMap[v] = true + } + Expect(envMap["TEST=1=1-01=9.01"]).To(BeTrue()) + }) }) diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go deleted file mode 100644 index 51544ff8b..000000000 --- a/test/e2e/rootless_test.go +++ /dev/null @@ -1,312 +0,0 @@ -// +build !remoteclient - -package integration - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "syscall" - - . "github.com/containers/libpod/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func canExec() bool { - const nsGetParent = 0xb702 - - u, err := os.Open("/proc/self/ns/user") - if err != nil { - return false - } - defer u.Close() - - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, u.Fd(), uintptr(nsGetParent), 0) - return errno != syscall.ENOTTY -} - -var _ = Describe("Podman rootless", func() { - var ( - tempdir string - err error - podmanTest *PodmanTestIntegration - ) - - BeforeEach(func() { - SkipIfRootless() - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - podmanTest = PodmanTestCreate(tempdir) - podmanTest.CgroupManager = "cgroupfs" - podmanTest.StorageOptions = ROOTLESS_STORAGE_OPTIONS - podmanTest.Setup() - podmanTest.RestoreAllArtifacts() - }) - - AfterEach(func() { - podmanTest.Cleanup() - f := CurrentGinkgoTestDescription() - processTestResult(f) - - }) - - It("podman rootless help|version", func() { - commands := []string{"help", "version"} - for _, v := range commands { - env := os.Environ() - env = append(env, "USER=foo") - cmd := podmanTest.PodmanAsUser([]string{v}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - } - }) - - chownFunc := func(p string, info os.FileInfo, err error) error { - if err != nil { - return err - } - return os.Lchown(p, 1000, 1000) - } - - type rootlessCB func(test *PodmanTestIntegration, xdgRuntimeDir string, home string, mountPath string) - - runInRootlessContext := func(cb rootlessCB) { - // Check if we can create an user namespace - err := exec.Command("unshare", "-r", "echo", "hello").Run() - if err != nil { - Skip("User namespaces not supported.") - } - setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) - cid := setup.OutputToString() - - mount := podmanTest.Podman([]string{"mount", cid}) - mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) - mountPath := mount.OutputToString() - - err = filepath.Walk(tempdir, chownFunc) - Expect(err).To(BeNil()) - - tempdir, err := CreateTempDirInTempDir() - Expect(err).To(BeNil()) - rootlessTest := PodmanTestCreate(tempdir) - rootlessTest.CgroupManager = "cgroupfs" - rootlessTest.StorageOptions = ROOTLESS_STORAGE_OPTIONS - err = filepath.Walk(tempdir, chownFunc) - Expect(err).To(BeNil()) - - xdgRuntimeDir, err := ioutil.TempDir("/run", "") - Expect(err).To(BeNil()) - defer os.RemoveAll(xdgRuntimeDir) - err = filepath.Walk(xdgRuntimeDir, chownFunc) - Expect(err).To(BeNil()) - - home, err := CreateTempDirInTempDir() - Expect(err).To(BeNil()) - err = filepath.Walk(home, chownFunc) - Expect(err).To(BeNil()) - - cb(rootlessTest, xdgRuntimeDir, home, mountPath) - - umount := podmanTest.Podman([]string{"umount", cid}) - umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) - } - - It("podman rootless pod", func() { - f := func(rootlessTest *PodmanTestIntegration, xdgRuntimeDir string, home string, mountPath string) { - env := os.Environ() - env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir)) - env = append(env, fmt.Sprintf("HOME=%s", home)) - env = append(env, "USER=foo") - - cmd := rootlessTest.PodmanAsUser([]string{"pod", "create", "--infra=false"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - podId := cmd.OutputToString() - - args := []string{"run", "--pod", podId, "--rootfs", mountPath, "echo", "hello"} - cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - Expect(cmd.LineInOutputContains("hello")).To(BeTrue()) - - args = []string{"pod", "top", podId} - cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Not(Equal(0))) - - args = []string{"run", "--pod", podId, "-d", "--rootfs", mountPath, "sleep", "100"} - cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - args = []string{"pod", "top", podId} - cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - } - runInRootlessContext(f) - }) - - It("podman rootless search", func() { - xdgRuntimeDir, err := ioutil.TempDir("/run", "") - Expect(err).To(BeNil()) - defer os.RemoveAll(xdgRuntimeDir) - err = filepath.Walk(xdgRuntimeDir, chownFunc) - Expect(err).To(BeNil()) - - home, err := CreateTempDirInTempDir() - Expect(err).To(BeNil()) - err = filepath.Walk(home, chownFunc) - Expect(err).To(BeNil()) - - env := os.Environ() - env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir)) - env = append(env, fmt.Sprintf("HOME=%s", home)) - env = append(env, "USER=foo") - cmd := podmanTest.PodmanAsUser([]string{"search", "docker.io/busybox"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - }) - - runRootlessHelper := func(args []string) { - f := func(rootlessTest *PodmanTestIntegration, xdgRuntimeDir string, home string, mountPath string) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - env := os.Environ() - env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir)) - env = append(env, fmt.Sprintf("HOME=%s", home)) - env = append(env, "USER=foo") - - allArgs := append([]string{"run"}, args...) - allArgs = append(allArgs, "--rootfs", mountPath, "echo", "hello") - cmd := rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - Expect(cmd.LineInOutputContains("hello")).To(BeTrue()) - - cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - allArgs = append([]string{"run", "-d"}, args...) - allArgs = append(allArgs, "--security-opt", "seccomp=unconfined", "--rootfs", mountPath, "top") - cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"restart", "-l", "-t", "0"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - canUseExec := canExec() - - if canUseExec { - cmd = rootlessTest.PodmanAsUser([]string{"top", "-l"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - } - - cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - allArgs = append([]string{"run", "-d"}, args...) - allArgs = append(allArgs, "--security-opt", "seccomp=unconfined", "--rootfs", mountPath, "unshare", "-r", "unshare", "-r", "top") - cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l", "--type", "container", "--format", "{{ .State.Status }}"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.LineInOutputContains("exited")).To(BeTrue()) - - cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - if len(args) == 0 { - cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - data := cmd.InspectContainerToJSON() - Expect(data[0].HostConfig.NetworkMode).To(ContainSubstring("slirp4netns")) - } - - if !canUseExec { - Skip("ioctl(NS_GET_PARENT) not supported.") - } - - cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "echo", "hello"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - Expect(cmd.LineInOutputContains("hello")).To(BeTrue()) - - cmd = rootlessTest.PodmanAsUser([]string{"ps", "-l", "-q"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - cid := cmd.OutputToString() - - cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "sh", "-c", "echo SeCreTMessage > /file"}, 1000, 1000, "", env) - cmd.WaitWithDefaultTimeout() - Expect(cmd.ExitCode()).To(Equal(0)) - - cmd = rootlessTest.PodmanAsUser([]string{"export", "-o", "export.tar", cid}, 1000, 1000, home, env) - cmd.WaitWithDefaultTimeout() - content, err := ioutil.ReadFile(filepath.Join(home, "export.tar")) - Expect(err).To(BeNil()) - Expect(strings.Contains(string(content), "SeCreTMessage")).To(BeTrue()) - } - runInRootlessContext(f) - } - - It("podman rootless rootfs", func() { - runRootlessHelper([]string{}) - }) - - It("podman rootless rootfs --net host", func() { - runRootlessHelper([]string{"--net", "host"}) - }) - - It("podman rootless rootfs --pid host", func() { - runRootlessHelper([]string{"--pid", "host"}) - }) - - It("podman rootless rootfs --privileged", func() { - runRootlessHelper([]string{"--privileged"}) - }) - - It("podman rootless rootfs --net host --privileged", func() { - runRootlessHelper([]string{"--net", "host", "--privileged"}) - }) - - It("podman rootless rootfs --uts host", func() { - runRootlessHelper([]string{"--uts", "host"}) - }) - - It("podman rootless rootfs --ipc host", func() { - runRootlessHelper([]string{"--ipc", "host"}) - }) -}) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 0e1f0d865..f908fe154 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -12,6 +12,7 @@ import ( "time" . "github.com/containers/libpod/test/utils" + "github.com/containers/storage/pkg/stringid" "github.com/mrunalp/fileutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -106,6 +107,46 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run a container with a --rootfs", func() { + rootfs := filepath.Join(tempdir, "rootfs") + uls := filepath.Join("/", "usr", "local", "share") + uniqueString := stringid.GenerateNonCryptoID() + testFilePath := filepath.Join(uls, uniqueString) + tarball := filepath.Join(tempdir, "rootfs.tar") + + err := os.Mkdir(rootfs, 0770) + Expect(err).Should(BeNil()) + + // Change image in predictable way to validate export + csession := podmanTest.Podman([]string{"run", "--name", uniqueString, ALPINE, + "/bin/sh", "-c", fmt.Sprintf("echo %s > %s", uniqueString, testFilePath)}) + csession.WaitWithDefaultTimeout() + Expect(csession.ExitCode()).To(Equal(0)) + + // Export from working container image guarantees working root + esession := podmanTest.Podman([]string{"export", "--output", tarball, uniqueString}) + esession.WaitWithDefaultTimeout() + Expect(esession.ExitCode()).To(Equal(0)) + Expect(tarball).Should(BeARegularFile()) + + // N/B: This will loose any extended attributes like SELinux types + fmt.Fprintf(os.Stderr, "Extracting container root tarball\n") + tarsession := SystemExec("tar", []string{"xf", tarball, "-C", rootfs}) + Expect(tarsession.ExitCode()).To(Equal(0)) + Expect(filepath.Join(rootfs, uls)).Should(BeADirectory()) + + // Other tests confirm SELinux types, just confirm --rootfs is working. + session := podmanTest.Podman([]string{"run", "-i", "--security-opt", "label=disable", + "--rootfs", rootfs, "cat", testFilePath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Validate changes made in original container and export + stdoutLines := session.OutputToStringArray() + Expect(stdoutLines).Should(HaveLen(1)) + Expect(stdoutLines[0]).Should(Equal(uniqueString)) + }) + It("podman run a container with --init", func() { session := podmanTest.Podman([]string{"run", "--init", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/transfer.md b/transfer.md index df91cdf21..79b6d3461 100644 --- a/transfer.md +++ b/transfer.md @@ -98,7 +98,7 @@ Those Docker commands currently do not have equivalents in `podman`: | `docker secret` || | `docker service` || | `docker stack` || -| `docker swarm` | podman does not support swarm. We support Kubernetes for orchestration using [CRI-O](https://github.com/kubernetes-sigs/cri-o).| +| `docker swarm` | podman does not support swarm. We support Kubernetes for orchestration using [CRI-O](https://github.com/cri-o/cri-o).| | `docker volume` | podman currently supports file volumes. Future enhancement planned to support Docker Volumes Plugins ## Missing commands in Docker diff --git a/vendor.conf b/vendor.conf index b71e947dc..0b1f13304 100644 --- a/vendor.conf +++ b/vendor.conf @@ -19,7 +19,7 @@ github.com/containers/image v1.5.1 github.com/vbauerster/mpb v3.3.4 github.com/mattn/go-isatty v0.0.4 github.com/VividCortex/ewma v1.1.1 -github.com/containers/storage v1.12.6 +github.com/containers/storage v1.12.7 github.com/containers/psgo v1.2.1 github.com/coreos/go-systemd v14 github.com/coreos/pkg v4 diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index 7bec0aea6..a35dd476b 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -402,12 +402,10 @@ func (r *layerStore) Save() error { if err != nil { return err } + defer r.Touch() if err := ioutils.AtomicWriteFile(rpath, jldata, 0600); err != nil { return err } - if !r.IsReadWrite() { - return nil - } r.mountsLockfile.Lock() defer r.mountsLockfile.Unlock() defer r.mountsLockfile.Touch() diff --git a/vendor/github.com/containers/storage/layers_ffjson.go b/vendor/github.com/containers/storage/layers_ffjson.go index 125b5d8c9..09b5d0f33 100644 --- a/vendor/github.com/containers/storage/layers_ffjson.go +++ b/vendor/github.com/containers/storage/layers_ffjson.go @@ -1,5 +1,5 @@ // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. -// source: layers.go +// source: ./layers.go package storage diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 27b00f6fe..9b967db6d 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -1197,18 +1197,20 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat } imageID = cimage.ID - createMappedLayer := imageHomeStore == istore + if cimage.TopLayer != "" { + createMappedLayer := imageHomeStore == istore + ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, createMappedLayer, rlstore, lstores, idMappingsOptions) + if err != nil { + return nil, err + } + imageTopLayer = ilayer - ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, createMappedLayer, rlstore, lstores, idMappingsOptions) - if err != nil { - return nil, err - } - imageTopLayer = ilayer - if !options.HostUIDMapping && len(options.UIDMap) == 0 { - uidMap = ilayer.UIDMap - } - if !options.HostGIDMapping && len(options.GIDMap) == 0 { - gidMap = ilayer.GIDMap + if !options.HostUIDMapping && len(options.UIDMap) == 0 { + uidMap = ilayer.UIDMap + } + if !options.HostGIDMapping && len(options.GIDMap) == 0 { + gidMap = ilayer.GIDMap + } } } else { rlstore.Lock() diff --git a/version/version.go b/version/version.go index c63f8b820..a917931b7 100644 --- a/version/version.go +++ b/version/version.go @@ -4,7 +4,7 @@ package version // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -const Version = "1.3.1-dev" +const Version = "1.3.2-dev" // RemoteAPIVersion is the version for the remote // client API. It is used to determine compatibility |