Merge pull request #10992 from cpuguy83/add_volume_mounting_for_cp

Make `docker cp` bind-mount volumes
This commit is contained in:
Jessie Frazelle 2015-04-22 15:59:28 -07:00
commit 1d48cccc99
3 changed files with 90 additions and 77 deletions

View file

@ -958,37 +958,35 @@ func (container *Container) GetSize() (int64, int64) {
}
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
container.Lock()
defer container.Unlock()
var err error
if err := container.Mount(); err != nil {
return nil, err
}
defer func() {
if err != nil {
container.Unmount()
}
}()
if err = container.mountVolumes(); err != nil {
container.unmountVolumes()
return nil, err
}
defer func() {
if err != nil {
container.unmountVolumes()
}
}()
basePath, err := container.getResourcePath(resource)
if err != nil {
container.Unmount()
return nil, err
}
// Check if this is actually in a volume
for _, mnt := range container.VolumeMounts() {
if len(mnt.MountToPath) > 0 && strings.HasPrefix(resource, mnt.MountToPath[1:]) {
return mnt.Export(resource)
}
}
// Check if this is a special one (resolv.conf, hostname, ..)
if resource == "etc/resolv.conf" {
basePath = container.ResolvConfPath
}
if resource == "etc/hostname" {
basePath = container.HostnamePath
}
if resource == "etc/hosts" {
basePath = container.HostsPath
}
stat, err := os.Stat(basePath)
if err != nil {
container.Unmount()
return nil, err
}
var filter []string
@ -1006,11 +1004,12 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
IncludeFiles: filter,
})
if err != nil {
container.Unmount()
return nil, err
}
return ioutils.NewReadCloserWrapper(archive, func() error {
err := archive.Close()
container.unmountVolumes()
container.Unmount()
return err
}),

View file

@ -2,7 +2,6 @@ package daemon
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -12,6 +11,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/volumes"
@ -27,18 +27,6 @@ type Mount struct {
isBind bool
}
func (mnt *Mount) Export(resource string) (io.ReadCloser, error) {
var name string
if resource == mnt.MountToPath[1:] {
name = filepath.Base(resource)
}
path, err := filepath.Rel(mnt.MountToPath[1:], resource)
if err != nil {
return nil, err
}
return mnt.volume.Export(path, name)
}
func (container *Container) prepareVolumes() error {
if container.Volumes == nil || len(container.Volumes) == 0 {
container.Volumes = make(map[string]string)
@ -320,6 +308,20 @@ func validMountMode(mode string) bool {
return validModes[mode]
}
func (container *Container) specialMounts() []execdriver.Mount {
var mounts []execdriver.Mount
if container.ResolvConfPath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
}
if container.HostnamePath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
}
if container.HostsPath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
}
return mounts
}
func (container *Container) setupMounts() error {
mounts := []execdriver.Mount{}
@ -336,17 +338,7 @@ func (container *Container) setupMounts() error {
})
}
if container.ResolvConfPath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
}
if container.HostnamePath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
}
if container.HostsPath != "" {
mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
}
mounts = append(mounts, container.specialMounts()...)
container.command.Mounts = mounts
return nil
@ -401,3 +393,57 @@ func copyOwnership(source, destination string) error {
return os.Chmod(destination, os.FileMode(stat.Mode()))
}
func (container *Container) mountVolumes() error {
for dest, source := range container.Volumes {
v := container.daemon.volumes.Get(source)
if v == nil {
return fmt.Errorf("could not find volume for %s:%s, impossible to mount", source, dest)
}
destPath, err := container.getResourcePath(dest)
if err != nil {
return err
}
if err := mount.Mount(source, destPath, "bind", "rbind,rw"); err != nil {
return fmt.Errorf("error while mounting volume %s: %v", source, err)
}
}
for _, mnt := range container.specialMounts() {
destPath, err := container.getResourcePath(mnt.Destination)
if err != nil {
return err
}
if err := mount.Mount(mnt.Source, destPath, "bind", "bind,rw"); err != nil {
return fmt.Errorf("error while mounting volume %s: %v", mnt.Source, err)
}
}
return nil
}
func (container *Container) unmountVolumes() {
for dest := range container.Volumes {
destPath, err := container.getResourcePath(dest)
if err != nil {
logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
continue
}
if err := mount.ForceUnmount(destPath); err != nil {
logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
continue
}
}
for _, mnt := range container.specialMounts() {
destPath, err := container.getResourcePath(mnt.Destination)
if err != nil {
logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
continue
}
if err := mount.ForceUnmount(destPath); err != nil {
logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
}
}
}

View file

@ -2,14 +2,11 @@ package volumes
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"sync"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/symlink"
)
@ -24,35 +21,6 @@ type Volume struct {
lock sync.Mutex
}
func (v *Volume) Export(resource, name string) (io.ReadCloser, error) {
if v.IsBindMount && filepath.Base(resource) == name {
name = ""
}
basePath, err := v.getResourcePath(resource)
if err != nil {
return nil, err
}
stat, err := os.Stat(basePath)
if err != nil {
return nil, err
}
var filter []string
if !stat.IsDir() {
d, f := path.Split(basePath)
basePath = d
filter = []string{f}
} else {
filter = []string{path.Base(basePath)}
basePath = path.Dir(basePath)
}
return archive.TarWithOptions(basePath, &archive.TarOptions{
Compression: archive.Uncompressed,
Name: name,
IncludeFiles: filter,
})
}
func (v *Volume) IsDir() (bool, error) {
stat, err := os.Stat(v.Path)
if err != nil {