Forráskód Böngészése

Merge pull request #18472 from calavera/api_client_lib

Api client lib
Tibor Vass 9 éve
szülő
commit
375f754f49
98 módosított fájl, 3064 hozzáadás és 1785 törlés
  1. 19 21
      api/client/attach.go
  2. 40 98
      api/client/build.go
  3. 39 67
      api/client/cli.go
  4. 71 0
      api/client/client.go
  5. 13 26
      api/client/commit.go
  6. 12 77
      api/client/cp.go
  7. 31 45
      api/client/create.go
  8. 1 10
      api/client/diff.go
  9. 14 35
      api/client/events.go
  10. 14 41
      api/client/exec.go
  11. 6 7
      api/client/export.go
  12. 18 198
      api/client/hijack.go
  13. 1 10
      api/client/history.go
  14. 8 24
      api/client/images.go
  15. 22 18
      api/client/import.go
  16. 31 43
      api/client/info.go
  17. 75 147
      api/client/inspect.go
  18. 111 0
      api/client/inspect/inspector.go
  19. 1 1
      api/client/kill.go
  20. 102 0
      api/client/lib/client.go
  21. 30 0
      api/client/lib/container_attach.go
  22. 37 0
      api/client/lib/container_commit.go
  23. 43 0
      api/client/lib/container_create.go
  24. 64 0
      api/client/lib/container_inspect.go
  25. 54 0
      api/client/lib/container_list.go
  26. 26 0
      api/client/lib/container_remove.go
  27. 12 0
      api/client/lib/container_rename.go
  28. 17 0
      api/client/lib/container_restart.go
  29. 8 0
      api/client/lib/container_start.go
  30. 22 0
      api/client/lib/container_stats.go
  31. 16 0
      api/client/lib/container_stop.go
  32. 27 0
      api/client/lib/container_top.go
  33. 8 0
      api/client/lib/container_unpause.go
  34. 95 0
      api/client/lib/copy.go
  35. 25 0
      api/client/lib/diff.go
  36. 94 0
      api/client/lib/errors.go
  37. 46 0
      api/client/lib/events.go
  38. 49 0
      api/client/lib/exec.go
  39. 18 0
      api/client/lib/export.go
  40. 164 0
      api/client/lib/hijack.go
  41. 23 0
      api/client/lib/history.go
  42. 113 0
      api/client/lib/image_build.go
  43. 26 0
      api/client/lib/image_create.go
  44. 27 0
      api/client/lib/image_import.go
  45. 37 0
      api/client/lib/image_inspect.go
  46. 39 0
      api/client/lib/image_list.go
  47. 17 0
      api/client/lib/image_load.go
  48. 34 0
      api/client/lib/image_pull.go
  49. 36 0
      api/client/lib/image_push.go
  50. 30 0
      api/client/lib/image_remove.go
  51. 20 0
      api/client/lib/image_save.go
  52. 39 0
      api/client/lib/image_search.go
  53. 21 0
      api/client/lib/image_tag.go
  54. 25 0
      api/client/lib/info.go
  55. 13 0
      api/client/lib/kill.go
  56. 28 0
      api/client/lib/login.go
  57. 46 0
      api/client/lib/logs.go
  58. 71 0
      api/client/lib/network.go
  59. 8 0
      api/client/lib/pause.go
  60. 9 0
      api/client/lib/privileged.go
  61. 165 0
      api/client/lib/request.go
  62. 28 0
      api/client/lib/resize.go
  63. 38 0
      api/client/lib/version.go
  64. 66 0
      api/client/lib/volume.go
  65. 24 0
      api/client/lib/wait.go
  66. 11 13
      api/client/load.go
  67. 9 19
      api/client/login.go
  68. 19 34
      api/client/logs.go
  69. 14 99
      api/client/network.go
  70. 1 1
      api/client/pause.go
  71. 1 14
      api/client/port.go
  72. 8 40
      api/client/ps.go
  73. 29 7
      api/client/pull.go
  74. 27 6
      api/client/push.go
  75. 1 1
      api/client/rename.go
  76. 1 7
      api/client/restart.go
  77. 9 15
      api/client/rm.go
  78. 7 11
      api/client/rmi.go
  79. 22 35
      api/client/run.go
  80. 6 12
      api/client/save.go
  81. 20 13
      api/client/search.go
  82. 54 82
      api/client/start.go
  83. 10 22
      api/client/stats.go
  84. 1 7
      api/client/stop.go
  85. 7 10
      api/client/tag.go
  86. 3 13
      api/client/top.go
  87. 15 22
      api/client/trust.go
  88. 1 1
      api/client/unpause.go
  89. 24 280
      api/client/utils.go
  90. 16 51
      api/client/version.go
  91. 9 93
      api/client/volume.go
  92. 1 1
      api/client/wait.go
  93. 234 0
      api/types/client.go
  94. 17 1
      cliconfig/config.go
  95. 11 0
      integration-cli/docker_cli_inspect_test.go
  96. 2 2
      integration-cli/docker_cli_logs_test.go
  97. 1 1
      integration-cli/docker_cli_network_unix_test.go
  98. 6 4
      integration-cli/docker_cli_volume_test.go

+ 19 - 21
api/client/attach.go

@@ -1,10 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -25,18 +23,11 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
+	c, err := cli.client.ContainerInspect(cmd.Arg(0))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	var c types.ContainerJSON
-	if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-		return err
-	}
-
 	if !c.State.Running {
 	if !c.State.Running {
 		return fmt.Errorf("You cannot attach to a stopped container, start it first")
 		return fmt.Errorf("You cannot attach to a stopped container, start it first")
 	}
 	}
@@ -55,28 +46,35 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	var in io.ReadCloser
+	options := types.ContainerAttachOptions{
+		ContainerID: cmd.Arg(0),
+		Stream:      true,
+		Stdin:       !*noStdin && c.Config.OpenStdin,
+		Stdout:      true,
+		Stderr:      true,
+	}
 
 
-	v := url.Values{}
-	v.Set("stream", "1")
-	if !*noStdin && c.Config.OpenStdin {
-		v.Set("stdin", "1")
+	var in io.ReadCloser
+	if options.Stdin {
 		in = cli.in
 		in = cli.in
 	}
 	}
 
 
-	v.Set("stdout", "1")
-	v.Set("stderr", "1")
-
 	if *proxy && !c.Config.Tty {
 	if *proxy && !c.Config.Tty {
-		sigc := cli.forwardAllSignals(cmd.Arg(0))
+		sigc := cli.forwardAllSignals(options.ContainerID)
 		defer signal.StopCatch(sigc)
 		defer signal.StopCatch(sigc)
 	}
 	}
 
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), c.Config.Tty, in, cli.out, cli.err, nil, nil); err != nil {
+	resp, err := cli.client.ContainerAttach(options)
+	if err != nil {
+		return err
+	}
+	defer resp.Close()
+
+	if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	_, status, err := getExitCode(cli, cmd.Arg(0))
+	_, status, err := getExitCode(cli, options.ContainerID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 40 - 98
api/client/build.go

@@ -3,23 +3,19 @@ package client
 import (
 import (
 	"archive/tar"
 	"archive/tar"
 	"bufio"
 	"bufio"
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
-	"net/http"
-	"net/url"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"regexp"
 	"regexp"
 	"runtime"
 	"runtime"
-	"strconv"
 	"strings"
 	"strings"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
@@ -33,7 +29,6 @@ import (
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
-	"github.com/docker/docker/runconfig"
 	tagpkg "github.com/docker/docker/tag"
 	tagpkg "github.com/docker/docker/tag"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
@@ -207,108 +202,55 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	// Send the build context
-	v := url.Values{
-		"t": flTags.GetAll(),
-	}
-	if *suppressOutput {
-		v.Set("q", "1")
-	}
+	var remoteContext string
 	if isRemote {
 	if isRemote {
-		v.Set("remote", cmd.Arg(0))
-	}
-	if *noCache {
-		v.Set("nocache", "1")
-	}
-	if *rm {
-		v.Set("rm", "1")
-	} else {
-		v.Set("rm", "0")
-	}
-
-	if *forceRm {
-		v.Set("forcerm", "1")
-	}
-
-	if *pull {
-		v.Set("pull", "1")
-	}
-
-	if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(*isolation)) {
-		v.Set("isolation", *isolation)
-	}
-
-	v.Set("cpusetcpus", *flCPUSetCpus)
-	v.Set("cpusetmems", *flCPUSetMems)
-	v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
-	v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
-	v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
-	v.Set("memory", strconv.FormatInt(memory, 10))
-	v.Set("memswap", strconv.FormatInt(memorySwap, 10))
-	v.Set("cgroupparent", *flCgroupParent)
-
-	if *flShmSize != "" {
-		parsedShmSize, err := units.RAMInBytes(*flShmSize)
-		if err != nil {
-			return err
-		}
-		v.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
-	}
-
-	v.Set("dockerfile", relDockerfile)
-
-	ulimitsVar := flUlimits.GetList()
-	ulimitsJSON, err := json.Marshal(ulimitsVar)
-	if err != nil {
-		return err
-	}
-	v.Set("ulimits", string(ulimitsJSON))
-
-	// collect all the build-time environment variables for the container
-	buildArgs := runconfig.ConvertKVStringsToMap(flBuildArg.GetAll())
-	buildArgsJSON, err := json.Marshal(buildArgs)
+		remoteContext = cmd.Arg(0)
+	}
+
+	options := types.ImageBuildOptions{
+		Context:        body,
+		Memory:         memory,
+		MemorySwap:     memorySwap,
+		Tags:           flTags.GetAll(),
+		SuppressOutput: *suppressOutput,
+		RemoteContext:  remoteContext,
+		NoCache:        *noCache,
+		Remove:         *rm,
+		ForceRemove:    *forceRm,
+		PullParent:     *pull,
+		Isolation:      *isolation,
+		CPUSetCPUs:     *flCPUSetCpus,
+		CPUSetMems:     *flCPUSetMems,
+		CPUShares:      *flCPUShares,
+		CPUQuota:       *flCPUQuota,
+		CPUPeriod:      *flCPUPeriod,
+		CgroupParent:   *flCgroupParent,
+		ShmSize:        *flShmSize,
+		Dockerfile:     relDockerfile,
+		Ulimits:        flUlimits.GetList(),
+		BuildArgs:      flBuildArg.GetAll(),
+		AuthConfigs:    cli.configFile.AuthConfigs,
+	}
+
+	response, err := cli.client.ImageBuild(options)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	v.Set("buildargs", string(buildArgsJSON))
 
 
-	headers := http.Header(make(map[string][]string))
-	buf, err := json.Marshal(cli.configFile.AuthConfigs)
+	err = jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut)
 	if err != nil {
 	if err != nil {
-		return err
-	}
-	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
-	headers.Set("Content-Type", "application/tar")
-
-	sopts := &streamOpts{
-		rawTerminal: true,
-		in:          body,
-		out:         cli.out,
-		headers:     headers,
-	}
-
-	serverResp, err := cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts)
-
-	// Windows: show error message about modified file permissions.
-	if runtime.GOOS == "windows" {
-		h, err := httputils.ParseServerHeader(serverResp.header.Get("Server"))
-		if err == nil {
-			if h.OS != "windows" {
-				fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
+		if jerr, ok := err.(*jsonmessage.JSONError); ok {
+			// If no error code is set, default to 1
+			if jerr.Code == 0 {
+				jerr.Code = 1
 			}
 			}
+			return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
 		}
 		}
 	}
 	}
 
 
-	if jerr, ok := err.(*jsonmessage.JSONError); ok {
-		// If no error code is set, default to 1
-		if jerr.Code == 0 {
-			jerr.Code = 1
-		}
-		return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
-	}
-
-	if err != nil {
-		return err
+	// Windows: show error message about modified file permissions.
+	if response.OSType == "windows" {
+		fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
 	}
 	}
 
 
 	// Since the build was successful, now we must tag any of the resolved
 	// Since the build was successful, now we must tag any of the resolved

+ 39 - 67
api/client/cli.go

@@ -1,19 +1,17 @@
 package client
 package client
 
 
 import (
 import (
-	"crypto/tls"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/http"
-	"net/url"
 	"os"
 	"os"
-	"strings"
+	"runtime"
 
 
+	"github.com/docker/docker/api/client/lib"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/sockets"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/tlsconfig"
 	"github.com/docker/docker/pkg/tlsconfig"
 )
 )
@@ -24,13 +22,6 @@ type DockerCli struct {
 	// initializing closure
 	// initializing closure
 	init func() error
 	init func() error
 
 
-	// proto holds the client protocol i.e. unix.
-	proto string
-	// addr holds the client address.
-	addr string
-	// basePath holds the path to prepend to the requests
-	basePath string
-
 	// configFile has the client configuration file
 	// configFile has the client configuration file
 	configFile *cliconfig.ConfigFile
 	configFile *cliconfig.ConfigFile
 	// in holds the input stream and closer (io.ReadCloser) for the client.
 	// in holds the input stream and closer (io.ReadCloser) for the client.
@@ -41,11 +32,6 @@ type DockerCli struct {
 	err io.Writer
 	err io.Writer
 	// keyFile holds the key file as a string.
 	// keyFile holds the key file as a string.
 	keyFile string
 	keyFile string
-	// tlsConfig holds the TLS configuration for the client, and will
-	// set the scheme to https in NewDockerCli if present.
-	tlsConfig *tls.Config
-	// scheme holds the scheme of the client i.e. https.
-	scheme string
 	// inFd holds the file descriptor of the client's STDIN (if valid).
 	// inFd holds the file descriptor of the client's STDIN (if valid).
 	inFd uintptr
 	inFd uintptr
 	// outFd holds file descriptor of the client's STDOUT (if valid).
 	// outFd holds file descriptor of the client's STDOUT (if valid).
@@ -54,8 +40,8 @@ type DockerCli struct {
 	isTerminalIn bool
 	isTerminalIn bool
 	// isTerminalOut indicates whether the client's STDOUT is a TTY
 	// isTerminalOut indicates whether the client's STDOUT is a TTY
 	isTerminalOut bool
 	isTerminalOut bool
-	// transport holds the client transport instance.
-	transport *http.Transport
+	// client is the http client that performs all API operations
+	client apiClient
 }
 }
 
 
 // Initialize calls the init function that will setup the configuration for the client
 // Initialize calls the init function that will setup the configuration for the client
@@ -98,50 +84,29 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
 	}
 	}
 
 
 	cli.init = func() error {
 	cli.init = func() error {
-
 		clientFlags.PostParse()
 		clientFlags.PostParse()
-
-		hosts := clientFlags.Common.Hosts
-
-		switch len(hosts) {
-		case 0:
-			hosts = []string{os.Getenv("DOCKER_HOST")}
-		case 1:
-			// only accept one host to talk to
-		default:
-			return errors.New("Please specify only one -H")
-		}
-
-		defaultHost := opts.DefaultTCPHost
-		if clientFlags.Common.TLSOptions != nil {
-			defaultHost = opts.DefaultTLSHost
+		configFile, e := cliconfig.Load(cliconfig.ConfigDir())
+		if e != nil {
+			fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
 		}
 		}
+		cli.configFile = configFile
 
 
-		var e error
-		if hosts[0], e = opts.ParseHost(defaultHost, hosts[0]); e != nil {
-			return e
+		host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
+		if err != nil {
+			return err
 		}
 		}
 
 
-		protoAddrParts := strings.SplitN(hosts[0], "://", 2)
-		cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1]
-
-		if cli.proto == "tcp" {
-			// error is checked in pkg/parsers already
-			parsed, _ := url.Parse("tcp://" + cli.addr)
-			cli.addr = parsed.Host
-			cli.basePath = parsed.Path
+		customHeaders := cli.configFile.HTTPHeaders
+		if customHeaders == nil {
+			customHeaders = map[string]string{}
 		}
 		}
+		customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
 
 
-		if clientFlags.Common.TLSOptions != nil {
-			cli.scheme = "https"
-			var e error
-			cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions)
-			if e != nil {
-				return e
-			}
-		} else {
-			cli.scheme = "http"
+		client, err := lib.NewClient(host, clientFlags.Common.TLSOptions, customHeaders)
+		if err != nil {
+			return err
 		}
 		}
+		cli.client = client
 
 
 		if cli.in != nil {
 		if cli.in != nil {
 			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
 			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
@@ -150,20 +115,27 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
 			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
 			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
 		}
 		}
 
 
-		// The transport is created here for reuse during the client session.
-		cli.transport = &http.Transport{
-			TLSClientConfig: cli.tlsConfig,
-		}
-		sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr)
-
-		configFile, e := cliconfig.Load(cliconfig.ConfigDir())
-		if e != nil {
-			fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
-		}
-		cli.configFile = configFile
-
 		return nil
 		return nil
 	}
 	}
 
 
 	return cli
 	return cli
 }
 }
+
+func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) {
+	switch len(hosts) {
+	case 0:
+		host = os.Getenv("DOCKER_HOST")
+	case 1:
+		host = hosts[0]
+	default:
+		return "", errors.New("Please specify only one -H")
+	}
+
+	defaultHost := opts.DefaultTCPHost
+	if tlsOptions != nil {
+		defaultHost = opts.DefaultTLSHost
+	}
+
+	host, err = opts.ParseHost(defaultHost, host)
+	return
+}

+ 71 - 0
api/client/client.go

@@ -3,3 +3,74 @@
 // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand.
 // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand.
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
 package client
 package client
+
+import (
+	"io"
+
+	"github.com/docker/docker/api/client/lib"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/pkg/parsers/filters"
+	"github.com/docker/docker/registry"
+	"github.com/docker/docker/runconfig"
+)
+
+// apiClient is an interface that clients that talk with a docker server must implement.
+type apiClient interface {
+	ContainerAttach(options types.ContainerAttachOptions) (types.HijackedResponse, error)
+	ContainerCommit(options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
+	ContainerCreate(config *runconfig.ContainerConfigWrapper, containerName string) (types.ContainerCreateResponse, error)
+	ContainerDiff(containerID string) ([]types.ContainerChange, error)
+	ContainerExecAttach(execID string, config runconfig.ExecConfig) (types.HijackedResponse, error)
+	ContainerExecCreate(config runconfig.ExecConfig) (types.ContainerExecCreateResponse, error)
+	ContainerExecInspect(execID string) (types.ContainerExecInspect, error)
+	ContainerExecResize(options types.ResizeOptions) error
+	ContainerExecStart(execID string, config types.ExecStartCheck) error
+	ContainerExport(containerID string) (io.ReadCloser, error)
+	ContainerInspect(containerID string) (types.ContainerJSON, error)
+	ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error)
+	ContainerKill(containerID, signal string) error
+	ContainerList(options types.ContainerListOptions) ([]types.Container, error)
+	ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error)
+	ContainerPause(containerID string) error
+	ContainerRemove(options types.ContainerRemoveOptions) error
+	ContainerRename(containerID, newContainerName string) error
+	ContainerResize(options types.ResizeOptions) error
+	ContainerRestart(containerID string, timeout int) error
+	ContainerStatPath(containerID, path string) (types.ContainerPathStat, error)
+	ContainerStats(containerID string, stream bool) (io.ReadCloser, error)
+	ContainerStart(containerID string) error
+	ContainerStop(containerID string, timeout int) error
+	ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
+	ContainerUnpause(containerID string) error
+	ContainerWait(containerID string) (int, error)
+	CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
+	CopyToContainer(options types.CopyToContainerOptions) error
+	Events(options types.EventsOptions) (io.ReadCloser, error)
+	ImageBuild(options types.ImageBuildOptions) (types.ImageBuildResponse, error)
+	ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error)
+	ImageHistory(imageID string) ([]types.ImageHistory, error)
+	ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
+	ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error)
+	ImageList(options types.ImageListOptions) ([]types.Image, error)
+	ImageLoad(input io.Reader) (io.ReadCloser, error)
+	ImagePull(options types.ImagePullOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error)
+	ImagePush(options types.ImagePushOptions, privilegeFunc lib.RequestPrivilegeFunc) (io.ReadCloser, error)
+	ImageRemove(options types.ImageRemoveOptions) ([]types.ImageDelete, error)
+	ImageSearch(options types.ImageSearchOptions, privilegeFunc lib.RequestPrivilegeFunc) ([]registry.SearchResult, error)
+	ImageSave(imageIDs []string) (io.ReadCloser, error)
+	ImageTag(options types.ImageTagOptions) error
+	Info() (types.Info, error)
+	NetworkConnect(networkID, containerID string) error
+	NetworkCreate(options types.NetworkCreate) (types.NetworkCreateResponse, error)
+	NetworkDisconnect(networkID, containerID string) error
+	NetworkInspect(networkID string) (types.NetworkResource, error)
+	NetworkList() ([]types.NetworkResource, error)
+	NetworkRemove(networkID string) error
+	RegistryLogin(auth cliconfig.AuthConfig) (types.AuthResponse, error)
+	SystemVersion() (types.VersionResponse, error)
+	VolumeCreate(options types.VolumeCreateRequest) (types.Volume, error)
+	VolumeInspect(volumeID string) (types.Volume, error)
+	VolumeList(filter filters.Args) (types.VolumesListResponse, error)
+	VolumeRemove(volumeID string) error
+}

+ 13 - 26
api/client/commit.go

@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"net/url"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -59,39 +58,27 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	v := url.Values{}
-	v.Set("container", name)
-	v.Set("repo", repositoryName)
-	v.Set("tag", tag)
-	v.Set("comment", *flComment)
-	v.Set("author", *flAuthor)
-	for _, change := range flChanges.GetAll() {
-		v.Add("changes", change)
-	}
-
-	if *flPause != true {
-		v.Set("pause", "0")
-	}
-
-	var (
-		config   *runconfig.Config
-		response types.ContainerCommitResponse
-	)
-
+	var config *runconfig.Config
 	if *flConfig != "" {
 	if *flConfig != "" {
 		config = &runconfig.Config{}
 		config = &runconfig.Config{}
 		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
 		if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	serverResp, err := cli.call("POST", "/commit?"+v.Encode(), config, nil)
-	if err != nil {
-		return err
-	}
 
 
-	defer serverResp.body.Close()
+	options := types.ContainerCommitOptions{
+		ContainerID:    name,
+		RepositoryName: repositoryName,
+		Tag:            tag,
+		Comment:        *flComment,
+		Author:         *flAuthor,
+		Changes:        flChanges.GetAll(),
+		Pause:          *flPause,
+		Config:         config,
+	}
 
 
-	if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
+	response, err := cli.client.ContainerCommit(options)
+	if err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 12 - 77
api/client/cp.go

@@ -1,12 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/http"
-	"net/url"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
@@ -129,38 +125,7 @@ func splitCpArg(arg string) (container, path string) {
 }
 }
 
 
 func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
 func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
-	var stat types.ContainerPathStat
-
-	query := make(url.Values, 1)
-	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
-
-	urlStr := fmt.Sprintf("/containers/%s/archive?%s", containerName, query.Encode())
-
-	response, err := cli.call("HEAD", urlStr, nil, nil)
-	if err != nil {
-		return stat, err
-	}
-	defer response.body.Close()
-
-	if response.statusCode != http.StatusOK {
-		return stat, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
-	}
-
-	return getContainerPathStatFromHeader(response.header)
-}
-
-func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
-	var stat types.ContainerPathStat
-
-	encodedStat := header.Get("X-Docker-Container-Path-Stat")
-	statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
-
-	err := json.NewDecoder(statDecoder).Decode(&stat)
-	if err != nil {
-		err = fmt.Errorf("unable to decode container path stat header: %s", err)
-	}
-
-	return stat, err
+	return cli.client.ContainerStatPath(containerName, path)
 }
 }
 
 
 func resolveLocalPath(localPath string) (absPath string, err error) {
 func resolveLocalPath(localPath string) (absPath string, err error) {
@@ -200,39 +165,19 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
 
 
 	}
 	}
 
 
