diff --git a/api/client/commands.go b/api/client/commands.go index 1853b513fe..ae1002b563 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -28,13 +28,14 @@ import ( "github.com/docker/docker/engine" "github.com/docker/docker/nat" "github.com/docker/docker/opts" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/term" "github.com/docker/docker/pkg/units" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" - "github.com/docker/docker/utils/filters" ) const ( @@ -204,7 +205,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { //Check if the given image name can be resolved if *tag != "" { - repository, _ := utils.ParseRepositoryTag(*tag) + repository, _ := parsers.ParseRepositoryTag(*tag) if _, _, err := registry.ResolveRepositoryName(repository); err != nil { return err } @@ -1137,7 +1138,7 @@ func (cli *DockerCli) CmdPush(args ...string) error { cli.LoadConfigFile() - remote, tag := utils.ParseRepositoryTag(name) + remote, tag := parsers.ParseRepositoryTag(name) // Resolve the Repository name from fqn to hostname + name hostname, _, err := registry.ResolveRepositoryName(remote) @@ -1210,7 +1211,7 @@ func (cli *DockerCli) CmdPull(args ...string) error { v.Set("tag", *tag) } - remote, _ = utils.ParseRepositoryTag(remote) + remote, _ = parsers.ParseRepositoryTag(remote) // Resolve the Repository name from fqn to hostname + name hostname, _, err := registry.ResolveRepositoryName(remote) if err != nil { @@ -1393,7 +1394,7 @@ func (cli *DockerCli) CmdImages(args ...string) error { for _, out := range outs.Data { for _, repotag := range out.GetList("RepoTags") { - repo, tag := utils.ParseRepositoryTag(repotag) + repo, tag := parsers.ParseRepositoryTag(repotag) outID := out.Get("Id") if !*noTrunc { outID = utils.TruncateID(outID) @@ -1594,7 +1595,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error { var ( name = cmd.Arg(0) - repository, tag = utils.ParseRepositoryTag(cmd.Arg(1)) + repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) ) if name == "" || len(cmd.Args()) > 2 { @@ -1918,7 +1919,7 @@ func (cli *DockerCli) CmdTag(args ...string) error { } var ( - repository, tag = utils.ParseRepositoryTag(cmd.Arg(1)) + repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) v = url.Values{} ) @@ -1941,7 +1942,7 @@ func (cli *DockerCli) CmdTag(args ...string) error { func (cli *DockerCli) pullImage(image string) error { v := url.Values{} - repos, tag := utils.ParseRepositoryTag(image) + repos, tag := parsers.ParseRepositoryTag(image) // pull only the image tagged 'latest' if no tag was specified if tag == "" { tag = "latest" diff --git a/api/common.go b/api/common.go index 5a03cf6ffe..ce92b10d4c 100644 --- a/api/common.go +++ b/api/common.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/version" "github.com/docker/docker/utils" ) @@ -17,7 +18,7 @@ const ( ) func ValidateHost(val string) (string, error) { - host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val) + host, err := parsers.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val) if err != nil { return val, err } diff --git a/api/server/server.go b/api/server/server.go index 440a468e69..83e2112766 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -25,6 +25,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/listenbuffer" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/systemd" "github.com/docker/docker/pkg/user" "github.com/docker/docker/pkg/version" @@ -484,7 +485,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon } if image != "" { //pull if tag == "" { - image, tag = utils.ParseRepositoryTag(image) + image, tag = parsers.ParseRepositoryTag(image) } metaHeaders := map[string][]string{} for k, v := range r.Header { @@ -498,7 +499,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon job.SetenvJson("authConfig", authConfig) } else { //import if tag == "" { - repo, tag = utils.ParseRepositoryTag(repo) + repo, tag = parsers.ParseRepositoryTag(repo) } job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag) job.Stdin.Add(r.Body) diff --git a/builder/builder.go b/builder/builder.go index ae402345dd..a99139896f 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -23,6 +23,7 @@ import ( "github.com/docker/docker/daemon" "github.com/docker/docker/engine" "github.com/docker/docker/nat" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" @@ -86,7 +87,7 @@ func (b *buildFile) CmdFrom(name string) error { image, err := b.daemon.Repositories().LookupImage(name) if err != nil { if b.daemon.Graph().IsNotExist(err) { - remote, tag := utils.ParseRepositoryTag(name) + remote, tag := parsers.ParseRepositoryTag(name) pullRegistryAuth := b.authConfig if len(b.configFile.Configs) > 0 { // The request came with a full auth config file, we prefer to use that diff --git a/builtins/builtins.go b/builtins/builtins.go index a42dec619e..9379c67c88 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/dockerversion" "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/registry" "github.com/docker/docker/server" - "github.com/docker/docker/utils" ) func Register(eng *engine.Engine) error { @@ -68,7 +68,7 @@ func dockerVersion(job *engine.Job) engine.Status { v.Set("GoVersion", runtime.Version()) v.Set("Os", runtime.GOOS) v.Set("Arch", runtime.GOARCH) - if kernelVersion, err := utils.GetKernelVersion(); err == nil { + if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.Set("KernelVersion", kernelVersion.String()) } if _, err := v.WriteTo(job.Stdout); err != nil { diff --git a/daemon/daemon.go b/daemon/daemon.go index 0fadc51d75..ecd515e8fd 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/pkg/graphdb" "github.com/docker/docker/pkg/namesgenerator" "github.com/docker/docker/pkg/networkfs/resolvconf" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" @@ -724,7 +725,7 @@ func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error { if hostConfig != nil && hostConfig.Links != nil { for _, l := range hostConfig.Links { - parts, err := utils.PartParser("name:alias", l) + parts, err := parsers.PartParser("name:alias", l) if err != nil { return err } diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index bbe034b148..1aef141ca8 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -19,6 +19,7 @@ import ( "time" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/units" "github.com/docker/docker/utils" "github.com/docker/libcontainer/label" @@ -1166,7 +1167,7 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error foundBlkDiscard := false for _, option := range options { - key, val, err := utils.ParseKeyValueOpt(option) + key, val, err := parsers.ParseKeyValueOpt(option) if err != nil { return nil, err } diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index d66ba67127..aeaed67c2e 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/engine" "github.com/docker/docker/pkg/iptables" "github.com/docker/docker/pkg/networkfs/resolvconf" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/utils" "github.com/docker/libcontainer/netlink" ) @@ -306,7 +307,7 @@ func createBridge(bridgeIP string) error { } func createBridgeIface(name string) error { - kv, err := utils.GetKernelVersion() + kv, err := kernel.GetKernelVersion() // only set the bridge's mac address if the kernel version is > 3.3 // before that it was not supported setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3) diff --git a/docker/docker.go b/docker/docker.go index 1ace730d48..5ffa0e2e6f 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -19,6 +19,7 @@ import ( "github.com/docker/docker/engine" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/sysinit" "github.com/docker/docker/utils" ) @@ -292,10 +293,10 @@ func checkKernelAndArch() error { // without actually causing a kernel panic, so we need this workaround until // the circumstances of pre-3.8 crashes are clearer. // For details see http://github.com/docker/docker/issues/407 - if k, err := utils.GetKernelVersion(); err != nil { + if k, err := kernel.GetKernelVersion(); err != nil { log.Printf("WARNING: %s\n", err) } else { - if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { + if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String()) } diff --git a/graph/service.go b/graph/service.go index 82fee68670..75c2b5c846 100644 --- a/graph/service.go +++ b/graph/service.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/engine" "github.com/docker/docker/image" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/utils" ) @@ -78,7 +79,7 @@ func (s *TagStore) CmdTag(job *engine.Job) engine.Status { newName = job.Args[0] oldName = job.Args[1] ) - newRepo, newTag := utils.ParseRepositoryTag(newName) + newRepo, newTag := parsers.ParseRepositoryTag(newName) // FIXME: Set should either parse both old and new name, or neither. // the current prototype is inconsistent. if err := s.Set(newRepo, newTag, oldName, true); err != nil { diff --git a/graph/tags.go b/graph/tags.go index 343cccf31f..f2a0646acb 100644 --- a/graph/tags.go +++ b/graph/tags.go @@ -11,6 +11,7 @@ import ( "sync" "github.com/docker/docker/image" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/utils" ) @@ -72,7 +73,7 @@ func (store *TagStore) reload() error { func (store *TagStore) LookupImage(name string) (*image.Image, error) { // FIXME: standardize on returning nil when the image doesn't exist, and err for everything else // (so we can pass all errors here) - repos, tag := utils.ParseRepositoryTag(name) + repos, tag := parsers.ParseRepositoryTag(name) if tag == "" { tag = DEFAULTTAG } diff --git a/nat/nat.go b/nat/nat.go index 8c402e94bf..ef89eaa0fe 100644 --- a/nat/nat.go +++ b/nat/nat.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/docker/docker/utils" + "github.com/docker/docker/pkg/parsers" ) const ( @@ -103,7 +103,7 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, rawPort = fmt.Sprintf(":%s", rawPort) } - parts, err := utils.PartParser(PortSpecTemplate, rawPort) + parts, err := parsers.PartParser(PortSpecTemplate, rawPort) if err != nil { return nil, nil, err } diff --git a/opts/opts.go b/opts/opts.go index 434ea03722..43bef37068 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -8,7 +8,7 @@ import ( "regexp" "strings" - "github.com/docker/docker/utils" + "github.com/docker/docker/pkg/parsers" ) // ListOpts type @@ -94,7 +94,7 @@ func ValidateAttach(val string) (string, error) { } func ValidateLink(val string) (string, error) { - if _, err := utils.PartParser("name:alias", val); err != nil { + if _, err := parsers.PartParser("name:alias", val); err != nil { return val, err } return val, nil diff --git a/pkg/parsers/MAINTAINERS b/pkg/parsers/MAINTAINERS new file mode 100644 index 0000000000..8c8902530a --- /dev/null +++ b/pkg/parsers/MAINTAINERS @@ -0,0 +1 @@ +Erik Hollensbe (@erikh) diff --git a/utils/filters/parse.go b/pkg/parsers/filters/parse.go similarity index 100% rename from utils/filters/parse.go rename to pkg/parsers/filters/parse.go diff --git a/utils/filters/parse_test.go b/pkg/parsers/filters/parse_test.go similarity index 100% rename from utils/filters/parse_test.go rename to pkg/parsers/filters/parse_test.go diff --git a/pkg/parsers/kernel/kernel.go b/pkg/parsers/kernel/kernel.go new file mode 100644 index 0000000000..70d09003a3 --- /dev/null +++ b/pkg/parsers/kernel/kernel.go @@ -0,0 +1,93 @@ +package kernel + +import ( + "bytes" + "errors" + "fmt" +) + +type KernelVersionInfo struct { + Kernel int + Major int + Minor int + Flavor string +} + +func (k *KernelVersionInfo) String() string { + return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor) +} + +// Compare two KernelVersionInfo struct. +// Returns -1 if a < b, 0 if a == b, 1 it a > b +func CompareKernelVersion(a, b *KernelVersionInfo) int { + if a.Kernel < b.Kernel { + return -1 + } else if a.Kernel > b.Kernel { + return 1 + } + + if a.Major < b.Major { + return -1 + } else if a.Major > b.Major { + return 1 + } + + if a.Minor < b.Minor { + return -1 + } else if a.Minor > b.Minor { + return 1 + } + + return 0 +} + +func GetKernelVersion() (*KernelVersionInfo, error) { + var ( + err error + ) + + uts, err := uname() + if err != nil { + return nil, err + } + + release := make([]byte, len(uts.Release)) + + i := 0 + for _, c := range uts.Release { + release[i] = byte(c) + i++ + } + + // Remove the \x00 from the release for Atoi to parse correctly + release = release[:bytes.IndexByte(release, 0)] + + return ParseRelease(string(release)) +} + +func ParseRelease(release string) (*KernelVersionInfo, error) { + var ( + kernel, major, minor, parsed int + flavor, partial string + ) + + // Ignore error from Sscanf to allow an empty flavor. Instead, just + // make sure we got all the version numbers. + parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial) + if parsed < 2 { + return nil, errors.New("Can't parse kernel version " + release) + } + + // sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64 + parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor) + if parsed < 1 { + flavor = partial + } + + return &KernelVersionInfo{ + Kernel: kernel, + Major: major, + Minor: minor, + Flavor: flavor, + }, nil +} diff --git a/pkg/parsers/kernel/kernel_test.go b/pkg/parsers/kernel/kernel_test.go new file mode 100644 index 0000000000..e211a63b7d --- /dev/null +++ b/pkg/parsers/kernel/kernel_test.go @@ -0,0 +1,61 @@ +package kernel + +import ( + "testing" +) + +func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) { + var ( + a *KernelVersionInfo + ) + a, _ = ParseRelease(release) + + if r := CompareKernelVersion(a, b); r != result { + t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) + } + if a.Flavor != b.Flavor { + t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor) + } +} + +func TestParseRelease(t *testing.T) { + assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0) + assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) + assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) + assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0) + assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0) + assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0) +} + +func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { + if r := CompareKernelVersion(a, b); r != result { + t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) + } +} + +func TestCompareKernelVersion(t *testing.T) { + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + 0) + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + -1) + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, + 1) + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + 0) + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + 1) + assertKernelVersion(t, + &KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20}, + &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, + -1) +} diff --git a/utils/uname_linux.go b/pkg/parsers/kernel/uname_linux.go similarity index 93% rename from utils/uname_linux.go rename to pkg/parsers/kernel/uname_linux.go index 2f4afb41bd..b29fcc3785 100644 --- a/utils/uname_linux.go +++ b/pkg/parsers/kernel/uname_linux.go @@ -1,6 +1,6 @@ // +build amd64 -package utils +package kernel import ( "syscall" diff --git a/utils/uname_unsupported.go b/pkg/parsers/kernel/uname_unsupported.go similarity index 93% rename from utils/uname_unsupported.go rename to pkg/parsers/kernel/uname_unsupported.go index 57b82ecab8..9cd8a1eb5e 100644 --- a/utils/uname_unsupported.go +++ b/pkg/parsers/kernel/uname_unsupported.go @@ -1,6 +1,6 @@ // +build !linux !amd64 -package utils +package kernel import ( "errors" diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go new file mode 100644 index 0000000000..e6e3718b40 --- /dev/null +++ b/pkg/parsers/parsers.go @@ -0,0 +1,110 @@ +package parsers + +import ( + "fmt" + "strconv" + "strings" +) + +// FIXME: Change this not to receive default value as parameter +func ParseHost(defaultHost string, defaultUnix, addr string) (string, error) { + var ( + proto string + host string + port int + ) + addr = strings.TrimSpace(addr) + switch { + case addr == "tcp://": + return "", fmt.Errorf("Invalid bind address format: %s", addr) + case strings.HasPrefix(addr, "unix://"): + proto = "unix" + addr = strings.TrimPrefix(addr, "unix://") + if addr == "" { + addr = defaultUnix + } + case strings.HasPrefix(addr, "tcp://"): + proto = "tcp" + addr = strings.TrimPrefix(addr, "tcp://") + case strings.HasPrefix(addr, "fd://"): + return addr, nil + case addr == "": + proto = "unix" + addr = defaultUnix + default: + if strings.Contains(addr, "://") { + return "", fmt.Errorf("Invalid bind address protocol: %s", addr) + } + proto = "tcp" + } + + if proto != "unix" && strings.Contains(addr, ":") { + hostParts := strings.Split(addr, ":") + if len(hostParts) != 2 { + return "", fmt.Errorf("Invalid bind address format: %s", addr) + } + if hostParts[0] != "" { + host = hostParts[0] + } else { + host = defaultHost + } + + if p, err := strconv.Atoi(hostParts[1]); err == nil && p != 0 { + port = p + } else { + return "", fmt.Errorf("Invalid bind address format: %s", addr) + } + + } else if proto == "tcp" && !strings.Contains(addr, ":") { + return "", fmt.Errorf("Invalid bind address format: %s", addr) + } else { + host = addr + } + if proto == "unix" { + return fmt.Sprintf("%s://%s", proto, host), nil + } + return fmt.Sprintf("%s://%s:%d", proto, host, port), nil +} + +// Get a repos name and returns the right reposName + tag +// The tag can be confusing because of a port in a repository name. +// Ex: localhost.localdomain:5000/samalba/hipache:latest +func ParseRepositoryTag(repos string) (string, string) { + n := strings.LastIndex(repos, ":") + if n < 0 { + return repos, "" + } + if tag := repos[n+1:]; !strings.Contains(tag, "/") { + return repos[:n], tag + } + return repos, "" +} + +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 +} + +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 +} diff --git a/pkg/parsers/parsers_test.go b/pkg/parsers/parsers_test.go new file mode 100644 index 0000000000..12b8df5708 --- /dev/null +++ b/pkg/parsers/parsers_test.go @@ -0,0 +1,83 @@ +package parsers + +import ( + "testing" +) + +func TestParseHost(t *testing.T) { + var ( + defaultHttpHost = "127.0.0.1" + defaultUnix = "/var/run/docker.sock" + ) + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.0"); err == nil { + t.Errorf("tcp 0.0.0.0 address expected error return, but err == nil, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://"); err == nil { + t.Errorf("default tcp:// address expected error return, but err == nil, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.1:5555"); err != nil || addr != "tcp://0.0.0.1:5555" { + t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, ":6666"); err != nil || addr != "tcp://127.0.0.1:6666" { + t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" { + t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" { + t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" { + t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix://"); err != nil || addr != "unix:///var/run/docker.sock" { + t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1"); err == nil { + t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr) + } + if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1:2375"); err == nil { + t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr) + } +} + +func TestParseRepositoryTag(t *testing.T) { + if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag) + } + if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag) + } + if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag) + } + if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag) + } + if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag) + } + if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" { + t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag) + } +} + +func TestParsePortMapping(t *testing.T) { + data, err := PartParser("ip:public:private", "192.168.1.1:80:8080") + if err != nil { + t.Fatal(err) + } + + if len(data) != 3 { + t.FailNow() + } + if data["ip"] != "192.168.1.1" { + t.Fail() + } + if data["public"] != "80" { + t.Fail() + } + if data["private"] != "8080" { + t.Fail() + } +} diff --git a/registry/registry.go b/registry/registry.go index 9563c3b286..0d4f2b3cc7 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -25,6 +25,7 @@ import ( "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/httputils" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/utils" ) @@ -956,7 +957,7 @@ func HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFacto httpVersion = append(httpVersion, &simpleVersionInfo{"docker", dockerversion.VERSION}) httpVersion = append(httpVersion, &simpleVersionInfo{"go", runtime.Version()}) httpVersion = append(httpVersion, &simpleVersionInfo{"git-commit", dockerversion.GITCOMMIT}) - if kernelVersion, err := utils.GetKernelVersion(); err == nil { + if kernelVersion, err := kernel.GetKernelVersion(); err == nil { httpVersion = append(httpVersion, &simpleVersionInfo{"kernel", kernelVersion.String()}) } httpVersion = append(httpVersion, &simpleVersionInfo{"os", runtime.GOOS}) diff --git a/runconfig/parse.go b/runconfig/parse.go index fe16e9caaf..741c7469ca 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/nat" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/units" "github.com/docker/docker/utils" @@ -306,7 +307,7 @@ func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) { func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) { out := make([]utils.KeyValuePair, opts.Len()) for i, o := range opts.GetAll() { - k, v, err := utils.ParseKeyValueOpt(o) + k, v, err := parsers.ParseKeyValueOpt(o) if err != nil { return nil, err } diff --git a/runconfig/parse_test.go b/runconfig/parse_test.go index a45a864924..aa6e4f2877 100644 --- a/runconfig/parse_test.go +++ b/runconfig/parse_test.go @@ -3,14 +3,14 @@ package runconfig import ( "testing" - "github.com/docker/docker/utils" + "github.com/docker/docker/pkg/parsers" ) func TestParseLxcConfOpt(t *testing.T) { opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "} for _, o := range opts { - k, v, err := utils.ParseKeyValueOpt(o) + k, v, err := parsers.ParseKeyValueOpt(o) if err != nil { t.FailNow() } diff --git a/server/server.go b/server/server.go index 072b36f816..9db3d3d1ed 100644 --- a/server/server.go +++ b/server/server.go @@ -54,12 +54,14 @@ import ( "github.com/docker/docker/graph" "github.com/docker/docker/image" "github.com/docker/docker/pkg/graphdb" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/parsers/filters" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/tailfile" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" - "github.com/docker/docker/utils/filters" ) func (srv *Server) handlerWrap(h engine.Handler) engine.Handler { @@ -383,7 +385,7 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status { } if img != nil { // This is a named image like 'busybox:latest' - repoName, repoTag := utils.ParseRepositoryTag(name) + repoName, repoTag := parsers.ParseRepositoryTag(name) if err := srv.exportImage(job.Eng, img.ID, tempdir); err != nil { return job.Error(err) } @@ -493,7 +495,7 @@ func (srv *Server) Build(job *engine.Job) engine.Status { ) job.GetenvJson("authConfig", authConfig) job.GetenvJson("configFile", configFile) - repoName, tag = utils.ParseRepositoryTag(repoName) + repoName, tag = parsers.ParseRepositoryTag(repoName) if remoteURL == "" { context = ioutil.NopCloser(job.Stdin) @@ -789,7 +791,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { imgcount = len(images) } kernelVersion := "" - if kv, err := utils.GetKernelVersion(); err == nil { + if kv, err := kernel.GetKernelVersion(); err == nil { kernelVersion = kv.String() } @@ -1746,7 +1748,7 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status { container, buildWarnings, err := srv.daemon.Create(config, name) if err != nil { if srv.daemon.Graph().IsNotExist(err) { - _, tag := utils.ParseRepositoryTag(config.Image) + _, tag := parsers.ParseRepositoryTag(config.Image) if tag == "" { tag = graph.DEFAULTTAG } @@ -1924,7 +1926,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, no tagDeleted bool ) - repoName, tag = utils.ParseRepositoryTag(name) + repoName, tag = parsers.ParseRepositoryTag(name) if tag == "" { tag = graph.DEFAULTTAG } @@ -1950,7 +1952,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force, no //If delete by id, see if the id belong only to one repository if repoName == "" { for _, repoAndTag := range srv.daemon.Repositories().ByID()[img.ID] { - parsedRepo, parsedTag := utils.ParseRepositoryTag(repoAndTag) + parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag) if repoName == "" || repoName == parsedRepo { repoName = parsedRepo if parsedTag != "" { diff --git a/utils/utils.go b/utils/utils.go index 0d3b4d0acb..4dd3034322 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -7,7 +7,6 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -401,92 +400,6 @@ func HashData(src io.Reader) (string, error) { return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil } -type KernelVersionInfo struct { - Kernel int - Major int - Minor int - Flavor string -} - -func (k *KernelVersionInfo) String() string { - return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor) -} - -// Compare two KernelVersionInfo struct. -// Returns -1 if a < b, 0 if a == b, 1 it a > b -func CompareKernelVersion(a, b *KernelVersionInfo) int { - if a.Kernel < b.Kernel { - return -1 - } else if a.Kernel > b.Kernel { - return 1 - } - - if a.Major < b.Major { - return -1 - } else if a.Major > b.Major { - return 1 - } - - if a.Minor < b.Minor { - return -1 - } else if a.Minor > b.Minor { - return 1 - } - - return 0 -} - -func GetKernelVersion() (*KernelVersionInfo, error) { - var ( - err error - ) - - uts, err := uname() - if err != nil { - return nil, err - } - - release := make([]byte, len(uts.Release)) - - i := 0 - for _, c := range uts.Release { - release[i] = byte(c) - i++ - } - - // Remove the \x00 from the release for Atoi to parse correctly - release = release[:bytes.IndexByte(release, 0)] - - return ParseRelease(string(release)) -} - -func ParseRelease(release string) (*KernelVersionInfo, error) { - var ( - kernel, major, minor, parsed int - flavor, partial string - ) - - // Ignore error from Sscanf to allow an empty flavor. Instead, just - // make sure we got all the version numbers. - parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial) - if parsed < 2 { - return nil, errors.New("Can't parse kernel version " + release) - } - - // sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64 - parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor) - if parsed < 1 { - flavor = partial - } - - return &KernelVersionInfo{ - Kernel: kernel, - Major: major, - Minor: minor, - Flavor: flavor, - }, nil -} - // FIXME: this is deprecated by CopyWithTar in archive.go func CopyDirectory(source, dest string) error { if output, err := exec.Command("cp", "-ra", source, dest).CombinedOutput(); err != nil { @@ -580,80 +493,6 @@ func GetLines(input []byte, commentMarker []byte) [][]byte { return output } -// FIXME: Change this not to receive default value as parameter -func ParseHost(defaultHost string, defaultUnix, addr string) (string, error) { - var ( - proto string - host string - port int - ) - addr = strings.TrimSpace(addr) - switch { - case addr == "tcp://": - return "", fmt.Errorf("Invalid bind address format: %s", addr) - case strings.HasPrefix(addr, "unix://"): - proto = "unix" - addr = strings.TrimPrefix(addr, "unix://") - if addr == "" { - addr = defaultUnix - } - case strings.HasPrefix(addr, "tcp://"): - proto = "tcp" - addr = strings.TrimPrefix(addr, "tcp://") - case strings.HasPrefix(addr, "fd://"): - return addr, nil - case addr == "": - proto = "unix" - addr = defaultUnix - default: - if strings.Contains(addr, "://") { - return "", fmt.Errorf("Invalid bind address protocol: %s", addr) - } - proto = "tcp" - } - - if proto != "unix" && strings.Contains(addr, ":") { - hostParts := strings.Split(addr, ":") - if len(hostParts) != 2 { - return "", fmt.Errorf("Invalid bind address format: %s", addr) - } - if hostParts[0] != "" { - host = hostParts[0] - } else { - host = defaultHost - } - - if p, err := strconv.Atoi(hostParts[1]); err == nil && p != 0 { - port = p - } else { - return "", fmt.Errorf("Invalid bind address format: %s", addr) - } - - } else if proto == "tcp" && !strings.Contains(addr, ":") { - return "", fmt.Errorf("Invalid bind address format: %s", addr) - } else { - host = addr - } - if proto == "unix" { - return fmt.Sprintf("%s://%s", proto, host), nil - } - return fmt.Sprintf("%s://%s:%d", proto, host, port), nil -} - -// Get a repos name and returns the right reposName + tag -// The tag can be confusing because of a port in a repository name. -// Ex: localhost.localdomain:5000/samalba/hipache:latest -func ParseRepositoryTag(repos string) (string, string) { - n := strings.LastIndex(repos, ":") - if n < 0 { - return repos, "" - } - if tag := repos[n+1:]; !strings.Contains(tag, "/") { - return repos[:n], tag - } - return repos, "" -} - // An StatusError reports an unsuccessful exit by a command. type StatusError struct { Status string @@ -699,27 +538,6 @@ func ShellQuoteArguments(args []string) string { return buf.String() } -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 -} - var globalTestID string // TestDirectory creates a new temporary directory and returns its path. @@ -833,14 +651,6 @@ func ReadSymlinkedDirectory(path string) (string, error) { return realPath, nil } -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 -} - // TreeSize walks a directory tree and returns its total size in bytes. func TreeSize(dir string) (size int64, err error) { data := make(map[uint64]struct{}) diff --git a/utils/utils_test.go b/utils/utils_test.go index 5ea1f30b21..cf11182f43 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -34,97 +34,6 @@ func TestBufReader(t *testing.T) { } } -func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { - if r := CompareKernelVersion(a, b); r != result { - t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) - } -} - -func TestCompareKernelVersion(t *testing.T) { - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 0) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - -1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 0) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - -1) -} - -func TestParseHost(t *testing.T) { - var ( - defaultHttpHost = "127.0.0.1" - defaultUnix = "/var/run/docker.sock" - ) - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.0"); err == nil { - t.Errorf("tcp 0.0.0.0 address expected error return, but err == nil, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://"); err == nil { - t.Errorf("default tcp:// address expected error return, but err == nil, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.1:5555"); err != nil || addr != "tcp://0.0.0.1:5555" { - t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, ":6666"); err != nil || addr != "tcp://127.0.0.1:6666" { - t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" { - t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" { - t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" { - t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix://"); err != nil || addr != "unix:///var/run/docker.sock" { - t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1"); err == nil { - t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr) - } - if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1:2375"); err == nil { - t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr) - } -} - -func TestParseRepositoryTag(t *testing.T) { - if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag) - } - if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag) - } - if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag) - } -} - func TestCheckLocalDns(t *testing.T) { for resolv, result := range map[string]bool{`# Dynamic nameserver 10.0.2.3 @@ -154,50 +63,6 @@ search docker.com`: true, } } } - -func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) { - var ( - a *KernelVersionInfo - ) - a, _ = ParseRelease(release) - - if r := CompareKernelVersion(a, b); r != result { - t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) - } - if a.Flavor != b.Flavor { - t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor) - } -} - -func TestParseRelease(t *testing.T) { - assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0) - assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) - assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) - assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0) - assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0) - assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0) -} - -func TestParsePortMapping(t *testing.T) { - data, err := PartParser("ip:public:private", "192.168.1.1:80:8080") - if err != nil { - t.Fatal(err) - } - - if len(data) != 3 { - t.FailNow() - } - if data["ip"] != "192.168.1.1" { - t.Fail() - } - if data["public"] != "80" { - t.Fail() - } - if data["private"] != "8080" { - t.Fail() - } -} - func TestReplaceAndAppendEnvVars(t *testing.T) { var ( d = []string{"HOME=/"}