Merge pull request #12407 from cpuguy83/move_integration_api_tests

Move some integration api tests
This commit is contained in:
Alexander Morozov 2015-04-16 11:40:29 -07:00
commit 27edbd2868
4 changed files with 281 additions and 329 deletions

View File

@ -369,10 +369,15 @@ func TestBuildApiDockerfilePath(t *testing.T) {
t.Fatalf("failed to close tar archive: %v", err)
}
_, out, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar")
_, body, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar")
if err == nil {
out, _ := readBody(body)
t.Fatalf("Build was supposed to fail: %s", out)
}
out, err := readBody(body)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(out), "must be within the build context") {
t.Fatalf("Didn't complain about leaving build context: %s", out)
@ -393,10 +398,14 @@ RUN find /tmp/`,
}
defer server.Close()
_, buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json")
_, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json")
if err != nil {
t.Fatalf("Build failed: %s", err)
}
buf, err := readBody(body)
if err != nil {
t.Fatal(err)
}
// Make sure Dockerfile exists.
// Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL
@ -419,10 +428,15 @@ RUN echo from dockerfile`,
}
defer git.Close()
_, buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
_, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
if err != nil {
buf, _ := readBody(body)
t.Fatalf("Build failed: %s\n%q", err, buf)
}
buf, err := readBody(body)
if err != nil {
t.Fatal(err)
}
out := string(buf)
if !strings.Contains(out, "from dockerfile") {
@ -445,10 +459,15 @@ RUN echo from Dockerfile`,
defer git.Close()
// Make sure it tries to 'dockerfile' query param value
_, buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json")
_, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json")
if err != nil {
buf, _ := readBody(body)
t.Fatalf("Build failed: %s\n%q", err, buf)
}
buf, err := readBody(body)
if err != nil {
t.Fatal(err)
}
out := string(buf)
if !strings.Contains(out, "from baz") {
@ -472,10 +491,14 @@ RUN echo from dockerfile`,
defer git.Close()
// Make sure it tries to 'dockerfile' query param value
_, buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
_, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
if err != nil {
t.Fatalf("Build failed: %s", err)
}
buf, err := readBody(body)
if err != nil {
t.Fatal(err)
}
out := string(buf)
if !strings.Contains(out, "from Dockerfile") {
@ -503,10 +526,15 @@ func TestBuildApiDockerfileSymlink(t *testing.T) {
t.Fatalf("failed to close tar archive: %v", err)
}
_, out, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
_, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
if err == nil {
out, _ := readBody(body)
t.Fatalf("Build was supposed to fail: %s", out)
}
out, err := readBody(body)
if err != nil {
t.Fatal(err)
}
// The reason the error is "Cannot locate specified Dockerfile" is because
// in the builder, the symlink is resolved within the context, therefore
@ -596,3 +624,196 @@ func TestContainerApiPause(t *testing.T) {
logDone("container REST API - check POST containers/pause and unpause")
}
func TestContainerApiTop(t *testing.T) {
defer deleteAllContainers()
out, _, _ := dockerCmd(t, "run", "-d", "-i", "busybox", "/bin/sh", "-c", "cat")
id := strings.TrimSpace(out)
if err := waitRun(id); err != nil {
t.Fatal(err)
}
type topResp struct {
Titles []string
Processes [][]string
}
var top topResp
_, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil)
if err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(b, &top); err != nil {
t.Fatal(err)
}
if len(top.Titles) != 11 {
t.Fatalf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles)
}
if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" {
t.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles)
}
if len(top.Processes) != 2 {
t.Fatalf("expeted 2 processes, found %d: %v", len(top.Processes), top.Processes)
}
if top.Processes[0][10] != "/bin/sh -c cat" {
t.Fatalf("expected `/bin/sh -c cat`, found: %s", top.Processes[0][10])
}
if top.Processes[1][10] != "cat" {
t.Fatalf("expected `cat`, found: %s", top.Processes[1][10])
}
logDone("containers REST API - GET /containers/<id>/top")
}
func TestContainerApiCommit(t *testing.T) {
out, _, _ := dockerCmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "touch /test")
id := strings.TrimSpace(out)
name := "testcommit"
_, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+id, nil)
if err != nil && !strings.Contains(err.Error(), "200 OK: 201") {
t.Fatal(err)
}
type resp struct {
Id string
}
var img resp
if err := json.Unmarshal(b, &img); err != nil {
t.Fatal(err)
}
defer deleteImages(img.Id)
out, err = inspectField(img.Id, "Config.Cmd")
if out != "[/bin/sh -c touch /test]" {
t.Fatalf("got wrong Cmd from commit: %q", out)
}
// sanity check, make sure the image is what we think it is
dockerCmd(t, "run", img.Id, "ls", "/test")
logDone("containers REST API - POST /commit")
}
func TestContainerApiCreate(t *testing.T) {
defer deleteAllContainers()
config := map[string]interface{}{
"Image": "busybox",
"Cmd": []string{"/bin/sh", "-c", "touch /test && ls /test"},
}
_, b, err := sockRequest("POST", "/containers/create", config)
if err != nil && !strings.Contains(err.Error(), "200 OK: 201") {
t.Fatal(err)
}
type createResp struct {
Id string
}
var container createResp
if err := json.Unmarshal(b, &container); err != nil {
t.Fatal(err)
}
out, _, _ := dockerCmd(t, "start", "-a", container.Id)
if strings.TrimSpace(out) != "/test" {
t.Fatalf("expected output `/test`, got %q", out)
}
logDone("containers REST API - POST /containers/create")
}
func TestContainerApiVerifyHeader(t *testing.T) {
defer deleteAllContainers()
config := map[string]interface{}{
"Image": "busybox",
}
create := func(ct string) (int, io.ReadCloser, error) {
jsonData := bytes.NewBuffer(nil)
if err := json.NewEncoder(jsonData).Encode(config); err != nil {
t.Fatal(err)
}
return sockRequestRaw("POST", "/containers/create", jsonData, ct)
}
// Try with no content-type
_, body, err := create("")
if err == nil {
b, _ := readBody(body)
t.Fatalf("expected error when content-type is not set: %q", string(b))
}
body.Close()
// Try with wrong content-type
_, body, err = create("application/xml")
if err == nil {
b, _ := readBody(body)
t.Fatalf("expected error when content-type is not set: %q", string(b))
}
body.Close()
// now application/json
_, body, err = create("application/json")
if err != nil && !strings.Contains(err.Error(), "200 OK: 201") {
b, _ := readBody(body)
t.Fatalf("%v - %q", err, string(b))
}
body.Close()
logDone("containers REST API - verify create header")
}
// Issue 7941 - test to make sure a "null" in JSON is just ignored.
// W/o this fix a null in JSON would be parsed into a string var as "null"
func TestContainerApiPostCreateNull(t *testing.T) {
config := `{
"Hostname":"",
"Domainname":"",
"Memory":0,
"MemorySwap":0,
"CpuShares":0,
"Cpuset":null,
"AttachStdin":true,
"AttachStdout":true,
"AttachStderr":true,
"PortSpecs":null,
"ExposedPorts":{},
"Tty":true,
"OpenStdin":true,
"StdinOnce":true,
"Env":[],
"Cmd":"ls",
"Image":"busybox",
"Volumes":{},
"WorkingDir":"",
"Entrypoint":null,
"NetworkDisabled":false,
"OnBuild":null}`
_, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
if err != nil && !strings.Contains(err.Error(), "200 OK: 201") {
b, _ := readBody(body)
t.Fatal(err, string(b))
}
b, err := readBody(body)
if err != nil {
t.Fatal(err)
}
type createResp struct {
Id string
}
var container createResp
if err := json.Unmarshal(b, &container); err != nil {
t.Fatal(err)
}
out, err := inspectField(container.Id, "HostConfig.CpusetCpus")
if err != nil {
t.Fatal(err, out)
}
if out != "" {
t.Fatalf("expected empty string, got %q", out)
}
logDone("containers REST API - Create Null")
}