-	query := make(url.Values, 1)
-	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
-
-	urlStr := fmt.Sprintf("/containers/%s/archive?%s", srcContainer, query.Encode())
-
-	response, err := cli.call("GET", urlStr, nil, nil)
+	content, stat, err := cli.client.CopyFromContainer(srcContainer, srcPath)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	defer response.body.Close()
-
-	if response.statusCode != http.StatusOK {
-		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
-	}
+	defer content.Close()
 
 
 	if dstPath == "-" {
 	if dstPath == "-" {
 		// Send the response to STDOUT.
 		// Send the response to STDOUT.
-		_, err = io.Copy(os.Stdout, response.body)
+		_, err = io.Copy(os.Stdout, content)
 
 
 		return err
 		return err
 	}
 	}
 
 
-	// In order to get the copy behavior right, we need to know information
-	// about both the source and the destination. The response headers include
-	// stat info about the source that we can use in deciding exactly how to
-	// copy it locally. Along with the stat info about the local destination,
-	// we have everything we need to handle the multiple possibilities there
-	// can be when copying a file/dir from one location to another file/dir.
-	stat, err := getContainerPathStatFromHeader(response.header)
-	if err != nil {
-		return fmt.Errorf("unable to get resource stat from response: %s", err)
-	}
-
 	// Prepare source copy info.
 	// Prepare source copy info.
 	srcInfo := archive.CopyInfo{
 	srcInfo := archive.CopyInfo{
 		Path:       srcPath,
 		Path:       srcPath,
@@ -241,10 +186,10 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
 		RebaseName: rebaseName,
 		RebaseName: rebaseName,
 	}
 	}
 
 
-	preArchive := response.body
+	preArchive := content
 	if len(srcInfo.RebaseName) != 0 {
 	if len(srcInfo.RebaseName) != 0 {
 		_, srcBase := archive.SplitPathDirEntry(srcInfo.Path)
 		_, srcBase := archive.SplitPathDirEntry(srcInfo.Path)
-		preArchive = archive.RebaseArchiveEntries(response.body, srcBase, srcInfo.RebaseName)
+		preArchive = archive.RebaseArchiveEntries(content, srcBase, srcInfo.RebaseName)
 	}
 	}
 	// See comments in the implementation of `archive.CopyTo` for exactly what
 	// See comments in the implementation of `archive.CopyTo` for exactly what
 	// goes into deciding how and whether the source archive needs to be
 	// goes into deciding how and whether the source archive needs to be
@@ -340,22 +285,12 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
 		content = preparedArchive
 		content = preparedArchive
 	}
 	}
 
 
-	query := make(url.Values, 2)
-	query.Set("path", filepath.ToSlash(resolvedDstPath)) // Normalize the paths used in the API.
-	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
-	query.Set("noOverwriteDirNonDir", "true")
-
-	urlStr := fmt.Sprintf("/containers/%s/archive?%s", dstContainer, query.Encode())
-
-	response, err := cli.stream("PUT", urlStr, &streamOpts{in: content})
-	if err != nil {
-		return err
-	}
-	defer response.body.Close()
-
-	if response.statusCode != http.StatusOK {
-		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
+	options := types.CopyToContainerOptions{
+		ContainerID:               dstContainer,
+		Path:                      resolvedDstPath,
+		Content:                   content,
+		AllowOverwriteDirWithFile: false,
 	}
 	}
 
 
-	return nil
+	return cli.client.CopyToContainer(options)
 }
 }

+ 31 - 45
api/client/create.go

@@ -1,17 +1,15 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 	"os"
 	"os"
-	"strings"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/client/lib"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
+	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 	tagpkg "github.com/docker/docker/tag"
 	tagpkg "github.com/docker/docker/tag"
@@ -22,8 +20,6 @@ func (cli *DockerCli) pullImage(image string) error {
 }
 }
 
 
 func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
 func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
-	v := url.Values{}
-
 	ref, err := reference.ParseNamed(image)
 	ref, err := reference.ParseNamed(image)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -40,9 +36,6 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
 		tag = tagpkg.DefaultTag
 		tag = tagpkg.DefaultTag
 	}
 	}
 
 
-	v.Set("fromImage", ref.Name())
-	v.Set("tag", tag)
-
 	// Resolve the Repository name from fqn to RepositoryInfo
 	// Resolve the Repository name from fqn to RepositoryInfo
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
 	if err != nil {
 	if err != nil {
@@ -50,24 +43,24 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
 	}
 	}
 
 
 	// Resolve the Auth config relevant for this server
 	// Resolve the Auth config relevant for this server
-	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
-	buf, err := json.Marshal(authConfig)
+	encodedAuth, err := cli.encodeRegistryAuth(repoInfo.Index)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	registryAuthHeader := []string{
-		base64.URLEncoding.EncodeToString(buf),
-	}
-	sopts := &streamOpts{
-		rawTerminal: true,
-		out:         out,
-		headers:     map[string][]string{"X-Registry-Auth": registryAuthHeader},
+	options := types.ImageCreateOptions{
+		Parent:       ref.Name(),
+		Tag:          tag,
+		RegistryAuth: encodedAuth,
 	}
 	}
-	if _, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil {
+
+	responseBody, err := cli.client.ImageCreate(options)
+	if err != nil {
 		return err
 		return err
 	}
 	}
-	return nil
+	defer responseBody.Close()
+
+	return jsonmessage.DisplayJSONMessagesStream(responseBody, out, cli.outFd, cli.isTerminalOut)
 }
 }
 
 
 type cidFile struct {
 type cidFile struct {
@@ -90,11 +83,6 @@ func newCIDFile(path string) (*cidFile, error) {
 }
 }
 
 
 func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
 func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
-	containerValues := url.Values{}
-	if name != "" {
-		containerValues.Set("name", name)
-	}
-
 	mergedConfig := runconfig.MergeConfigs(config, hostConfig)
 	mergedConfig := runconfig.MergeConfigs(config, hostConfig)
 
 
 	var containerIDFile *cidFile
 	var containerIDFile *cidFile
@@ -135,34 +123,32 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
 	}
 	}
 
 
 	//create the container
 	//create the container
-	serverResp, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
+	response, err := cli.client.ContainerCreate(mergedConfig, name)
 	//if image not found try to pull it
 	//if image not found try to pull it
-	if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
-		fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
+	if err != nil {
+		if lib.IsErrImageNotFound(err) {
+			fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
 
 
-		// we don't want to write to stdout anything apart from container.ID
-		if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
-			return nil, err
-		}
-		if trustedRef != nil && !isDigested {
-			if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil {
+			// we don't want to write to stdout anything apart from container.ID
+			if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
-		}
-		// Retry
-		if serverResp, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
+			if trustedRef != nil && !isDigested {
+				if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil {
+					return nil, err
+				}
+			}
+			// Retry
+			var retryErr error
+			response, retryErr = cli.client.ContainerCreate(mergedConfig, name)
+			if retryErr != nil {
+				return nil, retryErr
+			}
+		} else {
 			return nil, err
 			return nil, err
 		}
 		}
-	} else if err != nil {
-		return nil, err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	var response types.ContainerCreateResponse
-	if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
-		return nil, err
-	}
 	for _, warning := range response.Warnings {
 	for _, warning := range response.Warnings {
 		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
 		fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
 	}
 	}

+ 1 - 10
api/client/diff.go

@@ -1,10 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 
 
-	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -27,18 +25,11 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
 		return fmt.Errorf("Container name cannot be empty")
 		return fmt.Errorf("Container name cannot be empty")
 	}
 	}
 
 
-	serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, nil)
+	changes, err := cli.client.ContainerDiff(cmd.Arg(0))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	changes := []types.ContainerChange{}
-	if err := json.NewDecoder(serverResp.body).Decode(&changes); err != nil {
-		return err
-	}
-
 	for _, change := range changes {
 	for _, change := range changes {
 		var kind string
 		var kind string
 		switch change.Kind {
 		switch change.Kind {

+ 14 - 35
api/client/events.go

@@ -1,14 +1,12 @@
 package client
 package client
 
 
 import (
 import (
-	"net/url"
-	"time"
-
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
+	"github.com/docker/docker/pkg/jsonmessage"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/pkg/parsers/filters"
-	"github.com/docker/docker/pkg/timeutils"
 )
 )
 
 
 // CmdEvents prints a live stream of real time events from the server.
 // CmdEvents prints a live stream of real time events from the server.
@@ -24,10 +22,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	var (
-		v               = url.Values{}
-		eventFilterArgs = filters.NewArgs()
-	)
+	eventFilterArgs := filters.NewArgs()
 
 
 	// Consolidate all filter flags, and sanity check them early.
 	// Consolidate all filter flags, and sanity check them early.
 	// They'll get process in the daemon/server.
 	// They'll get process in the daemon/server.
@@ -38,34 +33,18 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	ref := time.Now()
-	if *since != "" {
-		ts, err := timeutils.GetTimestamp(*since, ref)
-		if err != nil {
-			return err
-		}
-		v.Set("since", ts)
-	}
-	if *until != "" {
-		ts, err := timeutils.GetTimestamp(*until, ref)
-		if err != nil {
-			return err
-		}
-		v.Set("until", ts)
-	}
-	if eventFilterArgs.Len() > 0 {
-		filterJSON, err := filters.ToParam(eventFilterArgs)
-		if err != nil {
-			return err
-		}
-		v.Set("filters", filterJSON)
-	}
-	sopts := &streamOpts{
-		rawTerminal: true,
-		out:         cli.out,
+
+	options := types.EventsOptions{
+		Since:   *since,
+		Until:   *until,
+		Filters: eventFilterArgs,
 	}
 	}
-	if _, err := cli.stream("GET", "/events?"+v.Encode(), sopts); err != nil {
+
+	responseBody, err := cli.client.Events(options)
+	if err != nil {
 		return err
 		return err
 	}
 	}
-	return nil
+	defer responseBody.Close()
+
+	return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut)
 }
 }

+ 14 - 41
api/client/exec.go

@@ -1,7 +1,6 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 
 
@@ -24,37 +23,29 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 		return Cli.StatusError{StatusCode: 1}
 		return Cli.StatusError{StatusCode: 1}
 	}
 	}
 
 
-	serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
+	response, err := cli.client.ContainerExecCreate(*execConfig)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	var response types.ContainerExecCreateResponse
-	if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
-		return err
-	}
-
 	execID := response.ID
 	execID := response.ID
-
 	if execID == "" {
 	if execID == "" {
 		fmt.Fprintf(cli.out, "exec ID empty")
 		fmt.Fprintf(cli.out, "exec ID empty")
 		return nil
 		return nil
 	}
 	}
 
 
 	//Temp struct for execStart so that we don't need to transfer all the execConfig
 	//Temp struct for execStart so that we don't need to transfer all the execConfig
-	execStartCheck := &types.ExecStartCheck{
-		Detach: execConfig.Detach,
-		Tty:    execConfig.Tty,
-	}
-
 	if !execConfig.Detach {
 	if !execConfig.Detach {
 		if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
 		if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
 			return err
 			return err
 		}
 		}
 	} else {
 	} else {
-		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execStartCheck, nil)); err != nil {
+		execStartCheck := types.ExecStartCheck{
+			Detach: execConfig.Detach,
+			Tty:    execConfig.Tty,
+		}
+
+		if err := cli.client.ContainerExecStart(execID, execStartCheck); err != nil {
 			return err
 			return err
 		}
 		}
 		// For now don't print this - wait for when we support exec wait()
 		// For now don't print this - wait for when we support exec wait()
@@ -66,18 +57,9 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 	var (
 	var (
 		out, stderr io.Writer
 		out, stderr io.Writer
 		in          io.ReadCloser
 		in          io.ReadCloser
-		hijacked    = make(chan io.Closer)
 		errCh       chan error
 		errCh       chan error
 	)
 	)
 
 
-	// Block the return until the chan gets closed
-	defer func() {
-		logrus.Debugf("End of CmdExec(), Waiting for hijack to finish.")
-		if _, ok := <-hijacked; ok {
-			fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)")
-		}
-	}()
-
 	if execConfig.AttachStdin {
 	if execConfig.AttachStdin {
 		in = cli.in
 		in = cli.in
 	}
 	}
@@ -91,24 +73,15 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 			stderr = cli.err
 			stderr = cli.err
 		}
 		}
 	}
 	}
-	errCh = promise.Go(func() error {
-		return cli.hijackWithContentType("POST", "/exec/"+execID+"/start", "application/json", execConfig.Tty, in, out, stderr, hijacked, execConfig)
-	})
 
 
-	// Acknowledge the hijack before starting
-	select {
-	case closer := <-hijacked:
-		// Make sure that hijack gets closed when returning. (result
-		// in closing hijack chan and freeing server's goroutines.
-		if closer != nil {
-			defer closer.Close()
-		}
-	case err := <-errCh:
-		if err != nil {
-			logrus.Debugf("Error hijack: %s", err)
-			return err
-		}
+	resp, err := cli.client.ContainerExecAttach(execID, *execConfig)
+	if err != nil {
+		return err
 	}
 	}
+	defer resp.Close()
+	errCh = promise.Go(func() error {
+		return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
+	})
 
 
 	if execConfig.Tty && cli.isTerminalIn {
 	if execConfig.Tty && cli.isTerminalIn {
 		if err := cli.monitorTtySize(execID, true); err != nil {
 		if err := cli.monitorTtySize(execID, true); err != nil {

+ 6 - 7
api/client/export.go

@@ -2,6 +2,7 @@ package client
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"io"
 	"os"
 	"os"
 
 
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
@@ -33,14 +34,12 @@ func (cli *DockerCli) CmdExport(args ...string) error {
 		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
 		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
 	}
 	}
 
 
-	image := cmd.Arg(0)
-	sopts := &streamOpts{
-		rawTerminal: true,
-		out:         output,
-	}
-	if _, err := cli.stream("GET", "/containers/"+image+"/export", sopts); err != nil {
+	responseBody, err := cli.client.ContainerExport(cmd.Arg(0))
+	if err != nil {
 		return err
 		return err
 	}
 	}
+	defer responseBody.Close()
 
 
-	return nil
+	_, err = io.Copy(output, responseBody)
+	return err
 }
 }

+ 18 - 198
api/client/hijack.go

@@ -1,197 +1,21 @@
 package client
 package client
 
 
 import (
 import (
-	"crypto/tls"
-	"errors"
-	"fmt"
 	"io"
 	"io"
-	"net"
-	"net/http"
-	"net/http/httputil"
 	"os"
 	"os"
-	"runtime"
-	"strings"
-	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/api"
-	"github.com/docker/docker/dockerversion"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
 )
 )
 
 
-type tlsClientCon struct {
-	*tls.Conn
-	rawConn net.Conn
-}
-
-func (c *tlsClientCon) CloseWrite() error {
-	// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
-	// on its underlying connection.
-	if cwc, ok := c.rawConn.(interface {
-		CloseWrite() error
-	}); ok {
-		return cwc.CloseWrite()
-	}
-	return nil
-}
-
-func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
-	return tlsDialWithDialer(new(net.Dialer), network, addr, config)
-}
-
-// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in
-// order to return our custom tlsClientCon struct which holds both the tls.Conn
-// object _and_ its underlying raw connection. The rationale for this is that
-// we need to be able to close the write end of the connection when attaching,
-// which tls.Conn does not provide.
-func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
-	// We want the Timeout and Deadline values from dialer to cover the
-	// whole process: TCP connection and TLS handshake. This means that we
-	// also need to start our own timers now.
-	timeout := dialer.Timeout
-
-	if !dialer.Deadline.IsZero() {
-		deadlineTimeout := dialer.Deadline.Sub(time.Now())
-		if timeout == 0 || deadlineTimeout < timeout {
-			timeout = deadlineTimeout
-		}
-	}
-
-	var errChannel chan error
-
-	if timeout != 0 {
-		errChannel = make(chan error, 2)
-		time.AfterFunc(timeout, func() {
-			errChannel <- errors.New("")
-		})
-	}
-
-	rawConn, err := dialer.Dial(network, addr)
-	if err != nil {
-		return nil, err
-	}
-	// When we set up a TCP connection for hijack, there could be long periods
-	// of inactivity (a long running command with no output) that in certain
-	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
-	// state. Setting TCP KeepAlive on the socket connection will prohibit
-	// ECONNTIMEOUT unless the socket connection truly is broken
-	if tcpConn, ok := rawConn.(*net.TCPConn); ok {
-		tcpConn.SetKeepAlive(true)
-		tcpConn.SetKeepAlivePeriod(30 * time.Second)
-	}
-
-	colonPos := strings.LastIndex(addr, ":")
-	if colonPos == -1 {
-		colonPos = len(addr)
-	}
-	hostname := addr[:colonPos]
-
-	// If no ServerName is set, infer the ServerName
-	// from the hostname we're connecting to.
-	if config.ServerName == "" {
-		// Make a copy to avoid polluting argument or default.
-		c := *config
-		c.ServerName = hostname
-		config = &c
-	}
-
-	conn := tls.Client(rawConn, config)
-
-	if timeout == 0 {
-		err = conn.Handshake()
-	} else {
-		go func() {
-			errChannel <- conn.Handshake()
-		}()
-
-		err = <-errChannel
-	}
-
-	if err != nil {
-		rawConn.Close()
-		return nil, err
-	}
-
-	// This is Docker difference with standard's crypto/tls package: returned a
-	// wrapper which holds both the TLS and raw connections.
-	return &tlsClientCon{conn, rawConn}, nil
-}
-
-func (cli *DockerCli) dial() (net.Conn, error) {
-	if cli.tlsConfig != nil && cli.proto != "unix" {
-		// Notice this isn't Go standard's tls.Dial function
-		return tlsDial(cli.proto, cli.addr, cli.tlsConfig)
-	}
-	return net.Dial(cli.proto, cli.addr)
-}
-
-func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}) error {
-	return cli.hijackWithContentType(method, path, "text/plain", setRawTerminal, in, stdout, stderr, started, data)
-}
-
-func (cli *DockerCli) hijackWithContentType(method, path, contentType string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}) error {
-	defer func() {
-		if started != nil {
-			close(started)
-		}
-	}()
-
-	params, err := cli.encodeData(data)
-	if err != nil {
-		return err
-	}
-	req, err := http.NewRequest(method, fmt.Sprintf("%s/v%s%s", cli.basePath, api.Version, path), params)
-	if err != nil {
-		return err
-	}
-
-	// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
-	// then the user can't change OUR headers
-	for k, v := range cli.configFile.HTTPHeaders {
-		req.Header.Set(k, v)
-	}
-
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.Version+" ("+runtime.GOOS+")")
-	req.Header.Set("Content-Type", contentType)
-	req.Header.Set("Connection", "Upgrade")
-	req.Header.Set("Upgrade", "tcp")
-	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 daemon' running on this host?")
-		}
-		return err
-	}
-
-	// When we set up a TCP connection for hijack, there could be long periods
-	// of inactivity (a long running command with no output) that in certain
-	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
-	// state. Setting TCP KeepAlive on the socket connection will prohibit
-	// ECONNTIMEOUT unless the socket connection truly is broken
-	if tcpConn, ok := dial.(*net.TCPConn); ok {
-		tcpConn.SetKeepAlive(true)
-		tcpConn.SetKeepAlivePeriod(30 * time.Second)
-	}
-
-	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 oldState *term.State
-
-	if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
+func (cli *DockerCli) holdHijackedConnection(setRawTerminal bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
+	var (
+		err      error
+		oldState *term.State
+	)
+	if inputStream != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
 		oldState, err = term.SetRawTerminal(cli.inFd)
 		oldState, err = term.SetRawTerminal(cli.inFd)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -200,22 +24,22 @@ func (cli *DockerCli) hijackWithContentType(method, path, contentType string, se
 	}
 	}
 
 
 	receiveStdout := make(chan error, 1)
 	receiveStdout := make(chan error, 1)
