github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/opts/opts.go (about)

     1  package opts // import "github.com/demonoid81/moby/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,
   258  // it does not use the reserved namespaces com.docker.*, io.docker.*, org.dockerproject.*
   259  // and returns it.
   260  // Labels are in the form on key=value.
   261  func ValidateLabel(val string) (string, error) {
   262  	if strings.Count(val, "=") < 1 {
   263  		return "", fmt.Errorf("bad attribute format: %s", val)
   264  	}
   265  
   266  	lowered := strings.ToLower(val)
   267  	if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") ||
   268  		strings.HasPrefix(lowered, "org.dockerproject.") {
   269  		return "", fmt.Errorf(
   270  			"label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
   271  			val)
   272  	}
   273  
   274  	return val, nil
   275  }
   276  
   277  // ValidateSingleGenericResource validates that a single entry in the
   278  // generic resource list is valid.
   279  // i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
   280  func ValidateSingleGenericResource(val string) (string, error) {
   281  	if strings.Count(val, "=") < 1 {
   282  		return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
   283  	}
   284  	return val, nil
   285  }
   286  
   287  // ParseLink parses and validates the specified string as a link format (name:alias)
   288  func ParseLink(val string) (string, string, error) {
   289  	if val == "" {
   290  		return "", "", fmt.Errorf("empty string specified for links")
   291  	}
   292  	arr := strings.Split(val, ":")
   293  	if len(arr) > 2 {
   294  		return "", "", fmt.Errorf("bad format for links: %s", val)
   295  	}
   296  	if len(arr) == 1 {
   297  		return val, val, nil
   298  	}
   299  	// This is kept because we can actually get a HostConfig with links
   300  	// from an already created container and the format is not `foo:bar`
   301  	// but `/foo:/c1/bar`
   302  	if strings.HasPrefix(arr[0], "/") {
   303  		_, alias := path.Split(arr[1])
   304  		return arr[0][1:], alias, nil
   305  	}
   306  	return arr[0], arr[1], nil
   307  }
   308  
   309  // MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
   310  type MemBytes int64
   311  
   312  // String returns the string format of the human readable memory bytes
   313  func (m *MemBytes) String() string {
   314  	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
   315  	// We return "0" in case value is 0 here so that the default value is hidden.
   316  	// (Sometimes "default 0 B" is actually misleading)
   317  	if m.Value() != 0 {
   318  		return units.BytesSize(float64(m.Value()))
   319  	}
   320  	return "0"
   321  }
   322  
   323  // Set sets the value of the MemBytes by passing a string
   324  func (m *MemBytes) Set(value string) error {
   325  	val, err := units.RAMInBytes(value)
   326  	*m = MemBytes(val)
   327  	return err
   328  }
   329  
   330  // Type returns the type
   331  func (m *MemBytes) Type() string {
   332  	return "bytes"
   333  }
   334  
   335  // Value returns the value in int64
   336  func (m *MemBytes) Value() int64 {
   337  	return int64(*m)
   338  }
   339  
   340  // UnmarshalJSON is the customized unmarshaler for MemBytes
   341  func (m *MemBytes) UnmarshalJSON(s []byte) error {
   342  	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
   343  		return fmt.Errorf("invalid size: %q", s)
   344  	}
   345  	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
   346  	*m = MemBytes(val)
   347  	return err
   348  }