github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/daemon/volumes.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"time"
     9  
    10  	containertypes "github.com/docker/docker/api/types/container"
    11  	"github.com/docker/docker/api/types/mount"
    12  	mounttypes "github.com/docker/docker/api/types/mount"
    13  	volumetypes "github.com/docker/docker/api/types/volume"
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/errdefs"
    16  	"github.com/docker/docker/volume"
    17  	volumemounts "github.com/docker/docker/volume/mounts"
    18  	"github.com/docker/docker/volume/service"
    19  	volumeopts "github.com/docker/docker/volume/service/opts"
    20  	"github.com/pkg/errors"
    21  	"github.com/sirupsen/logrus"
    22  )
    23  
    24  var (
    25  	// ErrVolumeReadonly is used to signal an error when trying to copy data into
    26  	// a volume mount that is not writable.
    27  	ErrVolumeReadonly = errors.New("mounted volume is marked read-only")
    28  )
    29  
    30  type mounts []container.Mount
    31  
    32  // Len returns the number of mounts. Used in sorting.
    33  func (m mounts) Len() int {
    34  	return len(m)
    35  }
    36  
    37  // Less returns true if the number of parts (a/b/c would be 3 parts) in the
    38  // mount indexed by parameter 1 is less than that of the mount indexed by
    39  // parameter 2. Used in sorting.
    40  func (m mounts) Less(i, j int) bool {
    41  	return m.parts(i) < m.parts(j)
    42  }
    43  
    44  // Swap swaps two items in an array of mounts. Used in sorting
    45  func (m mounts) Swap(i, j int) {
    46  	m[i], m[j] = m[j], m[i]
    47  }
    48  
    49  // parts returns the number of parts in the destination of a mount. Used in sorting.
    50  func (m mounts) parts(i int) int {
    51  	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
    52  }
    53  
    54  // registerMountPoints initializes the container mount points with the configured volumes and bind mounts.
    55  // It follows the next sequence to decide what to mount in each final destination:
    56  //
    57  // 1. Select the previously configured mount points for the containers, if any.
    58  // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination.
    59  // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations.
    60  // 4. Cleanup old volumes that are about to be reassigned.
    61  func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
    62  	binds := map[string]bool{}
    63  	mountPoints := map[string]*volumemounts.MountPoint{}
    64  	parser := volumemounts.NewParser()
    65  
    66  	ctx := context.TODO()
    67  	defer func() {
    68  		// clean up the container mountpoints once return with error
    69  		if retErr != nil {
    70  			for _, m := range mountPoints {
    71  				if m.Volume == nil {
    72  					continue
    73  				}
    74  				daemon.volumes.Release(ctx, m.Volume.Name(), container.ID)
    75  			}
    76  		}
    77  	}()
    78  
    79  	dereferenceIfExists := func(destination string) {
    80  		if v, ok := mountPoints[destination]; ok {
    81  			logrus.Debugf("Duplicate mount point '%s'", destination)
    82  			if v.Volume != nil {
    83  				daemon.volumes.Release(ctx, v.Volume.Name(), 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 := parser.ParseVolumesFrom(v)
    96  		if err != nil {
    97  			return errdefs.InvalidParameter(err)
    98  		}
    99  
   100  		c, err := daemon.GetContainer(containerID)
   101  		if err != nil {
   102  			return errdefs.InvalidParameter(err)
   103  		}
   104  
   105  		for _, m := range c.MountPoints {
   106  			cp := &volumemounts.MountPoint{
   107  				Type:        m.Type,
   108  				Name:        m.Name,
   109  				Source:      m.Source,
   110  				RW:          m.RW && parser.ReadWrite(mode),
   111  				Driver:      m.Driver,
   112  				Destination: m.Destination,
   113  				Propagation: m.Propagation,
   114  				Spec:        m.Spec,
   115  				CopyData:    false,
   116  			}
   117  
   118  			if len(cp.Source) == 0 {
   119  				v, err := daemon.volumes.Get(ctx, cp.Name, volumeopts.WithGetDriver(cp.Driver), volumeopts.WithGetReference(container.ID))
   120  				if err != nil {
   121  					return err
   122  				}
   123  				cp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   124  			}
   125  			dereferenceIfExists(cp.Destination)
   126  			mountPoints[cp.Destination] = cp
   127  		}
   128  	}
   129  
   130  	// 3. Read bind mounts
   131  	for _, b := range hostConfig.Binds {
   132  		bind, err := parser.ParseMountRaw(b, hostConfig.VolumeDriver)
   133  		if err != nil {
   134  			return err
   135  		}
   136  		needsSlavePropagation, err := daemon.validateBindDaemonRoot(bind.Spec)
   137  		if err != nil {
   138  			return err
   139  		}
   140  		if needsSlavePropagation {
   141  			bind.Propagation = mount.PropagationRSlave
   142  		}
   143  
   144  		// #10618
   145  		_, tmpfsExists := hostConfig.Tmpfs[bind.Destination]
   146  		if binds[bind.Destination] || tmpfsExists {
   147  			return duplicateMountPointError(bind.Destination)
   148  		}
   149  
   150  		if bind.Type == mounttypes.TypeVolume {
   151  			// create the volume
   152  			v, err := daemon.volumes.Create(ctx, bind.Name, bind.Driver, volumeopts.WithCreateReference(container.ID))
   153  			if err != nil {
   154  				return err
   155  			}
   156  			bind.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   157  			bind.Source = v.Mountpoint
   158  			// bind.Name is an already existing volume, we need to use that here
   159  			bind.Driver = v.Driver
   160  			if bind.Driver == volume.DefaultDriverName {
   161  				setBindModeIfNull(bind)
   162  			}
   163  		}
   164  
   165  		binds[bind.Destination] = true
   166  		dereferenceIfExists(bind.Destination)
   167  		mountPoints[bind.Destination] = bind
   168  	}
   169  
   170  	for _, cfg := range hostConfig.Mounts {
   171  		mp, err := parser.ParseMountSpec(cfg)
   172  		if err != nil {
   173  			return errdefs.InvalidParameter(err)
   174  		}
   175  		needsSlavePropagation, err := daemon.validateBindDaemonRoot(mp.Spec)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		if needsSlavePropagation {
   180  			mp.Propagation = mount.PropagationRSlave
   181  		}
   182  
   183  		if binds[mp.Destination] {
   184  			return duplicateMountPointError(cfg.Target)
   185  		}
   186  
   187  		if mp.Type == mounttypes.TypeVolume {
   188  			var v *volumetypes.Volume
   189  			if cfg.VolumeOptions != nil {
   190  				var driverOpts map[string]string
   191  				if cfg.VolumeOptions.DriverConfig != nil {
   192  					driverOpts = cfg.VolumeOptions.DriverConfig.Options
   193  				}
   194  				v, err = daemon.volumes.Create(ctx,
   195  					mp.Name,
   196  					mp.Driver,
   197  					volumeopts.WithCreateReference(container.ID),
   198  					volumeopts.WithCreateOptions(driverOpts),
   199  					volumeopts.WithCreateLabels(cfg.VolumeOptions.Labels),
   200  				)
   201  			} else {
   202  				v, err = daemon.volumes.Create(ctx, mp.Name, mp.Driver, volumeopts.WithCreateReference(container.ID))
   203  			}
   204  			if err != nil {
   205  				return err
   206  			}
   207  
   208  			mp.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   209  			mp.Name = v.Name
   210  			mp.Driver = v.Driver
   211  
   212  			// need to selinux-relabel local mounts
   213  			mp.Source = v.Mountpoint
   214  			if mp.Driver == volume.DefaultDriverName {
   215  				setBindModeIfNull(mp)
   216  			}
   217  		}
   218  
   219  		if mp.Type == mounttypes.TypeBind && (cfg.BindOptions == nil || !cfg.BindOptions.CreateMountpoint) {
   220  			mp.SkipMountpointCreation = true
   221  		}
   222  
   223  		binds[mp.Destination] = true
   224  		dereferenceIfExists(mp.Destination)
   225  		mountPoints[mp.Destination] = mp
   226  	}
   227  
   228  	container.Lock()
   229  
   230  	// 4. Cleanup old volumes that are about to be reassigned.
   231  	for _, m := range mountPoints {
   232  		if parser.IsBackwardCompatible(m) {
   233  			if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil {
   234  				daemon.volumes.Release(ctx, mp.Volume.Name(), container.ID)
   235  			}
   236  		}
   237  	}
   238  	container.MountPoints = mountPoints
   239  
   240  	container.Unlock()
   241  
   242  	return nil
   243  }
   244  
   245  // lazyInitializeVolume initializes a mountpoint's volume if needed.
   246  // This happens after a daemon restart.
   247  func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error {
   248  	if len(m.Driver) > 0 && m.Volume == nil {
   249  		v, err := daemon.volumes.Get(context.TODO(), m.Name, volumeopts.WithGetDriver(m.Driver), volumeopts.WithGetReference(containerID))
   250  		if err != nil {
   251  			return err
   252  		}
   253  		m.Volume = &volumeWrapper{v: v, s: daemon.volumes}
   254  	}
   255  	return nil
   256  }
   257  
   258  // VolumesService is used to perform volume operations
   259  func (daemon *Daemon) VolumesService() *service.VolumesService {
   260  	return daemon.volumes
   261  }
   262  
   263  type volumeMounter interface {
   264  	Mount(ctx context.Context, v *volumetypes.Volume, ref string) (string, error)
   265  	Unmount(ctx context.Context, v *volumetypes.Volume, ref string) error
   266  }
   267  
   268  type volumeWrapper struct {
   269  	v *volumetypes.Volume
   270  	s volumeMounter
   271  }
   272  
   273  func (v *volumeWrapper) Name() string {
   274  	return v.v.Name
   275  }
   276  
   277  func (v *volumeWrapper) DriverName() string {
   278  	return v.v.Driver
   279  }
   280  
   281  func (v *volumeWrapper) Path() string {
   282  	return v.v.Mountpoint
   283  }
   284  
   285  func (v *volumeWrapper) Mount(ref string) (string, error) {
   286  	return v.s.Mount(context.TODO(), v.v, ref)
   287  }
   288  
   289  func (v *volumeWrapper) Unmount(ref string) error {
   290  	return v.s.Unmount(context.TODO(), v.v, ref)
   291  }
   292  
   293  func (v *volumeWrapper) CreatedAt() (time.Time, error) {
   294  	return time.Time{}, errors.New("not implemented")
   295  }
   296  
   297  func (v *volumeWrapper) Status() map[string]interface{} {
   298  	return v.v.Status
   299  }