github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/opts/opts.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"path"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/pkg/parsers"
    12  	"github.com/docker/docker/volume"
    13  )
    14  
    15  var (
    16  	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
    17  	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
    18  	// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. docker daemon -H tcp://:8080
    19  	DefaultHTTPHost = "127.0.0.1"
    20  	// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. docker daemon -H tcp://
    21  	// TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter
    22  	// is not supplied. A better longer term solution would be to use a named
    23  	// pipe as the default on the Windows daemon.
    24  	DefaultHTTPPort = 2375 // Default HTTP Port
    25  	// DefaultUnixSocket Path for the unix socket.
    26  	// Docker daemon by default always listens on the default unix socket
    27  	DefaultUnixSocket = "/var/run/docker.sock"
    28  	// DefaultTCPHost constant defines the default host string used by docker on Windows
    29  	DefaultTCPHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
    30  )
    31  
    32  // ListOpts holds a list of values and a validation function.
    33  type ListOpts struct {
    34  	values    *[]string
    35  	validator ValidatorFctType
    36  }
    37  
    38  // NewListOpts creates a new ListOpts with the specified validator.
    39  func NewListOpts(validator ValidatorFctType) ListOpts {
    40  	var values []string
    41  	return *NewListOptsRef(&values, validator)
    42  }
    43  
    44  // NewListOptsRef creates a new ListOpts with the specified values and validator.
    45  func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
    46  	return &ListOpts{
    47  		values:    values,
    48  		validator: validator,
    49  	}
    50  }
    51  
    52  func (opts *ListOpts) String() string {
    53  	return fmt.Sprintf("%v", []string((*opts.values)))
    54  }
    55  
    56  // Set validates if needed the input value and add it to the
    57  // internal slice.
    58  func (opts *ListOpts) Set(value string) error {
    59  	if opts.validator != nil {
    60  		v, err := opts.validator(value)
    61  		if err != nil {
    62  			return err
    63  		}
    64  		value = v
    65  	}
    66  	(*opts.values) = append((*opts.values), value)
    67  	return nil
    68  }
    69  
    70  // Delete removes the specified element from the slice.
    71  func (opts *ListOpts) Delete(key string) {
    72  	for i, k := range *opts.values {
    73  		if k == key {
    74  			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
    75  			return
    76  		}
    77  	}
    78  }
    79  
    80  // GetMap returns the content of values in a map in order to avoid
    81  // duplicates.
    82  // FIXME: can we remove this?
    83  func (opts *ListOpts) GetMap() map[string]struct{} {
    84  	ret := make(map[string]struct{})
    85  	for _, k := range *opts.values {
    86  		ret[k] = struct{}{}
    87  	}
    88  	return ret
    89  }
    90  
    91  // GetAll returns the values of slice.
    92  // FIXME: Can we remove this?
    93  func (opts *ListOpts) GetAll() []string {
    94  	return (*opts.values)
    95  }
    96  
    97  // Get checks the existence of the specified key.
    98  func (opts *ListOpts) Get(key string) bool {
    99  	for _, k := range *opts.values {
   100  		if k == key {
   101  			return true
   102  		}
   103  	}
   104  	return false
   105  }
   106  
   107  // Len returns the amount of element in the slice.
   108  func (opts *ListOpts) Len() int {
   109  	return len((*opts.values))
   110  }
   111  
   112  //MapOpts holds a map of values and a validation function.
   113  type MapOpts struct {
   114  	values    map[string]string
   115  	validator ValidatorFctType
   116  }
   117  
   118  // Set validates if needed the input value and add it to the
   119  // internal map, by splitting on '='.
   120  func (opts *MapOpts) Set(value string) error {
   121  	if opts.validator != nil {
   122  		v, err := opts.validator(value)
   123  		if err != nil {
   124  			return err
   125  		}
   126  		value = v
   127  	}
   128  	vals := strings.SplitN(value, "=", 2)
   129  	if len(vals) == 1 {
   130  		(opts.values)[vals[0]] = ""
   131  	} else {
   132  		(opts.values)[vals[0]] = vals[1]
   133  	}
   134  	return nil
   135  }
   136  
   137  // GetAll returns the values of MapOpts as a map.
   138  func (opts *MapOpts) GetAll() map[string]string {
   139  	return opts.values
   140  }
   141  
   142  func (opts *MapOpts) String() string {
   143  	return fmt.Sprintf("%v", map[string]string((opts.values)))
   144  }
   145  
   146  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   147  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   148  	if values == nil {
   149  		values = make(map[string]string)
   150  	}
   151  	return &MapOpts{
   152  		values:    values,
   153  		validator: validator,
   154  	}
   155  }
   156  
   157  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   158  type ValidatorFctType func(val string) (string, error)
   159  
   160  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   161  type ValidatorFctListType func(val string) ([]string, error)
   162  
   163  // ValidateAttach validates that the specified string is a valid attach option.
   164  func ValidateAttach(val string) (string, error) {
   165  	s := strings.ToLower(val)
   166  	for _, str := range []string{"stdin", "stdout", "stderr"} {
   167  		if s == str {
   168  			return s, nil
   169  		}
   170  	}
   171  	return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR")
   172  }
   173  
   174  // ValidateLink validates that the specified string has a valid link format (containerName:alias).
   175  func ValidateLink(val string) (string, error) {
   176  	if _, _, err := parsers.ParseLink(val); err != nil {
   177  		return val, err
   178  	}
   179  	return val, nil
   180  }
   181  
   182  // ValidDeviceMode checks if the mode for device is valid or not.
   183  // Valid mode is a composition of r (read), w (write), and m (mknod).
   184  func ValidDeviceMode(mode string) bool {
   185  	var legalDeviceMode = map[rune]bool{
   186  		'r': true,
   187  		'w': true,
   188  		'm': true,
   189  	}
   190  	if mode == "" {
   191  		return false
   192  	}
   193  	for _, c := range mode {
   194  		if !legalDeviceMode[c] {
   195  			return false
   196  		}
   197  		legalDeviceMode[c] = false
   198  	}
   199  	return true
   200  }
   201  
   202  // ValidateDevice validates a path for devices
   203  // It will make sure 'val' is in the form:
   204  //    [host-dir:]container-path[:mode]
   205  // It also validates the device mode.
   206  func ValidateDevice(val string) (string, error) {
   207  	return validatePath(val, ValidDeviceMode)
   208  }
   209  
   210  // ValidatePath validates a path for volumes
   211  // It will make sure 'val' is in the form:
   212  //    [host-dir:]container-path[:rw|ro]
   213  // It also validates the mount mode.
   214  func ValidatePath(val string) (string, error) {
   215  	return validatePath(val, volume.ValidMountMode)
   216  }
   217  
   218  func validatePath(val string, validator func(string) bool) (string, error) {
   219  	var containerPath string
   220  	var mode string
   221  
   222  	if strings.Count(val, ":") > 2 {
   223  		return val, fmt.Errorf("bad format for path: %s", val)
   224  	}
   225  
   226  	split := strings.SplitN(val, ":", 3)
   227  	if split[0] == "" {
   228  		return val, fmt.Errorf("bad format for path: %s", val)
   229  	}
   230  	switch len(split) {
   231  	case 1:
   232  		containerPath = split[0]
   233  		val = path.Clean(containerPath)
   234  	case 2:
   235  		if isValid := validator(split[1]); isValid {
   236  			containerPath = split[0]
   237  			mode = split[1]
   238  			val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
   239  		} else {
   240  			containerPath = split[1]
   241  			val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
   242  		}
   243  	case 3:
   244  		containerPath = split[1]
   245  		mode = split[2]
   246  		if isValid := validator(split[2]); !isValid {
   247  			return val, fmt.Errorf("bad mode specified: %s", mode)
   248  		}
   249  		val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
   250  	}
   251  
   252  	if !path.IsAbs(containerPath) {
   253  		return val, fmt.Errorf("%s is not an absolute path", containerPath)
   254  	}
   255  	return val, nil
   256  }
   257  
   258  // ValidateEnv validates an environment variable and returns it.
   259  // It uses EnvironmentVariableRegexp to ensure the name of the environment variable is valid.
   260  // If no value is specified, it returns the current value using os.Getenv.
   261  func ValidateEnv(val string) (string, error) {
   262  	arr := strings.Split(val, "=")
   263  	if len(arr) > 1 {
   264  		return val, nil
   265  	}
   266  	if !EnvironmentVariableRegexp.MatchString(arr[0]) {
   267  		return val, ErrBadEnvVariable{fmt.Sprintf("variable '%s' is not a valid environment variable", val)}
   268  	}
   269  	if !doesEnvExist(val) {
   270  		return val, nil
   271  	}
   272  	return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
   273  }
   274  
   275  // ValidateIPAddress validates an Ip address.
   276  func ValidateIPAddress(val string) (string, error) {
   277  	var ip = net.ParseIP(strings.TrimSpace(val))
   278  	if ip != nil {
   279  		return ip.String(), nil
   280  	}
   281  	return "", fmt.Errorf("%s is not an ip address", val)
   282  }
   283  
   284  // ValidateMACAddress validates a MAC address.
   285  func ValidateMACAddress(val string) (string, error) {
   286  	_, err := net.ParseMAC(strings.TrimSpace(val))
   287  	if err != nil {
   288  		return "", err
   289  	}
   290  	return val, nil
   291  }
   292  
   293  // ValidateDNSSearch validates domain for resolvconf search configuration.
   294  // A zero length domain is represented by a dot (.).
   295  func ValidateDNSSearch(val string) (string, error) {
   296  	if val = strings.Trim(val, " "); val == "." {
   297  		return val, nil
   298  	}
   299  	return validateDomain(val)
   300  }
   301  
   302  func validateDomain(val string) (string, error) {
   303  	if alphaRegexp.FindString(val) == "" {
   304  		return "", fmt.Errorf("%s is not a valid domain", val)
   305  	}
   306  	ns := domainRegexp.FindSubmatch([]byte(val))
   307  	if len(ns) > 0 && len(ns[1]) < 255 {
   308  		return string(ns[1]), nil
   309  	}
   310  	return "", fmt.Errorf("%s is not a valid domain", val)
   311  }
   312  
   313  // ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
   314  // ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
   315  func ValidateExtraHost(val string) (string, error) {
   316  	// allow for IPv6 addresses in extra hosts by only splitting on first ":"
   317  	arr := strings.SplitN(val, ":", 2)
   318  	if len(arr) != 2 || len(arr[0]) == 0 {
   319  		return "", fmt.Errorf("bad format for add-host: %q", val)
   320  	}
   321  	if _, err := ValidateIPAddress(arr[1]); err != nil {
   322  		return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
   323  	}
   324  	return val, nil
   325  }
   326  
   327  // ValidateLabel validates that the specified string is a valid label, and returns it.
   328  // Labels are in the form on key=value.
   329  func ValidateLabel(val string) (string, error) {
   330  	if strings.Count(val, "=") < 1 {
   331  		return "", fmt.Errorf("bad attribute format: %s", val)
   332  	}
   333  	return val, nil
   334  }
   335  
   336  // ValidateHost validates that the specified string is a valid host and returns it.
   337  func ValidateHost(val string) (string, error) {
   338  	host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultUnixSocket, val)
   339  	if err != nil {
   340  		return val, err
   341  	}
   342  	return host, nil
   343  }
   344  
   345  func doesEnvExist(name string) bool {
   346  	for _, entry := range os.Environ() {
   347  		parts := strings.SplitN(entry, "=", 2)
   348  		if parts[0] == name {
   349  			return true
   350  		}
   351  	}
   352  	return false
   353  }