github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/opts/opts.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"net"
     7  	"path"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/api/types/filters"
    12  	units "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 adds 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  // Type returns a string name for this Option type
   109  func (opts *ListOpts) Type() string {
   110  	return "list"
   111  }
   112  
   113  // WithValidator returns the ListOpts with validator set.
   114  func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
   115  	opts.validator = validator
   116  	return opts
   117  }
   118  
   119  // NamedOption is an interface that list and map options
   120  // with names implement.
   121  type NamedOption interface {
   122  	Name() string
   123  }
   124  
   125  // NamedListOpts is a ListOpts with a configuration name.
   126  // This struct is useful to keep reference to the assigned
   127  // field name in the internal configuration struct.
   128  type NamedListOpts struct {
   129  	name string
   130  	ListOpts
   131  }
   132  
   133  var _ NamedOption = &NamedListOpts{}
   134  
   135  // NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
   136  func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
   137  	return &NamedListOpts{
   138  		name:     name,
   139  		ListOpts: *NewListOptsRef(values, validator),
   140  	}
   141  }
   142  
   143  // Name returns the name of the NamedListOpts in the configuration.
   144  func (o *NamedListOpts) Name() string {
   145  	return o.name
   146  }
   147  
   148  // MapOpts holds a map of values and a validation function.
   149  type MapOpts struct {
   150  	values    map[string]string
   151  	validator ValidatorFctType
   152  }
   153  
   154  // Set validates if needed the input value and add it to the
   155  // internal map, by splitting on '='.
   156  func (opts *MapOpts) Set(value string) error {
   157  	if opts.validator != nil {
   158  		v, err := opts.validator(value)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		value = v
   163  	}
   164  	vals := strings.SplitN(value, "=", 2)
   165  	if len(vals) == 1 {
   166  		(opts.values)[vals[0]] = ""
   167  	} else {
   168  		(opts.values)[vals[0]] = vals[1]
   169  	}
   170  	return nil
   171  }
   172  
   173  // GetAll returns the values of MapOpts as a map.
   174  func (opts *MapOpts) GetAll() map[string]string {
   175  	return opts.values
   176  }
   177  
   178  func (opts *MapOpts) String() string {
   179  	return fmt.Sprintf("%v", map[string]string((opts.values)))
   180  }
   181  
   182  // Type returns a string name for this Option type
   183  func (opts *MapOpts) Type() string {
   184  	return "map"
   185  }
   186  
   187  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   188  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   189  	if values == nil {
   190  		values = make(map[string]string)
   191  	}
   192  	return &MapOpts{
   193  		values:    values,
   194  		validator: validator,
   195  	}
   196  }
   197  
   198  // NamedMapOpts is a MapOpts struct with a configuration name.
   199  // This struct is useful to keep reference to the assigned
   200  // field name in the internal configuration struct.
   201  type NamedMapOpts struct {
   202  	name string
   203  	MapOpts
   204  }
   205  
   206  var _ NamedOption = &NamedMapOpts{}
   207  
   208  // NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
   209  func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
   210  	return &NamedMapOpts{
   211  		name:    name,
   212  		MapOpts: *NewMapOpts(values, validator),
   213  	}
   214  }
   215  
   216  // Name returns the name of the NamedMapOpts in the configuration.
   217  func (o *NamedMapOpts) Name() string {
   218  	return o.name
   219  }
   220  
   221  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   222  type ValidatorFctType func(val string) (string, error)
   223  
   224  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   225  type ValidatorFctListType func(val string) ([]string, error)
   226  
   227  // ValidateIPAddress validates an Ip address.
   228  func ValidateIPAddress(val string) (string, error) {
   229  	var ip = net.ParseIP(strings.TrimSpace(val))
   230  	if ip != nil {
   231  		return ip.String(), nil
   232  	}
   233  	return "", fmt.Errorf("%s is not an ip address", val)
   234  }
   235  
   236  // ValidateMACAddress validates a MAC address.
   237  func ValidateMACAddress(val string) (string, error) {
   238  	_, err := net.ParseMAC(strings.TrimSpace(val))
   239  	if err != nil {
   240  		return "", err
   241  	}
   242  	return val, nil
   243  }
   244  
   245  // ValidateDNSSearch validates domain for resolvconf search configuration.
   246  // A zero length domain is represented by a dot (.).
   247  func ValidateDNSSearch(val string) (string, error) {
   248  	if val = strings.Trim(val, " "); val == "." {
   249  		return val, nil
   250  	}
   251  	return validateDomain(val)
   252  }
   253  
   254  func validateDomain(val string) (string, error) {
   255  	if alphaRegexp.FindString(val) == "" {
   256  		return "", fmt.Errorf("%s is not a valid domain", val)
   257  	}
   258  	ns := domainRegexp.FindSubmatch([]byte(val))
   259  	if len(ns) > 0 && len(ns[1]) < 255 {
   260  		return string(ns[1]), nil
   261  	}
   262  	return "", fmt.Errorf("%s is not a valid domain", val)
   263  }
   264  
   265  // ValidateLabel validates that the specified string is a valid label, and returns it.
   266  // Labels are in the form on key=value.
   267  func ValidateLabel(val string) (string, error) {
   268  	if strings.Count(val, "=") < 1 {
   269  		return "", fmt.Errorf("bad attribute format: %s", val)
   270  	}
   271  	return val, nil
   272  }
   273  
   274  // ValidateSysctl validates a sysctl and returns it.
   275  func ValidateSysctl(val string) (string, error) {
   276  	validSysctlMap := map[string]bool{
   277  		"kernel.msgmax":          true,
   278  		"kernel.msgmnb":          true,
   279  		"kernel.msgmni":          true,
   280  		"kernel.sem":             true,
   281  		"kernel.shmall":          true,
   282  		"kernel.shmmax":          true,
   283  		"kernel.shmmni":          true,
   284  		"kernel.shm_rmid_forced": true,
   285  	}
   286  	validSysctlPrefixes := []string{
   287  		"net.",
   288  		"fs.mqueue.",
   289  	}
   290  	arr := strings.Split(val, "=")
   291  	if len(arr) < 2 {
   292  		return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
   293  	}
   294  	if validSysctlMap[arr[0]] {
   295  		return val, nil
   296  	}
   297  
   298  	for _, vp := range validSysctlPrefixes {
   299  		if strings.HasPrefix(arr[0], vp) {
   300  			return val, nil
   301  		}
   302  	}
   303  	return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
   304  }
   305  
   306  // FilterOpt is a flag type for validating filters
   307  type FilterOpt struct {
   308  	filter filters.Args
   309  }
   310  
   311  // NewFilterOpt returns a new FilterOpt
   312  func NewFilterOpt() FilterOpt {
   313  	return FilterOpt{filter: filters.NewArgs()}
   314  }
   315  
   316  func (o *FilterOpt) String() string {
   317  	repr, err := filters.ToParam(o.filter)
   318  	if err != nil {
   319  		return "invalid filters"
   320  	}
   321  	return repr
   322  }
   323  
   324  // Set sets the value of the opt by parsing the command line value
   325  func (o *FilterOpt) Set(value string) error {
   326  	var err error
   327  	o.filter, err = filters.ParseFlag(value, o.filter)
   328  	return err
   329  }
   330  
   331  // Type returns the option type
   332  func (o *FilterOpt) Type() string {
   333  	return "filter"
   334  }
   335  
   336  // Value returns the value of this option
   337  func (o *FilterOpt) Value() filters.Args {
   338  	return o.filter
   339  }
   340  
   341  // NanoCPUs is a type for fixed point fractional number.
   342  type NanoCPUs int64
   343  
   344  // String returns the string format of the number
   345  func (c *NanoCPUs) String() string {
   346  	return big.NewRat(c.Value(), 1e9).FloatString(3)
   347  }
   348  
   349  // Set sets the value of the NanoCPU by passing a string
   350  func (c *NanoCPUs) Set(value string) error {
   351  	cpus, err := ParseCPUs(value)
   352  	*c = NanoCPUs(cpus)
   353  	return err
   354  }
   355  
   356  // Type returns the type
   357  func (c *NanoCPUs) Type() string {
   358  	return "decimal"
   359  }
   360  
   361  // Value returns the value in int64
   362  func (c *NanoCPUs) Value() int64 {
   363  	return int64(*c)
   364  }
   365  
   366  // ParseCPUs takes a string ratio and returns an integer value of nano cpus
   367  func ParseCPUs(value string) (int64, error) {
   368  	cpu, ok := new(big.Rat).SetString(value)
   369  	if !ok {
   370  		return 0, fmt.Errorf("failed to parse %v as a rational number", value)
   371  	}
   372  	nano := cpu.Mul(cpu, big.NewRat(1e9, 1))
   373  	if !nano.IsInt() {
   374  		return 0, fmt.Errorf("value is too precise")
   375  	}
   376  	return nano.Num().Int64(), nil
   377  }
   378  
   379  // ParseLink parses and validates the specified string as a link format (name:alias)
   380  func ParseLink(val string) (string, string, error) {
   381  	if val == "" {
   382  		return "", "", fmt.Errorf("empty string specified for links")
   383  	}
   384  	arr := strings.Split(val, ":")
   385  	if len(arr) > 2 {
   386  		return "", "", fmt.Errorf("bad format for links: %s", val)
   387  	}
   388  	if len(arr) == 1 {
   389  		return val, val, nil
   390  	}
   391  	// This is kept because we can actually get a HostConfig with links
   392  	// from an already created container and the format is not `foo:bar`
   393  	// but `/foo:/c1/bar`
   394  	if strings.HasPrefix(arr[0], "/") {
   395  		_, alias := path.Split(arr[1])
   396  		return arr[0][1:], alias, nil
   397  	}
   398  	return arr[0], arr[1], nil
   399  }
   400  
   401  // ValidateLink validates that the specified string has a valid link format (containerName:alias).
   402  func ValidateLink(val string) (string, error) {
   403  	_, _, err := ParseLink(val)
   404  	return val, err
   405  }
   406  
   407  // MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
   408  type MemBytes int64
   409  
   410  // String returns the string format of the human readable memory bytes
   411  func (m *MemBytes) String() string {
   412  	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
   413  	// We return "0" in case value is 0 here so that the default value is hidden.
   414  	// (Sometimes "default 0 B" is actually misleading)
   415  	if m.Value() != 0 {
   416  		return units.BytesSize(float64(m.Value()))
   417  	}
   418  	return "0"
   419  }
   420  
   421  // Set sets the value of the MemBytes by passing a string
   422  func (m *MemBytes) Set(value string) error {
   423  	val, err := units.RAMInBytes(value)
   424  	*m = MemBytes(val)
   425  	return err
   426  }
   427  
   428  // Type returns the type
   429  func (m *MemBytes) Type() string {
   430  	return "bytes"
   431  }
   432  
   433  // Value returns the value in int64
   434  func (m *MemBytes) Value() int64 {
   435  	return int64(*m)
   436  }
   437  
   438  // UnmarshalJSON is the customized unmarshaler for MemBytes
   439  func (m *MemBytes) UnmarshalJSON(s []byte) error {
   440  	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
   441  		return fmt.Errorf("invalid size: %q", s)
   442  	}
   443  	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
   444  	*m = MemBytes(val)
   445  	return err
   446  }