Merge pull request #19309 from rhatdan/volumes

Add glob support to podman run/create --mount
This commit is contained in:
Daniel J Walsh 2023-07-27 15:22:12 -04:00 committed by GitHub
commit 538ac5dc8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 6 deletions

View File

@ -6,7 +6,7 @@
Attach a filesystem mount to the container
Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and **devpts**. <sup>[[1]](#Footnote1)</sup>
Current supported mount TYPEs are **bind**, **devpts**, **glob**, **image**, **tmpfs** and **volume**. <sup>[[1]](#Footnote1)</sup>
e.g.
@ -16,6 +16,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true
type=glob,src=/usr/lib/libfoo*,destination=/usr/lib,ro=true
type=volume,source=vol1,destination=/path/in/container,ro=true
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
@ -26,10 +28,12 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
Common Options:
· src, source: mount source spec for bind and volume. Mandatory for bind.
· src, source: mount source spec for bind, glob, and volume. Mandatory for bind and glob.
· dst, destination, target: mount destination spec.
Paths matching globs, are mounted on the destination directory with the identical name inside the container.
Options specific to volume:
· ro, readonly: true or false (default).
@ -47,7 +51,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· rw, readwrite: true or false (default).
Options specific to bind:
Options specific to bind and glob:
· ro, readonly: true or false (default).

View File

@ -443,6 +443,12 @@ $ podman create --name container3 --requires container1,container2 -t -i fedora
$ podman start --attach container3
```
### Exposing shared libraries inside of container as read-only using a glob
```
$ podman create --mount type=glob,src=/usr/lib64/libnvidia\*,ro -i -t fedora /bin/bash
```
### Configure keep supplemental groups for access to volume
```

View File

@ -468,6 +468,12 @@ $ podman run --read-only -i -t fedora /bin/bash
$ podman run --read-only --read-only-tmpfs=false --tmpfs /run -i -t fedora /bin/bash
```
### Exposing shared libraries inside of container as read-only using a glob
```
$ podman run --mount type=glob,src=/usr/lib64/libnvidia\*,ro=true -i -t fedora /bin/bash
```
### Exposing log messages from the container to the host's log
Bind mount the _/dev/log_ directory to have messages that are logged in the container show up in the host's

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"path"
"path/filepath"
"strings"
"github.com/containers/common/pkg/config"
@ -18,7 +19,7 @@ import (
var (
errOptionArg = errors.New("must provide an argument for option")
errNoDest = errors.New("must set volume destination")
errInvalidSyntax = errors.New("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
errInvalidSyntax = errors.New("incorrect mount format: should be --mount type=<bind|glob|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
)
// Parse all volume-related options in the create config into a set of mounts
@ -196,6 +197,20 @@ func Mounts(mountFlag []string, configMounts []string) (map[string]spec.Mount, m
return fmt.Errorf("%v: %w", mount.Destination, specgen.ErrDuplicateDest)
}
finalMounts[mount.Destination] = mount
case "glob":
mounts, err := getGlobMounts(tokens)
if err != nil {
return err
}
for _, mount := range mounts {
if _, ok := finalMounts[mount.Destination]; ok {
if ignoreDup {
continue
}
return fmt.Errorf("%v: %w", mount.Destination, specgen.ErrDuplicateDest)
}
finalMounts[mount.Destination] = mount
}
case define.TypeTmpfs:
mount, err := getTmpfsMount(tokens)
if err != nil {
@ -438,12 +453,49 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption)
}
}
if len(mnt.Destination) == 0 {
if mountType != "glob" && len(mnt.Destination) == 0 {
return nil, errNoDest
}
return &mnt, nil
}
// Parse glob mounts entry from the --mount flag.
func getGlobMounts(args []string) ([]spec.Mount, error) {
mounts := []spec.Mount{}
mnt, err := parseMountOptions("glob", args)
if err != nil {
return nil, err
}
globs, err := filepath.Glob(mnt.Source)
if err != nil {
return nil, err
}
if len(globs) == 0 {
return nil, fmt.Errorf("no file paths matching glob %q", mnt.Source)
}
options, err := parse.ValidateVolumeOpts(mnt.Options)
if err != nil {
return nil, err
}
for _, src := range globs {
var newMount spec.Mount
newMount.Type = define.TypeBind
newMount.Options = options
newMount.Source = src
if len(mnt.Destination) == 0 {
newMount.Destination = src
} else {
newMount.Destination = filepath.Join(mnt.Destination, filepath.Base(src))
}
mounts = append(mounts, newMount)
}
return mounts, nil
}
// Parse a single bind mount entry from the --mount flag.
func getBindMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{

View File

@ -664,7 +664,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse
paramsMap[kv[0]] = kv[1]
}
if paramType, ok := paramsMap["type"]; ok {
if paramType == "volume" || paramType == "bind" {
if paramType == "volume" || paramType == "bind" || paramType == "glob" {
var err error
if paramSource, ok := paramsMap["source"]; ok {
paramsMap["source"], err = handleStorageSource(container, service, paramSource, names)

View File

@ -247,4 +247,43 @@ EOF
buildah rm $external_cid
}
@test "podman volume globs" {
v1a=v1_$(random_string)
v1b=v1_$(random_string)
v2=v2_$(random_string)
vol1a=${PODMAN_TMPDIR}/$v1a
vol1b=${PODMAN_TMPDIR}/$v1b
vol2=${PODMAN_TMPDIR}/$v2
touch $vol1a $vol1b $vol2
# if volumes source and dest match then pass
run_podman run --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,ro $IMAGE ls $vol1a $vol1b
run_podman 1 run --rm --mount source=${PODMAN_TMPDIR}/v1\*,type=glob,ro $IMAGE ls $vol2
is "$output" ".*No such file or directory" "$vol2 should not be mounted in the container"
run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v3\*,type=glob,ro $IMAGE ls $vol2
is "$output" "Error: no file paths matching glob \"${PODMAN_TMPDIR}/v3\*\"" "Glob does not match so should throw error"
run_podman 1 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=glob,ro,Z $IMAGE touch $vol2
is "$output" "touch: $vol2: Read-only file system" "Mount should be read-only"
run_podman run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=glob,ro=false,Z $IMAGE touch $vol2
run_podman run --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,destination=/non/existing/directory,ro $IMAGE ls /non/existing/directory
is "$output" ".*$v1a" "podman images --inspect should include $v1a"
is "$output" ".*$v1b" "podman images --inspect should include $v1b"
run_podman create --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,ro $IMAGE ls $vol1a $vol1b
cid=$output
run_podman container inspect $output
is "$output" ".*$vol1a" "podman images --inspect should include $vol1a"
is "$output" ".*$vol1b" "podman images --inspect should include $vol1b"
run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=bind,ro=false $IMAGE touch $vol2
is "$output" "Error: must set volume destination" "Bind mounts require destination"
run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,destination=/tmp/foobar, ro=false $IMAGE touch $vol2
is "$output" "Error: invalid reference format" "Default mounts don not support globs"
}
# vim: filetype=sh