github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/cli/compose/convert/volume.go (about) 1 package convert 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/docker/docker/api/types/mount" 8 composetypes "github.com/docker/docker/cli/compose/types" 9 ) 10 11 type volumes map[string]composetypes.VolumeConfig 12 13 // Volumes from compose-file types to engine api types 14 func Volumes(serviceVolumes []string, stackVolumes volumes, namespace Namespace) ([]mount.Mount, error) { 15 var mounts []mount.Mount 16 17 for _, volumeSpec := range serviceVolumes { 18 mount, err := convertVolumeToMount(volumeSpec, stackVolumes, namespace) 19 if err != nil { 20 return nil, err 21 } 22 mounts = append(mounts, mount) 23 } 24 return mounts, nil 25 } 26 27 func convertVolumeToMount(volumeSpec string, stackVolumes volumes, namespace Namespace) (mount.Mount, error) { 28 var source, target string 29 var mode []string 30 31 // TODO: split Windows path mappings properly 32 parts := strings.SplitN(volumeSpec, ":", 3) 33 34 for _, part := range parts { 35 if strings.TrimSpace(part) == "" { 36 return mount.Mount{}, fmt.Errorf("invalid volume: %s", volumeSpec) 37 } 38 } 39 40 switch len(parts) { 41 case 3: 42 source = parts[0] 43 target = parts[1] 44 mode = strings.Split(parts[2], ",") 45 case 2: 46 source = parts[0] 47 target = parts[1] 48 case 1: 49 target = parts[0] 50 } 51 52 if source == "" { 53 // Anonymous volume 54 return mount.Mount{ 55 Type: mount.TypeVolume, 56 Target: target, 57 }, nil 58 } 59 60 // TODO: catch Windows paths here 61 if strings.HasPrefix(source, "/") { 62 return mount.Mount{ 63 Type: mount.TypeBind, 64 Source: source, 65 Target: target, 66 ReadOnly: isReadOnly(mode), 67 BindOptions: getBindOptions(mode), 68 }, nil 69 } 70 71 stackVolume, exists := stackVolumes[source] 72 if !exists { 73 return mount.Mount{}, fmt.Errorf("undefined volume: %s", source) 74 } 75 76 var volumeOptions *mount.VolumeOptions 77 if stackVolume.External.Name != "" { 78 volumeOptions = &mount.VolumeOptions{ 79 NoCopy: isNoCopy(mode), 80 } 81 source = stackVolume.External.Name 82 } else { 83 volumeOptions = &mount.VolumeOptions{ 84 Labels: AddStackLabel(namespace, stackVolume.Labels), 85 NoCopy: isNoCopy(mode), 86 } 87 88 if stackVolume.Driver != "" { 89 volumeOptions.DriverConfig = &mount.Driver{ 90 Name: stackVolume.Driver, 91 Options: stackVolume.DriverOpts, 92 } 93 } 94 source = namespace.Scope(source) 95 } 96 return mount.Mount{ 97 Type: mount.TypeVolume, 98 Source: source, 99 Target: target, 100 ReadOnly: isReadOnly(mode), 101 VolumeOptions: volumeOptions, 102 }, nil 103 } 104 105 func modeHas(mode []string, field string) bool { 106 for _, item := range mode { 107 if item == field { 108 return true 109 } 110 } 111 return false 112 } 113 114 func isReadOnly(mode []string) bool { 115 return modeHas(mode, "ro") 116 } 117 118 func isNoCopy(mode []string) bool { 119 return modeHas(mode, "nocopy") 120 } 121 122 func getBindOptions(mode []string) *mount.BindOptions { 123 for _, item := range mode { 124 for _, propagation := range mount.Propagations { 125 if mount.Propagation(item) == propagation { 126 return &mount.BindOptions{Propagation: mount.Propagation(item)} 127 } 128 } 129 } 130 return nil 131 }