diff --git a/third_party/src/github.com/fsouza/go-dockerclient/AUTHORS b/third_party/src/github.com/fsouza/go-dockerclient/AUTHORS index 52fa6cfd178..cd6f48208be 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/AUTHORS +++ b/third_party/src/github.com/fsouza/go-dockerclient/AUTHORS @@ -4,6 +4,7 @@ Andrews Medina Andy Goldstein Ben McCann Cezar Sa Espinola +Cheah Chu Yeow cheneydeng Ed Eric Anderson @@ -14,6 +15,7 @@ Jean-Baptiste Dalido Jeff Mitchell Jeffrey Hulten Lucas Clemente +Omeid Matten Paul Morie Peter Jihoon Kim Philippe Lafoucrière diff --git a/third_party/src/github.com/fsouza/go-dockerclient/client.go b/third_party/src/github.com/fsouza/go-dockerclient/client.go index aa5cf05a67a..59447e98bb4 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/client.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/client.go @@ -12,7 +12,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/fsouza/go-dockerclient/utils" "io" "io/ioutil" "net" @@ -23,6 +22,8 @@ import ( "strconv" "strings" "sync" + + "github.com/fsouza/go-dockerclient/utils" ) const userAgent = "go-dockerclient" @@ -33,32 +34,194 @@ var ( // ErrConnectionRefused is returned when the client cannot connect to the given endpoint. ErrConnectionRefused = errors.New("cannot connect to Docker endpoint") + + apiVersion_1_12, _ = NewApiVersion("1.12") ) +// ApiVersion is an internal representation of a version of the Remote API. +type ApiVersion []int + +// NewApiVersion returns an instance of ApiVersion for the given string. +// +// The given string must be in the form .., where , +// and are integer numbers. +func NewApiVersion(input string) (ApiVersion, error) { + if !strings.Contains(input, ".") { + return nil, fmt.Errorf("Unable to parse version '%s'", input) + } + + arr := strings.Split(input, ".") + ret := make(ApiVersion, len(arr)) + + var err error + for i, val := range arr { + ret[i], err = strconv.Atoi(val) + if err != nil { + return nil, err + } + } + + return ret, nil +} + +func (version ApiVersion) String() string { + var str string + for i, val := range version { + str += strconv.Itoa(val) + if i < len(version)-1 { + str += "." + } + } + return str +} + +func (version ApiVersion) LessThan(other ApiVersion) bool { + return version.compare(other) < 0 +} + +func (version ApiVersion) LessThanOrEqualTo(other ApiVersion) bool { + return version.compare(other) <= 0 +} + +func (version ApiVersion) GreaterThan(other ApiVersion) bool { + return version.compare(other) > 0 +} + +func (version ApiVersion) GreaterThanOrEqualTo(other ApiVersion) bool { + return version.compare(other) >= 0 +} + +func (version ApiVersion) compare(other ApiVersion) int { + for i, v := range version { + if i <= len(other)-1 { + otherVersion := other[i] + + if v < otherVersion { + return -1 + } else if v > otherVersion { + return 1 + } + } + } + if len(version) > len(other) { + return 1 + } + if len(version) < len(other) { + return -1 + } + return 0 +} + // Client is the basic type of this package. It provides methods for // interaction with the API. type Client struct { - endpoint string - endpointURL *url.URL - eventMonitor *eventMonitoringState - client *http.Client + SkipServerVersionCheck bool + + endpoint string + endpointURL *url.URL + eventMonitor *eventMonitoringState + client *http.Client + requestedApiVersion ApiVersion + serverApiVersion ApiVersion + expectedApiVersion ApiVersion } -// NewClient returns a Client instance ready for communication with the -// given server endpoint. +// NewClient returns a Client instance ready for communication with the given +// server endpoint. It will use the latest remote API version available in the +// server. func NewClient(endpoint string) (*Client, error) { + client, err := NewVersionedClient(endpoint, "") + if err != nil { + return nil, err + } + client.SkipServerVersionCheck = true + return client, nil +} + +// NewVersionedClient returns a Client instance ready for communication with +// the given server endpoint, using a specific remote API version. +func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) { u, err := parseEndpoint(endpoint) if err != nil { return nil, err } + + var requestedApiVersion ApiVersion + if strings.Contains(apiVersionString, ".") { + requestedApiVersion, err = NewApiVersion(apiVersionString) + if err != nil { + return nil, err + } + } + return &Client{ - endpoint: endpoint, - endpointURL: u, - client: http.DefaultClient, - eventMonitor: new(eventMonitoringState), + endpoint: endpoint, + endpointURL: u, + client: http.DefaultClient, + eventMonitor: new(eventMonitoringState), + requestedApiVersion: requestedApiVersion, }, nil } +func (c *Client) checkApiVersion() error { + serverApiVersionString, err := c.getServerApiVersionString() + if err != nil { + return err + } + c.serverApiVersion, err = NewApiVersion(serverApiVersionString) + if err != nil { + return err + } + if c.requestedApiVersion == nil { + c.expectedApiVersion = c.serverApiVersion + } else { + c.expectedApiVersion = c.requestedApiVersion + } + return nil +} + +func parseApiVersionString(input string) (version uint16, err error) { + version = 0 + + if !strings.Contains(input, ".") { + return 0, fmt.Errorf("Unable to parse version '%s'", input) + } + + arr := strings.Split(input, ".") + + major, err := strconv.Atoi(arr[0]) + if err != nil { + return version, err + } + + minor, err := strconv.Atoi(arr[1]) + if err != nil { + return version, err + } + + version = uint16(major)<<8 | uint16(minor) + return version, nil +} + +func (c *Client) getServerApiVersionString() (version string, err error) { + body, status, err := c.do("GET", "/version", nil) + if err != nil { + return "", err + } + if status != http.StatusOK { + return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", status) + } + + var versionResponse map[string]string + err = json.Unmarshal(body, &versionResponse) + if err != nil { + return "", err + } + + version = versionResponse["ApiVersion"] + return version, nil +} + func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) { var params io.Reader if data != nil { @@ -68,6 +231,14 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) } params = bytes.NewBuffer(buf) } + + if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil { + err := c.checkApiVersion() + if err != nil { + return nil, -1, err + } + } + req, err := http.NewRequest(method, c.getURL(path), params) if err != nil { return nil, -1, err @@ -88,6 +259,9 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) } clientconn := httputil.NewClientConn(dial, nil) resp, err = clientconn.Do(req) + if err != nil { + return nil, -1, err + } defer clientconn.Close() } else { resp, err = c.client.Do(req) @@ -113,6 +287,13 @@ func (c *Client) stream(method, path string, headers map[string]string, in io.Re if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader(nil) } + + if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil { + err := c.checkApiVersion() + if err != nil { + return err + } + } req, err := http.NewRequest(method, c.getURL(path), in) if err != nil { return err @@ -183,7 +364,13 @@ func (c *Client) stream(method, path string, headers map[string]string, in io.Re return nil } -func (c *Client) hijack(method, path string, success chan struct{}, in io.Reader, errStream io.Writer, out io.Writer) error { +func (c *Client) hijack(method, path string, success chan struct{}, setRawTerminal bool, in io.Reader, stderr, stdout io.Writer) error { + if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil { + err := c.checkApiVersion() + if err != nil { + return err + } + } req, err := http.NewRequest(method, c.getURL(path), nil) if err != nil { return err @@ -212,10 +399,10 @@ func (c *Client) hijack(method, path string, success chan struct{}, in io.Reader errs := make(chan error, 2) go func() { var err error - if in != nil { - _, err = io.Copy(out, br) + if setRawTerminal { + _, err = io.Copy(stdout, br) } else { - _, err = utils.StdCopy(out, errStream, br) + _, err = utils.StdCopy(stdout, stderr, br) } errs <- err wg.Done() @@ -244,7 +431,12 @@ func (c *Client) getURL(path string) string { if c.endpointURL.Scheme == "unix" { urlStr = "" } - return fmt.Sprintf("%s%s", urlStr, path) + + if c.requestedApiVersion != nil { + return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedApiVersion, path) + } else { + return fmt.Sprintf("%s%s", urlStr, path) + } } type jsonMessage struct { diff --git a/third_party/src/github.com/fsouza/go-dockerclient/client_test.go b/third_party/src/github.com/fsouza/go-dockerclient/client_test.go index 611f7a1a325..deab8358263 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/client_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/client_test.go @@ -37,7 +37,32 @@ func TestNewAPIClient(t *testing.T) { if client.endpoint != endpoint { t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) } + if !client.SkipServerVersionCheck { + t.Error("Expected SkipServerVersionCheck to be true, got false") + } + if client.requestedApiVersion != nil { + t.Errorf("Expected requestedApiVersion to be nil, got %#v.", client.requestedApiVersion) + } +} +func TestNewVersionedClient(t *testing.T) { + endpoint := "http://localhost:4243" + client, err := NewVersionedClient(endpoint, "1.12") + if err != nil { + t.Fatal(err) + } + if client.endpoint != endpoint { + t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) + } + if client.client != http.DefaultClient { + t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.client) + } + if reqVersion := client.requestedApiVersion.String(); reqVersion != "1.12" { + t.Errorf("Wrong requestApiVersion. Want %q. Got %q.", "1.12", reqVersion) + } + if client.SkipServerVersionCheck { + t.Error("Expected SkipServerVersionCheck to be false, got true") + } } func TestNewClientInvalidEndpoint(t *testing.T) { @@ -73,6 +98,7 @@ func TestGetURL(t *testing.T) { for _, tt := range tests { client, _ := NewClient(tt.endpoint) client.endpoint = tt.endpoint + client.SkipServerVersionCheck = true got := client.getURL(tt.path) if got != tt.expected { t.Errorf("getURL(%q): Got %s. Want %s.", tt.path, got, tt.expected) @@ -122,6 +148,50 @@ func TestQueryString(t *testing.T) { } } +func TestApiVersions(t *testing.T) { + var tests = []struct { + a string + b string + expectedALessThanB bool + expectedALessThanOrEqualToB bool + expectedAGreaterThanB bool + expectedAGreaterThanOrEqualToB bool + }{ + {"1.11", "1.11", false, true, false, true}, + {"1.10", "1.11", true, true, false, false}, + {"1.11", "1.10", false, false, true, true}, + + {"1.9", "1.11", true, true, false, false}, + {"1.11", "1.9", false, false, true, true}, + + {"1.1.1", "1.1", false, false, true, true}, + {"1.1", "1.1.1", true, true, false, false}, + + {"2.1", "1.1.1", false, false, true, true}, + {"2.1", "1.3.1", false, false, true, true}, + {"1.1.1", "2.1", true, true, false, false}, + {"1.3.1", "2.1", true, true, false, false}, + } + + for _, tt := range tests { + a, _ := NewApiVersion(tt.a) + b, _ := NewApiVersion(tt.b) + + if tt.expectedALessThanB && !a.LessThan(b) { + t.Errorf("Expected %#v < %#v", a, b) + } + if tt.expectedALessThanOrEqualToB && !a.LessThanOrEqualTo(b) { + t.Errorf("Expected %#v <= %#v", a, b) + } + if tt.expectedAGreaterThanB && !a.GreaterThan(b) { + t.Errorf("Expected %#v > %#v", a, b) + } + if tt.expectedAGreaterThanOrEqualToB && !a.GreaterThanOrEqualTo(b) { + t.Errorf("Expected %#v >= %#v", a, b) + } + } +} + type FakeRoundTripper struct { message string status int diff --git a/third_party/src/github.com/fsouza/go-dockerclient/container.go b/third_party/src/github.com/fsouza/go-dockerclient/container.go index 91cf3d7bdc7..0baaf3803fb 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/container.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/container.go @@ -175,7 +175,7 @@ type Config struct { StdinOnce bool Env []string Cmd []string - Dns []string // For Docker API v1.9 and below only + Dns []string // For Docker API v1.9 and below only Image string Volumes map[string]struct{} VolumesFrom string @@ -295,7 +295,10 @@ type HostConfig struct { PortBindings map[Port][]PortBinding Links []string PublishAllPorts bool - Dns []string // For Docker API v1.10 and above only + Dns []string // For Docker API v1.10 and above only + DnsSearch []string + VolumesFrom []string + NetworkMode string } // StartContainer starts a container, returning an errror in case of failure. @@ -465,20 +468,6 @@ type CommitContainerOptions struct { Run *Config `qs:"-"` } -type Image struct { - ID string `json:"id"` - Parent string `json:"parent,omitempty"` - Comment string `json:"comment,omitempty"` - Created time.Time `json:"created"` - Container string `json:"container,omitempty"` - ContainerConfig Config `json:"container_config,omitempty"` - DockerVersion string `json:"docker_version,omitempty"` - Author string `json:"author,omitempty"` - Config *Config `json:"config,omitempty"` - Architecture string `json:"architecture,omitempty"` - Size int64 -} - // CommitContainer creates a new image from a container's changes. // // See http://goo.gl/628gxm for more details. @@ -515,7 +504,7 @@ type AttachToContainerOptions struct { // Stream the response? Stream bool - // Attach to stdin, and use InputFile. + // Attach to stdin, and use InputStream. Stdin bool // Attach to stdout, and use OutputStream. @@ -530,6 +519,9 @@ type AttachToContainerOptions struct { // It must be an unbuffered channel. Using a buffered channel can lead // to unexpected behavior. Success chan struct{} + + // Use raw terminal? Usually true when the container contains a TTY. + RawTerminal bool `qs:"-"` } // AttachToContainer attaches to a container, using the given options. @@ -540,7 +532,31 @@ func (c *Client) AttachToContainer(opts AttachToContainerOptions) error { return &NoSuchContainer{ID: opts.Container} } path := "/containers/" + opts.Container + "/attach?" + queryString(opts) - return c.hijack("POST", path, opts.Success, opts.InputStream, opts.ErrorStream, opts.OutputStream) + return c.hijack("POST", path, opts.Success, opts.RawTerminal, opts.InputStream, opts.ErrorStream, opts.OutputStream) +} + +// LogsOptions represents the set of options used when getting logs from a +// container. +// +// See http://goo.gl/rLhKSU for more details. +type LogsOptions struct { + Container string `qs:"-"` + OutputStream io.Writer `qs:"-"` + Follow bool + Stdout bool + Stderr bool + Timestamps bool +} + +// Logs gets stdout and stderr logs from the specified container. +// +// See http://goo.gl/rLhKSU for more details. +func (c *Client) Logs(opts LogsOptions) error { + if opts.Container == "" { + return &NoSuchContainer{ID: opts.Container} + } + path := "/containers/" + opts.Container + "/logs?" + queryString(opts) + return c.stream("GET", path, nil, nil, opts.OutputStream) } // ResizeContainerTTY resizes the terminal to the given height and width. diff --git a/third_party/src/github.com/fsouza/go-dockerclient/container_test.go b/third_party/src/github.com/fsouza/go-dockerclient/container_test.go index 3828512a091..0517834e385 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/container_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/container_test.go @@ -678,6 +678,7 @@ func TestAttachToContainerLogs(t *testing.T) { })) defer server.Close() client, _ := NewClient(server.URL) + client.SkipServerVersionCheck = true var buf bytes.Buffer opts := AttachToContainerOptions{ Container: "a123456", @@ -722,6 +723,7 @@ func TestAttachToContainer(t *testing.T) { })) defer server.Close() client, _ := NewClient(server.URL) + client.SkipServerVersionCheck = true var stdout, stderr bytes.Buffer opts := AttachToContainerOptions{ Container: "a123456", @@ -732,6 +734,7 @@ func TestAttachToContainer(t *testing.T) { Stdout: true, Stderr: true, Stream: true, + RawTerminal: true, } var err = client.AttachToContainer(opts) if err != nil { @@ -749,6 +752,80 @@ func TestAttachToContainer(t *testing.T) { } } +func TestAttachToContainerSentinel(t *testing.T) { + var reader = strings.NewReader("send value") + var req http.Request + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) + w.Write([]byte("hello")) + req = *r + })) + defer server.Close() + client, _ := NewClient(server.URL) + client.SkipServerVersionCheck = true + var stdout, stderr bytes.Buffer + success := make(chan struct{}) + opts := AttachToContainerOptions{ + Container: "a123456", + OutputStream: &stdout, + ErrorStream: &stderr, + InputStream: reader, + Stdin: true, + Stdout: true, + Stderr: true, + Stream: true, + RawTerminal: true, + Success: success, + } + go client.AttachToContainer(opts) + success <- <-success +} + +func TestAttachToContainerRawTerminalFalse(t *testing.T) { + input := strings.NewReader("send value") + var req http.Request + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + prefix := []byte{1, 0, 0, 0, 0, 0, 0, 5} + w.Write(prefix) + w.Write([]byte("hello")) + req = *r + })) + defer server.Close() + client, _ := NewClient(server.URL) + client.SkipServerVersionCheck = true + var stdout, stderr bytes.Buffer + opts := AttachToContainerOptions{ + Container: "a123456", + OutputStream: &stdout, + ErrorStream: &stderr, + InputStream: input, + Stdin: true, + Stdout: true, + Stderr: true, + Stream: true, + RawTerminal: false, + } + err := client.AttachToContainer(opts) + if err != nil { + t.Fatal(err) + } + expected := map[string][]string{ + "stdin": {"1"}, + "stdout": {"1"}, + "stderr": {"1"}, + "stream": {"1"}, + } + got := map[string][]string(req.URL.Query()) + if !reflect.DeepEqual(got, expected) { + t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expected, got) + } + t.Log(stderr.String()) + t.Log(stdout.String()) + if stdout.String() != "hello" { + t.Errorf("AttachToContainer: wrong content written to stdout. Want %q. Got %q.", "hello", stderr.String()) + } +} + func TestAttachToContainerWithoutContainer(t *testing.T) { var client Client err := client.AttachToContainer(AttachToContainerOptions{}) @@ -758,6 +835,60 @@ func TestAttachToContainerWithoutContainer(t *testing.T) { } } +func TestLogs(t *testing.T) { + var req http.Request + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("something happened!")) + req = *r + })) + defer server.Close() + client, _ := NewClient(server.URL) + client.SkipServerVersionCheck = true + var buf bytes.Buffer + opts := LogsOptions{ + Container: "a123456", + OutputStream: &buf, + Follow: true, + Stdout: true, + Stderr: true, + Timestamps: true, + } + err := client.Logs(opts) + if err != nil { + t.Fatal(err) + } + expected := "something happened!" + if buf.String() != expected { + t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String()) + } + if req.Method != "GET" { + t.Errorf("Logs: wrong HTTP method. Want GET. Got %s.", req.Method) + } + u, _ := url.Parse(client.getURL("/containers/a123456/logs")) + if req.URL.Path != u.Path { + t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path) + } + expectedQs := map[string][]string{ + "follow": {"1"}, + "stdout": {"1"}, + "stderr": {"1"}, + "timestamps": {"1"}, + } + got := map[string][]string(req.URL.Query()) + if !reflect.DeepEqual(got, expectedQs) { + t.Errorf("Logs: wrong query string. Want %#v. Got %#v.", expectedQs, got) + } +} + +func TestLogsNoContainer(t *testing.T) { + var client Client + err := client.Logs(LogsOptions{}) + expected := &NoSuchContainer{ID: ""} + if !reflect.DeepEqual(err, expected) { + t.Errorf("AttachToContainer: wrong error. Want %#v. Got %#v.", expected, err) + } +} + func TestNoSuchContainerError(t *testing.T) { var err error = &NoSuchContainer{ID: "i345"} expected := "No such container: i345" @@ -792,9 +923,10 @@ func TestExportContainerViaUnixSocket(t *testing.T) { endpoint := "unix://" + tempSocket u, _ := parseEndpoint(endpoint) client := Client{ - endpoint: endpoint, - endpointURL: u, - client: http.DefaultClient, + endpoint: endpoint, + endpointURL: u, + client: http.DefaultClient, + SkipServerVersionCheck: true, } listening := make(chan string) done := make(chan int) diff --git a/third_party/src/github.com/fsouza/go-dockerclient/event.go b/third_party/src/github.com/fsouza/go-dockerclient/event.go index eb0ad435e89..1808e4160c5 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/event.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/event.go @@ -260,7 +260,7 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan for { var event APIEvents if err = decoder.Decode(&event); err != nil { - if err == io.EOF { + if err == io.EOF || err == io.ErrUnexpectedEOF { break } errChan <- err @@ -270,9 +270,8 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan } if !c.eventMonitor.isEnabled() { return - } else { - c.eventMonitor.C <- &event } + c.eventMonitor.C <- &event } }(res, conn) return nil diff --git a/third_party/src/github.com/fsouza/go-dockerclient/event_test.go b/third_party/src/github.com/fsouza/go-dockerclient/event_test.go index 558b9ca9e6d..cb54f4ae925 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/event_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/event_test.go @@ -37,6 +37,7 @@ func TestEventListeners(t *testing.T) { if err != nil { t.Errorf("Failed to create client: %s", err) } + client.SkipServerVersionCheck = true listener := make(chan *APIEvents, 10) defer func() { time.Sleep(10 * time.Millisecond); client.RemoveEventListener(listener) }() diff --git a/third_party/src/github.com/fsouza/go-dockerclient/example_test.go b/third_party/src/github.com/fsouza/go-dockerclient/example_test.go index 8dc11322e77..ba44dd8c4f7 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/example_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/example_test.go @@ -19,6 +19,7 @@ func ExampleClient_AttachToContainer() { if err != nil { log.Fatal(err) } + client.SkipServerVersionCheck = true // Reading logs from container a84849 and sending them to buf. var buf bytes.Buffer err = client.AttachToContainer(docker.AttachToContainerOptions{ diff --git a/third_party/src/github.com/fsouza/go-dockerclient/image.go b/third_party/src/github.com/fsouza/go-dockerclient/image.go index e040657a93d..3350e98e23c 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/image.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/image.go @@ -15,6 +15,7 @@ import ( "net/http" "net/url" "os" + "time" ) // APIImages represent an image returned in the ListImages call. @@ -29,6 +30,34 @@ type APIImages struct { Tag string `json:",omitempty"` } +type Image struct { + ID string `json:"id"` + Parent string `json:"parent,omitempty"` + Comment string `json:"comment,omitempty"` + Created time.Time `json:"created"` + Container string `json:"container,omitempty"` + ContainerConfig Config `json:"containerconfig,omitempty"` + DockerVersion string `json:"dockerversion,omitempty"` + Author string `json:"author,omitempty"` + Config *Config `json:"config,omitempty"` + Architecture string `json:"architecture,omitempty"` + Size int64 +} + +type ImagePre012 struct { + ID string `json:"id"` + Parent string `json:"parent,omitempty"` + Comment string `json:"comment,omitempty"` + Created time.Time `json:"created"` + Container string `json:"container,omitempty"` + ContainerConfig Config `json:"container_config,omitempty"` + DockerVersion string `json:"docker_version,omitempty"` + Author string `json:"author,omitempty"` + Config *Config `json:"config,omitempty"` + Architecture string `json:"architecture,omitempty"` + Size int64 +} + var ( // ErrNoSuchImage is the error returned when the image does not exist. ErrNoSuchImage = errors.New("no such image") @@ -86,11 +115,35 @@ func (c *Client) InspectImage(name string) (*Image, error) { if err != nil { return nil, err } + var image Image - err = json.Unmarshal(body, &image) - if err != nil { - return nil, err + + // if the caller elected to skip checking the server's version, assume it's the latest + if c.SkipServerVersionCheck || c.expectedApiVersion.GreaterThanOrEqualTo(apiVersion_1_12) { + err = json.Unmarshal(body, &image) + if err != nil { + return nil, err + } + } else { + var imagePre012 ImagePre012 + err = json.Unmarshal(body, &imagePre012) + if err != nil { + return nil, err + } + + image.ID = imagePre012.ID + image.Parent = imagePre012.Parent + image.Comment = imagePre012.Comment + image.Created = imagePre012.Created + image.Container = imagePre012.Container + image.ContainerConfig = imagePre012.ContainerConfig + image.DockerVersion = imagePre012.DockerVersion + image.Author = imagePre012.Author + image.Config = imagePre012.Config + image.Architecture = imagePre012.Architecture + image.Size = imagePre012.Size } + return &image, nil } diff --git a/third_party/src/github.com/fsouza/go-dockerclient/image_test.go b/third_party/src/github.com/fsouza/go-dockerclient/image_test.go index c61d5b04f7f..a2db84db155 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/image_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/image_test.go @@ -21,9 +21,10 @@ func newTestClient(rt *FakeRoundTripper) Client { endpoint := "http://localhost:4243" u, _ := parseEndpoint("http://localhost:4243") client := Client{ - endpoint: endpoint, - endpointURL: u, - client: &http.Client{Transport: rt}, + endpoint: endpoint, + endpointURL: u, + client: &http.Client{Transport: rt}, + SkipServerVersionCheck: true, } return client } diff --git a/third_party/src/github.com/fsouza/go-dockerclient/misc.go b/third_party/src/github.com/fsouza/go-dockerclient/misc.go index ab4c9193653..1b9267e28a3 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/misc.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/misc.go @@ -6,8 +6,9 @@ package docker import ( "bytes" - "github.com/fsouza/go-dockerclient/engine" "io" + + "github.com/fsouza/go-dockerclient/engine" ) // Version returns version information about the docker server. diff --git a/third_party/src/github.com/fsouza/go-dockerclient/misc_test.go b/third_party/src/github.com/fsouza/go-dockerclient/misc_test.go index 497ac26b0b9..8cf283e565f 100644 --- a/third_party/src/github.com/fsouza/go-dockerclient/misc_test.go +++ b/third_party/src/github.com/fsouza/go-dockerclient/misc_test.go @@ -5,12 +5,13 @@ package docker import ( - "github.com/fsouza/go-dockerclient/engine" "net/http" "net/url" "reflect" "sort" "testing" + + "github.com/fsouza/go-dockerclient/engine" ) type DockerVersion struct {