123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // Package parsers provides helper functions to parse and validate different type
- // of string. It can be hosts, unix addresses, tcp addresses, filters, kernel
- // operating system versions.
- package parsers
- import (
- "fmt"
- "net"
- "net/url"
- "path"
- "runtime"
- "strconv"
- "strings"
- )
- // ParseDockerDaemonHost parses the specified address and returns an address that will be used as the host.
- // Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr
- // defaultUnixAddr must be a absolute file path (no `unix://` prefix)
- // defaultTCPAddr must be the full `tcp://host:port` form
- func ParseDockerDaemonHost(defaultTCPAddr, defaultTLSHost, defaultUnixAddr, defaultAddr, addr string) (string, error) {
- addr = strings.TrimSpace(addr)
- if addr == "" {
- if defaultAddr == defaultTLSHost {
- return defaultTLSHost, nil
- }
- if runtime.GOOS != "windows" {
- return fmt.Sprintf("unix://%s", defaultUnixAddr), nil
- }
- return defaultTCPAddr, nil
- }
- addrParts := strings.Split(addr, "://")
- if len(addrParts) == 1 {
- addrParts = []string{"tcp", addrParts[0]}
- }
- switch addrParts[0] {
- case "tcp":
- return ParseTCPAddr(addrParts[1], defaultTCPAddr)
- case "unix":
- return ParseUnixAddr(addrParts[1], defaultUnixAddr)
- case "fd":
- return addr, nil
- default:
- return "", fmt.Errorf("Invalid bind address format: %s", addr)
- }
- }
- // ParseUnixAddr parses and validates that the specified address is a valid UNIX
- // socket address. It returns a formatted UNIX socket address, either using the
- // address parsed from addr, or the contents of defaultAddr if addr is a blank
- // string.
- func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
- addr = strings.TrimPrefix(addr, "unix://")
- if strings.Contains(addr, "://") {
- return "", fmt.Errorf("Invalid proto, expected unix: %s", addr)
- }
- if addr == "" {
- addr = defaultAddr
- }
- return fmt.Sprintf("unix://%s", addr), nil
- }
- // ParseTCPAddr parses and validates that the specified address is a valid TCP
- // address. It returns a formatted TCP address, either using the address parsed
- // from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
- // tryAddr is expected to have already been Trim()'d
- // defaultAddr must be in the full `tcp://host:port` form
- func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
- if tryAddr == "" || tryAddr == "tcp://" {
- return defaultAddr, nil
- }
- addr := strings.TrimPrefix(tryAddr, "tcp://")
- if strings.Contains(addr, "://") || addr == "" {
- return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
- }
- defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
- defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
- if err != nil {
- return "", err
- }
- // url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but
- // not 1.4. See https://github.com/golang/go/issues/12200 and
- // https://github.com/golang/go/issues/6530.
- if strings.HasSuffix(addr, "]:") {
- addr += defaultPort
- }
- u, err := url.Parse("tcp://" + addr)
- if err != nil {
- return "", err
- }
- host, port, err := net.SplitHostPort(u.Host)
- if err != nil {
- return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
- }
- if host == "" {
- host = defaultHost
- }
- if port == "" {
- port = defaultPort
- }
- p, err := strconv.Atoi(port)
- if err != nil && p == 0 {
- return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
- }
- if net.ParseIP(host).To4() == nil && strings.Contains(host, ":") {
- // This is either an ipv6 address
- host = "[" + host + "]"
- }
- return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil
- }
- // ParseRepositoryTag gets a repos name and returns the right reposName + tag|digest
- // The tag can be confusing because of a port in a repository name.
- // Ex: localhost.localdomain:5000/samalba/hipache:latest
- // Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
- func ParseRepositoryTag(repos string) (string, string) {
- n := strings.Index(repos, "@")
- if n >= 0 {
- parts := strings.Split(repos, "@")
- return parts[0], parts[1]
- }
- n = strings.LastIndex(repos, ":")
- if n < 0 {
- return repos, ""
- }
- if tag := repos[n+1:]; !strings.Contains(tag, "/") {
- return repos[:n], tag
- }
- return repos, ""
- }
- // PartParser parses and validates the specified string (data) using the specified template
- // e.g. ip:public:private -> 192.168.0.1:80:8000
- func PartParser(template, data string) (map[string]string, error) {
- // ip:public:private
- var (
- templateParts = strings.Split(template, ":")
- parts = strings.Split(data, ":")
- out = make(map[string]string, len(templateParts))
- )
- if len(parts) != len(templateParts) {
- return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template)
- }
- for i, t := range templateParts {
- value := ""
- if len(parts) > i {
- value = parts[i]
- }
- out[t] = value
- }
- return out, nil
- }
- // ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
- func ParseKeyValueOpt(opt string) (string, string, error) {
- parts := strings.SplitN(opt, "=", 2)
- if len(parts) != 2 {
- return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt)
- }
- return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
- }
- // ParsePortRange parses and validates the specified string as a port-range (8000-9000)
- func ParsePortRange(ports string) (uint64, uint64, error) {
- if ports == "" {
- return 0, 0, fmt.Errorf("Empty string specified for ports.")
- }
- if !strings.Contains(ports, "-") {
- start, err := strconv.ParseUint(ports, 10, 16)
- end := start
- return start, end, err
- }
- parts := strings.Split(ports, "-")
- start, err := strconv.ParseUint(parts[0], 10, 16)
- if err != nil {
- return 0, 0, err
- }
- end, err := strconv.ParseUint(parts[1], 10, 16)
- if err != nil {
- return 0, 0, err
- }
- if end < start {
- return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports)
- }
- return start, end, nil
- }
- // ParseLink parses and validates the specified string as a link format (name:alias)
- func ParseLink(val string) (string, string, error) {
- if val == "" {
- return "", "", fmt.Errorf("empty string specified for links")
- }
- arr := strings.Split(val, ":")
- if len(arr) > 2 {
- return "", "", fmt.Errorf("bad format for links: %s", val)
- }
- if len(arr) == 1 {
- return val, val, nil
- }
- // This is kept because we can actually get an HostConfig with links
- // from an already created container and the format is not `foo:bar`
- // but `/foo:/c1/bar`
- if strings.HasPrefix(arr[0], "/") {
- _, alias := path.Split(arr[1])
- return arr[0][1:], alias, nil
- }
- return arr[0], arr[1], nil
- }
- // ParseUintList parses and validates the specified string as the value
- // found in some cgroup file (e.g. `cpuset.cpus`, `cpuset.mems`), which could be
- // one of the formats below. Note that duplicates are actually allowed in the
- // input string. It returns a `map[int]bool` with available elements from `val`
- // set to `true`.
- // Supported formats:
- // 7
- // 1-6
- // 0,3-4,7,8-10
- // 0-0,0,1-7
- // 03,1-3 <- this is gonna get parsed as [1,2,3]
- // 3,2,1
- // 0-2,3,1
- func ParseUintList(val string) (map[int]bool, error) {
- if val == "" {
- return map[int]bool{}, nil
- }
- availableInts := make(map[int]bool)
- split := strings.Split(val, ",")
- errInvalidFormat := fmt.Errorf("invalid format: %s", val)
- for _, r := range split {
- if !strings.Contains(r, "-") {
- v, err := strconv.Atoi(r)
- if err != nil {
- return nil, errInvalidFormat
- }
- availableInts[v] = true
- } else {
- split := strings.SplitN(r, "-", 2)
- min, err := strconv.Atoi(split[0])
- if err != nil {
- return nil, errInvalidFormat
- }
- max, err := strconv.Atoi(split[1])
- if err != nil {
- return nil, errInvalidFormat
- }
- if max < min {
- return nil, errInvalidFormat
- }
- for i := min; i <= max; i++ {
- availableInts[i] = true
- }
- }
- }
- return availableInts, nil
- }
|