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