third_party: update go-dockerclient

math.MaxInt64 represents 8 exabytes, which is a good limit for memory.
Also, this is the type used by Docker, so it's not possible to get any
value bigger than math.MaxInt64 as memory limit (both ram and swap) on a
Docker container.

Relevant discussion at #589 (more precisely,
https://github.com/GoogleCloudPlatform/kubernetes/pull/589#issuecomment-50640605).
pull/6/head
Francisco Souza 2014-07-30 15:18:42 -03:00
parent dc6fdc423d
commit 2a345ffa9e
16 changed files with 656 additions and 101 deletions

View File

@ -289,7 +289,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: uint64(container.Memory), Memory: int64(container.Memory),
CpuShares: int64(milliCPUToShares(container.CPU)), CpuShares: int64(milliCPUToShares(container.CPU)),
Volumes: volumes, Volumes: volumes,
WorkingDir: container.WorkingDir, WorkingDir: container.WorkingDir,

View File

@ -1,5 +1,6 @@
# This is the official list of go-dockerclient authors for copyright purposes. # This is the official list of go-dockerclient authors for copyright purposes.
Andreas Jaekle <andreas@jaekle.net>
Andrews Medina <andrewsmedina@gmail.com> Andrews Medina <andrewsmedina@gmail.com>
Andy Goldstein <andy.goldstein@redhat.com> Andy Goldstein <andy.goldstein@redhat.com>
Ben McCann <benmccann.com> Ben McCann <benmccann.com>
@ -8,6 +9,7 @@ Cheah Chu Yeow <chuyeow@gmail.com>
cheneydeng <cheneydeng@qq.com> cheneydeng <cheneydeng@qq.com>
Ed <edrocksit@gmail.com> Ed <edrocksit@gmail.com>
Eric Anderson <anderson@copperegg.com> Eric Anderson <anderson@copperegg.com>
Fabio Rehm <fgrehm@gmail.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> Jari Kolehmainen <jari.kolehmainen@digia.com>
@ -15,6 +17,8 @@ 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>
Jeffrey Hulten <jhulten@gmail.com> Jeffrey Hulten <jhulten@gmail.com>
Johan Euphrosine <proppy@google.com>
Karan Misra <kidoman@gmail.com>
Lucas Clemente <lucas@clemente.io> Lucas Clemente <lucas@clemente.io>
Omeid Matten <public@omeid.me> Omeid Matten <public@omeid.me>
Paul Morie <pmorie@gmail.com> Paul Morie <pmorie@gmail.com>

View File

