Skip to content

Commit e89b6e8

Browse files
simonferquelvieux
authored andcommitted
Volume refactoring for LCOW
Signed-off-by: Simon Ferquel <[email protected]>
1 parent d60c186 commit e89b6e8

33 files changed

Lines changed: 1465 additions & 1289 deletions

container/container.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,19 @@ func (container *Container) ShouldRestart() bool {
435435

436436
// AddMountPointWithVolume adds a new mount point configured with a volume to the container.
437437
func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
438+
operatingSystem := container.Platform
439+
if operatingSystem == "" {
440+
operatingSystem = runtime.GOOS
441+
}
442+
volumeParser := volume.NewParser(operatingSystem)
438443
container.MountPoints[destination] = &volume.MountPoint{
439444
Type: mounttypes.TypeVolume,
440445
Name: vol.Name(),
441446
Driver: vol.DriverName(),
442447
Destination: destination,
443448
RW: rw,
444449
Volume: vol,
445-
CopyData: volume.DefaultCopyMode,
450+
CopyData: volumeParser.DefaultCopyMode(),
446451
}
447452
}
448453

container/container_unix.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func (container *Container) BuildHostnameFile() error {
6868
func (container *Container) NetworkMounts() []Mount {
6969
var mounts []Mount
7070
shared := container.HostConfig.NetworkMode.IsContainer()
71+
parser := volume.NewParser(container.Platform)
7172
if container.ResolvConfPath != "" {
7273
if _, err := os.Stat(container.ResolvConfPath); err != nil {
7374
logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err)
@@ -83,7 +84,7 @@ func (container *Container) NetworkMounts() []Mount {
8384
Source: container.ResolvConfPath,
8485
Destination: "/etc/resolv.conf",
8586
Writable: writable,
86-
Propagation: string(volume.DefaultPropagationMode),
87+
Propagation: string(parser.DefaultPropagationMode()),
8788
})
8889
}
8990
}
@@ -102,7 +103,7 @@ func (container *Container) NetworkMounts() []Mount {
102103
Source: container.HostnamePath,
103104
Destination: "/etc/hostname",
104105
Writable: writable,
105-
Propagation: string(volume.DefaultPropagationMode),
106+
Propagation: string(parser.DefaultPropagationMode()),
106107
})
107108
}
108109
}
@@ -121,7 +122,7 @@ func (container *Container) NetworkMounts() []Mount {
121122
Source: container.HostsPath,
122123
Destination: "/etc/hosts",
123124
Writable: writable,
124-
Propagation: string(volume.DefaultPropagationMode),
125+
Propagation: string(parser.DefaultPropagationMode()),
125126
})
126127
}
127128
}
@@ -196,6 +197,7 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro
196197
// IpcMounts returns the list of IPC mounts
197198
func (container *Container) IpcMounts() []Mount {
198199
var mounts []Mount
200+
parser := volume.NewParser(container.Platform)
199201

200202
if container.HasMountFor("/dev/shm") {
201203
return mounts
@@ -209,7 +211,7 @@ func (container *Container) IpcMounts() []Mount {
209211
Source: container.ShmPath,
210212
Destination: "/dev/shm",
211213
Writable: true,
212-
Propagation: string(volume.DefaultPropagationMode),
214+
Propagation: string(parser.DefaultPropagationMode()),
213215
})
214216

215217
return mounts
@@ -429,6 +431,7 @@ func copyOwnership(source, destination string) error {
429431

430432
// TmpfsMounts returns the list of tmpfs mounts
431433
func (container *Container) TmpfsMounts() ([]Mount, error) {
434+
parser := volume.NewParser(container.Platform)
432435
var mounts []Mount
433436
for dest, data := range container.HostConfig.Tmpfs {
434437
mounts = append(mounts, Mount{
@@ -439,7 +442,7 @@ func (container *Container) TmpfsMounts() ([]Mount, error) {
439442
}
440443
for dest, mnt := range container.MountPoints {
441444
if mnt.Type == mounttypes.TypeTmpfs {
442-
data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly)
445+
data, err := parser.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly)
443446
if err != nil {
444447
return nil, err
445448
}

daemon/archive_unix.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ package daemon
44

55
import (
66
"github.com/docker/docker/container"
7+
"github.com/docker/docker/volume"
78
)
89

910
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
1011
// cannot be in a read-only volume. If it is not in a volume, the container
1112
// cannot be configured with a read-only rootfs.
1213
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
1314
var toVolume bool
15+
parser := volume.NewParser(container.Platform)
1416
for _, mnt := range container.MountPoints {
15-
if toVolume = mnt.HasResource(absPath); toVolume {
17+
if toVolume = parser.HasResource(mnt, absPath); toVolume {
1618
if mnt.RW {
1719
break
1820
}

daemon/create_windows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
2626
}
2727
hostConfig.Isolation = "hyperv"
2828
}
29-
29+
parser := volume.NewParser(container.Platform)
3030
for spec := range config.Volumes {
3131

32-
mp, err := volume.ParseMountRaw(spec, hostConfig.VolumeDriver)
32+
mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)
3333
if err != nil {
3434
return fmt.Errorf("Unrecognised volume spec: %v", err)
3535
}

daemon/daemon_unix.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
612612
return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime)
613613
}
614614

615+
parser := volume.NewParser(runtime.GOOS)
615616
for dest := range hostConfig.Tmpfs {
616-
if err := volume.ValidateTmpfsMountDestination(dest); err != nil {
617+
if err := parser.ValidateTmpfsMountDestination(dest); err != nil {
617618
return warnings, err
618619
}
619620
}

daemon/oci_linux.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
498498

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

525526
if m.Source == "tmpfs" {
526527
data := m.Data
527-
options := []string{"noexec", "nosuid", "nodev", string(volume.DefaultPropagationMode)}
528+
parser := volume.NewParser("linux")
529+
options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())}
528530
if data != "" {
529531
options = append(options, strings.Split(data, ",")...)
530532
}

daemon/oci_windows.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"io/ioutil"
66
"path/filepath"
7+
"runtime"
78
"strings"
89

910
containertypes "github.com/docker/docker/api/types/container"
@@ -108,6 +109,11 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
108109
if !mount.Writable {
109110
m.Options = append(m.Options, "ro")
110111
}
112+
if img.OS != runtime.GOOS {
113+
m.Type = "bind"
114+
m.Options = append(m.Options, "rbind")
115+
m.Options = append(m.Options, fmt.Sprintf("uvmpath=/tmp/gcs/%s/binds", c.ID))
116+
}
111117
s.Mounts = append(s.Mounts, m)
112118
}
113119

daemon/volumes.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func (m mounts) parts(i int) int {
7575
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
7676
binds := map[string]bool{}
7777
mountPoints := map[string]*volume.MountPoint{}
78+
parser := volume.NewParser(container.Platform)
7879
defer func() {
7980
// clean up the container mountpoints once return with error
8081
if retErr != nil {
@@ -103,7 +104,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
103104

104105
// 2. Read volumes from other containers.
105106
for _, v := range hostConfig.VolumesFrom {
106-
containerID, mode, err := volume.ParseVolumesFrom(v)
107+
containerID, mode, err := parser.ParseVolumesFrom(v)
107108
if err != nil {
108109
return err
109110
}
@@ -118,7 +119,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
118119
Type: m.Type,
119120
Name: m.Name,
120121
Source: m.Source,
121-
RW: m.RW && volume.ReadWrite(mode),
122+
RW: m.RW && parser.ReadWrite(mode),
122123
Driver: m.Driver,
123124
Destination: m.Destination,
124125
Propagation: m.Propagation,
@@ -140,7 +141,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
140141

141142
// 3. Read bind mounts
142143
for _, b := range hostConfig.Binds {
143-
bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver)
144+
bind, err := parser.ParseMountRaw(b, hostConfig.VolumeDriver)
144145
if err != nil {
145146
return err
146147
}
@@ -172,7 +173,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
172173
}
173174

174175
for _, cfg := range hostConfig.Mounts {
175-
mp, err := volume.ParseMountSpec(cfg)
176+
mp, err := parser.ParseMountSpec(cfg)
176177
if err != nil {
177178
return validationError{err}
178179
}
@@ -217,7 +218,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
217218

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

256+
parser := volume.NewParser(container.Platform)
257+
255258
maybeUpdate := make(map[string]bool)
256259
for _, mp := range container.MountPoints {
257260
if mp.Spec.Source != "" && mp.Type != "" {
@@ -270,7 +273,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {
270273

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

281284
volumesFrom := make(map[string]volume.MountPoint)
282285
for _, fromSpec := range container.HostConfig.VolumesFrom {
283-
from, _, err := volume.ParseVolumesFrom(fromSpec)
286+
from, _, err := parser.ParseVolumesFrom(fromSpec)
284287
if err != nil {
285288
logrus.WithError(err).WithField("id", container.ID).Error("Error reading volumes-from spec during mount spec backport")
286289
continue

daemon/volumes_unit_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package daemon
22

33
import (
4+
"runtime"
45
"testing"
56

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

24+
parser := volume.NewParser(runtime.GOOS)
25+
2326
for _, c := range cases {
24-
id, mode, err := volume.ParseVolumesFrom(c.spec)
27+
id, mode, err := parser.ParseVolumesFrom(c.spec)
2528
if c.fail {
2629
if err == nil {
2730
t.Fatalf("Expected error, was nil, for spec %s\n", c.spec)

libcontainerd/client_windows.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"io/ioutil"
99
"os"
10+
"path"
1011
"path/filepath"
1112
"regexp"
1213
"strings"
@@ -388,11 +389,101 @@ func (clnt *client) createLinux(containerID string, checkpoint string, checkpoin
388389
configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
389390
}
390391

392+
// Add the mounts (volumes, bind mounts etc) to the structure. We have to do
393+
// some translation for both the mapped directories passed into HCS and in
394+
// the spec.
395+
//
396+
// For HCS, we only pass in the mounts from the spec which are type "bind".
397+
// Further, the "ContainerPath" field (which is a little mis-leadingly
398+
// named when it applies to the utility VM rather than the container in the
399+
// utility VM) is moved to under /tmp/gcs/<ID>/binds, where this is passed
400+
// by the caller through a 'uvmpath' option.
401+
//
402+
// We do similar translation for the mounts in the spec by stripping out
403+
// the uvmpath option, and translating the Source path to the location in the
404+
// utility VM calculated above.
405+
//
406+
// From inside the utility VM, you would see a 9p mount such as in the following
407+
// where a host folder has been mapped to /target. The line with /tmp/gcs/<ID>/binds
408+
// specifically:
409+
//
410+
// / # mount
411+
// rootfs on / type rootfs (rw,size=463736k,nr_inodes=115934)
412+
// proc on /proc type proc (rw,relatime)
413+
// sysfs on /sys type sysfs (rw,relatime)
414+
// udev on /dev type devtmpfs (rw,relatime,size=498100k,nr_inodes=124525,mode=755)
415+
// tmpfs on /run type tmpfs (rw,relatime)
416+
// 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)
417+
// mqueue on /dev/mqueue type mqueue (rw,relatime)
418+
// devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
419+
// /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target on /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target type 9p (rw,sync,dirsync,relatime,trans=fd,rfdno=6,wfdno=6)
420+
// /dev/pmem0 on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0 type ext4 (ro,relatime,block_validity,delalloc,norecovery,barrier,dax,user_xattr,acl)
421+
// /dev/sda on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch type ext4 (rw,relatime,block_validity,delalloc,barrier,user_xattr,acl)
422+
// 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)
423+
//
424+
// /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l
425+
// total 16
426+
// drwx------ 3 0 0 60 Sep 7 18:54 binds
427+
// -rw-r--r-- 1 0 0 3345 Sep 7 18:54 config.json
428+
// drwxr-xr-x 10 0 0 4096 Sep 6 17:26 layer0
429+
// drwxr-xr-x 1 0 0 4096 Sep 7 18:54 rootfs
430+
// drwxr-xr-x 5 0 0 4096 Sep 7 18:54 scratch
431+
//
432+
// /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l binds
433+
// total 0
434+
// drwxrwxrwt 2 0 0 4096 Sep 7 16:51 target
435+
436+
mds := []hcsshim.MappedDir{}
437+
specMounts := []specs.Mount{}
438+
for _, mount := range spec.Mounts {
439+
specMount := mount
440+
if mount.Type == "bind" {
441+
// Strip out the uvmpath from the options
442+
updatedOptions := []string{}
443+
uvmPath := ""
444+
readonly := false
445+
for _, opt := range mount.Options {
446+
dropOption := false
447+
elements := strings.SplitN(opt, "=", 2)
448+
switch elements[0] {
449+
case "uvmpath":
450+
uvmPath = elements[1]
451+
dropOption = true
452+
case "rw":
453+
case "ro":
454+
readonly = true
455+
case "rbind":
456+
default:
457+
return fmt.Errorf("unsupported option %q", opt)
458+
}
459+
if !dropOption {
460+
updatedOptions = append(updatedOptions, opt)
461+
}
462+
}
463+
mount.Options = updatedOptions
464+
if uvmPath == "" {
465+
return fmt.Errorf("no uvmpath for bind mount %+v", mount)
466+
}
467+
md := hcsshim.MappedDir{
468+
HostPath: mount.Source,
469+
ContainerPath: path.Join(uvmPath, mount.Destination),
470+
CreateInUtilityVM: true,
471+
ReadOnly: readonly,
472+
}
473+
mds = append(mds, md)
474+
specMount.Source = path.Join(uvmPath, mount.Destination)
475+
}
476+
specMounts = append(specMounts, specMount)
477+
}
478+
configuration.MappedDirectories = mds
479+
391480
hcsContainer, err := hcsshim.CreateContainer(containerID, configuration)
392481
if err != nil {
393482
return err
394483
}
395484

485+
spec.Mounts = specMounts
486+
396487
// Construct a container object for calling start on it.
397488
container := &container{
398489
containerCommon: containerCommon{

0 commit comments

Comments
 (0)