github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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 return fmt.Sprintf("%v", []string((*opts.values))) 42 } 43 44 // Set validates if needed the input value and adds it to the 45 // internal slice. 46 func (opts *ListOpts) Set(value string) error { 47 if opts.validator != nil { 48 v, err := opts.validator(value) 49 if err != nil { 50 return err 51 } 52 value = v 53 } 54 (*opts.values) = append((*opts.values), value) 55 return nil 56 } 57 58 // Delete removes the specified element from the slice. 59 func (opts *ListOpts) Delete(key string) { 60 for i, k := range *opts.values { 61 if k == key { 62 (*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...) 63 return 64 } 65 } 66 } 67 68 // GetMap returns the content of values in a map in order to avoid 69 // duplicates. 70 func (opts *ListOpts) GetMap() map[string]struct{} { 71 ret := make(map[string]struct{}) 72 for _, k := range *opts.values { 73 ret[k] = struct{}{} 74 } 75 return ret 76 } 77 78 // GetAll returns the values of slice. 79 func (opts *ListOpts) GetAll() []string { 80 return (*opts.values) 81 } 82 83 // GetAllOrEmpty returns the values of the slice 84 // or an empty slice when there are no values. 85 func (opts *ListOpts) GetAllOrEmpty() []string { 86 v := *opts.values 87 if v == nil { 88 return make([]string, 0) 89 } 90 return v 91 } 92 93 // Get checks the existence of the specified key. 94 func (opts *ListOpts) Get(key string) bool { 95 for _, k := range *opts.values { 96 if k == key { 97 return true 98 } 99 } 100 return false 101 } 102 103 // Len returns the amount of element in the slice. 104 func (opts *ListOpts) Len() int { 105 return len((*opts.values)) 106 } 107 108 // Type returns a string name for this Option type 109 func (opts *ListOpts) Type() string { 110 return "list" 111 } 112 113 // WithValidator returns the ListOpts with validator set. 114 func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts { 115 opts.validator = validator 116 return opts 117 } 118 119 // NamedOption is an interface that list and map options 120 // with names implement. 121 type NamedOption interface { 122 Name() string 123 } 124 125 // NamedListOpts is a ListOpts with a configuration name. 126 // This struct is useful to keep reference to the assigned 127 // field name in the internal configuration struct. 128 type NamedListOpts struct { 129 name string 130 ListOpts 131 } 132 133 var _ NamedOption = &NamedListOpts{} 134 135 // NewNamedListOptsRef creates a reference to a new NamedListOpts struct. 136 func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts { 137 return &NamedListOpts{ 138 name: name, 139 ListOpts: *NewListOptsRef(values, validator), 140 } 141 } 142 143 // Name returns the name of the NamedListOpts in the configuration. 144 func (o *NamedListOpts) Name() string { 145 return o.name 146 } 147 148 // MapOpts holds a map of values and a validation function. 149 type MapOpts struct { 150 values map[string]string 151 validator ValidatorFctType 152 } 153 154 // Set validates if needed the input value and add it to the 155 // internal map, by splitting on '='. 156 func (opts *MapOpts) Set(value string) error { 157 if opts.validator != nil { 158 v, err := opts.validator(value) 159 if err != nil { 160 return err 161 } 162 value = v 163 } 164 vals := strings.SplitN(value, "=", 2) 165 if len(vals) == 1 { 166 (opts.values)[vals[0]] = "" 167 } else { 168 (opts.values)[vals[0]] = vals[1] 169 } 170 return nil 171 } 172 173 // GetAll returns the values of MapOpts as a map. 174 func (opts *MapOpts) GetAll() map[string]string { 175 return opts.values 176 } 177 178 func (opts *MapOpts) String() string { 179 return fmt.Sprintf("%v", map[string]string((opts.values))) 180 } 181 182 // Type returns a string name for this Option type 183 func (opts *MapOpts) Type() string { 184 return "map" 185 } 186 187 // NewMapOpts creates a new MapOpts with the specified map of values and a validator. 188 func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { 189 if values == nil { 190 values = make(map[string]string) 191 } 192 return &MapOpts{ 193 values: values, 194 validator: validator, 195 } 196 } 197 198 // NamedMapOpts is a MapOpts struct with a configuration name. 199 // This struct is useful to keep reference to the assigned 200 // field name in the internal configuration struct. 201 type NamedMapOpts struct { 202 name string 203 MapOpts 204 } 205 206 var _ NamedOption = &NamedMapOpts{} 207 208 // NewNamedMapOpts creates a reference to a new NamedMapOpts struct. 209 func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts { 210 return &NamedMapOpts{ 211 name: name, 212 MapOpts: *NewMapOpts(values, validator), 213 } 214 } 215 216 // Name returns the name of the NamedMapOpts in the configuration. 217 func (o *NamedMapOpts) Name() string { 218 return o.name 219 } 220 221 // ValidatorFctType defines a validator function that returns a validated string and/or an error. 222 type ValidatorFctType func(val string) (string, error) 223 224 // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error 225 type ValidatorFctListType func(val string) ([]string, error) 226 227 // ValidateIPAddress validates an Ip address. 228 func ValidateIPAddress(val string) (string, error) { 229 var ip = net.ParseIP(strings.TrimSpace(val)) 230 if ip != nil { 231 return ip.String(), nil 232 } 233 return "", fmt.Errorf("%s is not an ip address", val) 234 } 235 236 // ValidateMACAddress validates a MAC address. 237 func ValidateMACAddress(val string) (string, error) { 238 _, err := net.ParseMAC(strings.TrimSpace(val)) 239 if err != nil { 240 return "", err 241 } 242 return val, nil 243 } 244 245 // ValidateDNSSearch validates domain for resolvconf search configuration. 246 // A zero length domain is represented by a dot (.). 247 func ValidateDNSSearch(val string) (string, error) { 248 if val = strings.Trim(val, " "); val == "." { 249 return val, nil 250 } 251 return validateDomain(val) 252 } 253 254 func validateDomain(val string) (string, error) { 255 if alphaRegexp.FindString(val) == "" { 256 return "", fmt.Errorf("%s is not a valid domain", val) 257 } 258 ns := domainRegexp.FindSubmatch([]byte(val)) 259 if len(ns) > 0 && len(ns[1]) < 255 { 260 return string(ns[1]), nil 261 } 262 return "", fmt.Errorf("%s is not a valid domain", val) 263 } 264 265 // ValidateLabel validates that the specified string is a valid label, and returns it. 266 // Labels are in the form on key=value. 267 func ValidateLabel(val string) (string, error) { 268 if strings.Count(val, "=") < 1 { 269 return "", fmt.Errorf("bad attribute format: %s", val) 270 } 271 return val, nil 272 } 273 274 // ValidateSysctl validates a sysctl and returns it. 275 func ValidateSysctl(val string) (string, error) { 276 validSysctlMap := map[string]bool{ 277 "kernel.msgmax": true, 278 "kernel.msgmnb": true, 279 "kernel.msgmni": true, 280 "kernel.sem": true, 281 "kernel.shmall": true, 282 "kernel.shmmax": true, 283 "kernel.shmmni": true, 284 "kernel.shm_rmid_forced": true, 285 } 286 validSysctlPrefixes := []string{ 287 "net.", 288 "fs.mqueue.", 289 } 290 arr := strings.Split(val, "=") 291 if len(arr) < 2 { 292 return "", fmt.Errorf("sysctl '%s' is not whitelisted", val) 293 } 294 if validSysctlMap[arr[0]] { 295 return val, nil 296 } 297 298 for _, vp := range validSysctlPrefixes { 299 if strings.HasPrefix(arr[0], vp) { 300 return val, nil 301 } 302 } 303 return "", fmt.Errorf("sysctl '%s' is not whitelisted", val) 304 } 305 306 // FilterOpt is a flag type for validating filters 307 type FilterOpt struct { 308 filter filters.Args 309 } 310 311 // NewFilterOpt returns a new FilterOpt 312 func NewFilterOpt() FilterOpt { 313 return FilterOpt{filter: filters.NewArgs()} 314 } 315 316 func (o *FilterOpt) String() string { 317 repr, err := filters.ToParam(o.filter) 318 if err != nil { 319 return "invalid filters" 320 } 321 return repr 322 } 323 324 // Set sets the value of the opt by parsing the command line value 325 func (o *FilterOpt) Set(value string) error { 326 var err error 327 o.filter, err = filters.ParseFlag(value, o.filter) 328 return err 329 } 330 331 // Type returns the option type 332 func (o *FilterOpt) Type() string { 333 return "filter" 334 } 335 336 // Value returns the value of this option 337 func (o *FilterOpt) Value() filters.Args { 338 return o.filter 339 } 340 341 // NanoCPUs is a type for fixed point fractional number. 342 type NanoCPUs int64 343 344 // String returns the string format of the number 345 func (c *NanoCPUs) String() string { 346 return big.NewRat(c.Value(), 1e9).FloatString(3) 347 } 348 349 // Set sets the value of the NanoCPU by passing a string 350 func (c *NanoCPUs) Set(value string) error { 351 cpus, err := ParseCPUs(value) 352 *c = NanoCPUs(cpus) 353 return err 354 } 355 356 // Type returns the type 357 func (c *NanoCPUs) Type() string { 358 return "decimal" 359 } 360 361 // Value returns the value in int64 362 func (c *NanoCPUs) Value() int64 { 363 return int64(*c) 364 } 365 366 // ParseCPUs takes a string ratio and returns an integer value of nano cpus 367 func ParseCPUs(value string) (int64, error) { 368 cpu, ok := new(big.Rat).SetString(value) 369 if !ok { 370 return 0, fmt.Errorf("failed to parse %v as a rational number", value) 371 } 372 nano := cpu.Mul(cpu, big.NewRat(1e9, 1)) 373 if !nano.IsInt() { 374 return 0, fmt.Errorf("value is too precise") 375 } 376 return nano.Num().Int64(), nil 377 } 378 379 // ParseLink parses and validates the specified string as a link format (name:alias) 380 func ParseLink(val string) (string, string, error) { 381 if val == "" { 382 return "", "", fmt.Errorf("empty string specified for links") 383 } 384 arr := strings.Split(val, ":") 385 if len(arr) > 2 { 386 return "", "", fmt.Errorf("bad format for links: %s", val) 387 } 388 if len(arr) == 1 { 389 return val, val, nil 390 } 391 // This is kept because we can actually get a HostConfig with links 392 // from an already created container and the format is not `foo:bar` 393 // but `/foo:/c1/bar` 394 if strings.HasPrefix(arr[0], "/") { 395 _, alias := path.Split(arr[1]) 396 return arr[0][1:], alias, nil 397 } 398 return arr[0], arr[1], nil 399 } 400 401 // ValidateLink validates that the specified string has a valid link format (containerName:alias). 402 func ValidateLink(val string) (string, error) { 403 _, _, err := ParseLink(val) 404 return val, err 405 } 406 407 // MemBytes is a type for human readable memory bytes (like 128M, 2g, etc) 408 type MemBytes int64 409 410 // String returns the string format of the human readable memory bytes 411 func (m *MemBytes) String() string { 412 // NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not. 413 // We return "0" in case value is 0 here so that the default value is hidden. 414 // (Sometimes "default 0 B" is actually misleading) 415 if m.Value() != 0 { 416 return units.BytesSize(float64(m.Value())) 417 } 418 return "0" 419 } 420 421 // Set sets the value of the MemBytes by passing a string 422 func (m *MemBytes) Set(value string) error { 423 val, err := units.RAMInBytes(value) 424 *m = MemBytes(val) 425 return err 426 } 427 428 // Type returns the type 429 func (m *MemBytes) Type() string { 430 return "bytes" 431 } 432 433 // Value returns the value in int64 434 func (m *MemBytes) Value() int64 { 435 return int64(*m) 436 } 437 438 // UnmarshalJSON is the customized unmarshaler for MemBytes 439 func (m *MemBytes) UnmarshalJSON(s []byte) error { 440 if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' { 441 return fmt.Errorf("invalid size: %q", s) 442 } 443 val, err := units.RAMInBytes(string(s[1 : len(s)-1])) 444 *m = MemBytes(val) 445 return err 446 }