diff --git a/api/api_unit_test.go b/api/api_unit_test.go index 2b3e76e75c..678331d369 100644 --- a/api/api_unit_test.go +++ b/api/api_unit_test.go @@ -1,9 +1,6 @@ package api import ( - "fmt" - "net/http" - "net/http/httptest" "testing" ) @@ -20,46 +17,3 @@ func TestJsonContentType(t *testing.T) { t.Fail() } } - -func TestGetBoolParam(t *testing.T) { - if ret, err := getBoolParam("true"); err != nil || !ret { - t.Fatalf("true -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("True"); err != nil || !ret { - t.Fatalf("True -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("1"); err != nil || !ret { - t.Fatalf("1 -> true, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam(""); err != nil || ret { - t.Fatalf("\"\" -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("false"); err != nil || ret { - t.Fatalf("false -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("0"); err != nil || ret { - t.Fatalf("0 -> false, nil | got %t %s", ret, err) - } - if ret, err := getBoolParam("faux"); err == nil || ret { - t.Fatalf("faux -> false, err | got %t %s", ret, err) - } -} - -func TesthttpError(t *testing.T) { - r := httptest.NewRecorder() - - httpError(r, fmt.Errorf("No such method")) - if r.Code != http.StatusNotFound { - t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code) - } - - httpError(r, fmt.Errorf("This accound hasn't been activated")) - if r.Code != http.StatusForbidden { - t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code) - } - - httpError(r, fmt.Errorf("Some error")) - if r.Code != http.StatusInternalServerError { - t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code) - } -} diff --git a/api/client/cli.go b/api/client/cli.go new file mode 100644 index 0000000000..b58d3c3c75 --- /dev/null +++ b/api/client/cli.go @@ -0,0 +1,102 @@ +package client + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "io" + "os" + "reflect" + "strings" + "text/template" + + flag "github.com/dotcloud/docker/pkg/mflag" + "github.com/dotcloud/docker/pkg/term" + "github.com/dotcloud/docker/registry" +) + +var funcMap = template.FuncMap{ + "json": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, +} + +func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) { + methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) + method := reflect.ValueOf(cli).MethodByName(methodName) + if !method.IsValid() { + return nil, false + } + return method.Interface().(func(...string) error), true +} + +func (cli *DockerCli) ParseCommands(args ...string) error { + if len(args) > 0 { + method, exists := cli.getMethod(args[0]) + if !exists { + fmt.Println("Error: Command not found:", args[0]) + return cli.CmdHelp(args[1:]...) + } + return method(args[1:]...) + } + return cli.CmdHelp(args...) +} + +func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { + flags := flag.NewFlagSet(name, flag.ContinueOnError) + flags.Usage = func() { + fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description) + flags.PrintDefaults() + os.Exit(2) + } + return flags +} + +func (cli *DockerCli) LoadConfigFile() (err error) { + cli.configFile, err = registry.LoadConfig(os.Getenv("HOME")) + if err != nil { + fmt.Fprintf(cli.err, "WARNING: %s\n", err) + } + return err +} + +func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli { + var ( + isTerminal = false + terminalFd uintptr + ) + + if in != nil { + if file, ok := in.(*os.File); ok { + terminalFd = file.Fd() + isTerminal = term.IsTerminal(terminalFd) + } + } + + if err == nil { + err = out + } + return &DockerCli{ + proto: proto, + addr: addr, + in: in, + out: out, + err: err, + isTerminal: isTerminal, + terminalFd: terminalFd, + tlsConfig: tlsConfig, + } +} + +type DockerCli struct { + proto string + addr string + configFile *registry.ConfigFile + in io.ReadCloser + out io.Writer + err io.Writer + isTerminal bool + terminalFd uintptr + tlsConfig *tls.Config +} diff --git a/api/client.go b/api/client/commands.go similarity index 82% rename from api/client.go rename to api/client/commands.go index 86858d0b30..49a5c008b3 100644 --- a/api/client.go +++ b/api/client/commands.go @@ -1,35 +1,18 @@ -package api +package client import ( "bufio" "bytes" - "crypto/tls" "encoding/base64" "encoding/json" - "errors" "fmt" - "github.com/dotcloud/docker/archive" - "github.com/dotcloud/docker/dockerversion" - "github.com/dotcloud/docker/engine" - "github.com/dotcloud/docker/nat" - flag "github.com/dotcloud/docker/pkg/mflag" - "github.com/dotcloud/docker/pkg/signal" - "github.com/dotcloud/docker/pkg/term" - "github.com/dotcloud/docker/registry" - "github.com/dotcloud/docker/runconfig" - "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "net" "net/http" - "net/http/httputil" "net/url" "os" "os/exec" - gosignal "os/signal" "path" - "reflect" - "regexp" goruntime "runtime" "strconv" "strings" @@ -37,40 +20,19 @@ import ( "text/tabwriter" "text/template" "time" + + "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/dockerversion" + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/nat" + "github.com/dotcloud/docker/pkg/signal" + "github.com/dotcloud/docker/pkg/term" + "github.com/dotcloud/docker/registry" + "github.com/dotcloud/docker/runconfig" + "github.com/dotcloud/docker/utils" ) -var funcMap = template.FuncMap{ - "json": func(v interface{}) string { - a, _ := json.Marshal(v) - return string(a) - }, -} - -var ( - ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") -) - -func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) { - methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) - method := reflect.ValueOf(cli).MethodByName(methodName) - if !method.IsValid() { - return nil, false - } - return method.Interface().(func(...string) error), true -} - -func (cli *DockerCli) ParseCommands(args ...string) error { - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - fmt.Println("Error: Command not found:", args[0]) - return cli.CmdHelp(args[1:]...) - } - return method(args[1:]...) - } - return cli.CmdHelp(args...) -} - func (cli *DockerCli) CmdHelp(args ...string) error { if len(args) > 0 { method, exists := cli.getMethod(args[0]) @@ -81,7 +43,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { return nil } } - help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTUNIXSOCKET) + help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", api.DEFAULTUNIXSOCKET) for _, command := range [][]string{ {"attach", "Attach to a running container"}, {"build", "Build a container from a Dockerfile"}, @@ -610,7 +572,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { return err } - container := &Container{} + container := &api.Container{} err = json.Unmarshal(body, container) if err != nil { return err @@ -797,9 +759,13 @@ func (cli *DockerCli) CmdPort(args ...string) error { return nil } - port := cmd.Arg(1) - proto := "tcp" - parts := strings.SplitN(port, "/", 2) + var ( + port = cmd.Arg(1) + proto = "tcp" + parts = strings.SplitN(port, "/", 2) + container api.Container + ) + if len(parts) == 2 && len(parts[1]) != 0 { port = parts[0] proto = parts[1] @@ -808,13 +774,13 @@ func (cli *DockerCli) CmdPort(args ...string) error { if err != nil { return err } - var out Container - err = json.Unmarshal(body, &out) + + err = json.Unmarshal(body, &container) if err != nil { return err } - if frontends, exists := out.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { + if frontends, exists := container.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { for _, frontend := range frontends { fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) } @@ -1425,7 +1391,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { outCommand = utils.Trunc(outCommand, 20) } ports.ReadListFrom([]byte(out.Get("Ports"))) - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), displayablePorts(ports), strings.Join(outNames, ",")) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ",")) if *size { if out.GetInt("SizeRootFs") > 0 { fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs"))) @@ -1606,7 +1572,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error { return err } - container := &Container{} + container := &api.Container{} err = json.Unmarshal(body, container) if err != nil { return err @@ -1643,7 +1609,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } - container := &Container{} + container := &api.Container{} err = json.Unmarshal(body, container) if err != nil { return err @@ -2130,417 +2096,3 @@ func (cli *DockerCli) CmdLoad(args ...string) error { } return nil } - -func (cli *DockerCli) dial() (net.Conn, error) { - if cli.tlsConfig != nil && cli.proto != "unix" { - return tls.Dial(cli.proto, cli.addr, cli.tlsConfig) - } - return net.Dial(cli.proto, cli.addr) -} - -func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { - params := bytes.NewBuffer(nil) - if data != nil { - if env, ok := data.(engine.Env); ok { - if err := env.Encode(params); err != nil { - return nil, -1, err - } - } else { - buf, err := json.Marshal(data) - if err != nil { - return nil, -1, err - } - if _, err := params.Write(buf); err != nil { - return nil, -1, err - } - } - } - // fixme: refactor client to support redirect - re := regexp.MustCompile("/+") - path = re.ReplaceAllString(path, "/") - - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), params) - if err != nil { - return nil, -1, err - } - if passAuthInfo { - cli.LoadConfigFile() - // Resolve the Auth config relevant for this server - authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress()) - getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) { - buf, err := json.Marshal(authConfig) - if err != nil { - return nil, err - } - registryAuthHeader := []string{ - base64.URLEncoding.EncodeToString(buf), - } - return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil - } - if headers, err := getHeaders(authConfig); err == nil && headers != nil { - for k, v := range headers { - req.Header[k] = v - } - } - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.Host = cli.addr - if data != nil { - req.Header.Set("Content-Type", "application/json") - } else if method == "POST" { - req.Header.Set("Content-Type", "plain/text") - } - dial, err := cli.dial() - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return nil, -1, ErrConnectionRefused - } - return nil, -1, err - } - clientconn := httputil.NewClientConn(dial, nil) - resp, err := clientconn.Do(req) - if err != nil { - clientconn.Close() - if strings.Contains(err.Error(), "connection refused") { - return nil, -1, ErrConnectionRefused - } - return nil, -1, err - } - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, -1, err - } - if len(body) == 0 { - return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL) - } - return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body)) - } - - wrapper := utils.NewReadCloserWrapper(resp.Body, func() error { - if resp != nil && resp.Body != nil { - resp.Body.Close() - } - return clientconn.Close() - }) - return wrapper, resp.StatusCode, nil -} - -func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { - if (method == "POST" || method == "PUT") && in == nil { - in = bytes.NewReader([]byte{}) - } - - // fixme: refactor client to support redirect - re := regexp.MustCompile("/+") - path = re.ReplaceAllString(path, "/") - - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), in) - if err != nil { - return err - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.Host = cli.addr - if method == "POST" { - req.Header.Set("Content-Type", "plain/text") - } - - if headers != nil { - for k, v := range headers { - req.Header[k] = v - } - } - - dial, err := cli.dial() - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") - } - return err - } - clientconn := httputil.NewClientConn(dial, nil) - resp, err := clientconn.Do(req) - defer clientconn.Close() - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") - } - return err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - if len(body) == 0 { - return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode)) - } - return fmt.Errorf("Error: %s", bytes.TrimSpace(body)) - } - - if MatchesContentType(resp.Header.Get("Content-Type"), "application/json") { - return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal) - } - if _, err := io.Copy(out, resp.Body); err != nil { - return err - } - return nil -} - -func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error { - defer func() { - if started != nil { - close(started) - } - }() - // fixme: refactor client to support redirect - re := regexp.MustCompile("/+") - path = re.ReplaceAllString(path, "/") - - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), nil) - if err != nil { - return err - } - req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) - req.Header.Set("Content-Type", "plain/text") - req.Host = cli.addr - - dial, err := cli.dial() - if err != nil { - if strings.Contains(err.Error(), "connection refused") { - return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") - } - return err - } - clientconn := httputil.NewClientConn(dial, nil) - defer clientconn.Close() - - // Server hijacks the connection, error 'connection closed' expected - clientconn.Do(req) - - rwc, br := clientconn.Hijack() - defer rwc.Close() - - if started != nil { - started <- rwc - } - - var receiveStdout chan error - - var oldState *term.State - - if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" { - oldState, err = term.SetRawTerminal(cli.terminalFd) - if err != nil { - return err - } - defer term.RestoreTerminal(cli.terminalFd, oldState) - } - - if stdout != nil || stderr != nil { - receiveStdout = utils.Go(func() (err error) { - defer func() { - if in != nil { - if setRawTerminal && cli.isTerminal { - term.RestoreTerminal(cli.terminalFd, oldState) - } - // For some reason this Close call blocks on darwin.. - // As the client exists right after, simply discard the close - // until we find a better solution. - if goruntime.GOOS != "darwin" { - in.Close() - } - } - }() - - // When TTY is ON, use regular copy - if setRawTerminal { - _, err = io.Copy(stdout, br) - } else { - _, err = utils.StdCopy(stdout, stderr, br) - } - utils.Debugf("[hijack] End of stdout") - return err - }) - } - - sendStdin := utils.Go(func() error { - if in != nil { - io.Copy(rwc, in) - utils.Debugf("[hijack] End of stdin") - } - if tcpc, ok := rwc.(*net.TCPConn); ok { - if err := tcpc.CloseWrite(); err != nil { - utils.Debugf("Couldn't send EOF: %s\n", err) - } - } else if unixc, ok := rwc.(*net.UnixConn); ok { - if err := unixc.CloseWrite(); err != nil { - utils.Debugf("Couldn't send EOF: %s\n", err) - } - } - // Discard errors due to pipe interruption - return nil - }) - - if stdout != nil || stderr != nil { - if err := <-receiveStdout; err != nil { - utils.Debugf("Error receiveStdout: %s", err) - return err - } - } - - if !cli.isTerminal { - if err := <-sendStdin; err != nil { - utils.Debugf("Error sendStdin: %s", err) - return err - } - } - return nil - -} - -func (cli *DockerCli) getTtySize() (int, int) { - if !cli.isTerminal { - return 0, 0 - } - ws, err := term.GetWinsize(cli.terminalFd) - if err != nil { - utils.Debugf("Error getting size: %s", err) - if ws == nil { - return 0, 0 - } - } - return int(ws.Height), int(ws.Width) -} - -func (cli *DockerCli) resizeTty(id string) { - height, width := cli.getTtySize() - if height == 0 && width == 0 { - return - } - v := url.Values{} - v.Set("h", strconv.Itoa(height)) - v.Set("w", strconv.Itoa(width)) - if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil { - utils.Debugf("Error resize: %s", err) - } -} - -func (cli *DockerCli) monitorTtySize(id string) error { - cli.resizeTty(id) - - sigchan := make(chan os.Signal, 1) - gosignal.Notify(sigchan, syscall.SIGWINCH) - go func() { - for _ = range sigchan { - cli.resizeTty(id) - } - }() - return nil -} - -func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { - flags := flag.NewFlagSet(name, flag.ContinueOnError) - flags.Usage = func() { - fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description) - flags.PrintDefaults() - os.Exit(2) - } - return flags -} - -func (cli *DockerCli) LoadConfigFile() (err error) { - cli.configFile, err = registry.LoadConfig(os.Getenv("HOME")) - if err != nil { - fmt.Fprintf(cli.err, "WARNING: %s\n", err) - } - return err -} - -func waitForExit(cli *DockerCli, containerId string) (int, error) { - stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false) - if err != nil { - return -1, err - } - - var out engine.Env - if err := out.Decode(stream); err != nil { - return -1, err - } - return out.GetInt("StatusCode"), nil -} - -// getExitCode perform an inspect on the container. It returns -// the running state and the exit code. -func getExitCode(cli *DockerCli, containerId string) (bool, int, error) { - body, _, err := readBody(cli.call("GET", "/containers/"+containerId+"/json", nil, false)) - if err != nil { - // If we can't connect, then the daemon probably died. - if err != ErrConnectionRefused { - return false, -1, err - } - return false, -1, nil - } - c := &Container{} - if err := json.Unmarshal(body, c); err != nil { - return false, -1, err - } - return c.State.Running, c.State.ExitCode, nil -} - -func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) { - if stream != nil { - defer stream.Close() - } - if err != nil { - return nil, statusCode, err - } - body, err := ioutil.ReadAll(stream) - if err != nil { - return nil, -1, err - } - return body, statusCode, nil -} - -func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli { - var ( - isTerminal = false - terminalFd uintptr - ) - - if in != nil { - if file, ok := in.(*os.File); ok { - terminalFd = file.Fd() - isTerminal = term.IsTerminal(terminalFd) - } - } - - if err == nil { - err = out - } - return &DockerCli{ - proto: proto, - addr: addr, - in: in, - out: out, - err: err, - isTerminal: isTerminal, - terminalFd: terminalFd, - tlsConfig: tlsConfig, - } -} - -type DockerCli struct { - proto string - addr string - configFile *registry.ConfigFile - in io.ReadCloser - out io.Writer - err io.Writer - isTerminal bool - terminalFd uintptr - tlsConfig *tls.Config -} diff --git a/api/client/utils.go b/api/client/utils.go new file mode 100644 index 0000000000..4ef09ba783 --- /dev/null +++ b/api/client/utils.go @@ -0,0 +1,390 @@ +package client + +import ( + "bytes" + "crypto/tls" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + gosignal "os/signal" + "regexp" + goruntime "runtime" + "strconv" + "strings" + "syscall" + + "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/dockerversion" + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/pkg/term" + "github.com/dotcloud/docker/registry" + "github.com/dotcloud/docker/utils" +) + +var ( + ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") +) + +func (cli *DockerCli) dial() (net.Conn, error) { + if cli.tlsConfig != nil && cli.proto != "unix" { + return tls.Dial(cli.proto, cli.addr, cli.tlsConfig) + } + return net.Dial(cli.proto, cli.addr) +} + +func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { + params := bytes.NewBuffer(nil) + if data != nil { + if env, ok := data.(engine.Env); ok { + if err := env.Encode(params); err != nil { + return nil, -1, err + } + } else { + buf, err := json.Marshal(data) + if err != nil { + return nil, -1, err + } + if _, err := params.Write(buf); err != nil { + return nil, -1, err + } + } + } + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") + + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) + if err != nil { + return nil, -1, err + } + if passAuthInfo { + cli.LoadConfigFile() + // Resolve the Auth config relevant for this server + authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress()) + getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) { + buf, err := json.Marshal(authConfig) + if err != nil { + return nil, err + } + registryAuthHeader := []string{ + base64.URLEncoding.EncodeToString(buf), + } + return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil + } + if headers, err := getHeaders(authConfig); err == nil && headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } + } + req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) + req.Host = cli.addr + if data != nil { + req.Header.Set("Content-Type", "application/json") + } else if method == "POST" { + req.Header.Set("Content-Type", "plain/text") + } + dial, err := cli.dial() + if err != nil { + if strings.Contains(err.Error(), "connection refused") { + return nil, -1, ErrConnectionRefused + } + return nil, -1, err + } + clientconn := httputil.NewClientConn(dial, nil) + resp, err := clientconn.Do(req) + if err != nil { + clientconn.Close() + if strings.Contains(err.Error(), "connection refused") { + return nil, -1, ErrConnectionRefused + } + return nil, -1, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, -1, err + } + if len(body) == 0 { + return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL) + } + return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body)) + } + + wrapper := utils.NewReadCloserWrapper(resp.Body, func() error { + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + return clientconn.Close() + }) + return wrapper, resp.StatusCode, nil +} + +func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { + if (method == "POST" || method == "PUT") && in == nil { + in = bytes.NewReader([]byte{}) + } + + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") + + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in) + if err != nil { + return err + } + req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) + req.Host = cli.addr + if method == "POST" { + req.Header.Set("Content-Type", "plain/text") + } + + if headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } + + dial, err := cli.dial() + if err != nil { + if strings.Contains(err.Error(), "connection refused") { + return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") + } + return err + } + clientconn := httputil.NewClientConn(dial, nil) + resp, err := clientconn.Do(req) + defer clientconn.Close() + if err != nil { + if strings.Contains(err.Error(), "connection refused") { + return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") + } + return err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + if len(body) == 0 { + return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode)) + } + return fmt.Errorf("Error: %s", bytes.TrimSpace(body)) + } + + if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") { + return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal) + } + if _, err := io.Copy(out, resp.Body); err != nil { + return err + } + return nil +} + +func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error { + defer func() { + if started != nil { + close(started) + } + }() + // fixme: refactor client to support redirect + re := regexp.MustCompile("/+") + path = re.ReplaceAllString(path, "/") + + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil) + if err != nil { + return err + } + req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) + req.Header.Set("Content-Type", "plain/text") + req.Host = cli.addr + + dial, err := cli.dial() + if err != nil { + if strings.Contains(err.Error(), "connection refused") { + return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") + } + return err + } + clientconn := httputil.NewClientConn(dial, nil) + defer clientconn.Close() + + // Server hijacks the connection, error 'connection closed' expected + clientconn.Do(req) + + rwc, br := clientconn.Hijack() + defer rwc.Close() + + if started != nil { + started <- rwc + } + + var receiveStdout chan error + + var oldState *term.State + + if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" { + oldState, err = term.SetRawTerminal(cli.terminalFd) + if err != nil { + return err + } + defer term.RestoreTerminal(cli.terminalFd, oldState) + } + + if stdout != nil || stderr != nil { + receiveStdout = utils.Go(func() (err error) { + defer func() { + if in != nil { + if setRawTerminal && cli.isTerminal { + term.RestoreTerminal(cli.terminalFd, oldState) + } + // For some reason this Close call blocks on darwin.. + // As the client exists right after, simply discard the close + // until we find a better solution. + if goruntime.GOOS != "darwin" { + in.Close() + } + } + }() + + // When TTY is ON, use regular copy + if setRawTerminal { + _, err = io.Copy(stdout, br) + } else { + _, err = utils.StdCopy(stdout, stderr, br) + } + utils.Debugf("[hijack] End of stdout") + return err + }) + } + + sendStdin := utils.Go(func() error { + if in != nil { + io.Copy(rwc, in) + utils.Debugf("[hijack] End of stdin") + } + if tcpc, ok := rwc.(*net.TCPConn); ok { + if err := tcpc.CloseWrite(); err != nil { + utils.Debugf("Couldn't send EOF: %s\n", err) + } + } else if unixc, ok := rwc.(*net.UnixConn); ok { + if err := unixc.CloseWrite(); err != nil { + utils.Debugf("Couldn't send EOF: %s\n", err) + } + } + // Discard errors due to pipe interruption + return nil + }) + + if stdout != nil || stderr != nil { + if err := <-receiveStdout; err != nil { + utils.Debugf("Error receiveStdout: %s", err) + return err + } + } + + if !cli.isTerminal { + if err := <-sendStdin; err != nil { + utils.Debugf("Error sendStdin: %s", err) + return err + } + } + return nil + +} + +func (cli *DockerCli) resizeTty(id string) { + height, width := cli.getTtySize() + if height == 0 && width == 0 { + return + } + v := url.Values{} + v.Set("h", strconv.Itoa(height)) + v.Set("w", strconv.Itoa(width)) + if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil { + utils.Debugf("Error resize: %s", err) + } +} + +func waitForExit(cli *DockerCli, containerId string) (int, error) { + stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false) + if err != nil { + return -1, err + } + + var out engine.Env + if err := out.Decode(stream); err != nil { + return -1, err + } + return out.GetInt("StatusCode"), nil +} + +// getExitCode perform an inspect on the container. It returns +// the running state and the exit code. +func getExitCode(cli *DockerCli, containerId string) (bool, int, error) { + body, _, err := readBody(cli.call("GET", "/containers/"+containerId+"/json", nil, false)) + if err != nil { + // If we can't connect, then the daemon probably died. + if err != ErrConnectionRefused { + return false, -1, err + } + return false, -1, nil + } + c := &api.Container{} + if err := json.Unmarshal(body, c); err != nil { + return false, -1, err + } + return c.State.Running, c.State.ExitCode, nil +} + +func (cli *DockerCli) monitorTtySize(id string) error { + cli.resizeTty(id) + + sigchan := make(chan os.Signal, 1) + gosignal.Notify(sigchan, syscall.SIGWINCH) + go func() { + for _ = range sigchan { + cli.resizeTty(id) + } + }() + return nil +} + +func (cli *DockerCli) getTtySize() (int, int) { + if !cli.isTerminal { + return 0, 0 + } + ws, err := term.GetWinsize(cli.terminalFd) + if err != nil { + utils.Debugf("Error getting size: %s", err) + if ws == nil { + return 0, 0 + } + } + return int(ws.Height), int(ws.Width) +} + +func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) { + if stream != nil { + defer stream.Close() + } + if err != nil { + return nil, statusCode, err + } + body, err := ioutil.ReadAll(stream) + if err != nil { + return nil, -1, err + } + return body, statusCode, nil +} diff --git a/api/common.go b/api/common.go index 5e5d2c5767..7273e5c56d 100644 --- a/api/common.go +++ b/api/common.go @@ -23,7 +23,7 @@ func ValidateHost(val string) (string, error) { } //TODO remove, used on < 1.5 in getContainersJSON -func displayablePorts(ports *engine.Table) string { +func DisplayablePorts(ports *engine.Table) string { result := []string{} ports.SetKey("PublicPort") ports.Sort() diff --git a/api/server.go b/api/server/server.go similarity index 98% rename from api/server.go rename to api/server/server.go index 29ea180030..18aefe42cd 100644 --- a/api/server.go +++ b/api/server/server.go @@ -1,4 +1,4 @@ -package api +package server import ( "bufio" @@ -10,14 +10,6 @@ import ( "encoding/json" "expvar" "fmt" - "github.com/dotcloud/docker/engine" - "github.com/dotcloud/docker/pkg/listenbuffer" - "github.com/dotcloud/docker/pkg/systemd" - "github.com/dotcloud/docker/pkg/user" - "github.com/dotcloud/docker/pkg/version" - "github.com/dotcloud/docker/registry" - "github.com/dotcloud/docker/utils" - "github.com/gorilla/mux" "io" "io/ioutil" "log" @@ -28,6 +20,16 @@ import ( "strconv" "strings" "syscall" + + "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/pkg/listenbuffer" + "github.com/dotcloud/docker/pkg/systemd" + "github.com/dotcloud/docker/pkg/user" + "github.com/dotcloud/docker/pkg/version" + "github.com/dotcloud/docker/registry" + "github.com/dotcloud/docker/utils" + "github.com/gorilla/mux" ) var ( @@ -315,7 +317,7 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo for _, out := range outs.Data { ports := engine.NewTable("", 0) ports.ReadListFrom([]byte(out.Get("Ports"))) - out.Set("Ports", displayablePorts(ports)) + out.Set("Ports", api.DisplayablePorts(ports)) } w.Header().Set("Content-Type", "application/json") if _, err = outs.WriteListTo(w); err != nil { @@ -638,7 +640,7 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res job := eng.Job("start", name) // allow a nil body for backwards compatibility if r.Body != nil { - if MatchesContentType(r.Header.Get("Content-Type"), "application/json") { + if api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") { if err := job.DecodeEnv(r.Body); err != nil { return err } @@ -885,7 +887,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp var copyData engine.Env - if contentType := r.Header.Get("Content-Type"); MatchesContentType(contentType, "application/json") { + if contentType := r.Header.Get("Content-Type"); api.MatchesContentType(contentType, "application/json") { if err := copyData.Decode(r.Body); err != nil { return err } @@ -943,14 +945,14 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local } version := version.Version(mux.Vars(r)["version"]) if version == "" { - version = APIVERSION + version = api.APIVERSION } if enableCors { writeCorsHeaders(w, r) } - if version.GreaterThan(APIVERSION) { - http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, APIVERSION).Error(), http.StatusNotFound) + if version.GreaterThan(api.APIVERSION) { + http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound) return } diff --git a/api/server/server_unit_test.go b/api/server/server_unit_test.go new file mode 100644 index 0000000000..5ea5af411c --- /dev/null +++ b/api/server/server_unit_test.go @@ -0,0 +1,52 @@ +package server + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" +) + +func TestGetBoolParam(t *testing.T) { + if ret, err := getBoolParam("true"); err != nil || !ret { + t.Fatalf("true -> true, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam("True"); err != nil || !ret { + t.Fatalf("True -> true, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam("1"); err != nil || !ret { + t.Fatalf("1 -> true, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam(""); err != nil || ret { + t.Fatalf("\"\" -> false, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam("false"); err != nil || ret { + t.Fatalf("false -> false, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam("0"); err != nil || ret { + t.Fatalf("0 -> false, nil | got %t %s", ret, err) + } + if ret, err := getBoolParam("faux"); err == nil || ret { + t.Fatalf("faux -> false, err | got %t %s", ret, err) + + } +} + +func TesthttpError(t *testing.T) { + r := httptest.NewRecorder() + + httpError(r, fmt.Errorf("No such method")) + if r.Code != http.StatusNotFound { + t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code) + } + + httpError(r, fmt.Errorf("This accound hasn't been activated")) + if r.Code != http.StatusForbidden { + t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code) + } + + httpError(r, fmt.Errorf("Some error")) + if r.Code != http.StatusInternalServerError { + t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code) + } +} diff --git a/builtins/builtins.go b/builtins/builtins.go index 10ee9b19e6..109bc5b913 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -1,7 +1,7 @@ package builtins import ( - "github.com/dotcloud/docker/api" + api "github.com/dotcloud/docker/api/server" "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/runtime/networkdriver/bridge" "github.com/dotcloud/docker/server" diff --git a/docker/docker.go b/docker/docker.go index e4ce8a0b74..e96c173d30 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/api/client" "github.com/dotcloud/docker/builtins" "github.com/dotcloud/docker/dockerversion" "github.com/dotcloud/docker/engine" @@ -178,7 +179,7 @@ func main() { protoAddrParts := strings.SplitN(flHosts.GetAll()[0], "://", 2) var ( - cli *api.DockerCli + cli *client.DockerCli tlsConfig tls.Config ) tlsConfig.InsecureSkipVerify = true @@ -211,9 +212,9 @@ func main() { } if *flTls || *flTlsVerify { - cli = api.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig) } else { - cli = api.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil) } if err := cli.ParseCommands(flag.Args()...); err != nil { diff --git a/integration/api_test.go b/integration/api_test.go index bac4efea53..d08617ea69 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -5,14 +5,6 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/dotcloud/docker/api" - "github.com/dotcloud/docker/dockerversion" - "github.com/dotcloud/docker/engine" - "github.com/dotcloud/docker/image" - "github.com/dotcloud/docker/runconfig" - "github.com/dotcloud/docker/runtime" - "github.com/dotcloud/docker/utils" - "github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" "io" "io/ioutil" "net" @@ -21,6 +13,16 @@ import ( "strings" "testing" "time" + + "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/api/server" + "github.com/dotcloud/docker/dockerversion" + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/image" + "github.com/dotcloud/docker/runconfig" + "github.com/dotcloud/docker/runtime" + "github.com/dotcloud/docker/utils" + "github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) func TestGetVersion(t *testing.T) { @@ -35,7 +37,7 @@ func TestGetVersion(t *testing.T) { t.Fatal(err) } // FIXME getting the version should require an actual running Server - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -77,7 +79,7 @@ func TestGetInfo(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -125,7 +127,7 @@ func TestGetEvents(t *testing.T) { r := httptest.NewRecorder() setTimeout(t, "", 500*time.Millisecond, func() { - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -166,7 +168,7 @@ func TestGetImagesJSON(t *testing.T) { r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -201,7 +203,7 @@ func TestGetImagesJSON(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil { t.Fatal(err) } assertHttpNotError(r2, t) @@ -234,7 +236,7 @@ func TestGetImagesJSON(t *testing.T) { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r3, req3); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r3, req3); err != nil { t.Fatal(err) } assertHttpNotError(r3, t) @@ -259,7 +261,7 @@ func TestGetImagesHistory(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -283,7 +285,7 @@ func TestGetImagesByName(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -327,7 +329,7 @@ func TestGetContainersJSON(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -363,7 +365,7 @@ func TestGetContainersExport(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -401,7 +403,7 @@ func TestSaveImageAndThenLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusOK { @@ -415,7 +417,7 @@ func TestSaveImageAndThenLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusOK { @@ -428,7 +430,7 @@ func TestSaveImageAndThenLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusNotFound { @@ -441,7 +443,7 @@ func TestSaveImageAndThenLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusOK { @@ -454,7 +456,7 @@ func TestSaveImageAndThenLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusOK { @@ -481,7 +483,7 @@ func TestGetContainersChanges(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -548,7 +550,7 @@ func TestGetContainersTop(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -596,7 +598,7 @@ func TestGetContainersByName(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -631,7 +633,7 @@ func TestPostCommit(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -667,7 +669,7 @@ func TestPostContainersCreate(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -716,7 +718,7 @@ func TestPostContainersKill(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -755,7 +757,7 @@ func TestPostContainersRestart(t *testing.T) { t.Fatal(err) } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -797,7 +799,7 @@ func TestPostContainersStart(t *testing.T) { req.Header.Set("Content-Type", "application/json") r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -814,7 +816,7 @@ func TestPostContainersStart(t *testing.T) { } r = httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } // Starting an already started container should return an error @@ -852,7 +854,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) { req.Header.Set("Content-Type", "application/json") r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusInternalServerError { @@ -889,7 +891,7 @@ func TestPostContainersStop(t *testing.T) { t.Fatal(err) } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -921,7 +923,7 @@ func TestPostContainersWait(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -979,7 +981,7 @@ func TestPostContainersAttach(t *testing.T) { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r.ResponseRecorder, t) @@ -1057,7 +1059,7 @@ func TestPostContainersAttachStderr(t *testing.T) { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r.ResponseRecorder, t) @@ -1114,7 +1116,7 @@ func TestDeleteContainers(t *testing.T) { t.Fatal(err) } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -1133,7 +1135,7 @@ func TestOptionsRoute(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -1152,7 +1154,7 @@ func TestGetEnabledCors(t *testing.T) { if err != nil { t.Fatal(err) } - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -1199,7 +1201,7 @@ func TestDeleteImages(t *testing.T) { } r := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusConflict { @@ -1212,7 +1214,7 @@ func TestDeleteImages(t *testing.T) { } r2 := httptest.NewRecorder() - if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil { t.Fatal(err) } assertHttpNotError(r2, t) @@ -1264,7 +1266,7 @@ func TestPostContainersCopy(t *testing.T) { t.Fatal(err) } req.Header.Add("Content-Type", "application/json") - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } assertHttpNotError(r, t) @@ -1312,7 +1314,7 @@ func TestPostContainersCopyWhenContainerNotFound(t *testing.T) { t.Fatal(err) } req.Header.Add("Content-Type", "application/json") - if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil { + if err := server.ServeRequest(eng, api.APIVERSION, r, req); err != nil { t.Fatal(err) } if r.Code != http.StatusNotFound { diff --git a/integration/commands_test.go b/integration/commands_test.go index 7de7a227ea..2dc0ff384a 100644 --- a/integration/commands_test.go +++ b/integration/commands_test.go @@ -3,7 +3,7 @@ package docker import ( "bufio" "fmt" - "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/api/client" "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/image" "github.com/dotcloud/docker/pkg/term" @@ -121,7 +121,7 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error func TestRunHostname(t *testing.T) { stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -166,7 +166,7 @@ func TestRunHostname(t *testing.T) { func TestRunWorkdir(t *testing.T) { stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -211,7 +211,7 @@ func TestRunWorkdir(t *testing.T) { func TestRunWorkdirExists(t *testing.T) { stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -255,7 +255,7 @@ func TestRunWorkdirExists(t *testing.T) { // TestRunWorkdirExistsAndIsFile checks that if 'docker run -w' with existing file can be detected func TestRunWorkdirExistsAndIsFile(t *testing.T) { - cli := api.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -275,7 +275,7 @@ func TestRunExit(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c1 := make(chan struct{}) @@ -328,7 +328,7 @@ func TestRunDisconnect(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c1 := make(chan struct{}) @@ -374,7 +374,7 @@ func TestRunDisconnectTty(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c1 := make(chan struct{}) @@ -426,7 +426,7 @@ func TestRunAttachStdin(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) ch := make(chan struct{}) @@ -490,7 +490,7 @@ func TestRunDetach(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) ch := make(chan struct{}) @@ -537,7 +537,7 @@ func TestAttachDetach(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) ch := make(chan struct{}) @@ -570,7 +570,7 @@ func TestAttachDetach(t *testing.T) { stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() - cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) ch = make(chan struct{}) go func() { @@ -618,7 +618,7 @@ func TestAttachDetachTruncatedID(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) // Discard the CmdRun output @@ -636,7 +636,7 @@ func TestAttachDetachTruncatedID(t *testing.T) { stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() - cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) ch := make(chan struct{}) go func() { @@ -683,7 +683,7 @@ func TestAttachDisconnect(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) go func() { @@ -752,7 +752,7 @@ func TestAttachDisconnect(t *testing.T) { func TestRunAutoRemove(t *testing.T) { t.Skip("Fixme. Skipping test for now, race condition") stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -788,7 +788,7 @@ func TestRunAutoRemove(t *testing.T) { func TestCmdLogs(t *testing.T) { t.Skip("Test not impemented") - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil { @@ -806,7 +806,7 @@ func TestCmdLogs(t *testing.T) { // Expected behaviour: error out when attempting to bind mount non-existing source paths func TestRunErrorBindNonExistingSource(t *testing.T) { - cli := api.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -826,7 +826,7 @@ func TestRunErrorBindNonExistingSource(t *testing.T) { func TestImagesViz(t *testing.T) { stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) image := buildTestImages(t, globalEngine) @@ -876,7 +876,7 @@ func TestImagesViz(t *testing.T) { func TestImagesTree(t *testing.T) { stdout, stdoutPipe := io.Pipe() - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) image := buildTestImages(t, globalEngine) @@ -959,7 +959,7 @@ func TestRunCidFileCheckIDLength(t *testing.T) { } tmpCidFile := path.Join(tmpDir, "cid") - cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -1008,7 +1008,7 @@ func TestRunCidFileCleanupIfEmpty(t *testing.T) { } tmpCidFile := path.Join(tmpDir, "cid") - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -1038,7 +1038,7 @@ func TestContainerOrphaning(t *testing.T) { defer os.RemoveAll(tmpDir) // setup a CLI and server - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) srv := mkServerFromEngine(globalEngine, t) @@ -1098,8 +1098,8 @@ func TestCmdKill(t *testing.T) { var ( stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() - cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) - cli2 = api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli2 = client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) ) defer cleanup(globalEngine, t) diff --git a/integration/https_test.go b/integration/https_test.go index a1c855e1a9..0b4abea881 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -3,7 +3,7 @@ package docker import ( "crypto/tls" "crypto/x509" - "github.com/dotcloud/docker/api" + "github.com/dotcloud/docker/api/client" "io/ioutil" "testing" "time" @@ -35,7 +35,7 @@ func getTlsConfig(certFile, keyFile string, t *testing.T) *tls.Config { // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint func TestHttpsInfo(t *testing.T) { - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() { @@ -48,7 +48,7 @@ func TestHttpsInfo(t *testing.T) { // TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint // by using a rogue client certificate and checks that it fails with the expected error. func TestHttpsInfoRogueCert(t *testing.T) { - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() { @@ -65,7 +65,7 @@ func TestHttpsInfoRogueCert(t *testing.T) { // TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint // which provides a rogue server certificate and checks that it fails with the expected error func TestHttpsInfoRogueServerCert(t *testing.T) { - cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() {