volume.go 7.7 KB

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