-	if stdout != nil || stderr != nil {
+	if outputStream != nil || errorStream != nil {
 		go func() {
 		go func() {
 			defer func() {
 			defer func() {
-				if in != nil {
+				if inputStream != nil {
 					if setRawTerminal && cli.isTerminalIn {
 					if setRawTerminal && cli.isTerminalIn {
 						term.RestoreTerminal(cli.inFd, oldState)
 						term.RestoreTerminal(cli.inFd, oldState)
 					}
 					}
-					in.Close()
+					inputStream.Close()
 				}
 				}
 			}()
 			}()
 
 
 			// When TTY is ON, use regular copy
 			// When TTY is ON, use regular copy
-			if setRawTerminal && stdout != nil {
-				_, err = io.Copy(stdout, br)
+			if setRawTerminal && outputStream != nil {
+				_, err = io.Copy(outputStream, resp.Reader)
 			} else {
 			} else {
-				_, err = stdcopy.StdCopy(stdout, stderr, br)
+				_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
 			}
 			}
 			logrus.Debugf("[hijack] End of stdout")
 			logrus.Debugf("[hijack] End of stdout")
 			receiveStdout <- err
 			receiveStdout <- err
@@ -224,17 +48,13 @@ func (cli *DockerCli) hijackWithContentType(method, path, contentType string, se
 
 
 	stdinDone := make(chan struct{})
 	stdinDone := make(chan struct{})
 	go func() {
 	go func() {
-		if in != nil {
-			io.Copy(rwc, in)
+		if inputStream != nil {
+			io.Copy(resp.Conn, inputStream)
 			logrus.Debugf("[hijack] End of stdin")
 			logrus.Debugf("[hijack] End of stdin")
 		}
 		}
 
 
-		if conn, ok := rwc.(interface {
-			CloseWrite() error
-		}); ok {
-			if err := conn.CloseWrite(); err != nil {
-				logrus.Debugf("Couldn't send EOF: %s", err)
-			}
+		if err := resp.CloseWrite(); err != nil {
+			logrus.Debugf("Couldn't send EOF: %s", err)
 		}
 		}
 		close(stdinDone)
 		close(stdinDone)
 	}()
 	}()
@@ -246,7 +66,7 @@ func (cli *DockerCli) hijackWithContentType(method, path, contentType string, se
 			return err
 			return err
 		}
 		}
 	case <-stdinDone:
 	case <-stdinDone:
-		if stdout != nil || stderr != nil {
+		if outputStream != nil || errorStream != nil {
 			if err := <-receiveStdout; err != nil {
 			if err := <-receiveStdout; err != nil {
 				logrus.Debugf("Error receiveStdout: %s", err)
 				logrus.Debugf("Error receiveStdout: %s", err)
 				return err
 				return err

+ 1 - 10
api/client/history.go

@@ -1,14 +1,12 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
 	"time"
 	"time"
 
 
-	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
@@ -28,18 +26,11 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	serverResp, err := cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil)
+	history, err := cli.client.ImageHistory(cmd.Arg(0))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	history := []types.ImageHistory{}
-	if err := json.NewDecoder(serverResp.body).Decode(&history); err != nil {
-		return err
-	}
-
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 
 
 	if *quiet {
 	if *quiet {

+ 8 - 24
api/client/images.go

@@ -1,9 +1,7 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
-	"net/url"
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
 	"time"
 	"time"
@@ -45,33 +43,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	matchName := cmd.Arg(0)
-	v := url.Values{}
-	if imageFilterArgs.Len() > 0 {
-		filterJSON, err := filters.ToParam(imageFilterArgs)
-		if err != nil {
-			return err
-		}
-		v.Set("filters", filterJSON)
-	}
-
+	var matchName string
 	if cmd.NArg() == 1 {
 	if cmd.NArg() == 1 {
-		// FIXME rename this parameter, to not be confused with the filters flag
-		v.Set("filter", matchName)
-	}
-	if *all {
-		v.Set("all", "1")
+		matchName = cmd.Arg(0)
 	}
 	}
 
 
-	serverResp, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil)
-	if err != nil {
-		return err
+	options := types.ImageListOptions{
+		MatchName: matchName,
+		All:       *all,
+		Filters:   imageFilterArgs,
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	images := []types.Image{}
-	if err := json.NewDecoder(serverResp.body).Decode(&images); err != nil {
+	images, err := cli.client.ImageList(options)
+	if err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 22 - 18
api/client/import.go

@@ -3,12 +3,13 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 	"os"
 	"os"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
+	"github.com/docker/docker/pkg/jsonmessage"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -29,20 +30,17 @@ func (cli *DockerCli) CmdImport(args ...string) error {
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
 	var (
 	var (
-		v          = url.Values{}
+		in         io.Reader
+		tag        string
 		src        = cmd.Arg(0)
 		src        = cmd.Arg(0)
+		srcName    = src
 		repository = cmd.Arg(1)
 		repository = cmd.Arg(1)
+		changes    = flChanges.GetAll()
 	)
 	)
 
 
-	v.Set("fromSrc", src)
-	v.Set("repo", repository)
-	v.Set("message", *message)
-	for _, change := range flChanges.GetAll() {
-		v.Add("changes", change)
-	}
 	if cmd.NArg() == 3 {
 	if cmd.NArg() == 3 {
 		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'file|URL|- [REPOSITORY [TAG]]' has been deprecated. Please use file|URL|- [REPOSITORY[:TAG]]\n")
 		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'file|URL|- [REPOSITORY [TAG]]' has been deprecated. Please use file|URL|- [REPOSITORY[:TAG]]\n")
-		v.Set("tag", cmd.Arg(2))
+		tag = cmd.Arg(2)
 	}
 	}
 
 
 	if repository != "" {
 	if repository != "" {
@@ -56,12 +54,10 @@ func (cli *DockerCli) CmdImport(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	var in io.Reader
-
 	if src == "-" {
 	if src == "-" {
 		in = cli.in
 		in = cli.in
 	} else if !urlutil.IsURL(src) {
 	} else if !urlutil.IsURL(src) {
-		v.Set("fromSrc", "-")
+		srcName = "-"
 		file, err := os.Open(src)
 		file, err := os.Open(src)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -71,12 +67,20 @@ func (cli *DockerCli) CmdImport(args ...string) error {
 
 
 	}
 	}
 
 
-	sopts := &streamOpts{
-		rawTerminal: true,
-		in:          in,
-		out:         cli.out,
+	options := types.ImageImportOptions{
+		Source:         in,
+		SourceName:     srcName,
+		RepositoryName: repository,
+		Message:        *message,
+		Tag:            tag,
+		Changes:        changes,
+	}
+
+	responseBody, err := cli.client.ImageImport(options)
+	if err != nil {
+		return err
 	}
 	}
+	defer responseBody.Close()
 
 
-	_, err := cli.stream("POST", "/images/create?"+v.Encode(), sopts)
-	return err
+	return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut)
 }
 }

+ 31 - 43
api/client/info.go

@@ -1,12 +1,9 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 
 
-	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
-	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/units"
@@ -21,18 +18,11 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	serverResp, err := cli.call("GET", "/info", nil, nil)
+	info, err := cli.client.Info()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	info := &types.Info{}
-	if err := json.NewDecoder(serverResp.body).Decode(info); err != nil {
-		return fmt.Errorf("Error reading remote info: %v", err)
-	}
-
 	fmt.Fprintf(cli.out, "Containers: %d\n", info.Containers)
 	fmt.Fprintf(cli.out, "Containers: %d\n", info.Containers)
 	fmt.Fprintf(cli.out, "Images: %d\n", info.Images)
 	fmt.Fprintf(cli.out, "Images: %d\n", info.Images)
 	ioutils.FprintfIfNotEmpty(cli.out, "Server Version: %s\n", info.ServerVersion)
 	ioutils.FprintfIfNotEmpty(cli.out, "Server Version: %s\n", info.ServerVersion)
@@ -90,38 +80,36 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 	}
 	}
 
 
 	// Only output these warnings if the server does not support these features
 	// Only output these warnings if the server does not support these features
-	if h, err := httputils.ParseServerHeader(serverResp.header.Get("Server")); err == nil {
-		if h.OS != "windows" {
-			if !info.MemoryLimit {
-				fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
-			}
-			if !info.SwapLimit {
-				fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
-			}
-			if !info.OomKillDisable {
-				fmt.Fprintf(cli.err, "WARNING: No oom kill disable support\n")
-			}
-			if !info.CPUCfsQuota {
-				fmt.Fprintf(cli.err, "WARNING: No cpu cfs quota support\n")
-			}
-			if !info.CPUCfsPeriod {
-				fmt.Fprintf(cli.err, "WARNING: No cpu cfs period support\n")
-			}
-			if !info.CPUShares {
-				fmt.Fprintf(cli.err, "WARNING: No cpu shares support\n")
-			}
-			if !info.CPUSet {
-				fmt.Fprintf(cli.err, "WARNING: No cpuset support\n")
-			}
-			if !info.IPv4Forwarding {
-				fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled\n")
-			}
-			if !info.BridgeNfIptables {
-				fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-iptables is disabled\n")
-			}
-			if !info.BridgeNfIP6tables {
-				fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled\n")
-			}
+	if info.OSType != "windows" {
+		if !info.MemoryLimit {
+			fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
+		}
+		if !info.SwapLimit {
+			fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
+		}
+		if !info.OomKillDisable {
+			fmt.Fprintf(cli.err, "WARNING: No oom kill disable support\n")
+		}
+		if !info.CPUCfsQuota {
+			fmt.Fprintf(cli.err, "WARNING: No cpu cfs quota support\n")
+		}
+		if !info.CPUCfsPeriod {
+			fmt.Fprintf(cli.err, "WARNING: No cpu cfs period support\n")
+		}
+		if !info.CPUShares {
+			fmt.Fprintf(cli.err, "WARNING: No cpu shares support\n")
+		}
+		if !info.CPUSet {
+			fmt.Fprintf(cli.err, "WARNING: No cpuset support\n")
+		}
+		if !info.IPv4Forwarding {
+			fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled\n")
+		}
+		if !info.BridgeNfIptables {
+			fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-iptables is disabled\n")
+		}
+		if !info.BridgeNfIP6tables {
+			fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled\n")
 		}
 		}
 	}
 	}
 
 

+ 75 - 147
api/client/inspect.go

@@ -1,16 +1,12 @@
 package client
 package client
 
 
 import (
 import (
-	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"io"
-	"net/http"
-	"net/url"
-	"strings"
 	"text/template"
 	"text/template"
 
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/client/inspect"
+	"github.com/docker/docker/api/client/lib"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
@@ -34,172 +30,104 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	var tmpl *template.Template
-	var err error
-	var obj []byte
-	var statusCode int
-
-	if *tmplStr != "" {
-		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
-			return Cli.StatusError{StatusCode: 64,
-				Status: "Template parsing error: " + err.Error()}
-		}
-	}
-
 	if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
 	if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
 		return fmt.Errorf("%q is not a valid value for --type", *inspectType)
 		return fmt.Errorf("%q is not a valid value for --type", *inspectType)
 	}
 	}
 
 
-	indented := new(bytes.Buffer)
-	indented.WriteString("[\n")
-	status := 0
-	isImage := false
+	var elementSearcher inspectSearcher
+	switch *inspectType {
+	case "container":
+		elementSearcher = cli.inspectContainers(*size)
+	case "image":
+		elementSearcher = cli.inspectImages(*size)
+	default:
+		elementSearcher = cli.inspectAll(*size)
+	}
+
+	return cli.inspectElements(*tmplStr, cmd.Args(), elementSearcher)
+}
 
 
-	v := url.Values{}
-	if *size {
-		v.Set("size", "1")
+func (cli *DockerCli) inspectContainers(getSize bool) inspectSearcher {
+	return func(ref string) (interface{}, []byte, error) {
+		return cli.client.ContainerInspectWithRaw(ref, getSize)
 	}
 	}
+}
 
 
-	for _, name := range cmd.Args() {
-		if *inspectType == "" || *inspectType == "container" {
-			obj, statusCode, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
-			if err != nil {
-				if err == errConnectionFailed {
-					return err
-				}
-				if *inspectType == "container" {
-					if statusCode == http.StatusNotFound {
-						fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
-					} else {
-						fmt.Fprintf(cli.err, "%s", err)
-					}
-					status = 1
-					continue
-				}
-			}
-		}
+func (cli *DockerCli) inspectImages(getSize bool) inspectSearcher {
+	return func(ref string) (interface{}, []byte, error) {
+		return cli.client.ImageInspectWithRaw(ref, getSize)
+	}
+}
 
 
-		if obj == nil && (*inspectType == "" || *inspectType == "image") {
-			obj, statusCode, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
-			isImage = true
-			if err != nil {
-				if err == errConnectionFailed {
-					return err
-				}
-				if statusCode == http.StatusNotFound {
-					if *inspectType == "" {
-						fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
-					} else {
-						fmt.Fprintf(cli.err, "Error: No such image: %s\n", name)
+func (cli *DockerCli) inspectAll(getSize bool) inspectSearcher {
+	return func(ref string) (interface{}, []byte, error) {
+		c, rawContainer, err := cli.client.ContainerInspectWithRaw(ref, getSize)
+		if err != nil {
+			// Search for image with that id if a container doesn't exist.
+			if lib.IsErrContainerNotFound(err) {
+				i, rawImage, err := cli.client.ImageInspectWithRaw(ref, getSize)
+				if err != nil {
+					if lib.IsErrImageNotFound(err) {
+						return nil, nil, fmt.Errorf("Error: No such image or container: %s", ref)
 					}
 					}
-				} else {
-					fmt.Fprintf(cli.err, "%s", err)
+					return nil, nil, err
 				}
 				}
-				status = 1
-				continue
+				return i, rawImage, err
 			}
 			}
+			return nil, nil, err
 		}
 		}
+		return c, rawContainer, err
+	}
+}
 
 
-		if tmpl == nil {
-			if err := json.Indent(indented, obj, "", "    "); err != nil {
-				fmt.Fprintf(cli.err, "%s\n", err)
-				status = 1
-				continue
-			}
-		} else {
-			rdr := bytes.NewReader(obj)
-			dec := json.NewDecoder(rdr)
-			buf := bytes.NewBufferString("")
-
-			if isImage {
-				inspPtr := types.ImageInspect{}
-				if err := dec.Decode(&inspPtr); err != nil {
-					fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
-					status = 1
-					break
-				}
-				if err := tmpl.Execute(buf, inspPtr); err != nil {
-					rdr.Seek(0, 0)
-					var ok bool
-
-					if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
-						fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
-						status = 1
-						break
-					}
-				}
-			} else {
-				inspPtr := types.ContainerJSON{}
-				if err := dec.Decode(&inspPtr); err != nil {
-					fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
-					status = 1
-					break
-				}
-				if err := tmpl.Execute(buf, inspPtr); err != nil {
-					rdr.Seek(0, 0)
-					var ok bool
-
-					if buf, ok = cli.decodeRawInspect(tmpl, dec); !ok {
-						fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
-						status = 1
-						break
-					}
-				}
-			}
+type inspectSearcher func(ref string) (interface{}, []byte, error)
 
 
-			cli.out.Write(buf.Bytes())
-			cli.out.Write([]byte{'\n'})
-		}
-		indented.WriteString(",")
+func (cli *DockerCli) inspectElements(tmplStr string, references []string, searchByReference inspectSearcher) error {
+	elementInspector, err := cli.newInspectorWithTemplate(tmplStr)
+	if err != nil {
+		return Cli.StatusError{StatusCode: 64, Status: err.Error()}
 	}
 	}
 
 
-	if indented.Len() > 1 {
-		// Remove trailing ','
-		indented.Truncate(indented.Len() - 1)
-	}
-	indented.WriteString("]\n")
-
-	if tmpl == nil {
-		// Note that we will always write "[]" when "-f" isn't specified,
-		// to make sure the output would always be array, see
-		// https://github.com/docker/docker/pull/9500#issuecomment-65846734
-		if _, err := io.Copy(cli.out, indented); err != nil {
-			return err
+	var inspectErr error
+	for _, ref := range references {
+		element, raw, err := searchByReference(ref)
+		if err != nil {
+			inspectErr = err
+			break
+		}
+
+		if err := elementInspector.Inspect(element, raw); err != nil {
+			inspectErr = err
+			break
 		}
 		}
 	}
 	}
 
 
-	if status != 0 {
+	if err := elementInspector.Flush(); err != nil {
+		cli.inspectErrorStatus(err)
+	}
+
+	if status := cli.inspectErrorStatus(inspectErr); status != 0 {
 		return Cli.StatusError{StatusCode: status}
 		return Cli.StatusError{StatusCode: status}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// decodeRawInspect executes the inspect template with a raw interface.
-// This allows docker cli to parse inspect structs injected with Swarm fields.
-// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
-// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
-// We assume that the template is invalid when there is a <no value>, if the template was valid
-// we'd get <nil> or "" values. In that case we fail with the original error raised executing the
-// template with the typed input.
-//
-// TODO: Go 1.5 allows to customize the error behavior, we can probably get rid of this as soon as
-// we build Docker with that version:
-// https://golang.org/pkg/text/template/#Template.Option
-func (cli *DockerCli) decodeRawInspect(tmpl *template.Template, dec *json.Decoder) (*bytes.Buffer, bool) {
-	var raw interface{}
-	buf := bytes.NewBufferString("")
-
-	if rawErr := dec.Decode(&raw); rawErr != nil {
-		fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", rawErr)
-		return buf, false
-	}
-
-	if rawErr := tmpl.Execute(buf, raw); rawErr != nil {
-		return buf, false
+func (cli *DockerCli) inspectErrorStatus(err error) (status int) {
+	if err != nil {
+		fmt.Fprintf(cli.err, "%s\n", err)
+		status = 1
 	}
 	}
+	return
+}
 
 
-	if strings.Contains(buf.String(), "<no value>") {
-		return buf, false
+func (cli *DockerCli) newInspectorWithTemplate(tmplStr string) (inspect.Inspector, error) {
+	elementInspector := inspect.NewIndentedInspector(cli.out)
+	if tmplStr != "" {
+		tmpl, err := template.New("").Funcs(funcMap).Parse(tmplStr)
+		if err != nil {
+			return nil, fmt.Errorf("Template parsing error: %s", err)
+		}
+		elementInspector = inspect.NewTemplateInspector(cli.out, tmpl)
 	}
 	}
-	return buf, true
+	return elementInspector, nil
 }
 }

+ 111 - 0
api/client/inspect/inspector.go

@@ -0,0 +1,111 @@
+package inspect
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"text/template"
+)
+
+// Inspector defines an interface to implement to process elements
+type Inspector interface {
+	Inspect(typedElement interface{}, rawElement []byte) error
+	Flush() error
+}
+
+// TemplateInspector uses a text template to inspect elements.
+type TemplateInspector struct {
+	outputStream io.Writer
+	buffer       *bytes.Buffer
+	tmpl         *template.Template
+}
+
+// NewTemplateInspector creates a new inspector with a template.
+func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
+	return &TemplateInspector{
+		outputStream: outputStream,
+		buffer:       new(bytes.Buffer),
+		tmpl:         tmpl,
+	}
+}
+
+// Inspect executes the inspect template.
+// It decodes the raw element into a map if the initial execution fails.
+// This allows docker cli to parse inspect structs injected with Swarm fields.
+func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte) error {
+	buffer := new(bytes.Buffer)
+	if err := i.tmpl.Execute(buffer, typedElement); err != nil {
+		if rawElement == nil {
+			return fmt.Errorf("Template parsing error: %v", err)
+		}
+		return i.tryRawInspectFallback(rawElement)
+	}
+	i.buffer.Write(buffer.Bytes())
+	i.buffer.WriteByte('\n')
+	return nil
+}
+
+func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
+	var raw interface{}
+	buffer := new(bytes.Buffer)
+	rdr := bytes.NewReader(rawElement)
+	dec := json.NewDecoder(rdr)
+
+	if rawErr := dec.Decode(&raw); rawErr != nil {
+		return fmt.Errorf("unable to read inspect data: %v", rawErr)
+	}
+
+	tmplMissingKey := i.tmpl.Option("missingkey=error")
+	if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
+		return fmt.Errorf("Template parsing error: %v", rawErr)
+	}
+
+	i.buffer.Write(buffer.Bytes())
+	i.buffer.WriteByte('\n')
+	return nil
+}
+
+// Flush write the result of inspecting all elements into the output stream.
+func (i *TemplateInspector) Flush() error {
+	_, err := io.Copy(i.outputStream, i.buffer)
+	return err
+}
+
+// IndentedInspector uses a buffer to stop the indented representation of an element.
+type IndentedInspector struct {
+	outputStream io.Writer
+	elements     []interface{}
+}
+
+// NewIndentedInspector generates a new IndentedInspector.
+func NewIndentedInspector(outputStream io.Writer) Inspector {
+	return &IndentedInspector{
+		outputStream: outputStream,
+	}
+}
+
+// Inspect writes the raw element with an indented json format.
+func (i *IndentedInspector) Inspect(typedElement interface{}, _ []byte) error {
+	i.elements = append(i.elements, typedElement)
+	return nil
+}
+
+// Flush write the result of inspecting all elements into the output stream.
+func (i *IndentedInspector) Flush() error {
+	if len(i.elements) == 0 {
+		_, err := io.WriteString(i.outputStream, "[]\n")
+		return err
+	}
+
+	buffer, err := json.MarshalIndent(i.elements, "", "    ")
+	if err != nil {
+		return err
+	}
+
+	if _, err := io.Copy(i.outputStream, bytes.NewReader(buffer)); err != nil {
+		return err
+	}
+	_, err = io.WriteString(i.outputStream, "\n")
+	return err
+}

+ 1 - 1
api/client/kill.go

@@ -19,7 +19,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
 
 
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil {
+		if err := cli.client.ContainerKill(name, *signal); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 102 - 0
api/client/lib/client.go

@@ -0,0 +1,102 @@
+package lib
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+
+	"github.com/docker/docker/api"
+	"github.com/docker/docker/pkg/sockets"
+	"github.com/docker/docker/pkg/tlsconfig"
+	"github.com/docker/docker/pkg/version"
+)
+
+// Client is the API client that performs all operations
+// against a docker server.
+type Client struct {
+	// proto holds the client protocol i.e. unix.
+	proto string
+	// addr holds the client address.
+	addr string
+	// basePath holds the path to prepend to the requests
+	basePath string
+	// scheme holds the scheme of the client i.e. https.
+	scheme string
+	// tlsConfig holds the tls configuration to use in hijacked requests.
+	tlsConfig *tls.Config
+	// httpClient holds the client transport instance. Exported to keep the old code running.
+	httpClient *http.Client
+	// version of the server to talk to.
+	version version.Version
+	// custom http headers configured by users
+	customHTTPHeaders map[string]string
+}
+
+// NewClient initializes a new API client
+// for the given host. It uses the tlsOptions
+// to decide whether to use a secure connection or not.
+// It also initializes the custom http headers to add to each request.
+func NewClient(host string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
+	return NewClientWithVersion(host, api.Version, tlsOptions, httpHeaders)
+}
+
+// NewClientWithVersion initializes a new API client
+// for the given host and API version. It uses the tlsOptions
+// to decide whether to use a secure connection or not.
+// It also initializes the custom http headers to add to each request.
+func NewClientWithVersion(host string, version version.Version, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
+	var (
+		basePath       string
+		tlsConfig      *tls.Config
+		scheme         = "http"
+		protoAddrParts = strings.SplitN(host, "://", 2)
+		proto, addr    = protoAddrParts[0], protoAddrParts[1]
+	)
+
+	if proto == "tcp" {
+		parsed, err := url.Parse("tcp://" + addr)
+		if err != nil {
+			return nil, err
+		}
+		addr = parsed.Host
+		basePath = parsed.Path
+	}
+
+	if tlsOptions != nil {
+		scheme = "https"
+		var err error
+		tlsConfig, err = tlsconfig.Client(*tlsOptions)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// The transport is created here for reuse during the client session.
+	transport := &http.Transport{
+		TLSClientConfig: tlsConfig,
+	}
+	sockets.ConfigureTCPTransport(transport, proto, addr)
+
+	return &Client{
+		proto:             proto,
+		addr:              addr,
+		basePath:          basePath,
+		scheme:            scheme,
+		tlsConfig:         tlsConfig,
+		httpClient:        &http.Client{Transport: transport},
+		version:           version,
+		customHTTPHeaders: httpHeaders,
+	}, nil
+}
+
+// getAPIPath returns the versioned request path to call the api.
+// It appends the query parameters to the path if they are not empty.
+func (cli *Client) getAPIPath(p string, query url.Values) string {
+	apiPath := fmt.Sprintf("%s/v%s%s", cli.basePath, cli.version, p)
+	if len(query) > 0 {
+		apiPath += "?" + query.Encode()
+	}
+	return apiPath
+}

+ 30 - 0
api/client/lib/container_attach.go

@@ -0,0 +1,30 @@
+package lib
+
+import (
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerAttach attaches a connection to a container in the server.
+// It returns a types.HijackedConnection with the hijacked connection
+// and the a reader to get output. It's up to the called to close
+// the hijacked connection by calling types.HijackedResponse.Close.
+func (cli *Client) ContainerAttach(options types.ContainerAttachOptions) (types.HijackedResponse, error) {
+	query := url.Values{}
+	if options.Stream {
+		query.Set("stream", "1")
+	}
+	if options.Stdin {
+		query.Set("stdin", "1")
+	}
+	if options.Stdout {
+		query.Set("stdout", "1")
+	}
+	if options.Stderr {
+		query.Set("stderr", "1")
+	}
+
+	headers := map[string][]string{"Content-Type": {"text/plain"}}
+	return cli.postHijacked("/containers/"+options.ContainerID+"/attach", query, nil, headers)
+}

+ 37 - 0
api/client/lib/container_commit.go

@@ -0,0 +1,37 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerCommit applies changes into a container and creates a new tagged image.
+func (cli *Client) ContainerCommit(options types.ContainerCommitOptions) (types.ContainerCommitResponse, error) {
+	query := url.Values{}
+	query.Set("container", options.ContainerID)
+	query.Set("repo", options.RepositoryName)
+	query.Set("tag", options.Tag)
+	query.Set("comment", options.Comment)
+	query.Set("author", options.Author)
+	for _, change := range options.Changes {
+		query.Add("changes", change)
+	}
+	if options.Pause != true {
+		query.Set("pause", "0")
+	}
+
+	var response types.ContainerCommitResponse
+	resp, err := cli.post("/commit", query, options.Config, nil)
+	if err != nil {
+		return response, err
+	}
+	defer ensureReaderClosed(resp)
+
+	if err := json.NewDecoder(resp.body).Decode(&response); err != nil {
+		return response, err
+	}
+
+	return response, nil
+}

+ 43 - 0
api/client/lib/container_create.go

@@ -0,0 +1,43 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+	"strings"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/runconfig"
+)
+
+// ContainerCreate creates a new container based in the given configuration.
+// It can be associated with a name, but it's not mandatory.
+func (cli *Client) ContainerCreate(config *runconfig.ContainerConfigWrapper, containerName string) (types.ContainerCreateResponse, error) {
+	var response types.ContainerCreateResponse
+	query := url.Values{}
+	if containerName != "" {
+		query.Set("name", containerName)
+	}
+
+	serverResp, err := cli.post("/containers/create", query, config, nil)
+	if err != nil {
+		if serverResp != nil && serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
+			return response, imageNotFoundError{config.Image}
+		}
+		return response, err
+	}
+
+	if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
+		return response, imageNotFoundError{config.Image}
+	}
+
+	if err != nil {
+		return response, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
+		return response, err
+	}
+
+	return response, nil
+}

+ 64 - 0
api/client/lib/container_inspect.go

@@ -0,0 +1,64 @@
+package lib
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerInspect returns the container information.
+func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) {
+	serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
+	if err != nil {
+		if serverResp.statusCode == http.StatusNotFound {
+			return types.ContainerJSON{}, containerNotFoundError{containerID}
+		}
+		return types.ContainerJSON{}, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	var response types.ContainerJSON
+	err = json.NewDecoder(serverResp.body).Decode(&response)
+	return response, err
+}
+
+// ContainerInspectWithRaw returns the container information and it's raw representation.
+func (cli *Client) ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error) {
+	query := url.Values{}
+	if getSize {
+		query.Set("size", "1")
+	}
+	serverResp, err := cli.get("/containers/"+containerID+"/json", query, nil)
+	if err != nil {
+		if serverResp.statusCode == http.StatusNotFound {
+			return types.ContainerJSON{}, nil, containerNotFoundError{containerID}
+		}
+		return types.ContainerJSON{}, nil, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	body, err := ioutil.ReadAll(serverResp.body)
+	if err != nil {
+		return types.ContainerJSON{}, nil, err
+	}
+
+	var response types.ContainerJSON
+	rdr := bytes.NewReader(body)
+	err = json.NewDecoder(rdr).Decode(&response)
+	return response, body, err
+}
+
+func (cli *Client) containerInspectWithResponse(containerID string, query url.Values) (types.ContainerJSON, *serverResponse, error) {
+	serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
+	if err != nil {
+		return types.ContainerJSON{}, serverResp, err
+	}
+
+	var response types.ContainerJSON
+	err = json.NewDecoder(serverResp.body).Decode(&response)
+	return response, serverResp, err
+}

+ 54 - 0
api/client/lib/container_list.go

@@ -0,0 +1,54 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+	"strconv"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/parsers/filters"
+)
+
+// ContainerList returns the list of containers in the docker host.
+func (cli *Client) ContainerList(options types.ContainerListOptions) ([]types.Container, error) {
+	query := url.Values{}
+
+	if options.All {
+		query.Set("all", "1")
+	}
+
+	if options.Limit != -1 {
+		query.Set("limit", strconv.Itoa(options.Limit))
+	}
+
+	if options.Since != "" {
+		query.Set("since", options.Since)
+	}
+
+	if options.Before != "" {
+		query.Set("before", options.Before)
+	}
+
+	if options.Size {
+		query.Set("size", "1")
+	}
+
+	if options.Filter.Len() > 0 {
+		filterJSON, err := filters.ToParam(options.Filter)
+		if err != nil {
+			return nil, err
+		}
+
+		query.Set("filters", filterJSON)
+	}
+
+	resp, err := cli.get("/containers/json", query, nil)
+	if err != nil {
+		return nil, err
+	}
+	defer ensureReaderClosed(resp)
+
+	var containers []types.Container
+	err = json.NewDecoder(resp.body).Decode(&containers)
+	return containers, err
+}

+ 26 - 0
api/client/lib/container_remove.go

@@ -0,0 +1,26 @@
+package lib
+
+import (
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerRemove kills and removes a container from the docker host.
+func (cli *Client) ContainerRemove(options types.ContainerRemoveOptions) error {
+	query := url.Values{}
+	if options.RemoveVolumes {
+		query.Set("v", "1")
+	}
+	if options.RemoveLinks {
+		query.Set("link", "1")
+	}
+
+	if options.Force {
+		query.Set("force", "1")
+	}
+
+	resp, err := cli.delete("/containers/"+options.ContainerID, query, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 12 - 0
api/client/lib/container_rename.go

@@ -0,0 +1,12 @@
+package lib
+
+import "net/url"
+
+// ContainerRename changes the name of a given container.
+func (cli *Client) ContainerRename(containerID, newContainerName string) error {
+	query := url.Values{}
+	query.Set("name", newContainerName)
+	resp, err := cli.post("/containers/"+containerID+"/rename", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 17 - 0
api/client/lib/container_restart.go

@@ -0,0 +1,17 @@
+package lib
+
+import (
+	"net/url"
+	"strconv"
+)
+
+// ContainerRestart stops and starts a container again.
+// It makes the daemon to wait for the container to be up again for
+// a specific amount of time, given the timeout.
+func (cli *Client) ContainerRestart(containerID string, timeout int) error {
+	query := url.Values{}
+	query.Set("t", strconv.Itoa(timeout))
+	resp, err := cli.post("/containers/"+containerID+"/restart", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 8 - 0
api/client/lib/container_start.go

@@ -0,0 +1,8 @@
+package lib
+
+// ContainerStart sends a request to the docker daemon to start a container.
+func (cli *Client) ContainerStart(containerID string) error {
+	resp, err := cli.post("/containers/"+containerID+"/start", nil, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 22 - 0
api/client/lib/container_stats.go

@@ -0,0 +1,22 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+)
+
+// ContainerStats returns near realtime stats for a given container.
+// It's up to the caller to close the io.ReadCloser returned.
+func (cli *Client) ContainerStats(containerID string, stream bool) (io.ReadCloser, error) {
+	query := url.Values{}
+	query.Set("stream", "0")
+	if stream {
+		query.Set("stream", "1")
+	}
+
+	resp, err := cli.get("/containers/"+containerID+"/stats", query, nil)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, err
+}

+ 16 - 0
api/client/lib/container_stop.go

@@ -0,0 +1,16 @@
+package lib
+
+import (
+	"net/url"
+	"strconv"
+)
+
+// ContainerStop stops a container without terminating the process.
+// The process is blocked until the container stops or the timeout expires.
+func (cli *Client) ContainerStop(containerID string, timeout int) error {
+	query := url.Values{}
+	query.Set("t", strconv.Itoa(timeout))
+	resp, err := cli.post("/containers/"+containerID+"/stop", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 27 - 0
api/client/lib/container_top.go

@@ -0,0 +1,27 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+	"strings"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerTop shows process information from within a container.
+func (cli *Client) ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error) {
+	var response types.ContainerProcessList
+	query := url.Values{}
+	if len(arguments) > 0 {
+		query.Set("ps_args", strings.Join(arguments, " "))
+	}
+
+	resp, err := cli.get("/containers/"+containerID+"/top", query, nil)
+	if err != nil {
+		return response, err
+	}
+	defer ensureReaderClosed(resp)
+
+	err = json.NewDecoder(resp.body).Decode(&response)
+	return response, err
+}

+ 8 - 0
api/client/lib/container_unpause.go

@@ -0,0 +1,8 @@
+package lib
+
+// ContainerUnpause resumes the process execution within a container
+func (cli *Client) ContainerUnpause(containerID string) error {
+	resp, err := cli.post("/containers/"+containerID+"/unpause", nil, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 95 - 0
api/client/lib/copy.go

@@ -0,0 +1,95 @@
+package lib
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"path/filepath"
+	"strings"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerStatPath returns Stat information about a path inside the container filesystem.
+func (cli *Client) ContainerStatPath(containerID, path string) (types.ContainerPathStat, error) {
+	query := url.Values{}
+	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
+
+	urlStr := fmt.Sprintf("/containers/%s/archive", containerID)
+	response, err := cli.head(urlStr, query, nil)
+	if err != nil {
+		return types.ContainerPathStat{}, err
+	}
+	defer ensureReaderClosed(response)
+	return getContainerPathStatFromHeader(response.header)
+}
+
+// CopyToContainer copies content into the container filesystem.
+func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
+	query := url.Values{}
+	query.Set("path", filepath.ToSlash(options.Path)) // Normalize the paths used in the API.
+	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
+	if !options.AllowOverwriteDirWithFile {
+		query.Set("noOverwriteDirNonDir", "true")
+	}
+
+	path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
+
+	response, err := cli.putRaw(path, query, options.Content, nil)
+	if err != nil {
+		return err
+	}
+	defer ensureReaderClosed(response)
+
+	if response.statusCode != http.StatusOK {
+		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
+	}
+
+	return nil
+}
+
+// CopyFromContainer get the content from the container and return it as a Reader
+// to manipulate it in the host. It's up to the caller to close the reader.
+func (cli *Client) CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
+	query := make(url.Values, 1)
+	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
+
+	apiPath := fmt.Sprintf("/containers/%s/archive", containerID)
+	response, err := cli.get(apiPath, query, nil)
+	if err != nil {
+		return nil, types.ContainerPathStat{}, err
+	}
+
+	if response.statusCode != http.StatusOK {
+		return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
+	}
+
+	// In order to get the copy behavior right, we need to know information
+	// about both the source and the destination. The response headers include
+	// stat info about the source that we can use in deciding exactly how to
+	// copy it locally. Along with the stat info about the local destination,
+	// we have everything we need to handle the multiple possibilities there
+	// can be when copying a file/dir from one location to another file/dir.
+	stat, err := getContainerPathStatFromHeader(response.header)
+	if err != nil {
+		return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
+	}
+	return response.body, stat, err
+}
+
+func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
+	var stat types.ContainerPathStat
+
+	encodedStat := header.Get("X-Docker-Container-Path-Stat")
+	statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
+
+	err := json.NewDecoder(statDecoder).Decode(&stat)
+	if err != nil {
+		err = fmt.Errorf("unable to decode container path stat header: %s", err)
+	}
+
+	return stat, err
+}

+ 25 - 0
api/client/lib/diff.go

@@ -0,0 +1,25 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerDiff shows differences in a container filesystem since it was started.
+func (cli *Client) ContainerDiff(containerID string) ([]types.ContainerChange, error) {
+	var changes []types.ContainerChange
+
+	serverResp, err := cli.get("/containers/"+containerID+"/changes", url.Values{}, nil)
+	if err != nil {
+		return changes, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	if err := json.NewDecoder(serverResp.body).Decode(&changes); err != nil {
+		return changes, err
+	}
+
+	return changes, nil
+}

+ 94 - 0
api/client/lib/errors.go

@@ -0,0 +1,94 @@
+package lib
+
+import (
+	"errors"
+	"fmt"
+)
+
+// ErrConnectionFailed is a error raised when the connection between the client and the server failed.
+var ErrConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
+
+// imageNotFoundError implements an error returned when an image is not in the docker host.
+type imageNotFoundError struct {
+	imageID string
+}
+
+// Error returns a string representation of an imageNotFoundError
+func (i imageNotFoundError) Error() string {
+	return fmt.Sprintf("Error: No such image: %s", i.imageID)
+}
+
+// IsErrImageNotFound returns true if the error is caused
+// when an image is not found in the docker host.
+func IsErrImageNotFound(err error) bool {
+	_, ok := err.(imageNotFoundError)
+	return ok
+}
+
+// containerNotFoundError implements an error returned when a container is not in the docker host.
+type containerNotFoundError struct {
+	containerID string
+}
+
+// Error returns a string representation of an containerNotFoundError
+func (e containerNotFoundError) Error() string {
+	return fmt.Sprintf("Error: No such container: %s", e.containerID)
+}
+
+// IsErrContainerNotFound returns true if the error is caused
+// when a container is not found in the docker host.
+func IsErrContainerNotFound(err error) bool {
+	_, ok := err.(containerNotFoundError)
+	return ok
+}
+
+// networkNotFoundError implements an error returned when a network is not in the docker host.
+type networkNotFoundError struct {
+	networkID string
+}
+
+// Error returns a string representation of an networkNotFoundError
+func (e networkNotFoundError) Error() string {
+	return fmt.Sprintf("Error: No such network: %s", e.networkID)
+}
+
+// IsErrNetworkNotFound returns true if the error is caused
+// when a network is not found in the docker host.
+func IsErrNetworkNotFound(err error) bool {
+	_, ok := err.(networkNotFoundError)
+	return ok
+}
+
+// volumeNotFoundError implements an error returned when a volume is not in the docker host.
+type volumeNotFoundError struct {
+	volumeID string
+}
+
+// Error returns a string representation of an networkNotFoundError
+func (e volumeNotFoundError) Error() string {
+	return fmt.Sprintf("Error: No such volume: %s", e.volumeID)
+}
+
+// IsErrVolumeNotFound returns true if the error is caused
+// when a volume is not found in the docker host.
+func IsErrVolumeNotFound(err error) bool {
+	_, ok := err.(networkNotFoundError)
+	return ok
+}
+
+// unauthorizedError represents an authorization error in a remote registry.
+type unauthorizedError struct {
+	cause error
+}
+
+// Error returns a string representation of an unauthorizedError
+func (u unauthorizedError) Error() string {
+	return u.cause.Error()
+}
+
+// IsErrUnauthorized returns true if the error is caused
+// when an the remote registry authentication fails
+func IsErrUnauthorized(err error) bool {
+	_, ok := err.(unauthorizedError)
+	return ok
+}

+ 46 - 0
api/client/lib/events.go

@@ -0,0 +1,46 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/parsers/filters"
+	"github.com/docker/docker/pkg/timeutils"
+)
+
+// Events returns a stream of events in the daemon in a ReadCloser.
+// It's up to the caller to close the stream.
+func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
+	query := url.Values{}
+	ref := time.Now()
+
+	if options.Since != "" {
+		ts, err := timeutils.GetTimestamp(options.Since, ref)
+		if err != nil {
+			return nil, err
+		}
+		query.Set("since", ts)
+	}
+	if options.Until != "" {
+		ts, err := timeutils.GetTimestamp(options.Until, ref)
+		if err != nil {
+			return nil, err
+		}
+		query.Set("until", ts)
+	}
+	if options.Filters.Len() > 0 {
+		filterJSON, err := filters.ToParam(options.Filters)
+		if err != nil {
+			return nil, err
+		}
+		query.Set("filters", filterJSON)
+	}
+
+	serverResponse, err := cli.get("/events", query, nil)
+	if err != nil {
+		return nil, err
+	}
+	return serverResponse.body, nil
+}

+ 49 - 0
api/client/lib/exec.go

@@ -0,0 +1,49 @@
+package lib
+
+import (
+	"encoding/json"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/runconfig"
+)
+
+// ContainerExecCreate creates a new exec configuration to run an exec process.
+func (cli *Client) ContainerExecCreate(config runconfig.ExecConfig) (types.ContainerExecCreateResponse, error) {
+	var response types.ContainerExecCreateResponse
+	resp, err := cli.post("/containers/"+config.Container+"/exec", nil, config, nil)
+	if err != nil {
+		return response, err
+	}
+	defer ensureReaderClosed(resp)
+	err = json.NewDecoder(resp.body).Decode(&response)
+	return response, err
+}
+
+// ContainerExecStart starts an exec process already create in the docker host.
+func (cli *Client) ContainerExecStart(execID string, config types.ExecStartCheck) error {
+	resp, err := cli.post("/exec/"+execID+"/start", nil, config, nil)
+	ensureReaderClosed(resp)
+	return err
+}
+
+// ContainerExecAttach attaches a connection to an exec process in the server.
+// It returns a types.HijackedConnection with the hijacked connection
+// and the a reader to get output. It's up to the called to close
+// the hijacked connection by calling types.HijackedResponse.Close.
+func (cli *Client) ContainerExecAttach(execID string, config runconfig.ExecConfig) (types.HijackedResponse, error) {
+	headers := map[string][]string{"Content-Type": {"application/json"}}
+	return cli.postHijacked("/exec/"+execID+"/start", nil, config, headers)
+}
+
+// ContainerExecInspect returns information about a specific exec process on the docker host.
+func (cli *Client) ContainerExecInspect(execID string) (types.ContainerExecInspect, error) {
+	var response types.ContainerExecInspect
+	resp, err := cli.get("/exec/"+execID+"/json", nil, nil)
+	if err != nil {
+		return response, err
+	}
+	defer ensureReaderClosed(resp)
+
+	err = json.NewDecoder(resp.body).Decode(&response)
+	return response, err
+}

+ 18 - 0
api/client/lib/export.go

@@ -0,0 +1,18 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+)
+
+// ContainerExport retrieves the raw contents of a container
+// and returns them as a io.ReadCloser. It's up to the caller
+// to close the stream.
+func (cli *Client) ContainerExport(containerID string) (io.ReadCloser, error) {
+	serverResp, err := cli.get("/containers/"+containerID+"/export", url.Values{}, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return serverResp.body, nil
+}

+ 164 - 0
api/client/lib/hijack.go

@@ -0,0 +1,164 @@
+package lib
+
+import (
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"net"
+	"net/http/httputil"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/docker/docker/api/types"
+)
+
+// tlsClientCon holds tls information and a dialed connection.
+type tlsClientCon struct {
+	*tls.Conn
+	rawConn net.Conn
+}
+
+func (c *tlsClientCon) CloseWrite() error {
+	// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
+	// on its underlying connection.
+	if conn, ok := c.rawConn.(types.CloseWriter); ok {
+		return conn.CloseWrite()
+	}
+	return nil
+}
+
+// postHijacked sends a POST request and hijacks the connection.
+func (cli *Client) postHijacked(path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
+	bodyEncoded, err := encodeData(body)
+	if err != nil {
+		return types.HijackedResponse{}, err
+	}
+
+	req, err := cli.newRequest("POST", path, query, bodyEncoded, headers)
+	if err != nil {
+		return types.HijackedResponse{}, err
+	}
+	req.Host = cli.addr
+
+	req.Header.Set("Connection", "Upgrade")
+	req.Header.Set("Upgrade", "tcp")
+
+	conn, err := dial(cli.proto, cli.addr, cli.tlsConfig)
+	if err != nil {
+		if strings.Contains(err.Error(), "connection refused") {
+			return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
+		}
+		return types.HijackedResponse{}, err
+	}
+
+	// When we set up a TCP connection for hijack, there could be long periods
+	// of inactivity (a long running command with no output) that in certain
+	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
+	// state. Setting TCP KeepAlive on the socket connection will prohibit
+	// ECONNTIMEOUT unless the socket connection truly is broken
+	if tcpConn, ok := conn.(*net.TCPConn); ok {
+		tcpConn.SetKeepAlive(true)
+		tcpConn.SetKeepAlivePeriod(30 * time.Second)
+	}
+
+	clientconn := httputil.NewClientConn(conn, nil)
+	defer clientconn.Close()
+
+	// Server hijacks the connection, error 'connection closed' expected
+	clientconn.Do(req)
+
+	rwc, br := clientconn.Hijack()
+
+	return types.HijackedResponse{rwc, br}, nil
+}
+
+func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
+	return tlsDialWithDialer(new(net.Dialer), network, addr, config)
+}
+
+// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in
+// order to return our custom tlsClientCon struct which holds both the tls.Conn
+// object _and_ its underlying raw connection. The rationale for this is that
+// we need to be able to close the write end of the connection when attaching,
+// which tls.Conn does not provide.
+func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
+	// We want the Timeout and Deadline values from dialer to cover the
+	// whole process: TCP connection and TLS handshake. This means that we
+	// also need to start our own timers now.
+	timeout := dialer.Timeout
+
+	if !dialer.Deadline.IsZero() {
+		deadlineTimeout := dialer.Deadline.Sub(time.Now())
+		if timeout == 0 || deadlineTimeout < timeout {
+			timeout = deadlineTimeout
+		}
+	}
+
+	var errChannel chan error
+
+	if timeout != 0 {
+		errChannel = make(chan error, 2)
+		time.AfterFunc(timeout, func() {
+			errChannel <- errors.New("")
+		})
+	}
+
+	rawConn, err := dialer.Dial(network, addr)
+	if err != nil {
+		return nil, err
+	}
+	// When we set up a TCP connection for hijack, there could be long periods
+	// of inactivity (a long running command with no output) that in certain
+	// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
+	// state. Setting TCP KeepAlive on the socket connection will prohibit
+	// ECONNTIMEOUT unless the socket connection truly is broken
+	if tcpConn, ok := rawConn.(*net.TCPConn); ok {
+		tcpConn.SetKeepAlive(true)
+		tcpConn.SetKeepAlivePeriod(30 * time.Second)
+	}
+
+	colonPos := strings.LastIndex(addr, ":")
+	if colonPos == -1 {
+		colonPos = len(addr)
+	}
+	hostname := addr[:colonPos]
+
+	// If no ServerName is set, infer the ServerName
+	// from the hostname we're connecting to.
+	if config.ServerName == "" {
+		// Make a copy to avoid polluting argument or default.
+		c := *config
+		c.ServerName = hostname
+		config = &c
+	}
+
+	conn := tls.Client(rawConn, config)
+
+	if timeout == 0 {
+		err = conn.Handshake()
+	} else {
+		go func() {
+			errChannel <- conn.Handshake()
+		}()
+
+		err = <-errChannel
+	}
+
+	if err != nil {
+		rawConn.Close()
+		return nil, err
+	}
+
+	// This is Docker difference with standard's crypto/tls package: returned a
+	// wrapper which holds both the TLS and raw connections.
+	return &tlsClientCon{conn, rawConn}, nil
+}
+
+func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
+	if tlsConfig != nil && proto != "unix" {
+		// Notice this isn't Go standard's tls.Dial function
+		return tlsDial(proto, addr, tlsConfig)
+	}
+	return net.Dial(proto, addr)
+}

+ 23 - 0
api/client/lib/history.go

@@ -0,0 +1,23 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageHistory returns the changes in an image in history format.
+func (cli *Client) ImageHistory(imageID string) ([]types.ImageHistory, error) {
+	var history []types.ImageHistory
+	serverResp, err := cli.get("/images/"+imageID+"/history", url.Values{}, nil)
+	if err != nil {
+		return history, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	if err := json.NewDecoder(serverResp.body).Decode(&history); err != nil {
+		return history, err
+	}
+	return history, nil
+}

+ 113 - 0
api/client/lib/image_build.go

@@ -0,0 +1,113 @@
+package lib
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"net/http"
+	"net/url"
+	"strconv"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/httputils"
+	"github.com/docker/docker/pkg/units"
+	"github.com/docker/docker/runconfig"
+)
+
+// ImageBuild sends request to the daemon to build images.
+// The Body in the response implement an io.ReadCloser and it's up to the caller to
+// close it.
+func (cli *Client) ImageBuild(options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
+	query, err := imageBuildOptionsToQuery(options)
+	if err != nil {
+		return types.ImageBuildResponse{}, err
+	}
+
+	headers := http.Header(make(map[string][]string))
+	buf, err := json.Marshal(options.AuthConfigs)
+	if err != nil {
+		return types.ImageBuildResponse{}, err
+	}
+	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
+	headers.Set("Content-Type", "application/tar")
+
+	serverResp, err := cli.postRaw("/build", query, options.Context, headers)
+	if err != nil {
+		return types.ImageBuildResponse{}, err
+	}
+
+	var osType string
+	if h, err := httputils.ParseServerHeader(serverResp.header.Get("Server")); err == nil {
+		osType = h.OS
+	}
+
+	return types.ImageBuildResponse{
+		Body:   serverResp.body,
+		OSType: osType,
+	}, nil
+}
+
+func imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) {
+	query := url.Values{
+		"t": options.Tags,
+	}
+	if options.SuppressOutput {
+		query.Set("q", "1")
+	}
+	if options.RemoteContext != "" {
+		query.Set("remote", options.RemoteContext)
+	}
+	if options.NoCache {
+		query.Set("nocache", "1")
+	}
+	if options.Remove {
+		query.Set("rm", "1")
+	} else {
+		query.Set("rm", "0")
+	}
+
+	if options.ForceRemove {
+		query.Set("forcerm", "1")
+	}
+
+	if options.PullParent {
+		query.Set("pull", "1")
+	}
+
+	if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(options.Isolation)) {
+		query.Set("isolation", options.Isolation)
+	}
+
+	query.Set("cpusetcpus", options.CPUSetCPUs)
+	query.Set("cpusetmems", options.CPUSetMems)
+	query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
+	query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
+	query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
+	query.Set("memory", strconv.FormatInt(options.Memory, 10))
+	query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
+	query.Set("cgroupparent", options.CgroupParent)
+
+	if options.ShmSize != "" {
+		parsedShmSize, err := units.RAMInBytes(options.ShmSize)
+		if err != nil {
+			return query, err
+		}
+		query.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
+	}
+
+	query.Set("dockerfile", options.Dockerfile)
+
+	ulimitsJSON, err := json.Marshal(options.Ulimits)
+	if err != nil {
+		return query, err
+	}
+	query.Set("ulimits", string(ulimitsJSON))
+
+	buildArgs := runconfig.ConvertKVStringsToMap(options.BuildArgs)
+	buildArgsJSON, err := json.Marshal(buildArgs)
+	if err != nil {
+		return query, err
+	}
+	query.Set("buildargs", string(buildArgsJSON))
+
+	return query, nil
+}

+ 26 - 0
api/client/lib/image_create.go

@@ -0,0 +1,26 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageCreate creates a new image based in the parent options.
+// It returns the JSON content in the response body.
+func (cli *Client) ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error) {
+	query := url.Values{}
+	query.Set("fromImage", options.Parent)
+	query.Set("tag", options.Tag)
+	resp, err := cli.tryImageCreate(query, options.RegistryAuth)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}
+
+func (cli *Client) tryImageCreate(query url.Values, registryAuth string) (*serverResponse, error) {
+	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
+	return cli.post("/images/create", query, nil, headers)
+}

+ 27 - 0
api/client/lib/image_import.go

@@ -0,0 +1,27 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageImport creates a new image based in the source options.
+// It returns the JSON content in the response body.
+func (cli *Client) ImageImport(options types.ImageImportOptions) (io.ReadCloser, error) {
+	query := url.Values{}
+	query.Set("fromSrc", options.SourceName)
+	query.Set("repo", options.RepositoryName)
+	query.Set("tag", options.Tag)
+	query.Set("message", options.Message)
+	for _, change := range options.Changes {
+		query.Add("changes", change)
+	}
+
+	resp, err := cli.postRaw("/images/create", query, options.Source, nil)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}

+ 37 - 0
api/client/lib/image_inspect.go

@@ -0,0 +1,37 @@
+package lib
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageInspectWithRaw returns the image information and it's raw representation.
+func (cli *Client) ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error) {
+	query := url.Values{}
+	if getSize {
+		query.Set("size", "1")
+	}
+	serverResp, err := cli.get("/images/"+imageID+"/json", query, nil)
+	if err != nil {
+		if serverResp.statusCode == http.StatusNotFound {
+			return types.ImageInspect{}, nil, imageNotFoundError{imageID}
+		}
+		return types.ImageInspect{}, nil, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	body, err := ioutil.ReadAll(serverResp.body)
+	if err != nil {
+		return types.ImageInspect{}, nil, err
+	}
+
+	var response types.ImageInspect
+	rdr := bytes.NewReader(body)
+	err = json.NewDecoder(rdr).Decode(&response)
+	return response, body, err
+}

+ 39 - 0
api/client/lib/image_list.go

@@ -0,0 +1,39 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/parsers/filters"
+)
+
+// ImageList returns a list of images in the docker host.
+func (cli *Client) ImageList(options types.ImageListOptions) ([]types.Image, error) {
+	var images []types.Image
+	query := url.Values{}
+
+	if options.Filters.Len() > 0 {
+		filterJSON, err := filters.ToParam(options.Filters)
+		if err != nil {
+			return images, err
+		}
+		query.Set("filters", filterJSON)
+	}
+	if options.MatchName != "" {
+		// FIXME rename this parameter, to not be confused with the filters flag
+		query.Set("filter", options.MatchName)
+	}
+	if options.All {
+		query.Set("all", "1")
+	}
+
+	serverResp, err := cli.get("/images/json", query, nil)
+	if err != nil {
+		return images, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	err = json.NewDecoder(serverResp.body).Decode(&images)
+	return images, err
+}

+ 17 - 0
api/client/lib/image_load.go

@@ -0,0 +1,17 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+)
+
+// ImageLoad loads an image in the docker host from the client host.
+// It's up to the caller to close the io.ReadCloser returned by
+// this function.
+func (cli *Client) ImageLoad(input io.Reader) (io.ReadCloser, error) {
+	resp, err := cli.postRaw("/images/load", url.Values{}, input, nil)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}

+ 34 - 0
api/client/lib/image_pull.go

@@ -0,0 +1,34 @@
+package lib
+
+import (
+	"io"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImagePull request the docker host to pull an image from a remote registry.
+// It executes the privileged function if the operation is unauthorized
+// and it tries one more time.
+// It's up to the caller to handle the io.ReadCloser and close it properly.
+func (cli *Client) ImagePull(options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
+	query := url.Values{}
+	query.Set("fromImage", options.ImageID)
+	if options.Tag != "" {
+		query.Set("tag", options.Tag)
+	}
+
+	resp, err := cli.tryImageCreate(query, options.RegistryAuth)
+	if resp.statusCode == http.StatusUnauthorized {
+		newAuthHeader, privilegeErr := privilegeFunc()
+		if privilegeErr != nil {
+			return nil, privilegeErr
+		}
+		resp, err = cli.tryImageCreate(query, newAuthHeader)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}

+ 36 - 0
api/client/lib/image_push.go

@@ -0,0 +1,36 @@
+package lib
+
+import (
+	"io"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImagePush request the docker host to push an image to a remote registry.
+// It executes the privileged function if the operation is unauthorized
+// and it tries one more time.
+// It's up to the caller to handle the io.ReadCloser and close it properly.
+func (cli *Client) ImagePush(options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
+	query := url.Values{}
+	query.Set("tag", options.Tag)
+
+	resp, err := cli.tryImagePush(options.ImageID, query, options.RegistryAuth)
+	if resp.statusCode == http.StatusUnauthorized {
+		newAuthHeader, privilegeErr := privilegeFunc()
+		if privilegeErr != nil {
+			return nil, privilegeErr
+		}
+		resp, err = cli.tryImagePush(options.ImageID, query, newAuthHeader)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}
+
+func (cli *Client) tryImagePush(imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
+	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
+	return cli.post("/images/"+imageID+"/push", query, nil, headers)
+}

+ 30 - 0
api/client/lib/image_remove.go

@@ -0,0 +1,30 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageRemove removes an image from the docker host.
+func (cli *Client) ImageRemove(options types.ImageRemoveOptions) ([]types.ImageDelete, error) {
+	query := url.Values{}
+
+	if options.Force {
+		query.Set("force", "1")
+	}
+	if !options.PruneChildren {
+		query.Set("noprune", "1")
+	}
+
+	resp, err := cli.delete("/images/"+options.ImageID, query, nil)
+	if err != nil {
+		return nil, err
+	}
+	defer ensureReaderClosed(resp)
+
+	var dels []types.ImageDelete
+	err = json.NewDecoder(resp.body).Decode(&dels)
+	return dels, err
+}

+ 20 - 0
api/client/lib/image_save.go

@@ -0,0 +1,20 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+)
+
+// ImageSave retrieves one or more images from the docker host as a io.ReadCloser.
+// It's up to the caller to store the images and close the stream.
+func (cli *Client) ImageSave(imageIDs []string) (io.ReadCloser, error) {
+	query := url.Values{
+		"names": imageIDs,
+	}
+
+	resp, err := cli.get("/images/get", query, nil)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}

+ 39 - 0
api/client/lib/image_search.go

@@ -0,0 +1,39 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/registry"
+)
+
+// ImageSearch makes the docker host to search by a term in a remote registry.
+// The list of results is not sorted in any fashion.
+func (cli *Client) ImageSearch(options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error) {
+	var results []registry.SearchResult
+	query := url.Values{}
+	query.Set("term", options.Term)
+
+	resp, err := cli.tryImageSearch(query, options.RegistryAuth)
+	if resp.statusCode == http.StatusUnauthorized {
+		newAuthHeader, privilegeErr := privilegeFunc()
+		if privilegeErr != nil {
+			return results, privilegeErr
+		}
+		resp, err = cli.tryImageSearch(query, newAuthHeader)
+	}
+	if err != nil {
+		return results, err
+	}
+	defer ensureReaderClosed(resp)
+
+	err = json.NewDecoder(resp.body).Decode(&results)
+	return results, err
+}
+
+func (cli *Client) tryImageSearch(query url.Values, registryAuth string) (*serverResponse, error) {
+	headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
+	return cli.get("/images/search", query, headers)
+}

+ 21 - 0
api/client/lib/image_tag.go

@@ -0,0 +1,21 @@
+package lib
+
+import (
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ImageTag tags an image in the docker host
+func (cli *Client) ImageTag(options types.ImageTagOptions) error {
+	query := url.Values{}
+	query.Set("repo", options.RepositoryName)
+	query.Set("tag", options.Tag)
+	if options.Force {
+		query.Set("force", "1")
+	}
+
+	resp, err := cli.post("/images/"+options.ImageID+"/tag", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 25 - 0
api/client/lib/info.go

@@ -0,0 +1,25 @@
+package lib
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+)
+
+// Info returns information about the docker server.
+func (cli *Client) Info() (types.Info, error) {
+	var info types.Info
+	serverResp, err := cli.get("/info", url.Values{}, nil)
+	if err != nil {
+		return info, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	if err := json.NewDecoder(serverResp.body).Decode(&info); err != nil {
+		return info, fmt.Errorf("Error reading remote info: %v", err)
+	}
+
+	return info, nil
+}

+ 13 - 0
api/client/lib/kill.go

@@ -0,0 +1,13 @@
+package lib
+
+import "net/url"
+
+// ContainerKill terminates the container process but does not remove the container from the docker host.
+func (cli *Client) ContainerKill(containerID, signal string) error {
+	query := url.Values{}
+	query.Set("signal", signal)
+
+	resp, err := cli.post("/containers/"+containerID+"/kill", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 28 - 0
api/client/lib/login.go

@@ -0,0 +1,28 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/cliconfig"
+)
+
+// RegistryLogin authenticates the docker server with a given docker registry.
+// It returns UnauthorizerError when the authentication fails.
+func (cli *Client) RegistryLogin(auth cliconfig.AuthConfig) (types.AuthResponse, error) {
+	resp, err := cli.post("/auth", url.Values{}, auth, nil)
+
+	if resp != nil && resp.statusCode == http.StatusUnauthorized {
+		return types.AuthResponse{}, unauthorizedError{err}
+	}
+	if err != nil {
+		return types.AuthResponse{}, err
+	}
+	defer ensureReaderClosed(resp)
+
+	var response types.AuthResponse
+	err = json.NewDecoder(resp.body).Decode(&response)
+	return response, err
+}

+ 46 - 0
api/client/lib/logs.go

@@ -0,0 +1,46 @@
+package lib
+
+import (
+	"io"
+	"net/url"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/timeutils"
+)
+
+// ContainerLogs returns the logs generated by a container in an io.ReadCloser.
+// It's up to the caller to close the stream.
+func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error) {
+	query := url.Values{}
+	if options.ShowStdout {
+		query.Set("stdout", "1")
+	}
+
+	if options.ShowStderr {
+		query.Set("stderr", "1")
+	}
+
+	if options.Since != "" {
+		ts, err := timeutils.GetTimestamp(options.Since, time.Now())
+		if err != nil {
+			return nil, err
+		}
+		query.Set("since", ts)
+	}
+
+	if options.Timestamps {
+		query.Set("timestamps", "1")
+	}
+
+	if options.Follow {
+		query.Set("follow", "1")
+	}
+	query.Set("tail", options.Tail)
+
+	resp, err := cli.get("/containers/"+options.ContainerID+"/logs", query, nil)
+	if err != nil {
+		return nil, err
+	}
+	return resp.body, nil
+}

+ 71 - 0
api/client/lib/network.go

@@ -0,0 +1,71 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/docker/docker/api/types"
+)
+
+// NetworkCreate creates a new network in the docker host.
+func (cli *Client) NetworkCreate(options types.NetworkCreate) (types.NetworkCreateResponse, error) {
+	var response types.NetworkCreateResponse
+	serverResp, err := cli.post("/networks/create", nil, options, nil)
+	if err != nil {
+		return response, err
+	}
+
+	json.NewDecoder(serverResp.body).Decode(&response)
+	ensureReaderClosed(serverResp)
+	return response, err
+}
+
+// NetworkRemove removes an existent network from the docker host.
+func (cli *Client) NetworkRemove(networkID string) error {
+	resp, err := cli.delete("/networks/"+networkID, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}
+
+// NetworkConnect connects a container to an existent network in the docker host.
+func (cli *Client) NetworkConnect(networkID, containerID string) error {
+	nc := types.NetworkConnect{containerID}
+	resp, err := cli.post("/networks/"+networkID+"/connect", nil, nc, nil)
+	ensureReaderClosed(resp)
+	return err
+}
+
+// NetworkDisconnect disconnects a container from an existent network in the docker host.
+func (cli *Client) NetworkDisconnect(networkID, containerID string) error {
+	nc := types.NetworkConnect{containerID}
+	resp, err := cli.post("/networks/"+networkID+"/disconnect", nil, nc, nil)
+	ensureReaderClosed(resp)
+	return err
+}
+
+// NetworkList returns the list of networks configured in the docker host.
+func (cli *Client) NetworkList() ([]types.NetworkResource, error) {
+	var networkResources []types.NetworkResource
+	resp, err := cli.get("/networks", nil, nil)
+	if err != nil {
+		return networkResources, err
+	}
+	defer ensureReaderClosed(resp)
+	err = json.NewDecoder(resp.body).Decode(&networkResources)
+	return networkResources, err
+}
+
+// NetworkInspect returns the information for a specific network configured in the docker host.
+func (cli *Client) NetworkInspect(networkID string) (types.NetworkResource, error) {
+	var networkResource types.NetworkResource
+	resp, err := cli.get("/networks/"+networkID, nil, nil)
+	if err != nil {
+		if resp.statusCode == http.StatusNotFound {
+			return networkResource, networkNotFoundError{networkID}
+		}
+		return networkResource, err
+	}
+	defer ensureReaderClosed(resp)
+	err = json.NewDecoder(resp.body).Decode(&networkResource)
+	return networkResource, err
+}

+ 8 - 0
api/client/lib/pause.go

@@ -0,0 +1,8 @@
+package lib
+
+// ContainerPause pauses the main process of a given container without terminating it.
+func (cli *Client) ContainerPause(containerID string) error {
+	resp, err := cli.post("/containers/"+containerID+"/pause", nil, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 9 - 0
api/client/lib/privileged.go

@@ -0,0 +1,9 @@
+package lib
+
+// RequestPrivilegeFunc is a function interface that
+// clients can supply to retry operations after
+// getting an authorization error.
+// This function returns the registry authentication
+// header value in base 64 format, or an error
+// if the privilege request fails.
+type RequestPrivilegeFunc func() (string, error)

+ 165 - 0
api/client/lib/request.go

@@ -0,0 +1,165 @@
+package lib
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strings"
+
+	"github.com/docker/docker/utils"
+)
+
+// serverResponse is a wrapper for http API responses.
+type serverResponse struct {
+	body       io.ReadCloser
+	header     http.Header
+	statusCode int
+}
+
+// head sends an http request to the docker API using the method HEAD.
+func (cli *Client) head(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendRequest("HEAD", path, query, nil, headers)
+}
+
+// get sends an http request to the docker API using the method GET.
+func (cli *Client) get(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendRequest("GET", path, query, nil, headers)
+}
+
+// post sends an http request to the docker API using the method POST.
+func (cli *Client) post(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendRequest("POST", path, query, body, headers)
+}
+
+// postRaw sends the raw input to the docker API using the method POST.
+func (cli *Client) postRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendClientRequest("POST", path, query, body, headers)
+}
+
+// put sends an http request to the docker API using the method PUT.
+func (cli *Client) put(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendRequest("PUT", path, query, body, headers)
+}
+
+// putRaw sends the raw input to the docker API using the method PUT.
+func (cli *Client) putRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendClientRequest("PUT", path, query, body, headers)
+}
+
+// delete sends an http request to the docker API using the method DELETE.
+func (cli *Client) delete(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
+	return cli.sendRequest("DELETE", path, query, nil, headers)
+}
+
+func (cli *Client) sendRequest(method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
+	params, err := encodeData(body)
+	if err != nil {
+		return nil, err
+	}
+
+	if body != nil {
+		if headers == nil {
+			headers = make(map[string][]string)
+		}
+		headers["Content-Type"] = []string{"application/json"}
+	}
+
+	return cli.sendClientRequest(method, path, query, params, headers)
+}
+
+func (cli *Client) sendClientRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
+	serverResp := &serverResponse{
+		body:       nil,
+		statusCode: -1,
+	}
+
+	expectedPayload := (method == "POST" || method == "PUT")
+	if expectedPayload && body == nil {
+		body = bytes.NewReader([]byte{})
+	}
+
+	req, err := cli.newRequest(method, path, query, body, headers)
+	req.URL.Host = cli.addr
+	req.URL.Scheme = cli.scheme
+
+	if expectedPayload && req.Header.Get("Content-Type") == "" {
+		req.Header.Set("Content-Type", "text/plain")
+	}
+
+	resp, err := cli.httpClient.Do(req)
+	if resp != nil {
+		serverResp.statusCode = resp.StatusCode
+	}
+
+	if err != nil {
+		if utils.IsTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
+			return serverResp, ErrConnectionFailed
+		}
+
+		if cli.scheme == "http" && strings.Contains(err.Error(), "malformed HTTP response") {
+			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
+		}
+		if cli.scheme == "https" && strings.Contains(err.Error(), "remote error: bad certificate") {
+			return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
+		}
+
+		return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
+	}
+
+	if serverResp.statusCode < 200 || serverResp.statusCode >= 400 {
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return serverResp, err
+		}
+		if len(body) == 0 {
+			return serverResp, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), req.URL)
+		}
+		return serverResp, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
+	}
+
+	serverResp.body = resp.Body
+	serverResp.header = resp.Header
+	return serverResp, nil
+}
+
+func (cli *Client) newRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*http.Request, error) {
+	apiPath := cli.getAPIPath(path, query)
+	req, err := http.NewRequest(method, apiPath, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
+	// then the user can't change OUR headers
+	for k, v := range cli.customHTTPHeaders {
+		req.Header.Set(k, v)
+	}
+
+	if headers != nil {
+		for k, v := range headers {
+			req.Header[k] = v
+		}
+	}
+
+	return req, nil
+}
+
+func encodeData(data interface{}) (*bytes.Buffer, error) {
+	params := bytes.NewBuffer(nil)
+	if data != nil {
+		if err := json.NewEncoder(params).Encode(data); err != nil {
+			return nil, err
+		}
+	}
+	return params, nil
+}
+
+func ensureReaderClosed(response *serverResponse) {
+	if response != nil && response.body != nil {
+		response.body.Close()
+	}
+}

+ 28 - 0
api/client/lib/resize.go

@@ -0,0 +1,28 @@
+package lib
+
+import (
+	"net/url"
+	"strconv"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerResize changes the size of the tty for a container.
+func (cli *Client) ContainerResize(options types.ResizeOptions) error {
+	return cli.resize("/containers/"+options.ID, options.Height, options.Width)
+}
+
+// ContainerExecResize changes the size of the tty for an exec process running inside a container.
+func (cli *Client) ContainerExecResize(options types.ResizeOptions) error {
+	return cli.resize("/exec/"+options.ID, options.Height, options.Width)
+}
+
+func (cli *Client) resize(basePath string, height, width int) error {
+	query := url.Values{}
+	query.Set("h", strconv.Itoa(height))
+	query.Set("w", strconv.Itoa(width))
+
+	resp, err := cli.post(basePath+"/resize", query, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 38 - 0
api/client/lib/version.go

@@ -0,0 +1,38 @@
+package lib
+
+import (
+	"encoding/json"
+	"runtime"
+
+	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/dockerversion"
+	"github.com/docker/docker/utils"
+)
+
+// SystemVersion returns information of the docker client and server host.
+func (cli *Client) SystemVersion() (types.VersionResponse, error) {
+	client := &types.Version{
+		Version:      dockerversion.Version,
+		APIVersion:   api.Version,
+		GoVersion:    runtime.Version(),
+		GitCommit:    dockerversion.GitCommit,
+		BuildTime:    dockerversion.BuildTime,
+		Os:           runtime.GOOS,
+		Arch:         runtime.GOARCH,
+		Experimental: utils.ExperimentalBuild(),
+	}
+
+	resp, err := cli.get("/version", nil, nil)
+	if err != nil {
+		return types.VersionResponse{Client: client}, err
+	}
+	defer ensureReaderClosed(resp)
+
+	var server types.Version
+	err = json.NewDecoder(resp.body).Decode(&server)
+	if err != nil {
+		return types.VersionResponse{Client: client}, err
+	}
+	return types.VersionResponse{Client: client, Server: &server}, nil
+}

+ 66 - 0
api/client/lib/volume.go

@@ -0,0 +1,66 @@
+package lib
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/url"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/parsers/filters"
+)
+
+// VolumeList returns the volumes configured in the docker host.
+func (cli *Client) VolumeList(filter filters.Args) (types.VolumesListResponse, error) {
+	var volumes types.VolumesListResponse
+	query := url.Values{}
+
+	if filter.Len() > 0 {
+		filterJSON, err := filters.ToParam(filter)
+		if err != nil {
+			return volumes, err
+		}
+		query.Set("filters", filterJSON)
+	}
+	resp, err := cli.get("/volumes", query, nil)
+	if err != nil {
+		return volumes, err
+	}
+	defer ensureReaderClosed(resp)
+
+	err = json.NewDecoder(resp.body).Decode(&volumes)
+	return volumes, err
+}
+
+// VolumeInspect returns the information about a specific volume in the docker host.
+func (cli *Client) VolumeInspect(volumeID string) (types.Volume, error) {
+	var volume types.Volume
+	resp, err := cli.get("/volumes/"+volumeID, nil, nil)
+	if err != nil {
+		if resp.statusCode == http.StatusNotFound {
+			return volume, volumeNotFoundError{volumeID}
+		}
+		return volume, err
+	}
+	defer ensureReaderClosed(resp)
+	err = json.NewDecoder(resp.body).Decode(&volume)
+	return volume, err
+}
+
+// VolumeCreate creates a volume in the docker host.
+func (cli *Client) VolumeCreate(options types.VolumeCreateRequest) (types.Volume, error) {
+	var volume types.Volume
+	resp, err := cli.post("/volumes/create", nil, options, nil)
+	if err != nil {
+		return volume, err
+	}
+	defer ensureReaderClosed(resp)
+	err = json.NewDecoder(resp.body).Decode(&volume)
+	return volume, err
+}
+
+// VolumeRemove removes a volume from the docker host.
+func (cli *Client) VolumeRemove(volumeID string) error {
+	resp, err := cli.delete("/volumes/"+volumeID, nil, nil)
+	ensureReaderClosed(resp)
+	return err
+}

+ 24 - 0
api/client/lib/wait.go

@@ -0,0 +1,24 @@
+package lib
+
+import (
+	"encoding/json"
+
+	"github.com/docker/docker/api/types"
+)
+
+// ContainerWait pauses execution util a container is exits.
+// It returns the API status code as response of its readiness.
+func (cli *Client) ContainerWait(containerID string) (int, error) {
+	resp, err := cli.post("/containers/"+containerID+"/wait", nil, nil, nil)
+	if err != nil {
+		return -1, err
+	}
+	defer ensureReaderClosed(resp)
+
+	var res types.ContainerWaitResponse
+	if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
+		return -1, err
+	}
+
+	return res.StatusCode, nil
+}

+ 11 - 13
api/client/load.go

@@ -17,26 +17,24 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
 	cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true)
 	cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true)
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
-
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	var (
-		input io.Reader = cli.in
-		err   error
-	)
+	var input io.Reader = cli.in
 	if *infile != "" {
 	if *infile != "" {
-		input, err = os.Open(*infile)
+		file, err := os.Open(*infile)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
+		defer file.Close()
+		input = file
 	}
 	}
-	sopts := &streamOpts{
-		rawTerminal: true,
-		in:          input,
-		out:         cli.out,
-	}
-	if _, err := cli.stream("POST", "/images/load", sopts); err != nil {
+
+	responseBody, err := cli.client.ImageLoad(input)
+	if err != nil {
 		return err
 		return err
 	}
 	}
-	return nil
+	defer responseBody.Close()
+
+	_, err = io.Copy(cli.out, responseBody)
+	return err
 }
 }

