diff --git a/vendor/github.com/fsouza/go-dockerclient/.gitignore b/vendor/github.com/fsouza/go-dockerclient/.gitignore deleted file mode 100644 index 5f6b48eae..000000000 --- a/vendor/github.com/fsouza/go-dockerclient/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# temporary symlink for testing -testing/data/symlink diff --git a/vendor/github.com/fsouza/go-dockerclient/.travis.yml b/vendor/github.com/fsouza/go-dockerclient/.travis.yml deleted file mode 100644 index de36cd45b..000000000 --- a/vendor/github.com/fsouza/go-dockerclient/.travis.yml +++ /dev/null @@ -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 diff --git a/vendor/github.com/fsouza/go-dockerclient/AUTHORS b/vendor/github.com/fsouza/go-dockerclient/AUTHORS index bb5b042ab..aac948fe6 100644 --- a/vendor/github.com/fsouza/go-dockerclient/AUTHORS +++ b/vendor/github.com/fsouza/go-dockerclient/AUTHORS @@ -8,9 +8,11 @@ Andreas Jaekle Andrews Medina Andrey Sibiryov Andy Goldstein +Antonio Murdaca Artem Sidorenko Ben Marini Ben McCann +Benno van den Berg Brendan Fosberry Brian Lalor Brian P. Hamachek @@ -24,6 +26,7 @@ Cheah Chu Yeow cheneydeng Chris Bednarski CMGS +Colin Hebert Craig Jellick Dan Williams Daniel, Dao Quang Minh @@ -60,6 +63,7 @@ Johan Euphrosine John Hughes Kamil Domanski Karan Misra +Ken Herner Kim, Hirokuni Kyle Allan Liron Levin @@ -76,6 +80,7 @@ Michael Schmatz Michal Fojtik Mike Dillon Mrunal Patel +Nguyen Sy Thanh Son Nick Ethier Omeid Matten Orivej Desh @@ -104,6 +109,7 @@ Sunjin Lee Tarsis Azevedo Tim Schindler Tobi Knaup +Tom Wilkie Tonic ttyh061 Victor Marmol diff --git a/vendor/github.com/fsouza/go-dockerclient/client.go b/vendor/github.com/fsouza/go-dockerclient/client.go index bdcfdcc6f..3a7d755f5 100644 --- a/vendor/github.com/fsouza/go-dockerclient/client.go +++ b/vendor/github.com/fsouza/go-dockerclient/client.go @@ -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 } diff --git a/vendor/github.com/fsouza/go-dockerclient/container.go b/vendor/github.com/fsouza/go-dockerclient/container.go index f2eadd5ed..0baf01f97 100644 --- a/vendor/github.com/fsouza/go-dockerclient/container.go +++ b/vendor/github.com/fsouza/go-dockerclient/container.go @@ -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 } diff --git a/vendor/github.com/fsouza/go-dockerclient/container_test.go b/vendor/github.com/fsouza/go-dockerclient/container_test.go index 2f93b6300..3e09c5f63 100644 --- a/vendor/github.com/fsouza/go-dockerclient/container_test.go +++ b/vendor/github.com/fsouza/go-dockerclient/container_test.go @@ -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, diff --git a/vendor/github.com/fsouza/go-dockerclient/exec.go b/vendor/github.com/fsouza/go-dockerclient/exec.go index e164bfb64..1a16da9d6 100644 --- a/vendor/github.com/fsouza/go-dockerclient/exec.go +++ b/vendor/github.com/fsouza/go-dockerclient/exec.go @@ -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 diff --git a/vendor/github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp/cleanhttp.go b/vendor/github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp/cleanhttp.go index 1676d79cb..c692e23f4 100644 --- a/vendor/github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp/cleanhttp.go +++ b/vendor/github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp/cleanhttp.go @@ -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() + }) +} diff --git a/vendor/github.com/fsouza/go-dockerclient/network.go b/vendor/github.com/fsouza/go-dockerclient/network.go index a7fc152de..38b343246 100644 --- a/vendor/github.com/fsouza/go-dockerclient/network.go +++ b/vendor/github.com/fsouza/go-dockerclient/network.go @@ -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) +} diff --git a/vendor/github.com/fsouza/go-dockerclient/network_test.go b/vendor/github.com/fsouza/go-dockerclient/network_test.go index 9e6be3082..2bff70fe4 100644 --- a/vendor/github.com/fsouza/go-dockerclient/network_test.go +++ b/vendor/github.com/fsouza/go-dockerclient/network_test.go @@ -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) + } +} diff --git a/vendor/github.com/fsouza/go-dockerclient/testing/data/.dockerignore b/vendor/github.com/fsouza/go-dockerclient/testing/data/.dockerignore deleted file mode 100644 index 027e8c20e..000000000 --- a/vendor/github.com/fsouza/go-dockerclient/testing/data/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -container.tar -dockerfile.tar -foofile diff --git a/vendor/github.com/fsouza/go-dockerclient/testing/server.go b/vendor/github.com/fsouza/go-dockerclient/testing/server.go index d24876b2b..2c09f21c2 100644 --- a/vendor/github.com/fsouza/go-dockerclient/testing/server.go +++ b/vendor/github.com/fsouza/go-dockerclient/testing/server.go @@ -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) +} diff --git a/vendor/github.com/fsouza/go-dockerclient/testing/server_test.go b/vendor/github.com/fsouza/go-dockerclient/testing/server_test.go index 78d5bb1bd..e39941e70 100644 --- a/vendor/github.com/fsouza/go-dockerclient/testing/server_test.go +++ b/vendor/github.com/fsouza/go-dockerclient/testing/server_test.go @@ -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) + } +} diff --git a/vendor/manifest b/vendor/manifest index 0f0786476..1c95648a5 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -521,7 +521,7 @@ { "importpath": "github.com/fsouza/go-dockerclient", "repository": "https://github.com/fsouza/go-dockerclient", - "revision": "44f75219dec4d25d3ac5483d38d3ada7eaf047ab", + "revision": "83211b6fbbfc6aa4a19eeb76eaf5cbfbe1381004", "branch": "master" }, {