diff --git a/dockerclient.go b/dockerclient.go index cdfd56d..3647938 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 ( @@ -441,29 +444,49 @@ 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, cliOut 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 { req.Header.Add("X-Registry-Auth", auth.encode()) } - 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() + errorReader := io.Reader(resp.Body) + 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 0b57518..205cd64 100644 --- a/dockerclient_test.go +++ b/dockerclient_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "os" "reflect" "strings" "testing" @@ -30,6 +31,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() @@ -49,17 +60,17 @@ func TestKillContainer(t *testing.T) { func TestPullImage(t *testing.T) { client := testDockerClient(t) - err := client.PullImage("busybox", nil) + err := client.PullImage("busybox", nil, nil) 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, nil) if err != nil { - t.Fatal("unable to pull haproxy") + t.Fatal("unable to pull haproxy: %v", err) } - err = client.PullImage("wrongimg", nil) + err = client.PullImage("wrongimg", nil, nil) if err == nil { t.Fatal("should return error when it fails to pull wrongimg") } diff --git a/interface.go b/interface.go index f3d3f14..352f682 100644 --- a/interface.go +++ b/interface.go @@ -32,7 +32,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, cliOut io.Writer) error LoadImage(reader io.Reader) error RemoveContainer(id string, force, volumes bool) error ListImages() ([]*Image, error) diff --git a/mockclient/mock.go b/mockclient/mock.go index 0facc9c..94505de 100644 --- a/mockclient/mock.go +++ b/mockclient/mock.go @@ -101,8 +101,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, cliOut io.Writer) error { + args := client.Mock.Called(name, auth, cliOut) return args.Error(0) }