+ 9 - 19
api/client/login.go

@@ -2,14 +2,13 @@ package client
 
 
 import (
 import (
 	"bufio"
 	"bufio"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
 
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/client/lib"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -120,24 +119,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	authconfig.ServerAddress = serverAddress
 	authconfig.ServerAddress = serverAddress
 	cli.configFile.AuthConfigs[serverAddress] = authconfig
 	cli.configFile.AuthConfigs[serverAddress] = authconfig
 
 
-	serverResp, err := cli.call("POST", "/auth", cli.configFile.AuthConfigs[serverAddress], nil)
-	if serverResp.statusCode == 401 {
-		delete(cli.configFile.AuthConfigs, serverAddress)
-		if err2 := cli.configFile.Save(); err2 != nil {
-			fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
-		}
-		return err
-	}
+	auth := cli.configFile.AuthConfigs[serverAddress]
+	response, err := cli.client.RegistryLogin(auth)
 	if err != nil {
 	if err != nil {
-		return err
-	}
-
-	defer serverResp.body.Close()
-
-	var response types.AuthResponse
-	if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
-		// Upon error, remove entry
-		delete(cli.configFile.AuthConfigs, serverAddress)
+		if lib.IsErrUnauthorized(err) {
+			delete(cli.configFile.AuthConfigs, serverAddress)
+			if err2 := cli.configFile.Save(); err2 != nil {
+				fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
+			}
+		}
 		return err
 		return err
 	}
 	}
 
 

+ 19 - 34
api/client/logs.go

@@ -1,15 +1,13 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
-	"net/url"
-	"time"
+	"io"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
-	"github.com/docker/docker/pkg/timeutils"
+	"github.com/docker/docker/pkg/stdcopy"
 )
 )
 
 
 var validDrivers = map[string]bool{
 var validDrivers = map[string]bool{
@@ -32,47 +30,34 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 
 
 	name := cmd.Arg(0)
 	name := cmd.Arg(0)
 
 
-	serverResp, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
+	c, err := cli.client.ContainerInspect(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	var c types.ContainerJSON
-	if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-		return err
-	}
-
 	if !validDrivers[c.HostConfig.LogConfig.Type] {
 	if !validDrivers[c.HostConfig.LogConfig.Type] {
 		return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
 		return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
 	}
 	}
 
 
-	v := url.Values{}
-	v.Set("stdout", "1")
-	v.Set("stderr", "1")
-
-	if *since != "" {
-		ts, err := timeutils.GetTimestamp(*since, time.Now())
-		if err != nil {
-			return err
-		}
-		v.Set("since", ts)
-	}
-
-	if *times {
-		v.Set("timestamps", "1")
+	options := types.ContainerLogsOptions{
+		ContainerID: name,
+		ShowStdout:  true,
+		ShowStderr:  true,
+		Since:       *since,
+		Timestamps:  *times,
+		Follow:      *follow,
+		Tail:        *tail,
 	}
 	}
-
-	if *follow {
-		v.Set("follow", "1")
+	responseBody, err := cli.client.ContainerLogs(options)
+	if err != nil {
+		return err
 	}
 	}
-	v.Set("tail", *tail)
+	defer responseBody.Close()
 
 
-	sopts := &streamOpts{
-		rawTerminal: c.Config.Tty,
-		out:         cli.out,
-		err:         cli.err,
+	if c.Config.Tty {
+		_, err = io.Copy(cli.out, responseBody)
+	} else {
+		_, err = stdcopy.StdCopy(cli.out, cli.err, responseBody)
 	}
 	}
-
-	_, err = cli.stream("GET", "/containers/"+name+"/logs?"+v.Encode(), sopts)
 	return err
 	return err
 }
 }

