github.com/shiroyuki/docker@v1.9.0/pkg/parsers/parsers.go (about) 1 // Package parsers provides helper functions to parse and validate different type 2 // of string. It can be hosts, unix addresses, tcp addresses, filters, kernel 3 // operating system versions. 4 package parsers 5 6 import ( 7 "fmt" 8 "net/url" 9 "path" 10 "runtime" 11 "strconv" 12 "strings" 13 ) 14 15 // ParseDockerDaemonHost parses the specified address and returns an address that will be used as the host. 16 // Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr 17 // defaultUnixAddr must be a absolute file path (no `unix://` prefix) 18 // defaultTCPAddr must be the full `tcp://host:port` form 19 func ParseDockerDaemonHost(defaultTCPAddr, defaultTLSHost, defaultUnixAddr, defaultAddr, addr string) (string, error) { 20 addr = strings.TrimSpace(addr) 21 if addr == "" { 22 if defaultAddr == defaultTLSHost { 23 return defaultTLSHost, nil 24 } 25 if runtime.GOOS != "windows" { 26 return fmt.Sprintf("unix://%s", defaultUnixAddr), nil 27 } 28 return defaultTCPAddr, nil 29 } 30 addrParts := strings.Split(addr, "://") 31 if len(addrParts) == 1 { 32 addrParts = []string{"tcp", addrParts[0]} 33 } 34 35 switch addrParts[0] { 36 case "tcp": 37 return ParseTCPAddr(addrParts[1], defaultTCPAddr) 38 case "unix": 39 return ParseUnixAddr(addrParts[1], defaultUnixAddr) 40 case "fd": 41 return addr, nil 42 default: 43 return "", fmt.Errorf("Invalid bind address format: %s", addr) 44 } 45 } 46 47 // ParseUnixAddr parses and validates that the specified address is a valid UNIX 48 // socket address. It returns a formatted UNIX socket address, either using the 49 // address parsed from addr, or the contents of defaultAddr if addr is a blank 50 // string. 51 func ParseUnixAddr(addr string, defaultAddr string) (string, error) { 52 addr = strings.TrimPrefix(addr, "unix://") 53 if strings.Contains(addr, "://") { 54 return "", fmt.Errorf("Invalid proto, expected unix: %s", addr) 55 } 56 if addr == "" { 57 addr = defaultAddr 58 } 59 return fmt.Sprintf("unix://%s", addr), nil 60 } 61 62 // ParseTCPAddr parses and validates that the specified address is a valid TCP 63 // address. It returns a formatted TCP address, either using the address parsed 64 // from tryAddr, or the contents of defaultAddr if tryAddr is a blank string. 65 // tryAddr is expected to have already been Trim()'d 66 // defaultAddr must be in the full `tcp://host:port` form 67 func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) { 68 if tryAddr == "" || tryAddr == "tcp://" { 69 return defaultAddr, nil 70 } 71 addr := strings.TrimPrefix(tryAddr, "tcp://") 72 if strings.Contains(addr, "://") || addr == "" { 73 return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr) 74 } 75 76 u, err := url.Parse("tcp://" + addr) 77 if err != nil { 78 return "", err 79 } 80 hostParts := strings.Split(u.Host, ":") 81 if len(hostParts) != 2 { 82 return "", fmt.Errorf("Invalid bind address format: %s", tryAddr) 83 } 84 defaults := strings.Split(defaultAddr, ":") 85 if len(defaults) != 3 { 86 return "", fmt.Errorf("Invalid defaults address format: %s", defaultAddr) 87 } 88 89 host := hostParts[0] 90 if host == "" { 91 host = strings.TrimPrefix(defaults[1], "//") 92 } 93 if hostParts[1] == "" { 94 hostParts[1] = defaults[2] 95 } 96 p, err := strconv.Atoi(hostParts[1]) 97 if err != nil && p == 0 { 98 return "", fmt.Errorf("Invalid bind address format: %s", tryAddr) 99 } 100 return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil 101 } 102 103 // ParseRepositoryTag gets a repos name and returns the right reposName + tag|digest 104 // The tag can be confusing because of a port in a repository name. 105 // Ex: localhost.localdomain:5000/samalba/hipache:latest 106 // Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb 107 func ParseRepositoryTag(repos string) (string, string) { 108 n := strings.Index(repos, "@") 109 if n >= 0 { 110 parts := strings.Split(repos, "@") 111 return parts[0], parts[1] 112 } 113 n = strings.LastIndex(repos, ":") 114 if n < 0 { 115 return repos, "" 116 } 117 if tag := repos[n+1:]; !strings.Contains(tag, "/") { 118 return repos[:n], tag 119 } 120 return repos, "" 121 } 122 123 // PartParser parses and validates the specified string (data) using the specified template 124 // e.g. ip:public:private -> 192.168.0.1:80:8000 125 func PartParser(template, data string) (map[string]string, error) { 126 // ip:public:private 127 var ( 128 templateParts = strings.Split(template, ":") 129 parts = strings.Split(data, ":") 130 out = make(map[string]string, len(templateParts)) 131 ) 132 if len(parts) != len(templateParts) { 133 return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template) 134 } 135 136 for i, t := range templateParts { 137 value := "" 138 if len(parts) > i { 139 value = parts[i] 140 } 141 out[t] = value 142 } 143 return out, nil 144 } 145 146 // ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value) 147 func ParseKeyValueOpt(opt string) (string, string, error) { 148 parts := strings.SplitN(opt, "=", 2) 149 if len(parts) != 2 { 150 return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt) 151 } 152 return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil 153 } 154 155 // ParsePortRange parses and validates the specified string as a port-range (8000-9000) 156 func ParsePortRange(ports string) (uint64, uint64, error) { 157 if ports == "" { 158 return 0, 0, fmt.Errorf("Empty string specified for ports.") 159 } 160 if !strings.Contains(ports, "-") { 161 start, err := strconv.ParseUint(ports, 10, 16) 162 end := start 163 return start, end, err 164 } 165 166 parts := strings.Split(ports, "-") 167 start, err := strconv.ParseUint(parts[0], 10, 16) 168 if err != nil { 169 return 0, 0, err 170 } 171 end, err := strconv.ParseUint(parts[1], 10, 16) 172 if err != nil { 173 return 0, 0, err 174 } 175 if end < start { 176 return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports) 177 } 178 return start, end, nil 179 } 180 181 // ParseLink parses and validates the specified string as a link format (name:alias) 182 func ParseLink(val string) (string, string, error) { 183 if val == "" { 184 return "", "", fmt.Errorf("empty string specified for links") 185 } 186 arr := strings.Split(val, ":") 187 if len(arr) > 2 { 188 return "", "", fmt.Errorf("bad format for links: %s", val) 189 } 190 if len(arr) == 1 { 191 return val, val, nil 192 } 193 // This is kept because we can actually get an HostConfig with links 194 // from an already created container and the format is not `foo:bar` 195 // but `/foo:/c1/bar` 196 if strings.HasPrefix(arr[0], "/") { 197 _, alias := path.Split(arr[1]) 198 return arr[0][1:], alias, nil 199 } 200 return arr[0], arr[1], nil 201 } 202 203 // ParseUintList parses and validates the specified string as the value 204 // found in some cgroup file (e.g. `cpuset.cpus`, `cpuset.mems`), which could be 205 // one of the formats below. Note that duplicates are actually allowed in the 206 // input string. It returns a `map[int]bool` with available elements from `val` 207 // set to `true`. 208 // Supported formats: 209 // 7 210 // 1-6 211 // 0,3-4,7,8-10 212 // 0-0,0,1-7 213 // 03,1-3 <- this is gonna get parsed as [1,2,3] 214 // 3,2,1 215 // 0-2,3,1 216 func ParseUintList(val string) (map[int]bool, error) { 217 if val == "" { 218 return map[int]bool{}, nil 219 } 220 221 availableInts := make(map[int]bool) 222 split := strings.Split(val, ",") 223 errInvalidFormat := fmt.Errorf("invalid format: %s", val) 224 225 for _, r := range split { 226 if !strings.Contains(r, "-") { 227 v, err := strconv.Atoi(r) 228 if err != nil { 229 return nil, errInvalidFormat 230 } 231 availableInts[v] = true 232 } else { 233 split := strings.SplitN(r, "-", 2) 234 min, err := strconv.Atoi(split[0]) 235 if err != nil { 236 return nil, errInvalidFormat 237 } 238 max, err := strconv.Atoi(split[1]) 239 if err != nil { 240 return nil, errInvalidFormat 241 } 242 if max < min { 243 return nil, errInvalidFormat 244 } 245 for i := min; i <= max; i++ { 246 availableInts[i] = true 247 } 248 } 249 } 250 return availableInts, nil 251 }