github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/opts/opts.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/pkg/blkiodev"
    12  	"github.com/docker/go-units"
    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  )
    19  
    20  // ListOpts holds a list of values and a validation function.
    21  type ListOpts struct {
    22  	values    *[]string
    23  	validator ValidatorFctType
    24  }
    25  
    26  // NewListOpts creates a new ListOpts with the specified validator.
    27  func NewListOpts(validator ValidatorFctType) ListOpts {
    28  	var values []string
    29  	return *NewListOptsRef(&values, validator)
    30  }
    31  
    32  // NewListOptsRef creates a new ListOpts with the specified values and validator.
    33  func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
    34  	return &ListOpts{
    35  		values:    values,
    36  		validator: validator,
    37  	}
    38  }
    39  
    40  func (opts *ListOpts) String() string {
    41  	return fmt.Sprintf("%v", []string((*opts.values)))
    42  }
    43  
    44  // Set validates if needed the input value and add it to the
    45  // internal slice.
    46  func (opts *ListOpts) Set(value string) error {
    47  	if opts.validator != nil {
    48  		v, err := opts.validator(value)
    49  		if err != nil {
    50  			return err
    51  		}
    52  		value = v
    53  	}
    54  	(*opts.values) = append((*opts.values), value)
    55  	return nil
    56  }
    57  
    58  // Delete removes the specified element from the slice.
    59  func (opts *ListOpts) Delete(key string) {
    60  	for i, k := range *opts.values {
    61  		if k == key {
    62  			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
    63  			return
    64  		}
    65  	}
    66  }
    67  
    68  // GetMap returns the content of values in a map in order to avoid
    69  // duplicates.
    70  func (opts *ListOpts) GetMap() map[string]struct{} {
    71  	ret := make(map[string]struct{})
    72  	for _, k := range *opts.values {
    73  		ret[k] = struct{}{}
    74  	}
    75  	return ret
    76  }
    77  
    78  // GetAll returns the values of slice.
    79  func (opts *ListOpts) GetAll() []string {
    80  	return (*opts.values)
    81  }
    82  
    83  // GetAllOrEmpty returns the values of the slice
    84  // or an empty slice when there are no values.
    85  func (opts *ListOpts) GetAllOrEmpty() []string {
    86  	v := *opts.values
    87  	if v == nil {
    88  		return make([]string, 0)
    89  	}
    90  	return v
    91  }
    92  
    93  // Get checks the existence of the specified key.
    94  func (opts *ListOpts) Get(key string) bool {
    95  	for _, k := range *opts.values {
    96  		if k == key {
    97  			return true
    98  		}
    99  	}
   100  	return false
   101  }
   102  
   103  // Len returns the amount of element in the slice.
   104  func (opts *ListOpts) Len() int {
   105  	return len((*opts.values))
   106  }
   107  
   108  //MapOpts holds a map of values and a validation function.
   109  type MapOpts struct {
   110  	values    map[string]string
   111  	validator ValidatorFctType
   112  }
   113  
   114  // Set validates if needed the input value and add it to the
   115  // internal map, by splitting on '='.
   116  func (opts *MapOpts) Set(value string) error {
   117  	if opts.validator != nil {
   118  		v, err := opts.validator(value)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		value = v
   123  	}
   124  	vals := strings.SplitN(value, "=", 2)
   125  	if len(vals) == 1 {
   126  		(opts.values)[vals[0]] = ""
   127  	} else {
   128  		(opts.values)[vals[0]] = vals[1]
   129  	}
   130  	return nil
   131  }
   132  
   133  // GetAll returns the values of MapOpts as a map.
   134  func (opts *MapOpts) GetAll() map[string]string {
   135  	return opts.values
   136  }
   137  
   138  func (opts *MapOpts) String() string {
   139  	return fmt.Sprintf("%v", map[string]string((opts.values)))
   140  }
   141  
   142  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   143  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   144  	if values == nil {
   145  		values = make(map[string]string)
   146  	}
   147  	return &MapOpts{
   148  		values:    values,
   149  		validator: validator,
   150  	}
   151  }
   152  
   153  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   154  type ValidatorFctType func(val string) (string, error)
   155  
   156  // ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error.
   157  type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error)
   158  
   159  // ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error.
   160  type ValidatorThrottleFctType func(val string) (*blkiodev.ThrottleDevice, error)
   161  
   162  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   163  type ValidatorFctListType func(val string) ([]string, error)
   164  
   165  // ValidateAttach validates that the specified string is a valid attach option.
   166  func ValidateAttach(val string) (string, error) {
   167  	s := strings.ToLower(val)
   168  	for _, str := range []string{"stdin", "stdout", "stderr"} {
   169  		if s == str {
   170  			return s, nil
   171  		}
   172  	}
   173  	return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR")
   174  }
   175  
   176  // ValidateWeightDevice validates that the specified string has a valid device-weight format.
   177  func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) {
   178  	split := strings.SplitN(val, ":", 2)
   179  	if len(split) != 2 {
   180  		return nil, fmt.Errorf("bad format: %s", val)
   181  	}
   182  	if !strings.HasPrefix(split[0], "/dev/") {
   183  		return nil, fmt.Errorf("bad format for device path: %s", val)
   184  	}
   185  	weight, err := strconv.ParseUint(split[1], 10, 0)
   186  	if err != nil {
   187  		return nil, fmt.Errorf("invalid weight for device: %s", val)
   188  	}
   189  	if weight > 0 && (weight < 10 || weight > 1000) {
   190  		return nil, fmt.Errorf("invalid weight for device: %s", val)
   191  	}
   192  
   193  	return &blkiodev.WeightDevice{
   194  		Path:   split[0],
   195  		Weight: uint16(weight),
   196  	}, nil
   197  }
   198  
   199  // ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format.
   200  func ValidateThrottleBpsDevice(val string) (*blkiodev.ThrottleDevice, error) {
   201  	split := strings.SplitN(val, ":", 2)
   202  	if len(split) != 2 {
   203  		return nil, fmt.Errorf("bad format: %s", val)
   204  	}
   205  	if !strings.HasPrefix(split[0], "/dev/") {
   206  		return nil, fmt.Errorf("bad format for device path: %s", val)
   207  	}
   208  	rate, err := units.RAMInBytes(split[1])
   209  	if err != nil {
   210  		return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
   211  	}
   212  	if rate < 0 {
   213  		return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
   214  	}
   215  
   216  	return &blkiodev.ThrottleDevice{
   217  		Path: split[0],
   218  		Rate: uint64(rate),
   219  	}, nil
   220  }
   221  
   222  // ValidateEnv validates an environment variable and returns it.
   223  // If no value is specified, it returns the current value using os.Getenv.
   224  //
   225  // As on ParseEnvFile and related to #16585, environment variable names
   226  // are not validate what so ever, it's up to application inside docker
   227  // to validate them or not.
   228  func ValidateEnv(val string) (string, error) {
   229  	arr := strings.Split(val, "=")
   230  	if len(arr) > 1 {
   231  		return val, nil
   232  	}
   233  	if !doesEnvExist(val) {
   234  		return val, nil
   235  	}
   236  	return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
   237  }
   238  
   239  // ValidateIPAddress validates an Ip address.
   240  func ValidateIPAddress(val string) (string, error) {
   241  	var ip = net.ParseIP(strings.TrimSpace(val))
   242  	if ip != nil {
   243  		return ip.String(), nil
   244  	}
   245  	return "", fmt.Errorf("%s is not an ip address", val)
   246  }
   247  
   248  // ValidateMACAddress validates a MAC address.
   249  func ValidateMACAddress(val string) (string, error) {
   250  	_, err := net.ParseMAC(strings.TrimSpace(val))
   251  	if err != nil {
   252  		return "", err
   253  	}
   254  	return val, nil
   255  }
   256  
   257  // ValidateDNSSearch validates domain for resolvconf search configuration.
   258  // A zero length domain is represented by a dot (.).
   259  func ValidateDNSSearch(val string) (string, error) {
   260  	if val = strings.Trim(val, " "); val == "." {
   261  		return val, nil
   262  	}
   263  	return validateDomain(val)
   264  }
   265  
   266  func validateDomain(val string) (string, error) {
   267  	if alphaRegexp.FindString(val) == "" {
   268  		return "", fmt.Errorf("%s is not a valid domain", val)
   269  	}
   270  	ns := domainRegexp.FindSubmatch([]byte(val))
   271  	if len(ns) > 0 && len(ns[1]) < 255 {
   272  		return string(ns[1]), nil
   273  	}
   274  	return "", fmt.Errorf("%s is not a valid domain", val)
   275  }
   276  
   277  // ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
   278  // ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
   279  func ValidateExtraHost(val string) (string, error) {
   280  	// allow for IPv6 addresses in extra hosts by only splitting on first ":"
   281  	arr := strings.SplitN(val, ":", 2)
   282  	if len(arr) != 2 || len(arr[0]) == 0 {
   283  		return "", fmt.Errorf("bad format for add-host: %q", val)
   284  	}
   285  	if _, err := ValidateIPAddress(arr[1]); err != nil {
   286  		return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
   287  	}
   288  	return val, nil
   289  }
   290  
   291  // ValidateLabel validates that the specified string is a valid label, and returns it.
   292  // Labels are in the form on key=value.
   293  func ValidateLabel(val string) (string, error) {
   294  	if strings.Count(val, "=") < 1 {
   295  		return "", fmt.Errorf("bad attribute format: %s", val)
   296  	}
   297  	return val, nil
   298  }
   299  
   300  func doesEnvExist(name string) bool {
   301  	for _, entry := range os.Environ() {
   302  		parts := strings.SplitN(entry, "=", 2)
   303  		if parts[0] == name {
   304  			return true
   305  		}
   306  	}
   307  	return false
   308  }