+ 14 - 99
api/client/network.go

@@ -1,15 +1,10 @@
 package client
 package client
 
 
 import (
 import (
-	"bytes"
-	"encoding/json"
 	"fmt"
 	"fmt"
-	"io"
 	"net"
 	"net"
-	"net/http"
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
-	"text/template"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
@@ -76,12 +71,8 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
 		Options:        flOpts.GetAll(),
 		Options:        flOpts.GetAll(),
 		CheckDuplicate: true,
 		CheckDuplicate: true,
 	}
 	}
-	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
-	if err != nil {
-		return err
-	}
-	var resp types.NetworkCreateResponse
-	err = json.Unmarshal(obj, &resp)
+
+	resp, err := cli.client.NetworkCreate(nc)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -95,15 +86,13 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
 func (cli *DockerCli) CmdNetworkRm(args ...string) error {
 func (cli *DockerCli) CmdNetworkRm(args ...string) error {
 	cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
 	cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
-	err := cmd.ParseFlags(args, true)
-	if err != nil {
+	if err := cmd.ParseFlags(args, true); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	status := 0
 	status := 0
 	for _, net := range cmd.Args() {
 	for _, net := range cmd.Args() {
-		_, _, err = readBody(cli.call("DELETE", "/networks/"+net, nil, nil))
-		if err != nil {
+		if err := cli.client.NetworkRemove(net); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			status = 1
 			status = 1
 			continue
 			continue
@@ -121,14 +110,11 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
 func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
 func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
 	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
 	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
 	cmd.Require(flag.Exact, 2)
 	cmd.Require(flag.Exact, 2)
-	err := cmd.ParseFlags(args, true)
-	if err != nil {
+	if err := cmd.ParseFlags(args, true); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	nc := types.NetworkConnect{Container: cmd.Arg(1)}
-	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
-	return err
+	return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1))
 }
 }
 
 
 // CmdNetworkDisconnect disconnects a container from a network
 // CmdNetworkDisconnect disconnects a container from a network
@@ -137,14 +123,11 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
 func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
 func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
 	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
 	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
 	cmd.Require(flag.Exact, 2)
 	cmd.Require(flag.Exact, 2)
-	err := cmd.ParseFlags(args, true)
-	if err != nil {
+	if err := cmd.ParseFlags(args, true); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	nc := types.NetworkConnect{Container: cmd.Arg(1)}
-	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
-	return err
+	return cli.client.NetworkDisconnect(cmd.Arg(0), cmd.Arg(1))
 }
 }
 
 
 // CmdNetworkLs lists all the netorks managed by docker daemon
 // CmdNetworkLs lists all the netorks managed by docker daemon
@@ -156,18 +139,11 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
 	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
 	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
 
 
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
-	err := cmd.ParseFlags(args, true)
-
-	if err != nil {
-		return err
-	}
-	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
-	if err != nil {
+	if err := cmd.ParseFlags(args, true); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	var networkResources []types.NetworkResource
-	err = json.Unmarshal(obj, &networkResources)
+	networkResources, err := cli.client.NetworkList()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -212,73 +188,12 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	var tmpl *template.Template
-	if *tmplStr != "" {
-		var err error
-		tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr)
-		if err != nil {
-			return err
-		}
-	}
-
-	status := 0
-	var networks []types.NetworkResource
-	buf := new(bytes.Buffer)
-	for _, name := range cmd.Args() {
-		obj, statusCode, err := readBody(cli.call("GET", "/networks/"+name, nil, nil))
-		if err != nil {
-			if statusCode == http.StatusNotFound {
-				fmt.Fprintf(cli.err, "Error: No such network: %s\n", name)
-			} else {
-				fmt.Fprintf(cli.err, "%s\n", err)
-			}
-			status = 1
-			continue
-		}
-		var networkResource types.NetworkResource
-		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil {
-			return err
-		}
-
-		if tmpl == nil {
-			networks = append(networks, networkResource)
-			continue
-		}
-
-		if err := tmpl.Execute(buf, &networkResource); err != nil {
-			if err := tmpl.Execute(buf, &networkResource); err != nil {
-				fmt.Fprintf(cli.err, "%s\n", err)
-				return Cli.StatusError{StatusCode: 1}
-			}
-		}
-		buf.WriteString("\n")
-	}
-
-	if tmpl != nil {
-		if _, err := io.Copy(cli.out, buf); err != nil {
-			return err
-		}
-		return nil
-	}
-
-	if len(networks) == 0 {
-		io.WriteString(cli.out, "[]")
-	}
-
-	b, err := json.MarshalIndent(networks, "", "    ")
-	if err != nil {
-		return err
-	}
-
-	if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil {
-		return err
+	inspectSearcher := func(name string) (interface{}, []byte, error) {
+		i, err := cli.client.NetworkInspect(name)
+		return i, nil, err
 	}
 	}
-	io.WriteString(cli.out, "\n")
 
 
-	if status != 0 {
-		return Cli.StatusError{StatusCode: status}
-	}
-	return nil
+	return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher)
 }
 }
 
 
 // Consolidates the ipam configuration as a group from different related configurations
 // Consolidates the ipam configuration as a group from different related configurations

