Merge pull request #19984 from calavera/vendor_engine_api_master

Vendor engine-api with client context changes.
This commit is contained in:
Tibor Vass 2016-02-04 16:20:06 -05:00
commit da58ee42bb
54 changed files with 714 additions and 195 deletions

View file

@ -14,6 +14,8 @@ import (
"runtime"
"strings"
"golang.org/x/net/context"
"github.com/docker/docker/api"
"github.com/docker/docker/builder/dockerignore"
Cli "github.com/docker/docker/cli"
@ -77,8 +79,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
cmd.ParseFlags(args, true)
var (
context io.ReadCloser
err error
ctx io.ReadCloser
err error
)
specifiedContext := cmd.Arg(0)
@ -100,11 +102,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
switch {
case specifiedContext == "-":
context, relDockerfile, err = getContextFromReader(cli.in, *dockerfileName)
ctx, relDockerfile, err = getContextFromReader(cli.in, *dockerfileName)
case urlutil.IsGitURL(specifiedContext):
tempDir, relDockerfile, err = getContextFromGitURL(specifiedContext, *dockerfileName)
case urlutil.IsURL(specifiedContext):
context, relDockerfile, err = getContextFromURL(progBuff, specifiedContext, *dockerfileName)
ctx, relDockerfile, err = getContextFromURL(progBuff, specifiedContext, *dockerfileName)
default:
contextDir, relDockerfile, err = getContextFromLocalDir(specifiedContext, *dockerfileName)
}
@ -121,7 +123,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
contextDir = tempDir
}
if context == nil {
if ctx == nil {
// And canonicalize dockerfile name to a platform-independent one
relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
if err != nil {
@ -159,7 +161,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
includes = append(includes, ".dockerignore", relDockerfile)
}
context, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
Compression: archive.Uncompressed,
ExcludePatterns: excludes,
IncludeFiles: includes,
@ -173,13 +175,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if isTrusted() {
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
// Dockerfile which uses trusted pulls.
context = replaceDockerfileTarWrapper(context, relDockerfile, cli.trustedReference, &resolvedTags)
ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
}
// Setup an upload progress bar
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
var body io.Reader = progress.NewProgressReader(context, progressOutput, 0, "", "Sending build context to Docker daemon")
var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
var memory int64
if *flMemoryString != "" {
@ -235,7 +237,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
AuthConfigs: cli.configFile.AuthConfigs,
}
response, err := cli.client.ImageBuild(options)
response, err := cli.client.ImageBuild(context.Background(), options)
if err != nil {
return err
}

View file

@ -15,6 +15,7 @@ import (
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/term"
"github.com/docker/engine-api/client"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
)
@ -142,12 +143,12 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
verStr = tmpStr
}
clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions)
httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
if err != nil {
return err
}
client, err := client.NewClient(host, verStr, clientTransport, customHeaders)
client, err := client.NewClient(host, verStr, httpClient, customHeaders)
if err != nil {
return err
}
@ -180,16 +181,27 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
return
}
func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) {
func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
if tlsOptions == nil {
return &http.Transport{}, nil
// let the api client configure the default transport.
return nil, nil
}
config, err := tlsconfig.Client(*tlsOptions)
if err != nil {
return nil, err
}
return &http.Transport{
tr := &http.Transport{
TLSClientConfig: config,
}
proto, addr, _, err := client.ParseHost(host)
if err != nil {
return nil, err
}
sockets.ConfigureTransport(tr, proto, addr)
return &http.Client{
Transport: tr,
}, nil
}

View file

@ -7,6 +7,8 @@ import (
"path/filepath"
"strings"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/archive"
flag "github.com/docker/docker/pkg/mflag"
@ -165,7 +167,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
}
content, stat, err := cli.client.CopyFromContainer(srcContainer, srcPath)
content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
if err != nil {
return err
}
@ -292,5 +294,5 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
AllowOverwriteDirWithFile: false,
}
return cli.client.CopyToContainer(options)
return cli.client.CopyToContainer(context.Background(), options)
}

View file

@ -5,6 +5,8 @@ import (
"io"
"os"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/reference"
@ -52,7 +54,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
RegistryAuth: encodedAuth,
}
responseBody, err := cli.client.ImageCreate(options)
responseBody, err := cli.client.ImageCreate(context.Background(), options)
if err != nil {
return err
}

View file

@ -8,6 +8,8 @@ import (
"strings"
"time"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/jsonlog"
@ -48,7 +50,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
Filters: eventFilterArgs,
}
responseBody, err := cli.client.Events(options)
responseBody, err := cli.client.Events(context.Background(), options)
if err != nil {
return err
}

View file

@ -4,6 +4,8 @@ import (
"errors"
"io"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
)
@ -24,7 +26,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
}
responseBody, err := cli.client.ContainerExport(cmd.Arg(0))
responseBody, err := cli.client.ContainerExport(context.Background(), cmd.Arg(0))
if err != nil {
return err
}