@ -11,27 +11,29 @@ For more details, check the [remote API documentation](http://docs.docker.io/en/
## Example ## Example
package main ```go
package main
import ( import (
"fmt" "fmt"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
) )
func main() { func main() {
endpoint := "unix:///var/run/docker.sock" endpoint := "unix:///var/run/docker.sock"
client, _ := docker.NewClient(endpoint) client, _ := docker.NewClient(endpoint)
imgs, _ := client.ListImages(true) imgs, _ := client.ListImages(true)
for _, img := range imgs { for _, img := range imgs {
fmt.Println("ID: ", img.ID) fmt.Println("ID: ", img.ID)
fmt.Println("RepoTags: ", img.RepoTags) fmt.Println("RepoTags: ", img.RepoTags)
fmt.Println("Created: ", img.Created) fmt.Println("Created: ", img.Created)
fmt.Println("Size: ", img.Size) fmt.Println("Size: ", img.Size)
fmt.Println("VirtualSize: ", img.VirtualSize) fmt.Println("VirtualSize: ", img.VirtualSize)
fmt.Println("ParentId: ", img.ParentId) fmt.Println("ParentId: ", img.ParentId)
fmt.Println("Repository: ", img.Repository) fmt.Println("Repository: ", img.Repository)
} }
} }
```
## Developing ## Developing

View File

@ -21,7 +21,6 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/fsouza/go-dockerclient/utils" "github.com/fsouza/go-dockerclient/utils"
) )
@ -113,11 +112,11 @@ func (version ApiVersion) compare(other ApiVersion) int {
// interaction with the API. // interaction with the API.
type Client struct { type Client struct {
SkipServerVersionCheck bool SkipServerVersionCheck bool
HTTPClient *http.Client
endpoint string endpoint string
endpointURL *url.URL endpointURL *url.URL
eventMonitor *eventMonitoringState eventMonitor *eventMonitoringState
client *http.Client
requestedApiVersion ApiVersion requestedApiVersion ApiVersion
serverApiVersion ApiVersion serverApiVersion ApiVersion
expectedApiVersion ApiVersion expectedApiVersion ApiVersion
@ -142,7 +141,6 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
var requestedApiVersion ApiVersion var requestedApiVersion ApiVersion
if strings.Contains(apiVersionString, ".") { if strings.Contains(apiVersionString, ".") {
requestedApiVersion, err = NewApiVersion(apiVersionString) requestedApiVersion, err = NewApiVersion(apiVersionString)
@ -150,11 +148,10 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
return nil, err return nil, err
} }
} }
return &Client{ return &Client{
HTTPClient: http.DefaultClient,
endpoint: endpoint, endpoint: endpoint,
endpointURL: u, endpointURL: u,
client: http.DefaultClient,
eventMonitor: new(eventMonitoringState), eventMonitor: new(eventMonitoringState),
requestedApiVersion: requestedApiVersion, requestedApiVersion: requestedApiVersion,
}, nil }, nil
@ -177,29 +174,6 @@ func (c *Client) checkApiVersion() error {
return nil 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
}
// Ping pings the docker server // Ping pings the docker server
// //
// See http://goo.gl/stJENm for more details. // See http://goo.gl/stJENm for more details.
@ -223,13 +197,11 @@ func (c *Client) getServerApiVersionString() (version string, err error) {
if status != http.StatusOK { if status != http.StatusOK {
return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", status) return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", status)
} }
var versionResponse map[string]string var versionResponse map[string]string
err = json.Unmarshal(body, &versionResponse) err = json.Unmarshal(body, &versionResponse)
if err != nil { if err != nil {
return "", err return "", err
} }
version = versionResponse["ApiVersion"] version = versionResponse["ApiVersion"]
return version, nil return version, nil
} }
@ -243,14 +215,12 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
} }
params = bytes.NewBuffer(buf) params = bytes.NewBuffer(buf)
} }
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 {
return nil, -1, err return nil, -1, err
} }
} }
req, err := http.NewRequest(method, c.getURL(path), params) req, err := http.NewRequest(method, c.getURL(path), params)
if err != nil { if err != nil {
return nil, -1, err return nil, -1, err
@ -277,7 +247,7 @@ func (c *Client) do(method, path string, data interface{}) ([]byte, int, error)
} }
defer clientconn.Close() defer clientconn.Close()
} else { } else {
resp, err = c.client.Do(req) resp, err = c.HTTPClient.Do(req)
} }
if err != nil { if err != nil {
if strings.Contains(err.Error(), "connection refused") { if strings.Contains(err.Error(), "connection refused") {
@ -335,7 +305,7 @@ func (c *Client) stream(method, path string, setRawTerminal bool, headers map[st
resp, err = clientconn.Do(req) resp, err = clientconn.Do(req)
defer clientconn.Close() defer clientconn.Close()
} else { } else {
resp, err = c.client.Do(req) resp, err = c.HTTPClient.Do(req)
} }
if err != nil { if err != nil {
if strings.Contains(err.Error(), "connection refused") { if strings.Contains(err.Error(), "connection refused") {
@ -418,10 +388,10 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
<-success <-success
} }
rwc, br := clientconn.Hijack() rwc, br := clientconn.Hijack()
var wg sync.WaitGroup
wg.Add(2)
errs := make(chan error, 2) errs := make(chan error, 2)
exit := make(chan bool)
go func() { go func() {
defer close(exit)
var err error var err error
if setRawTerminal { if setRawTerminal {
_, err = io.Copy(stdout, br) _, err = io.Copy(stdout, br)
@ -429,7 +399,6 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
_, err = utils.StdCopy(stdout, stderr, br) _, err = utils.StdCopy(stdout, stderr, br)
} }
errs <- err errs <- err
wg.Done()
}() }()
go func() { go func() {
var err error var err error
@ -440,14 +409,9 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
CloseWrite() error CloseWrite() error
}).CloseWrite() }).CloseWrite()
errs <- err errs <- err
wg.Done()
}() }()
wg.Wait() <-exit
close(errs) return <-errs
if err := <-errs; err != nil {
return err
}
return nil
} }
func (c *Client) getURL(path string) string { func (c *Client) getURL(path string) string {

View File

@ -24,10 +24,9 @@ func TestNewAPIClient(t *testing.T) {
if client.endpoint != endpoint { if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
} }
if client.client != http.DefaultClient { if client.HTTPClient != http.DefaultClient {
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.client) t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.HTTPClient)
} }
// test unix socket endpoints // test unix socket endpoints
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
client, err = NewClient(endpoint) client, err = NewClient(endpoint)
@ -54,8 +53,8 @@ func TestNewVersionedClient(t *testing.T) {
if client.endpoint != endpoint { if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
} }
if client.client != http.DefaultClient { if client.HTTPClient != http.DefaultClient {
t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.client) t.Errorf("Expected http.Client %#v. Got %#v.", http.DefaultClient, client.HTTPClient)
} }
if reqVersion := client.requestedApiVersion.String(); reqVersion != "1.12" { if reqVersion := client.requestedApiVersion.String(); reqVersion != "1.12" {
t.Errorf("Wrong requestApiVersion. Want %q. Got %q.", "1.12", reqVersion) t.Errorf("Wrong requestApiVersion. Want %q. Got %q.", "1.12", reqVersion)

View File

@ -158,8 +158,8 @@ type Config struct {
Hostname string Hostname string
Domainname string Domainname string
User string User string
Memory uint64 Memory int64
MemorySwap uint64 MemorySwap int64
CpuShares int64 CpuShares int64
AttachStdin bool AttachStdin bool
AttachStdout bool AttachStdout bool
@ -297,7 +297,7 @@ type HostConfig struct {
NetworkMode string NetworkMode string
} }
// StartContainer starts a container, returning an errror in case of failure. // StartContainer starts a container, returning an error in case of failure.
// //
// See http://goo.gl/y5GZlE for more details. // See http://goo.gl/y5GZlE for more details.
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error { func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {

View File

@ -225,6 +225,88 @@ func TestInspectContainer(t *testing.T) {
} }
} }
func TestInspectContainerNegativeSwap(t *testing.T) {
jsonContainer := `{
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
"Created": "2013-05-07T14:51:42.087658+02:00",
"Path": "date",
"Args": [],
"Config": {
"Hostname": "4fa6e0f0c678",
"User": "",
"Memory": 17179869184,
"MemorySwap": -1,
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"PortSpecs": null,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"date"
],
"Image": "base",
"Volumes": {},
"VolumesFrom": ""
},
"State": {
"Running": false,
"Pid": 0,
"ExitCode": 0,
"StartedAt": "2013-05-07T14:51:42.087658+02:00",
"Ghost": false
},
"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
"NetworkSettings": {
"IpAddress": "",
"IpPrefixLen": 0,
"Gateway": "",
"Bridge": "",
"PortMapping": null
},
"SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
"ResolvConfPath": "/etc/resolv.conf",
"Volumes": {},
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LxcConf": [],
"Privileged": false,
"PortBindings": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "49153"
}
]
},
"Links": null,
"PublishAllPorts": false
}
}`
var expected Container
err := json.Unmarshal([]byte(jsonContainer), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c678"
container, err := client.InspectContainer(id)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*container, expected) {
t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container)
}
expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestInspectContainerFailure(t *testing.T) { func TestInspectContainerFailure(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "server error", status: 500}) client := newTestClient(&FakeRoundTripper{message: "server error", status: 500})
expected := Error{Status: 500, Message: "server error"} expected := Error{Status: 500, Message: "server error"}
@ -1184,9 +1266,9 @@ func TestExportContainerViaUnixSocket(t *testing.T) {
endpoint := "unix://" + tempSocket endpoint := "unix://" + tempSocket
u, _ := parseEndpoint(endpoint) u, _ := parseEndpoint(endpoint)
client := Client{ client := Client{
HTTPClient: http.DefaultClient,
endpoint: endpoint, endpoint: endpoint,
endpointURL: u, endpointURL: u,
client: http.DefaultClient,
SkipServerVersionCheck: true, SkipServerVersionCheck: true,
} }
listening := make(chan string) listening := make(chan string)

View File

@ -0,0 +1,137 @@
// Copyright 2014 Docker 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 docker
import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
)
type Env []string
func (env *Env) Get(key string) (value string) {
return env.Map()[key]
}
func (env *Env) Exists(key string) bool {
_, exists := env.Map()[key]
return exists
}
func (env *Env) GetBool(key string) (value bool) {
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
return false
}
return true
}
func (env *Env) SetBool(key string, value bool) {
if value {
env.Set(key, "1")
} else {
env.Set(key, "0")
}
}
func (env *Env) GetInt(key string) int {
return int(env.GetInt64(key))
}
func (env *Env) SetInt(key string, value int) {
env.Set(key, strconv.Itoa(value))
}
func (env *Env) GetInt64(key string) int64 {
s := strings.Trim(env.Get(key), " \t")
val, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return -1
}
return val
}
func (env *Env) SetInt64(key string, value int64) {
env.Set(key, strconv.FormatInt(value, 10))
}
func (env *Env) GetJson(key string, iface interface{}) error {
sval := env.Get(key)
if sval == "" {
return nil
}
return json.Unmarshal([]byte(sval), iface)
}
func (env *Env) SetJson(key string, value interface{}) error {
sval, err := json.Marshal(value)
if err != nil {
return err
}
env.Set(key, string(sval))
return nil
}
func (env *Env) GetList(key string) []string {
sval := env.Get(key)
if sval == "" {
return nil
}
var l []string
if err := json.Unmarshal([]byte(sval), &l); err != nil {
l = append(l, sval)
}
return l
}
func (env *Env) SetList(key string, value []string) error {
return env.SetJson(key, value)
}
func (env *Env) Set(key, value string) {
*env = append(*env, key+"="+value)
}
// Decode decodes `src` as a json dictionary, and adds each decoded key-value
// pair to the environment.
//
// If `src` cannot be decoded as a json dictionary, an error is returned.
func (env *Env) Decode(src io.Reader) error {
m := make(map[string]interface{})
if err := json.NewDecoder(src).Decode(&m); err != nil {
return err
}
for k, v := range m {
env.SetAuto(k, v)
}
return nil
}
func (env *Env) SetAuto(k string, v interface{}) {
if fval, ok := v.(float64); ok {
env.SetInt64(k, int64(fval))
} else if sval, ok := v.(string); ok {
env.Set(k, sval)
} else if val, err := json.Marshal(v); err == nil {
env.Set(k, string(val))
} else {
env.Set(k, fmt.Sprintf("%v", v))
}
}
func (env *Env) Map() map[string]string {
if len(*env) == 0 {
return nil
}
m := make(map[string]string)
for _, kv := range *env {
parts := strings.SplitN(kv, "=", 2)
m[parts[0]] = parts[1]
}
return m
}