View File

@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"net/url"
"strings"
"testing"
"github.com/docker/docker/api/types"
@ -67,3 +68,33 @@ func TestApiImagesFilter(t *testing.T) {
logDone("images - filter param is applied")
}
func TestApiImagesSaveAndLoad(t *testing.T) {
out, err := buildImage("saveandload", "FROM hello-world\nENV FOO bar", false)
if err != nil {
t.Fatal(err)
}
id := strings.TrimSpace(out)
defer deleteImages("saveandload")
_, body, err := sockRequestRaw("GET", "/images/"+id+"/get", nil, "")
if err != nil {
t.Fatal(err)
}
defer body.Close()
dockerCmd(t, "rmi", id)
_, loadBody, err := sockRequestRaw("POST", "/images/load", body, "application/x-tar")
if err != nil {
t.Fatal(err)
}
defer loadBody.Close()
out, _, _ = dockerCmd(t, "inspect", "--format='{{ .Id }}'", id)
if strings.TrimSpace(out) != id {
t.Fatal("load did not work properly")
}
logDone("images API - save and load")
}

View File

@ -22,6 +22,7 @@ import (
"time"
"github.com/docker/docker/api"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/stringutils"
)
@ -304,41 +305,53 @@ func sockRequest(method, endpoint string, data interface{}) (int, []byte, error)
return -1, nil, err
}
return sockRequestRaw(method, endpoint, jsonData, "application/json")
status, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json")
if err != nil {
b, _ := ioutil.ReadAll(body)
return status, b, err
}
var b []byte
b, err = readBody(body)
return status, b, err
}
func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, []byte, error) {
func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, io.ReadCloser, error) {
c, err := sockConn(time.Duration(10 * time.Second))
if err != nil {
return -1, nil, fmt.Errorf("could not dial docker daemon: %v", err)
}
client := httputil.NewClientConn(c, nil)
defer client.Close()
req, err := http.NewRequest(method, endpoint, data)
if err != nil {
client.Close()
return -1, nil, fmt.Errorf("could not create new request: %v", err)
}
if ct == "" {
ct = "application/json"
if ct != "" {
req.Header.Set("Content-Type", ct)
}
req.Header.Set("Content-Type", ct)
resp, err := client.Do(req)
if err != nil {
client.Close()
return -1, nil, fmt.Errorf("could not perform request: %v", err)
}
defer resp.Body.Close()
body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
defer client.Close()
return resp.Body.Close()
})
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
return resp.StatusCode, body, fmt.Errorf("received status != 200 OK: %s", resp.Status)
}
b, err := ioutil.ReadAll(resp.Body)
return resp.StatusCode, body, err
}
return resp.StatusCode, b, err
func readBody(b io.ReadCloser) ([]byte, error) {
defer b.Close()
return ioutil.ReadAll(b)
}
func deleteContainer(container string) error {

View File

@ -4,335 +4,22 @@ import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server"
"github.com/docker/docker/api/types"
"github.com/docker/docker/builder"
"github.com/docker/docker/engine"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
)
func TestSaveImageAndThenLoad(t *testing.T) {
eng := NewTestEngine(t)
defer mkDaemonFromEngine(eng, t).Nuke()
// save image
r := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/images/"+unitTestImageID+"/get", nil)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
}
tarball := r.Body
// delete the image
r = httptest.NewRecorder()
req, err = http.NewRequest("DELETE", "/images/"+unitTestImageID, nil)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
}
// make sure there is no image
r = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/images/"+unitTestImageID+"/get", nil)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusNotFound {
t.Fatalf("%d NotFound expected, received %d\n", http.StatusNotFound, r.Code)
}
// load the image
r = httptest.NewRecorder()
req, err = http.NewRequest("POST", "/images/load", tarball)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
}
// finally make sure the image is there
r = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/images/"+unitTestImageID+"/get", nil)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
}
}
func TestGetContainersTop(t *testing.T) {
eng := NewTestEngine(t)
defer mkDaemonFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&runconfig.Config{
Image: unitTestImageID,
Cmd: runconfig.NewCommand("/bin/sh", "-c", "cat"),
OpenStdin: true,
},
t,
)
defer func() {
// Make sure the process dies before destroying daemon
containerKill(eng, containerID, t)
containerWait(eng, containerID, t)
}()
startContainer(eng, containerID, t)
setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
for {
if containerRunning(eng, containerID, t) {
break
}
time.Sleep(10 * time.Millisecond)
}
})
if !containerRunning(eng, containerID, t) {
t.Fatalf("Container should be running")
}
// Make sure sh spawn up cat
setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
in, out := containerAttach(eng, containerID, t)
if err := assertPipe("hello\n", "hello", out, in, 150); err != nil {
t.Fatal(err)
}
})
r := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/containers/"+containerID+"/top?ps_args=aux", nil)
if err != nil {
t.Fatal(err)
}
server.ServeRequest(eng, api.APIVERSION, r, req)
assertHttpNotError(r, t)
var procs engine.Env
if err := procs.Decode(r.Body); err != nil {
t.Fatal(err)
}
if len(procs.GetList("Titles")) != 11 {
t.Fatalf("Expected 11 titles, found %d.", len(procs.GetList("Titles")))
}
if procs.GetList("Titles")[0] != "USER" || procs.GetList("Titles")[10] != "COMMAND" {
t.Fatalf("Expected Titles[0] to be USER and Titles[10] to be COMMAND, found %s and %s.", procs.GetList("Titles")[0], procs.GetList("Titles")[10])
}
processes := [][]string{}
if err := procs.GetJson("Processes", &processes); err != nil {
t.Fatal(err)
}
if len(processes) != 2 {
t.Fatalf("Expected 2 processes, found %d.", len(processes))
}
if processes[0][10] != "/bin/sh -c cat" {
t.Fatalf("Expected `/bin/sh -c cat`, found %s.", processes[0][10])
}
if processes[1][10] != "/bin/sh -c cat" {
t.Fatalf("Expected `/bin/sh -c cat`, found %s.", processes[1][10])
}
}
func TestPostCommit(t *testing.T) {
eng := NewTestEngine(t)
b := &builder.BuilderJob{Engine: eng}
b.Install()
defer mkDaemonFromEngine(eng, t).Nuke()
// Create a container and remove a file
containerID := createTestContainer(eng,
&runconfig.Config{
Image: unitTestImageID,
Cmd: runconfig.NewCommand("touch", "/test"),
},
t,
)
containerRun(eng, containerID, t)
req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+containerID, bytes.NewReader([]byte{}))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
server.ServeRequest(eng, api.APIVERSION, r, req)
assertHttpNotError(r, t)
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
var env engine.Env
if err := env.Decode(r.Body); err != nil {
t.Fatal(err)
}
if err := eng.Job("image_inspect", env.Get("Id")).Run(); err != nil {
t.Fatalf("The image has not been committed")
}
}
func TestPostContainersCreate(t *testing.T) {
eng := NewTestEngine(t)
defer mkDaemonFromEngine(eng, t).Nuke()
configJSON, err := json.Marshal(&runconfig.Config{
Image: unitTestImageID,
Cmd: runconfig.NewCommand("touch", "/test"),
})
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
r := httptest.NewRecorder()
server.ServeRequest(eng, api.APIVERSION, r, req)
assertHttpNotError(r, t)
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
var apiRun engine.Env
if err := apiRun.Decode(r.Body); err != nil {
t.Fatal(err)
}
containerID := apiRun.Get("Id")
containerAssertExists(eng, containerID, t)
containerRun(eng, containerID, t)
if !containerFileExists(eng, containerID, "test", t) {
t.Fatal("Test file was not created")
}
}
func TestPostJsonVerify(t *testing.T) {
eng := NewTestEngine(t)
defer mkDaemonFromEngine(eng, t).Nuke()
configJSON, err := json.Marshal(&runconfig.Config{
Image: unitTestImageID,
Cmd: runconfig.NewCommand("touch", "/test"),
})
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
server.ServeRequest(eng, api.APIVERSION, r, req)
// Don't add Content-Type header
// req.Header.Set("Content-Type", "application/json")
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusInternalServerError || !strings.Contains(((*r.Body).String()), "application/json") {
t.Fatal("Create should have failed due to no Content-Type header - got:", r)
}
// Now add header but with wrong type and retest
req.Header.Set("Content-Type", "application/xml")
server.ServeRequest(eng, api.APIVERSION, r, req)
if r.Code != http.StatusInternalServerError || !strings.Contains(((*r.Body).String()), "application/json") {
t.Fatal("Create should have failed due to wrong Content-Type header - got:", r)
}
}
// Issue 7941 - test to make sure a "null" in JSON is just ignored.
// W/o this fix a null in JSON would be parsed into a string var as "null"
func TestPostCreateNull(t *testing.T) {
eng := NewTestEngine(t)
daemon := mkDaemonFromEngine(eng, t)
defer daemon.Nuke()
configStr := fmt.Sprintf(`{
"Hostname":"",
"Domainname":"",
"Memory":0,
"MemorySwap":0,
"CpuShares":0,
"Cpuset":null,
"AttachStdin":true,
"AttachStdout":true,
"AttachStderr":true,
"PortSpecs":null,
"ExposedPorts":{},
"Tty":true,
"OpenStdin":true,
"StdinOnce":true,
"Env":[],
"Cmd":"ls",
"Image":"%s",
"Volumes":{},
"WorkingDir":"",
"Entrypoint":null,
"NetworkDisabled":false,
"OnBuild":null}`, unitTestImageID)
req, err := http.NewRequest("POST", "/containers/create", strings.NewReader(configStr))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
r := httptest.NewRecorder()
server.ServeRequest(eng, api.APIVERSION, r, req)
assertHttpNotError(r, t)
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
var apiRun engine.Env
if err := apiRun.Decode(r.Body); err != nil {
t.Fatal(err)
}
containerID := apiRun.Get("Id")
containerAssertExists(eng, containerID, t)
c, _ := daemon.Get(containerID)
if c.HostConfig().CpusetCpus != "" {
t.Fatalf("Cpuset should have been empty - instead its:" + c.HostConfig().CpusetCpus)
}
}
func TestPostContainersKill(t *testing.T) {
eng := NewTestEngine(t)
defer mkDaemonFromEngine(eng, t).Nuke()