volume.go 7.5 KB

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