github.com/hms58/moby@v1.13.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/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  	// 1. Read already configured mount points.
    89  	for destination, point := range container.MountPoints {
    90  		mountPoints[destination] = point
    91  	}
    92  
    93  	// 2. Read volumes from other containers.
    94  	for _, v := range hostConfig.VolumesFrom {
    95  		containerID, mode, err := volume.ParseVolumesFrom(v)
    96  		if err != nil {
    97  			return err
    98  		}
    99  
   100  		c, err := daemon.GetContainer(containerID)
   101  		if err != nil {
   102  			return err
   103  		}
   104  
   105  		for _, m := range c.MountPoints {
   106  			cp := &volume.MountPoint{
   107  				Name:        m.Name,
   108  				Source:      m.Source,
   109  				RW:          m.RW && volume.ReadWrite(mode),
   110  				Driver:      m.Driver,
   111  				Destination: m.Destination,
   112  				Propagation: m.Propagation,
   113  				Spec:        m.Spec,
   114  				CopyData:    false,
   115  			}
   116  
   117  			if len(cp.Source) == 0 {
   118  				v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID)
   119  				if err != nil {
   120  					return err
   121  				}
   122  				cp.Volume = v
   123  			}
   124  
   125  			mountPoints[cp.Destination] = cp
   126  		}
   127  	}
   128  
   129  	// 3. Read bind mounts
   130  	for _, b := range hostConfig.Binds {
   131  		bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver)
   132  		if err != nil {
   133  			return err
   134  		}
   135  
   136  		// #10618
   137  		_, tmpfsExists := hostConfig.Tmpfs[bind.Destination]
   138  		if binds[bind.Destination] || tmpfsExists {
   139  			return fmt.Errorf("Duplicate mount point '%s'", bind.Destination)
   140  		}
   141  
   142  		if bind.Type == mounttypes.TypeVolume {
   143  			// create the volume
   144  			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
   145  			if err != nil {
   146  				return err
   147  			}
   148  			bind.Volume = v
   149  			bind.Source = v.Path()
   150  			// bind.Name is an already existing volume, we need to use that here
   151  			bind.Driver = v.DriverName()
   152  			if bind.Driver == volume.DefaultDriverName {
   153  				setBindModeIfNull(bind)
   154  			}
   155  		}
   156  
   157  		binds[bind.Destination] = true
   158  		mountPoints[bind.Destination] = bind
   159  	}
   160  
   161  	for _, cfg := range hostConfig.Mounts {
   162  		mp, err := volume.ParseMountSpec(cfg)
   163  		if err != nil {
   164  			return dockererrors.NewBadRequestError(err)
   165  		}
   166  
   167  		if binds[mp.Destination] {
   168  			return fmt.Errorf("Duplicate mount point '%s'", cfg.Target)
   169  		}
   170  
   171  		if mp.Type == mounttypes.TypeVolume {
   172  			var v volume.Volume
   173  			if cfg.VolumeOptions != nil {
   174  				var driverOpts map[string]string
   175  				if cfg.VolumeOptions.DriverConfig != nil {
   176  					driverOpts = cfg.VolumeOptions.DriverConfig.Options
   177  				}
   178  				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels)
   179  			} else {
   180  				v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil)
   181  			}
   182  			if err != nil {
   183  				return err
   184  			}
   185  
   186  			if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil {
   187  				return err
   188  			}
   189  			mp.Volume = v
   190  			mp.Name = v.Name()
   191  			mp.Driver = v.DriverName()
   192  
   193  			// only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow
   194  			if cv, ok := v.(interface {
   195  				CachedPath() string
   196  			}); ok {
   197  				mp.Source = cv.CachedPath()
   198  			}
   199  		}
   200  
   201  		binds[mp.Destination] = true
   202  		mountPoints[mp.Destination] = mp
   203  	}
   204  
   205  	container.Lock()
   206  
   207  	// 4. Cleanup old volumes that are about to be reassigned.
   208  	for _, m := range mountPoints {
   209  		if m.BackwardsCompatible() {
   210  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   211  				daemon.volumes.Dereference(mp.Volume, container.ID)
   212  			}
   213  		}
   214  	}
   215  	container.MountPoints = mountPoints
   216  
   217  	container.Unlock()
   218  
   219  	return nil
   220  }
   221  
   222  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   223  // This happens after a daemon restart.
   224  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error {
   225  	if len(m.Driver) > 0 && m.Volume == nil {
   226  		v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID)
   227  		if err != nil {
   228  			return err
   229  		}
   230  		m.Volume = v
   231  	}
   232  	return nil
   233  }
   234  
   235  func backportMountSpec(container *container.Container) error {
   236  	for target, m := range container.MountPoints {
   237  		if m.Spec.Type != "" {
   238  			// if type is set on even one mount, no need to migrate
   239  			return nil
   240  		}
   241  		if m.Name != "" {
   242  			m.Type = mounttypes.TypeVolume
   243  			m.Spec.Type = mounttypes.TypeVolume
   244  
   245  			// make sure this is not an anyonmous volume before setting the spec source
   246  			if _, exists := container.Config.Volumes[target]; !exists {
   247  				m.Spec.Source = m.Name
   248  			}
   249  			if container.HostConfig.VolumeDriver != "" {
   250  				m.Spec.VolumeOptions = &mounttypes.VolumeOptions{
   251  					DriverConfig: &mounttypes.Driver{Name: container.HostConfig.VolumeDriver},
   252  				}
   253  			}
   254  			if strings.Contains(m.Mode, "nocopy") {
   255  				if m.Spec.VolumeOptions == nil {
   256  					m.Spec.VolumeOptions = &mounttypes.VolumeOptions{}
   257  				}
   258  				m.Spec.VolumeOptions.NoCopy = true
   259  			}
   260  		} else {
   261  			m.Type = mounttypes.TypeBind
   262  			m.Spec.Type = mounttypes.TypeBind
   263  			m.Spec.Source = m.Source
   264  			if m.Propagation != "" {
   265  				m.Spec.BindOptions = &mounttypes.BindOptions{
   266  					Propagation: m.Propagation,
   267  				}
   268  			}
   269  		}
   270  
   271  		m.Spec.Target = m.Destination
   272  		if !m.RW {
   273  			m.Spec.ReadOnly = true
   274  		}
   275  	}
   276  	return container.ToDiskLocking()
   277  }
   278  
   279  func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error {
   280  	localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName)
   281  	if err != nil {
   282  		return fmt.Errorf("can't retrieve local volume driver: %v", err)
   283  	}
   284  	vols, err := localVolumeDriver.List()
   285  	if err != nil {
   286  		return fmt.Errorf("can't retrieve local volumes: %v", err)
   287  	}
   288  
   289  	for _, v := range vols {
   290  		name := v.Name()
   291  		_, err := daemon.volumes.Get(name)
   292  		if err != nil {
   293  			logrus.Warnf("failed to retrieve volume %s from store: %v", name, err)
   294  		}
   295  
   296  		err = fn(v)
   297  		if err != nil {
   298  			return err
   299  		}
   300  	}
   301  
   302  	return nil
   303  }