diff --git a/dockerclient.go b/dockerclient.go index c4302d5..7e900c7 100644 --- a/dockerclient.go +++ b/dockerclient.go @@ -14,6 +14,9 @@ import ( "strings" "sync/atomic" "time" + + "github.com/docker/docker/pkg/jsonmessage" + "github.com/docker/docker/pkg/term" ) const ( @@ -525,11 +528,11 @@ func (client *DockerClient) Version() (*Version, error) { return version, nil } -func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { +func (client *DockerClient) PullImage(name string, auth *AuthConfig, out ...io.Writer) (err error) { v := url.Values{} v.Set("fromImage", name) uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode()) - req, err := http.NewRequest("POST", client.URL.String()+uri, nil) + req, _ := http.NewRequest("POST", client.URL.String()+uri, nil) if auth != nil { encoded_auth, err := auth.encode() if err != nil { @@ -537,9 +540,10 @@ func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { } req.Header.Add("X-Registry-Auth", encoded_auth) } - resp, err := client.HTTPClient.Do(req) + var resp *http.Response + resp, err = client.HTTPClient.Do(req) if err != nil { - return err + return } defer resp.Body.Close() @@ -554,16 +558,39 @@ func (client *DockerClient) PullImage(name string, auth *AuthConfig) error { return fmt.Errorf("%s", string(data)) } + errorReader := io.Reader(resp.Body) + var cliOut io.Writer + if len(out) > 0 { + cliOut = out[0] + } + if cliOut != nil { + pipeReader, pipeWriter := io.Pipe() + streamErrChan := make(chan error) + defer func() { + pipeWriter.Close() + if err == nil { + err = <-streamErrChan + } + }() + errorReader = io.TeeReader(resp.Body, pipeWriter) + go func() { + fd, isTerminalIn := term.GetFdInfo(cliOut) + streamErrChan <- jsonmessage.DisplayJSONMessagesStream(pipeReader, cliOut, fd, isTerminalIn) + }() + } var finalObj map[string]interface{} - for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) { + for decoder := json.NewDecoder(errorReader); err == nil; err = decoder.Decode(&finalObj) { } if err != io.EOF { - return err + return + } else { + err = nil } - if err, ok := finalObj["error"]; ok { - return fmt.Errorf("%v", err) + if errObj, ok := finalObj["error"]; ok { + err = fmt.Errorf("%v", errObj) + return } - return nil + return } func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) { diff --git a/dockerclient_test.go b/dockerclient_test.go index afc1b07..1ca0be3 100644 --- a/dockerclient_test.go +++ b/dockerclient_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "os" "reflect" "strings" "testing" @@ -31,6 +32,16 @@ func testDockerClient(t *testing.T) *DockerClient { return client } +func ExampleDockerClient_PullImage() { + docker, err := NewDockerClient("unix:///var/run/docker.sock", nil) + if err != nil { + panic(err) + } + if err := docker.PullImage("busybox", nil, os.Stdout); err != nil { + panic(err) + } +} + func TestInfo(t *testing.T) { client := testDockerClient(t) info, err := client.Info() @@ -70,14 +81,14 @@ func TestWait(t *testing.T) { func TestPullImage(t *testing.T) { client := testDockerClient(t) - err := client.PullImage("busybox", nil) + err := client.PullImage("busybox", nil) // old clients do not break on the API change if err != nil { - t.Fatal("unable to pull busybox") + t.Fatal("unable to pull busybox: %v", err) } - err = client.PullImage("haproxy", nil) + err = client.PullImage("haproxy", nil, os.Stderr) // io.Writer is optional if err != nil { - t.Fatal("unable to pull haproxy") + t.Fatal("unable to pull haproxy: %v", err) } err = client.PullImage("wrongimg", nil) diff --git a/interface.go b/interface.go index 4215ca4..c67c9a3 100644 --- a/interface.go +++ b/interface.go @@ -35,7 +35,7 @@ type Client interface { StopAllMonitorStats() TagImage(nameOrID string, repo string, tag string, force bool) error Version() (*Version, error) - PullImage(name string, auth *AuthConfig) error + PullImage(name string, auth *AuthConfig, out ...io.Writer) error LoadImage(reader io.Reader) error RemoveContainer(id string, force, volumes bool) error ListImages(all bool) ([]*Image, error) diff --git a/mockclient/mock.go b/mockclient/mock.go index 9944512..1b80954 100644 --- a/mockclient/mock.go +++ b/mockclient/mock.go @@ -106,8 +106,8 @@ func (client *MockClient) Version() (*dockerclient.Version, error) { return args.Get(0).(*dockerclient.Version), args.Error(1) } -func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig) error { - args := client.Mock.Called(name, auth) +func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig, out ...io.Writer) error { + args := client.Mock.Called(name, auth, out...) return args.Error(0) } diff --git a/nopclient/nop.go b/nopclient/nop.go index ef64536..7083b77 100644 --- a/nopclient/nop.go +++ b/nopclient/nop.go @@ -94,7 +94,7 @@ func (client *NopClient) Version() (*dockerclient.Version, error) { return nil, ErrNoEngine } -func (client *NopClient) PullImage(name string, auth *dockerclient.AuthConfig) error { +func (client *NopClient) PullImage(name string, auth *dockerclient.AuthConfig, out ...io.Writer) error { return ErrNoEngine }