summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorumohnani8 <umohnani@redhat.com>2018-02-16 10:38:12 -0500
committerAtomic Bot <atomic-devel@projectatomic.io>2018-02-22 15:14:00 +0000
commit3d395767d8c3e467e784e3836c7175f6d11931a7 (patch)
treea64044df96164ad10873ad5a642e576b99b33bdd
parent7a7a6c2d79ebd831acf0321643903136dca7c2cb (diff)
downloadpodman-3d395767d8c3e467e784e3836c7175f6d11931a7.tar.gz
podman-3d395767d8c3e467e784e3836c7175f6d11931a7.tar.bz2
podman-3d395767d8c3e467e784e3836c7175f6d11931a7.zip
Implement --image-volumes for create and run
--image-volumes tells podman what to do with the image volumes in the image config There are 3 options: bind, tmpfs, and ignore bind puts the volume contents in /var/lib/containers/storage/container-id/volumes/vol-dir and bind mounts it into the container at /vol-dir tmpfs mounts /vol-dir as a tmps into the container ignore doesn't mount the image volumes onto the container Signed-off-by: umohnani8 <umohnani@redhat.com> Closes: #377 Approved by: rhatdan
-rw-r--r--cmd/podman/common.go5
-rw-r--r--cmd/podman/create.go42
-rw-r--r--cmd/podman/run.go3
-rw-r--r--cmd/podman/spec.go24
-rw-r--r--cmd/podman/spec_test.go2
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/podman-create.1.md8
-rw-r--r--docs/podman-run.1.md8
-rw-r--r--libpod/container_api.go7
-rw-r--r--libpod/container_internal.go45
-rw-r--r--libpod/util.go13
11 files changed, 141 insertions, 18 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 657535e63..8dee6cb98 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -197,6 +197,11 @@ var createFlags = []cli.Flag{
Name: "hostname",
Usage: "Set container hostname",
},
+ cli.StringFlag{
+ Name: "image-volume, builtin-volume",
+ Usage: "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')",
+ Value: "bind",
+ },
cli.BoolFlag{
Name: "interactive, i",
Usage: "Keep STDIN open even if not attached",
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 3d811b58b..810a5e3ed 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -89,6 +89,8 @@ type createConfig struct {
Hostname string //hostname
Image string
ImageID string
+ BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
+ ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
Interactive bool //interactive
IpcMode container.IpcMode //ipc
IP6Address string //ipv6
@@ -180,6 +182,7 @@ func createCmd(c *cli.Context) error {
if err != nil {
return err
}
+ useImageVolumes := createConfig.ImageVolumeType == "bind"
runtimeSpec, err := createConfigToOCISpec(createConfig)
if err != nil {
@@ -190,7 +193,7 @@ func createCmd(c *cli.Context) error {
return errors.Wrapf(err, "unable to parse new container options")
}
// Gather up the options for NewContainer which consist of With... funcs
- options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, true))
+ options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, useImageVolumes))
options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
options = append(options, libpod.WithLabels(createConfig.Labels))
options = append(options, libpod.WithUser(createConfig.User))
@@ -626,19 +629,32 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
}
+ ImageVolumes := data.ContainerConfig.Volumes
+
+ var imageVolType = map[string]string{
+ "bind": "",
+ "tmpfs": "",
+ "ignore": "",
+ }
+ if _, ok := imageVolType[c.String("image-volume")]; !ok {
+ return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume"))
+ }
+
config := &createConfig{
- Runtime: runtime,
- CapAdd: c.StringSlice("cap-add"),
- CapDrop: c.StringSlice("cap-drop"),
- CgroupParent: c.String("cgroup-parent"),
- Command: command,
- Detach: c.Bool("detach"),
- Devices: c.StringSlice("device"),
- DNSOpt: c.StringSlice("dns-opt"),
- DNSSearch: c.StringSlice("dns-search"),
- DNSServers: c.StringSlice("dns"),
- Entrypoint: entrypoint,
- Env: env,
+ Runtime: runtime,
+ BuiltinImgVolumes: ImageVolumes,
+ ImageVolumeType: c.String("image-volume"),
+ CapAdd: c.StringSlice("cap-add"),
+ CapDrop: c.StringSlice("cap-drop"),
+ CgroupParent: c.String("cgroup-parent"),
+ Command: command,
+ Detach: c.Bool("detach"),
+ Devices: c.StringSlice("device"),
+ DNSOpt: c.StringSlice("dns-opt"),
+ DNSSearch: c.StringSlice("dns-search"),
+ DNSServers: c.StringSlice("dns"),
+ Entrypoint: entrypoint,
+ Env: env,
//ExposedPorts: ports,
GroupAdd: groupAdd,
Hostname: c.String("hostname"),
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index f13e293bc..3d6175cef 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -54,6 +54,7 @@ func runCmd(c *cli.Context) error {
if err != nil {
return err
}
+ useImageVolumes := createConfig.ImageVolumeType == "bind"
runtimeSpec, err := createConfigToOCISpec(createConfig)
if err != nil {
@@ -66,7 +67,7 @@ func runCmd(c *cli.Context) error {
}
// Gather up the options for NewContainer which consist of With... funcs
- options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, true))
+ options = append(options, libpod.WithRootFSFromImage(createConfig.ImageID, createConfig.Image, useImageVolumes))
options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
options = append(options, libpod.WithLabels(createConfig.Labels))
options = append(options, libpod.WithUser(createConfig.User))
diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go
index e78118b2f..2c2005399 100644
--- a/cmd/podman/spec.go
+++ b/cmd/podman/spec.go
@@ -351,7 +351,7 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
}
// BIND MOUNTS
- mounts, err := config.GetVolumeMounts()
+ mounts, err := config.GetVolumeMounts(configSpec.Mounts)
if err != nil {
return nil, errors.Wrapf(err, "error getting volume mounts")
}
@@ -500,7 +500,7 @@ func getDefaultAnnotations() map[string]string {
}
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
-func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
+func (c *createConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
var m []spec.Mount
var options []string
for _, i := range c.Volumes {
@@ -509,6 +509,9 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
if len(spliti) > 2 {
options = strings.Split(spliti[2], ",")
}
+ if libpod.MountExists(specMounts, spliti[1]) {
+ continue
+ }
options = append(options, "rbind")
var foundrw, foundro, foundz, foundZ bool
var rootProp string
@@ -550,6 +553,23 @@ func (c *createConfig) GetVolumeMounts() ([]spec.Mount, error) {
Options: options,
})
}
+
+ // volumes from image config
+ if c.ImageVolumeType != "tmpfs" {
+ return m, nil
+ }
+ for vol := range c.BuiltinImgVolumes {
+ if libpod.MountExists(specMounts, vol) {
+ continue
+ }
+ mount := spec.Mount{
+ Destination: vol,
+ Type: string(TypeTmpfs),
+ Source: string(TypeTmpfs),
+ Options: []string{"rw", "noexec", "nosuid", "nodev", "tmpcopyup"},
+ }
+ m = append(m, mount)
+ }
return m, nil
}
diff --git a/cmd/podman/spec_test.go b/cmd/podman/spec_test.go
index 768b079aa..1e1064df9 100644
--- a/cmd/podman/spec_test.go
+++ b/cmd/podman/spec_test.go
@@ -18,7 +18,7 @@ func TestCreateConfig_GetVolumeMounts(t *testing.T) {
config := createConfig{
Volumes: []string{"foobar:/foobar:ro"},
}
- specMount, err := config.GetVolumeMounts()
+ specMount, err := config.GetVolumeMounts([]spec.Mount{})
assert.NoError(t, err)
assert.True(t, reflect.DeepEqual(data, specMount[0]))
}
diff --git a/completions/bash/podman b/completions/bash/podman
index 905792a9b..6d9098fc9 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1042,6 +1042,7 @@ _podman_container_run() {
--attach -a
--blkio-weight
--blkio-weight-device
+ --builtin-volume
--cap-add
--cap-drop
--cgroup-parent
@@ -1068,6 +1069,7 @@ _podman_container_run() {
--expose
--group-add
--hostname -h
+ --image-volume
--init-path
--ip
--ip6
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 7d2b62fb0..98141221b 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -217,6 +217,14 @@ inside of the container.
**--help**
Print usage statement
+**--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
+ Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind').
+ bind: A directory is created inside the container state directory and bind mounted into
+ the container for the volumes.
+ tmpfs: The volume is mounted onto the container as a tmpfs, which allows the users to create
+ content that dissapears when the container is stopped.
+ ignore: All volumes are just ignored and no action is taken.
+
**-i**, **--interactive**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 53239422d..5e235514c 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -214,6 +214,14 @@ inside of the container.
**--help**
Print usage statement
+**--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
+ Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')
+ bind: A directory is created inside the container state directory and bind mounted into
+ the container for the volumes.
+ tmpfs: The volume is mounted onto the container as a tmpfs, which allows the users to create
+ content that dissapears when the container is stopped.
+ ignore: All volumes are just ignored and no action is taken.
+
**-i**, **--interactive**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 6fdc45589..149197470 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -144,6 +144,13 @@ func (c *Container) Init() (err error) {
}
g.AddMount(hostnameMnt)
+ // Bind builtin image volumes
+ if c.config.ImageVolumes {
+ if err = c.addImageVolumes(&g); err != nil {
+ return errors.Wrapf(err, "error mounting image volumes")
+ }
+ }
+
if c.config.User != "" {
if !c.state.Mounted {
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID())
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 6e9852d1e..e22d36f99 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -12,10 +12,12 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/chrootarchive"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/docker/docker/pkg/stringid"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -271,6 +273,49 @@ func (c *Container) export(path string) error {
return err
}
+func (c *Container) addImageVolumes(g *generate.Generator) error {
+ mountPoint := c.state.Mountpoint
+ if !c.state.Mounted {
+ return errors.Wrapf(ErrInternal, "container is not mounted")
+ }
+
+ imageStorage, err := c.runtime.getImage(c.config.RootfsImageID)
+ if err != nil {
+ return err
+ }
+ imageData, err := c.runtime.getImageInspectInfo(*imageStorage)
+ if err != nil {
+ return err
+ }
+
+ for k := range imageData.ContainerConfig.Volumes {
+ mount := spec.Mount{
+ Destination: k,
+ Type: "bind",
+ Options: []string{"rbind", "rw"},
+ }
+ if MountExists(g.Mounts(), k) {
+ continue
+ }
+ volumePath := filepath.Join(c.config.StaticDir, "volumes", k)
+ if _, err := os.Stat(volumePath); os.IsNotExist(err) {
+ if err = os.MkdirAll(volumePath, 0755); err != nil {
+ return errors.Wrapf(err, "error creating directory %q for volume %q in container %q", volumePath, k, c.ID)
+ }
+ if err = label.Relabel(volumePath, c.config.MountLabel, false); err != nil {
+ return errors.Wrapf(err, "error relabeling directory %q for volume %q in container %q", volumePath, k, c.ID)
+ }
+ srcPath := filepath.Join(mountPoint, k)
+ if err = chrootarchive.NewArchiver(nil).CopyWithTar(srcPath, volumePath); err != nil && !os.IsNotExist(err) {
+ return errors.Wrapf(err, "error populating directory %q for volume %q in container %q using contents of %q", volumePath, k, c.ID, srcPath)
+ }
+ mount.Source = volumePath
+ }
+ g.AddMount(mount)
+ }
+ return nil
+}
+
// Get path of artifact with a given name for this container
func (c *Container) getArtifactPath(name string) string {
return filepath.Join(c.config.StaticDir, artifactsDir, name)
diff --git a/libpod/util.go b/libpod/util.go
index 1a033a940..0c6700fbf 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -4,13 +4,14 @@ import (
"fmt"
"os"
"path/filepath"
+ "strconv"
"strings"
"time"
"github.com/containers/image/signature"
"github.com/containers/image/types"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
- "strconv"
)
// Runtime API constants
@@ -96,3 +97,13 @@ func RemoveScientificNotationFromFloat(x float64) (float64, error) {
}
return result, nil
}
+
+// MountExists returns true if dest exists in the list of mounts
+func MountExists(specMounts []spec.Mount, dest string) bool {
+ for _, m := range specMounts {
+ if m.Destination == dest {
+ return true
+ }
+ }
+ return false
+}