github.com/simonferquel/app@v0.6.1-0.20181012141724-68b7cccf26ac/internal/helm/templateloader/volume.go (about)

     1  package templateloader
     2  
     3  import (
     4  	"strings"
     5  	"unicode"
     6  	"unicode/utf8"
     7  
     8  	"github.com/docker/app/internal/helm/templatetypes"
     9  	"github.com/docker/cli/cli/compose/types"
    10  	"github.com/docker/docker/api/types/mount"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  const endOfSpec = rune(0)
    15  
    16  // ParseVolume parses a volume spec without any knowledge of the target platform
    17  func ParseVolume(spec string) (templatetypes.ServiceVolumeConfig, error) {
    18  	volume := templatetypes.ServiceVolumeConfig{}
    19  
    20  	switch len(spec) {
    21  	case 0:
    22  		return volume, errors.New("invalid empty volume spec")
    23  	case 1, 2:
    24  		volume.Target = templatetypes.StringTemplate{Value: spec}
    25  		volume.Type = string(mount.TypeVolume)
    26  		return volume, nil
    27  	}
    28  
    29  	buffer := []rune{}
    30  	for _, char := range spec + string(endOfSpec) {
    31  		switch {
    32  		case isWindowsDrive(buffer, char):
    33  			buffer = append(buffer, char)
    34  		case char == ':' || char == endOfSpec:
    35  			if err := populateFieldFromBuffer(char, buffer, &volume); err != nil {
    36  				populateType(&volume)
    37  				return volume, errors.Wrapf(err, "invalid spec: %s", spec)
    38  			}
    39  			buffer = []rune{}
    40  		default:
    41  			buffer = append(buffer, char)
    42  		}
    43  	}
    44  
    45  	populateType(&volume)
    46  	return volume, nil
    47  }
    48  
    49  func isWindowsDrive(buffer []rune, char rune) bool {
    50  	return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0])
    51  }
    52  
    53  func populateFieldFromBuffer(char rune, buffer []rune, volume *templatetypes.ServiceVolumeConfig) error {
    54  	strBuffer := string(buffer)
    55  	switch {
    56  	case len(buffer) == 0:
    57  		return errors.New("empty section between colons")
    58  	// Anonymous volume
    59  	case volume.Source.Value == "" && char == endOfSpec:
    60  		volume.Target.Value = strBuffer
    61  		return nil
    62  	case volume.Source.Value == "":
    63  		volume.Source.Value = strBuffer
    64  		return nil
    65  	case volume.Target.Value == "":
    66  		volume.Target.Value = strBuffer
    67  		return nil
    68  	case char == ':':
    69  		return errors.New("too many colons")
    70  	}
    71  	for _, option := range strings.Split(strBuffer, ",") {
    72  		switch option {
    73  		case "ro":
    74  			volume.ReadOnly = templatetypes.BoolOrTemplate{Value: true}
    75  		case "rw":
    76  			volume.ReadOnly = templatetypes.BoolOrTemplate{Value: false}
    77  		case "nocopy":
    78  			volume.Volume = &types.ServiceVolumeVolume{NoCopy: true}
    79  		default:
    80  			if isBindOption(option) {
    81  				volume.Bind = &types.ServiceVolumeBind{Propagation: option}
    82  			}
    83  			// ignore unknown options
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  func isBindOption(option string) bool {
    90  	for _, propagation := range mount.Propagations {
    91  		if mount.Propagation(option) == propagation {
    92  			return true
    93  		}
    94  	}
    95  	return false
    96  }
    97  
    98  func populateType(volume *templatetypes.ServiceVolumeConfig) {
    99  	switch {
   100  	// Anonymous volume
   101  	case volume.Source.Value == "":
   102  		volume.Type = string(mount.TypeVolume)
   103  	case isFilePath(volume.Source.Value):
   104  		volume.Type = string(mount.TypeBind)
   105  	default:
   106  		volume.Type = string(mount.TypeVolume)
   107  	}
   108  }
   109  
   110  func isFilePath(source string) bool {
   111  	switch source[0] {
   112  	case '.', '/', '~':
   113  		return true
   114  	}
   115  
   116  	// windows named pipes
   117  	if strings.HasPrefix(source, `\\`) {
   118  		return true
   119  	}
   120  
   121  	first, nextIndex := utf8.DecodeRuneInString(source)
   122  	return isWindowsDrive([]rune{first}, rune(source[nextIndex]))
   123  }