github.com/docker/Engine@v17.12.1-ce-rc2+incompatible/volume/volume.go (about)

     1  package volume
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"syscall"
     8  	"time"
     9  
    10  	mounttypes "github.com/docker/docker/api/types/mount"
    11  	"github.com/docker/docker/pkg/idtools"
    12  	"github.com/docker/docker/pkg/stringid"
    13  	"github.com/opencontainers/selinux/go-selinux/label"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // DefaultDriverName is the driver name used for the driver
    18  // implemented in the local package.
    19  const DefaultDriverName = "local"
    20  
    21  // Scopes define if a volume has is cluster-wide (global) or local only.
    22  // Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
    23  const (
    24  	LocalScope  = "local"
    25  	GlobalScope = "global"
    26  )
    27  
    28  // Driver is for creating and removing volumes.
    29  type Driver interface {
    30  	// Name returns the name of the volume driver.
    31  	Name() string
    32  	// Create makes a new volume with the given name.
    33  	Create(name string, opts map[string]string) (Volume, error)
    34  	// Remove deletes the volume.
    35  	Remove(vol Volume) (err error)
    36  	// List lists all the volumes the driver has
    37  	List() ([]Volume, error)
    38  	// Get retrieves the volume with the requested name
    39  	Get(name string) (Volume, error)
    40  	// Scope returns the scope of the driver (e.g. `global` or `local`).
    41  	// Scope determines how the driver is handled at a cluster level
    42  	Scope() string
    43  }
    44  
    45  // Capability defines a set of capabilities that a driver is able to handle.
    46  type Capability struct {
    47  	// Scope is the scope of the driver, `global` or `local`
    48  	// A `global` scope indicates that the driver manages volumes across the cluster
    49  	// A `local` scope indicates that the driver only manages volumes resources local to the host
    50  	// Scope is declared by the driver
    51  	Scope string
    52  }
    53  
    54  // Volume is a place to store data. It is backed by a specific driver, and can be mounted.
    55  type Volume interface {
    56  	// Name returns the name of the volume
    57  	Name() string
    58  	// DriverName returns the name of the driver which owns this volume.
    59  	DriverName() string
    60  	// Path returns the absolute path to the volume.
    61  	Path() string
    62  	// Mount mounts the volume and returns the absolute path to
    63  	// where it can be consumed.
    64  	Mount(id string) (string, error)
    65  	// Unmount unmounts the volume when it is no longer in use.
    66  	Unmount(id string) error
    67  	// CreatedAt returns Volume Creation time
    68  	CreatedAt() (time.Time, error)
    69  	// Status returns low-level status information about a volume
    70  	Status() map[string]interface{}
    71  }
    72  
    73  // DetailedVolume wraps a Volume with user-defined labels, options, and cluster scope (e.g., `local` or `global`)
    74  type DetailedVolume interface {
    75  	Labels() map[string]string
    76  	Options() map[string]string
    77  	Scope() string
    78  	Volume
    79  }
    80  
    81  // MountPoint is the intersection point between a volume and a container. It
    82  // specifies which volume is to be used and where inside a container it should
    83  // be mounted.
    84  type MountPoint struct {
    85  	// Source is the source path of the mount.
    86  	// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
    87  	Source string
    88  	// Destination is the path relative to the container root (`/`) to the mount point
    89  	// It is where the `Source` is mounted to
    90  	Destination string
    91  	// RW is set to true when the mountpoint should be mounted as read-write
    92  	RW bool
    93  	// Name is the name reference to the underlying data defined by `Source`
    94  	// e.g., the volume name
    95  	Name string
    96  	// Driver is the volume driver used to create the volume (if it is a volume)
    97  	Driver string
    98  	// Type of mount to use, see `Type<foo>` definitions in github.com/docker/docker/api/types/mount
    99  	Type mounttypes.Type `json:",omitempty"`
   100  	// Volume is the volume providing data to this mountpoint.
   101  	// This is nil unless `Type` is set to `TypeVolume`
   102  	Volume Volume `json:"-"`
   103  
   104  	// Mode is the comma separated list of options supplied by the user when creating
   105  	// the bind/volume mount.
   106  	// Note Mode is not used on Windows
   107  	Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"
   108  
   109  	// Propagation describes how the mounts are propagated from the host into the
   110  	// mount point, and vice-versa.
   111  	// See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
   112  	// Note Propagation is not used on Windows
   113  	Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string
   114  
   115  	// Specifies if data should be copied from the container before the first mount
   116  	// Use a pointer here so we can tell if the user set this value explicitly
   117  	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
   118  	CopyData bool `json:"-"`
   119  	// ID is the opaque ID used to pass to the volume driver.
   120  	// This should be set by calls to `Mount` and unset by calls to `Unmount`
   121  	ID string `json:",omitempty"`
   122  
   123  	// Sepc is a copy of the API request that created this mount.
   124  	Spec mounttypes.Mount
   125  
   126  	// Track usage of this mountpoint
   127  	// Specifically needed for containers which are running and calls to `docker cp`
   128  	// because both these actions require mounting the volumes.
   129  	active int
   130  }
   131  
   132  // Cleanup frees resources used by the mountpoint
   133  func (m *MountPoint) Cleanup() error {
   134  	if m.Volume == nil || m.ID == "" {
   135  		return nil
   136  	}
   137  
   138  	if err := m.Volume.Unmount(m.ID); err != nil {
   139  		return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name())
   140  	}
   141  
   142  	m.active--
   143  	if m.active == 0 {
   144  		m.ID = ""
   145  	}
   146  	return nil
   147  }
   148  
   149  // Setup sets up a mount point by either mounting the volume if it is
   150  // configured, or creating the source directory if supplied.
   151  // The, optional, checkFun parameter allows doing additional checking
   152  // before creating the source directory on the host.
   153  func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) {
   154  	defer func() {
   155  		if err != nil || !label.RelabelNeeded(m.Mode) {
   156  			return
   157  		}
   158  
   159  		var sourcePath string
   160  		sourcePath, err = filepath.EvalSymlinks(m.Source)
   161  		if err != nil {
   162  			path = ""
   163  			err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source)
   164  			return
   165  		}
   166  		err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode))
   167  		if err == syscall.ENOTSUP {
   168  			err = nil
   169  		}
   170  		if err != nil {
   171  			path = ""
   172  			err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath)
   173  		}
   174  	}()
   175  
   176  	if m.Volume != nil {
   177  		id := m.ID
   178  		if id == "" {
   179  			id = stringid.GenerateNonCryptoID()
   180  		}
   181  		path, err := m.Volume.Mount(id)
   182  		if err != nil {
   183  			return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
   184  		}
   185  
   186  		m.ID = id
   187  		m.active++
   188  		return path, nil
   189  	}
   190  
   191  	if len(m.Source) == 0 {
   192  		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
   193  	}
   194  
   195  	if m.Type == mounttypes.TypeBind {
   196  		// Before creating the source directory on the host, invoke checkFun if it's not nil. One of
   197  		// the use case is to forbid creating the daemon socket as a directory if the daemon is in
   198  		// the process of shutting down.
   199  		if checkFun != nil {
   200  			if err := checkFun(m); err != nil {
   201  				return "", err
   202  			}
   203  		}
   204  		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
   205  		// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
   206  		if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil {
   207  			if perr, ok := err.(*os.PathError); ok {
   208  				if perr.Err != syscall.ENOTDIR {
   209  					return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
   210  				}
   211  			}
   212  		}
   213  	}
   214  	return m.Source, nil
   215  }
   216  
   217  // Path returns the path of a volume in a mount point.
   218  func (m *MountPoint) Path() string {
   219  	if m.Volume != nil {
   220  		return m.Volume.Path()
   221  	}
   222  	return m.Source
   223  }
   224  
   225  func errInvalidMode(mode string) error {
   226  	return errors.Errorf("invalid mode: %v", mode)
   227  }
   228  
   229  func errInvalidSpec(spec string) error {
   230  	return errors.Errorf("invalid volume specification: '%s'", spec)
   231  }