View file

@ -5,6 +5,8 @@ import (
"io"
"os"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/jsonmessage"
@ -70,7 +72,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
Changes: changes,
}
responseBody, err := cli.client.ImageImport(options)
responseBody, err := cli.client.ImageImport(context.Background(), options)
if err != nil {
return err
}

View file

@ -4,6 +4,8 @@ import (
"io"
"os"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/jsonmessage"
flag "github.com/docker/docker/pkg/mflag"
@ -30,7 +32,7 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
input = file
}
response, err := cli.client.ImageLoad(input)
response, err := cli.client.ImageLoad(context.Background(), input, true)
if err != nil {
return err
}

View file

@ -4,6 +4,8 @@ import (
"fmt"
"io"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stdcopy"
@ -48,7 +50,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
Follow: *follow,
Tail: *tail,
}
responseBody, err := cli.client.ContainerLogs(options)
responseBody, err := cli.client.ContainerLogs(context.Background(), options)
if err != nil {
return err
}

View file

@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/jsonmessage"
flag "github.com/docker/docker/pkg/mflag"
@ -77,7 +79,7 @@ func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, imageID,
RegistryAuth: encodedAuth,
}
responseBody, err := cli.client.ImagePull(options, requestPrivilege)
responseBody, err := cli.client.ImagePull(context.Background(), options, requestPrivilege)
if err != nil {
return err
}

View file

@ -4,6 +4,8 @@ import (
"errors"
"io"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/jsonmessage"
flag "github.com/docker/docker/pkg/mflag"
@ -70,5 +72,5 @@ func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, imageID,
RegistryAuth: encodedAuth,
}
return cli.client.ImagePush(options, requestPrivilege)
return cli.client.ImagePush(context.Background(), options, requestPrivilege)
}

View file

