diff --git a/client/container_attach.go b/client/container_attach.go index 4ed821f..439ec2d 100644 --- a/client/container_attach.go +++ b/client/container_attach.go @@ -10,7 +10,7 @@ import ( // 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) { +func (cli *Client) ContainerAttach(options types.ContainerAttachOptions) (types.HijackedResponse, error) { query := url.Values{} if options.Stream { query.Set("stream", "1") diff --git a/client/exec.go b/client/exec.go new file mode 100644 index 0000000..4b042c7 --- /dev/null +++ b/client/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 +} diff --git a/client/hijack.go b/client/hijack.go index 4c9475b..70ada03 100644 --- a/client/hijack.go +++ b/client/hijack.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "errors" "fmt" - "io" "net" "net/http/httputil" "net/url" @@ -30,15 +29,15 @@ func (c *tlsClientCon) CloseWrite() error { } // postHijacked sends a POST request and hijacks the connection. -func (cli *Client) postHijacked(path string, query url.Values, body io.Reader, headers map[string][]string) (*types.HijackedResponse, error) { +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 nil, err + return types.HijackedResponse{}, err } req, err := cli.newRequest("POST", path, query, bodyEncoded, headers) if err != nil { - return nil, err + return types.HijackedResponse{}, err } req.Host = cli.Addr @@ -48,9 +47,9 @@ func (cli *Client) postHijacked(path string, query url.Values, body io.Reader, h conn, err := dial(cli.Proto, cli.Addr, cli.tlsConfig) if err != nil { if strings.Contains(err.Error(), "connection refused") { - return nil, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") + return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") } - return nil, err + return types.HijackedResponse{}, err } // When we set up a TCP connection for hijack, there could be long periods @@ -71,7 +70,7 @@ func (cli *Client) postHijacked(path string, query url.Values, body io.Reader, h rwc, br := clientconn.Hijack() - return &types.HijackedResponse{rwc, br}, nil + return types.HijackedResponse{rwc, br}, nil } func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) { diff --git a/types/client.go b/types/client.go index dbe299c..8ce653d 100644 --- a/types/client.go +++ b/types/client.go @@ -31,6 +31,14 @@ type ContainerCommitOptions struct { JSONConfig string } +// 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