From 3f0393a442e18cd9bb8dfa333bdd1ff824f67fc9 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 17 Apr 2018 16:50:28 -0400 Subject: [PATCH] Move mount parsing to separate package. This moves the platform specific stuff in a separate package and keeps the `volume` package and the defined interfaces light to import. Signed-off-by: Brian Goff Upstream-commit: 6a70fd222b95643a8a6b88e2634d5f085ae4122a Component: engine --- components/engine/container/container.go | 9 +- components/engine/container/container_unix.go | 7 +- components/engine/daemon/archive_unix.go | 4 +- components/engine/daemon/container.go | 4 +- components/engine/daemon/create_windows.go | 4 +- components/engine/daemon/daemon_unix.go | 4 +- components/engine/daemon/oci_linux.go | 4 +- components/engine/daemon/volumes.go | 18 +- components/engine/daemon/volumes_unit_test.go | 4 +- components/engine/daemon/volumes_unix.go | 6 +- components/engine/daemon/volumes_unix_test.go | 28 +-- components/engine/daemon/volumes_windows.go | 6 +- .../engine/volume/{ => mounts}/lcow_parser.go | 2 +- .../volume/{ => mounts}/linux_parser.go | 5 +- components/engine/volume/mounts/mounts.go | 170 ++++++++++++++++++ .../engine/volume/{ => mounts}/parser.go | 2 +- .../{volume_test.go => mounts/parser_test.go} | 2 +- .../engine/volume/{ => mounts}/validate.go | 2 +- .../volume/{ => mounts}/validate_test.go | 2 +- .../volume/{ => mounts}/validate_unix_test.go | 2 +- .../{ => mounts}/validate_windows_test.go | 2 +- .../engine/volume/{ => mounts}/volume_copy.go | 2 +- .../engine/volume/{ => mounts}/volume_unix.go | 2 +- .../volume/{ => mounts}/volume_windows.go | 2 +- .../volume/{ => mounts}/windows_parser.go | 2 +- components/engine/volume/store/store.go | 3 +- components/engine/volume/volume.go | 162 ----------------- 27 files changed, 237 insertions(+), 223 deletions(-) rename components/engine/volume/{ => mounts}/lcow_parser.go (93%) rename components/engine/volume/{ => mounts}/linux_parser.go (98%) create mode 100644 components/engine/volume/mounts/mounts.go rename components/engine/volume/{ => mounts}/parser.go (95%) rename components/engine/volume/{volume_test.go => mounts/parser_test.go} (99%) rename components/engine/volume/{ => mounts}/validate.go (90%) rename components/engine/volume/{ => mounts}/validate_test.go (97%) rename components/engine/volume/{ => mounts}/validate_unix_test.go (57%) rename components/engine/volume/{ => mounts}/validate_windows_test.go (52%) rename components/engine/volume/{ => mounts}/volume_copy.go (87%) rename components/engine/volume/{ => mounts}/volume_unix.go (86%) rename components/engine/volume/{ => mounts}/volume_windows.go (74%) rename components/engine/volume/{ => mounts}/windows_parser.go (99%) diff --git a/components/engine/container/container.go b/components/engine/container/container.go index a076e80746..4e1fa918dd 100644 --- a/components/engine/container/container.go +++ b/components/engine/container/container.go @@ -37,6 +37,7 @@ import ( "github.com/docker/docker/restartmanager" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-connections/nat" units "github.com/docker/go-units" "github.com/docker/libnetwork" @@ -94,7 +95,7 @@ type Container struct { RestartCount int HasBeenStartedBefore bool HasBeenManuallyStopped bool // used for unless-stopped restart policy - MountPoints map[string]*volume.MountPoint + MountPoints map[string]*volumemounts.MountPoint HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable ExecCommands *exec.Store `json:"-"` DependencyStore agentexec.DependencyGetter `json:"-"` @@ -128,7 +129,7 @@ func NewBaseContainer(id, root string) *Container { State: NewState(), ExecCommands: exec.NewStore(), Root: root, - MountPoints: make(map[string]*volume.MountPoint), + MountPoints: make(map[string]*volumemounts.MountPoint), StreamConfig: stream.NewConfig(), attachContext: &attachContext{}, } @@ -450,8 +451,8 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu if operatingSystem == "" { operatingSystem = runtime.GOOS } - volumeParser := volume.NewParser(operatingSystem) - container.MountPoints[destination] = &volume.MountPoint{ + volumeParser := volumemounts.NewParser(operatingSystem) + container.MountPoints[destination] = &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Name: vol.Name(), Driver: vol.DriverName(), diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index e5cecf3166..9397cdf60b 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -61,7 +62,7 @@ func (container *Container) BuildHostnameFile() error { func (container *Container) NetworkMounts() []Mount { var mounts []Mount shared := container.HostConfig.NetworkMode.IsContainer() - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) 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) @@ -198,7 +199,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.OS) + parser := volumemounts.NewParser(container.OS) if container.HasMountFor("/dev/shm") { return mounts @@ -402,7 +403,7 @@ func copyExistingContents(source, destination string) error { // TmpfsMounts returns the list of tmpfs mounts func (container *Container) TmpfsMounts() ([]Mount, error) { - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) var mounts []Mount for dest, data := range container.HostConfig.Tmpfs { mounts = append(mounts, Mount{ diff --git a/components/engine/daemon/archive_unix.go b/components/engine/daemon/archive_unix.go index ca33e7ac9a..50e6fe24be 100644 --- a/components/engine/daemon/archive_unix.go +++ b/components/engine/daemon/archive_unix.go @@ -4,7 +4,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "github.com/docker/docker/container" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it @@ -12,7 +12,7 @@ import ( // cannot be configured with a read-only rootfs. func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { var toVolume bool - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) for _, mnt := range container.MountPoints { if toVolume = parser.HasResource(mnt, absPath); toVolume { if mnt.RW { diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index b1ae3daa94..c8e2053970 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -20,7 +20,7 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-connections/nat" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -296,7 +296,7 @@ func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *conta } // Validate mounts; check if host directories still exist - parser := volume.NewParser(platform) + parser := volumemounts.NewParser(platform) for _, cfg := range hostConfig.Mounts { if err := parser.ValidateMountConfig(&cfg); err != nil { return nil, err diff --git a/components/engine/daemon/create_windows.go b/components/engine/daemon/create_windows.go index a4cf7df939..a2e7a94d4e 100644 --- a/components/engine/daemon/create_windows.go +++ b/components/engine/daemon/create_windows.go @@ -7,7 +7,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // createContainerOSSpecificSettings performs host-OS specific container create functionality @@ -26,7 +26,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con } hostConfig.Isolation = "hyperv" } - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) for spec := range config.Volumes { mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver) diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index 670d564b8a..82a38839a2 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -33,7 +33,7 @@ import ( "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/drivers/bridge" @@ -626,7 +626,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime) } - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) for dest := range hostConfig.Tmpfs { if err := parser.ValidateTmpfsMountDestination(dest); err != nil { return warnings, err diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index a3638ace21..f23f0b9990 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -18,7 +18,7 @@ import ( "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/devices" @@ -580,7 +580,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c if m.Source == "tmpfs" { data := m.Data - parser := volume.NewParser("linux") + parser := volumemounts.NewParser("linux") options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())} if data != "" { options = append(options, strings.Split(data, ",")...) diff --git a/components/engine/daemon/volumes.go b/components/engine/daemon/volumes.go index 948409a00a..034924c777 100644 --- a/components/engine/daemon/volumes.go +++ b/components/engine/daemon/volumes.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -74,8 +75,9 @@ func (m mounts) parts(i int) int { // 4. Cleanup old volumes that are about to be reassigned. 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.OS) + mountPoints := map[string]*volumemounts.MountPoint{} + parser := volumemounts.NewParser(container.OS) + defer func() { // clean up the container mountpoints once return with error if retErr != nil { @@ -115,7 +117,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } for _, m := range c.MountPoints { - cp := &volume.MountPoint{ + cp := &volumemounts.MountPoint{ Type: m.Type, Name: m.Name, Source: m.Source, @@ -250,7 +252,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo // lazyInitializeVolume initializes a mountpoint's volume if needed. // This happens after a daemon restart. -func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { +func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error { if len(m.Driver) > 0 && m.Volume == nil { v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) if err != nil { @@ -270,7 +272,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { container.Lock() defer container.Unlock() - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) maybeUpdate := make(map[string]bool) for _, mp := range container.MountPoints { @@ -288,7 +290,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { mountSpecs[m.Target] = true } - binds := make(map[string]*volume.MountPoint, len(container.HostConfig.Binds)) + binds := make(map[string]*volumemounts.MountPoint, len(container.HostConfig.Binds)) for _, rawSpec := range container.HostConfig.Binds { mp, err := parser.ParseMountRaw(rawSpec, container.HostConfig.VolumeDriver) if err != nil { @@ -298,7 +300,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { binds[mp.Destination] = mp } - volumesFrom := make(map[string]volume.MountPoint) + volumesFrom := make(map[string]volumemounts.MountPoint) for _, fromSpec := range container.HostConfig.VolumesFrom { from, _, err := parser.ParseVolumesFrom(fromSpec) if err != nil { @@ -321,7 +323,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { fromC.Unlock() } - needsUpdate := func(containerMount, other *volume.MountPoint) bool { + needsUpdate := func(containerMount, other *volumemounts.MountPoint) bool { if containerMount.Type != other.Type || !reflect.DeepEqual(containerMount.Spec, other.Spec) { return true } diff --git a/components/engine/daemon/volumes_unit_test.go b/components/engine/daemon/volumes_unit_test.go index aa51b4a822..6bdebe467c 100644 --- a/components/engine/daemon/volumes_unit_test.go +++ b/components/engine/daemon/volumes_unit_test.go @@ -4,7 +4,7 @@ import ( "runtime" "testing" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) func TestParseVolumesFrom(t *testing.T) { @@ -21,7 +21,7 @@ func TestParseVolumesFrom(t *testing.T) { {"foobar:baz", "", "", true}, } - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) for _, c := range cases { id, mode, err := parser.ParseVolumesFrom(c.spec) diff --git a/components/engine/daemon/volumes_unix.go b/components/engine/daemon/volumes_unix.go index acf57436a6..efffefa76b 100644 --- a/components/engine/daemon/volumes_unix.go +++ b/components/engine/daemon/volumes_unix.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // setupMounts iterates through each of the mount points for a container and @@ -40,7 +40,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // mount the socket the daemon is listening on. During daemon shutdown, the socket // (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to // create at directory instead. This in turn will prevent the daemon to restart. - checkfunc := func(m *volume.MountPoint) error { + checkfunc := func(m *volumemounts.MountPoint) error { if _, exist := daemon.hosts[m.Source]; exist && daemon.IsShuttingDown() { return fmt.Errorf("Could not mount %q to container while the daemon is shutting down", m.Source) } @@ -102,7 +102,7 @@ func sortMounts(m []container.Mount) []container.Mount { // setBindModeIfNull is platform specific processing to ensure the // shared mode is set to 'z' if it is null. This is called in the case // of processing a named volume and not a typical bind. -func setBindModeIfNull(bind *volume.MountPoint) { +func setBindModeIfNull(bind *volumemounts.MountPoint) { if bind.Mode == "" { bind.Mode = "z" } diff --git a/components/engine/daemon/volumes_unix_test.go b/components/engine/daemon/volumes_unix_test.go index f80ea29fb3..36e19110d1 100644 --- a/components/engine/daemon/volumes_unix_test.go +++ b/components/engine/daemon/volumes_unix_test.go @@ -11,7 +11,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) func TestBackportMountSpec(t *testing.T) { @@ -19,7 +19,7 @@ func TestBackportMountSpec(t *testing.T) { c := &container.Container{ State: &container.State{}, - MountPoints: map[string]*volume.MountPoint{ + MountPoints: map[string]*volumemounts.MountPoint{ "/apple": {Destination: "/apple", Source: "/var/lib/docker/volumes/12345678", Name: "12345678", RW: true, CopyData: true}, // anonymous volume "/banana": {Destination: "/banana", Source: "/var/lib/docker/volumes/data", Name: "data", RW: true, CopyData: true}, // named volume "/cherry": {Destination: "/cherry", Source: "/var/lib/docker/volumes/data", Name: "data", CopyData: true}, // RO named volume @@ -73,7 +73,7 @@ func TestBackportMountSpec(t *testing.T) { d.containers.Add("1", &container.Container{ State: &container.State{}, ID: "1", - MountPoints: map[string]*volume.MountPoint{ + MountPoints: map[string]*volumemounts.MountPoint{ "/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true}, }, HostConfig: &containertypes.HostConfig{ @@ -84,11 +84,11 @@ func TestBackportMountSpec(t *testing.T) { }) type expected struct { - mp *volume.MountPoint + mp *volumemounts.MountPoint comment string } - pretty := func(mp *volume.MountPoint) string { + pretty := func(mp *volumemounts.MountPoint) string { b, err := json.MarshalIndent(mp, "\t", " ") if err != nil { return fmt.Sprintf("%#v", mp) @@ -98,7 +98,7 @@ func TestBackportMountSpec(t *testing.T) { for _, x := range []expected{ { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/apple", RW: true, @@ -114,7 +114,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "anonymous volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/banana", RW: true, @@ -130,7 +130,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "named volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/cherry", Name: "data", @@ -146,7 +146,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "read-only named volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/dates", Name: "data", @@ -162,7 +162,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "named volume with nocopy", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/elderberry", Name: "data", @@ -178,7 +178,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "masks an anonymous volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeBind, Destination: "/fig", Source: "/data", @@ -192,7 +192,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "bind mount with read/write", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeBind, Destination: "/guava", Source: "/data", @@ -209,7 +209,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "bind mount with read/write + shared propagation", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/honeydew", Source: "/var/lib/docker/volumes/data", @@ -229,7 +229,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "volume defined in mounts API", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/kumquat", Source: "/var/lib/docker/volumes/data", diff --git a/components/engine/daemon/volumes_windows.go b/components/engine/daemon/volumes_windows.go index 458f2851da..3d63d02e1c 100644 --- a/components/engine/daemon/volumes_windows.go +++ b/components/engine/daemon/volumes_windows.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // setupMounts configures the mount points for a container by appending each @@ -20,7 +20,7 @@ import ( func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) { var mnts []container.Mount - for _, mount := range c.MountPoints { // type is volume.MountPoint + for _, mount := range c.MountPoints { // type is volumemounts.MountPoint if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil { return nil, err } @@ -42,7 +42,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // setBindModeIfNull is platform specific processing which is a no-op on // Windows. -func setBindModeIfNull(bind *volume.MountPoint) { +func setBindModeIfNull(bind *volumemounts.MountPoint) { return } diff --git a/components/engine/volume/lcow_parser.go b/components/engine/volume/mounts/lcow_parser.go similarity index 93% rename from components/engine/volume/lcow_parser.go rename to components/engine/volume/mounts/lcow_parser.go index dba0eb66cd..bafb7b07f8 100644 --- a/components/engine/volume/lcow_parser.go +++ b/components/engine/volume/mounts/lcow_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/linux_parser.go b/components/engine/volume/mounts/linux_parser.go similarity index 98% rename from components/engine/volume/linux_parser.go rename to components/engine/volume/mounts/linux_parser.go index 6eb796b678..8e436aec0e 100644 --- a/components/engine/volume/linux_parser.go +++ b/components/engine/volume/mounts/linux_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" ) type linuxParser struct { @@ -405,7 +406,7 @@ func (p *linuxParser) ValidateVolumeName(name string) error { } func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool { - return len(m.Source) > 0 || m.Driver == DefaultDriverName + return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName } func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error { diff --git a/components/engine/volume/mounts/mounts.go b/components/engine/volume/mounts/mounts.go new file mode 100644 index 0000000000..8f255a5482 --- /dev/null +++ b/components/engine/volume/mounts/mounts.go @@ -0,0 +1,170 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + mounttypes "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" +) + +// MountPoint is the intersection point between a volume and a container. It +// specifies which volume is to be used and where inside a container it should +// be mounted. +// +// Note that this type is embedded in `container.Container` object and persisted to disk. +// Changes to this struct need to by synced with on disk state. +type MountPoint struct { + // Source is the source path of the mount. + // E.g. `mount --bind /foo /bar`, `/foo` is the `Source`. + Source string + // Destination is the path relative to the container root (`/`) to the mount point + // It is where the `Source` is mounted to + Destination string + // RW is set to true when the mountpoint should be mounted as read-write + RW bool + // Name is the name reference to the underlying data defined by `Source` + // e.g., the volume name + Name string + // Driver is the volume driver used to create the volume (if it is a volume) + Driver string + // Type of mount to use, see `Type` definitions in github.com/docker/docker/api/types/mount + Type mounttypes.Type `json:",omitempty"` + // Volume is the volume providing data to this mountpoint. + // This is nil unless `Type` is set to `TypeVolume` + Volume volume.Volume `json:"-"` + + // Mode is the comma separated list of options supplied by the user when creating + // the bind/volume mount. + // Note Mode is not used on Windows + Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`" + + // Propagation describes how the mounts are propagated from the host into the + // mount point, and vice-versa. + // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt + // Note Propagation is not used on Windows + Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string + + // Specifies if data should be copied from the container before the first mount + // Use a pointer here so we can tell if the user set this value explicitly + // This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated + CopyData bool `json:"-"` + // ID is the opaque ID used to pass to the volume driver. + // This should be set by calls to `Mount` and unset by calls to `Unmount` + ID string `json:",omitempty"` + + // Sepc is a copy of the API request that created this mount. + Spec mounttypes.Mount + + // Track usage of this mountpoint + // Specifically needed for containers which are running and calls to `docker cp` + // because both these actions require mounting the volumes. + active int +} + +// Cleanup frees resources used by the mountpoint +func (m *MountPoint) Cleanup() error { + if m.Volume == nil || m.ID == "" { + return nil + } + + if err := m.Volume.Unmount(m.ID); err != nil { + return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name()) + } + + m.active-- + if m.active == 0 { + m.ID = "" + } + return nil +} + +// Setup sets up a mount point by either mounting the volume if it is +// configured, or creating the source directory if supplied. +// The, optional, checkFun parameter allows doing additional checking +// before creating the source directory on the host. +func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) { + defer func() { + if err != nil || !label.RelabelNeeded(m.Mode) { + return + } + + var sourcePath string + sourcePath, err = filepath.EvalSymlinks(m.Source) + if err != nil { + path = "" + err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source) + return + } + err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode)) + if err == syscall.ENOTSUP { + err = nil + } + if err != nil { + path = "" + err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath) + } + }() + + if m.Volume != nil { + id := m.ID + if id == "" { + id = stringid.GenerateNonCryptoID() + } + path, err := m.Volume.Mount(id) + if err != nil { + return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source) + } + + m.ID = id + m.active++ + return path, nil + } + + if len(m.Source) == 0 { + return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") + } + + if m.Type == mounttypes.TypeBind { + // Before creating the source directory on the host, invoke checkFun if it's not nil. One of + // the use case is to forbid creating the daemon socket as a directory if the daemon is in + // the process of shutting down. + if checkFun != nil { + if err := checkFun(m); err != nil { + return "", err + } + } + // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) + // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it + if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { + if perr, ok := err.(*os.PathError); ok { + if perr.Err != syscall.ENOTDIR { + return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source) + } + } + } + } + return m.Source, nil +} + +// Path returns the path of a volume in a mount point. +func (m *MountPoint) Path() string { + if m.Volume != nil { + return m.Volume.Path() + } + return m.Source +} + +func errInvalidMode(mode string) error { + return errors.Errorf("invalid mode: %v", mode) +} + +func errInvalidSpec(spec string) error { + return errors.Errorf("invalid volume specification: '%s'", spec) +} diff --git a/components/engine/volume/parser.go b/components/engine/volume/mounts/parser.go similarity index 95% rename from components/engine/volume/parser.go rename to components/engine/volume/mounts/parser.go index 9a10267819..73681750ea 100644 --- a/components/engine/volume/parser.go +++ b/components/engine/volume/mounts/parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/volume_test.go b/components/engine/volume/mounts/parser_test.go similarity index 99% rename from components/engine/volume/volume_test.go rename to components/engine/volume/mounts/parser_test.go index f5bc1b0f54..347f7d9c4d 100644 --- a/components/engine/volume/volume_test.go +++ b/components/engine/volume/mounts/parser_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "io/ioutil" diff --git a/components/engine/volume/validate.go b/components/engine/volume/mounts/validate.go similarity index 90% rename from components/engine/volume/validate.go rename to components/engine/volume/mounts/validate.go index 6512fb11ba..0b71526901 100644 --- a/components/engine/volume/validate.go +++ b/components/engine/volume/mounts/validate.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "fmt" diff --git a/components/engine/volume/validate_test.go b/components/engine/volume/mounts/validate_test.go similarity index 97% rename from components/engine/volume/validate_test.go rename to components/engine/volume/mounts/validate_test.go index d230ef3193..4f83856043 100644 --- a/components/engine/volume/validate_test.go +++ b/components/engine/volume/mounts/validate_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/validate_unix_test.go b/components/engine/volume/mounts/validate_unix_test.go similarity index 57% rename from components/engine/volume/validate_unix_test.go rename to components/engine/volume/mounts/validate_unix_test.go index 5375db380e..a319371451 100644 --- a/components/engine/volume/validate_unix_test.go +++ b/components/engine/volume/mounts/validate_unix_test.go @@ -1,6 +1,6 @@ // +build !windows -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" var ( testDestinationPath = "/foo" diff --git a/components/engine/volume/validate_windows_test.go b/components/engine/volume/mounts/validate_windows_test.go similarity index 52% rename from components/engine/volume/validate_windows_test.go rename to components/engine/volume/mounts/validate_windows_test.go index 9053a01a8b..74b40a6c30 100644 --- a/components/engine/volume/validate_windows_test.go +++ b/components/engine/volume/mounts/validate_windows_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" var ( testDestinationPath = `c:\foo` diff --git a/components/engine/volume/volume_copy.go b/components/engine/volume/mounts/volume_copy.go similarity index 87% rename from components/engine/volume/volume_copy.go rename to components/engine/volume/mounts/volume_copy.go index c0dc1cf2c2..04056fa50a 100644 --- a/components/engine/volume/volume_copy.go +++ b/components/engine/volume/mounts/volume_copy.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import "strings" diff --git a/components/engine/volume/volume_unix.go b/components/engine/volume/mounts/volume_unix.go similarity index 86% rename from components/engine/volume/volume_unix.go rename to components/engine/volume/mounts/volume_unix.go index 228c6d73a3..c6d51e0710 100644 --- a/components/engine/volume/volume_unix.go +++ b/components/engine/volume/mounts/volume_unix.go @@ -1,6 +1,6 @@ // +build linux freebsd darwin -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "fmt" diff --git a/components/engine/volume/volume_windows.go b/components/engine/volume/mounts/volume_windows.go similarity index 74% rename from components/engine/volume/volume_windows.go rename to components/engine/volume/mounts/volume_windows.go index 1179dd4f4f..773e7db88a 100644 --- a/components/engine/volume/volume_windows.go +++ b/components/engine/volume/mounts/volume_windows.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool { return false diff --git a/components/engine/volume/windows_parser.go b/components/engine/volume/mounts/windows_parser.go similarity index 99% rename from components/engine/volume/windows_parser.go rename to components/engine/volume/mounts/windows_parser.go index 84b6717c95..ac61044043 100644 --- a/components/engine/volume/windows_parser.go +++ b/components/engine/volume/mounts/windows_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index 4f4cffafd7..67b4e7d7ef 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/pkg/locker" "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/sirupsen/logrus" ) @@ -387,7 +388,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st // volume name validation is specific to the host os and not on container image // windows/lcow should have an equivalent volumename validation logic so we create a parser for current host OS - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) err := parser.ValidateVolumeName(name) if err != nil { return nil, err diff --git a/components/engine/volume/volume.go b/components/engine/volume/volume.go index 0b1d4e8657..61c8243979 100644 --- a/components/engine/volume/volume.go +++ b/components/engine/volume/volume.go @@ -1,17 +1,7 @@ package volume // import "github.com/docker/docker/volume" import ( - "fmt" - "os" - "path/filepath" - "syscall" "time" - - mounttypes "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/stringid" - "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" ) // DefaultDriverName is the driver name used for the driver @@ -77,155 +67,3 @@ type DetailedVolume interface { Scope() string Volume } - -// MountPoint is the intersection point between a volume and a container. It -// specifies which volume is to be used and where inside a container it should -// be mounted. -type MountPoint struct { - // Source is the source path of the mount. - // E.g. `mount --bind /foo /bar`, `/foo` is the `Source`. - Source string - // Destination is the path relative to the container root (`/`) to the mount point - // It is where the `Source` is mounted to - Destination string - // RW is set to true when the mountpoint should be mounted as read-write - RW bool - // Name is the name reference to the underlying data defined by `Source` - // e.g., the volume name - Name string - // Driver is the volume driver used to create the volume (if it is a volume) - Driver string - // Type of mount to use, see `Type` definitions in github.com/docker/docker/api/types/mount - Type mounttypes.Type `json:",omitempty"` - // Volume is the volume providing data to this mountpoint. - // This is nil unless `Type` is set to `TypeVolume` - Volume Volume `json:"-"` - - // Mode is the comma separated list of options supplied by the user when creating - // the bind/volume mount. - // Note Mode is not used on Windows - Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`" - - // Propagation describes how the mounts are propagated from the host into the - // mount point, and vice-versa. - // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt - // Note Propagation is not used on Windows - Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string - - // Specifies if data should be copied from the container before the first mount - // Use a pointer here so we can tell if the user set this value explicitly - // This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated - CopyData bool `json:"-"` - // ID is the opaque ID used to pass to the volume driver. - // This should be set by calls to `Mount` and unset by calls to `Unmount` - ID string `json:",omitempty"` - - // Sepc is a copy of the API request that created this mount. - Spec mounttypes.Mount - - // Track usage of this mountpoint - // Specifically needed for containers which are running and calls to `docker cp` - // because both these actions require mounting the volumes. - active int -} - -// Cleanup frees resources used by the mountpoint -func (m *MountPoint) Cleanup() error { - if m.Volume == nil || m.ID == "" { - return nil - } - - if err := m.Volume.Unmount(m.ID); err != nil { - return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name()) - } - - m.active-- - if m.active == 0 { - m.ID = "" - } - return nil -} - -// Setup sets up a mount point by either mounting the volume if it is -// configured, or creating the source directory if supplied. -// The, optional, checkFun parameter allows doing additional checking -// before creating the source directory on the host. -func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) { - defer func() { - if err != nil || !label.RelabelNeeded(m.Mode) { - return - } - - var sourcePath string - sourcePath, err = filepath.EvalSymlinks(m.Source) - if err != nil { - path = "" - err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source) - return - } - err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode)) - if err == syscall.ENOTSUP { - err = nil - } - if err != nil { - path = "" - err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath) - } - }() - - if m.Volume != nil { - id := m.ID - if id == "" { - id = stringid.GenerateNonCryptoID() - } - path, err := m.Volume.Mount(id) - if err != nil { - return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source) - } - - m.ID = id - m.active++ - return path, nil - } - - if len(m.Source) == 0 { - return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") - } - - if m.Type == mounttypes.TypeBind { - // Before creating the source directory on the host, invoke checkFun if it's not nil. One of - // the use case is to forbid creating the daemon socket as a directory if the daemon is in - // the process of shutting down. - if checkFun != nil { - if err := checkFun(m); err != nil { - return "", err - } - } - // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) - // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it - if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { - if perr, ok := err.(*os.PathError); ok { - if perr.Err != syscall.ENOTDIR { - return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source) - } - } - } - } - return m.Source, nil -} - -// Path returns the path of a volume in a mount point. -func (m *MountPoint) Path() string { - if m.Volume != nil { - return m.Volume.Path() - } - return m.Source -} - -func errInvalidMode(mode string) error { - return errors.Errorf("invalid mode: %v", mode) -} - -func errInvalidSpec(spec string) error { - return errors.Errorf("invalid volume specification: '%s'", spec) -}