Update fsouza/go-dockerclient

This commit is contained in:
Tom Wilkie
2016-01-04 12:52:06 +00:00
parent c468dd5f32
commit 7df22c6f39
14 changed files with 535 additions and 107 deletions

View File

@@ -1,2 +0,0 @@
# temporary symlink for testing
testing/data/symlink

View File

@@ -1,21 +0,0 @@
language: go
sudo: required
go:
- 1.3.3
- 1.4.2
- 1.5.1
- tip
env:
- GOARCH=amd64 DOCKER_VERSION=1.7.1
- GOARCH=386 DOCKER_VERSION=1.7.1
- GOARCH=amd64 DOCKER_VERSION=1.8.3
- GOARCH=386 DOCKER_VERSION=1.8.3
- GOARCH=amd64 DOCKER_VERSION=1.9.1
- GOARCH=386 DOCKER_VERSION=1.9.1
install:
- make prepare_docker
script:
- make test
- DOCKER_HOST=tcp://127.0.0.1:2375 make integration
services:
- docker

View File

@@ -8,9 +8,11 @@ Andreas Jaekle <andreas@jaekle.net>
Andrews Medina <andrewsmedina@gmail.com>
Andrey Sibiryov <kobolog@uber.com>
Andy Goldstein <andy.goldstein@redhat.com>
Antonio Murdaca <runcom@redhat.com>
Artem Sidorenko <artem@2realities.com>
Ben Marini <ben@remind101.com>
Ben McCann <benmccann.com>
Benno van den Berg <bennovandenberg@gmail.com>
Brendan Fosberry <brendan@codeship.com>
Brian Lalor <blalor@bravo5.org>
Brian P. Hamachek <brian@brianhama.com>
@@ -24,6 +26,7 @@ Cheah Chu Yeow <chuyeow@gmail.com>
cheneydeng <cheneydeng@qq.com>
Chris Bednarski <banzaimonkey@gmail.com>
CMGS <ilskdw@gmail.com>
Colin Hebert <hebert.colin@gmail.com>
Craig Jellick <craig@rancher.com>
Dan Williams <dcbw@redhat.com>
Daniel, Dao Quang Minh <dqminh89@gmail.com>
@@ -60,6 +63,7 @@ Johan Euphrosine <proppy@google.com>
John Hughes <hughesj@visa.com>
Kamil Domanski <kamil@domanski.co>
Karan Misra <kidoman@gmail.com>
Ken Herner <chosenken@gmail.com>
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
Kyle Allan <kallan357@gmail.com>
Liron Levin <levinlir@gmail.com>
@@ -76,6 +80,7 @@ Michael Schmatz <michaelschmatz@gmail.com>
Michal Fojtik <mfojtik@redhat.com>
Mike Dillon <mike.dillon@synctree.com>
Mrunal Patel <mrunalp@gmail.com>
Nguyen Sy Thanh Son <sonnst@sigma-solutions.eu>
Nick Ethier <ncethier@gmail.com>
Omeid Matten <public@omeid.me>
Orivej Desh <orivej@gmx.fr>
@@ -104,6 +109,7 @@ Sunjin Lee <styner32@gmail.com>
Tarsis Azevedo <tarsis@corp.globo.com>
Tim Schindler <tim@catalyst-zero.com>
Tobi Knaup <tobi@mesosphere.io>
Tom Wilkie <tom.wilkie@gmail.com>
Tonic <tonicbupt@gmail.com>
ttyh061 <ttyh061@gmail.com>
Victor Marmol <vmarmol@google.com>

View File

@@ -730,13 +730,13 @@ func (c *Client) unixClient() *http.Client {
return c.unixHTTPClient
}
socketPath := c.endpointURL.Path
c.unixHTTPClient = &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return c.Dialer.Dial("unix", socketPath)
},
tr := &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return c.Dialer.Dial("unix", socketPath)
},
}
cleanhttp.SetTransportFinalizer(tr)
c.unixHTTPClient = &http.Client{Transport: tr}
return c.unixHTTPClient
}

View File

