mirror of
https://github.com/containers/podman.git
synced 2025-06-29 15:08:09 +08:00
Add --mount option for create
& run
command
Signed-off-by: Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> Signed-off-by: Daniel J Walsh <dwalsh@redhat.com> Closes: #1524 Approved by: mheon
This commit is contained in:

committed by
Atomic Bot

parent
9e81f9daa4
commit
52c1365f32
@ -417,6 +417,10 @@ var createFlags = []cli.Flag{
|
||||
Name: "uts",
|
||||
Usage: "UTS namespace to use",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "mount",
|
||||
Usage: "Attach a filesystem mount to the container (default [])",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "volume, v",
|
||||
Usage: "Bind mount a volume into the container (default [])",
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -459,6 +460,10 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
}
|
||||
blkioWeight = uint16(u)
|
||||
}
|
||||
var mountList []spec.Mount
|
||||
if mountList, err = parseMounts(c.StringSlice("mount")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = parseVolumes(c.StringSlice("volume")); err != nil {
|
||||
return nil, err
|
||||
@ -772,6 +777,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
Tty: tty,
|
||||
User: user,
|
||||
UsernsMode: usernsMode,
|
||||
Mounts: mountList,
|
||||
Volumes: c.StringSlice("volume"),
|
||||
WorkDir: workDir,
|
||||
Rootfs: rootfs,
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
cc "github.com/containers/libpod/pkg/spec"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"github.com/docker/go-units"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -74,6 +76,94 @@ func addWarning(warnings []string, msg string) []string {
|
||||
return append(warnings, msg)
|
||||
}
|
||||
|
||||
// Format supported.
|
||||
// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
|
||||
// podman run --mount type=tmpfs,target=/dev/shm ..
|
||||
func parseMounts(mounts []string) ([]spec.Mount, error) {
|
||||
var mountList []spec.Mount
|
||||
errInvalidSyntax := errors.Errorf("incorrect mount format : should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>,[options]")
|
||||
for _, mount := range mounts {
|
||||
var tokenCount int
|
||||
var mountInfo spec.Mount
|
||||
|
||||
arr := strings.SplitN(mount, ",", 2)
|
||||
if len(arr) < 2 {
|
||||
return nil, errInvalidSyntax
|
||||
}
|
||||
kv := strings.Split(arr[0], "=")
|
||||
if kv[0] != "type" {
|
||||
return nil, errInvalidSyntax
|
||||
}
|
||||
switch kv[1] {
|
||||
case "bind":
|
||||
mountInfo.Type = string(cc.TypeBind)
|
||||
case "tmpfs":
|
||||
mountInfo.Type = string(cc.TypeTmpfs)
|
||||
mountInfo.Source = string(cc.TypeTmpfs)
|
||||
mountInfo.Options = append(mountInfo.Options, []string{"rprivate", "noexec", "nosuid", "nodev", "size=65536k"}...)
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("invalid filesystem type %q", kv[1])
|
||||
}
|
||||
|
||||
tokens := strings.Split(arr[1], ",")
|
||||
for i, val := range tokens {
|
||||
if i == (tokenCount - 1) {
|
||||
//Parse tokens before options.
|
||||
break
|
||||
}
|
||||
kv := strings.Split(val, "=")
|
||||
switch kv[0] {
|
||||
case "ro", "nosuid", "nodev", "noexec":
|
||||
mountInfo.Options = append(mountInfo.Options, kv[0])
|
||||
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
|
||||
if mountInfo.Type != "bind" {
|
||||
return nil, errors.Errorf("%s can only be used with bind mounts", kv[0])
|
||||
}
|
||||
mountInfo.Options = append(mountInfo.Options, kv[0])
|
||||
case "tmpfs-mode":
|
||||
if mountInfo.Type != "tmpfs" {
|
||||
return nil, errors.Errorf("%s can only be used with tmpfs mounts", kv[0])
|
||||
}
|
||||
mountInfo.Options = append(mountInfo.Options, fmt.Sprintf("mode=%s", kv[1]))
|
||||
case "tmpfs-size":
|
||||
if mountInfo.Type != "tmpfs" {
|
||||
return nil, errors.Errorf("%s can only be used with tmpfs mounts", kv[0])
|
||||
}
|
||||
shmSize, err := units.FromHumanSize(kv[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to translate tmpfs-size")
|
||||
}
|
||||
|
||||
mountInfo.Options = append(mountInfo.Options, fmt.Sprintf("size=%d", shmSize))
|
||||
|
||||
case "bind-propagation":
|
||||
if mountInfo.Type != "bind" {
|
||||
return nil, errors.Errorf("%s can only be used with bind mounts", kv[0])
|
||||
}
|
||||
mountInfo.Options = append(mountInfo.Options, kv[1])
|
||||
case "src", "source":
|
||||
if mountInfo.Type == "tmpfs" {
|
||||
return nil, errors.Errorf("can not use src= on a tmpfs file system")
|
||||
}
|
||||
if err := validateVolumeHostDir(kv[1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mountInfo.Source = kv[1]
|
||||
case "target", "dst", "destination":
|
||||
if err := validateVolumeCtrDir(kv[1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mountInfo.Destination = kv[1]
|
||||
default:
|
||||
return nil, errors.Errorf("incorrect mount option : %s", kv[0])
|
||||
}
|
||||
}
|
||||
mountList = append(mountList, mountInfo)
|
||||
}
|
||||
return mountList, nil
|
||||
}
|
||||
|
||||
func parseVolumes(volumes []string) error {
|
||||
for _, volume := range volumes {
|
||||
arr := strings.SplitN(volume, ":", 3)
|
||||
|
@ -945,6 +945,7 @@ _podman_build() {
|
||||
--userns-uid-map-user
|
||||
--userns-gid-map-group
|
||||
--uts
|
||||
--mount
|
||||
--volume
|
||||
-v
|
||||
"
|
||||
|
@ -372,6 +372,36 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
|
||||
|
||||
Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
|
||||
|
||||
**--mount**=*type=TYPE,TYPE-SPECIFIC-OPTION[,...]*
|
||||
|
||||
Attach a filesystem mount to the container
|
||||
|
||||
Current supported mount TYPES are bind, and tmpfs.
|
||||
|
||||
e.g.
|
||||
|
||||
type=bind,source=/path/on/host,destination=/path/in/container
|
||||
|
||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
|
||||
Common Options:
|
||||
|
||||
· src, source: mount source spec for bind and volume. Mandatory for bind.
|
||||
|
||||
· dst, destination, target: mount destination spec.
|
||||
|
||||
· ro, read-only: true or false (default).
|
||||
|
||||
Options specific to bind:
|
||||
|
||||
· bind-propagation: shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
|
||||
|
||||
Options specific to tmpfs:
|
||||
|
||||
· tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
|
||||
|
||||
· tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
|
||||
|
||||
**--name**=""
|
||||
|
||||
Assign a name to the container
|
||||
|
@ -655,6 +655,36 @@ Set the UTS mode for the container
|
||||
|
||||
**NOTE**: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
|
||||
|
||||
**--mount**=*type=TYPE,TYPE-SPECIFIC-OPTION[,...]*
|
||||
|
||||
Attach a filesystem mount to the container
|
||||
|
||||
Current supported mount TYPES are bind, and tmpfs.
|
||||
|
||||
e.g.
|
||||
|
||||
type=bind,source=/path/on/host,destination=/path/in/container
|
||||
|
||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
|
||||
Common Options:
|
||||
|
||||
· src, source: mount source spec for bind and volume. Mandatory for bind.
|
||||
|
||||
· dst, destination, target: mount destination spec.
|
||||
|
||||
· ro, read-only: true or false (default).
|
||||
|
||||
Options specific to bind:
|
||||
|
||||
· bind-propagation: Z, z, shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
|
||||
|
||||
Options specific to tmpfs:
|
||||
|
||||
· tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
|
||||
|
||||
· tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
|
||||
|
||||
**-v**|**--volume**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
|
||||
|
||||
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
|
||||
@ -931,6 +961,12 @@ colon:
|
||||
$ podman run -v /var/db:/data1 -i -t fedora bash
|
||||
```
|
||||
|
||||
Using --mount flags, To mount a host directory as a container folder, specify
|
||||
the absolute path to the directory and the absolute path for the container
|
||||
directory:
|
||||
|
||||
$ podman run --mount type=bind,src=/var/db,target=/data1 busybox sh
|
||||
|
||||
When using SELinux, be aware that the host has no knowledge of container SELinux
|
||||
policy. Therefore, in the above example, if SELinux policy is enforced, the
|
||||
`/var/db` directory is not writable to the container. A "Permission Denied"
|
||||
@ -1030,6 +1066,8 @@ $ podman run --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
|
||||
subgid(5), subuid(5), libpod.conf(5)
|
||||
|
||||
## HISTORY
|
||||
September 2018, updated by Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
|
||||
|
||||
October 2017, converted from Docker documentation to podman by Dan Walsh for podman <dwalsh@redhat.com>
|
||||
|
||||
November 2015, updated by Sally O'Malley <somalley@redhat.com>
|
||||
|
@ -926,6 +926,9 @@ func (c *Container) makeBindMounts() error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
|
||||
}
|
||||
if err = label.Relabel(newResolv, c.config.MountLabel, false); err != nil {
|
||||
return errors.Wrapf(err, "error relabeling %q for container %q", newResolv, c.ID)
|
||||
}
|
||||
c.state.BindMounts["/etc/resolv.conf"] = newResolv
|
||||
|
||||
// Make /etc/hosts
|
||||
@ -937,6 +940,9 @@ func (c *Container) makeBindMounts() error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
|
||||
}
|
||||
if err = label.Relabel(newHosts, c.config.MountLabel, false); err != nil {
|
||||
return errors.Wrapf(err, "error relabeling %q for container %q", newHosts, c.ID)
|
||||
}
|
||||
c.state.BindMounts["/etc/hosts"] = newHosts
|
||||
|
||||
// Make /etc/hostname
|
||||
@ -946,6 +952,9 @@ func (c *Container) makeBindMounts() error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating hostname file for container %s", c.ID())
|
||||
}
|
||||
if err = label.Relabel(hostnamePath, c.config.MountLabel, false); err != nil {
|
||||
return errors.Wrapf(err, "error relabeling %q for container %q", hostnamePath, c.ID)
|
||||
}
|
||||
c.state.BindMounts["/etc/hostname"] = hostnamePath
|
||||
}
|
||||
|
||||
|
@ -283,6 +283,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
||||
mounts := sortMounts(g.Mounts())
|
||||
g.ClearMounts()
|
||||
for _, m := range mounts {
|
||||
switch m.Type {
|
||||
case "tmpfs", "devpts":
|
||||
o := label.FormatMountLabel("", c.config.MountLabel)
|
||||
if o != "" {
|
||||
m.Options = append(m.Options, o)
|
||||
}
|
||||
}
|
||||
g.AddMount(m)
|
||||
}
|
||||
return g.Config, nil
|
||||
|
@ -122,6 +122,7 @@ type CreateConfig struct {
|
||||
UsernsMode namespaces.UsernsMode //userns
|
||||
User string //user
|
||||
UtsMode namespaces.UTSMode //uts
|
||||
Mounts []spec.Mount //mounts
|
||||
Volumes []string //volume
|
||||
VolumesFrom []string
|
||||
WorkDir string //workdir
|
||||
@ -142,54 +143,59 @@ func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
|
||||
return c.createBlockIO()
|
||||
}
|
||||
|
||||
func processOptions(options []string) []string {
|
||||
var (
|
||||
foundrw, foundro bool
|
||||
rootProp string
|
||||
)
|
||||
options = append(options, "rbind")
|
||||
for _, opt := range options {
|
||||
switch opt {
|
||||
case "rw":
|
||||
foundrw = true
|
||||
case "ro":
|
||||
foundro = true
|
||||
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
|
||||
rootProp = opt
|
||||
}
|
||||
}
|
||||
if !foundrw && !foundro {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
if rootProp == "" {
|
||||
options = append(options, "rprivate")
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func (c *CreateConfig) initFSMounts() []spec.Mount {
|
||||
var mounts []spec.Mount
|
||||
for _, m := range c.Mounts {
|
||||
m.Options = processOptions(m.Options)
|
||||
if m.Type == "tmpfs" {
|
||||
m.Options = append(m.Options, "tmpcopyup")
|
||||
} else {
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
|
||||
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
|
||||
var m []spec.Mount
|
||||
for _, i := range c.Volumes {
|
||||
var (
|
||||
options []string
|
||||
foundrw, foundro, foundz, foundZ bool
|
||||
rootProp string
|
||||
)
|
||||
|
||||
// We need to handle SELinux options better here, specifically :Z
|
||||
var options []string
|
||||
spliti := strings.Split(i, ":")
|
||||
if len(spliti) > 2 {
|
||||
options = strings.Split(spliti[2], ",")
|
||||
}
|
||||
options = append(options, "rbind")
|
||||
for _, opt := range options {
|
||||
switch opt {
|
||||
case "rw":
|
||||
foundrw = true
|
||||
case "ro":
|
||||
foundro = true
|
||||
case "z":
|
||||
foundz = true
|
||||
case "Z":
|
||||
foundZ = true
|
||||
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
|
||||
rootProp = opt
|
||||
}
|
||||
}
|
||||
if !foundrw && !foundro {
|
||||
options = append(options, "rw")
|
||||
}
|
||||
if foundz {
|
||||
options = append(options, "z")
|
||||
}
|
||||
if foundZ {
|
||||
options = append(options, "Z")
|
||||
}
|
||||
if rootProp == "" {
|
||||
options = append(options, "rprivate")
|
||||
}
|
||||
|
||||
m = append(m, spec.Mount{
|
||||
Destination: spliti[1],
|
||||
Type: string(TypeBind),
|
||||
Source: spliti[0],
|
||||
Options: options,
|
||||
Options: processOptions(options),
|
||||
})
|
||||
|
||||
logrus.Debugf("User mount %s:%s options %v", spliti[0], spliti[1], options)
|
||||
|
@ -18,6 +18,34 @@ import (
|
||||
|
||||
const cpuPeriod = 100000
|
||||
|
||||
func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
|
||||
if len(mounts) > 0 {
|
||||
// If we have overlappings mounts, remove them from the spec in favor of
|
||||
// the user-added volume mounts
|
||||
destinations := make(map[string]bool)
|
||||
for _, mount := range mounts {
|
||||
destinations[path.Clean(mount.Destination)] = true
|
||||
}
|
||||
// Copy all mounts from spec to defaultMounts, except for
|
||||
// - mounts overridden by a user supplied mount;
|
||||
// - all mounts under /dev if a user supplied /dev is present;
|
||||
mountDev := destinations["/dev"]
|
||||
for _, mount := range configMount {
|
||||
if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
|
||||
if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
|
||||
// filter out everything under /dev if /dev is user-mounted
|
||||
continue
|
||||
}
|
||||
|
||||
logrus.Debugf("Adding mount %s", mount.Destination)
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
return configMount
|
||||
}
|
||||
|
||||
// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec
|
||||
func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
||||
cgroupPerm := "ro"
|
||||
@ -246,6 +274,12 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
||||
g.AddMount(tmpfsMnt)
|
||||
}
|
||||
|
||||
for _, m := range config.Mounts {
|
||||
if m.Type == "tmpfs" {
|
||||
g.AddMount(m)
|
||||
}
|
||||
}
|
||||
|
||||
for name, val := range config.Env {
|
||||
g.AddProcessEnv(name, val)
|
||||
}
|
||||
@ -305,36 +339,14 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
||||
return nil, errors.Wrap(err, "error getting volume mounts from --volumes-from flag")
|
||||
}
|
||||
|
||||
mounts, err := config.GetVolumeMounts(configSpec.Mounts)
|
||||
volumeMounts, err := config.GetVolumeMounts(configSpec.Mounts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting volume mounts")
|
||||
}
|
||||
if len(mounts) > 0 {
|
||||
// If we have overlappings mounts, remove them from the spec in favor of
|
||||
// the user-added volume mounts
|
||||
destinations := make(map[string]bool)
|
||||
for _, mount := range mounts {
|
||||
destinations[path.Clean(mount.Destination)] = true
|
||||
}
|
||||
|
||||
// Copy all mounts from spec to defaultMounts, except for
|
||||
// - mounts overridden by a user supplied mount;
|
||||
// - all mounts under /dev if a user supplied /dev is present;
|
||||
mountDev := destinations["/dev"]
|
||||
for _, mount := range configSpec.Mounts {
|
||||
if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
|
||||
if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
|
||||
// filter out everything under /dev if /dev is user-mounted
|
||||
continue
|
||||
}
|
||||
|
||||
logrus.Debugf("Adding mount %s", mount.Destination)
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
}
|
||||
configSpec.Mounts = mounts
|
||||
}
|
||||
|
||||
configSpec.Mounts = supercedeUserMounts(volumeMounts, configSpec.Mounts)
|
||||
//--mount
|
||||
configSpec.Mounts = supercedeUserMounts(config.initFSMounts(), configSpec.Mounts)
|
||||
if canAddResources {
|
||||
// BLOCK IO
|
||||
blkio, err := config.CreateBlockIO()
|
||||
|
@ -234,6 +234,32 @@ var _ = Describe("Podman run", func() {
|
||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test rw,relatime, shared"))
|
||||
})
|
||||
|
||||
It("podman run with mount flag", func() {
|
||||
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
|
||||
os.Mkdir(mountPath, 0755)
|
||||
session := podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test rw"))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,ro", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test ro"))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/run/test,shared", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test rw,relatime shared"))
|
||||
|
||||
mountPath = filepath.Join(podmanTest.TempDir, "scratchpad")
|
||||
os.Mkdir(mountPath, 0755)
|
||||
session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/run/test", ALPINE, "grep", "/run/test", "/proc/self/mountinfo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test rw,nosuid,nodev,noexec,relatime - tmpfs"))
|
||||
})
|
||||
|
||||
It("podman run with cidfile", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--cidfile", tempdir + "cidfile", ALPINE, "ls"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
@ -565,6 +591,19 @@ USER mail`
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman run --mount flag with multiple mounts", func() {
|
||||
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
|
||||
err := os.MkdirAll(vol1, 0755)
|
||||
Expect(err).To(BeNil())
|
||||
vol2 := filepath.Join(podmanTest.TempDir, "vol-test2")
|
||||
err = os.MkdirAll(vol2, 0755)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
session := podmanTest.Podman([]string{"run", "--mount", "type=bind,src=" + vol1 + ",target=/myvol1,z", "--mount", "type=bind,src=" + vol2 + ",target=/myvol2,z", ALPINE, "touch", "/myvol2/foo.txt"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman run findmnt nothing shared", func() {
|
||||
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
|
||||
err := os.MkdirAll(vol1, 0755)
|
||||
|
Reference in New Issue
Block a user