github.com/rawahars/moby@v24.0.4+incompatible/opts/opts.go (about)

     1  package opts // import "github.com/docker/docker/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  // NamedMapMapOpts is a MapMapOpts with a configuration name.
   150  // This struct is useful to keep reference to the assigned
   151  // field name in the internal configuration struct.
   152  type NamedMapMapOpts struct {
   153  	name string
   154  	MapMapOpts
   155  }
   156  
   157  // NewNamedMapMapOpts creates a reference to a new NamedMapOpts struct.
   158  func NewNamedMapMapOpts(name string, values map[string]map[string]string, validator ValidatorFctType) *NamedMapMapOpts {
   159  	return &NamedMapMapOpts{
   160  		name:       name,
   161  		MapMapOpts: *NewMapMapOpts(values, validator),
   162  	}
   163  }
   164  
   165  // Name returns the name of the NamedListOpts in the configuration.
   166  func (o *NamedMapMapOpts) Name() string {
   167  	return o.name
   168  }
   169  
   170  // MapMapOpts holds a map of maps of values and a validation function.
   171  type MapMapOpts struct {
   172  	values    map[string]map[string]string
   173  	validator ValidatorFctType
   174  }
   175  
   176  // Set validates if needed the input value and add it to the
   177  // internal map, by splitting on '='.
   178  func (opts *MapMapOpts) Set(value string) error {
   179  	if opts.validator != nil {
   180  		v, err := opts.validator(value)
   181  		if err != nil {
   182  			return err
   183  		}
   184  		value = v
   185  	}
   186  	rk, rv, found := strings.Cut(value, "=")
   187  	if !found {
   188  		return fmt.Errorf("invalid value %q for map option, should be root-key=key=value", value)
   189  	}
   190  	k, v, found := strings.Cut(rv, "=")
   191  	if !found {
   192  		return fmt.Errorf("invalid value %q for map option, should be root-key=key=value", value)
   193  	}
   194  	if _, ok := opts.values[rk]; !ok {
   195  		opts.values[rk] = make(map[string]string)
   196  	}
   197  	opts.values[rk][k] = v
   198  	return nil
   199  }
   200  
   201  // GetAll returns the values of MapOpts as a map.
   202  func (opts *MapMapOpts) GetAll() map[string]map[string]string {
   203  	return opts.values
   204  }
   205  
   206  func (opts *MapMapOpts) String() string {
   207  	return fmt.Sprintf("%v", opts.values)
   208  }
   209  
   210  // Type returns a string name for this Option type
   211  func (opts *MapMapOpts) Type() string {
   212  	return "mapmap"
   213  }
   214  
   215  // NewMapMapOpts creates a new MapMapOpts with the specified map of values and a validator.
   216  func NewMapMapOpts(values map[string]map[string]string, validator ValidatorFctType) *MapMapOpts {
   217  	if values == nil {
   218  		values = make(map[string]map[string]string)
   219  	}
   220  	return &MapMapOpts{
   221  		values:    values,
   222  		validator: validator,
   223  	}
   224  }
   225  
   226  // MapOpts holds a map of values and a validation function.
   227  type MapOpts struct {
   228  	values    map[string]string
   229  	validator ValidatorFctType
   230  }
   231  
   232  // Set validates if needed the input value and add it to the
   233  // internal map, by splitting on '='.
   234  func (opts *MapOpts) Set(value string) error {
   235  	if opts.validator != nil {
   236  		v, err := opts.validator(value)
   237  		if err != nil {
   238  			return err
   239  		}
   240  		value = v
   241  	}
   242  	k, v, _ := strings.Cut(value, "=")
   243  	(opts.values)[k] = v
   244  	return nil
   245  }
   246  
   247  // GetAll returns the values of MapOpts as a map.
   248  func (opts *MapOpts) GetAll() map[string]string {
   249  	return opts.values
   250  }
   251  
   252  func (opts *MapOpts) String() string {
   253  	return fmt.Sprintf("%v", opts.values)
   254  }
   255  
   256  // Type returns a string name for this Option type
   257  func (opts *MapOpts) Type() string {
   258  	return "map"
   259  }
   260  
   261  // NewMapOpts creates a new MapOpts with the specified map of values and a validator.
   262  func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
   263  	if values == nil {
   264  		values = make(map[string]string)
   265  	}
   266  	return &MapOpts{
   267  		values:    values,
   268  		validator: validator,
   269  	}
   270  }
   271  
   272  // NamedMapOpts is a MapOpts struct with a configuration name.
   273  // This struct is useful to keep reference to the assigned
   274  // field name in the internal configuration struct.
   275  type NamedMapOpts struct {
   276  	name string
   277  	MapOpts
   278  }
   279  
   280  var _ NamedOption = &NamedMapOpts{}
   281  
   282  // NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
   283  func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
   284  	return &NamedMapOpts{
   285  		name:    name,
   286  		MapOpts: *NewMapOpts(values, validator),
   287  	}
   288  }
   289  
   290  // Name returns the name of the NamedMapOpts in the configuration.
   291  func (o *NamedMapOpts) Name() string {
   292  	return o.name
   293  }
   294  
   295  // ValidatorFctType defines a validator function that returns a validated string and/or an error.
   296  type ValidatorFctType func(val string) (string, error)
   297  
   298  // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
   299  type ValidatorFctListType func(val string) ([]string, error)
   300  
   301  // ValidateIPAddress validates an Ip address.
   302  func ValidateIPAddress(val string) (string, error) {
   303  	var ip = net.ParseIP(strings.TrimSpace(val))
   304  	if ip != nil {
   305  		return ip.String(), nil
   306  	}
   307  	return "", fmt.Errorf("%s is not an ip address", val)
   308  }
   309  
   310  // ValidateDNSSearch validates domain for resolvconf search configuration.
   311  // A zero length domain is represented by a dot (.).
   312  func ValidateDNSSearch(val string) (string, error) {
   313  	if val = strings.Trim(val, " "); val == "." {
   314  		return val, nil
   315  	}
   316  	return validateDomain(val)
   317  }
   318  
   319  func validateDomain(val string) (string, error) {
   320  	if alphaRegexp.FindString(val) == "" {
   321  		return "", fmt.Errorf("%s is not a valid domain", val)
   322  	}
   323  	ns := domainRegexp.FindSubmatch([]byte(val))
   324  	if len(ns) > 0 && len(ns[1]) < 255 {
   325  		return string(ns[1]), nil
   326  	}
   327  	return "", fmt.Errorf("%s is not a valid domain", val)
   328  }
   329  
   330  // ValidateLabel validates that the specified string is a valid label,
   331  // it does not use the reserved namespaces com.docker.*, io.docker.*, org.dockerproject.*
   332  // and returns it.
   333  // Labels are in the form on key=value.
   334  func ValidateLabel(val string) (string, error) {
   335  	if strings.Count(val, "=") < 1 {
   336  		return "", fmt.Errorf("bad attribute format: %s", val)
   337  	}
   338  
   339  	lowered := strings.ToLower(val)
   340  	if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") ||
   341  		strings.HasPrefix(lowered, "org.dockerproject.") {
   342  		return "", fmt.Errorf(
   343  			"label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
   344  			val)
   345  	}
   346  
   347  	return val, nil
   348  }
   349  
   350  // ValidateSingleGenericResource validates that a single entry in the
   351  // generic resource list is valid.
   352  // i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
   353  func ValidateSingleGenericResource(val string) (string, error) {
   354  	if strings.Count(val, "=") < 1 {
   355  		return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
   356  	}
   357  	return val, nil
   358  }
   359  
   360  // ParseLink parses and validates the specified string as a link format (name:alias)
   361  func ParseLink(val string) (string, string, error) {
   362  	if val == "" {
   363  		return "", "", fmt.Errorf("empty string specified for links")
   364  	}
   365  	arr := strings.Split(val, ":")
   366  	if len(arr) > 2 {
   367  		return "", "", fmt.Errorf("bad format for links: %s", val)
   368  	}
   369  	if len(arr) == 1 {
   370  		return val, val, nil
   371  	}
   372  	// This is kept because we can actually get a HostConfig with links
   373  	// from an already created container and the format is not `foo:bar`
   374  	// but `/foo:/c1/bar`
   375  	if strings.HasPrefix(arr[0], "/") {
   376  		_, alias := path.Split(arr[1])
   377  		return arr[0][1:], alias, nil
   378  	}
   379  	return arr[0], arr[1], nil
   380  }
   381  
   382  // MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
   383  type MemBytes int64
   384  
   385  // String returns the string format of the human readable memory bytes
   386  func (m *MemBytes) String() string {
   387  	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
   388  	// We return "0" in case value is 0 here so that the default value is hidden.
   389  	// (Sometimes "default 0 B" is actually misleading)
   390  	if m.Value() != 0 {
   391  		return units.BytesSize(float64(m.Value()))
   392  	}
   393  	return "0"
   394  }
   395  
   396  // Set sets the value of the MemBytes by passing a string
   397  func (m *MemBytes) Set(value string) error {
   398  	val, err := units.RAMInBytes(value)
   399  	*m = MemBytes(val)
   400  	return err
   401  }
   402  
   403  // Type returns the type
   404  func (m *MemBytes) Type() string {
   405  	return "bytes"
   406  }
   407  
   408  // Value returns the value in int64
   409  func (m *MemBytes) Value() int64 {
   410  	return int64(*m)
   411  }
   412  
   413  // UnmarshalJSON is the customized unmarshaler for MemBytes
   414  func (m *MemBytes) UnmarshalJSON(s []byte) error {
   415  	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
   416  		return fmt.Errorf("invalid size: %q", s)
   417  	}
   418  	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
   419  	*m = MemBytes(val)
   420  	return err
   421  }