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