mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix uneccessary calls to volume.Unmount()
Fixes #22564 When an error occurs on mount, there should not be any call later to unmount. This can throw off refcounting in the underlying driver unexpectedly. Consider these two cases: ``` $ docker run -v foo:/bar busybox true ``` ``` $ docker run -v foo:/bar -w /foo busybox true ``` In the first case, if mounting `foo` fails, the volume driver will not get a call to unmount (this is the incorrect behavior). In the second case, the volume driver will not get a call to unmount (correct behavior). This occurs because in the first case, `/bar` does not exist in the container, and as such there is no call to `volume.Mount()` during the `create` phase. It will error out during the `start` phase. In the second case `/bar` is created before dealing with the volume because of the `-w`. Because of this, when the volume is being setup docker will try to copy the image path contents in the volume, in which case it will attempt to mount the volume and fail. This happens during the `create` phase. This makes it so the container will not be created (or at least fully created) and the user gets the error on `create` instead of `start`. The error handling is different in these two phases. Changed to only send `unmount` if the volume is mounted. While investigating the cause of the reported issue I found some odd behavior in unmount calls so I've cleaned those up a bit here as well. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
c13bf5ba51
commit
9a2d0bc3ad
7 changed files with 113 additions and 107 deletions
|
@ -567,6 +567,32 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmountVolumes unmounts all volumes
|
||||||
|
func (container *Container) UnmountVolumes(volumeEventLog func(name, action string, attributes map[string]string)) error {
|
||||||
|
var errors []string
|
||||||
|
for _, volumeMount := range container.MountPoints {
|
||||||
|
// Check if the mounpoint has an ID, this is currently the best way to tell if it's actually mounted
|
||||||
|
// TODO(cpuguyh83): there should be a better way to handle this
|
||||||
|
if volumeMount.Volume != nil && volumeMount.ID != "" {
|
||||||
|
if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
volumeMount.ID = ""
|
||||||
|
|
||||||
|
attributes := map[string]string{
|
||||||
|
"driver": volumeMount.Volume.DriverName(),
|
||||||
|
"container": container.ID,
|
||||||
|
}
|
||||||
|
volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errors, "; "))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsDestinationMounted checks whether a path is mounted on the container or not.
|
// IsDestinationMounted checks whether a path is mounted on the container or not.
|
||||||
func (container *Container) IsDestinationMounted(destination string) bool {
|
func (container *Container) IsDestinationMounted(destination string) bool {
|
||||||
return container.MountPoints[destination] != nil
|
return container.MountPoints[destination] != nil
|
||||||
|
|
|
@ -102,18 +102,6 @@ func (container *Container) BuildHostnameFile() error {
|
||||||
return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
|
return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendNetworkMounts appends any network mounts to the array of mount points passed in
|
|
||||||
func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
|
|
||||||
for _, mnt := range container.NetworkMounts() {
|
|
||||||
dest, err := container.GetResourcePath(mnt.Destination)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest})
|
|
||||||
}
|
|
||||||
return volumeMounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkMounts returns the list of network mounts.
|
// NetworkMounts returns the list of network mounts.
|
||||||
func (container *Container) NetworkMounts() []Mount {
|
func (container *Container) NetworkMounts() []Mount {
|
||||||
var mounts []Mount
|
var mounts []Mount
|
||||||
|
@ -353,49 +341,37 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmountVolumes unmounts all volumes
|
// DetachAndUnmount uses a detached mount on all mount destinations, then
|
||||||
func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error {
|
// unmounts each volume normally.
|
||||||
var (
|
// This is used from daemon/archive for `docker cp`
|
||||||
volumeMounts []volume.MountPoint
|
func (container *Container) DetachAndUnmount(volumeEventLog func(name, action string, attributes map[string]string)) error {
|
||||||
err error
|
networkMounts := container.NetworkMounts()
|
||||||
)
|
mountPaths := make([]string, 0, len(container.MountPoints)+len(networkMounts))
|
||||||
|
|
||||||
for _, mntPoint := range container.MountPoints {
|
for _, mntPoint := range container.MountPoints {
|
||||||
dest, err := container.GetResourcePath(mntPoint.Destination)
|
dest, err := container.GetResourcePath(mntPoint.Destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
logrus.Warnf("Failed to get volume destination path for container '%s' at '%s' while lazily unmounting: %v", container.ID, mntPoint.Destination, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
mountPaths = append(mountPaths, dest)
|
||||||
volumeMounts = append(volumeMounts, volume.MountPoint{Destination: dest, Volume: mntPoint.Volume, ID: mntPoint.ID})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append any network mounts to the list (this is a no-op on Windows)
|
for _, m := range networkMounts {
|
||||||
if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil {
|
dest, err := container.GetResourcePath(m.Destination)
|
||||||
return err
|
if err != nil {
|
||||||
|
logrus.Warnf("Failed to get volume destination path for container '%s' at '%s' while lazily unmounting: %v", container.ID, m.Destination, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mountPaths = append(mountPaths, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, volumeMount := range volumeMounts {
|
for _, mountPath := range mountPaths {
|
||||||
if forceSyscall {
|
if err := detachMounted(mountPath); err != nil {
|
||||||
if err := detachMounted(volumeMount.Destination); err != nil {
|
logrus.Warnf("%s unmountVolumes: Failed to do lazy umount fo volume '%s': %v", container.ID, mountPath, err)
|
||||||
logrus.Warnf("%s unmountVolumes: Failed to do lazy umount %v", container.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if volumeMount.Volume != nil {
|
|
||||||
if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
volumeMount.ID = ""
|
|
||||||
|
|
||||||
attributes := map[string]string{
|
|
||||||
"driver": volumeMount.Volume.DriverName(),
|
|
||||||
"container": container.ID,
|
|
||||||
}
|
|
||||||
volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return container.UnmountVolumes(volumeEventLog)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyExistingContents copies from the source to the destination and
|
// copyExistingContents copies from the source to the destination and
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
"github.com/docker/docker/volume"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Container holds fields specific to the Windows implementation. See
|
// Container holds fields specific to the Windows implementation. See
|
||||||
|
@ -54,41 +53,11 @@ func (container *Container) UnmountSecrets() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmountVolumes explicitly unmounts volumes from the container.
|
// DetachAndUnmount unmounts all volumes.
|
||||||
func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error {
|
// On Windows it only delegates to `UnmountVolumes` since there is nothing to
|
||||||
var (
|
// force unmount.
|
||||||
volumeMounts []volume.MountPoint
|
func (container *Container) DetachAndUnmount(volumeEventLog func(name, action string, attributes map[string]string)) error {
|
||||||
err error
|
return container.UnmountVolumes(volumeEventLog)
|
||||||
)
|
|
||||||
|
|
||||||
for _, mntPoint := range container.MountPoints {
|
|
||||||
// Do not attempt to get the mountpoint destination on the host as it
|
|
||||||
// is not accessible on Windows in the case that a container is running.
|
|
||||||
// (It's a special reparse point which doesn't have any contextual meaning).
|
|
||||||
volumeMounts = append(volumeMounts, volume.MountPoint{Volume: mntPoint.Volume, ID: mntPoint.ID})
|
|
||||||
}
|
|
||||||
|
|
||||||
// atm, this is a no-op.
|
|
||||||
if volumeMounts, err = appendNetworkMounts(container, volumeMounts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, volumeMount := range volumeMounts {
|
|
||||||
if volumeMount.Volume != nil {
|
|
||||||
if err := volumeMount.Volume.Unmount(volumeMount.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
volumeMount.ID = ""
|
|
||||||
|
|
||||||
attributes := map[string]string{
|
|
||||||
"driver": volumeMount.Volume.DriverName(),
|
|
||||||
"container": container.ID,
|
|
||||||
}
|
|
||||||
volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TmpfsMounts returns the list of tmpfs mounts
|
// TmpfsMounts returns the list of tmpfs mounts
|
||||||
|
@ -119,13 +88,6 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendNetworkMounts appends any network mounts to the array of mount points passed in.
|
|
||||||
// Windows does not support network mounts (not to be confused with SMB network mounts), so
|
|
||||||
// this is a no-op.
|
|
||||||
func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
|
|
||||||
return volumeMounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanResourcePath cleans a resource path by removing C:\ syntax, and prepares
|
// cleanResourcePath cleans a resource path by removing C:\ syntax, and prepares
|
||||||
// to combine with a volume path
|
// to combine with a volume path
|
||||||
func cleanResourcePath(path string) string {
|
func cleanResourcePath(path string) string {
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (daemon *Daemon) containerStatPath(container *container.Container, path str
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
defer container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
|
||||||
|
|
||||||
content = ioutils.NewReadCloserWrapper(data, func() error {
|
content = ioutils.NewReadCloserWrapper(data, func() error {
|
||||||
err := data.Close()
|
err := data.Close()
|
||||||
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
|
@ -184,7 +184,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
defer container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
|
||||||
|
|
||||||
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
container.DetachAndUnmount(daemon.LogVolumeEvent)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -221,7 +221,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.BaseFS != "" {
|
if container.BaseFS != "" {
|
||||||
if err := container.UnmountVolumes(false, daemon.LogVolumeEvent); err != nil {
|
if err := container.UnmountVolumes(daemon.LogVolumeEvent); err != nil {
|
||||||
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
|
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ type vol struct {
|
||||||
Mountpoint string
|
Mountpoint string
|
||||||
Ninja bool // hack used to trigger a null volume return on `Get`
|
Ninja bool // hack used to trigger a null volume return on `Get`
|
||||||
Status map[string]interface{}
|
Status map[string]interface{}
|
||||||
|
Options map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *volumePlugin) Close() {
|
func (p *volumePlugin) Close() {
|
||||||
|
@ -130,7 +131,7 @@ func newVolumePlugin(c *check.C, name string) *volumePlugin {
|
||||||
}
|
}
|
||||||
_, isNinja := pr.Opts["ninja"]
|
_, isNinja := pr.Opts["ninja"]
|
||||||
status := map[string]interface{}{"Hello": "world"}
|
status := map[string]interface{}{"Hello": "world"}
|
||||||
s.vols[pr.Name] = vol{Name: pr.Name, Ninja: isNinja, Status: status}
|
s.vols[pr.Name] = vol{Name: pr.Name, Ninja: isNinja, Status: status, Options: pr.Opts}
|
||||||
send(w, nil)
|
send(w, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -212,6 +213,14 @@ func newVolumePlugin(c *check.C, name string) *volumePlugin {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, exists := s.vols[pr.Name]; exists {
|
||||||
|
// Use this to simulate a mount failure
|
||||||
|
if _, exists := v.Options["invalidOption"]; exists {
|
||||||
|
send(w, fmt.Errorf("invalid argument"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p := hostVolumePath(pr.Name)
|
p := hostVolumePath(pr.Name)
|
||||||
if err := os.MkdirAll(p, 0755); err != nil {
|
if err := os.MkdirAll(p, 0755); err != nil {
|
||||||
send(w, &pluginResp{Err: err.Error()})
|
send(w, &pluginResp{Err: err.Error()})
|
||||||
|
@ -562,3 +571,13 @@ func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverOutOfBandDelete(c *c
|
||||||
out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
|
out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
|
||||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *check.C) {
|
||||||
|
c.Assert(s.d.StartWithBusybox(), checker.IsNil)
|
||||||
|
s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--opt=invalidOption=1", "--name=testumount")
|
||||||
|
|
||||||
|
out, _ := s.d.Cmd("run", "-v", "testumount:/foo", "busybox", "true")
|
||||||
|
c.Assert(s.ec.unmounts, checker.Equals, 0, check.Commentf(out))
|
||||||
|
out, _ = s.d.Cmd("run", "-w", "/foo", "-v", "testumount:/foo", "busybox", "true")
|
||||||
|
c.Assert(s.ec.unmounts, checker.Equals, 0, check.Commentf(out))
|
||||||
|
}
|
||||||
|
|
|
@ -80,17 +80,33 @@ type DetailedVolume interface {
|
||||||
// specifies which volume is to be used and where inside a container it should
|
// specifies which volume is to be used and where inside a container it should
|
||||||
// be mounted.
|
// be mounted.
|
||||||
type MountPoint struct {
|
type MountPoint struct {
|
||||||
Source string // Container host directory
|
// Source is the source path of the mount.
|
||||||
Destination string // Inside the container
|
// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
|
||||||
RW bool // True if writable
|
Source string
|
||||||
Name string // Name set by user
|
// Destination is the path relative to the container root (`/`) to the mount point
|
||||||
Driver string // Volume driver to use
|
// It is where the `Source` is mounted to
|
||||||
Type mounttypes.Type `json:",omitempty"` // Type of mount to use, see `Type<foo>` definitions
|
Destination string
|
||||||
Volume Volume `json:"-"`
|
// 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<foo>` 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
|
// Note Mode is not used on Windows
|
||||||
Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"
|
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
|
// Note Propagation is not used on Windows
|
||||||
Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string
|
Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string
|
||||||
|
|
||||||
|
@ -100,7 +116,9 @@ type MountPoint struct {
|
||||||
CopyData bool `json:"-"`
|
CopyData bool `json:"-"`
|
||||||
// ID is the opaque ID used to pass to the volume driver.
|
// 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`
|
// This should be set by calls to `Mount` and unset by calls to `Unmount`
|
||||||
ID string `json:",omitempty"`
|
ID string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Sepc is a copy of the API request that created this mount.
|
||||||
Spec mounttypes.Mount
|
Spec mounttypes.Mount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +126,16 @@ type MountPoint struct {
|
||||||
// configured, or creating the source directory if supplied.
|
// configured, or creating the source directory if supplied.
|
||||||
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (string, error) {
|
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (string, error) {
|
||||||
if m.Volume != nil {
|
if m.Volume != nil {
|
||||||
if m.ID == "" {
|
id := m.ID
|
||||||
m.ID = stringid.GenerateNonCryptoID()
|
if id == "" {
|
||||||
|
id = stringid.GenerateNonCryptoID()
|
||||||
}
|
}
|
||||||
path, err := m.Volume.Mount(m.ID)
|
path, err := m.Volume.Mount(id)
|
||||||
return path, errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
|
||||||
|
}
|
||||||
|
m.ID = id
|
||||||
|
return path, nil
|
||||||
}
|
}
|
||||||
if len(m.Source) == 0 {
|
if len(m.Source) == 0 {
|
||||||
return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
|
return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
|
||||||
|
|
Loading…
Add table
Reference in a new issue