@@ -238,8 +238,10 @@ type Config struct {
// It has been added in the version 1.20 of the Docker API, available since
// Docker 1.8.
type Mount struct {
Name string
Source string
Destination string
Driver string
Mode string
RW bool
}
@@ -635,17 +637,8 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
//
// See https://goo.gl/GNmLHb for more details.
type Stats struct {
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
Network struct {
RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty"`
RxBytes uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty"`
RxErrors uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty"`
TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty"`
TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty"`
RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty"`
TxErrors uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty"`
TxBytes uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty"`
} `json:"network,omitempty" yaml:"network,omitempty"`
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
Networks map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty"`
MemoryStats struct {
Stats struct {
TotalPgmafault uint64 `json:"total_pgmafault,omitempty" yaml:"total_pgmafault,omitempty"`
@@ -697,6 +690,18 @@ type Stats struct {
PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
}
// NetworkStats is a stats entry for network stats
type NetworkStats struct {
RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty"`
RxBytes uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty"`
RxErrors uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty"`
TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty"`
TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty"`
RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty"`
TxErrors uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty"`
TxBytes uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty"`
}
// CPUStats is a stats entry for cpu stats
type CPUStats struct {
CPUUsage struct {
@@ -971,7 +976,7 @@ type CommitContainerOptions struct {
Container string
Repository string `qs:"repo"`
Tag string
Message string `qs:"m"`
Message string `qs:"comment"`
Author string
Run *Config `qs:"-"`
}
@@ -1036,17 +1041,7 @@ type AttachToContainerOptions struct {
//
// See https://goo.gl/NKpkFk for more details.
func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
if opts.Container == "" {
return &NoSuchContainer{ID: opts.Container}
}
path := "/containers/" + opts.Container + "/attach?" + queryString(opts)
cw, err := c.hijack("POST", path, hijackOptions{
success: opts.Success,
setRawTerminal: opts.RawTerminal,
in: opts.InputStream,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
})
cw, err := c.AttachToContainerNonBlocking(opts)
if err != nil {
return err
}

View File

@@ -1081,7 +1081,7 @@ func TestCommitContainerParams(t *testing.T) {
{CommitContainerOptions{Container: "44c004db4b17"}, map[string][]string{"container": {"44c004db4b17"}}, nil},
{
CommitContainerOptions{Container: "44c004db4b17", Repository: "tsuru/python", Message: "something"},
map[string][]string{"container": {"44c004db4b17"}, "repo": {"tsuru/python"}, "m": {"something"}},
map[string][]string{"container": {"44c004db4b17"}, "repo": {"tsuru/python"}, "comment": {"something"}},
nil,
},
{
@@ -1924,16 +1924,18 @@ func TestStatsTimeout(t *testing.T) {
func TestStats(t *testing.T) {
jsonStats1 := `{
"read" : "2015-01-08T22:57:31.547920715Z",
"network" : {
"rx_dropped" : 0,
"rx_bytes" : 648,
"rx_errors" : 0,
"tx_packets" : 8,
"tx_dropped" : 0,
"rx_packets" : 8,
"tx_errors" : 0,
"tx_bytes" : 648
},
"networks" : {
"eth0":{
"rx_dropped" : 0,
"rx_bytes" : 648,
"rx_errors" : 0,
"tx_packets" : 8,
"tx_dropped" : 0,
"rx_packets" : 8,
"tx_errors" : 0,
"tx_bytes" : 648
}
},
"memory_stats" : {
"stats" : {
"total_pgmajfault" : 0,
@@ -2039,17 +2041,19 @@ func TestStats(t *testing.T) {
// 1 second later, cache is 100
jsonStats2 := `{
"read" : "2015-01-08T22:57:32.547920715Z",
"network" : {
"rx_dropped" : 0,
"rx_bytes" : 648,
"rx_errors" : 0,
"tx_packets" : 8,
"tx_dropped" : 0,
"rx_packets" : 8,
"tx_errors" : 0,
"tx_bytes" : 648
},
"memory_stats" : {
"networks" : {
"eth0":{
"rx_dropped" : 0,
"rx_bytes" : 648,
"rx_errors" : 0,
"tx_packets" : 8,
"tx_dropped" : 0,
"rx_packets" : 8,
"tx_errors" : 0,
"tx_bytes" : 648
}
},
"memory_stats" : {
"stats" : {
"total_pgmajfault" : 0,
"cache" : 100,

View File

@@ -83,36 +83,14 @@ type StartExecOptions struct {
//
// See https://goo.gl/iQCnto for more details
func (c *Client) StartExec(id string, opts StartExecOptions) error {
if id == "" {
return &NoSuchExec{ID: id}
}
path := fmt.Sprintf("/exec/%s/start", id)
if opts.Detach {
resp, err := c.do("POST", path, doOptions{data: opts})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchExec{ID: id}
}
return err
}
defer resp.Body.Close()
return nil
}
cw, err := c.hijack("POST", path, hijackOptions{
success: opts.Success,
setRawTerminal: opts.RawTerminal,
in: opts.InputStream,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
data: opts,
})
cw, err := c.StartExecNonBlocking(id, opts)
if err != nil {
return err
}
return cw.Wait()
if cw != nil {
return cw.Wait()
}
return nil
}
// StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is

View File

@@ -3,13 +3,14 @@ package cleanhttp
import (
"net"
"net/http"
"runtime"
"time"
)
// DefaultTransport returns a new http.Transport with the same default values
// as http.DefaultTransport
func DefaultTransport() *http.Transport {
return &http.Transport{
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
@@ -17,6 +18,8 @@ func DefaultTransport() *http.Transport {
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
}
SetTransportFinalizer(transport)
return transport
}
// DefaultClient returns a new http.Client with the same default values as
@@ -26,3 +29,12 @@ func DefaultClient() *http.Client {
Transport: DefaultTransport(),
}
}
// SetTransportFinalizer sets a finalizer on the transport to ensure that
// idle connections are closed prior to garbage collection; otherwise
// these may leak
func SetTransportFinalizer(transport *http.Transport) {
runtime.SetFinalizer(&transport, func(t **http.Transport) {
(*t).CloseIdleConnections()
})
}

View File

@@ -142,7 +142,7 @@ func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
return &network, nil
}
// RemoveNetwork removes a network or an error in case of failure.
// RemoveNetwork removes a network or returns an error in case of failure.
//
// See https://goo.gl/1kmPKZ for more details.
func (c *Client) RemoveNetwork(id string) error {
@@ -157,6 +157,43 @@ func (c *Client) RemoveNetwork(id string) error {
return nil
}
// NetworkConnectionOptions specify parameters to the ConnectNetwork and DisconnectNetwork function.
//
// See https://goo.gl/1kmPKZ for more details.
type NetworkConnectionOptions struct {
Container string
}
// ConnectNetwork adds a container to a network or returns an error in case of failure.
//
// See https://goo.gl/1kmPKZ for more details.
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
resp, err := c.do("POST", "/networks/"+id+"/connect", doOptions{data: opts})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
}
return err
}
resp.Body.Close()
return nil
}
// DisconnectNetwork removes a container from a network or returns an error in case of failure.
//
// See https://goo.gl/1kmPKZ for more details.
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
resp, err := c.do("POST", "/networks/"+id+"/disconnect", doOptions{data: opts})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
}
return err
}
resp.Body.Close()
return nil
}
// NoSuchNetwork is the error returned when a given network does not exist.
type NoSuchNetwork struct {
ID string
@@ -165,3 +202,13 @@ type NoSuchNetwork struct {
func (err *NoSuchNetwork) Error() string {
return fmt.Sprintf("No such network: %s", err.ID)
}
// NoSuchNetwork is the error returned when a given network or container does not exist.
type NoSuchNetworkOrContainer struct {
NetworkID string
ContainerID string
}
func (err *NoSuchNetworkOrContainer) Error() string {
return fmt.Sprintf("No such network (%s) or container (%s)", err.NetworkID, err.ContainerID)
}

View File

@@ -113,3 +113,61 @@ func TestNetworkRemove(t *testing.T) {
t.Errorf("RemoveNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkConnect(t *testing.T) {
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
opts := NetworkConnectionOptions{"foobar"}
err := client.ConnectNetwork(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("ConnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id + "/connect"))
if req.URL.Path != u.Path {
t.Errorf("ConnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkConnectNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
opts := NetworkConnectionOptions{"foobar"}
err := client.ConnectNetwork("8dfafdbc3a40", opts)
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
t.Errorf("ConnectNetwork: wrong error type: %s.", serr)
}
}
func TestNetworkDisconnect(t *testing.T) {
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
opts := NetworkConnectionOptions{"foobar"}
err := client.DisconnectNetwork(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("DisconnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id + "/disconnect"))
if req.URL.Path != u.Path {
t.Errorf("DisconnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkDisconnectNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
opts := NetworkConnectionOptions{"foobar"}
err := client.DisconnectNetwork("8dfafdbc3a40", opts)
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
t.Errorf("DisconnectNetwork: wrong error type: %s.", serr)
}
}

View File

@@ -1,3 +0,0 @@
container.tar
dockerfile.tar
foofile

View File

@@ -56,6 +56,13 @@ type DockerServer struct {
customHandlers map[string]http.Handler
handlerMutex sync.RWMutex
cChan chan<- *docker.Container
volStore map[string]*volumeCounter
volMut sync.RWMutex
}
type volumeCounter struct {
volume docker.Volume
count int
}
// NewServer returns a new instance of the fake server, in standalone mode. Use
@@ -130,6 +137,10 @@ func (s *DockerServer) buildMuxer() {
s.mux.Path("/networks").Methods("GET").HandlerFunc(s.handlerWrapper(s.listNetworks))
s.mux.Path("/networks/{id:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.networkInfo))
s.mux.Path("/networks").Methods("POST").HandlerFunc(s.handlerWrapper(s.createNetwork))
s.mux.Path("/volumes").Methods("GET").HandlerFunc(s.handlerWrapper(s.listVolumes))
s.mux.Path("/volumes/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createVolume))
s.mux.Path("/volumes/{name:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume))
s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume))
}
// SetHook changes the hook function used by the server.
@@ -376,7 +387,7 @@ func (s *DockerServer) createContainer(w http.ResponseWriter, r *http.Request) {
for port := range config.ExposedPorts {
ports[port] = []docker.PortBinding{{
HostIP: "0.0.0.0",
HostPort: strconv.Itoa(mathrand.Int() % 65536),
HostPort: strconv.Itoa(mathrand.Int() % 0xffff),
}}
}
@@ -532,6 +543,27 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) {
return
}
container.HostConfig = &hostConfig
if len(hostConfig.PortBindings) > 0 {
ports := map[docker.Port][]docker.PortBinding{}
for key, items := range hostConfig.PortBindings {
bindings := make([]docker.PortBinding, len(items))
for i := range items {
binding := docker.PortBinding{
HostIP: items[i].HostIP,
HostPort: items[i].HostPort,
}
if binding.HostIP == "" {
binding.HostIP = "0.0.0.0"
}
if binding.HostPort == "" {
binding.HostPort = strconv.Itoa(mathrand.Int() % 0xffff)
}
bindings[i] = binding
}
ports[key] = bindings
}
container.NetworkSettings.Ports = ports
}
if container.State.Running {
http.Error(w, "", http.StatusNotModified)
return
@@ -1096,3 +1128,99 @@ func (s *DockerServer) createNetwork(w http.ResponseWriter, r *http.Request) {
var c = struct{ ID string }{ID: network.ID}
json.NewEncoder(w).Encode(c)
}
func (s *DockerServer) listVolumes(w http.ResponseWriter, r *http.Request) {
s.volMut.RLock()
result := make([]docker.Volume, 0, len(s.volStore))
for _, volumeCounter := range s.volStore {
result = append(result, volumeCounter.volume)
}
s.volMut.RUnlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result)
}
func (s *DockerServer) createVolume(w http.ResponseWriter, r *http.Request) {
var data struct {
*docker.CreateVolumeOptions
}
defer r.Body.Close()
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
volume := &docker.Volume{
Name: data.CreateVolumeOptions.Name,
Driver: data.CreateVolumeOptions.Driver,
}
// If the name is not specified, generate one. Just using generateID for now
if len(volume.Name) == 0 {
volume.Name = s.generateID()
}
// If driver is not specified, use local
if len(volume.Driver) == 0 {
volume.Driver = "local"
}
// Mount point is a default one with name
volume.Mountpoint = "/var/lib/docker/volumes/" + volume.Name
// If the volume already exists, don't re-add it.
exists := false
s.volMut.Lock()
if s.volStore != nil {
_, exists = s.volStore[volume.Name]
} else {
// No volumes, create volStore
s.volStore = make(map[string]*volumeCounter)
}
if !exists {
s.volStore[volume.Name] = &volumeCounter{
volume: *volume,
count: 0,
}
}
s.volMut.Unlock()
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(volume)
}
func (s *DockerServer) inspectVolume(w http.ResponseWriter, r *http.Request) {
s.volMut.RLock()
defer s.volMut.RUnlock()
name := mux.Vars(r)["name"]
vol, err := s.findVolume(name)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(vol.volume)
}
func (s *DockerServer) findVolume(name string) (*volumeCounter, error) {
vol, ok := s.volStore[name]
if !ok {
return nil, errors.New("no such volume")
}
return vol, nil
}
func (s *DockerServer) removeVolume(w http.ResponseWriter, r *http.Request) {
s.volMut.Lock()
defer s.volMut.Unlock()
name := mux.Vars(r)["name"]
vol, err := s.findVolume(name)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
if vol.count != 0 {
http.Error(w, "volume in use and cannot be removed", http.StatusConflict)
return
}
s.volStore[vol.volume.Name] = nil
w.WriteHeader(http.StatusNoContent)
}

View File

@@ -586,6 +586,36 @@ func TestStartContainer(t *testing.T) {
}
}
func TestStartContainerChangeNetwork(t *testing.T) {
server := DockerServer{}
addContainers(&server, 1)
server.buildMuxer()
hostConfig := docker.HostConfig{
PortBindings: map[docker.Port][]docker.PortBinding{
"8888/tcp": {{HostIP: "", HostPort: "12345"}},
},
}
configBytes, err := json.Marshal(hostConfig)
if err != nil {
t.Fatal(err)
}
recorder := httptest.NewRecorder()
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
request, _ := http.NewRequest("POST", path, bytes.NewBuffer(configBytes))
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
}
if !server.containers[0].State.Running {
t.Error("StartContainer: did not set the container to running state")
}
portMapping := server.containers[0].NetworkSettings.Ports["8888/tcp"]
expected := []docker.PortBinding{{HostIP: "0.0.0.0", HostPort: "12345"}}
if !reflect.DeepEqual(portMapping, expected) {
t.Errorf("StartContainer: network not updated. Wants %#v ports. Got %#v", expected, portMapping)
}
}
func TestStartContainerWithNotifyChannel(t *testing.T) {
ch := make(chan *docker.Container, 1)
server := DockerServer{}
@@ -1154,6 +1184,11 @@ func addContainers(server *DockerServer, n int) {
PortMapping: map[string]docker.PortMapping{
"Tcp": {"8888": fmt.Sprintf("%d", 49600+i)},
},
Ports: map[docker.Port][]docker.PortBinding{
"8888/tcp": []docker.PortBinding{
{HostIP: "0.0.0.0", HostPort: fmt.Sprintf("%d", 49600+i)},
},
},
},
ResolvConfPath: "/etc/resolv.conf",
}
@@ -1844,3 +1879,194 @@ func TestCreateNetworkDuplicateName(t *testing.T) {
t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusForbidden, recorder.Code)
}
}
func TestListVolumes(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
expected := []docker.Volume{docker.Volume{
Name: "test-vol-1",
Driver: "local",
Mountpoint: "/var/lib/docker/volumes/test-vol-1",
}}
server.volStore = make(map[string]*volumeCounter)
for _, vol := range expected {
server.volStore[vol.Name] = &volumeCounter{
volume: vol,
count: 0,
}
}
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/volumes", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Errorf("ListVolumes: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
}
var got []docker.Volume
err := json.NewDecoder(recorder.Body).Decode(&got)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("ListVolumes. Want %#v. Got %#v.", expected, got)
}
}
func TestCreateVolume(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
recorder := httptest.NewRecorder()
body := `{"Name":"test-volume"}`
request, _ := http.NewRequest("POST", "/volumes/create", strings.NewReader(body))
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusCreated {
t.Errorf("CreateVolume: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
}
var returned docker.Volume
err := json.NewDecoder(recorder.Body).Decode(&returned)
if err != nil {
t.Error(err)
}
if returned.Name != "test-volume" {
t.Errorf("CreateVolume: Name mismatch. Expected: test-volume. Returned %q.", returned.Name)
}
if returned.Driver != "local" {
t.Errorf("CreateVolume: Driver mismatch. Expected: local. Returned: %q", returned.Driver)
}
if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" {
t.Errorf("CreateVolume: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint)
}
}
func TestCreateVolumeAlreadExists(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
server.volStore = make(map[string]*volumeCounter)
server.volStore["test-volume"] = &volumeCounter{
volume: docker.Volume{
Name: "test-volume",
Driver: "local",
Mountpoint: "/var/lib/docker/volumes/test-volume",
},
count: 0,
}
body := `{"Name":"test-volume"}`
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("POST", "/volumes/create", strings.NewReader(body))
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusCreated {
t.Errorf("CreateVolumeAlreadExists: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
}
var returned docker.Volume
err := json.NewDecoder(recorder.Body).Decode(&returned)
if err != nil {
t.Error(err)
}
if returned.Name != "test-volume" {
t.Errorf("CreateVolumeAlreadExists: Name mismatch. Expected: test-volume. Returned %q.", returned.Name)
}
if returned.Driver != "local" {
t.Errorf("CreateVolumeAlreadExists: Driver mismatch. Expected: local. Returned: %q", returned.Driver)
}
if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" {
t.Errorf("CreateVolumeAlreadExists: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint)
}
}
func TestInspectVolume(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
recorder := httptest.NewRecorder()
expected := docker.Volume{
Name: "test-volume",
Driver: "local",
Mountpoint: "/var/lib/docker/volumes/test-volume",
}
volC := &volumeCounter{
volume: expected,
count: 0,
}
volStore := make(map[string]*volumeCounter)
volStore["test-volume"] = volC
server.volStore = volStore
request, _ := http.NewRequest("GET", "/volumes/test-volume", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Errorf("InspectVolume: wrong status. Want %d. God %d.", http.StatusOK, recorder.Code)
}
var returned docker.Volume
err := json.NewDecoder(recorder.Body).Decode(&returned)
if err != nil {
t.Error(err)
}
if returned.Name != "test-volume" {
t.Errorf("InspectVolume: Name mismatch. Expected: test-volume. Returned %q.", returned.Name)
}
if returned.Driver != "local" {
t.Errorf("InspectVolume: Driver mismatch. Expected: local. Returned: %q", returned.Driver)
}
if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" {
t.Errorf("InspectVolume: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint)
}
}
func TestInspectVolumeNotFound(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/volumes/test-volume", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusNotFound {
t.Errorf("RemoveMissingVolume: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
}
}
func TestRemoveVolume(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
server.volStore = make(map[string]*volumeCounter)
server.volStore["test-volume"] = &volumeCounter{
volume: docker.Volume{
Name: "test-volume",
Driver: "local",
Mountpoint: "/var/lib/docker/volumes/test-volume",
},
count: 0,
}
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusNoContent {
t.Errorf("RemoveVolume: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code)
}
}
func TestRemoveMissingVolume(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusNotFound {
t.Errorf("RemoveMissingVolume: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
}
}
func TestRemoveVolumeInuse(t *testing.T) {
server := DockerServer{}
server.buildMuxer()
server.volStore = make(map[string]*volumeCounter)
server.volStore["test-volume"] = &volumeCounter{
volume: docker.Volume{
Name: "test-volume",
Driver: "local",
Mountpoint: "/var/lib/docker/volumes/test-volume",
},
count: 1,
}
recorder := httptest.NewRecorder()
request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil)
server.ServeHTTP(recorder, request)
if recorder.Code != http.StatusConflict {
t.Errorf("RemoveVolume: wrong status. Want %d. Got %d.", http.StatusConflict, recorder.Code)
}
}

2
vendor/manifest vendored
View File

@@ -521,7 +521,7 @@
{
"importpath": "github.com/fsouza/go-dockerclient",
"repository": "https://github.com/fsouza/go-dockerclient",
"revision": "44f75219dec4d25d3ac5483d38d3ada7eaf047ab",
"revision": "83211b6fbbfc6aa4a19eeb76eaf5cbfbe1381004",
"branch": "master"
},
{