diff options
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | contrib/podmanimage/stable/Containerfile | 1 | ||||
-rw-r--r-- | contrib/podmanimage/stable/storage.conf | 6 | ||||
-rw-r--r-- | contrib/podmanimage/testing/Containerfile | 1 | ||||
-rw-r--r-- | contrib/podmanimage/upstream/Containerfile | 1 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 68 | ||||
-rw-r--r-- | pkg/specgen/volumes.go | 14 | ||||
-rw-r--r-- | test/e2e/run_volume_test.go | 45 |
8 files changed, 109 insertions, 39 deletions
@@ -3,6 +3,7 @@ # Podman: A tool for managing OCI containers and pods Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers. +Podman runs containers on Linux, but can also be used on Mac and Windows systems using a Podman-managed virtual machine. Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes. * [Latest Version: 4.1.0](https://github.com/containers/podman/releases/tag/v4.1.0) @@ -24,16 +25,15 @@ At a high level, the scope of Podman and libpod is the following: * Support for pods, groups of containers that share resources and are managed together. * Support for running containers and pods without root or other elevated privileges. * Resource isolation of containers and pods. -* Support for a Docker-compatible CLI interface. +* Support for a Docker-compatible CLI interface, which can both run containers locally and on remote systems. * No manager daemon, for improved security and lower resource utilization at idle. * Support for a REST API providing both a Docker-compatible interface and an improved interface exposing advanced Podman functionality. - -Podman presently only supports running containers on Linux. However, we are building a remote client which can run on Windows and macOS and manage Podman containers on a Linux system via the REST API using SSH tunneling. +* Support for running on Windows and Mac via virtual machines run by `podman machine`. ## Roadmap -1. Further improvements to the REST API, with a focus on bugfixes and implementing missing functionality -1. Improvements on rootless containers, with a focus on improving the user experience and exposing presently-unavailable features when possible +1. A fully-featured GUI frontend for `podman machine` +1. Further improvements to `podman generate kube` and `podman play kube` 1. Improvements to Pods, including the addition of pod-level resource limits ## Communications @@ -111,7 +111,7 @@ includes tables showing Docker commands and their Podman equivalent commands. Tutorials on using Podman. **[Remote Client](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md)** -A brief how-to on using the Podman remote-client. +A brief how-to on using the Podman remote client. **[Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)** A tutorial showing the setup and configuration necessary to run Rootless Podman. diff --git a/contrib/podmanimage/stable/Containerfile b/contrib/podmanimage/stable/Containerfile index 40a2cb5f3..f4ab0cc79 100644 --- a/contrib/podmanimage/stable/Containerfile +++ b/contrib/podmanimage/stable/Containerfile @@ -23,7 +23,6 @@ echo -e "podman:1:999\npodman:1001:64535" > /etc/subuid; \ echo -e "podman:1:999\npodman:1001:64535" > /etc/subgid; ARG _REPO_URL="https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable" -ADD $_REPO_URL/storage.conf /etc/containers/storage.conf ADD $_REPO_URL/containers.conf /etc/containers/containers.conf ADD $_REPO_URL/podman-containers.conf /home/podman/.config/containers/containers.conf diff --git a/contrib/podmanimage/stable/storage.conf b/contrib/podmanimage/stable/storage.conf deleted file mode 100644 index bc8d8c111..000000000 --- a/contrib/podmanimage/stable/storage.conf +++ /dev/null @@ -1,6 +0,0 @@ -[storage.options] -additionalimagestores = ["/var/lib/shared"] - -[storage.options.overlay] -mountopt = "nodev,fsync=0" -mount_program = "/usr/bin/fuse-overlayfs" diff --git a/contrib/podmanimage/testing/Containerfile b/contrib/podmanimage/testing/Containerfile index 5fa794baf..0f2dd891f 100644 --- a/contrib/podmanimage/testing/Containerfile +++ b/contrib/podmanimage/testing/Containerfile @@ -23,7 +23,6 @@ echo -e "podman:1:999\npodman:1001:64535" > /etc/subuid; \ echo -e "podman:1:999\npodman:1001:64535" > /etc/subgid; ARG _REPO_URL="https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable" -ADD $_REPO_URL/storage.conf /etc/containers/storage.conf ADD $_REPO_URL/containers.conf /etc/containers/containers.conf ADD $_REPO_URL/podman-containers.conf /home/podman/.config/containers/containers.conf diff --git a/contrib/podmanimage/upstream/Containerfile b/contrib/podmanimage/upstream/Containerfile index b338a33ae..256c31232 100644 --- a/contrib/podmanimage/upstream/Containerfile +++ b/contrib/podmanimage/upstream/Containerfile @@ -29,7 +29,6 @@ echo -e "podman:1:999\npodman:1001:64535" > /etc/subuid; \ echo -e "podman:1:999\npodman:1001:64535" > /etc/subgid; ARG _REPO_URL="https://raw.githubusercontent.com/containers/podman/main/contrib/podmanimage/stable" -ADD $_REPO_URL/storage.conf /etc/containers/storage.conf ADD $_REPO_URL/containers.conf /etc/containers/containers.conf ADD $_REPO_URL/podman-containers.conf /home/podman/.config/containers/containers.conf diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index e19d75deb..0056b8e86 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -388,6 +388,37 @@ func lookupHostUser(name string) (*runcuser.ExecUser, error) { return &execUser, nil } +// Internal only function which returns upper and work dir from +// overlay options. +func getOverlayUpperAndWorkDir(options []string) (string, string, error) { + upperDir := "" + workDir := "" + for _, o := range options { + if strings.HasPrefix(o, "upperdir") { + splitOpt := strings.SplitN(o, "=", 2) + if len(splitOpt) > 1 { + upperDir = splitOpt[1] + if upperDir == "" { + return "", "", errors.New("cannot accept empty value for upperdir") + } + } + } + if strings.HasPrefix(o, "workdir") { + splitOpt := strings.SplitN(o, "=", 2) + if len(splitOpt) > 1 { + workDir = splitOpt[1] + if workDir == "" { + return "", "", errors.New("cannot accept empty value for workdir") + } + } + } + } + if (upperDir != "" && workDir == "") || (upperDir == "" && workDir != "") { + return "", "", errors.New("must specify both upperdir and workdir") + } + return upperDir, workDir, nil +} + // Generate spec for a container // Accepts a map of the container's dependencies func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { @@ -460,23 +491,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range namedVol.Options { if o == "O" { overlayFlag = true - } - if overlayFlag && strings.Contains(o, "upperdir") { - splitOpt := strings.SplitN(o, "=", 2) - if len(splitOpt) > 1 { - upperDir = splitOpt[1] - if upperDir == "" { - return nil, errors.New("cannot accept empty value for upperdir") - } - } - } - if overlayFlag && strings.Contains(o, "workdir") { - splitOpt := strings.SplitN(o, "=", 2) - if len(splitOpt) > 1 { - workDir = splitOpt[1] - if workDir == "" { - return nil, errors.New("cannot accept empty value for workdir") - } + upperDir, workDir, err = getOverlayUpperAndWorkDir(namedVol.Options) + if err != nil { + return nil, err } } } @@ -489,10 +506,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { return nil, err } - if (upperDir != "" && workDir == "") || (upperDir == "" && workDir != "") { - return nil, errors.Wrapf(err, "must specify both upperdir and workdir") - } - overlayOpts = &overlay.Options{RootUID: c.RootUID(), RootGID: c.RootGID(), UpperDirOptionFragment: upperDir, @@ -585,11 +598,22 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { // Add overlay volumes for _, overlayVol := range c.config.OverlayVolumes { + upperDir, workDir, err := getOverlayUpperAndWorkDir(overlayVol.Options) + if err != nil { + return nil, err + } contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID()) if err != nil { return nil, err } - overlayMount, err := overlay.Mount(contentDir, overlayVol.Source, overlayVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) + overlayOpts := &overlay.Options{RootUID: c.RootUID(), + RootGID: c.RootGID(), + UpperDirOptionFragment: upperDir, + WorkDirOptionFragment: workDir, + GraphOpts: c.runtime.store.GraphOptions(), + } + + overlayMount, err := overlay.MountWithOptions(contentDir, overlayVol.Source, overlayVol.Dest, overlayOpts) if err != nil { return nil, errors.Wrapf(err, "mounting overlay failed %q", overlayVol.Source) } diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index b26666df3..a7a1022b0 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -97,6 +97,8 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na // This is not a named volume overlayFlag := false chownFlag := false + upperDirFlag := false + workDirFlag := false for _, o := range options { if o == "O" { overlayFlag = true @@ -105,8 +107,16 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na if strings.Contains(joinedOpts, "U") { chownFlag = true } - - if len(options) > 2 || (len(options) == 2 && !chownFlag) { + if strings.Contains(joinedOpts, "upperdir") { + upperDirFlag = true + } + if strings.Contains(joinedOpts, "workdir") { + workDirFlag = true + } + if (workDirFlag && !upperDirFlag) || (!workDirFlag && upperDirFlag) { + return nil, nil, nil, errors.New("must set both `upperdir` and `workdir`") + } + if len(options) > 2 && !(len(options) == 3 && upperDirFlag && workDirFlag) || (len(options) == 2 && !chownFlag) { return nil, nil, nil, errors.New("can't use 'O' with other options") } } diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 3bef889b7..1c0480407 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -325,6 +325,51 @@ var _ = Describe("Podman run with volumes", func() { }) + It("podman support overlay volume with custom upperdir and workdir", func() { + SkipIfRemote("Overlay volumes only work locally") + if os.Getenv("container") != "" { + Skip("Overlay mounts not supported when running in a container") + } + if rootless.IsRootless() { + if _, err := exec.LookPath("fuse-overlayfs"); err != nil { + Skip("Fuse-Overlayfs required for rootless overlay mount test") + } + } + + // Use bindsource instead of named volume + bindSource := filepath.Join(tempdir, "bindsource") + err := os.Mkdir(bindSource, 0755) + Expect(err).To(BeNil(), "mkdir "+bindSource) + + // create persistent upperdir on host + upperDir := filepath.Join(tempdir, "upper") + err = os.Mkdir(upperDir, 0755) + Expect(err).To(BeNil(), "mkdir "+upperDir) + + // create persistent workdir on host + workDir := filepath.Join(tempdir, "work") + err = os.Mkdir(workDir, 0755) + Expect(err).To(BeNil(), "mkdir "+workDir) + + overlayOpts := fmt.Sprintf("upperdir=%s,workdir=%s", upperDir, workDir) + + // create file on overlay volume + session := podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "ls /data"}) + session.WaitWithDefaultTimeout() + // must contain `overlay` file since it should be persistent on specified upper and workdir + Expect(session.OutputToString()).To(ContainSubstring("overlay")) + + session = podmanTest.Podman([]string{"run", "--volume", bindSource + ":/data:O", ALPINE, "sh", "-c", "ls /data"}) + session.WaitWithDefaultTimeout() + // must not contain `overlay` file which was on custom upper and workdir since we have not specified any upper or workdir + Expect(session.OutputToString()).To(Not(ContainSubstring("overlay"))) + + }) + It("podman run with noexec can't exec", func() { session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"}) session.WaitWithDefaultTimeout() |