View File

@ -0,0 +1,349 @@
// 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 docker
import (
"bytes"
"errors"
"reflect"
"sort"
"testing"
)
func TestGet(t *testing.T) {
var tests = []struct {
input []string
query string
expected string
}{
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PATH", "/usr/bin:/bin"},
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", "/usr/local"},
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", ""},
{[]string{"WAT="}, "WAT", ""},
}
for _, tt := range tests {
env := Env(tt.input)
got := env.Get(tt.query)
if got != tt.expected {
t.Errorf("Env.Get(%q): wrong result. Want %q. Got %q", tt.query, tt.expected, got)
}
}
}
func TestExists(t *testing.T) {
var tests = []struct {
input []string
query string
expected bool
}{
{[]string{"WAT=", "PYTHONPATH=/usr/local"}, "WAT", true},
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", true},
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", false},
}
for _, tt := range tests {
env := Env(tt.input)
got := env.Exists(tt.query)
if got != tt.expected {
t.Errorf("Env.Exists(%q): wrong result. Want %v. Got %v", tt.query, tt.expected, got)
}
}
}
func TestGetBool(t *testing.T) {
var tests = []struct {
input string
expected bool
}{
{"EMTPY_VAR", false}, {"ZERO_VAR", false}, {"NO_VAR", false},
{"FALSE_VAR", false}, {"NONE_VAR", false}, {"TRUE_VAR", true},
{"WAT", true}, {"PATH", true}, {"ONE_VAR", true}, {"NO_VAR_TAB", false},
}
env := Env([]string{
"EMPTY_VAR=", "ZERO_VAR=0", "NO_VAR=no", "FALSE_VAR=false",
"NONE_VAR=none", "TRUE_VAR=true", "WAT=wat", "PATH=/usr/bin:/bin",
"ONE_VAR=1", "NO_VAR_TAB=0 \t\t\t",
})
for _, tt := range tests {
got := env.GetBool(tt.input)
if got != tt.expected {
t.Errorf("Env.GetBool(%q): wrong result. Want %v. Got %v.", tt.input, tt.expected, got)
}
}
}
func TestSetBool(t *testing.T) {
var tests = []struct {
input bool
expected string
}{
{true, "1"}, {false, "0"},
}
for _, tt := range tests {
var env Env
env.SetBool("SOME", tt.input)
if got := env.Get("SOME"); got != tt.expected {
t.Errorf("Env.SetBool(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
}
}
}
func TestGetInt(t *testing.T) {
var tests = []struct {
input string
expected int
}{
{"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2},
}
env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"})
for _, tt := range tests {
got := env.GetInt(tt.input)
if got != tt.expected {
t.Errorf("Env.GetInt(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got)
}
}
}
func TestSetInt(t *testing.T) {
var tests = []struct {
input int
expected string
}{
{10, "10"}, {13, "13"}, {7, "7"}, {33, "33"},
{0, "0"}, {-34, "-34"},
}
for _, tt := range tests {
var env Env
env.SetInt("SOME", tt.input)
if got := env.Get("SOME"); got != tt.expected {
t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
}
}
}
func TestGetInt64(t *testing.T) {
var tests = []struct {
input string
expected int64
}{
{"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2},
}
env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"})
for _, tt := range tests {
got := env.GetInt64(tt.input)
if got != tt.expected {
t.Errorf("Env.GetInt64(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got)
}
}
}
func TestSetInt64(t *testing.T) {
var tests = []struct {
input int64
expected string
}{
{10, "10"}, {13, "13"}, {7, "7"}, {33, "33"},
{0, "0"}, {-34, "-34"},
}
for _, tt := range tests {
var env Env
env.SetInt64("SOME", tt.input)
if got := env.Get("SOME"); got != tt.expected {
t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
}
}
}
func TestGetJson(t *testing.T) {
var p struct {
Name string `json:"name"`
Age int `json:"age"`
}
var env Env
env.Set("person", `{"name":"Gopher","age":5}`)
err := env.GetJson("person", &p)
if err != nil {
t.Error(err)
}
if p.Name != "Gopher" {
t.Errorf("Env.GetJson(%q): wrong name. Want %q. Got %q", "person", "Gopher", p.Name)
}
if p.Age != 5 {
t.Errorf("Env.GetJson(%q): wrong age. Want %d. Got %d", "person", 5, p.Age)
}
}
func TestGetJsonAbsent(t *testing.T) {
var l []string
var env Env
err := env.GetJson("person", &l)
if err != nil {
t.Error(err)
}
if l != nil {
t.Errorf("Env.GetJson(): get unexpected list %v", l)
}
}
func TestGetJsonFailure(t *testing.T) {
var p []string
var env Env
env.Set("list-person", `{"name":"Gopher","age":5}`)
err := env.GetJson("list-person", &p)
if err == nil {
t.Errorf("Env.GetJson(%q): got unexpected <nil> error.", "list-person")
}
}
func TestSetJson(t *testing.T) {
var p1 = struct {
Name string `json:"name"`
Age int `json:"age"`
}{Name: "Gopher", Age: 5}
var env Env
err := env.SetJson("person", p1)
if err != nil {
t.Error(err)
}
var p2 struct {
Name string `json:"name"`
Age int `json:"age"`
}
err = env.GetJson("person", &p2)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(p1, p2) {
t.Errorf("Env.SetJson(%q): wrong result. Want %v. Got %v", "person", p1, p2)
}
}
func TestSetJsonFailure(t *testing.T) {
var env Env
err := env.SetJson("person", unmarshable{})
if err == nil {
t.Error("Env.SetJson(): got unexpected <nil> error")
}
if env.Exists("person") {
t.Errorf("Env.SetJson(): should not define the key %q, but did", "person")
}
}
func TestGetList(t *testing.T) {
var tests = []struct {
input string
expected []string
}{
{"WAT=wat", []string{"wat"}},
{`WAT=["wat","wet","wit","wot","wut"]`, []string{"wat", "wet", "wit", "wot", "wut"}},
{"WAT=", nil},
}
for _, tt := range tests {
env := Env([]string{tt.input})
got := env.GetList("WAT")
if !reflect.DeepEqual(got, tt.expected) {
t.Errorf("Env.GetList(%q): wrong result. Want %v. Got %v", "WAT", tt.expected, got)
}
}
}
func TestSetList(t *testing.T) {
list := []string{"a", "b", "c"}
var env Env
env.SetList("SOME", list)
if got := env.GetList("SOME"); !reflect.DeepEqual(got, list) {
t.Errorf("Env.SetList(%v): wrong result. Got %v", list, got)
}
}
func TestSet(t *testing.T) {
var env Env
env.Set("PATH", "/home/bin:/bin")
env.Set("SOMETHING", "/usr/bin")
env.Set("PATH", "/bin")
if expected, got := "/usr/bin", env.Get("SOMETHING"); got != expected {
t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got)
}
if expected, got := "/bin", env.Get("PATH"); got != expected {
t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got)
}
}
func TestDecode(t *testing.T) {
var tests = []struct {
input string
expectedOut []string
expectedErr string
}{
{
`{"PATH":"/usr/bin:/bin","containers":54,"wat":["123","345"]}`,
[]string{"PATH=/usr/bin:/bin", "containers=54", `wat=["123","345"]`},
"",
},
{"}}", nil, "invalid character '}' looking for beginning of value"},
{`{}`, nil, ""},
}
for _, tt := range tests {
var env Env
err := env.Decode(bytes.NewBufferString(tt.input))
if tt.expectedErr == "" {
if err != nil {
t.Error(err)
}
} else if tt.expectedErr != err.Error() {
t.Errorf("Env.Decode(): invalid error. Want %q. Got %q.", tt.expectedErr, err)
}
got := []string(env)
sort.Strings(got)
sort.Strings(tt.expectedOut)
if !reflect.DeepEqual(got, tt.expectedOut) {
t.Errorf("Env.Decode(): wrong result. Want %v. Got %v.", tt.expectedOut, got)
}
}
}
func TestSetAuto(t *testing.T) {
buf := bytes.NewBufferString("oi")
var tests = []struct {
input interface{}
expected string
}{
{10, "10"},
{10.3, "10"},
{"oi", "oi"},
{buf, "{}"},
{unmarshable{}, "{}"},
}
for _, tt := range tests {
var env Env
env.SetAuto("SOME", tt.input)
if got := env.Get("SOME"); got != tt.expected {
t.Errorf("Env.SetAuto(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got)
}
}
}
func TestMap(t *testing.T) {
var tests = []struct {
input []string
expected map[string]string
}{
{[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, map[string]string{"PATH": "/usr/bin:/bin", "PYTHONPATH": "/usr/local"}},
{nil, nil},
}
for _, tt := range tests {
env := Env(tt.input)
got := env.Map()
if !reflect.DeepEqual(got, tt.expected) {
t.Errorf("Env.Map(): wrong result. Want %v. Got %v", tt.expected, got)
}
}
}
type unmarshable struct {
}
func (unmarshable) MarshalJSON() ([]byte, error) {
return nil, errors.New("cannot marshal")
}

View File

@ -53,7 +53,7 @@ func TestEventListeners(t *testing.T) {
for { for {
select { select {
case msg := <-listener: case msg := <-listener:
t.Logf("Received: %s", *msg) t.Logf("Recieved: %s", *msg)
count++ count++
err = checkEvent(count, msg) err = checkEvent(count, msg)
if err != nil { if err != nil {

View File

@ -21,9 +21,9 @@ func newTestClient(rt *FakeRoundTripper) Client {
endpoint := "http://localhost:4243" endpoint := "http://localhost:4243"
u, _ := parseEndpoint("http://localhost:4243") u, _ := parseEndpoint("http://localhost:4243")
client := Client{ client := Client{
HTTPClient: &http.Client{Transport: rt},
endpoint: endpoint, endpoint: endpoint,
endpointURL: u, endpointURL: u,
client: &http.Client{Transport: rt},
SkipServerVersionCheck: true, SkipServerVersionCheck: true,
} }
return client return client

View File

@ -6,39 +6,32 @@ package docker
import ( import (
"bytes" "bytes"
"io"
"github.com/fsouza/go-dockerclient/engine"
) )
// Version returns version information about the docker server. // Version returns version information about the docker server.
// //
// See http://goo.gl/IqKNRE for more details. // See http://goo.gl/IqKNRE for more details.
func (c *Client) Version() (*engine.Env, error) { func (c *Client) Version() (*Env, error) {
body, _, err := c.do("GET", "/version", nil) body, _, err := c.do("GET", "/version", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
out := engine.NewOutput() var env Env
remoteVersion, err := out.AddEnv() if err := env.Decode(bytes.NewReader(body)); err != nil {
if err != nil {
return nil, err return nil, err
} }
if _, err := io.Copy(out, bytes.NewReader(body)); err != nil { return &env, nil
return nil, err
}
return remoteVersion, nil
} }
// Info returns system-wide information, like the number of running containers. // Info returns system-wide information, like the number of running containers.
// //
// See http://goo.gl/LOmySw for more details. // See http://goo.gl/LOmySw for more details.
func (c *Client) Info() (*engine.Env, error) { func (c *Client) Info() (*Env, error) {
body, _, err := c.do("GET", "/info", nil) body, _, err := c.do("GET", "/info", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var info engine.Env var info Env
err = info.Decode(bytes.NewReader(body)) err = info.Decode(bytes.NewReader(body))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -10,8 +10,6 @@ import (
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
"github.com/fsouza/go-dockerclient/engine"
) )
type DockerVersion struct { type DockerVersion struct {
@ -81,7 +79,7 @@ func TestInfo(t *testing.T) {
}` }`
fakeRT := FakeRoundTripper{message: body, status: http.StatusOK} fakeRT := FakeRoundTripper{message: body, status: http.StatusOK}
client := newTestClient(&fakeRT) client := newTestClient(&fakeRT)
expected := engine.Env{} expected := Env{}
expected.SetInt("Containers", 11) expected.SetInt("Containers", 11)
expected.SetInt("Images", 16) expected.SetInt("Images", 16)
expected.SetBool("Debug", false) expected.SetBool("Debug", false)

View File

@ -170,6 +170,12 @@ func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
// Returns default http.Handler mux, it allows customHandlers to call the
// default behavior if wanted.
func (s *DockerServer) DefaultHandler() http.Handler {
return s.mux
}
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, urlRegexp := range s.failures { for errorID, urlRegexp := range s.failures {
@ -218,6 +224,11 @@ func (s *DockerServer) listImages(w http.ResponseWriter, r *http.Request) {
ID: image.ID, ID: image.ID,
Created: image.Created.Unix(), Created: image.Created.Unix(),
} }
for tag, id := range s.imgIDs {
if id == image.ID {
result[i].RepoTags = append(result[i].RepoTags, tag)
}
}
} }
s.cMut.RUnlock() s.cMut.RUnlock()
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@ -563,8 +574,9 @@ func (s *DockerServer) pushImage(w http.ResponseWriter, r *http.Request) {
func (s *DockerServer) removeImage(w http.ResponseWriter, r *http.Request) { func (s *DockerServer) removeImage(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"] id := mux.Vars(r)["id"]
s.iMut.RLock() s.iMut.RLock()
var tag string
if img, ok := s.imgIDs[id]; ok { if img, ok := s.imgIDs[id]; ok {
id = img id, tag = img, id
} }
s.iMut.RUnlock() s.iMut.RUnlock()
_, index, err := s.findImageByID(id) _, index, err := s.findImageByID(id)
@ -577,6 +589,9 @@ func (s *DockerServer) removeImage(w http.ResponseWriter, r *http.Request) {
defer s.iMut.Unlock() defer s.iMut.Unlock()
s.images[index] = s.images[len(s.images)-1] s.images[index] = s.images[len(s.images)-1]
s.images = s.images[:len(s.images)-1] s.images = s.images[:len(s.images)-1]
if tag != "" {
delete(s.imgIDs, tag)
}
} }
func (s *DockerServer) inspectImage(w http.ResponseWriter, r *http.Request) { func (s *DockerServer) inspectImage(w http.ResponseWriter, r *http.Request) {

View File

@ -780,7 +780,7 @@ func addImages(server *DockerServer, n int, repo bool) {
func TestListImages(t *testing.T) { func TestListImages(t *testing.T) {
server := DockerServer{} server := DockerServer{}
addImages(&server, 2, false) addImages(&server, 2, true)
server.buildMuxer() server.buildMuxer()
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/images/json?all=1", nil) request, _ := http.NewRequest("GET", "/images/json?all=1", nil)
@ -791,8 +791,9 @@ func TestListImages(t *testing.T) {
expected := make([]docker.APIImages, 2) expected := make([]docker.APIImages, 2)
for i, image := range server.images { for i, image := range server.images {
expected[i] = docker.APIImages{ expected[i] = docker.APIImages{
ID: image.ID, ID: image.ID,
Created: image.Created.Unix(), Created: image.Created.Unix(),
RepoTags: []string{"docker/python-" + image.ID},
} }
} }
var got []docker.APIImages var got []docker.APIImages
@ -826,7 +827,8 @@ func TestRemoveImageByName(t *testing.T) {
addImages(&server, 1, true) addImages(&server, 1, true)
server.buildMuxer() server.buildMuxer()
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
path := "/images/docker/python-" + server.images[0].ID imgName := "docker/python-" + server.images[0].ID
path := "/images/" + imgName
request, _ := http.NewRequest("DELETE", path, nil) request, _ := http.NewRequest("DELETE", path, nil)
server.ServeHTTP(recorder, request) server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusNoContent { if recorder.Code != http.StatusNoContent {
@ -835,6 +837,10 @@ func TestRemoveImageByName(t *testing.T) {
if len(server.images) > 0 { if len(server.images) > 0 {
t.Error("RemoveImage: did not remove the image.") t.Error("RemoveImage: did not remove the image.")
} }
_, ok := server.imgIDs[imgName]
if ok {
t.Error("RemoveImage: did not remove image tag name.")
}
} }
func TestPrepareFailure(t *testing.T) { func TestPrepareFailure(t *testing.T) {
@ -945,3 +951,14 @@ func TestPing(t *testing.T) {
t.Errorf("Ping: Expected code %d, got: %d", http.StatusOK, recorder.Code) t.Errorf("Ping: Expected code %d, got: %d", http.StatusOK, recorder.Code)
} }
} }
func TestDefaultHandler(t *testing.T) {
server, err := NewServer("127.0.0.1:0", nil, nil)
if err != nil {
t.Fatal(err)
}
defer server.listener.Close()
if server.mux != server.DefaultHandler() {
t.Fatalf("DefaultHandler: Expected to return server.mux, got: %#v", server.DefaultHandler())
}
}

View File

@ -109,7 +109,6 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
// Write on stderr // Write on stderr
out = dsterr out = dsterr
default: default:
Debugf("Error selecting output fd: (%d)", buf[StdWriterFdIndex])
return 0, ErrInvalidStdHeader return 0, ErrInvalidStdHeader
} }
@ -119,7 +118,6 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
// Check if the buffer is big enough to read the frame. // Check if the buffer is big enough to read the frame.
// Extend it if necessary. // Extend it if necessary.
if frameSize+StdWriterPrefixLen > bufLen { if frameSize+StdWriterPrefixLen > bufLen {
Debugf("Extending buffer cap.")
buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-len(buf)+1)...) buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-len(buf)+1)...)
bufLen = len(buf) bufLen = len(buf)
} }
@ -135,7 +133,6 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
nr += nr2 nr += nr2
break break
} else if er != nil { } else if er != nil {
Debugf("Error reading frame: %s", er)
return 0, er return 0, er
} }
nr += nr2 nr += nr2
@ -151,12 +148,10 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
written += int64(nw) written += int64(nw)
} }
if ew != nil { if ew != nil {
Debugf("Error writing frame: %s", ew)
return 0, ew return 0, ew
} }
// 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)
return written, io.ErrShortWrite return written, io.ErrShortWrite
} }