+ 1 - 1
api/client/pause.go

@@ -18,7 +18,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
 
 
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil {
+		if err := cli.client.ContainerPause(name); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 1 - 14
api/client/port.go

@@ -1,7 +1,6 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"strings"
 	"strings"
 
 
@@ -20,23 +19,11 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
+	c, err := cli.client.ContainerInspect(cmd.Arg(0))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	var c struct {
-		NetworkSettings struct {
-			Ports nat.PortMap
-		}
-	}
-
-	if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-		return err
-	}
-
 	if cmd.NArg() == 2 {
 	if cmd.NArg() == 2 {
 		var (
 		var (
 			port  = cmd.Arg(1)
 			port  = cmd.Arg(1)

+ 8 - 40
api/client/ps.go

@@ -1,10 +1,6 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
-	"net/url"
-	"strconv"
-
 	"github.com/docker/docker/api/client/ps"
 	"github.com/docker/docker/api/client/ps"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
@@ -21,7 +17,6 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		err error
 		err error
 
 
 		psFilterArgs = filters.NewArgs()
 		psFilterArgs = filters.NewArgs()
-		v            = url.Values{}
 
 
 		cmd      = Cli.Subcmd("ps", nil, Cli.DockerCommands["ps"].Description, true)
 		cmd      = Cli.Subcmd("ps", nil, Cli.DockerCommands["ps"].Description, true)
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
@@ -44,26 +39,6 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		*last = 1
 		*last = 1
 	}
 	}
 
 
-	if *all {
-		v.Set("all", "1")
-	}
-
-	if *last != -1 {
-		v.Set("limit", strconv.Itoa(*last))
-	}
-
-	if *since != "" {
-		v.Set("since", *since)
-	}
-
-	if *before != "" {
-		v.Set("before", *before)
-	}
-
-	if *size {
-		v.Set("size", "1")
-	}
-
 	// Consolidate all filter flags, and sanity check them.
 	// Consolidate all filter flags, and sanity check them.
 	// They'll get processed in the daemon/server.
 	// They'll get processed in the daemon/server.
 	for _, f := range flFilter.GetAll() {
 	for _, f := range flFilter.GetAll() {
@@ -72,27 +47,20 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	if psFilterArgs.Len() > 0 {
-		filterJSON, err := filters.ToParam(psFilterArgs)
-		if err != nil {
-			return err
-		}
-
-		v.Set("filters", filterJSON)
+	options := types.ContainerListOptions{
+		All:    *all,
+		Limit:  *last,
+		Since:  *since,
+		Before: *before,
+		Size:   *size,
+		Filter: psFilterArgs,
 	}
 	}
 
 
-	serverResp, err := cli.call("GET", "/containers/json?"+v.Encode(), nil, nil)
+	containers, err := cli.client.ContainerList(options)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	containers := []types.Container{}
-	if err := json.NewDecoder(serverResp.body).Decode(&containers); err != nil {
-		return err
-	}
-
 	f := *format
 	f := *format
 	if len(f) == 0 {
 	if len(f) == 0 {
 		if len(cli.PsFormat()) > 0 && !*quiet {
 		if len(cli.PsFormat()) > 0 && !*quiet {

+ 29 - 7
api/client/pull.go

@@ -3,10 +3,13 @@ package client
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"net/url"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/client/lib"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/pkg/jsonmessage"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	tagpkg "github.com/docker/docker/tag"
 	tagpkg "github.com/docker/docker/tag"
@@ -62,15 +65,34 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
+	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
+	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "pull")
+
 	if isTrusted() && !ref.HasDigest() {
 	if isTrusted() && !ref.HasDigest() {
 		// Check if tag is digest
 		// Check if tag is digest
-		authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
-		return cli.trustedPull(repoInfo, ref, authConfig)
+		return cli.trustedPull(repoInfo, ref, authConfig, requestPrivilege)
+	}
+
+	return cli.imagePullPrivileged(authConfig, distributionRef.String(), "", requestPrivilege)
+}
+
+func (cli *DockerCli) imagePullPrivileged(authConfig cliconfig.AuthConfig, imageID, tag string, requestPrivilege lib.RequestPrivilegeFunc) error {
+
+	encodedAuth, err := authConfig.EncodeToBase64()
+	if err != nil {
+		return err
+	}
+	options := types.ImagePullOptions{
+		ImageID:      imageID,
+		Tag:          tag,
+		RegistryAuth: encodedAuth,
 	}
 	}
 
 
-	v := url.Values{}
-	v.Set("fromImage", distributionRef.String())
+	responseBody, err := cli.client.ImagePull(options, requestPrivilege)
+	if err != nil {
+		return err
+	}
+	defer responseBody.Close()
 
 
-	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
-	return err
+	return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut)
 }
 }

+ 27 - 6
api/client/push.go

@@ -3,10 +3,14 @@ package client
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"net/url"
+	"io"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/client/lib"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/pkg/jsonmessage"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 )
 )
@@ -53,13 +57,30 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
 		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
 	}
 	}
 
 
+	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
 	if isTrusted() {
 	if isTrusted() {
-		return cli.trustedPush(repoInfo, tag, authConfig)
+		return cli.trustedPush(repoInfo, tag, authConfig, requestPrivilege)
 	}
 	}
 
 
-	v := url.Values{}
-	v.Set("tag", tag)
+	return cli.imagePushPrivileged(authConfig, ref.Name(), tag, cli.out, requestPrivilege)
+}
+
+func (cli *DockerCli) imagePushPrivileged(authConfig cliconfig.AuthConfig, imageID, tag string, outputStream io.Writer, requestPrivilege lib.RequestPrivilegeFunc) error {
+	encodedAuth, err := authConfig.EncodeToBase64()
+	if err != nil {
+		return err
+	}
+	options := types.ImagePushOptions{
+		ImageID:      imageID,
+		Tag:          tag,
+		RegistryAuth: encodedAuth,
+	}
+
+	responseBody, err := cli.client.ImagePush(options, requestPrivilege)
+	if err != nil {
+		return err
+	}
+	defer responseBody.Close()
 
 
-	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+ref.Name()+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
-	return err
+	return jsonmessage.DisplayJSONMessagesStream(responseBody, outputStream, cli.outFd, cli.isTerminalOut)
 }
 }

+ 1 - 1
api/client/rename.go

@@ -24,7 +24,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
 		return fmt.Errorf("Error: Neither old nor new names may be empty")
 		return fmt.Errorf("Error: Neither old nor new names may be empty")
 	}
 	}
 
 
-	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName), nil, nil)); err != nil {
+	if err := cli.client.ContainerRename(oldName, newName); err != nil {
 		fmt.Fprintf(cli.err, "%s\n", err)
 		fmt.Fprintf(cli.err, "%s\n", err)
 		return fmt.Errorf("Error: failed to rename container named %s", oldName)
 		return fmt.Errorf("Error: failed to rename container named %s", oldName)
 	}
 	}

+ 1 - 7
api/client/restart.go

@@ -2,8 +2,6 @@ package client
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"net/url"
-	"strconv"
 
 
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -19,13 +17,9 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	v := url.Values{}
-	v.Set("t", strconv.Itoa(*nSeconds))
-
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil))
-		if err != nil {
+		if err := cli.client.ContainerRestart(name, *nSeconds); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 9 - 15
api/client/rm.go

@@ -2,9 +2,9 @@ package client
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"net/url"
 	"strings"
 	"strings"
 
 
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
@@ -21,18 +21,6 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	val := url.Values{}
-	if *v {
-		val.Set("v", "1")
-	}
-	if *link {
-		val.Set("link", "1")
-	}
-
-	if *force {
-		val.Set("force", "1")
-	}
-
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		if name == "" {
 		if name == "" {
@@ -40,8 +28,14 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 		}
 		}
 		name = strings.Trim(name, "/")
 		name = strings.Trim(name, "/")
 
 
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil))
-		if err != nil {
+		options := types.ContainerRemoveOptions{
+			ContainerID:   name,
+			RemoveVolumes: *v,
+			RemoveLinks:   *link,
+			Force:         *force,
+		}
+
+		if err := cli.client.ContainerRemove(options); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 7 - 11
api/client/rmi.go

@@ -1,7 +1,6 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
 
 
@@ -31,20 +30,17 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 
 
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		serverResp, err := cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil)
+		options := types.ImageRemoveOptions{
+			ImageID:       name,
+			Force:         *force,
+			PruneChildren: !*noprune,
+		}
+
+		dels, err := cli.client.ImageRemove(options)
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {
-			defer serverResp.body.Close()
-
-			dels := []types.ImageDelete{}
-			if err := json.NewDecoder(serverResp.body).Decode(&dels); err != nil {
-				fmt.Fprintf(cli.err, "%s\n", err)
-				errNames = append(errNames, name)
-				continue
-			}
-
 			for _, del := range dels {
 			for _, del := range dels {
 				if del.Deleted != "" {
 				if del.Deleted != "" {
 					fmt.Fprintf(cli.out, "Deleted: %s\n", del.Deleted)
 					fmt.Fprintf(cli.out, "Deleted: %s\n", del.Deleted)

+ 22 - 35
api/client/run.go

@@ -3,12 +3,12 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	derr "github.com/docker/docker/errors"
 	derr "github.com/docker/docker/errors"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
@@ -168,70 +168,57 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
 	if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
 		return ErrConflictRestartPolicyAndAutoRemove
 		return ErrConflictRestartPolicyAndAutoRemove
 	}
 	}
-	// We need to instantiate the chan because the select needs it. It can
-	// be closed but can't be uninitialized.
-	hijacked := make(chan io.Closer)
-	// Block the return until the chan gets closed
-	defer func() {
-		logrus.Debugf("End of CmdRun(), Waiting for hijack to finish.")
-		if _, ok := <-hijacked; ok {
-			fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)")
-		}
-	}()
+
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
 		var (
 		var (
 			out, stderr io.Writer
 			out, stderr io.Writer
 			in          io.ReadCloser
 			in          io.ReadCloser
-			v           = url.Values{}
 		)
 		)
-		v.Set("stream", "1")
 		if config.AttachStdin {
 		if config.AttachStdin {
-			v.Set("stdin", "1")
 			in = cli.in
 			in = cli.in
 		}
 		}
 		if config.AttachStdout {
 		if config.AttachStdout {
-			v.Set("stdout", "1")
 			out = cli.out
 			out = cli.out
 		}
 		}
 		if config.AttachStderr {
 		if config.AttachStderr {
-			v.Set("stderr", "1")
 			if config.Tty {
 			if config.Tty {
 				stderr = cli.out
 				stderr = cli.out
 			} else {
 			} else {
 				stderr = cli.err
 				stderr = cli.err
 			}
 			}
 		}
 		}
-		errCh = promise.Go(func() error {
-			return cli.hijack("POST", "/containers/"+createResponse.ID+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil)
-		})
-	} else {
-		close(hijacked)
-	}
-	// Acknowledge the hijack before starting
-	select {
-	case closer := <-hijacked:
-		// Make sure that the hijack gets closed when returning (results
-		// in closing the hijack chan and freeing server's goroutines)
-		if closer != nil {
-			defer closer.Close()
+
+		options := types.ContainerAttachOptions{
+			ContainerID: createResponse.ID,
+			Stream:      true,
+			Stdin:       config.AttachStdin,
+			Stdout:      config.AttachStdout,
+			Stderr:      config.AttachStderr,
 		}
 		}
-	case err := <-errCh:
+
+		resp, err := cli.client.ContainerAttach(options)
 		if err != nil {
 		if err != nil {
-			logrus.Debugf("Error hijack: %s", err)
 			return err
 			return err
 		}
 		}
+		errCh = promise.Go(func() error {
+			return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp)
+		})
 	}
 	}
 
 
 	defer func() {
 	defer func() {
 		if *flAutoRemove {
 		if *flAutoRemove {
-			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, nil)); err != nil {
+			options := types.ContainerRemoveOptions{
+				ContainerID:   createResponse.ID,
+				RemoveVolumes: true,
+			}
+			if err := cli.client.ContainerRemove(options); err != nil {
 				fmt.Fprintf(cli.err, "Error deleting container: %s\n", err)
 				fmt.Fprintf(cli.err, "Error deleting container: %s\n", err)
 			}
 			}
 		}
 		}
 	}()
 	}()
 
 
 	//start the container
 	//start the container
-	if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, nil)); err != nil {
+	if err := cli.client.ContainerStart(createResponse.ID); err != nil {
 		cmd.ReportError(err.Error(), false)
 		cmd.ReportError(err.Error(), false)
 		return runStartContainerErr(err)
 		return runStartContainerErr(err)
 	}
 	}
@@ -262,7 +249,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	if *flAutoRemove {
 	if *flAutoRemove {
 		// Autoremove: wait for the container to finish, retrieve
 		// Autoremove: wait for the container to finish, retrieve
 		// the exit code and remove the container
 		// the exit code and remove the container
-		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, nil)); err != nil {
+		if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
 			return runStartContainerErr(err)
 			return runStartContainerErr(err)
 		}
 		}
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
@@ -272,7 +259,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		// No Autoremove: Simply retrieve the exit code
 		// No Autoremove: Simply retrieve the exit code
 		if !config.Tty {
 		if !config.Tty {
 			// In non-TTY mode, we can't detach, so we must wait for container exit
 			// In non-TTY mode, we can't detach, so we must wait for container exit
-			if status, err = waitForExit(cli, createResponse.ID); err != nil {
+			if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
 				return err
 				return err
 			}
 			}
 		} else {
 		} else {

+ 6 - 12
api/client/save.go

@@ -2,7 +2,7 @@ package client
 
 
 import (
 import (
 	"errors"
 	"errors"
-	"net/url"
+	"io"
 	"os"
 	"os"
 
 
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
@@ -35,18 +35,12 @@ func (cli *DockerCli) CmdSave(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	sopts := &streamOpts{
-		rawTerminal: true,
-		out:         output,
-	}
-
-	v := url.Values{}
-	for _, arg := range cmd.Args() {
-		v.Add("names", arg)
-	}
-	if _, err := cli.stream("GET", "/images/get?"+v.Encode(), sopts); err != nil {
+	responseBody, err := cli.client.ImageSave(cmd.Args())
+	if err != nil {
 		return err
 		return err
 	}
 	}
+	defer responseBody.Close()
 
 
-	return nil
+	_, err = io.Copy(output, responseBody)
+	return err
 }
 }

+ 20 - 13
api/client/search.go

@@ -1,26 +1,19 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
 
 
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 )
 )
 
 
