DockerCLI/compose/convert/volume.go

129 lines
2.8 KiB
Go
Raw Normal View History

package convert
import (
"fmt"
"strings"
"github.com/docker/docker/api/types/mount"
composetypes "github.com/docker/docker/cli/compose/types"
)
type volumes map[string]composetypes.VolumeConfig
// Volumes from compose-file types to engine api types
func Volumes(serviceVolumes []string, stackVolumes volumes, namespace Namespace) ([]mount.Mount, error) {
var mounts []mount.Mount
for _, volumeSpec := range serviceVolumes {
mount, err := convertVolumeToMount(volumeSpec, stackVolumes, namespace)
if err != nil {
return nil, err
}
mounts = append(mounts, mount)
}
return mounts, nil
}
func convertVolumeToMount(volumeSpec string, stackVolumes volumes, namespace Namespace) (mount.Mount, error) {
var source, target string
var mode []string
// TODO: split Windows path mappings properly
parts := strings.SplitN(volumeSpec, ":", 3)
for _, part := range parts {
if strings.TrimSpace(part) == "" {
return mount.Mount{}, fmt.Errorf("invalid volume: %s", volumeSpec)
}
}
switch len(parts) {
case 3:
source = parts[0]
target = parts[1]
mode = strings.Split(parts[2], ",")
case 2:
source = parts[0]
target = parts[1]
case 1:
target = parts[0]
}
if source == "" {
// Anonymous volume
return mount.Mount{
Type: mount.TypeVolume,
Target: target,
}, nil
}
// TODO: catch Windows paths here
if strings.HasPrefix(source, "/") {
return mount.Mount{
Type: mount.TypeBind,
Source: source,
Target: target,
ReadOnly: isReadOnly(mode),
BindOptions: getBindOptions(mode),
}, nil
}
stackVolume, exists := stackVolumes[source]
if !exists {
return mount.Mount{}, fmt.Errorf("undefined volume: %s", source)
}
var volumeOptions *mount.VolumeOptions
if stackVolume.External.Name != "" {
source = stackVolume.External.Name
} else {
volumeOptions = &mount.VolumeOptions{
Labels: AddStackLabel(namespace, stackVolume.Labels),
NoCopy: isNoCopy(mode),
}
if stackVolume.Driver != "" {
volumeOptions.DriverConfig = &mount.Driver{
Name: stackVolume.Driver,
Options: stackVolume.DriverOpts,
}
}
source = namespace.Scope(source)
}
return mount.Mount{
Type: mount.TypeVolume,
Source: source,
Target: target,
ReadOnly: isReadOnly(mode),
VolumeOptions: volumeOptions,
}, nil
}
func modeHas(mode []string, field string) bool {
for _, item := range mode {
if item == field {
return true
}
}
return false
}
func isReadOnly(mode []string) bool {
return modeHas(mode, "ro")
}
func isNoCopy(mode []string) bool {
return modeHas(mode, "nocopy")
}
func getBindOptions(mode []string) *mount.BindOptions {
for _, item := range mode {
for _, propagation := range mount.Propagations {
if mount.Propagation(item) == propagation {
return &mount.BindOptions{Propagation: mount.Propagation(item)}
}
}
}
return nil
}