github.com/dinever/docker@v1.11.1/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  	"github.com/opencontainers/runc/libcontainer/label"
    15  )
    16  
    17  var (
    18  	// ErrVolumeReadonly is used to signal an error when trying to copy data into
    19  	// a volume mount that is not writable.
    20  	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
    21  )
    22  
    23  type mounts []container.Mount
    24  
    25  // volumeToAPIType converts a volume.Volume to the type used by the remote API
    26  func volumeToAPIType(v volume.Volume) *types.Volume {
    27  	tv := &types.Volume{
    28  		Name:       v.Name(),
    29  		Driver:     v.DriverName(),
    30  		Mountpoint: v.Path(),
    31  	}
    32  	if v, ok := v.(interface {
    33  		Labels() map[string]string
    34  	}); ok {
    35  		tv.Labels = v.Labels()
    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) error {
    70  	binds := map[string]bool{}
    71  	mountPoints := map[string]*volume.MountPoint{}
    72  
    73  	// 1. Read already configured mount points.
    74  	for name, point := range container.MountPoints {
    75  		mountPoints[name] = point
    76  	}
    77  
    78  	// 2. Read volumes from other containers.
    79  	for _, v := range hostConfig.VolumesFrom {
    80  		containerID, mode, err := volume.ParseVolumesFrom(v)
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		c, err := daemon.GetContainer(containerID)
    86  		if err != nil {
    87  			return err
    88  		}
    89  
    90  		for _, m := range c.MountPoints {
    91  			cp := &volume.MountPoint{
    92  				Name:        m.Name,
    93  				Source:      m.Source,
    94  				RW:          m.RW && volume.ReadWrite(mode),
    95  				Driver:      m.Driver,
    96  				Destination: m.Destination,
    97  				Propagation: m.Propagation,
    98  				Named:       m.Named,
    99  			}
   100  
   101  			if len(cp.Source) == 0 {
   102  				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
   103  				if err != nil {
   104  					return err
   105  				}
   106  				cp.Volume = v
   107  			}
   108  
   109  			mountPoints[cp.Destination] = cp
   110  		}
   111  	}
   112  
   113  	// 3. Read bind mounts
   114  	for _, b := range hostConfig.Binds {
   115  		// #10618
   116  		bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  		if binds[bind.Destination] {
   122  			return fmt.Errorf("Duplicate mount point '%s'", bind.Destination)
   123  		}
   124  
   125  		if len(bind.Name) > 0 {
   126  			// create the volume
   127  			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
   128  			if err != nil {
   129  				return err
   130  			}
   131  			bind.Volume = v
   132  			bind.Source = v.Path()
   133  			// bind.Name is an already existing volume, we need to use that here
   134  			bind.Driver = v.DriverName()
   135  			bind.Named = true
   136  			if bind.Driver == "local" {
   137  				bind = setBindModeIfNull(bind)
   138  			}
   139  		}
   140  
   141  		if label.RelabelNeeded(bind.Mode) {
   142  			if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
   143  				return err
   144  			}
   145  		}
   146  		binds[bind.Destination] = true
   147  		mountPoints[bind.Destination] = bind
   148  	}
   149  
   150  	container.Lock()
   151  
   152  	// 4. Cleanup old volumes that are about to be reassigned.
   153  	for _, m := range mountPoints {
   154  		if m.BackwardsCompatible() {
   155  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   156  				daemon.volumes.Dereference(mp.Volume, container.ID)
   157  			}
   158  		}
   159  	}
   160  	container.MountPoints = mountPoints
   161  
   162  	container.Unlock()
   163  
   164  	return nil
   165  }
   166  
   167  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   168  // This happens after a daemon restart.
   169  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
   170  	if len(m.Driver) > 0 && m.Volume == nil {
   171  		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		m.Volume = v
   176  	}
   177  	return nil
   178  }