Skip to content

Commit

Permalink
volume: add support for non-volatile upperdir,workdir for overlay vol…
Browse files Browse the repository at this point in the history
…umes

Often users want their overlayed volumes to be `non-volatile` in nature
that means that same `upper` dir can be re-used by one or more
containers but overall of nature of volumes still have to be `overlay`
so work done is still on a overlay not on the actual volume.

Following PR adds support for more advanced options i.e custom `workdir`
and `upperdir` for overlayed volumes. So that users can re-use `workdir`
and `upperdir` across new containers as well.

Usage
```console

$ podman run -it -v myvol:/data:O,upperdir=/path/persistant/upper,workdir=/path/persistant/work alpine sh

```

Signed-off-by: Aditya Rajan <arajan@redhat.com>
  • Loading branch information
flouthoc committed Dec 28, 2021
1 parent e06631d commit d678b25
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 10 deletions.
25 changes: 24 additions & 1 deletion libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,18 +391,40 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}

overlayFlag := false
upperDir := ""
workDir := ""
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 overlayFlag && strings.Contains(o, "workdir") {
splitOpt := strings.SplitN(o, "=", 2)
if len(splitOpt) > 1 {
workDir = splitOpt[1]
}
}
}

if overlayFlag {
var overlayMount spec.Mount
var overlayOpts *overlay.OverlayOptions
contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID())
if err != nil {
return nil, err
}
overlayMount, err := overlay.Mount(contentDir, mountPoint, namedVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())

overlayOpts = nil
if upperDir != "" && workDir != "" {
overlayOpts = &overlay.OverlayOptions{Upperdir: upperDir, Workdir: workDir}
}

overlayMount, err = overlay.MountWithOptions(contentDir, mountPoint, namedVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions(), overlayOpts, false)
if err != nil {
return nil, errors.Wrapf(err, "mounting overlay failed %q", mountPoint)
}
Expand Down Expand Up @@ -2781,6 +2803,7 @@ func (c *Container) copyTimezoneFile(zonePath string) (string, error) {
}

func (c *Container) cleanupOverlayMounts() error {
//overlay.CleanupContent("/tmp/exp-overlay")
return overlay.CleanupContent(c.config.StaticDir)
}

Expand Down
19 changes: 17 additions & 2 deletions pkg/util/mountOpts.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,31 @@ type defaultMountOptions struct {
// The sourcePath variable, if not empty, contains a bind mount source.
func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
var (
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU bool
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay bool
)

newOptions := make([]string, 0, len(options))
for _, opt := range options {
// Some options have parameters - size, mode
splitOpt := strings.SplitN(opt, "=", 2)

// add advanced options such as upperdir=/path and workdir=/path, when overlay is specified
if foundOverlay {
if strings.Contains(opt, "upperdir") {
newOptions = append(newOptions, opt)
continue
}
if strings.Contains(opt, "workdir") {
newOptions = append(newOptions, opt)
continue
}

}

switch splitOpt[0] {
case "idmap":
case "O":
foundOverlay = true
case "idmap":
if len(options) > 1 {
return nil, errors.Wrapf(ErrDupeMntOption, "'O' option can not be used with other options")
}
Expand Down
47 changes: 47 additions & 0 deletions test/e2e/run_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,53 @@ var _ = Describe("Podman run with volumes", func() {

})

It("podman support overlay on named 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")
}
}

// create persistant upperdir on host
upperDir := filepath.Join(tempdir, "upper")
err := os.Mkdir(upperDir, 0755)
Expect(err).To(BeNil(), "mkdir "+upperDir)

// create persistant 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)

session := podmanTest.Podman([]string{"volume", "create", "myvolume"})
session.WaitWithDefaultTimeout()
volName := session.OutputToString()
Expect(session).Should(Exit(0))

// create file on actual volume
session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

// create file on overlay volume
session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "echo hello >> " + "/data/overlay"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

// volume should contain only `test` not `overlay`
session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data:O," + overlayOpts, ALPINE, "sh", "-c", "ls /data"})
session.WaitWithDefaultTimeout()
// must contain stuff on persistant upper and workdir
Expect(session.OutputToString()).To(ContainSubstring("overlay"))
Expect(session.OutputToString()).To(ContainSubstring("test"))

})

It("podman run with noexec can't exec", func() {
session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"})
session.WaitWithDefaultTimeout()
Expand Down
35 changes: 29 additions & 6 deletions vendor/github.com/containers/buildah/pkg/overlay/overlay.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion vendor/github.com/containers/common/pkg/parse/parse.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d678b25

Please sign in to comment.