зеркало из https://github.com/microsoft/docker.git
Windows:Fix VOLUME statement
Signed-off-by: John Howard <jhoward@microsoft.com> This fixes an issue which was reported internally. I think the cause of it is more my mis-understanding of the VOLUME statement in a dockerfile from some 3+ years ago. While it mostly worked, there was a bug (obviously). The issue was something such as the following was causing an error starting a Windows container using this image: ``` FROM microsoft/windowsservercore RUN mkdir c:\source VOLUME c:/source ``` The error was `"docker: Error response from daemon: Unrecognised volume spec: file 'c:/source' cannot be mapped. Only directories can be mapped on this platform."` It was root caused that on the Windows path, it was erroneously attempting to parse the spec as a volume spec, not as a destination. In that scenario, the parse looked at the spec as it if were local (host) rather than a container path. Now in the case (such as above) that c:/source (c:\\source) exists, and is a file rather than a directory, the above error would be seen. The fix is to treat (as Linux does) the spec as a destination. And manual verification: First WCOW: ``` PS E:\docker\build\volume> docker build -t wcowvolume . Sending build context to Docker daemon 3.584kB Step 1/4 : FROM microsoft/windowsservercore ---> e523d49b4b94 Step 2/4 : RUN mkdir c:\source ---> Using cache ---> ba84419e9d41 Step 3/4 : RUN copy c:\windows\system32\ntdll.dll c:\source ---> Using cache ---> 409ed22dd31a Step 4/4 : VOLUME c:/source ---> Using cache ---> 8c56416c697f Successfully built 8c56416c697f Successfully tagged wcowvolume:latest PS E:\docker\build\volume> dir c:\source Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 11/20/2018 8:45 AM 125 source PS E:\docker\build\volume> docker run volume Microsoft Windows [Version 10.0.17763.1] (c) 2018 Microsoft Corporation. All rights reserved. C:\> PS E:\docker\build\volume> ``` And an LCOW equivalent: ``` PS E:\docker\build\volume\Linux> type Dockerfile FROM alpine RUN mkdir /source RUN cp /bin/ls /source VOLUME /source PS E:\docker\build\volume\Linux> docker build -t lcowvolume . Sending build context to Docker daemon 2.048kB Step 1/4 : FROM alpine ---> 196d12cf6ab1 Step 2/4 : RUN mkdir /source ---> Using cache ---> 8f2f87ba69b7 Step 3/4 : RUN cp /bin/ls /source ---> Using cache ---> 9d2cf70ecb0e Step 4/4 : VOLUME /source ---> Using cache ---> a0ee39598ee2 Successfully built a0ee39598ee2 Successfully tagged lcowvolume:latest PS E:\docker\build\volume\Linux> docker run lcowvolume PS E:\docker\build\volume\Linux> ```
This commit is contained in:
Родитель
68cbc3712c
Коммит
41766e95c0
|
@ -3,18 +3,18 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
volumemounts "github.com/docker/docker/volume/mounts"
|
|
||||||
volumeopts "github.com/docker/docker/volume/service/opts"
|
volumeopts "github.com/docker/docker/volume/service/opts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createContainerOSSpecificSettings performs host-OS specific container create functionality
|
// createContainerOSSpecificSettings performs host-OS specific container create functionality
|
||||||
func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
|
func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
|
||||||
|
|
||||||
if container.OS == runtime.GOOS {
|
if container.OS == runtime.GOOS {
|
||||||
// Make sure the host config has the default daemon isolation if not specified by caller.
|
// Make sure the host config has the default daemon isolation if not specified by caller.
|
||||||
if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) {
|
if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) {
|
||||||
|
@ -28,66 +28,76 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
|
||||||
}
|
}
|
||||||
hostConfig.Isolation = "hyperv"
|
hostConfig.Isolation = "hyperv"
|
||||||
}
|
}
|
||||||
parser := volumemounts.NewParser(container.OS)
|
|
||||||
for spec := range config.Volumes {
|
for spec := range config.Volumes {
|
||||||
|
var destination string
|
||||||
mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)
|
if container.OS == runtime.GOOS {
|
||||||
if err != nil {
|
// We do a filepath.FromSlash here to not break "legacy" Windows
|
||||||
return fmt.Errorf("Unrecognised volume spec: %v", err)
|
// dockerfiles use Unix-style paths such as "VOLUME c:/somevolume".
|
||||||
}
|
// If we don't do this, HCS will balk.
|
||||||
|
destination = filepath.Clean(filepath.FromSlash(spec))
|
||||||
// If the mountpoint doesn't have a name, generate one.
|
} else {
|
||||||
if len(mp.Name) == 0 {
|
destination = path.Clean(spec)
|
||||||
mp.Name = stringid.GenerateNonCryptoID()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip volumes for which we already have something mounted on that
|
// Skip volumes for which we already have something mounted on that
|
||||||
// destination because of a --volume-from.
|
// destination because of a --volume-from.
|
||||||
if container.IsDestinationMounted(mp.Destination) {
|
if container.IsDestinationMounted(destination) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeDriver := hostConfig.VolumeDriver
|
// Create the volume in the volume driver.
|
||||||
|
v, err := daemon.volumes.Create(
|
||||||
// Create the volume in the volume driver. If it doesn't exist,
|
context.TODO(),
|
||||||
// a new one will be created.
|
stringid.GenerateNonCryptoID(),
|
||||||
v, err := daemon.volumes.Create(context.TODO(), mp.Name, volumeDriver, volumeopts.WithCreateReference(container.ID))
|
hostConfig.VolumeDriver,
|
||||||
|
volumeopts.WithCreateReference(container.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME Windows: This code block is present in the Linux version and
|
// Add it to container.MountPoints. Note that the last parameter is true - as in read-write
|
||||||
// allows the contents to be copied to the container FS prior to it
|
container.AddMountPointWithVolume(destination, &volumeWrapper{v: v, s: daemon.volumes}, true)
|
||||||
// being started. However, the function utilizes the FollowSymLinkInScope
|
|
||||||
// path which does not cope with Windows volume-style file paths. There
|
|
||||||
// is a separate effort to resolve this (@swernli), so this processing
|
|
||||||
// is deferred for now. A case where this would be useful is when
|
|
||||||
// a dockerfile includes a VOLUME statement, but something is created
|
|
||||||
// in that directory during the dockerfile processing. What this means
|
|
||||||
// on Windows for TP5 is that in that scenario, the contents will not
|
|
||||||
// copied, but that's (somewhat) OK as HCS will bomb out soon after
|
|
||||||
// at it doesn't support mapped directories which have contents in the
|
|
||||||
// destination path anyway.
|
|
||||||
//
|
|
||||||
// Example for repro later:
|
|
||||||
// FROM windowsservercore
|
|
||||||
// RUN mkdir c:\myvol
|
|
||||||
// RUN copy c:\windows\system32\ntdll.dll c:\myvol
|
|
||||||
// VOLUME "c:\myvol"
|
|
||||||
//
|
|
||||||
// Then
|
|
||||||
// docker build -t vol .
|
|
||||||
// docker run -it --rm vol cmd <-- This is where HCS will error out.
|
|
||||||
//
|
|
||||||
// // never attempt to copy existing content in a container FS to a shared volume
|
|
||||||
// if v.DriverName() == volume.DefaultDriverName {
|
|
||||||
// if err := container.CopyImagePathContent(v, mp.Destination); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Add it to container.MountPoints
|
|
||||||
container.AddMountPointWithVolume(mp.Destination, &volumeWrapper{v: v, s: daemon.volumes}, mp.RW)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: On Linux, this function returns daemon.populateVolumes(container).
|
||||||
|
// This allows the contents of a volume to be copied to the containers
|
||||||
|
// filesystem prior to it be in started.
|
||||||
|
//
|
||||||
|
// There are many issues with solving this problem, and the reality is
|
||||||
|
// that it will likely be one thing that can't be done on Windows.
|
||||||
|
//
|
||||||
|
// FollowSymLinkInScope doesn't cope with Windows volume-style file paths.
|
||||||
|
// To avoid break outs we need to do scoped access. This is surmountable.
|
||||||
|
//
|
||||||
|
// Argons might be possible, but at this execution point on Windows,
|
||||||
|
// the container filesystem isn't mounted as it's done much later than
|
||||||
|
// on Linux.
|
||||||
|
//
|
||||||
|
// Xenons (both WCOW and LCOW) are difficult as we don't want to mount
|
||||||
|
// the filesystem on the host, for both security and perf reasons.
|
||||||
|
// Further, obviously the LCOW filesystem can't be mounted on the host
|
||||||
|
// directly.
|
||||||
|
//
|
||||||
|
// What this means is that on Windows, the contents of a VOLUME created
|
||||||
|
// in a Dockerfile with contents will NOT be copied.
|
||||||
|
//
|
||||||
|
// Pre ~RS3 had limitations in mapped directories which preclude actually
|
||||||
|
// doing this in the platform anyway.
|
||||||
|
//
|
||||||
|
// Example for repro later:
|
||||||
|
// FROM windowsservercore
|
||||||
|
// RUN mkdir c:\myvol
|
||||||
|
// RUN copy c:\windows\system32\ntdll.dll c:\myvol
|
||||||
|
// VOLUME "c:\myvol"
|
||||||
|
//
|
||||||
|
// Then
|
||||||
|
// docker build -t vol .
|
||||||
|
// docker run -it --rm vol cmd
|
||||||
|
//
|
||||||
|
// Result
|
||||||
|
// RS1 to ~RS3 HCS would error out
|
||||||
|
// RS4+ Succeeds, container starts, but c:\source will be empty
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче