mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 02:45:42 +03:00
vendor docker/docker volume utils
This commit is contained in:
34
vendor/github.com/docker/docker/volume/mounts/lcow_parser.go
generated
vendored
Normal file
34
vendor/github.com/docker/docker/volume/mounts/lcow_parser.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
)
|
||||
|
||||
var lcowSpecificValidators mountValidator = func(m *mount.Mount) error {
|
||||
if path.Clean(m.Target) == "/" {
|
||||
return ErrVolumeTargetIsRoot
|
||||
}
|
||||
if m.Type == mount.TypeNamedPipe {
|
||||
return errors.New("Linux containers on Windows do not support named pipe mounts")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type lcowParser struct {
|
||||
windowsParser
|
||||
}
|
||||
|
||||
func (p *lcowParser) ValidateMountConfig(mnt *mount.Mount) error {
|
||||
return p.validateMountConfigReg(mnt, rxLCOWDestination, lcowSpecificValidators)
|
||||
}
|
||||
|
||||
func (p *lcowParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
|
||||
return p.parseMountRaw(raw, volumeDriver, rxLCOWDestination, false, lcowSpecificValidators)
|
||||
}
|
||||
|
||||
func (p *lcowParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
|
||||
return p.parseMountSpec(cfg, rxLCOWDestination, false, lcowSpecificValidators)
|
||||
}
|
||||
420
vendor/github.com/docker/docker/volume/mounts/linux_parser.go
generated
vendored
Normal file
420
vendor/github.com/docker/docker/volume/mounts/linux_parser.go
generated
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/volume"
|
||||
)
|
||||
|
||||
type linuxParser struct {
|
||||
}
|
||||
|
||||
func linuxSplitRawSpec(raw string) ([]string, error) {
|
||||
if strings.Count(raw, ":") > 2 {
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
|
||||
arr := strings.SplitN(raw, ":", 3)
|
||||
if arr[0] == "" {
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func linuxValidateNotRoot(p string) error {
|
||||
p = path.Clean(strings.Replace(p, `\`, `/`, -1))
|
||||
if p == "/" {
|
||||
return ErrVolumeTargetIsRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func linuxValidateAbsolute(p string) error {
|
||||
p = strings.Replace(p, `\`, `/`, -1)
|
||||
if path.IsAbs(p) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
|
||||
}
|
||||
func (p *linuxParser) ValidateMountConfig(mnt *mount.Mount) error {
|
||||
// there was something looking like a bug in existing codebase:
|
||||
// - validateMountConfig on linux was called with options skipping bind source existence when calling ParseMountRaw
|
||||
// - but not when calling ParseMountSpec directly... nor when the unit test called it directly
|
||||
return p.validateMountConfigImpl(mnt, true)
|
||||
}
|
||||
func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSourceExists bool) error {
|
||||
if len(mnt.Target) == 0 {
|
||||
return &errMountConfig{mnt, errMissingField("Target")}
|
||||
}
|
||||
|
||||
if err := linuxValidateNotRoot(mnt.Target); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
|
||||
if err := linuxValidateAbsolute(mnt.Target); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
|
||||
switch mnt.Type {
|
||||
case mount.TypeBind:
|
||||
if len(mnt.Source) == 0 {
|
||||
return &errMountConfig{mnt, errMissingField("Source")}
|
||||
}
|
||||
// Don't error out just because the propagation mode is not supported on the platform
|
||||
if opts := mnt.BindOptions; opts != nil {
|
||||
if len(opts.Propagation) > 0 && len(linuxPropagationModes) > 0 {
|
||||
if _, ok := linuxPropagationModes[opts.Propagation]; !ok {
|
||||
return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)}
|
||||
}
|
||||
}
|
||||
}
|
||||
if mnt.VolumeOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("VolumeOptions")}
|
||||
}
|
||||
|
||||
if err := linuxValidateAbsolute(mnt.Source); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
|
||||
if validateBindSourceExists {
|
||||
exists, _, _ := currentFileInfoProvider.fileInfo(mnt.Source)
|
||||
if !exists {
|
||||
return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
|
||||
}
|
||||
}
|
||||
|
||||
case mount.TypeVolume:
|
||||
if mnt.BindOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("BindOptions")}
|
||||
}
|
||||
|
||||
if len(mnt.Source) == 0 && mnt.ReadOnly {
|
||||
return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")}
|
||||
}
|
||||
case mount.TypeTmpfs:
|
||||
if mnt.BindOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("BindOptions")}
|
||||
}
|
||||
if len(mnt.Source) != 0 {
|
||||
return &errMountConfig{mnt, errExtraField("Source")}
|
||||
}
|
||||
if _, err := p.ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
default:
|
||||
return &errMountConfig{mnt, errors.New("mount type unknown")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// read-write modes
|
||||
var rwModes = map[string]bool{
|
||||
"rw": true,
|
||||
"ro": true,
|
||||
}
|
||||
|
||||
// label modes
|
||||
var linuxLabelModes = map[string]bool{
|
||||
"Z": true,
|
||||
"z": true,
|
||||
}
|
||||
|
||||
// consistency modes
|
||||
var linuxConsistencyModes = map[mount.Consistency]bool{
|
||||
mount.ConsistencyFull: true,
|
||||
mount.ConsistencyCached: true,
|
||||
mount.ConsistencyDelegated: true,
|
||||
}
|
||||
var linuxPropagationModes = map[mount.Propagation]bool{
|
||||
mount.PropagationPrivate: true,
|
||||
mount.PropagationRPrivate: true,
|
||||
mount.PropagationSlave: true,
|
||||
mount.PropagationRSlave: true,
|
||||
mount.PropagationShared: true,
|
||||
mount.PropagationRShared: true,
|
||||
}
|
||||
|
||||
const linuxDefaultPropagationMode = mount.PropagationRPrivate
|
||||
|
||||
func linuxGetPropagation(mode string) mount.Propagation {
|
||||
for _, o := range strings.Split(mode, ",") {
|
||||
prop := mount.Propagation(o)
|
||||
if linuxPropagationModes[prop] {
|
||||
return prop
|
||||
}
|
||||
}
|
||||
return linuxDefaultPropagationMode
|
||||
}
|
||||
|
||||
func linuxHasPropagation(mode string) bool {
|
||||
for _, o := range strings.Split(mode, ",") {
|
||||
if linuxPropagationModes[mount.Propagation(o)] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func linuxValidMountMode(mode string) bool {
|
||||
if mode == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
rwModeCount := 0
|
||||
labelModeCount := 0
|
||||
propagationModeCount := 0
|
||||
copyModeCount := 0
|
||||
consistencyModeCount := 0
|
||||
|
||||
for _, o := range strings.Split(mode, ",") {
|
||||
switch {
|
||||
case rwModes[o]:
|
||||
rwModeCount++
|
||||
case linuxLabelModes[o]:
|
||||
labelModeCount++
|
||||
case linuxPropagationModes[mount.Propagation(o)]:
|
||||
propagationModeCount++
|
||||
case copyModeExists(o):
|
||||
copyModeCount++
|
||||
case linuxConsistencyModes[mount.Consistency(o)]:
|
||||
consistencyModeCount++
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Only one string for each mode is allowed.
|
||||
if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 || consistencyModeCount > 1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *linuxParser) ReadWrite(mode string) bool {
|
||||
if !linuxValidMountMode(mode) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, o := range strings.Split(mode, ",") {
|
||||
if o == "ro" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
|
||||
arr, err := linuxSplitRawSpec(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec mount.Mount
|
||||
var mode string
|
||||
switch len(arr) {
|
||||
case 1:
|
||||
// Just a destination path in the container
|
||||
spec.Target = arr[0]
|
||||
case 2:
|
||||
if linuxValidMountMode(arr[1]) {
|
||||
// Destination + Mode is not a valid volume - volumes
|
||||
// cannot include a mode. e.g. /foo:rw
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
// Host Source Path or Name + Destination
|
||||
spec.Source = arr[0]
|
||||
spec.Target = arr[1]
|
||||
case 3:
|
||||
// HostSourcePath+DestinationPath+Mode
|
||||
spec.Source = arr[0]
|
||||
spec.Target = arr[1]
|
||||
mode = arr[2]
|
||||
default:
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
|
||||
if !linuxValidMountMode(mode) {
|
||||
return nil, errInvalidMode(mode)
|
||||
}
|
||||
|
||||
if path.IsAbs(spec.Source) {
|
||||
spec.Type = mount.TypeBind
|
||||
} else {
|
||||
spec.Type = mount.TypeVolume
|
||||
}
|
||||
|
||||
spec.ReadOnly = !p.ReadWrite(mode)
|
||||
|
||||
// cannot assume that if a volume driver is passed in that we should set it
|
||||
if volumeDriver != "" && spec.Type == mount.TypeVolume {
|
||||
spec.VolumeOptions = &mount.VolumeOptions{
|
||||
DriverConfig: &mount.Driver{Name: volumeDriver},
|
||||
}
|
||||
}
|
||||
|
||||
if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
|
||||
if spec.VolumeOptions == nil {
|
||||
spec.VolumeOptions = &mount.VolumeOptions{}
|
||||
}
|
||||
spec.VolumeOptions.NoCopy = !copyData
|
||||
}
|
||||
if linuxHasPropagation(mode) {
|
||||
spec.BindOptions = &mount.BindOptions{
|
||||
Propagation: linuxGetPropagation(mode),
|
||||
}
|
||||
}
|
||||
|
||||
mp, err := p.parseMountSpec(spec, false)
|
||||
if mp != nil {
|
||||
mp.Mode = mode
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
|
||||
}
|
||||
return mp, err
|
||||
}
|
||||
func (p *linuxParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
|
||||
return p.parseMountSpec(cfg, true)
|
||||
}
|
||||
func (p *linuxParser) parseMountSpec(cfg mount.Mount, validateBindSourceExists bool) (*MountPoint, error) {
|
||||
if err := p.validateMountConfigImpl(&cfg, validateBindSourceExists); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp := &MountPoint{
|
||||
RW: !cfg.ReadOnly,
|
||||
Destination: path.Clean(filepath.ToSlash(cfg.Target)),
|
||||
Type: cfg.Type,
|
||||
Spec: cfg,
|
||||
}
|
||||
|
||||
switch cfg.Type {
|
||||
case mount.TypeVolume:
|
||||
if cfg.Source == "" {
|
||||
mp.Name = stringid.GenerateNonCryptoID()
|
||||
} else {
|
||||
mp.Name = cfg.Source
|
||||
}
|
||||
mp.CopyData = p.DefaultCopyMode()
|
||||
|
||||
if cfg.VolumeOptions != nil {
|
||||
if cfg.VolumeOptions.DriverConfig != nil {
|
||||
mp.Driver = cfg.VolumeOptions.DriverConfig.Name
|
||||
}
|
||||
if cfg.VolumeOptions.NoCopy {
|
||||
mp.CopyData = false
|
||||
}
|
||||
}
|
||||
case mount.TypeBind:
|
||||
mp.Source = path.Clean(filepath.ToSlash(cfg.Source))
|
||||
if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 {
|
||||
mp.Propagation = cfg.BindOptions.Propagation
|
||||
} else {
|
||||
// If user did not specify a propagation mode, get
|
||||
// default propagation mode.
|
||||
mp.Propagation = linuxDefaultPropagationMode
|
||||
}
|
||||
case mount.TypeTmpfs:
|
||||
// NOP
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) {
|
||||
if len(spec) == 0 {
|
||||
return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
|
||||
}
|
||||
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
id := specParts[0]
|
||||
mode := "rw"
|
||||
|
||||
if len(specParts) == 2 {
|
||||
mode = specParts[1]
|
||||
if !linuxValidMountMode(mode) {
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
// For now don't allow propagation properties while importing
|
||||
// volumes from data container. These volumes will inherit
|
||||
// the same propagation property as of the original volume
|
||||
// in data container. This probably can be relaxed in future.
|
||||
if linuxHasPropagation(mode) {
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
// Do not allow copy modes on volumes-from
|
||||
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
}
|
||||
return id, mode, nil
|
||||
}
|
||||
|
||||
func (p *linuxParser) DefaultPropagationMode() mount.Propagation {
|
||||
return linuxDefaultPropagationMode
|
||||
}
|
||||
|
||||
func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
|
||||
var rawOpts []string
|
||||
if readOnly {
|
||||
rawOpts = append(rawOpts, "ro")
|
||||
}
|
||||
|
||||
if opt != nil && opt.Mode != 0 {
|
||||
rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode))
|
||||
}
|
||||
|
||||
if opt != nil && opt.SizeBytes != 0 {
|
||||
// calculate suffix here, making this linux specific, but that is
|
||||
// okay, since API is that way anyways.
|
||||
|
||||
// we do this by finding the suffix that divides evenly into the
|
||||
// value, returning the value itself, with no suffix, if it fails.
|
||||
//
|
||||
// For the most part, we don't enforce any semantic to this values.
|
||||
// The operating system will usually align this and enforce minimum
|
||||
// and maximums.
|
||||
var (
|
||||
size = opt.SizeBytes
|
||||
suffix string
|
||||
)
|
||||
for _, r := range []struct {
|
||||
suffix string
|
||||
divisor int64
|
||||
}{
|
||||
{"g", 1 << 30},
|
||||
{"m", 1 << 20},
|
||||
{"k", 1 << 10},
|
||||
} {
|
||||
if size%r.divisor == 0 {
|
||||
size = size / r.divisor
|
||||
suffix = r.suffix
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix))
|
||||
}
|
||||
return strings.Join(rawOpts, ","), nil
|
||||
}
|
||||
|
||||
func (p *linuxParser) DefaultCopyMode() bool {
|
||||
return true
|
||||
}
|
||||
func (p *linuxParser) ValidateVolumeName(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool {
|
||||
return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName
|
||||
}
|
||||
|
||||
func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error {
|
||||
if err := linuxValidateNotRoot(dest); err != nil {
|
||||
return err
|
||||
}
|
||||
return linuxValidateAbsolute(dest)
|
||||
}
|
||||
181
vendor/github.com/docker/docker/volume/mounts/mounts.go
generated
vendored
Normal file
181
vendor/github.com/docker/docker/volume/mounts/mounts.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MountPoint is the intersection point between a volume and a container. It
|
||||
// specifies which volume is to be used and where inside a container it should
|
||||
// be mounted.
|
||||
//
|
||||
// Note that this type is embedded in `container.Container` object and persisted to disk.
|
||||
// Changes to this struct need to by synced with on disk state.
|
||||
type MountPoint struct {
|
||||
// Source is the source path of the mount.
|
||||
// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
|
||||
Source string
|
||||
// Destination is the path relative to the container root (`/`) to the mount point
|
||||
// It is where the `Source` is mounted to
|
||||
Destination string
|
||||
// RW is set to true when the mountpoint should be mounted as read-write
|
||||
RW bool
|
||||
// Name is the name reference to the underlying data defined by `Source`
|
||||
// e.g., the volume name
|
||||
Name string
|
||||
// Driver is the volume driver used to create the volume (if it is a volume)
|
||||
Driver string
|
||||
// Type of mount to use, see `Type<foo>` definitions in github.com/docker/docker/api/types/mount
|
||||
Type mounttypes.Type `json:",omitempty"`
|
||||
// Volume is the volume providing data to this mountpoint.
|
||||
// This is nil unless `Type` is set to `TypeVolume`
|
||||
Volume volume.Volume `json:"-"`
|
||||
|
||||
// Mode is the comma separated list of options supplied by the user when creating
|
||||
// the bind/volume mount.
|
||||
// Note Mode is not used on Windows
|
||||
Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"
|
||||
|
||||
// Propagation describes how the mounts are propagated from the host into the
|
||||
// mount point, and vice-versa.
|
||||
// See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
|
||||
// Note Propagation is not used on Windows
|
||||
Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string
|
||||
|
||||
// Specifies if data should be copied from the container before the first mount
|
||||
// Use a pointer here so we can tell if the user set this value explicitly
|
||||
// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
|
||||
CopyData bool `json:"-"`
|
||||
// ID is the opaque ID used to pass to the volume driver.
|
||||
// This should be set by calls to `Mount` and unset by calls to `Unmount`
|
||||
ID string `json:",omitempty"`
|
||||
|
||||
// Sepc is a copy of the API request that created this mount.
|
||||
Spec mounttypes.Mount
|
||||
|
||||
// Some bind mounts should not be automatically created.
|
||||
// (Some are auto-created for backwards-compatibility)
|
||||
// This is checked on the API but setting this here prevents race conditions.
|
||||
// where a bind dir existed during validation was removed before reaching the setup code.
|
||||
SkipMountpointCreation bool
|
||||
|
||||
// Track usage of this mountpoint
|
||||
// Specifically needed for containers which are running and calls to `docker cp`
|
||||
// because both these actions require mounting the volumes.
|
||||
active int
|
||||
}
|
||||
|
||||
// Cleanup frees resources used by the mountpoint
|
||||
func (m *MountPoint) Cleanup() error {
|
||||
if m.Volume == nil || m.ID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Volume.Unmount(m.ID); err != nil {
|
||||
return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name())
|
||||
}
|
||||
|
||||
m.active--
|
||||
if m.active == 0 {
|
||||
m.ID = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setup sets up a mount point by either mounting the volume if it is
|
||||
// configured, or creating the source directory if supplied.
|
||||
// The, optional, checkFun parameter allows doing additional checking
|
||||
// before creating the source directory on the host.
|
||||
func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, err error) {
|
||||
if m.SkipMountpointCreation {
|
||||
return m.Source, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil || !label.RelabelNeeded(m.Mode) {
|
||||
return
|
||||
}
|
||||
|
||||
var sourcePath string
|
||||
sourcePath, err = filepath.EvalSymlinks(m.Source)
|
||||
if err != nil {
|
||||
path = ""
|
||||
err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source)
|
||||
return
|
||||
}
|
||||
err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode))
|
||||
if err == syscall.ENOTSUP {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
path = ""
|
||||
err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath)
|
||||
}
|
||||
}()
|
||||
|
||||
if m.Volume != nil {
|
||||
id := m.ID
|
||||
if id == "" {
|
||||
id = stringid.GenerateNonCryptoID()
|
||||
}
|
||||
path, err := m.Volume.Mount(id)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
|
||||
}
|
||||
|
||||
m.ID = id
|
||||
m.active++
|
||||
return path, nil
|
||||
}
|
||||
|
||||
if len(m.Source) == 0 {
|
||||
return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
|
||||
}
|
||||
|
||||
if m.Type == mounttypes.TypeBind {
|
||||
// Before creating the source directory on the host, invoke checkFun if it's not nil. One of
|
||||
// the use case is to forbid creating the daemon socket as a directory if the daemon is in
|
||||
// the process of shutting down.
|
||||
if checkFun != nil {
|
||||
if err := checkFun(m); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
|
||||
// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
|
||||
if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil {
|
||||
if perr, ok := err.(*os.PathError); ok {
|
||||
if perr.Err != syscall.ENOTDIR {
|
||||
return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.Source, nil
|
||||
}
|
||||
|
||||
// Path returns the path of a volume in a mount point.
|
||||
func (m *MountPoint) Path() string {
|
||||
if m.Volume != nil {
|
||||
return m.Volume.Path()
|
||||
}
|
||||
return m.Source
|
||||
}
|
||||
|
||||
func errInvalidMode(mode string) error {
|
||||
return errors.Errorf("invalid mode: %v", mode)
|
||||
}
|
||||
|
||||
func errInvalidSpec(spec string) error {
|
||||
return errors.Errorf("invalid volume specification: '%s'", spec)
|
||||
}
|
||||
47
vendor/github.com/docker/docker/volume/mounts/parser.go
generated
vendored
Normal file
47
vendor/github.com/docker/docker/volume/mounts/parser.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
)
|
||||
|
||||
const (
|
||||
// OSLinux is the same as runtime.GOOS on linux
|
||||
OSLinux = "linux"
|
||||
// OSWindows is the same as runtime.GOOS on windows
|
||||
OSWindows = "windows"
|
||||
)
|
||||
|
||||
// ErrVolumeTargetIsRoot is returned when the target destination is root.
|
||||
// It's used by both LCOW and Linux parsers.
|
||||
var ErrVolumeTargetIsRoot = errors.New("invalid specification: destination can't be '/'")
|
||||
|
||||
// Parser represents a platform specific parser for mount expressions
|
||||
type Parser interface {
|
||||
ParseMountRaw(raw, volumeDriver string) (*MountPoint, error)
|
||||
ParseMountSpec(cfg mount.Mount) (*MountPoint, error)
|
||||
ParseVolumesFrom(spec string) (string, string, error)
|
||||
DefaultPropagationMode() mount.Propagation
|
||||
ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error)
|
||||
DefaultCopyMode() bool
|
||||
ValidateVolumeName(name string) error
|
||||
ReadWrite(mode string) bool
|
||||
IsBackwardCompatible(m *MountPoint) bool
|
||||
HasResource(m *MountPoint, absPath string) bool
|
||||
ValidateTmpfsMountDestination(dest string) error
|
||||
ValidateMountConfig(mt *mount.Mount) error
|
||||
}
|
||||
|
||||
// NewParser creates a parser for a given container OS, depending on the current host OS (linux on a windows host will resolve to an lcowParser)
|
||||
func NewParser(containerOS string) Parser {
|
||||
switch containerOS {
|
||||
case OSWindows:
|
||||
return &windowsParser{}
|
||||
}
|
||||
if runtime.GOOS == OSWindows {
|
||||
return &lcowParser{}
|
||||
}
|
||||
return &linuxParser{}
|
||||
}
|
||||
28
vendor/github.com/docker/docker/volume/mounts/validate.go
generated
vendored
Normal file
28
vendor/github.com/docker/docker/volume/mounts/validate.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type errMountConfig struct {
|
||||
mount *mount.Mount
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *errMountConfig) Error() string {
|
||||
return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error())
|
||||
}
|
||||
|
||||
func errBindSourceDoesNotExist(path string) error {
|
||||
return errors.Errorf("bind source path does not exist: %s", path)
|
||||
}
|
||||
|
||||
func errExtraField(name string) error {
|
||||
return errors.Errorf("field %s must not be specified", name)
|
||||
}
|
||||
func errMissingField(name string) error {
|
||||
return errors.Errorf("field %s must not be empty", name)
|
||||
}
|
||||
23
vendor/github.com/docker/docker/volume/mounts/volume_copy.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/volume/mounts/volume_copy.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import "strings"
|
||||
|
||||
// {<copy mode>=isEnabled}
|
||||
var copyModes = map[string]bool{
|
||||
"nocopy": false,
|
||||
}
|
||||
|
||||
func copyModeExists(mode string) bool {
|
||||
_, exists := copyModes[mode]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetCopyMode gets the copy mode from the mode string for mounts
|
||||
func getCopyMode(mode string, def bool) (bool, bool) {
|
||||
for _, o := range strings.Split(mode, ",") {
|
||||
if isEnabled, exists := copyModes[o]; exists {
|
||||
return isEnabled, true
|
||||
}
|
||||
}
|
||||
return def, false
|
||||
}
|
||||
18
vendor/github.com/docker/docker/volume/mounts/volume_unix.go
generated
vendored
Normal file
18
vendor/github.com/docker/docker/volume/mounts/volume_unix.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// +build linux freebsd darwin
|
||||
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *linuxParser) HasResource(m *MountPoint, absolutePath string) bool {
|
||||
relPath, err := filepath.Rel(m.Destination, absolutePath)
|
||||
return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator))
|
||||
}
|
||||
|
||||
func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool {
|
||||
return false
|
||||
}
|
||||
8
vendor/github.com/docker/docker/volume/mounts/volume_windows.go
generated
vendored
Normal file
8
vendor/github.com/docker/docker/volume/mounts/volume_windows.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool {
|
||||
return false
|
||||
}
|
||||
func (p *linuxParser) HasResource(m *MountPoint, absolutePath string) bool {
|
||||
return false
|
||||
}
|
||||
456
vendor/github.com/docker/docker/volume/mounts/windows_parser.go
generated
vendored
Normal file
456
vendor/github.com/docker/docker/volume/mounts/windows_parser.go
generated
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
package mounts // import "github.com/docker/docker/volume/mounts"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
||||
type windowsParser struct {
|
||||
}
|
||||
|
||||
const (
|
||||
// Spec should be in the format [source:]destination[:mode]
|
||||
//
|
||||
// Examples: c:\foo bar:d:rw
|
||||
// c:\foo:d:\bar
|
||||
// myname:d:
|
||||
// d:\
|
||||
//
|
||||
// Explanation of this regex! Thanks @thaJeztah on IRC and gist for help. See
|
||||
// https://gist.github.com/thaJeztah/6185659e4978789fb2b2. A good place to
|
||||
// test is https://regex-golang.appspot.com/assets/html/index.html
|
||||
//
|
||||
// Useful link for referencing named capturing groups:
|
||||
// http://stackoverflow.com/questions/20750843/using-named-matches-from-go-regex
|
||||
//
|
||||
// There are three match groups: source, destination and mode.
|
||||
//
|
||||
|
||||
// rxHostDir is the first option of a source
|
||||
rxHostDir = `(?:\\\\\?\\)?[a-z]:[\\/](?:[^\\/:*?"<>|\r\n]+[\\/]?)*`
|
||||
// rxName is the second option of a source
|
||||
rxName = `[^\\/:*?"<>|\r\n]+`
|
||||
|
||||
// RXReservedNames are reserved names not possible on Windows
|
||||
rxReservedNames = `(con)|(prn)|(nul)|(aux)|(com[1-9])|(lpt[1-9])`
|
||||
|
||||
// rxPipe is a named path pipe (starts with `\\.\pipe\`, possibly with / instead of \)
|
||||
rxPipe = `[/\\]{2}.[/\\]pipe[/\\][^:*?"<>|\r\n]+`
|
||||
// rxSource is the combined possibilities for a source
|
||||
rxSource = `((?P<source>((` + rxHostDir + `)|(` + rxName + `)|(` + rxPipe + `))):)?`
|
||||
|
||||
// Source. Can be either a host directory, a name, or omitted:
|
||||
// HostDir:
|
||||
// - Essentially using the folder solution from
|
||||
// https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch08s18.html
|
||||
// but adding case insensitivity.
|
||||
// - Must be an absolute path such as c:\path
|
||||
// - Can include spaces such as `c:\program files`
|
||||
// - And then followed by a colon which is not in the capture group
|
||||
// - And can be optional
|
||||
// Name:
|
||||
// - Must not contain invalid NTFS filename characters (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx)
|
||||
// - And then followed by a colon which is not in the capture group
|
||||
// - And can be optional
|
||||
|
||||
// rxDestination is the regex expression for the mount destination
|
||||
rxDestination = `(?P<destination>((?:\\\\\?\\)?([a-z]):((?:[\\/][^\\/:*?"<>\r\n]+)*[\\/]?))|(` + rxPipe + `))`
|
||||
|
||||
rxLCOWDestination = `(?P<destination>/(?:[^\\/:*?"<>\r\n]+[/]?)*)`
|
||||
// Destination (aka container path):
|
||||
// - Variation on hostdir but can be a drive followed by colon as well
|
||||
// - If a path, must be absolute. Can include spaces
|
||||
// - Drive cannot be c: (explicitly checked in code, not RegEx)
|
||||
|
||||
// rxMode is the regex expression for the mode of the mount
|
||||
// Mode (optional):
|
||||
// - Hopefully self explanatory in comparison to above regex's.
|
||||
// - Colon is not in the capture group
|
||||
rxMode = `(:(?P<mode>(?i)ro|rw))?`
|
||||
)
|
||||
|
||||
type mountValidator func(mnt *mount.Mount) error
|
||||
|
||||
func windowsSplitRawSpec(raw, destRegex string) ([]string, error) {
|
||||
specExp := regexp.MustCompile(`^` + rxSource + destRegex + rxMode + `$`)
|
||||
match := specExp.FindStringSubmatch(strings.ToLower(raw))
|
||||
|
||||
// Must have something back
|
||||
if len(match) == 0 {
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
|
||||
var split []string
|
||||
matchgroups := make(map[string]string)
|
||||
// Pull out the sub expressions from the named capture groups
|
||||
for i, name := range specExp.SubexpNames() {
|
||||
matchgroups[name] = strings.ToLower(match[i])
|
||||
}
|
||||
if source, exists := matchgroups["source"]; exists {
|
||||
if source != "" {
|
||||
split = append(split, source)
|
||||
}
|
||||
}
|
||||
if destination, exists := matchgroups["destination"]; exists {
|
||||
if destination != "" {
|
||||
split = append(split, destination)
|
||||
}
|
||||
}
|
||||
if mode, exists := matchgroups["mode"]; exists {
|
||||
if mode != "" {
|
||||
split = append(split, mode)
|
||||
}
|
||||
}
|
||||
// Fix #26329. If the destination appears to be a file, and the source is null,
|
||||
// it may be because we've fallen through the possible naming regex and hit a
|
||||
// situation where the user intention was to map a file into a container through
|
||||
// a local volume, but this is not supported by the platform.
|
||||
if matchgroups["source"] == "" && matchgroups["destination"] != "" {
|
||||
volExp := regexp.MustCompile(`^` + rxName + `$`)
|
||||
reservedNameExp := regexp.MustCompile(`^` + rxReservedNames + `$`)
|
||||
|
||||
if volExp.MatchString(matchgroups["destination"]) {
|
||||
if reservedNameExp.MatchString(matchgroups["destination"]) {
|
||||
return nil, fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", matchgroups["destination"])
|
||||
}
|
||||
} else {
|
||||
|
||||
exists, isDir, _ := currentFileInfoProvider.fileInfo(matchgroups["destination"])
|
||||
if exists && !isDir {
|
||||
return nil, fmt.Errorf("file '%s' cannot be mapped. Only directories can be mapped on this platform", matchgroups["destination"])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return split, nil
|
||||
}
|
||||
|
||||
func windowsValidMountMode(mode string) bool {
|
||||
if mode == "" {
|
||||
return true
|
||||
}
|
||||
return rwModes[strings.ToLower(mode)]
|
||||
}
|
||||
func windowsValidateNotRoot(p string) error {
|
||||
p = strings.ToLower(strings.Replace(p, `/`, `\`, -1))
|
||||
if p == "c:" || p == `c:\` {
|
||||
return fmt.Errorf("destination path cannot be `c:` or `c:\\`: %v", p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var windowsSpecificValidators mountValidator = func(mnt *mount.Mount) error {
|
||||
return windowsValidateNotRoot(mnt.Target)
|
||||
}
|
||||
|
||||
func windowsValidateRegex(p, r string) error {
|
||||
if regexp.MustCompile(`^` + r + `$`).MatchString(strings.ToLower(p)) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid mount path: '%s'", p)
|
||||
}
|
||||
func windowsValidateAbsolute(p string) error {
|
||||
if err := windowsValidateRegex(p, rxDestination); err != nil {
|
||||
return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func windowsDetectMountType(p string) mount.Type {
|
||||
if strings.HasPrefix(p, `\\.\pipe\`) {
|
||||
return mount.TypeNamedPipe
|
||||
} else if regexp.MustCompile(`^` + rxHostDir + `$`).MatchString(p) {
|
||||
return mount.TypeBind
|
||||
} else {
|
||||
return mount.TypeVolume
|
||||
}
|
||||
}
|
||||
|
||||
func (p *windowsParser) ReadWrite(mode string) bool {
|
||||
return strings.ToLower(mode) != "ro"
|
||||
}
|
||||
|
||||
// IsVolumeNameValid checks a volume name in a platform specific manner.
|
||||
func (p *windowsParser) ValidateVolumeName(name string) error {
|
||||
nameExp := regexp.MustCompile(`^` + rxName + `$`)
|
||||
if !nameExp.MatchString(name) {
|
||||
return errors.New("invalid volume name")
|
||||
}
|
||||
nameExp = regexp.MustCompile(`^` + rxReservedNames + `$`)
|
||||
if nameExp.MatchString(name) {
|
||||
return fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *windowsParser) ValidateMountConfig(mnt *mount.Mount) error {
|
||||
return p.validateMountConfigReg(mnt, rxDestination, windowsSpecificValidators)
|
||||
}
|
||||
|
||||
type fileInfoProvider interface {
|
||||
fileInfo(path string) (exist, isDir bool, err error)
|
||||
}
|
||||
|
||||
type defaultFileInfoProvider struct {
|
||||
}
|
||||
|
||||
func (defaultFileInfoProvider) fileInfo(path string) (exist, isDir bool, err error) {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return false, false, err
|
||||
}
|
||||
return false, false, nil
|
||||
}
|
||||
return true, fi.IsDir(), nil
|
||||
}
|
||||
|
||||
var currentFileInfoProvider fileInfoProvider = defaultFileInfoProvider{}
|
||||
|
||||
func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex string, additionalValidators ...mountValidator) error {
|
||||
|
||||
for _, v := range additionalValidators {
|
||||
if err := v(mnt); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
}
|
||||
if len(mnt.Target) == 0 {
|
||||
return &errMountConfig{mnt, errMissingField("Target")}
|
||||
}
|
||||
|
||||
if err := windowsValidateRegex(mnt.Target, destRegex); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
|
||||
switch mnt.Type {
|
||||
case mount.TypeBind:
|
||||
if len(mnt.Source) == 0 {
|
||||
return &errMountConfig{mnt, errMissingField("Source")}
|
||||
}
|
||||
// Don't error out just because the propagation mode is not supported on the platform
|
||||
if opts := mnt.BindOptions; opts != nil {
|
||||
if len(opts.Propagation) > 0 {
|
||||
return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)}
|
||||
}
|
||||
}
|
||||
if mnt.VolumeOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("VolumeOptions")}
|
||||
}
|
||||
|
||||
if err := windowsValidateAbsolute(mnt.Source); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
|
||||
exists, isdir, err := currentFileInfoProvider.fileInfo(mnt.Source)
|
||||
if err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
if !exists {
|
||||
return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
|
||||
}
|
||||
if !isdir {
|
||||
return &errMountConfig{mnt, fmt.Errorf("source path must be a directory")}
|
||||
}
|
||||
|
||||
case mount.TypeVolume:
|
||||
if mnt.BindOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("BindOptions")}
|
||||
}
|
||||
|
||||
if len(mnt.Source) == 0 && mnt.ReadOnly {
|
||||
return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")}
|
||||
}
|
||||
|
||||
if len(mnt.Source) != 0 {
|
||||
if err := p.ValidateVolumeName(mnt.Source); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
}
|
||||
case mount.TypeNamedPipe:
|
||||
if len(mnt.Source) == 0 {
|
||||
return &errMountConfig{mnt, errMissingField("Source")}
|
||||
}
|
||||
|
||||
if mnt.BindOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("BindOptions")}
|
||||
}
|
||||
|
||||
if mnt.ReadOnly {
|
||||
return &errMountConfig{mnt, errExtraField("ReadOnly")}
|
||||
}
|
||||
|
||||
if windowsDetectMountType(mnt.Source) != mount.TypeNamedPipe {
|
||||
return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Source)}
|
||||
}
|
||||
|
||||
if windowsDetectMountType(mnt.Target) != mount.TypeNamedPipe {
|
||||
return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Target)}
|
||||
}
|
||||
default:
|
||||
return &errMountConfig{mnt, errors.New("mount type unknown")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *windowsParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
|
||||
return p.parseMountRaw(raw, volumeDriver, rxDestination, true, windowsSpecificValidators)
|
||||
}
|
||||
|
||||
func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
|
||||
arr, err := windowsSplitRawSpec(raw, destRegex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec mount.Mount
|
||||
var mode string
|
||||
switch len(arr) {
|
||||
case 1:
|
||||
// Just a destination path in the container
|
||||
spec.Target = arr[0]
|
||||
case 2:
|
||||
if windowsValidMountMode(arr[1]) {
|
||||
// Destination + Mode is not a valid volume - volumes
|
||||
// cannot include a mode. e.g. /foo:rw
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
// Host Source Path or Name + Destination
|
||||
spec.Source = strings.Replace(arr[0], `/`, `\`, -1)
|
||||
spec.Target = arr[1]
|
||||
case 3:
|
||||
// HostSourcePath+DestinationPath+Mode
|
||||
spec.Source = strings.Replace(arr[0], `/`, `\`, -1)
|
||||
spec.Target = arr[1]
|
||||
mode = arr[2]
|
||||
default:
|
||||
return nil, errInvalidSpec(raw)
|
||||
}
|
||||
if convertTargetToBackslash {
|
||||
spec.Target = strings.Replace(spec.Target, `/`, `\`, -1)
|
||||
}
|
||||
|
||||
if !windowsValidMountMode(mode) {
|
||||
return nil, errInvalidMode(mode)
|
||||
}
|
||||
|
||||
spec.Type = windowsDetectMountType(spec.Source)
|
||||
spec.ReadOnly = !p.ReadWrite(mode)
|
||||
|
||||
// cannot assume that if a volume driver is passed in that we should set it
|
||||
if volumeDriver != "" && spec.Type == mount.TypeVolume {
|
||||
spec.VolumeOptions = &mount.VolumeOptions{
|
||||
DriverConfig: &mount.Driver{Name: volumeDriver},
|
||||
}
|
||||
}
|
||||
|
||||
if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
|
||||
if spec.VolumeOptions == nil {
|
||||
spec.VolumeOptions = &mount.VolumeOptions{}
|
||||
}
|
||||
spec.VolumeOptions.NoCopy = !copyData
|
||||
}
|
||||
|
||||
mp, err := p.parseMountSpec(spec, destRegex, convertTargetToBackslash, additionalValidators...)
|
||||
if mp != nil {
|
||||
mp.Mode = mode
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
|
||||
}
|
||||
return mp, err
|
||||
}
|
||||
|
||||
func (p *windowsParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) {
|
||||
return p.parseMountSpec(cfg, rxDestination, true, windowsSpecificValidators)
|
||||
}
|
||||
func (p *windowsParser) parseMountSpec(cfg mount.Mount, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) {
|
||||
if err := p.validateMountConfigReg(&cfg, destRegex, additionalValidators...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp := &MountPoint{
|
||||
RW: !cfg.ReadOnly,
|
||||
Destination: cfg.Target,
|
||||
Type: cfg.Type,
|
||||
Spec: cfg,
|
||||
}
|
||||
if convertTargetToBackslash {
|
||||
mp.Destination = strings.Replace(cfg.Target, `/`, `\`, -1)
|
||||
}
|
||||
|
||||
switch cfg.Type {
|
||||
case mount.TypeVolume:
|
||||
if cfg.Source == "" {
|
||||
mp.Name = stringid.GenerateNonCryptoID()
|
||||
} else {
|
||||
mp.Name = cfg.Source
|
||||
}
|
||||
mp.CopyData = p.DefaultCopyMode()
|
||||
|
||||
if cfg.VolumeOptions != nil {
|
||||
if cfg.VolumeOptions.DriverConfig != nil {
|
||||
mp.Driver = cfg.VolumeOptions.DriverConfig.Name
|
||||
}
|
||||
if cfg.VolumeOptions.NoCopy {
|
||||
mp.CopyData = false
|
||||
}
|
||||
}
|
||||
case mount.TypeBind:
|
||||
mp.Source = strings.Replace(cfg.Source, `/`, `\`, -1)
|
||||
case mount.TypeNamedPipe:
|
||||
mp.Source = strings.Replace(cfg.Source, `/`, `\`, -1)
|
||||
}
|
||||
// cleanup trailing `\` except for paths like `c:\`
|
||||
if len(mp.Source) > 3 && mp.Source[len(mp.Source)-1] == '\\' {
|
||||
mp.Source = mp.Source[:len(mp.Source)-1]
|
||||
}
|
||||
if len(mp.Destination) > 3 && mp.Destination[len(mp.Destination)-1] == '\\' {
|
||||
mp.Destination = mp.Destination[:len(mp.Destination)-1]
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
func (p *windowsParser) ParseVolumesFrom(spec string) (string, string, error) {
|
||||
if len(spec) == 0 {
|
||||
return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
|
||||
}
|
||||
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
id := specParts[0]
|
||||
mode := "rw"
|
||||
|
||||
if len(specParts) == 2 {
|
||||
mode = specParts[1]
|
||||
if !windowsValidMountMode(mode) {
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
|
||||
// Do not allow copy modes on volumes-from
|
||||
if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet {
|
||||
return "", "", errInvalidMode(mode)
|
||||
}
|
||||
}
|
||||
return id, mode, nil
|
||||
}
|
||||
|
||||
func (p *windowsParser) DefaultPropagationMode() mount.Propagation {
|
||||
return mount.Propagation("")
|
||||
}
|
||||
|
||||
func (p *windowsParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) {
|
||||
return "", fmt.Errorf("%s does not support tmpfs", runtime.GOOS)
|
||||
}
|
||||
func (p *windowsParser) DefaultCopyMode() bool {
|
||||
return false
|
||||
}
|
||||
func (p *windowsParser) IsBackwardCompatible(m *MountPoint) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *windowsParser) ValidateTmpfsMountDestination(dest string) error {
|
||||
return errors.New("Platform does not support tmpfs")
|
||||
}
|
||||
69
vendor/github.com/docker/docker/volume/volume.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/volume/volume.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package volume // import "github.com/docker/docker/volume"
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultDriverName is the driver name used for the driver
|
||||
// implemented in the local package.
|
||||
const DefaultDriverName = "local"
|
||||
|
||||
// Scopes define if a volume has is cluster-wide (global) or local only.
|
||||
// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
|
||||
const (
|
||||
LocalScope = "local"
|
||||
GlobalScope = "global"
|
||||
)
|
||||
|
||||
// Driver is for creating and removing volumes.
|
||||
type Driver interface {
|
||||
// Name returns the name of the volume driver.
|
||||
Name() string
|
||||
// Create makes a new volume with the given name.
|
||||
Create(name string, opts map[string]string) (Volume, error)
|
||||
// Remove deletes the volume.
|
||||
Remove(vol Volume) (err error)
|
||||
// List lists all the volumes the driver has
|
||||
List() ([]Volume, error)
|
||||
// Get retrieves the volume with the requested name
|
||||
Get(name string) (Volume, error)
|
||||
// Scope returns the scope of the driver (e.g. `global` or `local`).
|
||||
// Scope determines how the driver is handled at a cluster level
|
||||
Scope() string
|
||||
}
|
||||
|
||||
// Capability defines a set of capabilities that a driver is able to handle.
|
||||
type Capability struct {
|
||||
// Scope is the scope of the driver, `global` or `local`
|
||||
// A `global` scope indicates that the driver manages volumes across the cluster
|
||||
// A `local` scope indicates that the driver only manages volumes resources local to the host
|
||||
// Scope is declared by the driver
|
||||
Scope string
|
||||
}
|
||||
|
||||
// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
|
||||
type Volume interface {
|
||||
// Name returns the name of the volume
|
||||
Name() string
|
||||
// DriverName returns the name of the driver which owns this volume.
|
||||
DriverName() string
|
||||
// Path returns the absolute path to the volume.
|
||||
Path() string
|
||||
// Mount mounts the volume and returns the absolute path to
|
||||
// where it can be consumed.
|
||||
Mount(id string) (string, error)
|
||||
// Unmount unmounts the volume when it is no longer in use.
|
||||
Unmount(id string) error
|
||||
// CreatedAt returns Volume Creation time
|
||||
CreatedAt() (time.Time, error)
|
||||
// Status returns low-level status information about a volume
|
||||
Status() map[string]interface{}
|
||||
}
|
||||
|
||||
// DetailedVolume wraps a Volume with user-defined labels, options, and cluster scope (e.g., `local` or `global`)
|
||||
type DetailedVolume interface {
|
||||
Labels() map[string]string
|
||||
Options() map[string]string
|
||||
Scope() string
|
||||
Volume
|
||||
}
|
||||
Reference in New Issue
Block a user