From 8b40158898b25205b72e68782010c904ae3e70ef Mon Sep 17 00:00:00 2001 From: Francisco Souza Date: Sun, 29 Jun 2014 12:10:44 -0300 Subject: [PATCH] testing: add method to notify containers through a channel Related to issue #105. --- testing/server.go | 29 ++++++++++++++++--- testing/server_test.go | 66 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/testing/server.go b/testing/server.go index f3ba7ebf..111cfab3 100644 --- a/testing/server.go +++ b/testing/server.go @@ -43,6 +43,7 @@ type DockerServer struct { mux *mux.Router hook func(*http.Request) failures map[string]FailureSpec + cChan chan<- *docker.Container } // FailureSpec is used with PrepareFailure and describes in which situations @@ -58,19 +59,36 @@ type FailureSpec struct { // the method URL to get the URL of the server. // // It receives the bind address (use 127.0.0.1:0 for getting an available port -// on the host) and a hook function, that will be called on every request. -func NewServer(bind string, hook func(*http.Request)) (*DockerServer, error) { +// on the host), a channel of containers and a hook function, that will be +// called on every request. +// +// The fake server will send containers in the channel whenever the container +// changes its state, via the HTTP API (i.e.: create, start and stop). This +// channel may be nil, which means that the server won't notify on state +// changes. +func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*http.Request)) (*DockerServer, error) { listener, err := net.Listen("tcp", bind) if err != nil { return nil, err } - server := DockerServer{listener: listener, imgIDs: make(map[string]string), hook: hook, - failures: make(map[string]FailureSpec)} + server := DockerServer{ + listener: listener, + imgIDs: make(map[string]string), + hook: hook, + failures: make(map[string]FailureSpec), + cChan: containerChan, + } server.buildMuxer() go http.Serve(listener, &server) return &server, nil } +func (s *DockerServer) notify(container *docker.Container) { + if s.cChan != nil { + s.cChan <- container + } +} + func (s *DockerServer) buildMuxer() { s.mux = mux.NewRouter() s.mux.Path("/commit").Methods("POST").HandlerFunc(s.handlerWrapper(s.commitContainer)) @@ -284,6 +302,7 @@ func (s *DockerServer) createContainer(w http.ResponseWriter, r *http.Request) { s.cMut.Lock() s.containers = append(s.containers, &container) s.cMut.Unlock() + s.notify(&container) var c = struct{ ID string }{ID: container.ID} json.NewEncoder(w).Encode(c) } @@ -320,6 +339,7 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) { return } container.State.Running = true + s.notify(container) } func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) { @@ -337,6 +357,7 @@ func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) { } w.WriteHeader(http.StatusNoContent) container.State.Running = false + s.notify(container) } func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) { diff --git a/testing/server_test.go b/testing/server_test.go index ab9ee65d..3c307a2a 100644 --- a/testing/server_test.go +++ b/testing/server_test.go @@ -20,7 +20,7 @@ import ( ) func TestNewServer(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil) + server, err := NewServer("127.0.0.1:0", nil, nil) if err != nil { t.Fatal(err) } @@ -33,7 +33,7 @@ func TestNewServer(t *testing.T) { } func TestServerStop(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil) + server, err := NewServer("127.0.0.1:0", nil, nil) if err != nil { t.Fatal(err) } @@ -50,7 +50,7 @@ func TestServerStopNoListener(t *testing.T) { } func TestServerURL(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil) + server, err := NewServer("127.0.0.1:0", nil, nil) if err != nil { t.Fatal(err) } @@ -71,7 +71,7 @@ func TestServerURLNoListener(t *testing.T) { func TestHandleWithHook(t *testing.T) { var called bool - server, _ := NewServer("127.0.0.1:0", func(*http.Request) { called = true }) + server, _ := NewServer("127.0.0.1:0", nil, func(*http.Request) { called = true }) defer server.Stop() recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) @@ -158,6 +158,25 @@ func TestCreateContainer(t *testing.T) { } } +func TestCreateContainerWithNotifyChannel(t *testing.T) { + ch := make(chan *docker.Container, 1) + server := DockerServer{} + server.imgIDs = map[string]string{"base": "a1234"} + server.cChan = ch + server.buildMuxer() + recorder := httptest.NewRecorder() + body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, +"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":""}` + request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body)) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusCreated { + t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) + } + if notified := <-ch; notified != server.containers[0] { + t.Errorf("CreateContainer: did not notify the proper container. Want %q. Got %q.", server.containers[0].ID, notified.ID) + } +} + func TestCreateContainerInvalidBody(t *testing.T) { server := DockerServer{} server.buildMuxer() @@ -320,6 +339,25 @@ func TestStartContainer(t *testing.T) { } } +func TestStartContainerWithNotifyChannel(t *testing.T) { + ch := make(chan *docker.Container, 1) + server := DockerServer{} + server.cChan = ch + addContainers(&server, 1) + addContainers(&server, 1) + server.buildMuxer() + recorder := httptest.NewRecorder() + path := fmt.Sprintf("/containers/%s/start", server.containers[1].ID) + request, _ := http.NewRequest("POST", path, nil) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusOK { + t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code) + } + if notified := <-ch; notified != server.containers[1] { + t.Errorf("StartContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID) + } +} + func TestStartContainerNotFound(t *testing.T) { server := DockerServer{} server.buildMuxer() @@ -363,6 +401,26 @@ func TestStopContainer(t *testing.T) { } } +func TestStopContainerWithNotifyChannel(t *testing.T) { + ch := make(chan *docker.Container, 1) + server := DockerServer{} + server.cChan = ch + addContainers(&server, 1) + addContainers(&server, 1) + server.containers[1].State.Running = true + server.buildMuxer() + recorder := httptest.NewRecorder() + path := fmt.Sprintf("/containers/%s/stop", server.containers[1].ID) + request, _ := http.NewRequest("POST", path, nil) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusNoContent { + t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) + } + if notified := <-ch; notified != server.containers[1] { + t.Errorf("StopContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID) + } +} + func TestStopContainerNotFound(t *testing.T) { server := DockerServer{} server.buildMuxer()