github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/opts/opts.go (about)

     1  package opts
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"path"
     7  	"regexp"
     8  	"strings"
     9  
    10  	units "github.com/docker/go-units"
    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  	if len(*opts.values) == 0 {
    40  		return ""
    41  	}
    42  	return fmt.Sprintf("%v", *opts.values)
    43  }
    44  
    45  // Set validates if needed the input value and adds it to the
    46  // internal slice.
    47  func (opts *ListOpts) Set(value string) error {
    48  	if opts.validator != nil {
    49  		v, err := opts.validator(value)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		value = v
    54  	}
    55  	(*opts.values) = append((*opts.values), value)
    56  	return nil
    57  }
    58  
    59  // Delete removes the specified element from the slice.
    60  func (opts *ListOpts) Delete(key string) {
    61  	for i, k := range *opts.values {
    62  		if k == key {
    63  			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
    64  			return
    65  		}
    66  	}
    67  }
    68  
    69  // GetMap returns the content of values in a map in order to avoid
    70  // duplicates.
    71  func (opts *ListOpts) GetMap() map[string]struct{} {
    72  	ret := make(map[string]struct{})
    73  	for _, k := range *opts.values {
    74  		ret[k] = struct{}{}
    75  	}
    76  	return ret
    77  }
    78  
    79  // GetAll returns the values of slice.
    80  func (opts *ListOpts) GetAll() []string {
    81  	return (*opts.values)
    82  }
    83  
    84  // GetAllOrEmpty returns the values of the slice
    85  // or an empty slice when there are no values.
    86  func (opts *ListOpts) GetAllOrEmpty() []string {
    87  	v := *opts.values
    88  	if v == nil {
    89  		return make([]string, 0)
    90  	}
    91  	return v
    92  }
    93  
    94  // Get checks the existence of the specified key.
    95  func (opts *ListOpts) Get(key string) bool {
    96  	for _, k := range *opts.values {
    97  		if k == key {
    98  			return true
    99  		}
   100  	}
   101  	return false
   102  }
   103  
   104  // Len returns the amount of element in the slice.
   105  func (opts *ListOpts) Len() int {
   106  	return len((*opts.values))
   107  }
   108  
   109  // Type returns a string name for this Option type
   110  func (opts *ListOpts) Type() string {
   111  	return "list"
   112  }
   113  
   114  // WithValidator returns the ListOpts with validator set.
   115  func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
   116  	opts.validator = validator
   117  	return opts
   118  }
   119  
   120  // NamedOption is an interface that list and map options
   121  // with names implement.
   122  type NamedOption interface {
   123  	Name() string
   124  }
   125  
   126  // NamedListOpts is a ListOpts with a configuration name.
   127  // This struct is useful to keep reference to the assigned
   128  // field name in the internal configuration struct.
   129  type NamedListOpts struct {
   130  	name string
   131  	ListOpts
   132  }
   133  
   134  var _ NamedOption = &NamedListOpts{}
   135  
   136  // NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
   137  func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
   138  	return &NamedListOpts{
   139  		name:     name,
   140  		ListOpts: *NewListOptsRef(values, validator),
   141  	}
   142  }
   143  
   144  // Name returns the name of the NamedListOpts in the configuration.
   145  func (o *NamedListOpts) Name() string {
   146  	return o.name
   147  }
   148  
   149  // MapOpts holds a map of values and a validation function.
   150  type MapOpts struct {
   151  	values    map[string]string
   152  	validator ValidatorFctType
   153  }
   154  
   155  // Set validates if needed the input value and add it to the
   156  // internal map, by splitting on '='.
   157  func (opts *MapOpts) Set(value string) error {
   158  	if opts.validator != nil {
   159  		v, err := opts.validator(value)
   160  		if err != nil {
   161  			return err
   162  		}
   163  		value = v
   164  	}
   165  	vals := strings.SplitN(value, "=", 2)
   166  	if len(vals) == 1 {
   167  		(opts.values)[vals[0]] = ""
   168  	} else {
   169  		(opts.values)[vals[0]] = vals[1]
   170  	}
   171  	return nil
   172  }
   173  
   174  // GetAll returns the values of MapOpts as a map.
   175  func (opts *MapOpts) GetAll() map[string]string {
   176  	return opts.values
   177  }
   178  
   179  func (opts *MapOpts) String() string {
   180  	return fmt.Sprintf("%v", opts.values)
   181  }
   182  
   183  // Type returns a string name for this Option type
   184  func (opts *MapOpts) Type() string {
   185  	return "map"
   186  }
   187  
   188  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   189  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   190  	if values == nil {
   191  		values = make(map[string]string)
   192  	}
   193  	return &MapOpts{
   194  		values:    values,
   195  		validator: validator,
   196  	}
   197  }
   198  
   199  // NamedMapOpts is a MapOpts struct with a configuration name.
   200  // This struct is useful to keep reference to the assigned
   201  // field name in the internal configuration struct.
   202  type NamedMapOpts struct {
   203  	name string
   204  	MapOpts
   205  }
   206  
   207  var _ NamedOption = &NamedMapOpts{}
   208  
   209  // NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
   210  func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
   211  	return &NamedMapOpts{
   212  		name:    name,
   213  		MapOpts: *NewMapOpts(values, validator),
   214  	}
   215  }
   216  
   217  // Name returns the name of the NamedMapOpts in the configuration.
   218  func (o *NamedMapOpts) Name() string {
   219  	return o.name
   220  }
   221  
   222  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   223  type ValidatorFctType func(val string) (string, error)
   224  
   225  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   226  type ValidatorFctListType func(val string) ([]string, error)
   227  
   228  // ValidateIPAddress validates an Ip address.
   229  func ValidateIPAddress(val string) (string, error) {
   230  	var ip = net.ParseIP(strings.TrimSpace(val))
   231  	if ip != nil {
   232  		return ip.String(), nil
   233  	}
   234  	return "", fmt.Errorf("%s is not an ip address", val)
   235  }
   236  
   237  // ValidateDNSSearch validates domain for resolvconf search configuration.
   238  // A zero length domain is represented by a dot (.).
   239  func ValidateDNSSearch(val string) (string, error) {
   240  	if val = strings.Trim(val, " "); val == "." {
   241  		return val, nil
   242  	}
   243  	return validateDomain(val)
   244  }
   245  
   246  func validateDomain(val string) (string, error) {
   247  	if alphaRegexp.FindString(val) == "" {
   248  		return "", fmt.Errorf("%s is not a valid domain", val)
   249  	}
   250  	ns := domainRegexp.FindSubmatch([]byte(val))
   251  	if len(ns) > 0 && len(ns[1]) < 255 {
   252  		return string(ns[1]), nil
   253  	}
   254  	return "", fmt.Errorf("%s is not a valid domain", val)
   255  }
   256  
   257  // ValidateLabel validates that the specified string is a valid label, and returns it.
   258  // Labels are in the form on key=value.
   259  func ValidateLabel(val string) (string, error) {
   260  	if strings.Count(val, "=") < 1 {
   261  		return "", fmt.Errorf("bad attribute format: %s", val)
   262  	}
   263  	return val, nil
   264  }
   265  
   266  // ValidateSingleGenericResource validates that a single entry in the
   267  // generic resource list is valid.
   268  // i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
   269  func ValidateSingleGenericResource(val string) (string, error) {
   270  	if strings.Count(val, "=") < 1 {
   271  		return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
   272  	}
   273  	return val, nil
   274  }
   275  
   276  // ParseLink parses and validates the specified string as a link format (name:alias)
   277  func ParseLink(val string) (string, string, error) {
   278  	if val == "" {
   279  		return "", "", fmt.Errorf("empty string specified for links")
   280  	}
   281  	arr := strings.Split(val, ":")
   282  	if len(arr) > 2 {
   283  		return "", "", fmt.Errorf("bad format for links: %s", val)
   284  	}
   285  	if len(arr) == 1 {
   286  		return val, val, nil
   287  	}
   288  	// This is kept because we can actually get a HostConfig with links
   289  	// from an already created container and the format is not `foo:bar`
   290  	// but `/foo:/c1/bar`
   291  	if strings.HasPrefix(arr[0], "/") {
   292  		_, alias := path.Split(arr[1])
   293  		return arr[0][1:], alias, nil
   294  	}
   295  	return arr[0], arr[1], nil
   296  }
   297  
   298  // MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
   299  type MemBytes int64
   300  
   301  // String returns the string format of the human readable memory bytes
   302  func (m *MemBytes) String() string {
   303  	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
   304  	// We return "0" in case value is 0 here so that the default value is hidden.
   305  	// (Sometimes "default 0 B" is actually misleading)
   306  	if m.Value() != 0 {
   307  		return units.BytesSize(float64(m.Value()))
   308  	}
   309  	return "0"
   310  }
   311  
   312  // Set sets the value of the MemBytes by passing a string
   313  func (m *MemBytes) Set(value string) error {
   314  	val, err := units.RAMInBytes(value)
   315  	*m = MemBytes(val)
   316  	return err
   317  }
   318  
   319  // Type returns the type
   320  func (m *MemBytes) Type() string {
   321  	return "bytes"
   322  }
   323  
   324  // Value returns the value in int64
   325  func (m *MemBytes) Value() int64 {
   326  	return int64(*m)
   327  }
   328  
   329  // UnmarshalJSON is the customized unmarshaler for MemBytes
   330  func (m *MemBytes) UnmarshalJSON(s []byte) error {
   331  	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
   332  		return fmt.Errorf("invalid size: %q", s)
   333  	}
   334  	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
   335  	*m = MemBytes(val)
   336  	return err
   337  }