Skip to content

Commit

Permalink
Volume refactoring for LCOW
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Ferquel <[email protected]>
  • Loading branch information
simonferquel authored and vieux committed Sep 14, 2017
1 parent d60c186 commit e89b6e8
Show file tree
Hide file tree
Showing 33 changed files with 1,465 additions and 1,289 deletions.
7 changes: 6 additions & 1 deletion container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,19 @@ func (container *Container) ShouldRestart() bool {

// AddMountPointWithVolume adds a new mount point configured with a volume to the container.
func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
operatingSystem := container.Platform
if operatingSystem == "" {
operatingSystem = runtime.GOOS
}
volumeParser := volume.NewParser(operatingSystem)
container.MountPoints[destination] = &volume.MountPoint{
Type: mounttypes.TypeVolume,
Name: vol.Name(),
Driver: vol.DriverName(),
Destination: destination,
RW: rw,
Volume: vol,
CopyData: volume.DefaultCopyMode,
CopyData: volumeParser.DefaultCopyMode(),
}
}

Expand Down
13 changes: 8 additions & 5 deletions container/container_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (container *Container) BuildHostnameFile() error {
func (container *Container) NetworkMounts() []Mount {
var mounts []Mount
shared := container.HostConfig.NetworkMode.IsContainer()
parser := volume.NewParser(container.Platform)
if container.ResolvConfPath != "" {
if _, err := os.Stat(container.ResolvConfPath); err != nil {
logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err)
Expand All @@ -83,7 +84,7 @@ func (container *Container) NetworkMounts() []Mount {
Source: container.ResolvConfPath,
Destination: "/etc/resolv.conf",
Writable: writable,
Propagation: string(volume.DefaultPropagationMode),
Propagation: string(parser.DefaultPropagationMode()),
})
}
}
Expand All @@ -102,7 +103,7 @@ func (container *Container) NetworkMounts() []Mount {
Source: container.HostnamePath,
Destination: "/etc/hostname",
Writable: writable,
Propagation: string(volume.DefaultPropagationMode),
Propagation: string(parser.DefaultPropagationMode()),
})
}
}
Expand All @@ -121,7 +122,7 @@ func (container *Container) NetworkMounts() []Mount {
Source: container.HostsPath,
Destination: "/etc/hosts",
Writable: writable,
Propagation: string(volume.DefaultPropagationMode),
Propagation: string(parser.DefaultPropagationMode()),
})
}
}
Expand Down Expand Up @@ -196,6 +197,7 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro
// IpcMounts returns the list of IPC mounts
func (container *Container) IpcMounts() []Mount {
var mounts []Mount
parser := volume.NewParser(container.Platform)

if container.HasMountFor("/dev/shm") {
return mounts
Expand All @@ -209,7 +211,7 @@ func (container *Container) IpcMounts() []Mount {
Source: container.ShmPath,
Destination: "/dev/shm",
Writable: true,
Propagation: string(volume.DefaultPropagationMode),
Propagation: string(parser.DefaultPropagationMode()),
})

return mounts
Expand Down Expand Up @@ -429,6 +431,7 @@ func copyOwnership(source, destination string) error {

// TmpfsMounts returns the list of tmpfs mounts
func (container *Container) TmpfsMounts() ([]Mount, error) {
parser := volume.NewParser(container.Platform)
var mounts []Mount
for dest, data := range container.HostConfig.Tmpfs {
mounts = append(mounts, Mount{
Expand All @@ -439,7 +442,7 @@ func (container *Container) TmpfsMounts() ([]Mount, error) {
}
for dest, mnt := range container.MountPoints {
if mnt.Type == mounttypes.TypeTmpfs {
data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly)
data, err := parser.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly)
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion daemon/archive_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ package daemon

import (
"github.com/docker/docker/container"
"github.com/docker/docker/volume"
)

// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
// cannot be in a read-only volume. If it is not in a volume, the container
// cannot be configured with a read-only rootfs.
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
var toVolume bool
parser := volume.NewParser(container.Platform)
for _, mnt := range container.MountPoints {
if toVolume = mnt.HasResource(absPath); toVolume {
if toVolume = parser.HasResource(mnt, absPath); toVolume {
if mnt.RW {
break
}
Expand Down
4 changes: 2 additions & 2 deletions daemon/create_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
}
hostConfig.Isolation = "hyperv"
}

parser := volume.NewParser(container.Platform)
for spec := range config.Volumes {

mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)
if err != nil {
return fmt.Errorf("Unrecognised volume spec: %v", err)
}
Expand Down
3 changes: 2 additions & 1 deletion daemon/daemon_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime)
}

parser := volume.NewParser(runtime.GOOS)
for dest := range hostConfig.Tmpfs {
if err := volume.ValidateTmpfsMountDestination(dest); err != nil {
if err := parser.ValidateTmpfsMountDestination(dest); err != nil {
return warnings, err
}
}
Expand Down
4 changes: 3 additions & 1 deletion daemon/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c

// Filter out mounts from spec
noIpc := c.HostConfig.IpcMode.IsNone()
// Filter out mounts that are overridden by user supplied mounts
var defaultMounts []specs.Mount
_, mountDev := userMounts["/dev"]
for _, m := range s.Mounts {
Expand All @@ -524,7 +525,8 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c

if m.Source == "tmpfs" {
data := m.Data
options := []string{"noexec", "nosuid", "nodev", string(volume.DefaultPropagationMode)}
parser := volume.NewParser("linux")
options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())}
if data != "" {
options = append(options, strings.Split(data, ",")...)
}
Expand Down
6 changes: 6 additions & 0 deletions daemon/oci_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
"strings"

containertypes "github.com/docker/docker/api/types/container"
Expand Down Expand Up @@ -108,6 +109,11 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
if !mount.Writable {
m.Options = append(m.Options, "ro")
}
if img.OS != runtime.GOOS {
m.Type = "bind"
m.Options = append(m.Options, "rbind")
m.Options = append(m.Options, fmt.Sprintf("uvmpath=/tmp/gcs/%s/binds", c.ID))
}
s.Mounts = append(s.Mounts, m)
}

Expand Down
17 changes: 10 additions & 7 deletions daemon/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func (m mounts) parts(i int) int {
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
binds := map[string]bool{}
mountPoints := map[string]*volume.MountPoint{}
parser := volume.NewParser(container.Platform)
defer func() {
// clean up the container mountpoints once return with error
if retErr != nil {
Expand Down Expand Up @@ -103,7 +104,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo

// 2. Read volumes from other containers.
for _, v := range hostConfig.VolumesFrom {
containerID, mode, err := volume.ParseVolumesFrom(v)
containerID, mode, err := parser.ParseVolumesFrom(v)
if err != nil {
return err
}
Expand All @@ -118,7 +119,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
Type: m.Type,
Name: m.Name,
Source: m.Source,
RW: m.RW && volume.ReadWrite(mode),
RW: m.RW && parser.ReadWrite(mode),
Driver: m.Driver,
Destination: m.Destination,
Propagation: m.Propagation,
Expand All @@ -140,7 +141,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo

// 3. Read bind mounts
for _, b := range hostConfig.Binds {
bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver)
bind, err := parser.ParseMountRaw(b, hostConfig.VolumeDriver)
if err != nil {
return err
}
Expand Down Expand Up @@ -172,7 +173,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
}

for _, cfg := range hostConfig.Mounts {
mp, err := volume.ParseMountSpec(cfg)
mp, err := parser.ParseMountSpec(cfg)
if err != nil {
return validationError{err}
}
Expand Down Expand Up @@ -217,7 +218,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo

// 4. Cleanup old volumes that are about to be reassigned.
for _, m := range mountPoints {
if m.BackwardsCompatible() {
if parser.IsBackwardCompatible(m) {
if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
daemon.volumes.Dereference(mp.Volume, container.ID)
}
Expand Down Expand Up @@ -252,6 +253,8 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {
container.Lock()
defer container.Unlock()

parser := volume.NewParser(container.Platform)

maybeUpdate := make(map[string]bool)
for _, mp := range container.MountPoints {
if mp.Spec.Source != "" && mp.Type != "" {
Expand All @@ -270,7 +273,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {

binds := make(map[string]*volume.MountPoint, len(container.HostConfig.Binds))
for _, rawSpec := range container.HostConfig.Binds {
mp, err := volume.ParseMountRaw(rawSpec, container.HostConfig.VolumeDriver)
mp, err := parser.ParseMountRaw(rawSpec, container.HostConfig.VolumeDriver)
if err != nil {
logrus.WithError(err).Error("Got unexpected error while re-parsing raw volume spec during spec backport")
continue
Expand All @@ -280,7 +283,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {

volumesFrom := make(map[string]volume.MountPoint)
for _, fromSpec := range container.HostConfig.VolumesFrom {
from, _, err := volume.ParseVolumesFrom(fromSpec)
from, _, err := parser.ParseVolumesFrom(fromSpec)
if err != nil {
logrus.WithError(err).WithField("id", container.ID).Error("Error reading volumes-from spec during mount spec backport")
continue
Expand Down
5 changes: 4 additions & 1 deletion daemon/volumes_unit_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package daemon

import (
"runtime"
"testing"

"github.com/docker/docker/volume"
Expand All @@ -20,8 +21,10 @@ func TestParseVolumesFrom(t *testing.T) {
{"foobar:baz", "", "", true},
}

parser := volume.NewParser(runtime.GOOS)

for _, c := range cases {
id, mode, err := volume.ParseVolumesFrom(c.spec)
id, mode, err := parser.ParseVolumesFrom(c.spec)
if c.fail {
if err == nil {
t.Fatalf("Expected error, was nil, for spec %s\n", c.spec)
Expand Down
91 changes: 91 additions & 0 deletions libcontainerd/client_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
Expand Down Expand Up @@ -388,11 +389,101 @@ func (clnt *client) createLinux(containerID string, checkpoint string, checkpoin
configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
}

// Add the mounts (volumes, bind mounts etc) to the structure. We have to do
// some translation for both the mapped directories passed into HCS and in
// the spec.
//
// For HCS, we only pass in the mounts from the spec which are type "bind".
// Further, the "ContainerPath" field (which is a little mis-leadingly
// named when it applies to the utility VM rather than the container in the
// utility VM) is moved to under /tmp/gcs/<ID>/binds, where this is passed
// by the caller through a 'uvmpath' option.
//
// We do similar translation for the mounts in the spec by stripping out
// the uvmpath option, and translating the Source path to the location in the
// utility VM calculated above.
//
// From inside the utility VM, you would see a 9p mount such as in the following
// where a host folder has been mapped to /target. The line with /tmp/gcs/<ID>/binds
// specifically:
//
// / # mount
// rootfs on / type rootfs (rw,size=463736k,nr_inodes=115934)
// proc on /proc type proc (rw,relatime)
// sysfs on /sys type sysfs (rw,relatime)
// udev on /dev type devtmpfs (rw,relatime,size=498100k,nr_inodes=124525,mode=755)
// tmpfs on /run type tmpfs (rw,relatime)
// cgroup on /sys/fs/cgroup type cgroup (rw,relatime,cpuset,cpu,cpuacct,blkio,memory,devices,freezer,net_cls,perf_event,net_prio,hugetlb,pids,rdma)
// mqueue on /dev/mqueue type mqueue (rw,relatime)
// devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
// /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target on /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target type 9p (rw,sync,dirsync,relatime,trans=fd,rfdno=6,wfdno=6)
// /dev/pmem0 on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0 type ext4 (ro,relatime,block_validity,delalloc,norecovery,barrier,dax,user_xattr,acl)
// /dev/sda on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch type ext4 (rw,relatime,block_validity,delalloc,barrier,user_xattr,acl)
// overlay on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/rootfs type overlay (rw,relatime,lowerdir=/tmp/base/:/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0,upperdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/upper,workdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/work)
//
// /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l
// total 16
// drwx------ 3 0 0 60 Sep 7 18:54 binds
// -rw-r--r-- 1 0 0 3345 Sep 7 18:54 config.json
// drwxr-xr-x 10 0 0 4096 Sep 6 17:26 layer0
// drwxr-xr-x 1 0 0 4096 Sep 7 18:54 rootfs
// drwxr-xr-x 5 0 0 4096 Sep 7 18:54 scratch
//
// /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l binds
// total 0
// drwxrwxrwt 2 0 0 4096 Sep 7 16:51 target

mds := []hcsshim.MappedDir{}
specMounts := []specs.Mount{}
for _, mount := range spec.Mounts {
specMount := mount
if mount.Type == "bind" {
// Strip out the uvmpath from the options
updatedOptions := []string{}
uvmPath := ""
readonly := false
for _, opt := range mount.Options {
dropOption := false
elements := strings.SplitN(opt, "=", 2)
switch elements[0] {
case "uvmpath":
uvmPath = elements[1]
dropOption = true
case "rw":
case "ro":
readonly = true
case "rbind":
default:
return fmt.Errorf("unsupported option %q", opt)
}
if !dropOption {
updatedOptions = append(updatedOptions, opt)
}
}
mount.Options = updatedOptions
if uvmPath == "" {
return fmt.Errorf("no uvmpath for bind mount %+v", mount)
}
md := hcsshim.MappedDir{
HostPath: mount.Source,
ContainerPath: path.Join(uvmPath, mount.Destination),
CreateInUtilityVM: true,
ReadOnly: readonly,
}
mds = append(mds, md)
specMount.Source = path.Join(uvmPath, mount.Destination)
}
specMounts = append(specMounts, specMount)
}
configuration.MappedDirectories = mds

hcsContainer, err := hcsshim.CreateContainer(containerID, configuration)
if err != nil {
return err
}

spec.Mounts = specMounts

// Construct a container object for calling start on it.
container := &container{
containerCommon: containerCommon{
Expand Down
Loading

0 comments on commit e89b6e8

Please sign in to comment.