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 }