github.com/vvnotw/moby@v1.13.1/opts/opts.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"net"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/docker/docker/api/types/filters"
    11  )
    12  
    13  var (
    14  	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
    15  	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*$`)
    16  )
    17  
    18  // ListOpts holds a list of values and a validation function.
    19  type ListOpts struct {
    20  	values    *[]string
    21  	validator ValidatorFctType
    22  }
    23  
    24  // NewListOpts creates a new ListOpts with the specified validator.
    25  func NewListOpts(validator ValidatorFctType) ListOpts {
    26  	var values []string
    27  	return *NewListOptsRef(&values, validator)
    28  }
    29  
    30  // NewListOptsRef creates a new ListOpts with the specified values and validator.
    31  func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
    32  	return &ListOpts{
    33  		values:    values,
    34  		validator: validator,
    35  	}
    36  }
    37  
    38  func (opts *ListOpts) String() string {
    39  	return fmt.Sprintf("%v", []string((*opts.values)))
    40  }
    41  
    42  // Set validates if needed the input value and adds it to the
    43  // internal slice.
    44  func (opts *ListOpts) Set(value string) error {
    45  	if opts.validator != nil {
    46  		v, err := opts.validator(value)
    47  		if err != nil {
    48  			return err
    49  		}
    50  		value = v
    51  	}
    52  	(*opts.values) = append((*opts.values), value)
    53  	return nil
    54  }
    55  
    56  // Delete removes the specified element from the slice.
    57  func (opts *ListOpts) Delete(key string) {
    58  	for i, k := range *opts.values {
    59  		if k == key {
    60  			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
    61  			return
    62  		}
    63  	}
    64  }
    65  
    66  // GetMap returns the content of values in a map in order to avoid
    67  // duplicates.
    68  func (opts *ListOpts) GetMap() map[string]struct{} {
    69  	ret := make(map[string]struct{})
    70  	for _, k := range *opts.values {
    71  		ret[k] = struct{}{}
    72  	}
    73  	return ret
    74  }
    75  
    76  // GetAll returns the values of slice.
    77  func (opts *ListOpts) GetAll() []string {
    78  	return (*opts.values)
    79  }
    80  
    81  // GetAllOrEmpty returns the values of the slice
    82  // or an empty slice when there are no values.
    83  func (opts *ListOpts) GetAllOrEmpty() []string {
    84  	v := *opts.values
    85  	if v == nil {
    86  		return make([]string, 0)
    87  	}
    88  	return v
    89  }
    90  
    91  // Get checks the existence of the specified key.
    92  func (opts *ListOpts) Get(key string) bool {
    93  	for _, k := range *opts.values {
    94  		if k == key {
    95  			return true
    96  		}
    97  	}
    98  	return false
    99  }
   100  
   101  // Len returns the amount of element in the slice.
   102  func (opts *ListOpts) Len() int {
   103  	return len((*opts.values))
   104  }
   105  
   106  // Type returns a string name for this Option type
   107  func (opts *ListOpts) Type() string {
   108  	return "list"
   109  }
   110  
   111  // NamedOption is an interface that list and map options
   112  // with names implement.
   113  type NamedOption interface {
   114  	Name() string
   115  }
   116  
   117  // NamedListOpts is a ListOpts with a configuration name.
   118  // This struct is useful to keep reference to the assigned
   119  // field name in the internal configuration struct.
   120  type NamedListOpts struct {
   121  	name string
   122  	ListOpts
   123  }
   124  
   125  var _ NamedOption = &NamedListOpts{}
   126  
   127  // NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
   128  func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
   129  	return &NamedListOpts{
   130  		name:     name,
   131  		ListOpts: *NewListOptsRef(values, validator),
   132  	}
   133  }
   134  
   135  // Name returns the name of the NamedListOpts in the configuration.
   136  func (o *NamedListOpts) Name() string {
   137  	return o.name
   138  }
   139  
   140  // MapOpts holds a map of values and a validation function.
   141  type MapOpts struct {
   142  	values    map[string]string
   143  	validator ValidatorFctType
   144  }
   145  
   146  // Set validates if needed the input value and add it to the
   147  // internal map, by splitting on '='.
   148  func (opts *MapOpts) Set(value string) error {
   149  	if opts.validator != nil {
   150  		v, err := opts.validator(value)
   151  		if err != nil {
   152  			return err
   153  		}
   154  		value = v
   155  	}
   156  	vals := strings.SplitN(value, "=", 2)
   157  	if len(vals) == 1 {
   158  		(opts.values)[vals[0]] = ""
   159  	} else {
   160  		(opts.values)[vals[0]] = vals[1]
   161  	}
   162  	return nil
   163  }
   164  
   165  // GetAll returns the values of MapOpts as a map.
   166  func (opts *MapOpts) GetAll() map[string]string {
   167  	return opts.values
   168  }
   169  
   170  func (opts *MapOpts) String() string {
   171  	return fmt.Sprintf("%v", map[string]string((opts.values)))
   172  }
   173  
   174  // Type returns a string name for this Option type
   175  func (opts *MapOpts) Type() string {
   176  	return "map"
   177  }
   178  
   179  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   180  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   181  	if values == nil {
   182  		values = make(map[string]string)
   183  	}
   184  	return &MapOpts{
   185  		values:    values,
   186  		validator: validator,
   187  	}
   188  }
   189  
   190  // NamedMapOpts is a MapOpts struct with a configuration name.
   191  // This struct is useful to keep reference to the assigned
   192  // field name in the internal configuration struct.
   193  type NamedMapOpts struct {
   194  	name string
   195  	MapOpts
   196  }
   197  
   198  var _ NamedOption = &NamedMapOpts{}
   199  
   200  // NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
   201  func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
   202  	return &NamedMapOpts{
   203  		name:    name,
   204  		MapOpts: *NewMapOpts(values, validator),
   205  	}
   206  }
   207  
   208  // Name returns the name of the NamedMapOpts in the configuration.
   209  func (o *NamedMapOpts) Name() string {
   210  	return o.name
   211  }
   212  
   213  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   214  type ValidatorFctType func(val string) (string, error)
   215  
   216  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   217  type ValidatorFctListType func(val string) ([]string, error)
   218  
   219  // ValidateIPAddress validates an Ip address.
   220  func ValidateIPAddress(val string) (string, error) {
   221  	var ip = net.ParseIP(strings.TrimSpace(val))
   222  	if ip != nil {
   223  		return ip.String(), nil
   224  	}
   225  	return "", fmt.Errorf("%s is not an ip address", val)
   226  }
   227  
   228  // ValidateDNSSearch validates domain for resolvconf search configuration.
   229  // A zero length domain is represented by a dot (.).
   230  func ValidateDNSSearch(val string) (string, error) {
   231  	if val = strings.Trim(val, " "); val == "." {
   232  		return val, nil
   233  	}
   234  	return validateDomain(val)
   235  }
   236  
   237  func validateDomain(val string) (string, error) {
   238  	if alphaRegexp.FindString(val) == "" {
   239  		return "", fmt.Errorf("%s is not a valid domain", val)
   240  	}
   241  	ns := domainRegexp.FindSubmatch([]byte(val))
   242  	if len(ns) > 0 && len(ns[1]) < 255 {
   243  		return string(ns[1]), nil
   244  	}
   245  	return "", fmt.Errorf("%s is not a valid domain", val)
   246  }
   247  
   248  // ValidateLabel validates that the specified string is a valid label, and returns it.
   249  // Labels are in the form on key=value.
   250  func ValidateLabel(val string) (string, error) {
   251  	if strings.Count(val, "=") < 1 {
   252  		return "", fmt.Errorf("bad attribute format: %s", val)
   253  	}
   254  	return val, nil
   255  }
   256  
   257  // ValidateSysctl validates a sysctl and returns it.
   258  func ValidateSysctl(val string) (string, error) {
   259  	validSysctlMap := map[string]bool{
   260  		"kernel.msgmax":          true,
   261  		"kernel.msgmnb":          true,
   262  		"kernel.msgmni":          true,
   263  		"kernel.sem":             true,
   264  		"kernel.shmall":          true,
   265  		"kernel.shmmax":          true,
   266  		"kernel.shmmni":          true,
   267  		"kernel.shm_rmid_forced": true,
   268  	}
   269  	validSysctlPrefixes := []string{
   270  		"net.",
   271  		"fs.mqueue.",
   272  	}
   273  	arr := strings.Split(val, "=")
   274  	if len(arr) < 2 {
   275  		return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
   276  	}
   277  	if validSysctlMap[arr[0]] {
   278  		return val, nil
   279  	}
   280  
   281  	for _, vp := range validSysctlPrefixes {
   282  		if strings.HasPrefix(arr[0], vp) {
   283  			return val, nil
   284  		}
   285  	}
   286  	return "", fmt.Errorf("sysctl '%s' is not whitelisted", val)
   287  }
   288  
   289  // FilterOpt is a flag type for validating filters
   290  type FilterOpt struct {
   291  	filter filters.Args
   292  }
   293  
   294  // NewFilterOpt returns a new FilterOpt
   295  func NewFilterOpt() FilterOpt {
   296  	return FilterOpt{filter: filters.NewArgs()}
   297  }
   298  
   299  func (o *FilterOpt) String() string {
   300  	repr, err := filters.ToParam(o.filter)
   301  	if err != nil {
   302  		return "invalid filters"
   303  	}
   304  	return repr
   305  }
   306  
   307  // Set sets the value of the opt by parsing the command line value
   308  func (o *FilterOpt) Set(value string) error {
   309  	var err error
   310  	o.filter, err = filters.ParseFlag(value, o.filter)
   311  	return err
   312  }
   313  
   314  // Type returns the option type
   315  func (o *FilterOpt) Type() string {
   316  	return "filter"
   317  }
   318  
   319  // Value returns the value of this option
   320  func (o *FilterOpt) Value() filters.Args {
   321  	return o.filter
   322  }
   323  
   324  // NanoCPUs is a type for fixed point fractional number.
   325  type NanoCPUs int64
   326  
   327  // String returns the string format of the number
   328  func (c *NanoCPUs) String() string {
   329  	return big.NewRat(c.Value(), 1e9).FloatString(3)
   330  }
   331  
   332  // Set sets the value of the NanoCPU by passing a string
   333  func (c *NanoCPUs) Set(value string) error {
   334  	cpus, err := ParseCPUs(value)
   335  	*c = NanoCPUs(cpus)
   336  	return err
   337  }
   338  
   339  // Type returns the type
   340  func (c *NanoCPUs) Type() string {
   341  	return "decimal"
   342  }
   343  
   344  // Value returns the value in int64
   345  func (c *NanoCPUs) Value() int64 {
   346  	return int64(*c)
   347  }
   348  
   349  // ParseCPUs takes a string ratio and returns an integer value of nano cpus
   350  func ParseCPUs(value string) (int64, error) {
   351  	cpu, ok := new(big.Rat).SetString(value)
   352  	if !ok {
   353  		return 0, fmt.Errorf("failed to parse %v as a rational number", value)
   354  	}
   355  	nano := cpu.Mul(cpu, big.NewRat(1e9, 1))
   356  	if !nano.IsInt() {
   357  		return 0, fmt.Errorf("value is too precise")
   358  	}
   359  	return nano.Num().Int64(), nil
   360  }