-// ByStars sorts search results in ascending order by number of stars.
-type ByStars []registry.SearchResult
-
-func (r ByStars) Len() int           { return len(r) }
-func (r ByStars) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
-func (r ByStars) Less(i, j int) bool { return r[i].StarCount < r[j].StarCount }
-
 // CmdSearch searches the Docker Hub for images.
 // CmdSearch searches the Docker Hub for images.
 //
 //
 // Usage: docker search [OPTIONS] TERM
 // Usage: docker search [OPTIONS] TERM
@@ -42,19 +35,26 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	rdr, _, err := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, indexInfo, "search")
+	authConfig := registry.ResolveAuthConfig(cli.configFile, indexInfo)
+	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(indexInfo, "search")
+
+	encodedAuth, err := authConfig.EncodeToBase64()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer rdr.Close()
+	options := types.ImageSearchOptions{
+		Term:         name,
+		RegistryAuth: encodedAuth,
+	}
 
 
-	results := ByStars{}
-	if err := json.NewDecoder(rdr).Decode(&results); err != nil {
+	unorderedResults, err := cli.client.ImageSearch(options, requestPrivilege)
+	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	sort.Sort(sort.Reverse(results))
+	results := searchResultsByStars(unorderedResults)
+	sort.Sort(results)
 
 
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
@@ -81,3 +81,10 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 	w.Flush()
 	w.Flush()
 	return nil
 	return nil
 }
 }
+
+// SearchResultsByStars sorts search results in descending order by number of stars.
+type searchResultsByStars []registry.SearchResult
+
+func (r searchResultsByStars) Len() int           { return len(r) }
+func (r searchResultsByStars) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
+func (r searchResultsByStars) Less(i, j int) bool { return r[j].StarCount < r[i].StarCount }

+ 54 - 82
api/client/start.go

@@ -1,11 +1,10 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 	"os"
 	"os"
+	"strings"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -34,7 +33,8 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
 				fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
 				fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
 				continue
 				continue
 			}
 			}
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil {
+
+			if err := cli.client.ContainerKill(cid, sig); err != nil {
 				logrus.Debugf("Error sending signal: %s", err)
 				logrus.Debugf("Error sending signal: %s", err)
 			}
 			}
 		}
 		}
@@ -53,119 +53,91 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	var (
-		cErr chan error
-		tty  bool
-	)
-
 	if *attach || *openStdin {
 	if *attach || *openStdin {
+		// We're going to attach to a container.
+		// 1. Ensure we only have one container.
 		if cmd.NArg() > 1 {
 		if cmd.NArg() > 1 {
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
 		}
 		}
 
 
-		serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
+		// 2. Attach to the container.
+		containerID := cmd.Arg(0)
+		c, err := cli.client.ContainerInspect(containerID)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 
 
-		defer serverResp.body.Close()
-
-		var c types.ContainerJSON
-		if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-			return err
+		if !c.Config.Tty {
+			sigc := cli.forwardAllSignals(containerID)
+			defer signal.StopCatch(sigc)
 		}
 		}
 
 
-		tty = c.Config.Tty
-
-		if !tty {
-			sigc := cli.forwardAllSignals(cmd.Arg(0))
-			defer signal.StopCatch(sigc)
+		options := types.ContainerAttachOptions{
+			ContainerID: containerID,
+			Stream:      true,
+			Stdin:       *openStdin && c.Config.OpenStdin,
+			Stdout:      true,
+			Stderr:      true,
 		}
 		}
 
 
 		var in io.ReadCloser
 		var in io.ReadCloser
-
-		v := url.Values{}
-		v.Set("stream", "1")
-
-		if *openStdin && c.Config.OpenStdin {
-			v.Set("stdin", "1")
+		if options.Stdin {
 			in = cli.in
 			in = cli.in
 		}
 		}
 
 
-		v.Set("stdout", "1")
-		v.Set("stderr", "1")
+		resp, err := cli.client.ContainerAttach(options)
+		if err != nil {
+			return err
+		}
+		defer resp.Close()
 
 
-		hijacked := make(chan io.Closer)
-		// Block the return until the chan gets closed
-		defer func() {
-			logrus.Debugf("CmdStart() returned, defer waiting for hijack to finish.")
-			if _, ok := <-hijacked; ok {
-				fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)")
-			}
-			cli.in.Close()
-		}()
-		cErr = promise.Go(func() error {
-			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil)
+		cErr := promise.Go(func() error {
+			return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)
 		})
 		})
 
 
-		// Acknowledge the hijack before starting
-		select {
-		case closer := <-hijacked:
-			// Make sure that the hijack gets closed when returning (results
-			// in closing the hijack chan and freeing server's goroutines)
-			if closer != nil {
-				defer closer.Close()
-			}
-		case err := <-cErr:
-			if err != nil {
-				return err
-			}
-		}
-	}
-
-	var encounteredError error
-	var errNames []string
-	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
-		if err != nil {
-			if !*attach && !*openStdin {
-				// attach and openStdin is false means it could be starting multiple containers
-				// when a container start failed, show the error message and start next
-				fmt.Fprintf(cli.err, "%s\n", err)
-				errNames = append(errNames, name)
-			} else {
-				encounteredError = err
-			}
-		} else {
-			if !*attach && !*openStdin {
-				fmt.Fprintf(cli.out, "%s\n", name)
-			}
+		// 3. Start the container.
+		if err := cli.client.ContainerStart(containerID); err != nil {
+			return err
 		}
 		}
-	}
-
-	if len(errNames) > 0 {
-		encounteredError = fmt.Errorf("Error: failed to start containers: %v", errNames)
-	}
-	if encounteredError != nil {
-		return encounteredError
-	}
 
 
-	if *openStdin || *attach {
-		if tty && cli.isTerminalOut {
-			if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
+		// 4. Wait for attachement to break.
+		if c.Config.Tty && cli.isTerminalOut {
+			if err := cli.monitorTtySize(containerID, false); err != nil {
 				fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
 				fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
 			}
 			}
 		}
 		}
 		if attchErr := <-cErr; attchErr != nil {
 		if attchErr := <-cErr; attchErr != nil {
 			return attchErr
 			return attchErr
 		}
 		}
-		_, status, err := getExitCode(cli, cmd.Arg(0))
+		_, status, err := getExitCode(cli, containerID)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		if status != 0 {
 		if status != 0 {
 			return Cli.StatusError{StatusCode: status}
 			return Cli.StatusError{StatusCode: status}
 		}
 		}
+	} else {
+		// We're not going to attach to anything.
+		// Start as many containers as we want.
+		return cli.startContainersWithoutAttachments(cmd.Args())
+	}
+
+	return nil
+}
+
+func (cli *DockerCli) startContainersWithoutAttachments(containerIDs []string) error {
+	var failedContainers []string
+	for _, containerID := range containerIDs {
+		if err := cli.client.ContainerStart(containerID); err != nil {
+			fmt.Fprintf(cli.err, "%s\n", err)
+			failedContainers = append(failedContainers, containerID)
+		} else {
+			fmt.Fprintf(cli.out, "%s\n", containerID)
+		}
+	}
+
+	if len(failedContainers) > 0 {
+		return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 10 - 22
api/client/stats.go

@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"net/url"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
@@ -37,26 +36,19 @@ type stats struct {
 }
 }
 
 
 func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
 func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
-	v := url.Values{}
-	if streamStats {
-		v.Set("stream", "1")
-	} else {
-		v.Set("stream", "0")
-	}
-	serverResp, err := cli.call("GET", "/containers/"+s.Name+"/stats?"+v.Encode(), nil, nil)
+	responseBody, err := cli.client.ContainerStats(s.Name, streamStats)
 	if err != nil {
 	if err != nil {
 		s.mu.Lock()
 		s.mu.Lock()
 		s.err = err
 		s.err = err
 		s.mu.Unlock()
 		s.mu.Unlock()
 		return
 		return
 	}
 	}
-
-	defer serverResp.body.Close()
+	defer responseBody.Close()
 
 
 	var (
 	var (
 		previousCPU    uint64
 		previousCPU    uint64
 		previousSystem uint64
 		previousSystem uint64
-		dec            = json.NewDecoder(serverResp.body)
+		dec            = json.NewDecoder(responseBody)
 		u              = make(chan error, 1)
 		u              = make(chan error, 1)
 	)
 	)
 	go func() {
 	go func() {
@@ -156,18 +148,13 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 	showAll := len(names) == 0
 	showAll := len(names) == 0
 
 
 	if showAll {
 	if showAll {
-		v := url.Values{}
-		if *all {
-			v.Set("all", "1")
+		options := types.ContainerListOptions{
+			All: *all,
 		}
 		}
-		body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
+		cs, err := cli.client.ContainerList(options)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		var cs []types.Container
-		if err := json.Unmarshal(body, &cs); err != nil {
-			return err
-		}
 		for _, c := range cs {
 		for _, c := range cs {
 			names = append(names, c.ID[:12])
 			names = append(names, c.ID[:12])
 		}
 		}
@@ -202,14 +189,15 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 			err   error
 			err   error
 		}
 		}
 		getNewContainers := func(c chan<- watch) {
 		getNewContainers := func(c chan<- watch) {
-			res, err := cli.call("GET", "/events", nil, nil)
+			options := types.EventsOptions{}
+			resBody, err := cli.client.Events(options)
 			if err != nil {
 			if err != nil {
 				c <- watch{err: err}
 				c <- watch{err: err}
 				return
 				return
 			}
 			}
-			defer res.body.Close()
+			defer resBody.Close()
 
 
-			dec := json.NewDecoder(res.body)
+			dec := json.NewDecoder(resBody)
 			for {
 			for {
 				var j *jsonmessage.JSONMessage
 				var j *jsonmessage.JSONMessage
 				if err := dec.Decode(&j); err != nil {
 				if err := dec.Decode(&j); err != nil {

+ 1 - 7
api/client/stop.go

@@ -2,8 +2,6 @@ package client
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"net/url"
-	"strconv"
 
 
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -21,13 +19,9 @@ func (cli *DockerCli) CmdStop(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	v := url.Values{}
-	v.Set("t", strconv.Itoa(*nSeconds))
-
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil))
-		if err != nil {
+		if err := cli.client.ContainerStop(name, *nSeconds); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 7 - 10
api/client/tag.go

@@ -2,9 +2,9 @@ package client
 
 
 import (
 import (
 	"errors"
 	"errors"
-	"net/url"
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -20,7 +20,6 @@ func (cli *DockerCli) CmdTag(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	v := url.Values{}
 	ref, err := reference.ParseNamed(cmd.Arg(1))
 	ref, err := reference.ParseNamed(cmd.Arg(1))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -41,15 +40,13 @@ func (cli *DockerCli) CmdTag(args ...string) error {
 	if err := registry.ValidateRepositoryName(ref); err != nil {
 	if err := registry.ValidateRepositoryName(ref); err != nil {
 		return err
 		return err
 	}
 	}
-	v.Set("repo", ref.Name())
-	v.Set("tag", tag)
 
 
-	if *force {
-		v.Set("force", "1")
+	options := types.ImageTagOptions{
+		ImageID:        cmd.Arg(0),
+		RepositoryName: ref.Name(),
+		Tag:            tag,
+		Force:          *force,
 	}
 	}
 
 
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, nil)); err != nil {
-		return err
-	}
-	return nil
+	return cli.client.ImageTag(options)
 }
 }

+ 3 - 13
api/client/top.go

@@ -1,13 +1,10 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
-	"net/url"
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
 
 
-	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
@@ -21,23 +18,16 @@ func (cli *DockerCli) CmdTop(args ...string) error {
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	val := url.Values{}
+	var arguments []string
 	if cmd.NArg() > 1 {
 	if cmd.NArg() > 1 {
-		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
+		arguments = cmd.Args()[1:]
 	}
 	}
 
 
-	serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, nil)
+	procList, err := cli.client.ContainerTop(cmd.Arg(0), arguments)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	procList := types.ContainerProcessList{}
-	if err := json.NewDecoder(serverResp.body).Decode(&procList); err != nil {
-		return err
-	}
-
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
 	fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
 
 

+ 15 - 22
api/client/trust.go

@@ -22,6 +22,8 @@ import (
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
+	"github.com/docker/docker/api/client/lib"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/ansiescape"
 	"github.com/docker/docker/pkg/ansiescape"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
@@ -250,16 +252,15 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can
 
 
 func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.NamedTagged) error {
 func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.NamedTagged) error {
 	fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
 	fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
-	tv := url.Values{}
-	tv.Set("repo", trustedRef.Name())
-	tv.Set("tag", ref.Tag())
-	tv.Set("force", "1")
 
 
-	if _, _, err := readBody(cli.call("POST", "/images/"+trustedRef.String()+"/tag?"+tv.Encode(), nil, nil)); err != nil {
-		return err
+	options := types.ImageTagOptions{
+		ImageID:        trustedRef.String(),
+		RepositoryName: trustedRef.Name(),
+		Tag:            ref.Tag(),
+		Force:          true,
 	}
 	}
 
 
-	return nil
+	return cli.client.ImageTag(options)
 }
 }
 
 
 func notaryError(err error) error {
 func notaryError(err error) error {
@@ -278,11 +279,8 @@ func notaryError(err error) error {
 	return err
 	return err
 }
 }
 
 
-func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig cliconfig.AuthConfig) error {
-	var (
-		v    = url.Values{}
-		refs = []target{}
-	)
+func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig cliconfig.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error {
+	var refs []target
 
 
 	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig)
 	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig)
 	if err != nil {
 	if err != nil {
@@ -317,17 +315,14 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 		refs = append(refs, r)
 		refs = append(refs, r)
 	}
 	}
 
 
-	v.Set("fromImage", repoInfo.LocalName.Name())
 	for i, r := range refs {
 	for i, r := range refs {
 		displayTag := r.reference.String()
 		displayTag := r.reference.String()
 		if displayTag != "" {
 		if displayTag != "" {
 			displayTag = ":" + displayTag
 			displayTag = ":" + displayTag
 		}
 		}
 		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest)
 		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest)
-		v.Set("tag", r.digest.String())
 
 
-		_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
-		if err != nil {
+		if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil {
 			return err
 			return err
 		}
 		}
 
 
@@ -385,20 +380,18 @@ func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
 	return ioutils.NewWriteCloserWrapper(out, w.Close), targetChan
 	return ioutils.NewWriteCloserWrapper(out, w.Close), targetChan
 }
 }
 
 
-func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig cliconfig.AuthConfig) error {
+func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig cliconfig.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error {
 	streamOut, targetChan := targetStream(cli.out)
 	streamOut, targetChan := targetStream(cli.out)
 
 
-	v := url.Values{}
-	v.Set("tag", tag)
+	reqError := cli.imagePushPrivileged(authConfig, repoInfo.LocalName.Name(), tag, streamOut, requestPrivilege)
 
 
-	_, _, err := cli.clientRequestAttemptLogin("POST", "/images/"+repoInfo.LocalName.Name()+"/push?"+v.Encode(), nil, streamOut, repoInfo.Index, "push")
 	// Close stream channel to finish target parsing
 	// Close stream channel to finish target parsing
 	if err := streamOut.Close(); err != nil {
 	if err := streamOut.Close(); err != nil {
 		return err
 		return err
 	}
 	}
 	// Check error from request
 	// Check error from request
-	if err != nil {
-		return err
+	if reqError != nil {
+		return reqError
 	}
 	}
 
 
 	// Get target results
 	// Get target results

+ 1 - 1
api/client/unpause.go

@@ -18,7 +18,7 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
 
 
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil {
+		if err := cli.client.ContainerUnpause(name); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)
 		} else {
 		} else {

+ 24 - 280
api/client/utils.go

@@ -1,242 +1,33 @@
 package client
 package client
 
 
 import (
 import (
-	"bytes"
-	"encoding/base64"
-	"encoding/json"
-	"errors"
 	"fmt"
 	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
 	"os"
 	"os"
 	gosignal "os/signal"
 	gosignal "os/signal"
 	"runtime"
 	"runtime"
-	"strconv"
-	"strings"
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/client/lib"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/dockerversion"
-	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
-	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
-	"github.com/docker/docker/utils"
 )
 )
 
 
-var (
-	errConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
-)
-
-type serverResponse struct {
-	body       io.ReadCloser
-	header     http.Header
-	statusCode int
-}
-
-// HTTPClient creates a new HTTP client with the cli's client transport instance.
-func (cli *DockerCli) HTTPClient() *http.Client {
-	return &http.Client{Transport: cli.transport}
-}
-
-func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
-	params := bytes.NewBuffer(nil)
-	if data != nil {
-		if err := json.NewEncoder(params).Encode(data); err != nil {
-			return nil, err
-		}
-	}
-	return params, nil
-}
-
-func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (*serverResponse, error) {
-
-	serverResp := &serverResponse{
-		body:       nil,
-		statusCode: -1,
-	}
-
-	expectedPayload := (method == "POST" || method == "PUT")
-	if expectedPayload && in == nil {
-		in = bytes.NewReader([]byte{})
-	}
-	req, err := http.NewRequest(method, fmt.Sprintf("%s/v%s%s", cli.basePath, api.Version, path), in)
-	if err != nil {
-		return serverResp, err
-	}
-
-	// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
-	// then the user can't change OUR headers
-	for k, v := range cli.configFile.HTTPHeaders {
-		req.Header.Set(k, v)
-	}
-
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.Version+" ("+runtime.GOOS+")")
-	req.URL.Host = cli.addr
-	req.URL.Scheme = cli.scheme
-
-	if headers != nil {
-		for k, v := range headers {
-			req.Header[k] = v
-		}
-	}
-
-	if expectedPayload && req.Header.Get("Content-Type") == "" {
-		req.Header.Set("Content-Type", "text/plain")
-	}
-
-	resp, err := cli.HTTPClient().Do(req)
-	if resp != nil {
-		serverResp.statusCode = resp.StatusCode
-	}
-
-	if err != nil {
-		if utils.IsTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
-			return serverResp, errConnectionFailed
-		}
-
-		if cli.tlsConfig == nil && strings.Contains(err.Error(), "malformed HTTP response") {
-			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
-		}
-		if cli.tlsConfig != nil && strings.Contains(err.Error(), "remote error: bad certificate") {
-			return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
-		}
-
-		return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
-	}
-
-	if serverResp.statusCode < 200 || serverResp.statusCode >= 400 {
-		body, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return serverResp, err
-		}
-		if len(body) == 0 {
-			return serverResp, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), req.URL)
-		}
-		return serverResp, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
-	}
-
-	serverResp.body = resp.Body
-	serverResp.header = resp.Header
-	return serverResp, nil
-}
-
-// cmdAttempt builds the corresponding registry Auth Header from the given
-// authConfig. It returns the servers body, status, error response
-func (cli *DockerCli) cmdAttempt(authConfig cliconfig.AuthConfig, method, path string, in io.Reader, out io.Writer) (io.ReadCloser, int, error) {
-	buf, err := json.Marshal(authConfig)
-	if err != nil {
-		return nil, -1, err
-	}
-	registryAuthHeader := []string{
-		base64.URLEncoding.EncodeToString(buf),
-	}
-
-	// begin the request
-	serverResp, err := cli.clientRequest(method, path, in, map[string][]string{
-		"X-Registry-Auth": registryAuthHeader,
-	})
-	if err == nil && out != nil {
-		// If we are streaming output, complete the stream since
-		// errors may not appear until later.
-		err = cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), true, out, nil)
-	}
-	if err != nil {
-		// Since errors in a stream appear after status 200 has been written,
-		// we may need to change the status code.
-		if strings.Contains(err.Error(), "Authentication is required") ||
-			strings.Contains(err.Error(), "Status 401") ||
-			strings.Contains(err.Error(), "401 Unauthorized") ||
-			strings.Contains(err.Error(), "status code 401") {
-			serverResp.statusCode = http.StatusUnauthorized
-		}
-	}
-	return serverResp.body, serverResp.statusCode, err
-}
-
-func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
-
-	// Resolve the Auth config relevant for this server
+func (cli *DockerCli) encodeRegistryAuth(index *registry.IndexInfo) (string, error) {
 	authConfig := registry.ResolveAuthConfig(cli.configFile, index)
 	authConfig := registry.ResolveAuthConfig(cli.configFile, index)
-	body, statusCode, err := cli.cmdAttempt(authConfig, method, path, in, out)
-	if statusCode == http.StatusUnauthorized {
-		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
-		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
-			return nil, -1, err
-		}
-		authConfig = registry.ResolveAuthConfig(cli.configFile, index)
-		return cli.cmdAttempt(authConfig, method, path, in, out)
-	}
-	return body, statusCode, err
-}
-
-func (cli *DockerCli) callWrapper(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
-	sr, err := cli.call(method, path, data, headers)
-	return sr.body, sr.header, sr.statusCode, err
-}
-
-func (cli *DockerCli) call(method, path string, data interface{}, headers map[string][]string) (*serverResponse, error) {
-	params, err := cli.encodeData(data)
-	if err != nil {
-		sr := &serverResponse{
-			body:       nil,
-			header:     nil,
-			statusCode: -1,
-		}
-		return sr, nil
-	}
-
-	if data != nil {
-		if headers == nil {
-			headers = make(map[string][]string)
-		}
-		headers["Content-Type"] = []string{"application/json"}
-	}
-
-	serverResp, err := cli.clientRequest(method, path, params, headers)
-	return serverResp, err
-}
-
-type streamOpts struct {
-	rawTerminal bool
-	in          io.Reader
-	out         io.Writer
-	err         io.Writer
-	headers     map[string][]string
+	return authConfig.EncodeToBase64()
 }
 }
 
 
-func (cli *DockerCli) stream(method, path string, opts *streamOpts) (*serverResponse, error) {
-	serverResp, err := cli.clientRequest(method, path, opts.in, opts.headers)
-	if err != nil {
-		return serverResp, err
-	}
-	return serverResp, cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), opts.rawTerminal, opts.out, opts.err)
-}
-
-func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, rawTerminal bool, stdout, stderr io.Writer) error {
-	defer body.Close()
-
-	if api.MatchesContentType(contentType, "application/json") {
-		return jsonmessage.DisplayJSONMessagesStream(body, stdout, cli.outFd, cli.isTerminalOut)
-	}
-	if stdout != nil || stderr != nil {
-		// When TTY is ON, use regular copy
-		var err error
-		if rawTerminal {
-			_, err = io.Copy(stdout, body)
-		} else {
-			_, err = stdcopy.StdCopy(stdout, stderr, body)
+func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registry.IndexInfo, cmdName string) lib.RequestPrivilegeFunc {
+	return func() (string, error) {
+		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
+		if err := cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
+			return "", err
 		}
 		}
-		logrus.Debugf("[stream] End of stdout")
-		return err
+		return cli.encodeRegistryAuth(index)
 	}
 	}
