github.com/tompao/docker@v1.9.1/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 // GetAllOrEmpty returns the values of the slice 105 // or an empty slice when there are no values. 106 func (opts *ListOpts) GetAllOrEmpty() []string { 107 v := *opts.values 108 if v == nil { 109 return make([]string, 0) 110 } 111 return v 112 } 113 114 // Get checks the existence of the specified key. 115 func (opts *ListOpts) Get(key string) bool { 116 for _, k := range *opts.values { 117 if k == key { 118 return true 119 } 120 } 121 return false 122 } 123 124 // Len returns the amount of element in the slice. 125 func (opts *ListOpts) Len() int { 126 return len((*opts.values)) 127 } 128 129 //MapOpts holds a map of values and a validation function. 130 type MapOpts struct { 131 values map[string]string 132 validator ValidatorFctType 133 } 134 135 // Set validates if needed the input value and add it to the 136 // internal map, by splitting on '='. 137 func (opts *MapOpts) Set(value string) error { 138 if opts.validator != nil { 139 v, err := opts.validator(value) 140 if err != nil { 141 return err 142 } 143 value = v 144 } 145 vals := strings.SplitN(value, "=", 2) 146 if len(vals) == 1 { 147 (opts.values)[vals[0]] = "" 148 } else { 149 (opts.values)[vals[0]] = vals[1] 150 } 151 return nil 152 } 153 154 // GetAll returns the values of MapOpts as a map. 155 func (opts *MapOpts) GetAll() map[string]string { 156 return opts.values 157 } 158 159 func (opts *MapOpts) String() string { 160 return fmt.Sprintf("%v", map[string]string((opts.values))) 161 } 162 163 // NewMapOpts creates a new MapOpts with the specified map of values and a validator. 164 func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { 165 if values == nil { 166 values = make(map[string]string) 167 } 168 return &MapOpts{ 169 values: values, 170 validator: validator, 171 } 172 } 173 174 // ValidatorFctType defines a validator function that returns a validated string and/or an error. 175 type ValidatorFctType func(val string) (string, error) 176 177 // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error 178 type ValidatorFctListType func(val string) ([]string, error) 179 180 // ValidateAttach validates that the specified string is a valid attach option. 181 func ValidateAttach(val string) (string, error) { 182 s := strings.ToLower(val) 183 for _, str := range []string{"stdin", "stdout", "stderr"} { 184 if s == str { 185 return s, nil 186 } 187 } 188 return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR") 189 } 190 191 // ValidateLink validates that the specified string has a valid link format (containerName:alias). 192 func ValidateLink(val string) (string, error) { 193 if _, _, err := parsers.ParseLink(val); err != nil { 194 return val, err 195 } 196 return val, nil 197 } 198 199 // ValidDeviceMode checks if the mode for device is valid or not. 200 // Valid mode is a composition of r (read), w (write), and m (mknod). 201 func ValidDeviceMode(mode string) bool { 202 var legalDeviceMode = map[rune]bool{ 203 'r': true, 204 'w': true, 205 'm': true, 206 } 207 if mode == "" { 208 return false 209 } 210 for _, c := range mode { 211 if !legalDeviceMode[c] { 212 return false 213 } 214 legalDeviceMode[c] = false 215 } 216 return true 217 } 218 219 // ValidateDevice validates a path for devices 220 // It will make sure 'val' is in the form: 221 // [host-dir:]container-path[:mode] 222 // It also validates the device mode. 223 func ValidateDevice(val string) (string, error) { 224 return validatePath(val, ValidDeviceMode) 225 } 226 227 // ValidatePath validates a path for volumes 228 // It will make sure 'val' is in the form: 229 // [host-dir:]container-path[:rw|ro] 230 // It also validates the mount mode. 231 func ValidatePath(val string) (string, error) { 232 return validatePath(val, volume.ValidMountMode) 233 } 234 235 func validatePath(val string, validator func(string) bool) (string, error) { 236 var containerPath string 237 var mode string 238 239 if strings.Count(val, ":") > 2 { 240 return val, fmt.Errorf("bad format for path: %s", val) 241 } 242 243 split := strings.SplitN(val, ":", 3) 244 if split[0] == "" { 245 return val, fmt.Errorf("bad format for path: %s", val) 246 } 247 switch len(split) { 248 case 1: 249 containerPath = split[0] 250 val = path.Clean(containerPath) 251 case 2: 252 if isValid := validator(split[1]); isValid { 253 containerPath = split[0] 254 mode = split[1] 255 val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode) 256 } else { 257 containerPath = split[1] 258 val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath)) 259 } 260 case 3: 261 containerPath = split[1] 262 mode = split[2] 263 if isValid := validator(split[2]); !isValid { 264 return val, fmt.Errorf("bad mode specified: %s", mode) 265 } 266 val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode) 267 } 268 269 if !path.IsAbs(containerPath) { 270 return val, fmt.Errorf("%s is not an absolute path", containerPath) 271 } 272 return val, nil 273 } 274 275 // ValidateEnv validates an environment variable and returns it. 276 // If no value is specified, it returns the current value using os.Getenv. 277 // 278 // As on ParseEnvFile and related to #16585, environment variable names 279 // are not validate what so ever, it's up to application inside docker 280 // to validate them or not. 281 func ValidateEnv(val string) (string, error) { 282 arr := strings.Split(val, "=") 283 if len(arr) > 1 { 284 return val, nil 285 } 286 if !doesEnvExist(val) { 287 return val, nil 288 } 289 return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil 290 } 291 292 // ValidateIPAddress validates an Ip address. 293 func ValidateIPAddress(val string) (string, error) { 294 var ip = net.ParseIP(strings.TrimSpace(val)) 295 if ip != nil { 296 return ip.String(), nil 297 } 298 return "", fmt.Errorf("%s is not an ip address", val) 299 } 300 301 // ValidateMACAddress validates a MAC address. 302 func ValidateMACAddress(val string) (string, error) { 303 _, err := net.ParseMAC(strings.TrimSpace(val)) 304 if err != nil { 305 return "", err 306 } 307 return val, nil 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 // ValidateExtraHost validates that the specified string is a valid extrahost and returns it. 331 // ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6). 332 func ValidateExtraHost(val string) (string, error) { 333 // allow for IPv6 addresses in extra hosts by only splitting on first ":" 334 arr := strings.SplitN(val, ":", 2) 335 if len(arr) != 2 || len(arr[0]) == 0 { 336 return "", fmt.Errorf("bad format for add-host: %q", val) 337 } 338 if _, err := ValidateIPAddress(arr[1]); err != nil { 339 return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) 340 } 341 return val, nil 342 } 343 344 // ValidateLabel validates that the specified string is a valid label, and returns it. 345 // Labels are in the form on key=value. 346 func ValidateLabel(val string) (string, error) { 347 if strings.Count(val, "=") < 1 { 348 return "", fmt.Errorf("bad attribute format: %s", val) 349 } 350 return val, nil 351 } 352 353 // ValidateHost validates that the specified string is a valid host and returns it. 354 func ValidateHost(val string) (string, error) { 355 _, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, "", val) 356 if err != nil { 357 return val, err 358 } 359 // Note: unlike most flag validators, we don't return the mutated value here 360 // we need to know what the user entered later (using ParseHost) to adjust for tls 361 return val, nil 362 } 363 364 // ParseHost and set defaults for a Daemon host string 365 func ParseHost(defaultHost, val string) (string, error) { 366 host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, defaultHost, val) 367 if err != nil { 368 return val, err 369 } 370 return host, nil 371 } 372 373 func doesEnvExist(name string) bool { 374 for _, entry := range os.Environ() { 375 parts := strings.SplitN(entry, "=", 2) 376 if parts[0] == name { 377 return true 378 } 379 } 380 return false 381 }