github.com/kobeld/docker@v1.12.0-rc1/daemon/volumes.go (about)

     1  package daemon
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/volume"
    12  	"github.com/docker/engine-api/types"
    13  	containertypes "github.com/docker/engine-api/types/container"
    14  )
    15  
    16  var (
    17  	// ErrVolumeReadonly is used to signal an error when trying to copy data into
    18  	// a volume mount that is not writable.
    19  	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
    20  )
    21  
    22  type mounts []container.Mount
    23  
    24  // volumeToAPIType converts a volume.Volume to the type used by the remote API
    25  func volumeToAPIType(v volume.Volume) *types.Volume {
    26  	tv := &types.Volume{
    27  		Name:   v.Name(),
    28  		Driver: v.DriverName(),
    29  	}
    30  	if v, ok := v.(volume.LabeledVolume); ok {
    31  		tv.Labels = v.Labels()
    32  	}
    33  
    34  	if v, ok := v.(volume.ScopedVolume); ok {
    35  		tv.Scope = v.Scope()
    36  	}
    37  	return tv
    38  }
    39  
    40  // Len returns the number of mounts. Used in sorting.
    41  func (m mounts) Len() int {
    42  	return len(m)
    43  }
    44  
    45  // Less returns true if the number of parts (a/b/c would be 3 parts) in the
    46  // mount indexed by parameter 1 is less than that of the mount indexed by
    47  // parameter 2. Used in sorting.
    48  func (m mounts) Less(i, j int) bool {
    49  	return m.parts(i) < m.parts(j)
    50  }
    51  
    52  // Swap swaps two items in an array of mounts. Used in sorting
    53  func (m mounts) Swap(i, j int) {
    54  	m[i], m[j] = m[j], m[i]
    55  }
    56  
    57  // parts returns the number of parts in the destination of a mount. Used in sorting.
    58  func (m mounts) parts(i int) int {
    59  	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
    60  }
    61  
    62  // registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
    63  // It follows the next sequence to decide what to mount in each final destination:
    64  //
    65  // 1. Select the previously configured mount points for the containers, if any.
    66  // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
    67  // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
    68  // 4. Cleanup old volumes that are about to be reassigned.
    69  func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
    70  	binds := map[string]bool{}
    71  	mountPoints := map[string]*volume.MountPoint{}
    72  	defer func() {
    73  		// clean up the container mountpoints once return with error
    74  		if retErr != nil {
    75  			for _, m := range mountPoints {
    76  				if m.Volume == nil {
    77  					continue
    78  				}
    79  				daemon.volumes.Dereference(m.Volume, container.ID)
    80  			}
    81  		}
    82  	}()
    83  
    84  	// 1. Read already configured mount points.
    85  	for name, point := range container.MountPoints {
    86  		mountPoints[name] = point
    87  	}
    88  
    89  	// 2. Read volumes from other containers.
    90  	for _, v := range hostConfig.VolumesFrom {
    91  		containerID, mode, err := volume.ParseVolumesFrom(v)
    92  		if err != nil {
    93  			return err
    94  		}
    95  
    96  		c, err := daemon.GetContainer(containerID)
    97  		if err != nil {
    98  			return err
    99  		}
   100  
   101  		for _, m := range c.MountPoints {
   102  			cp := &volume.MountPoint{
   103  				Name:        m.Name,
   104  				Source:      m.Source,
   105  				RW:          m.RW && volume.ReadWrite(mode),
   106  				Driver:      m.Driver,
   107  				Destination: m.Destination,
   108  				Propagation: m.Propagation,
   109  				Named:       m.Named,
   110  			}
   111  
   112  			if len(cp.Source) == 0 {
   113  				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
   114  				if err != nil {
   115  					return err
   116  				}
   117  				cp.Volume = v
   118  			}
   119  
   120  			mountPoints[cp.Destination] = cp
   121  		}
   122  	}
   123  
   124  	// 3. Read bind mounts
   125  	for _, b := range hostConfig.Binds {
   126  		// #10618
   127  		bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver)
   128  		if err != nil {
   129  			return err
   130  		}
   131  
   132  		if binds[bind.Destination] {
   133  			return fmt.Errorf("Duplicate mount point '%s'", bind.Destination)
   134  		}
   135  
   136  		if len(bind.Name) > 0 {
   137  			// create the volume
   138  			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
   139  			if err != nil {
   140  				return err
   141  			}
   142  			bind.Volume = v
   143  			bind.Source = v.Path()
   144  			// bind.Name is an already existing volume, we need to use that here
   145  			bind.Driver = v.DriverName()
   146  			bind.Named = true
   147  			if bind.Driver == "local" {
   148  				bind = setBindModeIfNull(bind)
   149  			}
   150  		}
   151  
   152  		binds[bind.Destination] = true
   153  		mountPoints[bind.Destination] = bind
   154  	}
   155  
   156  	container.Lock()
   157  
   158  	// 4. Cleanup old volumes that are about to be reassigned.
   159  	for _, m := range mountPoints {
   160  		if m.BackwardsCompatible() {
   161  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   162  				daemon.volumes.Dereference(mp.Volume, container.ID)
   163  			}
   164  		}
   165  	}
   166  	container.MountPoints = mountPoints
   167  
   168  	container.Unlock()
   169  
   170  	return nil
   171  }
   172  
   173  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   174  // This happens after a daemon restart.
   175  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
   176  	if len(m.Driver) > 0 && m.Volume == nil {
   177  		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
   178  		if err != nil {
   179  			return err
   180  		}
   181  		m.Volume = v
   182  	}
   183  	return nil
   184  }