mirror of https://github.com/k3s-io/k3s
bump(github.com/fsouza/go-dockerclient): a735a3dbbfdd1822886f6b4235318c8809b41538
parent
b1531bbcf5
commit
5d2eb7dfff
|
@ -4,6 +4,7 @@ Andrews Medina <andrewsmedina@gmail.com>
|
|||
Andy Goldstein <andy.goldstein@redhat.com>
|
||||
Ben McCann <benmccann.com>
|
||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||
cheneydeng <cheneydeng@qq.com>
|
||||
Ed <edrocksit@gmail.com>
|
||||
Eric Anderson <anderson@copperegg.com>
|
||||
|
@ -14,6 +15,7 @@ Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
|||
Jeff Mitchell <jeffrey.mitchell@gmail.com>
|
||||
Jeffrey Hulten <jhulten@gmail.com>
|
||||
Lucas Clemente <lucas@clemente.io>
|
||||
Omeid Matten <public@omeid.me>
|
||||
Paul Morie <pmorie@gmail.com>
|
||||
Peter Jihoon Kim <raingrove@gmail.com>
|
||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||
|
|
|
@ -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 <major>.<minor>.<patch>, where <major>,
|
||||
// <minor> and <patch> 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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }()
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue