github.com/olljanat/moby@v1.13.1/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  		source = stackVolume.External.Name
    79  	} else {
    80  		volumeOptions = &mount.VolumeOptions{
    81  			Labels: AddStackLabel(namespace, stackVolume.Labels),
    82  			NoCopy: isNoCopy(mode),
    83  		}
    84  
    85  		if stackVolume.Driver != "" {
    86  			volumeOptions.DriverConfig = &mount.Driver{
    87  				Name:    stackVolume.Driver,
    88  				Options: stackVolume.DriverOpts,
    89  			}
    90  		}
    91  		source = namespace.Scope(source)
    92  	}
    93  	return mount.Mount{
    94  		Type:          mount.TypeVolume,
    95  		Source:        source,
    96  		Target:        target,
    97  		ReadOnly:      isReadOnly(mode),
    98  		VolumeOptions: volumeOptions,
    99  	}, nil
   100  }
   101  
   102  func modeHas(mode []string, field string) bool {
   103  	for _, item := range mode {
   104  		if item == field {
   105  			return true
   106  		}
   107  	}
   108  	return false
   109  }
   110  
   111  func isReadOnly(mode []string) bool {
   112  	return modeHas(mode, "ro")
   113  }
   114  
   115  func isNoCopy(mode []string) bool {
   116  	return modeHas(mode, "nocopy")
   117  }
   118  
   119  func getBindOptions(mode []string) *mount.BindOptions {
   120  	for _, item := range mode {
   121  		for _, propagation := range mount.Propagations {
   122  			if mount.Propagation(item) == propagation {
   123  				return &mount.BindOptions{Propagation: mount.Propagation(item)}
   124  			}
   125  		}
   126  	}
   127  	return nil
   128  }