-	return nil
 }
 }
 
 
 func (cli *DockerCli) resizeTty(id string, isExec bool) {
 func (cli *DockerCli) resizeTty(id string, isExec bool) {
@@ -244,86 +35,53 @@ func (cli *DockerCli) resizeTty(id string, isExec bool) {
 	if height == 0 && width == 0 {
 	if height == 0 && width == 0 {
 		return
 		return
 	}
 	}
-	v := url.Values{}
-	v.Set("h", strconv.Itoa(height))
-	v.Set("w", strconv.Itoa(width))
 
 
-	path := ""
-	if !isExec {
-		path = "/containers/" + id + "/resize?"
-	} else {
-		path = "/exec/" + id + "/resize?"
+	options := types.ResizeOptions{
+		ID:     id,
+		Height: height,
+		Width:  width,
 	}
 	}
 
 
-	if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, nil)); err != nil {
-		logrus.Debugf("Error resize: %s", err)
+	var err error
+	if !isExec {
+		err = cli.client.ContainerExecResize(options)
+	} else {
+		err = cli.client.ContainerResize(options)
 	}
 	}
-}
 
 
-func waitForExit(cli *DockerCli, containerID string) (int, error) {
-	serverResp, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, nil)
 	if err != nil {
 	if err != nil {
-		return -1, err
-	}
-
-	defer serverResp.body.Close()
-
-	var res types.ContainerWaitResponse
-	if err := json.NewDecoder(serverResp.body).Decode(&res); err != nil {
-		return -1, err
+		logrus.Debugf("Error resize: %s", err)
 	}
 	}
-
-	return res.StatusCode, nil
 }
 }
 
 
 // getExitCode perform an inspect on the container. It returns
 // getExitCode perform an inspect on the container. It returns
 // the running state and the exit code.
 // the running state and the exit code.
 func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
 func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
-	serverResp, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
+	c, err := cli.client.ContainerInspect(containerID)
 	if err != nil {
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		// If we can't connect, then the daemon probably died.
-		if err != errConnectionFailed {
+		if err != lib.ErrConnectionFailed {
 			return false, -1, err
 			return false, -1, err
 		}
 		}
 		return false, -1, nil
 		return false, -1, nil
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	var c types.ContainerJSON
-	if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-		return false, -1, err
-	}
-
 	return c.State.Running, c.State.ExitCode, nil
 	return c.State.Running, c.State.ExitCode, nil
 }
 }
 
 
 // getExecExitCode perform an inspect on the exec command. It returns
 // getExecExitCode perform an inspect on the exec command. It returns
 // the running state and the exit code.
 // the running state and the exit code.
 func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
 func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
-	serverResp, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
+	resp, err := cli.client.ContainerExecInspect(execID)
 	if err != nil {
 	if err != nil {
 		// If we can't connect, then the daemon probably died.
 		// If we can't connect, then the daemon probably died.
-		if err != errConnectionFailed {
+		if err != lib.ErrConnectionFailed {
 			return false, -1, err
 			return false, -1, err
 		}
 		}
 		return false, -1, nil
 		return false, -1, nil
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	//TODO: Should we reconsider having a type in api/types?
-	//this is a response to exex/id/json not container
-	var c struct {
-		Running  bool
-		ExitCode int
-	}
-
-	if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
-		return false, -1, err
-	}
-
-	return c.Running, c.ExitCode, nil
+	return resp.Running, resp.ExitCode, nil
 }
 }
 
 
 func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
 func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
@@ -368,17 +126,3 @@ func (cli *DockerCli) getTtySize() (int, int) {
 	}
 	}
 	return int(ws.Height), int(ws.Width)
 	return int(ws.Height), int(ws.Width)
 }
 }
-
-func readBody(serverResp *serverResponse, err error) ([]byte, int, error) {
-	if serverResp.body != nil {
-		defer serverResp.body.Close()
-	}
-	if err != nil {
-		return nil, serverResp.statusCode, err
-	}
-	body, err := ioutil.ReadAll(serverResp.body)
-	if err != nil {
-		return nil, -1, err
-	}
-	return body, serverResp.statusCode, nil
-}

+ 16 - 51
api/client/version.go

@@ -1,17 +1,11 @@
 package client
 package client
 
 
 import (
 import (
-	"encoding/json"
-	"runtime"
 	"text/template"
 	"text/template"
 	"time"
 	"time"
 
 
-	"github.com/docker/docker/api"
-	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
-	"github.com/docker/docker/dockerversion"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
-	"github.com/docker/docker/utils"
 )
 )
 
 
 var versionTemplate = `Client:
 var versionTemplate = `Client:
@@ -32,12 +26,6 @@ Server:
  OS/Arch:      {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
  OS/Arch:      {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
  Experimental: {{.Server.Experimental}}{{end}}{{end}}`
  Experimental: {{.Server.Experimental}}{{end}}{{end}}`
 
 
-type versionData struct {
-	Client   types.Version
-	ServerOK bool
-	Server   types.Version
-}
-
 // CmdVersion shows Docker version information.
 // CmdVersion shows Docker version information.
 //
 //
 // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch.
 // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch.
@@ -49,59 +37,36 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
-	if *tmplStr == "" {
-		*tmplStr = versionTemplate
+
+	templateFormat := versionTemplate
+	if *tmplStr != "" {
+		templateFormat = *tmplStr
 	}
 	}
 
 
 	var tmpl *template.Template
 	var tmpl *template.Template
-	if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
+	if tmpl, err = template.New("").Funcs(funcMap).Parse(templateFormat); err != nil {
 		return Cli.StatusError{StatusCode: 64,
 		return Cli.StatusError{StatusCode: 64,
 			Status: "Template parsing error: " + err.Error()}
 			Status: "Template parsing error: " + err.Error()}
 	}
 	}
 
 
-	vd := versionData{
-		Client: types.Version{
-			Version:      dockerversion.Version,
-			APIVersion:   api.Version,
-			GoVersion:    runtime.Version(),
-			GitCommit:    dockerversion.GitCommit,
-			BuildTime:    dockerversion.BuildTime,
-			Os:           runtime.GOOS,
-			Arch:         runtime.GOARCH,
-			Experimental: utils.ExperimentalBuild(),
-		},
+	vd, err := cli.client.SystemVersion()
+
+	// first we need to make BuildTime more human friendly
+	t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime)
+	if errTime == nil {
+		vd.Client.BuildTime = t.Format(time.ANSIC)
 	}
 	}
 
 
-	defer func() {
-		// first we need to make BuildTime more human friendly
-		t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime)
-		if errTime == nil {
-			vd.Client.BuildTime = t.Format(time.ANSIC)
-		}
+	if vd.ServerOK() {
 		t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime)
 		t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime)
 		if errTime == nil {
 		if errTime == nil {
 			vd.Server.BuildTime = t.Format(time.ANSIC)
 			vd.Server.BuildTime = t.Format(time.ANSIC)
 		}
 		}
-
-		if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil {
-			err = err2
-		}
-		cli.out.Write([]byte{'\n'})
-	}()
-
-	serverResp, err := cli.call("GET", "/version", nil, nil)
-	if err != nil {
-		return err
 	}
 	}
 
 
-	defer serverResp.body.Close()
-
-	if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil {
-		return Cli.StatusError{StatusCode: 1,
-			Status: "Error reading remote version: " + err.Error()}
+	if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil {
+		err = err2
 	}
 	}
-
-	vd.ServerOK = true
-
-	return
+	cli.out.Write([]byte{'\n'})
+	return err
 }
 }

+ 9 - 93
api/client/volume.go

@@ -1,14 +1,8 @@
 package client
 package client
 
 
 import (
 import (
-	"bytes"
-	"encoding/json"
 	"fmt"
 	"fmt"
-	"io"
-	"net/http"
-	"net/url"
 	"text/tabwriter"
 	"text/tabwriter"
-	"text/template"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
 	Cli "github.com/docker/docker/cli"
@@ -64,25 +58,11 @@ func (cli *DockerCli) CmdVolumeLs(args ...string) error {
 		}
 		}
 	}
 	}
 
 
-	v := url.Values{}
-	if volFilterArgs.Len() > 0 {
-		filterJSON, err := filters.ToParam(volFilterArgs)
-		if err != nil {
-			return err
-		}
-		v.Set("filters", filterJSON)
-	}
-
-	resp, err := cli.call("GET", "/volumes?"+v.Encode(), nil, nil)
+	volumes, err := cli.client.VolumeList(volFilterArgs)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	var volumes types.VolumesListResponse
-	if err := json.NewDecoder(resp.body).Decode(&volumes); err != nil {
-		return err
-	}
-
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	if !*quiet {
 	if !*quiet {
 		fmt.Fprintf(w, "DRIVER \tVOLUME NAME")
 		fmt.Fprintf(w, "DRIVER \tVOLUME NAME")
@@ -114,68 +94,12 @@ func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	var tmpl *template.Template
-	if *tmplStr != "" {
-		var err error
-		tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr)
-		if err != nil {
-			return err
-		}
-	}
-
-	var status = 0
-	var volumes []*types.Volume
-
-	for _, name := range cmd.Args() {
-		resp, err := cli.call("GET", "/volumes/"+name, nil, nil)
-		if err != nil {
-			if resp.statusCode != http.StatusNotFound {
-				return err
-			}
-			status = 1
-			fmt.Fprintf(cli.err, "Error: No such volume: %s\n", name)
-			continue
-		}
-
-		var volume types.Volume
-		if err := json.NewDecoder(resp.body).Decode(&volume); err != nil {
-			fmt.Fprintf(cli.err, "Unable to read inspect data: %v\n", err)
-			status = 1
-			break
-		}
-
-		if tmpl == nil {
-			volumes = append(volumes, &volume)
-			continue
-		}
-
-		buf := bytes.NewBufferString("")
-		if err := tmpl.Execute(buf, &volume); err != nil {
-			fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
-			status = 1
-			break
-		}
-
-		cli.out.Write(buf.Bytes())
-		cli.out.Write([]byte{'\n'})
-	}
-
-	if tmpl == nil {
-		b, err := json.MarshalIndent(volumes, "", "    ")
-		if err != nil {
-			return err
-		}
-		_, err = io.Copy(cli.out, bytes.NewReader(b))
-		if err != nil {
-			return err
-		}
-		io.WriteString(cli.out, "\n")
+	inspectSearcher := func(name string) (interface{}, []byte, error) {
+		i, err := cli.client.VolumeInspect(name)
+		return i, nil, err
 	}
 	}
 
 
-	if status != 0 {
-		return Cli.StatusError{StatusCode: status}
-	}
-	return nil
+	return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher)
 }
 }
 
 
 // CmdVolumeCreate creates a new container from a given image.
 // CmdVolumeCreate creates a new container from a given image.
@@ -192,24 +116,17 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
-	volReq := &types.VolumeCreateRequest{
+	volReq := types.VolumeCreateRequest{
 		Driver:     *flDriver,
 		Driver:     *flDriver,
 		DriverOpts: flDriverOpts.GetAll(),
 		DriverOpts: flDriverOpts.GetAll(),
+		Name:       *flName,
 	}
 	}
 
 
-	if *flName != "" {
-		volReq.Name = *flName
-	}
-
-	resp, err := cli.call("POST", "/volumes/create", volReq, nil)
+	vol, err := cli.client.VolumeCreate(volReq)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	var vol types.Volume
-	if err := json.NewDecoder(resp.body).Decode(&vol); err != nil {
-		return err
-	}
 	fmt.Fprintf(cli.out, "%s\n", vol.Name)
 	fmt.Fprintf(cli.out, "%s\n", vol.Name)
 	return nil
 	return nil
 }
 }
@@ -224,8 +141,7 @@ func (cli *DockerCli) CmdVolumeRm(args ...string) error {
 
 
 	var status = 0
 	var status = 0
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		_, err := cli.call("DELETE", "/volumes/"+name, nil, nil)
-		if err != nil {
+		if err := cli.client.VolumeRemove(name); err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			status = 1
 			status = 1
 			continue
 			continue

+ 1 - 1
api/client/wait.go

@@ -20,7 +20,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
 
 
 	var errNames []string
 	var errNames []string
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
-		status, err := waitForExit(cli, name)
+		status, err := cli.client.ContainerWait(name)
 		if err != nil {
 		if err != nil {
 			fmt.Fprintf(cli.err, "%s\n", err)
 			fmt.Fprintf(cli.err, "%s\n", err)
 			errNames = append(errNames, name)
 			errNames = append(errNames, name)

+ 234 - 0
api/types/client.go

@@ -0,0 +1,234 @@
+package types
+
+import (
+	"bufio"
+	"io"
+	"net"
+
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/pkg/parsers/filters"
+	"github.com/docker/docker/pkg/ulimit"
+	"github.com/docker/docker/runconfig"
+)
+
+// ContainerAttachOptions holds parameters to attach to a container.
+type ContainerAttachOptions struct {
+	ContainerID string
+	Stream      bool
+	Stdin       bool
+	Stdout      bool
+	Stderr      bool
+}
+
+// ContainerCommitOptions holds parameters to commit changes into a container.
+type ContainerCommitOptions struct {
+	ContainerID    string
+	RepositoryName string
+	Tag            string
+	Comment        string
+	Author         string
+	Changes        []string
+	Pause          bool
+	Config         *runconfig.Config
+}
+
+// ContainerExecInspect holds information returned by exec inspect.
+type ContainerExecInspect struct {
+	ExecID      string
+	ContainerID string
+	Running     bool
+	ExitCode    int
+}
+
+// ContainerListOptions holds parameters to list containers with.
+type ContainerListOptions struct {
+	Quiet  bool
+	Size   bool
+	All    bool
+	Latest bool
+	Since  string
+	Before string
+	Limit  int
+	Filter filters.Args
+}
+
+// ContainerLogsOptions holds parameters to filter logs with.
+type ContainerLogsOptions struct {
+	ContainerID string
+	ShowStdout  bool
+	ShowStderr  bool
+	Since       string
+	Timestamps  bool
+	Follow      bool
+	Tail        string
+}
+
+// ContainerRemoveOptions holds parameters to remove containers.
+type ContainerRemoveOptions struct {
+	ContainerID   string
+	RemoveVolumes bool
+	RemoveLinks   bool
+	Force         bool
+}
+
+// CopyToContainerOptions holds information
+// about files to copy into a container
+type CopyToContainerOptions struct {
+	ContainerID               string
+	Path                      string
+	Content                   io.Reader
+	AllowOverwriteDirWithFile bool
+}
+
+// EventsOptions hold parameters to filter events with.
+type EventsOptions struct {
+	Since   string
+	Until   string
+	Filters filters.Args
+}
+
+// HijackedResponse holds connection information for a hijacked request.
+type HijackedResponse struct {
+	Conn   net.Conn
+	Reader *bufio.Reader
+}
+
+// Close closes the hijacked connection and reader.
+func (h *HijackedResponse) Close() {
+	h.Conn.Close()
+}
+
+// CloseWriter is an interface that implement structs
+// that close input streams to prevent from writing.
+type CloseWriter interface {
+	CloseWrite() error
+}
+
+// CloseWrite closes a readWriter for writing.
+func (h *HijackedResponse) CloseWrite() error {
+	if conn, ok := h.Conn.(CloseWriter); ok {
+		return conn.CloseWrite()
+	}
+	return nil
+}
+
+// ImageBuildOptions holds the information
+// necessary to build images.
+type ImageBuildOptions struct {
+	Tags           []string
+	SuppressOutput bool
+	RemoteContext  string
+	NoCache        bool
+	Remove         bool
+	ForceRemove    bool
+	PullParent     bool
+	Isolation      string
+	CPUSetCPUs     string
+	CPUSetMems     string
+	CPUShares      int64
+	CPUQuota       int64
+	CPUPeriod      int64
+	Memory         int64
+	MemorySwap     int64
+	CgroupParent   string
+	ShmSize        string
+	Dockerfile     string
+	Ulimits        []*ulimit.Ulimit
+	BuildArgs      []string
+	AuthConfigs    map[string]cliconfig.AuthConfig
+	Context        io.Reader
+}
+
+// ImageBuildResponse holds information
+// returned by a server after building
+// an image.
+type ImageBuildResponse struct {
+	Body   io.ReadCloser
+	OSType string
+}
+
+// ImageCreateOptions holds information to create images.
+type ImageCreateOptions struct {
+	// Parent is the image to create this image from
+	Parent string
+	// Tag is the name to tag this image
+	Tag string
+	// RegistryAuth is the base64 encoded credentials for this server
+	RegistryAuth string
+}
+
+// ImageImportOptions holds information to import images from the client host.
+type ImageImportOptions struct {
+	// Source is the data to send to the server to create this image from
+	Source io.Reader
+	// Source is the name of the source to import this image from
+	SourceName string
+	// RepositoryName is the name of the repository to import this image
+	RepositoryName string
+	// Message is the message to tag the image with
+	Message string
+	// Tag is the name to tag this image
+	Tag string
+	// Changes are the raw changes to apply to the image
+	Changes []string
+}
+
+// ImageListOptions holds parameters to filter the list of images with.
+type ImageListOptions struct {
+	MatchName string
+	All       bool
+	Filters   filters.Args
+}
+
+// ImagePullOptions holds information to pull images.
+type ImagePullOptions struct {
+	ImageID string
+	Tag     string
+	// RegistryAuth is the base64 encoded credentials for this server
+	RegistryAuth string
+}
+
+//ImagePushOptions holds information to push images.
+type ImagePushOptions ImagePullOptions
+
+// ImageRemoveOptions holds parameters to remove images.
+type ImageRemoveOptions struct {
+	ImageID       string
+	Force         bool
+	PruneChildren bool
+}
+
+// ImageSearchOptions holds parameters to search images with.
+type ImageSearchOptions struct {
+	Term         string
+	RegistryAuth string
+}
+
+// ImageTagOptions holds parameters to tag an image
+type ImageTagOptions struct {
+	ImageID        string
+	RepositoryName string
+	Tag            string
+	Force          bool
+}
+
+// ResizeOptions holds parameters to resize a tty.
+// It can be used to resize container ttys and
+// exec process ttys too.
+type ResizeOptions struct {
+	ID     string
+	Height int
+	Width  int
+}
+
+// VersionResponse holds version information for the client and the server
+type VersionResponse struct {
+	Client *Version
+	Server *Version
+}
+
+// ServerOK return true when the client could connect to the docker server
+// and parse the information received. It returns false otherwise.
+func (v VersionResponse) ServerOK() bool {
+	return v.Server != nil
+}

+ 17 - 1
cliconfig/config.go

@@ -54,6 +54,15 @@ type AuthConfig struct {
 	RegistryToken string `json:"registrytoken,omitempty"`
 	RegistryToken string `json:"registrytoken,omitempty"`
 }
 }
 
 
+// EncodeToBase64 serializes the auth configuration as JSON base64 payload
+func (a AuthConfig) EncodeToBase64() (string, error) {
+	buf, err := json.Marshal(a)
+	if err != nil {
+		return "", err
+	}
+	return base64.URLEncoding.EncodeToString(buf), nil
+}
+
 // ConfigFile ~/.docker/config.json file info
 // ConfigFile ~/.docker/config.json file info
 type ConfigFile struct {
 type ConfigFile struct {
 	AuthConfigs map[string]AuthConfig `json:"auths"`
 	AuthConfigs map[string]AuthConfig `json:"auths"`
@@ -192,7 +201,14 @@ func Load(configDir string) (*ConfigFile, error) {
 	}
 	}
 	defer file.Close()
 	defer file.Close()
 	err = configFile.LegacyLoadFromReader(file)
 	err = configFile.LegacyLoadFromReader(file)
-	return &configFile, err
+	if err != nil {
+		return &configFile, err
+	}
+
+	if configFile.HTTPHeaders == nil {
+		configFile.HTTPHeaders = map[string]string{}
+	}
+	return &configFile, nil
 }
 }
 
 
 // SaveToWriter encodes and writes out all the authorization information to
 // SaveToWriter encodes and writes out all the authorization information to

+ 11 - 0
integration-cli/docker_cli_inspect_test.go

@@ -356,3 +356,14 @@ func (s *DockerSuite) TestInspectByPrefix(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(id, checker.Equals, id3)
 	c.Assert(id, checker.Equals, id3)
 }
 }
+
+func (s *DockerSuite) TestInspectStopWhenNotFound(c *check.C) {
+	dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top")
+	dockerCmd(c, "run", "--name=not-shown", "-d", "busybox", "top")
+	out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='{{.Name}}'", "busybox", "missing", "not-shown")
+
+	c.Assert(err, checker.Not(check.IsNil))
+	c.Assert(out, checker.Contains, "busybox")
+	c.Assert(out, checker.Not(checker.Contains), "not-shown")
+	c.Assert(out, checker.Contains, "Error: No such container: missing")
+}

+ 2 - 2
integration-cli/docker_cli_logs_test.go

@@ -349,6 +349,6 @@ func (s *DockerSuite) TestLogsFollowGoroutinesNoOutput(c *check.C) {
 func (s *DockerSuite) TestLogsCLIContainerNotFound(c *check.C) {
 func (s *DockerSuite) TestLogsCLIContainerNotFound(c *check.C) {
 	name := "testlogsnocontainer"
 	name := "testlogsnocontainer"
 	out, _, _ := dockerCmdWithError("logs", name)
 	out, _, _ := dockerCmdWithError("logs", name)
-	message := fmt.Sprintf(".*No such container: %s.*\n", name)
-	c.Assert(out, checker.Matches, message)
+	message := fmt.Sprintf("Error: No such container: %s\n", name)
+	c.Assert(out, checker.Equals, message)
 }
 }

+ 1 - 1
integration-cli/docker_cli_network_unix_test.go

@@ -317,7 +317,7 @@ func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
 	c.Assert(exitCode, checker.Equals, 1)
 	c.Assert(exitCode, checker.Equals, 1)
 	c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
 	c.Assert(out, checker.Contains, "Error: No such network: nonexistent")
 	networkResources = []types.NetworkResource{}
 	networkResources = []types.NetworkResource{}
-	inspectOut := strings.SplitN(out, "\n", 2)[1]
+	inspectOut := strings.SplitN(out, "\nError: No such network: nonexistent\n", 2)[0]
 	err = json.Unmarshal([]byte(inspectOut), &networkResources)
 	err = json.Unmarshal([]byte(inspectOut), &networkResources)
 	c.Assert(networkResources, checker.HasLen, 1)
 	c.Assert(networkResources, checker.HasLen, 1)
 
 

+ 6 - 4
integration-cli/docker_cli_volume_test.go

@@ -53,15 +53,17 @@ func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
 func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
 func (s *DockerSuite) TestVolumeCliInspectMulti(c *check.C) {
 	dockerCmd(c, "volume", "create", "--name", "test1")
 	dockerCmd(c, "volume", "create", "--name", "test1")
 	dockerCmd(c, "volume", "create", "--name", "test2")
 	dockerCmd(c, "volume", "create", "--name", "test2")
+	dockerCmd(c, "volume", "create", "--name", "not-shown")
 
 
-	out, _, err := dockerCmdWithError("volume", "inspect", "--format='{{ .Name }}'", "test1", "test2", "doesntexist")
+	out, _, err := dockerCmdWithError("volume", "inspect", "--format='{{ .Name }}'", "test1", "test2", "doesntexist", "not-shown")
 	c.Assert(err, checker.NotNil)
 	c.Assert(err, checker.NotNil)
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
 	c.Assert(len(outArr), check.Equals, 3, check.Commentf("\n%s", out))
 	c.Assert(len(outArr), check.Equals, 3, check.Commentf("\n%s", out))
 
 
-	c.Assert(strings.Contains(out, "test1\n"), check.Equals, true)
-	c.Assert(strings.Contains(out, "test2\n"), check.Equals, true)
-	c.Assert(strings.Contains(out, "Error: No such volume: doesntexist\n"), check.Equals, true)
+	c.Assert(out, checker.Contains, "test1")
+	c.Assert(out, checker.Contains, "test2")
+	c.Assert(out, checker.Contains, "Error: No such volume: doesntexist")
+	c.Assert(out, checker.Not(checker.Contains), "not-shown")
 }
 }
 
 
 func (s *DockerSuite) TestVolumeCliLs(c *check.C) {
 func (s *DockerSuite) TestVolumeCliLs(c *check.C) {