github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/opts/opts.go (about) 1 package opts 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/docker/docker/pkg/blkiodev" 12 "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 add 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 //MapOpts holds a map of values and a validation function. 109 type MapOpts struct { 110 values map[string]string 111 validator ValidatorFctType 112 } 113 114 // Set validates if needed the input value and add it to the 115 // internal map, by splitting on '='. 116 func (opts *MapOpts) Set(value string) error { 117 if opts.validator != nil { 118 v, err := opts.validator(value) 119 if err != nil { 120 return err 121 } 122 value = v 123 } 124 vals := strings.SplitN(value, "=", 2) 125 if len(vals) == 1 { 126 (opts.values)[vals[0]] = "" 127 } else { 128 (opts.values)[vals[0]] = vals[1] 129 } 130 return nil 131 } 132 133 // GetAll returns the values of MapOpts as a map. 134 func (opts *MapOpts) GetAll() map[string]string { 135 return opts.values 136 } 137 138 func (opts *MapOpts) String() string { 139 return fmt.Sprintf("%v", map[string]string((opts.values))) 140 } 141 142 // NewMapOpts creates a new MapOpts with the specified map of values and a validator. 143 func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { 144 if values == nil { 145 values = make(map[string]string) 146 } 147 return &MapOpts{ 148 values: values, 149 validator: validator, 150 } 151 } 152 153 // ValidatorFctType defines a validator function that returns a validated string and/or an error. 154 type ValidatorFctType func(val string) (string, error) 155 156 // ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error. 157 type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error) 158 159 // ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error. 160 type ValidatorThrottleFctType func(val string) (*blkiodev.ThrottleDevice, error) 161 162 // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error 163 type ValidatorFctListType func(val string) ([]string, error) 164 165 // ValidateAttach validates that the specified string is a valid attach option. 166 func ValidateAttach(val string) (string, error) { 167 s := strings.ToLower(val) 168 for _, str := range []string{"stdin", "stdout", "stderr"} { 169 if s == str { 170 return s, nil 171 } 172 } 173 return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR") 174 } 175 176 // ValidateWeightDevice validates that the specified string has a valid device-weight format. 177 func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) { 178 split := strings.SplitN(val, ":", 2) 179 if len(split) != 2 { 180 return nil, fmt.Errorf("bad format: %s", val) 181 } 182 if !strings.HasPrefix(split[0], "/dev/") { 183 return nil, fmt.Errorf("bad format for device path: %s", val) 184 } 185 weight, err := strconv.ParseUint(split[1], 10, 0) 186 if err != nil { 187 return nil, fmt.Errorf("invalid weight for device: %s", val) 188 } 189 if weight > 0 && (weight < 10 || weight > 1000) { 190 return nil, fmt.Errorf("invalid weight for device: %s", val) 191 } 192 193 return &blkiodev.WeightDevice{ 194 Path: split[0], 195 Weight: uint16(weight), 196 }, nil 197 } 198 199 // ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format. 200 func ValidateThrottleBpsDevice(val string) (*blkiodev.ThrottleDevice, error) { 201 split := strings.SplitN(val, ":", 2) 202 if len(split) != 2 { 203 return nil, fmt.Errorf("bad format: %s", val) 204 } 205 if !strings.HasPrefix(split[0], "/dev/") { 206 return nil, fmt.Errorf("bad format for device path: %s", val) 207 } 208 rate, err := units.RAMInBytes(split[1]) 209 if err != nil { 210 return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) 211 } 212 if rate < 0 { 213 return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) 214 } 215 216 return &blkiodev.ThrottleDevice{ 217 Path: split[0], 218 Rate: uint64(rate), 219 }, nil 220 } 221 222 // ValidateEnv validates an environment variable and returns it. 223 // If no value is specified, it returns the current value using os.Getenv. 224 // 225 // As on ParseEnvFile and related to #16585, environment variable names 226 // are not validate what so ever, it's up to application inside docker 227 // to validate them or not. 228 func ValidateEnv(val string) (string, error) { 229 arr := strings.Split(val, "=") 230 if len(arr) > 1 { 231 return val, nil 232 } 233 if !doesEnvExist(val) { 234 return val, nil 235 } 236 return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil 237 } 238 239 // ValidateIPAddress validates an Ip address. 240 func ValidateIPAddress(val string) (string, error) { 241 var ip = net.ParseIP(strings.TrimSpace(val)) 242 if ip != nil { 243 return ip.String(), nil 244 } 245 return "", fmt.Errorf("%s is not an ip address", val) 246 } 247 248 // ValidateMACAddress validates a MAC address. 249 func ValidateMACAddress(val string) (string, error) { 250 _, err := net.ParseMAC(strings.TrimSpace(val)) 251 if err != nil { 252 return "", err 253 } 254 return val, nil 255 } 256 257 // ValidateDNSSearch validates domain for resolvconf search configuration. 258 // A zero length domain is represented by a dot (.). 259 func ValidateDNSSearch(val string) (string, error) { 260 if val = strings.Trim(val, " "); val == "." { 261 return val, nil 262 } 263 return validateDomain(val) 264 } 265 266 func validateDomain(val string) (string, error) { 267 if alphaRegexp.FindString(val) == "" { 268 return "", fmt.Errorf("%s is not a valid domain", val) 269 } 270 ns := domainRegexp.FindSubmatch([]byte(val)) 271 if len(ns) > 0 && len(ns[1]) < 255 { 272 return string(ns[1]), nil 273 } 274 return "", fmt.Errorf("%s is not a valid domain", val) 275 } 276 277 // ValidateExtraHost validates that the specified string is a valid extrahost and returns it. 278 // ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6). 279 func ValidateExtraHost(val string) (string, error) { 280 // allow for IPv6 addresses in extra hosts by only splitting on first ":" 281 arr := strings.SplitN(val, ":", 2) 282 if len(arr) != 2 || len(arr[0]) == 0 { 283 return "", fmt.Errorf("bad format for add-host: %q", val) 284 } 285 if _, err := ValidateIPAddress(arr[1]); err != nil { 286 return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) 287 } 288 return val, nil 289 } 290 291 // ValidateLabel validates that the specified string is a valid label, and returns it. 292 // Labels are in the form on key=value. 293 func ValidateLabel(val string) (string, error) { 294 if strings.Count(val, "=") < 1 { 295 return "", fmt.Errorf("bad attribute format: %s", val) 296 } 297 return val, nil 298 } 299 300 func doesEnvExist(name string) bool { 301 for _, entry := range os.Environ() { 302 parts := strings.SplitN(entry, "=", 2) 303 if parts[0] == name { 304 return true 305 } 306 } 307 return false 308 }