github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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/Sirupsen/logrus"
    11  	dockererrors "github.com/docker/docker/api/errors"
    12  	"github.com/docker/docker/api/types"
    13  	containertypes "github.com/docker/docker/api/types/container"
    14  	mounttypes "github.com/docker/docker/api/types/mount"
    15  	"github.com/docker/docker/container"
    16  	"github.com/docker/docker/volume"
    17  	"github.com/docker/docker/volume/drivers"
    18  	"github.com/opencontainers/runc/libcontainer/label"
    19  )
    20  
    21  var (
    22  	// ErrVolumeReadonly is used to signal an error when trying to copy data into
    23  	// a volume mount that is not writable.
    24  	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
    25  )
    26  
    27  type mounts []container.Mount
    28  
    29  // volumeToAPIType converts a volume.Volume to the type used by the Engine API
    30  func volumeToAPIType(v volume.Volume) *types.Volume {
    31  	tv := &types.Volume{
    32  		Name:   v.Name(),
    33  		Driver: v.DriverName(),
    34  	}
    35  	if v, ok := v.(volume.DetailedVolume); ok {
    36  		tv.Labels = v.Labels()
    37  		tv.Options = v.Options()
    38  		tv.Scope = v.Scope()
    39  	}
    40  
    41  	return tv
    42  }
    43  
    44  // Len returns the number of mounts. Used in sorting.
    45  func (m mounts) Len() int {
    46  	return len(m)
    47  }
    48  
    49  // Less returns true if the number of parts (a/b/c would be 3 parts) in the
    50  // mount indexed by parameter 1 is less than that of the mount indexed by
    51  // parameter 2. Used in sorting.
    52  func (m mounts) Less(i, j int) bool {
    53  	return m.parts(i) < m.parts(j)
    54  }
    55  
    56  // Swap swaps two items in an array of mounts. Used in sorting
    57  func (m mounts) Swap(i, j int) {
    58  	m[i], m[j] = m[j], m[i]
    59  }
    60  
    61  // parts returns the number of parts in the destination of a mount. Used in sorting.
    62  func (m mounts) parts(i int) int {
    63  	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
    64  }
    65  
    66  // registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
    67  // It follows the next sequence to decide what to mount in each final destination:
    68  //
    69  // 1. Select the previously configured mount points for the containers, if any.
    70  // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
    71  // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
    72  // 4. Cleanup old volumes that are about to be reassigned.
    73  func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
    74  	binds := map[string]bool{}
    75  	mountPoints := map[string]*volume.MountPoint{}
    76  	defer func() {
    77  		// clean up the container mountpoints once return with error
    78  		if retErr != nil {
    79  			for _, m := range mountPoints {
    80  				if m.Volume == nil {
    81  					continue
    82  				}
    83  				daemon.volumes.Dereference(m.Volume, container.ID)
    84  			}
    85  		}
    86  	}()
    87  
    88  	dereferenceIfExists := func(destination string) {
    89  		if v, ok := mountPoints[destination]; ok {
    90  			logrus.Debugf("Duplicate mount point '%s'", destination)
    91  			if v.Volume != nil {
    92  				daemon.volumes.Dereference(v.Volume, container.ID)
    93  			}
    94  		}
    95  	}
    96  
    97  	// 1. Read already configured mount points.
    98  	for destination, point := range container.MountPoints {
    99  		mountPoints[destination] = point
   100  	}
   101  
   102  	// 2. Read volumes from other containers.
   103  	for _, v := range hostConfig.VolumesFrom {
   104  		containerID, mode, err := volume.ParseVolumesFrom(v)
   105  		if err != nil {
   106  			return err
   107  		}
   108  
   109  		c, err := daemon.GetContainer(containerID)
   110  		if err != nil {
   111  			return err
   112  		}
   113  
   114  		for _, m := range c.MountPoints {
   115  			cp := &volume.MountPoint{
   116  				Name:        m.Name,
   117  				Source:      m.Source,
   118  				RW:          m.RW && volume.ReadWrite(mode),
   119  				Driver:      m.Driver,
   120  				Destination: m.Destination,
   121  				Propagation: m.Propagation,
   122  				Spec:        m.Spec,
   123  				CopyData:    false,
   124  			}
   125  
   126  			if len(cp.Source) == 0 {
   127  				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
   128  				if err != nil {
   129  					return err
   130  				}
   131  				cp.Volume = v
   132  			}
   133  			dereferenceIfExists(cp.Destination)
   134  			mountPoints[cp.Destination] = cp
   135  		}
   136  	}
   137  
   138  	// 3. Read bind mounts
   139  	for _, b := range hostConfig.Binds {
   140  		bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver)
   141  		if err != nil {
   142  			return err
   143  		}
   144  
   145  		// #10618
   146  		_, tmpfsExists := hostConfig.Tmpfs[bind.Destination]
   147  		if binds[bind.Destination] || tmpfsExists {
   148  			return fmt.Errorf("Duplicate mount point '%s'", bind.Destination)
   149  		}
   150  
   151  		if bind.Type == mounttypes.TypeVolume {
   152  			// create the volume
   153  			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
   154  			if err != nil {
   155  				return err
   156  			}
   157  			bind.Volume = v
   158  			bind.Source = v.Path()
   159  			// bind.Name is an already existing volume, we need to use that here
   160  			bind.Driver = v.DriverName()
   161  			if bind.Driver == volume.DefaultDriverName {
   162  				setBindModeIfNull(bind)
   163  			}
   164  		}
   165  
   166  		binds[bind.Destination] = true
   167  		dereferenceIfExists(bind.Destination)
   168  		mountPoints[bind.Destination] = bind
   169  	}
   170  
   171  	for _, cfg := range hostConfig.Mounts {
   172  		mp, err := volume.ParseMountSpec(cfg)
   173  		if err != nil {
   174  			return dockererrors.NewBadRequestError(err)
   175  		}
   176  
   177  		if binds[mp.Destination] {
   178  			return fmt.Errorf("Duplicate mount point '%s'", cfg.Target)
   179  		}
   180  
   181  		if mp.Type == mounttypes.TypeVolume {
   182  			var v volume.Volume
   183  			if cfg.VolumeOptions != nil {
   184  				var driverOpts map[string]string
   185  				if cfg.VolumeOptions.DriverConfig != nil {
   186  					driverOpts = cfg.VolumeOptions.DriverConfig.Options
   187  				}
   188  				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels)
   189  			} else {
   190  				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil)
   191  			}
   192  			if err != nil {
   193  				return err
   194  			}
   195  
   196  			if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil {
   197  				return err
   198  			}
   199  			mp.Volume = v
   200  			mp.Name = v.Name()
   201  			mp.Driver = v.DriverName()
   202  
   203  			// only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow
   204  			if cv, ok := v.(interface {
   205  				CachedPath() string
   206  			}); ok {
   207  				mp.Source = cv.CachedPath()
   208  			}
   209  		}
   210  
   211  		binds[mp.Destination] = true
   212  		dereferenceIfExists(mp.Destination)
   213  		mountPoints[mp.Destination] = mp
   214  	}
   215  
   216  	container.Lock()
   217  
   218  	// 4. Cleanup old volumes that are about to be reassigned.
   219  	for _, m := range mountPoints {
   220  		if m.BackwardsCompatible() {
   221  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   222  				daemon.volumes.Dereference(mp.Volume, container.ID)
   223  			}
   224  		}
   225  	}
   226  	container.MountPoints = mountPoints
   227  
   228  	container.Unlock()
   229  
   230  	return nil
   231  }
   232  
   233  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   234  // This happens after a daemon restart.
   235  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
   236  	if len(m.Driver) > 0 && m.Volume == nil {
   237  		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
   238  		if err != nil {
   239  			return err
   240  		}
   241  		m.Volume = v
   242  	}
   243  	return nil
   244  }
   245  
   246  func backportMountSpec(container *container.Container) error {
   247  	for target, m := range container.MountPoints {
   248  		if m.Spec.Type != "" {
   249  			// if type is set on even one mount, no need to migrate
   250  			return nil
   251  		}
   252  		if m.Name != "" {
   253  			m.Type = mounttypes.TypeVolume
   254  			m.Spec.Type = mounttypes.TypeVolume
   255  
   256  			// make sure this is not an anyonmous volume before setting the spec source
   257  			if _, exists := container.Config.Volumes[target]; !exists {
   258  				m.Spec.Source = m.Name
   259  			}
   260  			if container.HostConfig.VolumeDriver != "" {
   261  				m.Spec.VolumeOptions = &mounttypes.VolumeOptions{
   262  					DriverConfig: &mounttypes.Driver{Name: container.HostConfig.VolumeDriver},
   263  				}
   264  			}
   265  			if strings.Contains(m.Mode, "nocopy") {
   266  				if m.Spec.VolumeOptions == nil {
   267  					m.Spec.VolumeOptions = &mounttypes.VolumeOptions{}
   268  				}
   269  				m.Spec.VolumeOptions.NoCopy = true
   270  			}
   271  		} else {
   272  			m.Type = mounttypes.TypeBind
   273  			m.Spec.Type = mounttypes.TypeBind
   274  			m.Spec.Source = m.Source
   275  			if m.Propagation != "" {
   276  				m.Spec.BindOptions = &mounttypes.BindOptions{
   277  					Propagation: m.Propagation,
   278  				}
   279  			}
   280  		}
   281  
   282  		m.Spec.Target = m.Destination
   283  		if !m.RW {
   284  			m.Spec.ReadOnly = true
   285  		}
   286  	}
   287  	return container.ToDiskLocking()
   288  }
   289  
   290  func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error {
   291  	localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName)
   292  	if err != nil {
   293  		return fmt.Errorf("can't retrieve local volume driver: %v", err)
   294  	}
   295  	vols, err := localVolumeDriver.List()
   296  	if err != nil {
   297  		return fmt.Errorf("can't retrieve local volumes: %v", err)
   298  	}
   299  
   300  	for _, v := range vols {
   301  		name := v.Name()
   302  		_, err := daemon.volumes.Get(name)
   303  		if err != nil {
   304  			logrus.Warnf("failed to retrieve volume %s from store: %v", name, err)
   305  		}
   306  
   307  		err = fn(v)
   308  		if err != nil {
   309  			return err
   310  		}
   311  	}
   312  
   313  	return nil
   314  }