mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Merge pull request #589 from monnand/update-docker-client
update github.com/fsouza/go-dockerclient
This commit is contained in:
commit
65575953c0
@ -239,7 +239,7 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
|
|||||||
ExposedPorts: exposedPorts,
|
ExposedPorts: exposedPorts,
|
||||||
Hostname: container.Name,
|
Hostname: container.Name,
|
||||||
Image: container.Image,
|
Image: container.Image,
|
||||||
Memory: int64(container.Memory),
|
Memory: uint64(container.Memory),
|
||||||
CpuShares: int64(milliCPUToShares(container.CPU)),
|
CpuShares: int64(milliCPUToShares(container.CPU)),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
WorkingDir: container.WorkingDir,
|
WorkingDir: container.WorkingDir,
|
||||||
|
@ -2,6 +2,7 @@ language: go
|
|||||||
go:
|
go:
|
||||||
- 1.1.2
|
- 1.1.2
|
||||||
- 1.2
|
- 1.2
|
||||||
|
- 1.3
|
||||||
- tip
|
- tip
|
||||||
env:
|
env:
|
||||||
- GOARCH=amd64
|
- GOARCH=amd64
|
||||||
|
@ -10,6 +10,7 @@ Ed <edrocksit@gmail.com>
|
|||||||
Eric Anderson <anderson@copperegg.com>
|
Eric Anderson <anderson@copperegg.com>
|
||||||
Flavia Missi <flaviamissi@gmail.com>
|
Flavia Missi <flaviamissi@gmail.com>
|
||||||
Francisco Souza <f@souza.cc>
|
Francisco Souza <f@souza.cc>
|
||||||
|
Jari Kolehmainen <jari.kolehmainen@digia.com>
|
||||||
Jason Wilder <jwilder@litl.com>
|
Jason Wilder <jwilder@litl.com>
|
||||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||||
Jeff Mitchell <jeffrey.mitchell@gmail.com>
|
Jeff Mitchell <jeffrey.mitchell@gmail.com>
|
||||||
@ -19,6 +20,7 @@ Omeid Matten <public@omeid.me>
|
|||||||
Paul Morie <pmorie@gmail.com>
|
Paul Morie <pmorie@gmail.com>
|
||||||
Peter Jihoon Kim <raingrove@gmail.com>
|
Peter Jihoon Kim <raingrove@gmail.com>
|
||||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||||
|
Rafe Colton <r.colton@modcloth.com>
|
||||||
Salvador Gironès <salvadorgirones@gmail.com>
|
Salvador Gironès <salvadorgirones@gmail.com>
|
||||||
Simon Eskildsen <sirup@sirupsen.com>
|
Simon Eskildsen <sirup@sirupsen.com>
|
||||||
Simon Menke <simon.menke@gmail.com>
|
Simon Menke <simon.menke@gmail.com>
|
||||||
|
@ -9,12 +9,6 @@ This package presents a client for the Docker remote API.
|
|||||||
|
|
||||||
For more details, check the [remote API documentation](http://docs.docker.io/en/latest/reference/api/docker_remote_api/).
|
For more details, check the [remote API documentation](http://docs.docker.io/en/latest/reference/api/docker_remote_api/).
|
||||||
|
|
||||||
##Versioning
|
|
||||||
|
|
||||||
* Version 0.1 is compatible with Docker v0.7.1
|
|
||||||
* The master is compatible with Docker's master
|
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -47,20 +47,17 @@ type ApiVersion []int
|
|||||||
// <minor> and <patch> are integer numbers.
|
// <minor> and <patch> are integer numbers.
|
||||||
func NewApiVersion(input string) (ApiVersion, error) {
|
func NewApiVersion(input string) (ApiVersion, error) {
|
||||||
if !strings.Contains(input, ".") {
|
if !strings.Contains(input, ".") {
|
||||||
return nil, fmt.Errorf("Unable to parse version '%s'", input)
|
return nil, fmt.Errorf("Unable to parse version %q", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := strings.Split(input, ".")
|
arr := strings.Split(input, ".")
|
||||||
ret := make(ApiVersion, len(arr))
|
ret := make(ApiVersion, len(arr))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for i, val := range arr {
|
for i, val := range arr {
|
||||||
ret[i], err = strconv.Atoi(val)
|
ret[i], err = strconv.Atoi(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("Unable to parse version %q: %q is not an integer", input, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +200,21 @@ func parseApiVersionString(input string) (version uint16, err error) {
|
|||||||
return version, nil
|
return version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ping pings the docker server
|
||||||
|
//
|
||||||
|
// See http://goo.gl/stJENm for more details.
|
||||||
|
func (c *Client) Ping() error {
|
||||||
|
path := "/_ping"
|
||||||
|
body, status, err := c.do("GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status != http.StatusOK {
|
||||||
|
return newError(status, body)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) getServerApiVersionString() (version string, err error) {
|
func (c *Client) getServerApiVersionString() (version string, err error) {
|
||||||
body, status, err := c.do("GET", "/version", nil)
|
body, status, err := c.do("GET", "/version", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -257,6 +269,7 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
defer dial.Close()
|
||||||
clientconn := httputil.NewClientConn(dial, nil)
|
clientconn := httputil.NewClientConn(dial, nil)
|
||||||
resp, err = clientconn.Do(req)
|
resp, err = clientconn.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -283,11 +296,10 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
|
|||||||
return body, resp.StatusCode, nil
|
return body, resp.StatusCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) stream(method, path string, headers map[string]string, in io.Reader, out io.Writer) error {
|
func (c *Client) stream(method, path string, setRawTerminal bool, headers map[string]string, in io.Reader, stdout, stderr io.Writer) error {
|
||||||
if (method == "POST" || method == "PUT") && in == nil {
|
if (method == "POST" || method == "PUT") && in == nil {
|
||||||
in = bytes.NewReader(nil)
|
in = bytes.NewReader(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil {
|
if path != "/version" && !c.SkipServerVersionCheck && c.expectedApiVersion == nil {
|
||||||
err := c.checkApiVersion()
|
err := c.checkApiVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -308,8 +320,11 @@ func (c *Client) stream(method, path string, headers map[string]string, in io.Re
|
|||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
protocol := c.endpointURL.Scheme
|
protocol := c.endpointURL.Scheme
|
||||||
address := c.endpointURL.Path
|
address := c.endpointURL.Path
|
||||||
if out == nil {
|
if stdout == nil {
|
||||||
out = ioutil.Discard
|
stdout = ioutil.Discard
|
||||||
|
}
|
||||||
|
if stderr == nil {
|
||||||
|
stderr = ioutil.Discard
|
||||||
}
|
}
|
||||||
if protocol == "unix" {
|
if protocol == "unix" {
|
||||||
dial, err := net.Dial(protocol, address)
|
dial, err := net.Dial(protocol, address)
|
||||||
@ -346,20 +361,23 @@ func (c *Client) stream(method, path string, headers map[string]string, in io.Re
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if m.Stream != "" {
|
if m.Stream != "" {
|
||||||
fmt.Fprint(out, m.Stream)
|
fmt.Fprint(stdout, m.Stream)
|
||||||
} else if m.Progress != "" {
|
} else if m.Progress != "" {
|
||||||
fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
|
fmt.Fprintf(stdout, "%s %s\r", m.Status, m.Progress)
|
||||||
} else if m.Error != "" {
|
} else if m.Error != "" {
|
||||||
return errors.New(m.Error)
|
return errors.New(m.Error)
|
||||||
}
|
}
|
||||||
if m.Status != "" {
|
if m.Status != "" {
|
||||||
fmt.Fprintln(out, m.Status)
|
fmt.Fprintln(stdout, m.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := io.Copy(out, resp.Body); err != nil {
|
if setRawTerminal {
|
||||||
return err
|
_, err = io.Copy(stdout, resp.Body)
|
||||||
|
} else {
|
||||||
|
_, err = utils.StdCopy(stdout, stderr, resp.Body)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -371,6 +389,12 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if stdout == nil {
|
||||||
|
stdout = ioutil.Discard
|
||||||
|
}
|
||||||
|
if stderr == nil {
|
||||||
|
stderr = ioutil.Discard
|
||||||
|
}
|
||||||
req, err := http.NewRequest(method, c.getURL(path), nil)
|
req, err := http.NewRequest(method, c.getURL(path), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -148,6 +148,25 @@ func TestQueryString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewApiVersionFailures(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{"1-0", `Unable to parse version "1-0"`},
|
||||||
|
{"1.0-beta", `Unable to parse version "1.0-beta": "0-beta" is not an integer`},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
v, err := NewApiVersion(tt.input)
|
||||||
|
if v != nil {
|
||||||
|
t.Errorf("Expected <nil> version, got %v.", v)
|
||||||
|
}
|
||||||
|
if err.Error() != tt.expectedError {
|
||||||
|
t.Errorf("NewApiVersion(%q): wrong error. Want %q. Got %q", tt.input, tt.expectedError, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApiVersions(t *testing.T) {
|
func TestApiVersions(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
a string
|
a string
|
||||||
@ -192,6 +211,41 @@ func TestApiVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPing(t *testing.T) {
|
||||||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
err := client.Ping()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingFailing(t *testing.T) {
|
||||||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusInternalServerError}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
err := client.Ping()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected non nil error, got nil")
|
||||||
|
}
|
||||||
|
expectedErrMsg := "API error (500): "
|
||||||
|
if err.Error() != expectedErrMsg {
|
||||||
|
t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingFailingWrongStatus(t *testing.T) {
|
||||||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusAccepted}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
err := client.Ping()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected non nil error, got nil")
|
||||||
|
}
|
||||||
|
expectedErrMsg := "API error (202): "
|
||||||
|
if err.Error() != expectedErrMsg {
|
||||||
|
t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type FakeRoundTripper struct {
|
type FakeRoundTripper struct {
|
||||||
message string
|
message string
|
||||||
status int
|
status int
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,22 +86,19 @@ func (p Port) Proto() string {
|
|||||||
|
|
||||||
// State represents the state of a container.
|
// State represents the state of a container.
|
||||||
type State struct {
|
type State struct {
|
||||||
sync.RWMutex
|
|
||||||
Running bool
|
Running bool
|
||||||
|
Paused bool
|
||||||
Pid int
|
Pid int
|
||||||
ExitCode int
|
ExitCode int
|
||||||
StartedAt time.Time
|
StartedAt time.Time
|
||||||
FinishedAt time.Time
|
FinishedAt time.Time
|
||||||
Ghost bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the string representation of a state.
|
// String returns the string representation of a state.
|
||||||
func (s *State) String() string {
|
func (s *State) String() string {
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
if s.Running {
|
if s.Running {
|
||||||
if s.Ghost {
|
if s.Paused {
|
||||||
return "Ghost"
|
return "paused"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
|
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
|
||||||
}
|
}
|
||||||
@ -162,8 +158,8 @@ type Config struct {
|
|||||||
Hostname string
|
Hostname string
|
||||||
Domainname string
|
Domainname string
|
||||||
User string
|
User string
|
||||||
Memory int64
|
Memory uint64
|
||||||
MemorySwap int64
|
MemorySwap uint64
|
||||||
CpuShares int64
|
CpuShares int64
|
||||||
AttachStdin bool
|
AttachStdin bool
|
||||||
AttachStdout bool
|
AttachStdout bool
|
||||||
@ -351,6 +347,36 @@ func (c *Client) RestartContainer(id string, timeout uint) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PauseContainer pauses the given container.
|
||||||
|
//
|
||||||
|
// See http://goo.gl/AM5t42 for more details.
|
||||||
|
func (c *Client) PauseContainer(id string) error {
|
||||||
|
path := fmt.Sprintf("/containers/%s/pause", id)
|
||||||
|
_, status, err := c.do("POST", path, nil)
|
||||||
|
if status == http.StatusNotFound {
|
||||||
|
return &NoSuchContainer{ID: id}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnpauseContainer pauses the given container.
|
||||||
|
//
|
||||||
|
// See http://goo.gl/eBrNSL for more details.
|
||||||
|
func (c *Client) UnpauseContainer(id string) error {
|
||||||
|
path := fmt.Sprintf("/containers/%s/unpause", id)
|
||||||
|
_, status, err := c.do("POST", path, nil)
|
||||||
|
if status == http.StatusNotFound {
|
||||||
|
return &NoSuchContainer{ID: id}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// KillContainerOptions represents the set of options that can be used in a
|
// KillContainerOptions represents the set of options that can be used in a
|
||||||
// call to KillContainer.
|
// call to KillContainer.
|
||||||
type KillContainerOptions struct {
|
type KillContainerOptions struct {
|
||||||
@ -542,10 +568,15 @@ func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
|||||||
type LogsOptions struct {
|
type LogsOptions struct {
|
||||||
Container string `qs:"-"`
|
Container string `qs:"-"`
|
||||||
OutputStream io.Writer `qs:"-"`
|
OutputStream io.Writer `qs:"-"`
|
||||||
|
ErrorStream io.Writer `qs:"-"`
|
||||||
Follow bool
|
Follow bool
|
||||||
Stdout bool
|
Stdout bool
|
||||||
Stderr bool
|
Stderr bool
|
||||||
Timestamps bool
|
Timestamps bool
|
||||||
|
Tail string
|
||||||
|
|
||||||
|
// Use raw terminal? Usually true when the container contains a TTY.
|
||||||
|
RawTerminal bool `qs:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs gets stdout and stderr logs from the specified container.
|
// Logs gets stdout and stderr logs from the specified container.
|
||||||
@ -555,8 +586,11 @@ func (c *Client) Logs(opts LogsOptions) error {
|
|||||||
if opts.Container == "" {
|
if opts.Container == "" {
|
||||||
return &NoSuchContainer{ID: opts.Container}
|
return &NoSuchContainer{ID: opts.Container}
|
||||||
}
|
}
|
||||||
|
if opts.Tail == "" {
|
||||||
|
opts.Tail = "all"
|
||||||
|
}
|
||||||
path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
|
path := "/containers/" + opts.Container + "/logs?" + queryString(opts)
|
||||||
return c.stream("GET", path, nil, nil, opts.OutputStream)
|
return c.stream("GET", path, opts.RawTerminal, nil, nil, opts.OutputStream, opts.ErrorStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResizeContainerTTY resizes the terminal to the given height and width.
|
// ResizeContainerTTY resizes the terminal to the given height and width.
|
||||||
@ -586,7 +620,7 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
|||||||
return NoSuchContainer{ID: opts.ID}
|
return NoSuchContainer{ID: opts.ID}
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("/containers/%s/export", opts.ID)
|
url := fmt.Sprintf("/containers/%s/export", opts.ID)
|
||||||
return c.stream("GET", url, nil, nil, opts.OutputStream)
|
return c.stream("GET", url, true, nil, nil, opts.OutputStream, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoSuchContainer is the error returned when a given container does not exist.
|
// NoSuchContainer is the error returned when a given container does not exist.
|
||||||
|
@ -14,12 +14,32 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestStateString(t *testing.T) {
|
||||||
|
started := time.Now().Add(-3 * time.Hour)
|
||||||
|
var tests = []struct {
|
||||||
|
input State
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{State{Running: true, Paused: true}, "^paused$"},
|
||||||
|
{State{Running: true, StartedAt: started}, "^Up 3h.*$"},
|
||||||
|
{State{Running: false, ExitCode: 7}, "^Exit 7$"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
re := regexp.MustCompile(tt.expected)
|
||||||
|
if got := tt.input.String(); !re.MatchString(got) {
|
||||||
|
t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListContainers(t *testing.T) {
|
func TestListContainers(t *testing.T) {
|
||||||
jsonContainers := `[
|
jsonContainers := `[
|
||||||
{
|
{
|
||||||
@ -132,8 +152,8 @@ func TestInspectContainer(t *testing.T) {
|
|||||||
"Config": {
|
"Config": {
|
||||||
"Hostname": "4fa6e0f0c678",
|
"Hostname": "4fa6e0f0c678",
|
||||||
"User": "",
|
"User": "",
|
||||||
"Memory": 0,
|
"Memory": 17179869184,
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 34359738368,
|
||||||
"AttachStdin": false,
|
"AttachStdin": false,
|
||||||
"AttachStdout": true,
|
"AttachStdout": true,
|
||||||
"AttachStderr": true,
|
"AttachStderr": true,
|
||||||
@ -445,6 +465,60 @@ func TestRestartContainerNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPauseContainer(t *testing.T) {
|
||||||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
|
||||||
|
err := client.PauseContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := fakeRT.requests[0]
|
||||||
|
if req.Method != "POST" {
|
||||||
|
t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
|
||||||
|
}
|
||||||
|
expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/pause"))
|
||||||
|
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
|
||||||
|
t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPauseContainerNotFound(t *testing.T) {
|
||||||
|
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
|
||||||
|
err := client.PauseContainer("a2334")
|
||||||
|
expected := &NoSuchContainer{ID: "a2334"}
|
||||||
|
if !reflect.DeepEqual(err, expected) {
|
||||||
|
t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpauseContainer(t *testing.T) {
|
||||||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||||
|
client := newTestClient(fakeRT)
|
||||||
|
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
|
||||||
|
err := client.UnpauseContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := fakeRT.requests[0]
|
||||||
|
if req.Method != "POST" {
|
||||||
|
t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
|
||||||
|
}
|
||||||
|
expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/unpause"))
|
||||||
|
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
|
||||||
|
t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpauseContainerNotFound(t *testing.T) {
|
||||||
|
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
|
||||||
|
err := client.UnpauseContainer("a2334")
|
||||||
|
expected := &NoSuchContainer{ID: "a2334"}
|
||||||
|
if !reflect.DeepEqual(err, expected) {
|
||||||
|
t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestKillContainer(t *testing.T) {
|
func TestKillContainer(t *testing.T) {
|
||||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||||
client := newTestClient(fakeRT)
|
client := newTestClient(fakeRT)
|
||||||
@ -736,7 +810,7 @@ func TestAttachToContainer(t *testing.T) {
|
|||||||
Stream: true,
|
Stream: true,
|
||||||
RawTerminal: true,
|
RawTerminal: true,
|
||||||
}
|
}
|
||||||
var err = client.AttachToContainer(opts)
|
err := client.AttachToContainer(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -781,6 +855,63 @@ func TestAttachToContainerSentinel(t *testing.T) {
|
|||||||
success <- <-success
|
success <- <-success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAttachToContainerNilStdout(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 stderr bytes.Buffer
|
||||||
|
opts := AttachToContainerOptions{
|
||||||
|
Container: "a123456",
|
||||||
|
OutputStream: nil,
|
||||||
|
ErrorStream: &stderr,
|
||||||
|
InputStream: reader,
|
||||||
|
Stdin: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Stream: true,
|
||||||
|
RawTerminal: true,
|
||||||
|
}
|
||||||
|
err := client.AttachToContainer(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAttachToContainerNilStderr(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 bytes.Buffer
|
||||||
|
opts := AttachToContainerOptions{
|
||||||
|
Container: "a123456",
|
||||||
|
OutputStream: &stdout,
|
||||||
|
InputStream: reader,
|
||||||
|
Stdin: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Stream: true,
|
||||||
|
RawTerminal: true,
|
||||||
|
}
|
||||||
|
err := client.AttachToContainer(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAttachToContainerRawTerminalFalse(t *testing.T) {
|
func TestAttachToContainerRawTerminalFalse(t *testing.T) {
|
||||||
input := strings.NewReader("send value")
|
input := strings.NewReader("send value")
|
||||||
var req http.Request
|
var req http.Request
|
||||||
@ -838,6 +969,8 @@ func TestAttachToContainerWithoutContainer(t *testing.T) {
|
|||||||
func TestLogs(t *testing.T) {
|
func TestLogs(t *testing.T) {
|
||||||
var req http.Request
|
var req http.Request
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19}
|
||||||
|
w.Write(prefix)
|
||||||
w.Write([]byte("something happened!"))
|
w.Write([]byte("something happened!"))
|
||||||
req = *r
|
req = *r
|
||||||
}))
|
}))
|
||||||
@ -873,6 +1006,7 @@ func TestLogs(t *testing.T) {
|
|||||||
"stdout": {"1"},
|
"stdout": {"1"},
|
||||||
"stderr": {"1"},
|
"stderr": {"1"},
|
||||||
"timestamps": {"1"},
|
"timestamps": {"1"},
|
||||||
|
"tail": {"all"},
|
||||||
}
|
}
|
||||||
got := map[string][]string(req.URL.Query())
|
got := map[string][]string(req.URL.Query())
|
||||||
if !reflect.DeepEqual(got, expectedQs) {
|
if !reflect.DeepEqual(got, expectedQs) {
|
||||||
@ -880,6 +1014,133 @@ func TestLogs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLogsNilStdoutDoesntFail(t *testing.T) {
|
||||||
|
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, 19}
|
||||||
|
w.Write(prefix)
|
||||||
|
w.Write([]byte("something happened!"))
|
||||||
|
req = *r
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
client, _ := NewClient(server.URL)
|
||||||
|
client.SkipServerVersionCheck = true
|
||||||
|
opts := LogsOptions{
|
||||||
|
Container: "a123456",
|
||||||
|
Follow: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Timestamps: true,
|
||||||
|
}
|
||||||
|
err := client.Logs(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogsNilStderrDoesntFail(t *testing.T) {
|
||||||
|
var req http.Request
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
prefix := []byte{2, 0, 0, 0, 0, 0, 0, 19}
|
||||||
|
w.Write(prefix)
|
||||||
|
w.Write([]byte("something happened!"))
|
||||||
|
req = *r
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
client, _ := NewClient(server.URL)
|
||||||
|
client.SkipServerVersionCheck = true
|
||||||
|
opts := LogsOptions{
|
||||||
|
Container: "a123456",
|
||||||
|
Follow: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Timestamps: true,
|
||||||
|
}
|
||||||
|
err := client.Logs(opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogsSpecifyingTail(t *testing.T) {
|
||||||
|
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, 19}
|
||||||
|
w.Write(prefix)
|
||||||
|
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,
|
||||||
|
Tail: "100",
|
||||||
|
}
|
||||||
|
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"},
|
||||||
|
"tail": {"100"},
|
||||||
|
}
|
||||||
|
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 TestLogsRawTerminal(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,
|
||||||
|
RawTerminal: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Timestamps: true,
|
||||||
|
Tail: "100",
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLogsNoContainer(t *testing.T) {
|
func TestLogsNoContainer(t *testing.T) {
|
||||||
var client Client
|
var client Client
|
||||||
err := client.Logs(LogsOptions{})
|
err := client.Logs(LogsOptions{})
|
||||||
|
@ -154,6 +154,9 @@ type PushImageOptions struct {
|
|||||||
// Name of the image
|
// Name of the image
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// Tag of the image
|
||||||
|
Tag string
|
||||||
|
|
||||||
// Registry server to push the image
|
// Registry server to push the image
|
||||||
Registry string
|
Registry string
|
||||||
|
|
||||||
@ -187,7 +190,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
|
|||||||
|
|
||||||
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||||
|
|
||||||
return c.stream("POST", path, headers, nil, opts.OutputStream)
|
return c.stream("POST", path, true, headers, nil, opts.OutputStream, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PullImageOptions present the set of options available for pulling an image
|
// PullImageOptions present the set of options available for pulling an image
|
||||||
@ -219,7 +222,7 @@ func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error
|
|||||||
|
|
||||||
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer) error {
|
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer) error {
|
||||||
path := "/images/create?" + qs
|
path := "/images/create?" + qs
|
||||||
return c.stream("POST", path, headers, in, w)
|
return c.stream("POST", path, true, headers, in, w, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportImageOptions present the set of informations available for importing
|
// ImportImageOptions present the set of informations available for importing
|
||||||
@ -286,13 +289,14 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
|
|||||||
return ErrMissingRepo
|
return ErrMissingRepo
|
||||||
}
|
}
|
||||||
return c.stream("POST", fmt.Sprintf("/build?%s",
|
return c.stream("POST", fmt.Sprintf("/build?%s",
|
||||||
queryString(&opts)), headers, opts.InputStream, opts.OutputStream)
|
queryString(&opts)), true, headers, opts.InputStream, opts.OutputStream, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagImageOptions present the set of options to tag an image
|
// TagImageOptions present the set of options to tag an image
|
||||||
type TagImageOptions struct {
|
type TagImageOptions struct {
|
||||||
Repo string `qs:"repo"`
|
Repo string
|
||||||
Force bool `qs:"force"`
|
Tag string
|
||||||
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagImage adds a tag to the image 'name'
|
// TagImage adds a tag to the image 'name'
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -34,43 +33,55 @@ import (
|
|||||||
//
|
//
|
||||||
// For more details on the remote API, check http://goo.gl/yMI1S.
|
// For more details on the remote API, check http://goo.gl/yMI1S.
|
||||||
type DockerServer struct {
|
type DockerServer struct {
|
||||||
containers []*docker.Container
|
containers []*docker.Container
|
||||||
cMut sync.RWMutex
|
cMut sync.RWMutex
|
||||||
images []docker.Image
|
images []docker.Image
|
||||||
iMut sync.RWMutex
|
iMut sync.RWMutex
|
||||||
imgIDs map[string]string
|
imgIDs map[string]string
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
mux *mux.Router
|
mux *mux.Router
|
||||||
hook func(*http.Request)
|
hook func(*http.Request)
|
||||||
failures map[string]FailureSpec
|
failures map[string]string
|
||||||
}
|
customHandlers map[string]http.Handler
|
||||||
|
handlerMutex sync.RWMutex
|
||||||
// FailureSpec is used with PrepareFailure and describes in which situations
|
cChan chan<- *docker.Container
|
||||||
// the request should fail. UrlRegex is mandatory, if a container id is sent
|
|
||||||
// on the request you can also specify the other properties.
|
|
||||||
type FailureSpec struct {
|
|
||||||
UrlRegex string
|
|
||||||
ContainerPath string
|
|
||||||
ContainerArgs []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns a new instance of the fake server, in standalone mode. Use
|
// NewServer returns a new instance of the fake server, in standalone mode. Use
|
||||||
// the method URL to get the URL of the server.
|
// the method URL to get the URL of the server.
|
||||||
//
|
//
|
||||||
// It receives the bind address (use 127.0.0.1:0 for getting an available port
|
// It receives the bind address (use 127.0.0.1:0 for getting an available port
|
||||||
// on the host) and a hook function, that will be called on every request.
|
// on the host), a channel of containers and a hook function, that will be
|
||||||
func NewServer(bind string, hook func(*http.Request)) (*DockerServer, error) {
|
// called on every request.
|
||||||
|
//
|
||||||
|
// The fake server will send containers in the channel whenever the container
|
||||||
|
// changes its state, via the HTTP API (i.e.: create, start and stop). This
|
||||||
|
// channel may be nil, which means that the server won't notify on state
|
||||||
|
// changes.
|
||||||
|
func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*http.Request)) (*DockerServer, error) {
|
||||||
listener, err := net.Listen("tcp", bind)
|
listener, err := net.Listen("tcp", bind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
server := DockerServer{listener: listener, imgIDs: make(map[string]string), hook: hook,
|
server := DockerServer{
|
||||||
failures: make(map[string]FailureSpec)}
|
listener: listener,
|
||||||
|
imgIDs: make(map[string]string),
|
||||||
|
hook: hook,
|
||||||
|
failures: make(map[string]string),
|
||||||
|
customHandlers: make(map[string]http.Handler),
|
||||||
|
cChan: containerChan,
|
||||||
|
}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
go http.Serve(listener, &server)
|
go http.Serve(listener, &server)
|
||||||
return &server, nil
|
return &server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) notify(container *docker.Container) {
|
||||||
|
if s.cChan != nil {
|
||||||
|
s.cChan <- container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DockerServer) buildMuxer() {
|
func (s *DockerServer) buildMuxer() {
|
||||||
s.mux = mux.NewRouter()
|
s.mux = mux.NewRouter()
|
||||||
s.mux.Path("/commit").Methods("POST").HandlerFunc(s.handlerWrapper(s.commitContainer))
|
s.mux.Path("/commit").Methods("POST").HandlerFunc(s.handlerWrapper(s.commitContainer))
|
||||||
@ -79,6 +90,8 @@ func (s *DockerServer) buildMuxer() {
|
|||||||
s.mux.Path("/containers/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectContainer))
|
s.mux.Path("/containers/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectContainer))
|
||||||
s.mux.Path("/containers/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startContainer))
|
s.mux.Path("/containers/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startContainer))
|
||||||
s.mux.Path("/containers/{id:.*}/stop").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer))
|
s.mux.Path("/containers/{id:.*}/stop").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer))
|
||||||
|
s.mux.Path("/containers/{id:.*}/pause").Methods("POST").HandlerFunc(s.handlerWrapper(s.pauseContainer))
|
||||||
|
s.mux.Path("/containers/{id:.*}/unpause").Methods("POST").HandlerFunc(s.handlerWrapper(s.unpauseContainer))
|
||||||
s.mux.Path("/containers/{id:.*}/wait").Methods("POST").HandlerFunc(s.handlerWrapper(s.waitContainer))
|
s.mux.Path("/containers/{id:.*}/wait").Methods("POST").HandlerFunc(s.handlerWrapper(s.waitContainer))
|
||||||
s.mux.Path("/containers/{id:.*}/attach").Methods("POST").HandlerFunc(s.handlerWrapper(s.attachContainer))
|
s.mux.Path("/containers/{id:.*}/attach").Methods("POST").HandlerFunc(s.handlerWrapper(s.attachContainer))
|
||||||
s.mux.Path("/containers/{id:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeContainer))
|
s.mux.Path("/containers/{id:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeContainer))
|
||||||
@ -89,19 +102,45 @@ func (s *DockerServer) buildMuxer() {
|
|||||||
s.mux.Path("/images/{name:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectImage))
|
s.mux.Path("/images/{name:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectImage))
|
||||||
s.mux.Path("/images/{name:.*}/push").Methods("POST").HandlerFunc(s.handlerWrapper(s.pushImage))
|
s.mux.Path("/images/{name:.*}/push").Methods("POST").HandlerFunc(s.handlerWrapper(s.pushImage))
|
||||||
s.mux.Path("/events").Methods("GET").HandlerFunc(s.listEvents)
|
s.mux.Path("/events").Methods("GET").HandlerFunc(s.listEvents)
|
||||||
|
s.mux.Path("/_ping").Methods("GET").HandlerFunc(s.handlerWrapper(s.pingDocker))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareFailure adds a new expected failure based on a FailureSpec
|
// PrepareFailure adds a new expected failure based on a URL regexp it receives
|
||||||
// it receives an id for the failure and the spec.
|
// an id for the failure.
|
||||||
func (s *DockerServer) PrepareFailure(id string, spec FailureSpec) {
|
func (s *DockerServer) PrepareFailure(id string, urlRegexp string) {
|
||||||
s.failures[id] = spec
|
s.failures[id] = urlRegexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetFailure removes an expected failure identified by the id
|
// ResetFailure removes an expected failure identified by the given id.
|
||||||
func (s *DockerServer) ResetFailure(id string) {
|
func (s *DockerServer) ResetFailure(id string) {
|
||||||
delete(s.failures, id)
|
delete(s.failures, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CustomHandler registers a custom handler for a specific path.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// server.CustomHandler("/containers/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// http.Error(w, "Something wrong is not right", http.StatusInternalServerError)
|
||||||
|
// }))
|
||||||
|
func (s *DockerServer) CustomHandler(path string, handler http.Handler) {
|
||||||
|
s.handlerMutex.Lock()
|
||||||
|
s.customHandlers[path] = handler
|
||||||
|
s.handlerMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MutateContainer changes the state of a container, returning an error if the
|
||||||
|
// given id does not match to any container "running" in the server.
|
||||||
|
func (s *DockerServer) MutateContainer(id string, state docker.State) error {
|
||||||
|
for _, container := range s.containers {
|
||||||
|
if container.ID == id {
|
||||||
|
container.State = state
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("container not found")
|
||||||
|
}
|
||||||
|
|
||||||
// Stop stops the server.
|
// Stop stops the server.
|
||||||
func (s *DockerServer) Stop() {
|
func (s *DockerServer) Stop() {
|
||||||
if s.listener != nil {
|
if s.listener != nil {
|
||||||
@ -119,6 +158,12 @@ func (s *DockerServer) URL() string {
|
|||||||
|
|
||||||
// ServeHTTP handles HTTP requests sent to the server.
|
// ServeHTTP handles HTTP requests sent to the server.
|
||||||
func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.handlerMutex.RLock()
|
||||||
|
defer s.handlerMutex.RUnlock()
|
||||||
|
if handler, ok := s.customHandlers[r.URL.Path]; ok {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
s.mux.ServeHTTP(w, r)
|
s.mux.ServeHTTP(w, r)
|
||||||
if s.hook != nil {
|
if s.hook != nil {
|
||||||
s.hook(r)
|
s.hook(r)
|
||||||
@ -127,8 +172,8 @@ func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
for errorId, spec := range s.failures {
|
for errorID, urlRegexp := range s.failures {
|
||||||
matched, err := regexp.MatchString(spec.UrlRegex, r.URL.Path)
|
matched, err := regexp.MatchString(urlRegexp, r.URL.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@ -136,21 +181,7 @@ func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)
|
|||||||
if !matched {
|
if !matched {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
id := mux.Vars(r)["id"]
|
http.Error(w, errorID, http.StatusBadRequest)
|
||||||
if id != "" {
|
|
||||||
container, _, err := s.findContainer(id)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if spec.ContainerPath != "" && container.Path != spec.ContainerPath {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if spec.ContainerArgs != nil && reflect.DeepEqual(container.Args, spec.ContainerArgs) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http.Error(w, errorId, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f(w, r)
|
f(w, r)
|
||||||
@ -272,6 +303,7 @@ func (s *DockerServer) createContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.cMut.Lock()
|
s.cMut.Lock()
|
||||||
s.containers = append(s.containers, &container)
|
s.containers = append(s.containers, &container)
|
||||||
s.cMut.Unlock()
|
s.cMut.Unlock()
|
||||||
|
s.notify(&container)
|
||||||
var c = struct{ ID string }{ID: container.ID}
|
var c = struct{ ID string }{ID: container.ID}
|
||||||
json.NewEncoder(w).Encode(c)
|
json.NewEncoder(w).Encode(c)
|
||||||
}
|
}
|
||||||
@ -308,6 +340,7 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
container.State.Running = true
|
container.State.Running = true
|
||||||
|
s.notify(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -325,6 +358,41 @@ func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
container.State.Running = false
|
container.State.Running = false
|
||||||
|
s.notify(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) pauseContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
container, _, err := s.findContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.cMut.Lock()
|
||||||
|
defer s.cMut.Unlock()
|
||||||
|
if container.State.Paused {
|
||||||
|
http.Error(w, "Container already paused", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
container.State.Paused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) unpauseContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
container, _, err := s.findContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.cMut.Lock()
|
||||||
|
defer s.cMut.Unlock()
|
||||||
|
if !container.State.Paused {
|
||||||
|
http.Error(w, "Container not paused", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
container.State.Paused = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -361,7 +429,8 @@ func (s *DockerServer) waitContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
s.cMut.RUnlock()
|
s.cMut.RUnlock()
|
||||||
}
|
}
|
||||||
w.Write([]byte(`{"StatusCode":0}`))
|
result := map[string]int{"StatusCode": container.State.ExitCode}
|
||||||
|
json.NewEncoder(w).Encode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) removeContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) removeContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -547,6 +616,10 @@ func (s *DockerServer) listEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerServer) pingDocker(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DockerServer) generateEvent() *docker.APIEvents {
|
func (s *DockerServer) generateEvent() *docker.APIEvents {
|
||||||
var eventType string
|
var eventType string
|
||||||
switch mathrand.Intn(4) {
|
switch mathrand.Intn(4) {
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewServer(t *testing.T) {
|
func TestNewServer(t *testing.T) {
|
||||||
server, err := NewServer("127.0.0.1:0", nil)
|
server, err := NewServer("127.0.0.1:0", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ func TestNewServer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerStop(t *testing.T) {
|
func TestServerStop(t *testing.T) {
|
||||||
server, err := NewServer("127.0.0.1:0", nil)
|
server, err := NewServer("127.0.0.1:0", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ func TestServerStopNoListener(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerURL(t *testing.T) {
|
func TestServerURL(t *testing.T) {
|
||||||
server, err := NewServer("127.0.0.1:0", nil)
|
server, err := NewServer("127.0.0.1:0", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ func TestServerURLNoListener(t *testing.T) {
|
|||||||
|
|
||||||
func TestHandleWithHook(t *testing.T) {
|
func TestHandleWithHook(t *testing.T) {
|
||||||
var called bool
|
var called bool
|
||||||
server, _ := NewServer("127.0.0.1:0", func(*http.Request) { called = true })
|
server, _ := NewServer("127.0.0.1:0", nil, func(*http.Request) { called = true })
|
||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||||
@ -81,6 +81,25 @@ func TestHandleWithHook(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomHandler(t *testing.T) {
|
||||||
|
var called bool
|
||||||
|
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||||
|
addContainers(server, 2)
|
||||||
|
server.CustomHandler("/containers/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
called = true
|
||||||
|
fmt.Fprint(w, "Hello world")
|
||||||
|
}))
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if !called {
|
||||||
|
t.Error("Did not call the custom handler")
|
||||||
|
}
|
||||||
|
if got := recorder.Body.String(); got != "Hello world" {
|
||||||
|
t.Errorf("Wrong output for custom handler: want %q. Got %q.", "Hello world", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListContainers(t *testing.T) {
|
func TestListContainers(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 2)
|
addContainers(&server, 2)
|
||||||
@ -158,6 +177,25 @@ func TestCreateContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateContainerWithNotifyChannel(t *testing.T) {
|
||||||
|
ch := make(chan *docker.Container, 1)
|
||||||
|
server := DockerServer{}
|
||||||
|
server.imgIDs = map[string]string{"base": "a1234"}
|
||||||
|
server.cChan = ch
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true,
|
||||||
|
"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":""}`
|
||||||
|
request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body))
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusCreated {
|
||||||
|
t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
|
||||||
|
}
|
||||||
|
if notified := <-ch; notified != server.containers[0] {
|
||||||
|
t.Errorf("CreateContainer: did not notify the proper container. Want %q. Got %q.", server.containers[0].ID, notified.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateContainerInvalidBody(t *testing.T) {
|
func TestCreateContainerInvalidBody(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
@ -320,6 +358,25 @@ func TestStartContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStartContainerWithNotifyChannel(t *testing.T) {
|
||||||
|
ch := make(chan *docker.Container, 1)
|
||||||
|
server := DockerServer{}
|
||||||
|
server.cChan = ch
|
||||||
|
addContainers(&server, 1)
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/start", server.containers[1].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusOK {
|
||||||
|
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||||
|
}
|
||||||
|
if notified := <-ch; notified != server.containers[1] {
|
||||||
|
t.Errorf("StartContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStartContainerNotFound(t *testing.T) {
|
func TestStartContainerNotFound(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
@ -363,6 +420,26 @@ func TestStopContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStopContainerWithNotifyChannel(t *testing.T) {
|
||||||
|
ch := make(chan *docker.Container, 1)
|
||||||
|
server := DockerServer{}
|
||||||
|
server.cChan = ch
|
||||||
|
addContainers(&server, 1)
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.containers[1].State.Running = true
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/stop", server.containers[1].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusNoContent {
|
||||||
|
t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code)
|
||||||
|
}
|
||||||
|
if notified := <-ch; notified != server.containers[1] {
|
||||||
|
t.Errorf("StopContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStopContainerNotFound(t *testing.T) {
|
func TestStopContainerNotFound(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
@ -388,6 +465,90 @@ func TestStopContainerNotRunning(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPauseContainer(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/pause", server.containers[0].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusNoContent {
|
||||||
|
t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code)
|
||||||
|
}
|
||||||
|
if !server.containers[0].State.Paused {
|
||||||
|
t.Error("PauseContainer: did not pause the container")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPauseContainerAlreadyPaused(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.containers[0].State.Paused = true
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/pause", server.containers[0].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusBadRequest {
|
||||||
|
t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPauseContainerNotFound(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := "/containers/abc123/pause"
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusNotFound {
|
||||||
|
t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpauseContainer(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.containers[0].State.Paused = true
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/unpause", server.containers[0].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusNoContent {
|
||||||
|
t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code)
|
||||||
|
}
|
||||||
|
if server.containers[0].State.Paused {
|
||||||
|
t.Error("UnpauseContainer: did not unpause the container")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpauseContainerNotPaused(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/unpause", server.containers[0].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusBadRequest {
|
||||||
|
t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpauseContainerNotFound(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
server.buildMuxer()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := "/containers/abc123/unpause"
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusNotFound {
|
||||||
|
t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWaitContainer(t *testing.T) {
|
func TestWaitContainer(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 1)
|
addContainers(&server, 1)
|
||||||
@ -405,7 +566,25 @@ func TestWaitContainer(t *testing.T) {
|
|||||||
if recorder.Code != http.StatusOK {
|
if recorder.Code != http.StatusOK {
|
||||||
t.Errorf("WaitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
t.Errorf("WaitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||||
}
|
}
|
||||||
expected := `{"StatusCode":0}`
|
expected := `{"StatusCode":0}` + "\n"
|
||||||
|
if body := recorder.Body.String(); body != expected {
|
||||||
|
t.Errorf("WaitContainer: wrong body. Want %q. Got %q.", expected, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWaitContainerStatus(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
addContainers(&server, 1)
|
||||||
|
server.buildMuxer()
|
||||||
|
server.containers[0].State.ExitCode = 63
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
path := fmt.Sprintf("/containers/%s/wait", server.containers[0].ID)
|
||||||
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
|
server.ServeHTTP(recorder, request)
|
||||||
|
if recorder.Code != http.StatusOK {
|
||||||
|
t.Errorf("WaitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||||
|
}
|
||||||
|
expected := `{"StatusCode":63}` + "\n"
|
||||||
if body := recorder.Body.String(); body != expected {
|
if body := recorder.Body.String(); body != expected {
|
||||||
t.Errorf("WaitContainer: wrong body. Want %q. Got %q.", expected, body)
|
t.Errorf("WaitContainer: wrong body. Want %q. Got %q.", expected, body)
|
||||||
}
|
}
|
||||||
@ -659,70 +838,33 @@ func TestRemoveImageByName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPrepareFailure(t *testing.T) {
|
func TestPrepareFailure(t *testing.T) {
|
||||||
server := DockerServer{failures: make(map[string]FailureSpec)}
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
errorId := "my_error"
|
errorID := "my_error"
|
||||||
failure := FailureSpec{UrlRegex: "containers/json"}
|
server.PrepareFailure(errorID, "containers/json")
|
||||||
server.PrepareFailure(errorId, failure)
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||||
server.ServeHTTP(recorder, request)
|
server.ServeHTTP(recorder, request)
|
||||||
if recorder.Code != http.StatusBadRequest {
|
if recorder.Code != http.StatusBadRequest {
|
||||||
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||||
}
|
}
|
||||||
if recorder.Body.String() != errorId+"\n" {
|
if recorder.Body.String() != errorID+"\n" {
|
||||||
t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorId, recorder.Body.String())
|
t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrepareFailureUsingContainerPath(t *testing.T) {
|
|
||||||
server := DockerServer{failures: make(map[string]FailureSpec)}
|
|
||||||
addContainers(&server, 1)
|
|
||||||
server.buildMuxer()
|
|
||||||
errorId := "my_path_error"
|
|
||||||
failure := FailureSpec{UrlRegex: "containers/.*?/start", ContainerPath: "ls"}
|
|
||||||
server.PrepareFailure(errorId, failure)
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
|
|
||||||
request, _ := http.NewRequest("POST", path, nil)
|
|
||||||
server.ServeHTTP(recorder, request)
|
|
||||||
if recorder.Code != http.StatusBadRequest {
|
|
||||||
t.Errorf("TestPrepareFailureUsingContainerPath: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
|
||||||
}
|
|
||||||
if recorder.Body.String() != errorId+"\n" {
|
|
||||||
t.Errorf("TestPrepareFailureUsingContainerPath: wrong message. Want %s. Got %s.", errorId, recorder.Body.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrepareFailureUsingContainerPathWithWrongPath(t *testing.T) {
|
|
||||||
server := DockerServer{failures: make(map[string]FailureSpec)}
|
|
||||||
addContainers(&server, 1)
|
|
||||||
server.buildMuxer()
|
|
||||||
errorId := "my_path_error"
|
|
||||||
failure := FailureSpec{UrlRegex: "containers/.*?/start", ContainerPath: "xxx"}
|
|
||||||
server.PrepareFailure(errorId, failure)
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
|
|
||||||
request, _ := http.NewRequest("POST", path, nil)
|
|
||||||
server.ServeHTTP(recorder, request)
|
|
||||||
if recorder.Code != http.StatusOK {
|
|
||||||
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveFailure(t *testing.T) {
|
func TestRemoveFailure(t *testing.T) {
|
||||||
server := DockerServer{failures: make(map[string]FailureSpec)}
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
errorId := "my_error"
|
errorID := "my_error"
|
||||||
failure := FailureSpec{UrlRegex: "containers/json"}
|
server.PrepareFailure(errorID, "containers/json")
|
||||||
server.PrepareFailure(errorId, failure)
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||||
server.ServeHTTP(recorder, request)
|
server.ServeHTTP(recorder, request)
|
||||||
if recorder.Code != http.StatusBadRequest {
|
if recorder.Code != http.StatusBadRequest {
|
||||||
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||||
}
|
}
|
||||||
server.ResetFailure(errorId)
|
server.ResetFailure(errorID)
|
||||||
recorder = httptest.NewRecorder()
|
recorder = httptest.NewRecorder()
|
||||||
request, _ = http.NewRequest("GET", "/containers/json?all=1", nil)
|
request, _ = http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||||
server.ServeHTTP(recorder, request)
|
server.ServeHTTP(recorder, request)
|
||||||
@ -731,6 +873,34 @@ func TestRemoveFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMutateContainer(t *testing.T) {
|
||||||
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
|
server.buildMuxer()
|
||||||
|
server.containers = append(server.containers, &docker.Container{ID: "id123"})
|
||||||
|
state := docker.State{Running: false, ExitCode: 1}
|
||||||
|
err := server.MutateContainer("id123", state)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(server.containers[0].State, state) {
|
||||||
|
t.Errorf("Wrong state after mutation.\nWant %#v.\nGot %#v.",
|
||||||
|
state, server.containers[0].State)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMutateContainerNotFound(t *testing.T) {
|
||||||
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
|
server.buildMuxer()
|
||||||
|
state := docker.State{Running: false, ExitCode: 1}
|
||||||
|
err := server.MutateContainer("id123", state)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Unexpected <nil> error")
|
||||||
|
}
|
||||||
|
if err.Error() != "container not found" {
|
||||||
|
t.Errorf("wrong error message. Want %q. Got %q.", "container not found", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildImageWithContentTypeTar(t *testing.T) {
|
func TestBuildImageWithContentTypeTar(t *testing.T) {
|
||||||
server := DockerServer{imgIDs: make(map[string]string)}
|
server := DockerServer{imgIDs: make(map[string]string)}
|
||||||
imageName := "teste"
|
imageName := "teste"
|
||||||
@ -762,3 +932,16 @@ func TestBuildImageWithRemoteDockerfile(t *testing.T) {
|
|||||||
t.Errorf("BuildImage: image %s not builded", imageName)
|
t.Errorf("BuildImage: image %s not builded", imageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPing(t *testing.T) {
|
||||||
|
server := DockerServer{}
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
request, _ := http.NewRequest("GET", "/_ping", nil)
|
||||||
|
server.pingDocker(recorder, request)
|
||||||
|
if recorder.Body.String() != "" {
|
||||||
|
t.Errorf("Ping: Unexpected body: %s", recorder.Body.String())
|
||||||
|
}
|
||||||
|
if recorder.Code != http.StatusOK {
|
||||||
|
t.Errorf("Ping: Expected code %d, got: %d", http.StatusOK, recorder.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -87,9 +87,12 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
|||||||
var nr2 int
|
var nr2 int
|
||||||
nr2, er = src.Read(buf[nr:])
|
nr2, er = src.Read(buf[nr:])
|
||||||
if er == io.EOF {
|
if er == io.EOF {
|
||||||
return written, nil
|
if nr < StdWriterPrefixLen && nr2 < StdWriterPrefixLen {
|
||||||
}
|
return written, nil
|
||||||
if er != nil {
|
}
|
||||||
|
nr += nr2
|
||||||
|
break
|
||||||
|
} else if er != nil {
|
||||||
return 0, er
|
return 0, er
|
||||||
}
|
}
|
||||||
nr += nr2
|
nr += nr2
|
||||||
@ -117,7 +120,7 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
|||||||
// Extend it if necessary.
|
// Extend it if necessary.
|
||||||
if frameSize+StdWriterPrefixLen > bufLen {
|
if frameSize+StdWriterPrefixLen > bufLen {
|
||||||
Debugf("Extending buffer cap.")
|
Debugf("Extending buffer cap.")
|
||||||
buf = append(buf, make([]byte, frameSize-len(buf)+1)...)
|
buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-len(buf)+1)...)
|
||||||
bufLen = len(buf)
|
bufLen = len(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,9 +129,12 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
|||||||
var nr2 int
|
var nr2 int
|
||||||
nr2, er = src.Read(buf[nr:])
|
nr2, er = src.Read(buf[nr:])
|
||||||
if er == io.EOF {
|
if er == io.EOF {
|
||||||
return written, nil
|
if nr == 0 {
|
||||||
}
|
return written, nil
|
||||||
if er != nil {
|
}
|
||||||
|
nr += nr2
|
||||||
|
break
|
||||||
|
} else if er != nil {
|
||||||
Debugf("Error reading frame: %s", er)
|
Debugf("Error reading frame: %s", er)
|
||||||
return 0, er
|
return 0, er
|
||||||
}
|
}
|
||||||
@ -136,7 +142,11 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the retrieved frame (without header)
|
// Write the retrieved frame (without header)
|
||||||
nw, ew = out.Write(buf[StdWriterPrefixLen : frameSize+StdWriterPrefixLen])
|
bound := frameSize + StdWriterPrefixLen
|
||||||
|
if bound > nr {
|
||||||
|
bound = nr
|
||||||
|
}
|
||||||
|
nw, ew = out.Write(buf[StdWriterPrefixLen:bound])
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
written += int64(nw)
|
written += int64(nw)
|
||||||
}
|
}
|
||||||
@ -147,7 +157,7 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
|||||||
// If the frame has not been fully written: error
|
// If the frame has not been fully written: error
|
||||||
if nw != frameSize {
|
if nw != frameSize {
|
||||||
Debugf("Error Short Write: (%d on %d)", nw, frameSize)
|
Debugf("Error Short Write: (%d on %d)", nw, frameSize)
|
||||||
return 0, io.ErrShortWrite
|
return written, io.ErrShortWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the rest of the buffer to the beginning
|
// Move the rest of the buffer to the beginning
|
||||||
|
217
third_party/src/github.com/fsouza/go-dockerclient/utils/stdcopy_test.go
vendored
Normal file
217
third_party/src/github.com/fsouza/go-dockerclient/utils/stdcopy_test.go
vendored
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the DOCKER-LICENSE file.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"testing/iotest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type errorWriter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (errorWriter) Write([]byte) (int, error) {
|
||||||
|
return 0, errors.New("something went wrong")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopy(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
input.Write([]byte{1, 0, 0, 0, 0, 0, 0, 12})
|
||||||
|
input.Write([]byte("just kidding"))
|
||||||
|
input.Write([]byte{0, 0, 0, 0, 0, 0, 0, 6})
|
||||||
|
input.Write([]byte("\nyeah!"))
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := int64(19 + 12 + 6); n != expected {
|
||||||
|
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "something happened!" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "just kidding\nyeah!" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "just kidding\nyeah!", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyStress(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
value := strings.Repeat("something ", 4096)
|
||||||
|
writer := NewStdWriter(&input, Stdout)
|
||||||
|
writer.Write([]byte(value))
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if n != 40960 {
|
||||||
|
t.Errorf("Wrong number of bytes. Want 40960. Got %d.", n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want empty string. Got %q", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != value {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q", value, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyInvalidStdHeader(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{3, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d", n)
|
||||||
|
}
|
||||||
|
if err != ErrInvalidStdHeader {
|
||||||
|
t.Errorf("StdCopy: wrong error. Want ErrInvalidStdHeader. Got %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyBigFrame(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 18})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := int64(18); n != expected {
|
||||||
|
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "something happened" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopySmallFrame(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 20})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != io.ErrShortWrite {
|
||||||
|
t.Errorf("StdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||||
|
}
|
||||||
|
if expected := int64(19); n != expected {
|
||||||
|
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "something happened!" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyEmpty(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyCorruptedHeader(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0})
|
||||||
|
n, err := StdCopy(&stdout, &stderr, &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyTruncateWriter(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
n, err := StdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := int64(19); n != expected {
|
||||||
|
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "somethi" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "somethi", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyHeaderOnly(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
n, err := StdCopy(&stdout, iotest.TruncateWriter(&stderr, 7), &input)
|
||||||
|
if err != io.ErrShortWrite {
|
||||||
|
t.Errorf("StdCopy: wrong error. Want ShortWrite. Got %#v", err)
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("Wrong number of bytes. Want 0. Got %d.", n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyDataErrReader(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
n, err := StdCopy(&stdout, &stderr, iotest.DataErrReader(&input))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := int64(19); n != expected {
|
||||||
|
t.Errorf("Wrong number of bytes. Want %d. Got %d.", expected, n)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "something happened!" {
|
||||||
|
t.Errorf("StdCopy: wrong stderr. Want %q. Got %q.", "something happened!", got)
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Errorf("StdCopy: wrong stdout. Want %q. Got %q.", "", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyTimeoutReader(t *testing.T) {
|
||||||
|
var input, stdout, stderr bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
_, err := StdCopy(&stdout, &stderr, iotest.TimeoutReader(&input))
|
||||||
|
if err != iotest.ErrTimeout {
|
||||||
|
t.Errorf("StdCopy: wrong error. Want ErrTimeout. Got %#v.", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdCopyWriteError(t *testing.T) {
|
||||||
|
var input bytes.Buffer
|
||||||
|
input.Write([]byte{2, 0, 0, 0, 0, 0, 0, 19})
|
||||||
|
input.Write([]byte("something happened!"))
|
||||||
|
var stdout, stderr errorWriter
|
||||||
|
n, err := StdCopy(stdout, stderr, &input)
|
||||||
|
if err.Error() != "something went wrong" {
|
||||||
|
t.Errorf("StdCopy: wrong error. Want %q. Got %q", "something went wrong", err)
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("StdCopy: wrong number of bytes. Want 0. Got %d.", n)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user