github.com/campoy/docker@v1.8.0-rc1/opts/opts.go (about)

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