@ -7,6 +7,8 @@ import (
"runtime"
"strings"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
Cli "github.com/docker/docker/cli"
derr "github.com/docker/docker/errors"
@ -269,7 +271,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
// Autoremove: wait for the container to finish, retrieve
// the exit code and remove the container
if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
return runStartContainerErr(err)
}
if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
@ -279,7 +281,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
// No Autoremove: Simply retrieve the exit code
if !config.Tty {
// In non-TTY mode, we can't detach, so we must wait for container exit
if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
return err
}
} else {

View file

@ -4,6 +4,8 @@ import (
"errors"
"io"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
)
@ -24,7 +26,7 @@ func (cli *DockerCli) CmdSave(args ...string) error {
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
}
responseBody, err := cli.client.ImageSave(cmd.Args())
responseBody, err := cli.client.ImageSave(context.Background(), cmd.Args())
if err != nil {
return err
}

View file

@ -10,6 +10,8 @@ import (
"text/tabwriter"
"time"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/events"
@ -37,7 +39,7 @@ type stats struct {
}
func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
responseBody, err := cli.client.ContainerStats(s.Name, streamStats)
responseBody, err := cli.client.ContainerStats(context.Background(), s.Name, streamStats)
if err != nil {
s.mu.Lock()
s.err = err
@ -195,7 +197,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
options := types.EventsOptions{
Filters: f,
}
resBody, err := cli.client.Events(options)
resBody, err := cli.client.Events(context.Background(), options)
if err != nil {
c <- watch{err: err}
return

View file

@ -4,6 +4,8 @@ import (
"fmt"
"strings"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
)
@ -21,7 +23,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
var errs []string
for _, name := range cmd.Args() {
status, err := cli.client.ContainerWait(name)
status, err := cli.client.ContainerWait(context.Background(), name)
if err != nil {
errs = append(errs, err.Error())
} else {

View file

@ -7,7 +7,7 @@ source 'hack/.vendor-helpers.sh'
# the following lines are in sorted order, FYI
clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
clone git github.com/Microsoft/go-winio 2b085935f02c272e7a1855df6f8fe03029ffcadd
clone git github.com/Microsoft/go-winio eb176a9831c54b88eaf9eb4fbc24b94080d910ad
clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
clone git github.com/go-check/check 11d3bc7aa68e238947792f30573146a3231fc0f1
@ -23,8 +23,8 @@ clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b
clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://github.com/golang/net.git
clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
clone git github.com/docker/go-connections v0.1.2
clone git github.com/docker/engine-api bdbab71ec21209ef56dffdbe42c9d21843c30862
clone git github.com/docker/go-connections v0.1.3
clone git github.com/docker/engine-api 9a940e4ead265e18d4feb9e3c515428966a08278
clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
clone git github.com/imdario/mergo 0.2.1

View file

@ -194,7 +194,7 @@ func (d *Daemon) getClientConfig() (*clientConfig, error) {
transport = &http.Transport{}
}
sockets.ConfigureTCPTransport(transport, proto, addr)
sockets.ConfigureTransport(transport, proto, addr)
return &clientConfig{
transport: transport,

View file

@ -30,7 +30,7 @@ func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
tr.TLSClientConfig = c
protoAndAddr := strings.Split(addr, "://")
sockets.ConfigureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1])
sockets.ConfigureTransport(tr, protoAndAddr[0], protoAndAddr[1])
scheme := protoAndAddr[0]
if scheme != "https" {

View file

@ -65,7 +65,7 @@ func LookupSidByName(name string) (sid string, err error) {
if err != nil {
return "", &AccountLookupError{name, err}
}
sid = syscall.UTF16ToString((*[1 << 30]uint16)(unsafe.Pointer(strBuffer))[:])
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
localFree(uintptr(unsafe.Pointer(strBuffer)))
return sid, nil
}

View file

@ -1,16 +1,14 @@
package client
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/docker/engine-api/client/transport"
"github.com/docker/go-connections/tlsconfig"
)
@ -21,17 +19,13 @@ type Client struct {
proto string
// addr holds the client address.
addr string
// basePath holds the path to prepend to the requests
// 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
// transport is the interface to sends request with, it implements transport.Client.
transport transport.Client
// version of the server to talk to.
version string
// custom http headers configured by users
// custom http headers configured by users.
customHTTPHeaders map[string]string
}
@ -41,7 +35,7 @@ type Client struct {
// Use DOCKER_CERT_PATH to load the tls certificates from.
// Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
func NewEnvClient() (*Client, error) {
var transport *http.Transport
var client *http.Client
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
options := tlsconfig.Options{
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
@ -54,8 +48,10 @@ func NewEnvClient() (*Client, error) {
return nil, err
}
transport = &http.Transport{
TLSClientConfig: tlsc,
client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsc,
},
}
}
@ -63,42 +59,29 @@ func NewEnvClient() (*Client, error) {
if host == "" {
host = DefaultDockerHost
}
return NewClient(host, os.Getenv("DOCKER_API_VERSION"), transport, nil)
return NewClient(host, os.Getenv("DOCKER_API_VERSION"), client, nil)
}
// NewClient initializes a new API client for the given host and API version.
// It won't send any version information if the version number is empty.
// It uses the transport to create a new http client.
// It uses the given http client as transport.
// It also initializes the custom http headers to add to each request.
func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
var (
basePath string
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
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
proto, addr, basePath, err := ParseHost(host)
if err != nil {
return nil, err
}
transport = configureTransport(transport, proto, addr)
if transport.TLSClientConfig != nil {
scheme = "https"
transport, err := transport.NewTransportWithHTTP(proto, addr, client)
if err != nil {
return nil, err
}
return &Client{
proto: proto,
addr: addr,
basePath: basePath,
scheme: scheme,
tlsConfig: transport.TLSClientConfig,
httpClient: &http.Client{Transport: transport},
transport: transport,
version: version,
customHTTPHeaders: httpHeaders,
}, nil
@ -127,23 +110,22 @@ func (cli *Client) ClientVersion() string {
return cli.version
}
func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
if tr == nil {
tr = &http.Transport{}
// ParseHost verifies that the given host strings is valid.
func ParseHost(host string) (string, string, string, error) {
protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 {
return "", "", "", fmt.Errorf("unable to parse docker host `%s`", host)
}
// Why 32? See https://github.com/docker/docker/pull/8035.
timeout := 32 * time.Second
if proto == "unix" {
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, timeout)
var basePath string
proto, addr := protoAddrParts[0], protoAddrParts[1]
if proto == "tcp" {
parsed, err := url.Parse("tcp://" + addr)
if err != nil {
return "", "", "", err
}
} else {
tr.Proxy = http.ProxyFromEnvironment
tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
addr = parsed.Host
basePath = parsed.Path
}
return tr
return proto, addr, basePath, nil
}

View file

@ -1,4 +1,4 @@
// +build linux freebsd
// +build linux freebsd solaris
package client

View file

@ -3,18 +3,20 @@ package client
import (
"io"
"net/url"
"golang.org/x/net/context"
)
// 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) {
func (cli *Client) ContainerStats(ctx context.Context, 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)
resp, err := cli.getWithContext(ctx, "/containers/"+containerID+"/stats", query, nil)
if err != nil {
return nil, err
}

View file

@ -10,6 +10,8 @@ import (
"path/filepath"
"strings"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
)
@ -28,7 +30,7 @@ func (cli *Client) ContainerStatPath(containerID, path string) (types.ContainerP
}
// CopyToContainer copies content into the container filesystem.
func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
func (cli *Client) CopyToContainer(ctx context.Context, 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.
@ -38,7 +40,7 @@ func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
response, err := cli.putRaw(path, query, options.Content, nil)
response, err := cli.putRawWithContext(ctx, path, query, options.Content, nil)
if err != nil {
return err
}
@ -53,12 +55,12 @@ func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
// 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) {
func (cli *Client) CopyFromContainer(ctx context.Context, 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)
response, err := cli.getWithContext(ctx, apiPath, query, nil)
if err != nil {
return nil, types.ContainerPathStat{}, err
}

View file

@ -5,6 +5,8 @@ import (
"net/url"
"time"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
timetypes "github.com/docker/engine-api/types/time"
@ -12,7 +14,7 @@ import (
// 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) {
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error) {
query := url.Values{}
ref := time.Now()
@ -38,7 +40,7 @@ func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
query.Set("filters", filterJSON)
}
serverResponse, err := cli.get("/events", query, nil)
serverResponse, err := cli.getWithContext(ctx, "/events", query, nil)
if err != nil {
return nil, err
}

View file

@ -3,13 +3,15 @@ package client
import (
"io"
"net/url"
"golang.org/x/net/context"
)
// 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)
func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) {
serverResp, err := cli.getWithContext(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)
if err != nil {
return nil, err
}

View file

@ -11,6 +11,7 @@ import (
"time"
"github.com/docker/engine-api/types"
"github.com/docker/go-connections/sockets"
)
// tlsClientCon holds tls information and a dialed connection.
@ -44,7 +45,7 @@ func (cli *Client) postHijacked(path string, query url.Values, body interface{},
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", "tcp")
conn, err := dial(cli.proto, cli.addr, cli.tlsConfig)
conn, err := dial(cli.proto, cli.addr, cli.transport.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?")
@ -156,9 +157,12 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
}
func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
if tlsConfig != nil && proto != "unix" {
if tlsConfig != nil && proto != "unix" && proto != "npipe" {
// Notice this isn't Go standard's tls.Dial function
return tlsDial(proto, addr, tlsConfig)
}
if proto == "npipe" {
return sockets.DialPipe(addr, 32*time.Second)
}
return net.Dial(proto, addr)
}

View file

@ -9,6 +9,8 @@ import (
"strconv"
"strings"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/container"
)
@ -18,7 +20,7 @@ var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
// 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) {
func (cli *Client) ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
query, err := imageBuildOptionsToQuery(options)
if err != nil {
return types.ImageBuildResponse{}, err
@ -32,7 +34,7 @@ func (cli *Client) ImageBuild(options types.ImageBuildOptions) (types.ImageBuild
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
headers.Set("Content-Type", "application/tar")
serverResp, err := cli.postRaw("/build", query, options.Context, headers)
serverResp, err := cli.postRaw(ctx, "/build", query, options.Context, headers)
if err != nil {
return types.ImageBuildResponse{}, err
}

View file

@ -4,23 +4,25 @@ import (
"io"
"net/url"
"golang.org/x/net/context"
"github.com/docker/engine-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) {
func (cli *Client) ImageCreate(ctx context.Context, 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)
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if err != nil {
return nil, err
}
return resp.body, nil
}
func (cli *Client) tryImageCreate(query url.Values, registryAuth string) (*serverResponse, error) {
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (*serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
return cli.post("/images/create", query, nil, headers)
return cli.postWithContext(ctx, "/images/create", query, nil, headers)
}

View file

@ -4,12 +4,14 @@ import (
"io"
"net/url"
"golang.org/x/net/context"
"github.com/docker/engine-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) {
func (cli *Client) ImageImport(ctx context.Context, options types.ImageImportOptions) (io.ReadCloser, error) {
query := url.Values{}
query.Set("fromSrc", options.SourceName)
query.Set("repo", options.RepositoryName)
@ -19,7 +21,7 @@ func (cli *Client) ImageImport(options types.ImageImportOptions) (io.ReadCloser,
query.Add("changes", change)
}
resp, err := cli.postRaw("/images/create", query, options.Source, nil)
resp, err := cli.postRaw(ctx, "/images/create", query, options.Source, nil)
if err != nil {
return nil, err
}

View file

@ -4,14 +4,21 @@ import (
"io"
"net/url"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
)
// 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) (types.ImageLoadResponse, error) {
resp, err := cli.postRaw("/images/load", url.Values{}, input, nil)
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
v := url.Values{}
v.Set("quiet", "0")
if quiet {
v.Set("quiet", "1")
}
resp, err := cli.postRaw(ctx, "/images/load", v, input, nil)
if err != nil {
return types.ImageLoadResponse{}, err
}

View file

@ -5,6 +5,8 @@ import (
"net/http"
"net/url"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
)
@ -12,20 +14,20 @@ import (
// 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) {
func (cli *Client) ImagePull(ctx context.Context, 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)
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized {
newAuthHeader, privilegeErr := privilegeFunc()
if privilegeErr != nil {
return nil, privilegeErr
}
resp, err = cli.tryImageCreate(query, newAuthHeader)
resp, err = cli.tryImageCreate(ctx, query, newAuthHeader)
}
if err != nil {
return nil, err

View file

@ -5,6 +5,8 @@ import (
"net/http"
"net/url"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
)
@ -12,17 +14,17 @@ import (
// 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) {
func (cli *Client) ImagePush(ctx context.Context, 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)
resp, err := cli.tryImagePush(ctx, 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)
resp, err = cli.tryImagePush(ctx, options.ImageID, query, newAuthHeader)
}
if err != nil {
return nil, err
@ -30,7 +32,7 @@ func (cli *Client) ImagePush(options types.ImagePushOptions, privilegeFunc Reque
return resp.body, nil
}
func (cli *Client) tryImagePush(imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
func (cli *Client) tryImagePush(ctx context.Context, 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)
return cli.postWithContext(ctx, "/images/"+imageID+"/push", query, nil, headers)
}

View file

@ -3,16 +3,18 @@ package client
import (
"io"
"net/url"
"golang.org/x/net/context"
)
// 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) {
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error) {
query := url.Values{
"names": imageIDs,
}
resp, err := cli.get("/images/get", query, nil)
resp, err := cli.getWithContext(ctx, "/images/get", query, nil)
if err != nil {
return nil, err
}

View file

@ -3,6 +3,8 @@ package client
import (
"io"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/container"
"github.com/docker/engine-api/types/filters"
@ -22,40 +24,40 @@ type APIClient interface {
ContainerExecInspect(execID string) (types.ContainerExecInspect, error)
ContainerExecResize(options types.ResizeOptions) error
ContainerExecStart(execID string, config types.ExecStartCheck) error
ContainerExport(containerID string) (io.ReadCloser, error)
ContainerExport(ctx context.Context, 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)
ContainerLogs(ctx context.Context, 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)
ContainerStats(ctx context.Context, 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
ContainerUpdate(containerID string, updateConfig container.UpdateConfig) 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)
ContainerWait(ctx context.Context, containerID string) (int, error)
CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(ctx context.Context, options types.CopyToContainerOptions) error
Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error)
ImageBuild(ctx context.Context, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
ImageCreate(ctx context.Context, options types.ImageCreateOptions) (io.ReadCloser, error)
ImageHistory(imageID string) ([]types.ImageHistory, error)
ImageImport(options types.ImageImportOptions) (io.ReadCloser, error)
ImageImport(ctx context.Context, 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) (types.ImageLoadResponse, error)
ImagePull(options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImagePush(options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error)
ImagePull(ctx context.Context, options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImagePush(ctx context.Context, options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error)
ImageRemove(options types.ImageRemoveOptions) ([]types.ImageDelete, error)
ImageSearch(options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error)
ImageSave(imageIDs []string) (io.ReadCloser, error)
ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error)
ImageTag(options types.ImageTagOptions) error
Info() (types.Info, error)
NetworkConnect(networkID, containerID string, config *network.EndpointSettings) error

View file

@ -5,13 +5,15 @@ import (
"net/url"
"time"
"golang.org/x/net/context"
"github.com/docker/engine-api/types"
timetypes "github.com/docker/engine-api/types/time"
)
// 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) {
func (cli *Client) ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error) {
query := url.Values{}
if options.ShowStdout {
query.Set("stdout", "1")
@ -38,7 +40,7 @@ func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadClo
}
query.Set("tail", options.Tail)
resp, err := cli.get("/containers/"+options.ContainerID+"/logs", query, nil)
resp, err := cli.getWithContext(ctx, "/containers/"+options.ContainerID+"/logs", query, nil)
if err != nil {
return nil, err
}

View file

@ -9,6 +9,10 @@ import (
"net/http"
"net/url"
"strings"
"github.com/docker/engine-api/client/transport/cancellable"
"golang.org/x/net/context"
)
// serverResponse is a wrapper for http API responses.
@ -20,40 +24,55 @@ type serverResponse struct {
// 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)
return cli.sendRequest(context.Background(), "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)
return cli.getWithContext(context.Background(), path, query, headers)
}
// getWithContext sends an http request to the docker API using the method GET with a specific go context.
func (cli *Client) getWithContext(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
return cli.sendRequest(ctx, "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)
return cli.postWithContext(context.Background(), 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)
// postWithContext sends an http request to the docker API using the method POST with a specific go context.
func (cli *Client) postWithContext(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
return cli.sendRequest(ctx, "POST", path, query, body, headers)
}
// postRaw sends the raw input to the docker API using the method POST with a specific go context.
func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
return cli.sendClientRequest(ctx, "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)
return cli.sendRequest(context.Background(), "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)
return cli.putRawWithContext(context.Background(), path, query, body, headers)
}
// putRawWithContext sends the raw input to the docker API using the method PUT with a specific go context.
func (cli *Client) putRawWithContext(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
return cli.sendClientRequest(ctx, "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)
return cli.sendRequest(context.Background(), "DELETE", path, query, nil, headers)
}
func (cli *Client) sendRequest(method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
params, err := encodeData(body)
if err != nil {
return nil, err
@ -66,10 +85,10 @@ func (cli *Client) sendRequest(method, path string, query url.Values, body inter
headers["Content-Type"] = []string{"application/json"}
}
return cli.sendClientRequest(method, path, query, params, headers)
return cli.sendClientRequest(ctx, method, path, query, params, headers)
}
func (cli *Client) sendClientRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
serverResp := &serverResponse{
body: nil,
statusCode: -1,
@ -82,13 +101,13 @@ func (cli *Client) sendClientRequest(method, path string, query url.Values, body
req, err := cli.newRequest(method, path, query, body, headers)
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme
req.URL.Scheme = cli.transport.Scheme()
if expectedPayload && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "text/plain")
}
resp, err := cli.httpClient.Do(req)
resp, err := cancellable.Do(ctx, cli.transport, req)
if resp != nil {
serverResp.statusCode = resp.StatusCode
}
@ -98,10 +117,10 @@ func (cli *Client) sendClientRequest(method, path string, query url.Values, body
return serverResp, ErrConnectionFailed
}
if cli.scheme == "http" && strings.Contains(err.Error(), "malformed HTTP response") {
if !cli.transport.Secure() && 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") {
if cli.transport.Secure() && 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)
}

View file

@ -0,0 +1,23 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.5
package cancellable
import (
"net/http"
"github.com/docker/engine-api/client/transport"
)
func canceler(client transport.Sender, req *http.Request) func() {
// TODO(djd): Respect any existing value of req.Cancel.
ch := make(chan struct{})
req.Cancel = ch
return func() {
close(ch)
}
}

View file

@ -0,0 +1,27 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.5
package cancellable
import (
"net/http"
"github.com/docker/engine-api/client/transport"
)
type requestCanceler interface {
CancelRequest(*http.Request)
}
func canceler(client transport.Sender, req *http.Request) func() {
rc, ok := client.(requestCanceler)
if !ok {
return func() {}
}
return func() {
rc.CancelRequest(req)
}
}

View file

@ -0,0 +1,113 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package cancellable provides helper function to cancel http requests.
package cancellable
import (
"io"
"net/http"
"github.com/docker/engine-api/client/transport"
"golang.org/x/net/context"
)
func nop() {}
var (
testHookContextDoneBeforeHeaders = nop
testHookDoReturned = nop
testHookDidBodyClose = nop
)
// Do sends an HTTP request with the provided transport.Sender and returns an HTTP response.
// If the client is nil, http.DefaultClient is used.
// If the context is canceled or times out, ctx.Err() will be returned.
//
// FORK INFORMATION:
//
// This function deviates from the upstream version in golang.org/x/net/context/ctxhttp by
// taking a Sender interface rather than a *http.Client directly. That allow us to use
// this funcion with mocked clients and hijacked connections.
func Do(ctx context.Context, client transport.Sender, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
// Request cancelation changed in Go 1.5, see canceler.go and canceler_go14.go.
cancel := canceler(client, req)
type responseAndError struct {
resp *http.Response
err error
}
result := make(chan responseAndError, 1)
go func() {
resp, err := client.Do(req)
testHookDoReturned()
result <- responseAndError{resp, err}
}()
var resp *http.Response
select {
case <-ctx.Done():
testHookContextDoneBeforeHeaders()
cancel()
// Clean up after the goroutine calling client.Do:
go func() {
if r := <-result; r.resp != nil && r.resp.Body != nil {
testHookDidBodyClose()
r.resp.Body.Close()
}
}()
return nil, ctx.Err()
case r := <-result:
var err error
resp, err = r.resp, r.err
if err != nil {
return resp, err
}
}
c := make(chan struct{})
go func() {
select {
case <-ctx.Done():
cancel()
case <-c:
// The response's Body is closed.
}
}()
resp.Body = &notifyingReader{resp.Body, c}
return resp, nil
}
// notifyingReader is an io.ReadCloser that closes the notify channel after
// Close is called or a Read fails on the underlying ReadCloser.
type notifyingReader struct {
io.ReadCloser
notify chan<- struct{}
}
func (r *notifyingReader) Read(p []byte) (int, error) {
n, err := r.ReadCloser.Read(p)
if err != nil && r.notify != nil {
close(r.notify)
r.notify = nil
}
return n, err
}
func (r *notifyingReader) Close() error {
err := r.ReadCloser.Close()
if r.notify != nil {
close(r.notify)
r.notify = nil
}
return err
}

View file

@ -0,0 +1,47 @@
package transport
import (
"crypto/tls"
"net/http"
)
// Sender is an interface that clients must implement
// to be able to send requests to a remote connection.
type Sender interface {
// Do sends request to a remote endpoint.
Do(*http.Request) (*http.Response, error)
}
// Client is an interface that abstracts all remote connections.
type Client interface {
Sender
// Secure tells whether the connection is secure or not.
Secure() bool
// Scheme returns the connection protocol the client uses.
Scheme() string
// TLSConfig returns any TLS configuration the client uses.
TLSConfig() *tls.Config
}
// tlsInfo returns information about the TLS configuration.
type tlsInfo struct {
tlsConfig *tls.Config
}
// TLSConfig returns the TLS configuration.
func (t *tlsInfo) TLSConfig() *tls.Config {
return t.tlsConfig
}
// Scheme returns protocol scheme to use.
func (t *tlsInfo) Scheme() string {
if t.tlsConfig != nil {
return "https"
}
return "http"
}
// Secure returns true if there is a TLS configuration.
func (t *tlsInfo) Secure() bool {
return t.tlsConfig != nil
}

View file

@ -0,0 +1,26 @@
// +build test
package transport
import (
"crypto/tls"
"net/http"
)
type mockClient struct {
*tlsInfo
do func(*http.Request) (*http.Response, error)
}
// NewMockClient returns a mocked client that runs the function supplied as `client.Do` call
func NewMockClient(tlsConfig *tls.Config, doer func(*http.Request) (*http.Response, error)) Client {
return mockClient{
tlsInfo: &tlsInfo{tlsConfig},
do: doer,
}
}
// Do executes the supplied function for the mock.
func (m mockClient) Do(req *http.Request) (*http.Response, error) {
return m.do(req)
}

View file

@ -0,0 +1,57 @@
// Package transport provides function to send request to remote endpoints.
package transport
import (
"fmt"
"net/http"
"github.com/docker/go-connections/sockets"
)
// apiTransport holds information about the http transport to connect with the API.
type apiTransport struct {
*http.Client
*tlsInfo
transport *http.Transport
}
// NewTransportWithHTTP creates a new transport based on the provided proto, address and http client.
// It uses Docker's default http transport configuration if the client is nil.
// It does not modify the client's transport if it's not nil.
func NewTransportWithHTTP(proto, addr string, client *http.Client) (Client, error) {
var transport *http.Transport
if client != nil {
tr, ok := client.Transport.(*http.Transport)
if !ok {
return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", client.Transport)
}
transport = tr
} else {
transport = defaultTransport(proto, addr)
client = &http.Client{
Transport: transport,
}
}
return &apiTransport{
Client: client,
tlsInfo: &tlsInfo{transport.TLSClientConfig},
transport: transport,
}, nil
}
// CancelRequest stops a request execution.
func (a *apiTransport) CancelRequest(req *http.Request) {
a.transport.CancelRequest(req)
}
// defaultTransport creates a new http.Transport with Docker's
// default transport configuration.
func defaultTransport(proto, addr string) *http.Transport {
tr := new(http.Transport)
sockets.ConfigureTransport(tr, proto, addr)
return tr
}
var _ Client = &apiTransport{}

View file

@ -3,13 +3,15 @@ package client
import (
"encoding/json"
"golang.org/x/net/context"
"github.com/docker/engine-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)
func (cli *Client) ContainerWait(ctx context.Context, containerID string) (int, error) {
resp, err := cli.postWithContext(ctx, "/containers/"+containerID+"/wait", nil, nil, nil)
if err != nil {
return -1, err
}

View file

@ -72,12 +72,6 @@ func (n NetworkMode) IsUserDefined() bool {
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
}
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
func IsPreDefinedNetwork(network string) bool {
n := NetworkMode(network)
return n.IsBridge() || n.IsHost() || n.IsNone()
}
//UserDefined indicates user-created network
func (n NetworkMode) UserDefined() string {
if n.IsUserDefined() {

View file

@ -49,11 +49,6 @@ func (n NetworkMode) NetworkName() string {
return ""
}
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
func IsPreDefinedNetwork(network string) bool {
return false
}
// ValidateNetMode ensures that the various combinations of requested
// network settings are valid.
func ValidateNetMode(c *Config, hc *HostConfig) error {

View file

@ -387,6 +387,7 @@ type NetworkResource struct {
ID string `json:"Id"`
Scope string
Driver string
EnableIPv6 bool
IPAM network.IPAM
Internal bool
Containers map[string]EndpointResource
@ -407,6 +408,7 @@ type NetworkCreate struct {
Name string
CheckDuplicate bool
Driver string
EnableIPv6 bool
IPAM network.IPAM
Internal bool
Options map[string]string

View file

@ -0,0 +1,89 @@
package sockets
import (
"errors"
"net"
"sync"
)
var errClosed = errors.New("use of closed network connection")
// InmemSocket implements net.Listener using in-memory only connections.
type InmemSocket struct {
chConn chan net.Conn
chClose chan struct{}
addr string
mu sync.Mutex
}
// dummyAddr is used to satisfy net.Addr for the in-mem socket
// it is just stored as a string and returns the string for all calls
type dummyAddr string
// NewInmemSocket creates an in-memory only net.Listener
// The addr argument can be any string, but is used to satisfy the `Addr()` part
// of the net.Listener interface
func NewInmemSocket(addr string, bufSize int) *InmemSocket {
return &InmemSocket{
chConn: make(chan net.Conn, bufSize),
chClose: make(chan struct{}),
addr: addr,
}
}
// Addr returns the socket's addr string to satisfy net.Listener
func (s *InmemSocket) Addr() net.Addr {
return dummyAddr(s.addr)
}
// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.
func (s *InmemSocket) Accept() (net.Conn, error) {
select {
case conn := <-s.chConn:
return conn, nil
case <-s.chClose:
return nil, errClosed
}
}
// Close closes the listener. It will be unavailable for use once closed.
func (s *InmemSocket) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
select {
case <-s.chClose:
default:
close(s.chClose)
}
return nil
}
// Dial is used to establish a connection with the in-mem server
func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) {
srvConn, clientConn := net.Pipe()
select {
case s.chConn <- srvConn:
case <-s.chClose:
return nil, errClosed
}
return clientConn, nil
}
// Network returns the addr string, satisfies net.Addr
func (a dummyAddr) Network() string {
return string(a)
}
// String returns the string form
func (a dummyAddr) String() string {
return string(a)
}
// timeoutError is used when there is a timeout with a connection
// this implements the net.Error interface
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }

View file

@ -0,0 +1,35 @@
// Package sockets provides helper functions to create and configure Unix or TCP sockets.
package sockets
import (
"net"
"net/http"
"time"
)
// Why 32? See https://github.com/docker/docker/pull/8035.
const defaulTimeout = 32 * time.Second
// ConfigureTransport configures the specified Transport according to the
// specified proto and addr.
// If the proto is unix (using a unix socket to communicate) the compression
// is disabled.
func ConfigureTransport(tr *http.Transport, proto, addr string) {
switch proto {
case "unix":
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, defaulTimeout)
}
case "npipe":
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return DialPipe(addr, defaulTimeout)
}
default:
tr.Proxy = http.ProxyFromEnvironment
tr.Dial = (&net.Dialer{Timeout: defaulTimeout}).Dial
}
}

View file

@ -0,0 +1,15 @@
// +build !windows
package sockets
import (
"net"
"syscall"
"time"
)
// DialPipe connects to a Windows named pipe.
// This is not supported on other OSes.
func DialPipe(_ string, _ time.Duration) (net.Conn, error) {
return nil, syscall.EAFNOSUPPORT
}

View file

@ -0,0 +1,13 @@
package sockets
import (
"net"
"time"
"github.com/Microsoft/go-winio"
)
// DialPipe connects to a Windows named pipe.
func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
return winio.DialPipe(addr, &timeout)
}

View file

@ -4,8 +4,6 @@ package sockets
import (
"crypto/tls"
"net"
"net/http"
"time"
)
// NewTCPSocket creates a TCP socket listener with the specified address and
@ -22,22 +20,3 @@ func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) {
}
return l, nil
}
// ConfigureTCPTransport configures the specified Transport according to the
// specified proto and addr.
// If the proto is unix (using a unix socket to communicate) the compression
// is disabled.
func ConfigureTCPTransport(tr *http.Transport, proto, addr string) {
// Why 32? See https://github.com/docker/docker/pull/8035.
timeout := 32 * time.Second
if proto == "unix" {
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, timeout)
}
} else {
tr.Proxy = http.ProxyFromEnvironment
tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
}
}

View file

@ -41,12 +41,6 @@ var acceptedCBCCiphers = []uint16{
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}
// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
var clientCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}
// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
// options struct but wants to use a commonly accepted set of TLS cipher suites, with
// known weak algorithms removed.
@ -91,7 +85,7 @@ func certPool(caFile string) (*x509.CertPool, error) {
func Client(options Options) (*tls.Config, error) {
tlsConfig := ClientDefault
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
if !options.InsecureSkipVerify {
if !options.InsecureSkipVerify && options.CAFile != "" {
CAs, err := certPool(options.CAFile)
if err != nil {
return nil, err
@ -99,7 +93,7 @@ func Client(options Options) (*tls.Config, error) {
tlsConfig.RootCAs = CAs
}
if options.CertFile != "" && options.KeyFile != "" {
if options.CertFile != "" || options.KeyFile != "" {
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
if err != nil {
return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)

View file

@ -0,0 +1,17 @@
// +build go1.5
// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
//
package tlsconfig
import (
"crypto/tls"
)
// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
var clientCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}

View file

@ -0,0 +1,15 @@
// +build !go1.5
// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
//
package tlsconfig
import (
"crypto/tls"
)
// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
var clientCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}