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