github.com/fntlnz/docker@v1.9.0-rc3/opts/opts.go (about) 1 package opts 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "path" 8 "regexp" 9 "strings" 10 11 "github.com/docker/docker/pkg/parsers" 12 "github.com/docker/docker/volume" 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 // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. docker daemon -H tcp://:8080 19 DefaultHTTPHost = "localhost" 20 21 // DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. docker daemon -H tcp:// 22 // TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter 23 // is not supplied. A better longer term solution would be to use a named 24 // pipe as the default on the Windows daemon. 25 // These are the IANA registered port numbers for use with Docker 26 // see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker 27 DefaultHTTPPort = 2375 // Default HTTP Port 28 // DefaultTLSHTTPPort Default HTTP Port used when TLS enabled 29 DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port 30 // DefaultUnixSocket Path for the unix socket. 31 // Docker daemon by default always listens on the default unix socket 32 DefaultUnixSocket = "/var/run/docker.sock" 33 // DefaultTCPHost constant defines the default host string used by docker on Windows 34 DefaultTCPHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort) 35 // DefaultTLSHost constant defines the default host string used by docker for TLS sockets 36 DefaultTLSHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultTLSHTTPPort) 37 ) 38 39 // ListOpts holds a list of values and a validation function. 40 type ListOpts struct { 41 values *[]string 42 validator ValidatorFctType 43 } 44 45 // NewListOpts creates a new ListOpts with the specified validator. 46 func NewListOpts(validator ValidatorFctType) ListOpts { 47 var values []string 48 return *NewListOptsRef(&values, validator) 49 } 50 51 // NewListOptsRef creates a new ListOpts with the specified values and validator. 52 func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts { 53 return &ListOpts{ 54 values: values, 55 validator: validator, 56 } 57 } 58 59 func (opts *ListOpts) String() string { 60 return fmt.Sprintf("%v", []string((*opts.values))) 61 } 62 63 // Set validates if needed the input value and add it to the 64 // internal slice. 65 func (opts *ListOpts) Set(value string) error { 66 if opts.validator != nil { 67 v, err := opts.validator(value) 68 if err != nil { 69 return err 70 } 71 value = v 72 } 73 (*opts.values) = append((*opts.values), value) 74 return nil 75 } 76 77 // Delete removes the specified element from the slice. 78 func (opts *ListOpts) Delete(key string) { 79 for i, k := range *opts.values { 80 if k == key { 81 (*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...) 82 return 83 } 84 } 85 } 86 87 // GetMap returns the content of values in a map in order to avoid 88 // duplicates. 89 // FIXME: can we remove this? 90 func (opts *ListOpts) GetMap() map[string]struct{} { 91 ret := make(map[string]struct{}) 92 for _, k := range *opts.values { 93 ret[k] = struct{}{} 94 } 95 return ret 96 } 97 98 // GetAll returns the values of slice. 99 // FIXME: Can we remove this? 100 func (opts *ListOpts) GetAll() []string { 101 return (*opts.values) 102 } 103 104 // Get checks the existence of the specified key. 105 func (opts *ListOpts) Get(key string) bool { 106 for _, k := range *opts.values { 107 if k == key { 108 return true 109 } 110 } 111 return false 112 } 113 114 // Len returns the amount of element in the slice. 115 func (opts *ListOpts) Len() int { 116 return len((*opts.values)) 117 } 118 119 //MapOpts holds a map of values and a validation function. 120 type MapOpts struct { 121 values map[string]string 122 validator ValidatorFctType 123 } 124 125 // Set validates if needed the input value and add it to the 126 // internal map, by splitting on '='. 127 func (opts *MapOpts) Set(value string) error { 128 if opts.validator != nil { 129 v, err := opts.validator(value) 130 if err != nil { 131 return err 132 } 133 value = v 134 } 135 vals := strings.SplitN(value, "=", 2) 136 if len(vals) == 1 { 137 (opts.values)[vals[0]] = "" 138 } else { 139 (opts.values)[vals[0]] = vals[1] 140 } 141 return nil 142 } 143 144 // GetAll returns the values of MapOpts as a map. 145 func (opts *MapOpts) GetAll() map[string]string { 146 return opts.values 147 } 148 149 func (opts *MapOpts) String() string { 150 return fmt.Sprintf("%v", map[string]string((opts.values))) 151 } 152 153 // NewMapOpts creates a new MapOpts with the specified map of values and a validator. 154 func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { 155 if values == nil { 156 values = make(map[string]string) 157 } 158 return &MapOpts{ 159 values: values, 160 validator: validator, 161 } 162 } 163 164 // ValidatorFctType defines a validator function that returns a validated string and/or an error. 165 type ValidatorFctType func(val string) (string, error) 166 167 // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error 168 type ValidatorFctListType func(val string) ([]string, error) 169 170 // ValidateAttach validates that the specified string is a valid attach option. 171 func ValidateAttach(val string) (string, error) { 172 s := strings.ToLower(val) 173 for _, str := range []string{"stdin", "stdout", "stderr"} { 174 if s == str { 175 return s, nil 176 } 177 } 178 return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR") 179 } 180 181 // ValidateLink validates that the specified string has a valid link format (containerName:alias). 182 func ValidateLink(val string) (string, error) { 183 if _, _, err := parsers.ParseLink(val); err != nil { 184 return val, err 185 } 186 return val, nil 187 } 188 189 // ValidDeviceMode checks if the mode for device is valid or not. 190 // Valid mode is a composition of r (read), w (write), and m (mknod). 191 func ValidDeviceMode(mode string) bool { 192 var legalDeviceMode = map[rune]bool{ 193 'r': true, 194 'w': true, 195 'm': true, 196 } 197 if mode == "" { 198 return false 199 } 200 for _, c := range mode { 201 if !legalDeviceMode[c] { 202 return false 203 } 204 legalDeviceMode[c] = false 205 } 206 return true 207 } 208 209 // ValidateDevice validates a path for devices 210 // It will make sure 'val' is in the form: 211 // [host-dir:]container-path[:mode] 212 // It also validates the device mode. 213 func ValidateDevice(val string) (string, error) { 214 return validatePath(val, ValidDeviceMode) 215 } 216 217 // ValidatePath validates a path for volumes 218 // It will make sure 'val' is in the form: 219 // [host-dir:]container-path[:rw|ro] 220 // It also validates the mount mode. 221 func ValidatePath(val string) (string, error) { 222 return validatePath(val, volume.ValidMountMode) 223 } 224 225 func validatePath(val string, validator func(string) bool) (string, error) { 226 var containerPath string 227 var mode string 228 229 if strings.Count(val, ":") > 2 { 230 return val, fmt.Errorf("bad format for path: %s", val) 231 } 232 233 split := strings.SplitN(val, ":", 3) 234 if split[0] == "" { 235 return val, fmt.Errorf("bad format for path: %s", val) 236 } 237 switch len(split) { 238 case 1: 239 containerPath = split[0] 240 val = path.Clean(containerPath) 241 case 2: 242 if isValid := validator(split[1]); isValid { 243 containerPath = split[0] 244 mode = split[1] 245 val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode) 246 } else { 247 containerPath = split[1] 248 val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath)) 249 } 250 case 3: 251 containerPath = split[1] 252 mode = split[2] 253 if isValid := validator(split[2]); !isValid { 254 return val, fmt.Errorf("bad mode specified: %s", mode) 255 } 256 val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode) 257 } 258 259 if !path.IsAbs(containerPath) { 260 return val, fmt.Errorf("%s is not an absolute path", containerPath) 261 } 262 return val, nil 263 } 264 265 // ValidateEnv validates an environment variable and returns it. 266 // If no value is specified, it returns the current value using os.Getenv. 267 // 268 // As on ParseEnvFile and related to #16585, environment variable names 269 // are not validate what so ever, it's up to application inside docker 270 // to validate them or not. 271 func ValidateEnv(val string) (string, error) { 272 arr := strings.Split(val, "=") 273 if len(arr) > 1 { 274 return val, nil 275 } 276 if !doesEnvExist(val) { 277 return val, nil 278 } 279 return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil 280 } 281 282 // ValidateIPAddress validates an Ip address. 283 func ValidateIPAddress(val string) (string, error) { 284 var ip = net.ParseIP(strings.TrimSpace(val)) 285 if ip != nil { 286 return ip.String(), nil 287 } 288 return "", fmt.Errorf("%s is not an ip address", val) 289 } 290 291 // ValidateMACAddress validates a MAC address. 292 func ValidateMACAddress(val string) (string, error) { 293 _, err := net.ParseMAC(strings.TrimSpace(val)) 294 if err != nil { 295 return "", err 296 } 297 return val, nil 298 } 299 300 // ValidateDNSSearch validates domain for resolvconf search configuration. 301 // A zero length domain is represented by a dot (.). 302 func ValidateDNSSearch(val string) (string, error) { 303 if val = strings.Trim(val, " "); val == "." { 304 return val, nil 305 } 306 return validateDomain(val) 307 } 308 309 func validateDomain(val string) (string, error) { 310 if alphaRegexp.FindString(val) == "" { 311 return "", fmt.Errorf("%s is not a valid domain", val) 312 } 313 ns := domainRegexp.FindSubmatch([]byte(val)) 314 if len(ns) > 0 && len(ns[1]) < 255 { 315 return string(ns[1]), nil 316 } 317 return "", fmt.Errorf("%s is not a valid domain", val) 318 } 319 320 // ValidateExtraHost validates that the specified string is a valid extrahost and returns it. 321 // ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6). 322 func ValidateExtraHost(val string) (string, error) { 323 // allow for IPv6 addresses in extra hosts by only splitting on first ":" 324 arr := strings.SplitN(val, ":", 2) 325 if len(arr) != 2 || len(arr[0]) == 0 { 326 return "", fmt.Errorf("bad format for add-host: %q", val) 327 } 328 if _, err := ValidateIPAddress(arr[1]); err != nil { 329 return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) 330 } 331 return val, nil 332 } 333 334 // ValidateLabel validates that the specified string is a valid label, and returns it. 335 // Labels are in the form on key=value. 336 func ValidateLabel(val string) (string, error) { 337 if strings.Count(val, "=") < 1 { 338 return "", fmt.Errorf("bad attribute format: %s", val) 339 } 340 return val, nil 341 } 342 343 // ValidateHost validates that the specified string is a valid host and returns it. 344 func ValidateHost(val string) (string, error) { 345 _, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, "", val) 346 if err != nil { 347 return val, err 348 } 349 // Note: unlike most flag validators, we don't return the mutated value here 350 // we need to know what the user entered later (using ParseHost) to adjust for tls 351 return val, nil 352 } 353 354 // ParseHost and set defaults for a Daemon host string 355 func ParseHost(defaultHost, val string) (string, error) { 356 host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, defaultHost, val) 357 if err != nil { 358 return val, err 359 } 360 return host, nil 361 } 362 363 func doesEnvExist(name string) bool { 364 for _, entry := range os.Environ() { 365 parts := strings.SplitN(entry, "=", 2) 366 if parts[0] == name { 367 return true 368 } 369 } 370 return false 371 }