mirror of
https://github.com/containers/podman.git
synced 2025-06-29 06:57:13 +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",
|
Name: "uts",
|
||||||
Usage: "UTS namespace to use",
|
Usage: "UTS namespace to use",
|
||||||
},
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "mount",
|
||||||
|
Usage: "Attach a filesystem mount to the container (default [])",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "volume, v",
|
Name: "volume, v",
|
||||||
Usage: "Bind mount a volume into the container (default [])",
|
Usage: "Bind mount a volume into the container (default [])",
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -459,6 +460,10 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
}
|
}
|
||||||
blkioWeight = uint16(u)
|
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 {
|
if err = parseVolumes(c.StringSlice("volume")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -772,6 +777,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
Tty: tty,
|
Tty: tty,
|
||||||
User: user,
|
User: user,
|
||||||
UsernsMode: usernsMode,
|
UsernsMode: usernsMode,
|
||||||
|
Mounts: mountList,
|
||||||
Volumes: c.StringSlice("volume"),
|
Volumes: c.StringSlice("volume"),
|
||||||
WorkDir: workDir,
|
WorkDir: workDir,
|
||||||
Rootfs: rootfs,
|
Rootfs: rootfs,
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
|
|
||||||
cc "github.com/containers/libpod/pkg/spec"
|
cc "github.com/containers/libpod/pkg/spec"
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"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/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -74,6 +76,94 @@ func addWarning(warnings []string, msg string) []string {
|
|||||||
return append(warnings, msg)
|
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 {
|
func parseVolumes(volumes []string) error {
|
||||||
for _, volume := range volumes {
|
for _, volume := range volumes {
|
||||||
arr := strings.SplitN(volume, ":", 3)
|
arr := strings.SplitN(volume, ":", 3)
|
||||||
|
@ -945,6 +945,7 @@ _podman_build() {
|
|||||||
--userns-uid-map-user
|
--userns-uid-map-user
|
||||||
--userns-gid-map-group
|
--userns-gid-map-group
|
||||||
--uts
|
--uts
|
||||||
|
--mount
|
||||||
--volume
|
--volume
|
||||||
-v
|
-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.
|
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**=""
|
**--name**=""
|
||||||
|
|
||||||
Assign a name to the container
|
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.
|
**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]]*]
|
**-v**|**--volume**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
|
||||||
|
|
||||||
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
|
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
|
$ 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
|
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
|
policy. Therefore, in the above example, if SELinux policy is enforced, the
|
||||||
`/var/db` directory is not writable to the container. A "Permission Denied"
|
`/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)
|
subgid(5), subuid(5), libpod.conf(5)
|
||||||
|
|
||||||
## HISTORY
|
## 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>
|
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>
|
November 2015, updated by Sally O'Malley <somalley@redhat.com>
|
||||||
|
@ -926,6 +926,9 @@ func (c *Container) makeBindMounts() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
|
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
|
c.state.BindMounts["/etc/resolv.conf"] = newResolv
|
||||||
|
|
||||||
// Make /etc/hosts
|
// Make /etc/hosts
|
||||||
@ -937,6 +940,9 @@ func (c *Container) makeBindMounts() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
|
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
|
c.state.BindMounts["/etc/hosts"] = newHosts
|
||||||
|
|
||||||
// Make /etc/hostname
|
// Make /etc/hostname
|
||||||
@ -946,6 +952,9 @@ func (c *Container) makeBindMounts() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating hostname file for container %s", c.ID())
|
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
|
c.state.BindMounts["/etc/hostname"] = hostnamePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +283,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||||||
mounts := sortMounts(g.Mounts())
|
mounts := sortMounts(g.Mounts())
|
||||||
g.ClearMounts()
|
g.ClearMounts()
|
||||||
for _, m := range mounts {
|
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)
|
g.AddMount(m)
|
||||||
}
|
}
|
||||||
return g.Config, nil
|
return g.Config, nil
|
||||||
|
@ -122,6 +122,7 @@ type CreateConfig struct {
|
|||||||
UsernsMode namespaces.UsernsMode //userns
|
UsernsMode namespaces.UsernsMode //userns
|
||||||
User string //user
|
User string //user
|
||||||
UtsMode namespaces.UTSMode //uts
|
UtsMode namespaces.UTSMode //uts
|
||||||
|
Mounts []spec.Mount //mounts
|
||||||
Volumes []string //volume
|
Volumes []string //volume
|
||||||
VolumesFrom []string
|
VolumesFrom []string
|
||||||
WorkDir string //workdir
|
WorkDir string //workdir
|
||||||
@ -142,54 +143,59 @@ func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
|
|||||||
return c.createBlockIO()
|
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
|
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
|
||||||
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
|
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
|
||||||
var m []spec.Mount
|
var m []spec.Mount
|
||||||
for _, i := range c.Volumes {
|
for _, i := range c.Volumes {
|
||||||
var (
|
var options []string
|
||||||
options []string
|
|
||||||
foundrw, foundro, foundz, foundZ bool
|
|
||||||
rootProp string
|
|
||||||
)
|
|
||||||
|
|
||||||
// We need to handle SELinux options better here, specifically :Z
|
|
||||||
spliti := strings.Split(i, ":")
|
spliti := strings.Split(i, ":")
|
||||||
if len(spliti) > 2 {
|
if len(spliti) > 2 {
|
||||||
options = strings.Split(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{
|
m = append(m, spec.Mount{
|
||||||
Destination: spliti[1],
|
Destination: spliti[1],
|
||||||
Type: string(TypeBind),
|
Type: string(TypeBind),
|
||||||
Source: spliti[0],
|
Source: spliti[0],
|
||||||
Options: options,
|
Options: processOptions(options),
|
||||||
})
|
})
|
||||||
|
|
||||||
logrus.Debugf("User mount %s:%s options %v", spliti[0], spliti[1], options)
|
logrus.Debugf("User mount %s:%s options %v", spliti[0], spliti[1], options)
|
||||||
|
@ -18,6 +18,34 @@ import (
|
|||||||
|
|
||||||
const cpuPeriod = 100000
|
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
|
// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec
|
||||||
func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
||||||
cgroupPerm := "ro"
|
cgroupPerm := "ro"
|
||||||
@ -246,6 +274,12 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
|||||||
g.AddMount(tmpfsMnt)
|
g.AddMount(tmpfsMnt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, m := range config.Mounts {
|
||||||
|
if m.Type == "tmpfs" {
|
||||||
|
g.AddMount(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for name, val := range config.Env {
|
for name, val := range config.Env {
|
||||||
g.AddProcessEnv(name, val)
|
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")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error getting volume mounts")
|
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 {
|
if canAddResources {
|
||||||
// BLOCK IO
|
// BLOCK IO
|
||||||
blkio, err := config.CreateBlockIO()
|
blkio, err := config.CreateBlockIO()
|
||||||
|
@ -234,6 +234,32 @@ var _ = Describe("Podman run", func() {
|
|||||||
Expect(session.OutputToString()).To(ContainSubstring("/run/test rw,relatime, shared"))
|
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() {
|
It("podman run with cidfile", func() {
|
||||||
session := podmanTest.Podman([]string{"run", "--cidfile", tempdir + "cidfile", ALPINE, "ls"})
|
session := podmanTest.Podman([]string{"run", "--cidfile", tempdir + "cidfile", ALPINE, "ls"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
@ -565,6 +591,19 @@ USER mail`
|
|||||||
Expect(session.ExitCode()).To(Equal(0))
|
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() {
|
It("podman run findmnt nothing shared", func() {
|
||||||
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
|
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
|
||||||
err := os.MkdirAll(vol1, 0755)
|
err := os.MkdirAll(vol1, 0755)
|
||||||
|
Reference in New Issue
Block a user