Selaa lähdekoodia

Merge pull request #4159 from crosbymichael/move-volumes

Move volumes out of container.go and into volumes.go
Guillaume J. Charmes 11 vuotta sitten
vanhempi
commit
a3bc3bb8c3
2 muutettua tiedostoa jossa 339 lisäystä ja 301 poistoa
  1. 7 301
      container.go
  2. 332 0
      volumes.go

+ 7 - 301
container.go

@@ -10,7 +10,6 @@ import (
 	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/links"
 	"github.com/dotcloud/docker/nat"
-	"github.com/dotcloud/docker/pkg/mount"
 	"github.com/dotcloud/docker/pkg/term"
 	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
@@ -20,7 +19,6 @@ import (
 	"log"
 	"os"
 	"path"
-	"path/filepath"
 	"strings"
 	"sync"
 	"syscall"
@@ -28,8 +26,10 @@ import (
 )
 
 var (
-	ErrNotATTY = errors.New("The PTY is not a file")
-	ErrNoTTY   = errors.New("No PTY found")
+	ErrNotATTY               = errors.New("The PTY is not a file")
+	ErrNoTTY                 = errors.New("No PTY found")
+	ErrContainerStart        = errors.New("The container failed to start. Unknown error")
+	ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
 )
 
 type Container struct {
@@ -75,17 +75,6 @@ type Container struct {
 	activeLinks map[string]*links.Link
 }
 
-type BindMap struct {
-	SrcPath string
-	DstPath string
-	Mode    string
-}
-
-var (
-	ErrContainerStart        = errors.New("The container failed to start. Unknown error")
-	ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
-)
-
 // FIXME: move deprecated port stuff to nat to clean up the core.
 type PortMapping map[string]string // Deprecated
 
@@ -504,17 +493,7 @@ func (container *Container) Start() (err error) {
 		log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
 	}
 
-	if container.Volumes == nil || len(container.Volumes) == 0 {
-		container.Volumes = make(map[string]string)
-		container.VolumesRW = make(map[string]bool)
-	}
-
-	// Apply volumes from another container if requested
-	if err := container.applyExternalVolumes(); err != nil {
-		return err
-	}
-
-	if err := container.createVolumes(); err != nil {
+	if err := prepareVolumesForContainer(container); err != nil {
 		return err
 	}
 
@@ -599,62 +578,10 @@ func (container *Container) Start() (err error) {
 		return err
 	}
 
-	// Setup the root fs as a bind mount of the base fs
-	root := container.RootfsPath()
-	if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) {
-		return nil
-	}
-
-	// Create a bind mount of the base fs as a place where we can add mounts
-	// without affecting the ability to access the base fs
-	if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil {
-		return err
-	}
-
-	// Make sure the root fs is private so the mounts here don't propagate to basefs
-	if err := mount.ForceMount(root, root, "none", "private"); err != nil {
+	if err := mountVolumesForContainer(container, envPath); err != nil {
 		return err
 	}
 
-	// Mount docker specific files into the containers root fs
-	if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
-		return err
-	}
-	if err := mount.Mount(envPath, path.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
-		return err
-	}
-	if err := mount.Mount(container.ResolvConfPath, path.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
-		return err
-	}
-
-	if container.HostnamePath != "" && container.HostsPath != "" {
-		if err := mount.Mount(container.HostnamePath, path.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
-			return err
-		}
-		if err := mount.Mount(container.HostsPath, path.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
-			return err
-		}
-	}
-
-	// Mount user specified volumes
-	for r, v := range container.Volumes {
-		mountAs := "ro"
-		if container.VolumesRW[r] {
-			mountAs = "rw"
-		}
-
-		r = path.Join(root, r)
-		if p, err := utils.FollowSymlinkInScope(r, root); err != nil {
-			return err
-		} else {
-			r = p
-		}
-
-		if err := mount.Mount(v, r, "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
-			return err
-		}
-	}
-
 	populateCommand(container)
 
 	// Setup logging of stdout and stderr to disk
@@ -707,205 +634,6 @@ func (container *Container) Start() (err error) {
 	return nil
 }
 
-func (container *Container) getBindMap() (map[string]BindMap, error) {
-	// Create the requested bind mounts
-	binds := make(map[string]BindMap)
-	// Define illegal container destinations
-	illegalDsts := []string{"/", "."}
-
-	for _, bind := range container.hostConfig.Binds {
-		// FIXME: factorize bind parsing in parseBind
-		var src, dst, mode string
-		arr := strings.Split(bind, ":")
-		if len(arr) == 2 {
-			src = arr[0]
-			dst = arr[1]
-			mode = "rw"
-		} else if len(arr) == 3 {
-			src = arr[0]
-			dst = arr[1]
-			mode = arr[2]
-		} else {
-			return nil, fmt.Errorf("Invalid bind specification: %s", bind)
-		}
-
-		// Bail if trying to mount to an illegal destination
-		for _, illegal := range illegalDsts {
-			if dst == illegal {
-				return nil, fmt.Errorf("Illegal bind destination: %s", dst)
-			}
-		}
-
-		bindMap := BindMap{
-			SrcPath: src,
-			DstPath: dst,
-			Mode:    mode,
-		}
-		binds[path.Clean(dst)] = bindMap
-	}
-	return binds, nil
-}
-
-func (container *Container) createVolumes() error {
-	binds, err := container.getBindMap()
-	if err != nil {
-		return err
-	}
-	volumesDriver := container.runtime.volumes.driver
-	// Create the requested volumes if they don't exist
-	for volPath := range container.Config.Volumes {
-		volPath = path.Clean(volPath)
-		volIsDir := true
-		// Skip existing volumes
-		if _, exists := container.Volumes[volPath]; exists {
-			continue
-		}
-		var srcPath string
-		var isBindMount bool
-		srcRW := false
-		// If an external bind is defined for this volume, use that as a source
-		if bindMap, exists := binds[volPath]; exists {
-			isBindMount = true
-			srcPath = bindMap.SrcPath
-			if strings.ToLower(bindMap.Mode) == "rw" {
-				srcRW = true
-			}
-			if stat, err := os.Stat(bindMap.SrcPath); err != nil {
-				return err
-			} else {
-				volIsDir = stat.IsDir()
-			}
-			// Otherwise create an directory in $ROOT/volumes/ and use that
-		} else {
-
-			// Do not pass a container as the parameter for the volume creation.
-			// The graph driver using the container's information ( Image ) to
-			// create the parent.
-			c, err := container.runtime.volumes.Create(nil, nil, "", "", nil)
-			if err != nil {
-				return err
-			}
-			srcPath, err = volumesDriver.Get(c.ID)
-			if err != nil {
-				return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
-			}
-			srcRW = true // RW by default
-		}
-
-		if p, err := filepath.EvalSymlinks(srcPath); err != nil {
-			return err
-		} else {
-			srcPath = p
-		}
-
-		container.Volumes[volPath] = srcPath
-		container.VolumesRW[volPath] = srcRW
-
-		// Create the mountpoint
-		volPath = path.Join(container.basefs, volPath)
-		rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs)
-		if err != nil {
-			return err
-		}
-
-		if _, err := os.Stat(rootVolPath); err != nil {
-			if os.IsNotExist(err) {
-				if volIsDir {
-					if err := os.MkdirAll(rootVolPath, 0755); err != nil {
-						return err
-					}
-				} else {
-					if err := os.MkdirAll(path.Dir(rootVolPath), 0755); err != nil {
-						return err
-					}
-					if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
-						return err
-					} else {
-						f.Close()
-					}
-				}
-			}
-		}
-
-		// Do not copy or change permissions if we are mounting from the host
-		if srcRW && !isBindMount {
-			volList, err := ioutil.ReadDir(rootVolPath)
-			if err != nil {
-				return err
-			}
-			if len(volList) > 0 {
-				srcList, err := ioutil.ReadDir(srcPath)
-				if err != nil {
-					return err
-				}
-				if len(srcList) == 0 {
-					// If the source volume is empty copy files from the root into the volume
-					if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
-						return err
-					}
-
-					var stat syscall.Stat_t
-					if err := syscall.Stat(rootVolPath, &stat); err != nil {
-						return err
-					}
-					var srcStat syscall.Stat_t
-					if err := syscall.Stat(srcPath, &srcStat); err != nil {
-						return err
-					}
-					// Change the source volume's ownership if it differs from the root
-					// files that were just copied
-					if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
-						if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
-							return err
-						}
-					}
-				}
-			}
-		}
-	}
-	return nil
-}
-
-func (container *Container) applyExternalVolumes() error {
-	if container.Config.VolumesFrom != "" {
-		containerSpecs := strings.Split(container.Config.VolumesFrom, ",")
-		for _, containerSpec := range containerSpecs {
-			mountRW := true
-			specParts := strings.SplitN(containerSpec, ":", 2)
-			switch len(specParts) {
-			case 0:
-				return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
-			case 2:
-				switch specParts[1] {
-				case "ro":
-					mountRW = false
-				case "rw": // mountRW is already true
-				default:
-					return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
-				}
-			}
-			c := container.runtime.Get(specParts[0])
-			if c == nil {
-				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
-			}
-			for volPath, id := range c.Volumes {
-				if _, exists := container.Volumes[volPath]; exists {
-					continue
-				}
-				if err := os.MkdirAll(path.Join(container.basefs, volPath), 0755); err != nil {
-					return err
-				}
-				container.Volumes[volPath] = id
-				if isRW, exists := c.VolumesRW[volPath]; exists {
-					container.VolumesRW[volPath] = isRW && mountRW
-				}
-			}
-
-		}
-	}
-	return nil
-}
-
 func (container *Container) Run() error {
 	if err := container.Start(); err != nil {
 		return err
@@ -1178,29 +906,7 @@ func (container *Container) cleanup() {
 		}
 	}
 
-	var (
-		root   = container.RootfsPath()
-		mounts = []string{
-			root,
-			path.Join(root, "/.dockerinit"),
-			path.Join(root, "/.dockerenv"),
-			path.Join(root, "/etc/resolv.conf"),
-		}
-	)
-
-	if container.HostnamePath != "" && container.HostsPath != "" {
-		mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts"))
-	}
-
-	for r := range container.Volumes {
-		mounts = append(mounts, path.Join(root, r))
-	}
-
-	for i := len(mounts) - 1; i >= 0; i-- {
-		if lastError := mount.Unmount(mounts[i]); lastError != nil {
-			log.Printf("Failed to umount %v: %v", mounts[i], lastError)
-		}
-	}
+	unmountVolumesForContainer(container)
 
 	if err := container.Unmount(); err != nil {
 		log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)

+ 332 - 0
volumes.go

@@ -0,0 +1,332 @@
+package docker
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/archive"
+	"github.com/dotcloud/docker/pkg/mount"
+	"github.com/dotcloud/docker/utils"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"syscall"
+)
+
+type BindMap struct {
+	SrcPath string
+	DstPath string
+	Mode    string
+}
+
+func prepareVolumesForContainer(container *Container) error {
+	if container.Volumes == nil || len(container.Volumes) == 0 {
+		container.Volumes = make(map[string]string)
+		container.VolumesRW = make(map[string]bool)
+	}
+
+	if err := applyVolumesFrom(container); err != nil {
+		return err
+	}
+	if err := createVolumes(container); err != nil {
+		return err
+	}
+	return nil
+}
+
+func mountVolumesForContainer(container *Container, envPath string) error {
+	// Setup the root fs as a bind mount of the base fs
+	var (
+		root    = container.RootfsPath()
+		runtime = container.runtime
+	)
+	if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) {
+		return nil
+	}
+
+	// Create a bind mount of the base fs as a place where we can add mounts
+	// without affecting the ability to access the base fs
+	if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil {
+		return err
+	}
+
+	// Make sure the root fs is private so the mounts here don't propagate to basefs
+	if err := mount.ForceMount(root, root, "none", "private"); err != nil {
+		return err
+	}
+
+	// Mount docker specific files into the containers root fs
+	if err := mount.Mount(runtime.sysInitPath, filepath.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
+		return err
+	}
+	if err := mount.Mount(envPath, filepath.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
+		return err
+	}
+	if err := mount.Mount(container.ResolvConfPath, filepath.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
+		return err
+	}
+
+	if container.HostnamePath != "" && container.HostsPath != "" {
+		if err := mount.Mount(container.HostnamePath, filepath.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
+			return err
+		}
+		if err := mount.Mount(container.HostsPath, filepath.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
+			return err
+		}
+	}
+
+	// Mount user specified volumes
+	for r, v := range container.Volumes {
+		mountAs := "ro"
+		if container.VolumesRW[r] {
+			mountAs = "rw"
+		}
+
+		r = filepath.Join(root, r)
+		if p, err := utils.FollowSymlinkInScope(r, root); err != nil {
+			return err
+		} else {
+			r = p
+		}
+
+		if err := mount.Mount(v, r, "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func unmountVolumesForContainer(container *Container) {
+	var (
+		root   = container.RootfsPath()
+		mounts = []string{
+			root,
+			filepath.Join(root, "/.dockerinit"),
+			filepath.Join(root, "/.dockerenv"),
+			filepath.Join(root, "/etc/resolv.conf"),
+		}
+	)
+
+	if container.HostnamePath != "" && container.HostsPath != "" {
+		mounts = append(mounts, filepath.Join(root, "/etc/hostname"), filepath.Join(root, "/etc/hosts"))
+	}
+
+	for r := range container.Volumes {
+		mounts = append(mounts, filepath.Join(root, r))
+	}
+
+	for i := len(mounts) - 1; i >= 0; i-- {
+		if lastError := mount.Unmount(mounts[i]); lastError != nil {
+			log.Printf("Failed to umount %v: %v", mounts[i], lastError)
+		}
+	}
+}
+
+func applyVolumesFrom(container *Container) error {
+	if container.Config.VolumesFrom != "" {
+		for _, containerSpec := range strings.Split(container.Config.VolumesFrom, ",") {
+			var (
+				mountRW   = true
+				specParts = strings.SplitN(containerSpec, ":", 2)
+			)
+
+			switch len(specParts) {
+			case 0:
+				return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
+			case 2:
+				switch specParts[1] {
+				case "ro":
+					mountRW = false
+				case "rw": // mountRW is already true
+				default:
+					return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
+				}
+			}
+
+			c := container.runtime.Get(specParts[0])
+			if c == nil {
+				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
+			}
+
+			for volPath, id := range c.Volumes {
+				if _, exists := container.Volumes[volPath]; exists {
+					continue
+				}
+				if err := os.MkdirAll(filepath.Join(container.basefs, volPath), 0755); err != nil {
+					return err
+				}
+				container.Volumes[volPath] = id
+				if isRW, exists := c.VolumesRW[volPath]; exists {
+					container.VolumesRW[volPath] = isRW && mountRW
+				}
+			}
+
+		}
+	}
+	return nil
+}
+
+func getBindMap(container *Container) (map[string]BindMap, error) {
+	var (
+		// Create the requested bind mounts
+		binds = make(map[string]BindMap)
+		// Define illegal container destinations
+		illegalDsts = []string{"/", "."}
+	)
+
+	for _, bind := range container.hostConfig.Binds {
+		// FIXME: factorize bind parsing in parseBind
+		var (
+			src, dst, mode string
+			arr            = strings.Split(bind, ":")
+		)
+
+		if len(arr) == 2 {
+			src = arr[0]
+			dst = arr[1]
+			mode = "rw"
+		} else if len(arr) == 3 {
+			src = arr[0]
+			dst = arr[1]
+			mode = arr[2]
+		} else {
+			return nil, fmt.Errorf("Invalid bind specification: %s", bind)
+		}
+
+		// Bail if trying to mount to an illegal destination
+		for _, illegal := range illegalDsts {
+			if dst == illegal {
+				return nil, fmt.Errorf("Illegal bind destination: %s", dst)
+			}
+		}
+
+		bindMap := BindMap{
+			SrcPath: src,
+			DstPath: dst,
+			Mode:    mode,
+		}
+		binds[filepath.Clean(dst)] = bindMap
+	}
+	return binds, nil
+}
+
+func createVolumes(container *Container) error {
+	binds, err := getBindMap(container)
+	if err != nil {
+		return err
+	}
+
+	volumesDriver := container.runtime.volumes.driver
+	// Create the requested volumes if they don't exist
+	for volPath := range container.Config.Volumes {
+		volPath = filepath.Clean(volPath)
+		volIsDir := true
+		// Skip existing volumes
+		if _, exists := container.Volumes[volPath]; exists {
+			continue
+		}
+		var srcPath string
+		var isBindMount bool
+		srcRW := false
+		// If an external bind is defined for this volume, use that as a source
+		if bindMap, exists := binds[volPath]; exists {
+			isBindMount = true
+			srcPath = bindMap.SrcPath
+			if strings.ToLower(bindMap.Mode) == "rw" {
+				srcRW = true
+			}
+			if stat, err := os.Stat(bindMap.SrcPath); err != nil {
+				return err
+			} else {
+				volIsDir = stat.IsDir()
+			}
+			// Otherwise create an directory in $ROOT/volumes/ and use that
+		} else {
+
+			// Do not pass a container as the parameter for the volume creation.
+			// The graph driver using the container's information ( Image ) to
+			// create the parent.
+			c, err := container.runtime.volumes.Create(nil, nil, "", "", nil)
+			if err != nil {
+				return err
+			}
+			srcPath, err = volumesDriver.Get(c.ID)
+			if err != nil {
+				return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
+			}
+			srcRW = true // RW by default
+		}
+
+		if p, err := filepath.EvalSymlinks(srcPath); err != nil {
+			return err
+		} else {
+			srcPath = p
+		}
+
+		container.Volumes[volPath] = srcPath
+		container.VolumesRW[volPath] = srcRW
+
+		// Create the mountpoint
+		volPath = filepath.Join(container.basefs, volPath)
+		rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs)
+		if err != nil {
+			return err
+		}
+
+		if _, err := os.Stat(rootVolPath); err != nil {
+			if os.IsNotExist(err) {
+				if volIsDir {
+					if err := os.MkdirAll(rootVolPath, 0755); err != nil {
+						return err
+					}
+				} else {
+					if err := os.MkdirAll(filepath.Dir(rootVolPath), 0755); err != nil {
+						return err
+					}
+					if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
+						return err
+					} else {
+						f.Close()
+					}
+				}
+			}
+		}
+
+		// Do not copy or change permissions if we are mounting from the host
+		if srcRW && !isBindMount {
+			volList, err := ioutil.ReadDir(rootVolPath)
+			if err != nil {
+				return err
+			}
+			if len(volList) > 0 {
+				srcList, err := ioutil.ReadDir(srcPath)
+				if err != nil {
+					return err
+				}
+				if len(srcList) == 0 {
+					// If the source volume is empty copy files from the root into the volume
+					if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
+						return err
+					}
+
+					var stat syscall.Stat_t
+					if err := syscall.Stat(rootVolPath, &stat); err != nil {
+						return err
+					}
+					var srcStat syscall.Stat_t
+					if err := syscall.Stat(srcPath, &srcStat); err != nil {
+						return err
+					}
+					// Change the source volume's ownership if it differs from the root
+					// files that were just copied
+					if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
+						if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
+							return err
+						}
+					}
+				}
+			}
+		}
+	}
+	return nil
+}