api_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. package docker
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "io"
  7. "io/ioutil"
  8. "net"
  9. "net/http"
  10. "net/http/httptest"
  11. "testing"
  12. "time"
  13. "github.com/docker/docker/api"
  14. "github.com/docker/docker/api/server"
  15. "github.com/docker/docker/api/types"
  16. "github.com/docker/docker/engine"
  17. "github.com/docker/docker/runconfig"
  18. "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
  19. )
  20. func TestPostContainersKill(t *testing.T) {
  21. eng := NewTestEngine(t)
  22. defer mkDaemonFromEngine(eng, t).Nuke()
  23. containerID := createTestContainer(eng,
  24. &runconfig.Config{
  25. Image: unitTestImageID,
  26. Cmd: runconfig.NewCommand("/bin/cat"),
  27. OpenStdin: true,
  28. },
  29. t,
  30. )
  31. startContainer(eng, containerID, t)
  32. // Give some time to the process to start
  33. containerWaitTimeout(eng, containerID, t)
  34. if !containerRunning(eng, containerID, t) {
  35. t.Errorf("Container should be running")
  36. }
  37. r := httptest.NewRecorder()
  38. req, err := http.NewRequest("POST", "/containers/"+containerID+"/kill", bytes.NewReader([]byte{}))
  39. if err != nil {
  40. t.Fatal(err)
  41. }
  42. server.ServeRequest(eng, api.APIVERSION, r, req)
  43. assertHttpNotError(r, t)
  44. if r.Code != http.StatusNoContent {
  45. t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
  46. }
  47. if containerRunning(eng, containerID, t) {
  48. t.Fatalf("The container hasn't been killed")
  49. }
  50. }
  51. func TestPostContainersRestart(t *testing.T) {
  52. eng := NewTestEngine(t)
  53. defer mkDaemonFromEngine(eng, t).Nuke()
  54. containerID := createTestContainer(eng,
  55. &runconfig.Config{
  56. Image: unitTestImageID,
  57. Cmd: runconfig.NewCommand("/bin/top"),
  58. OpenStdin: true,
  59. },
  60. t,
  61. )
  62. startContainer(eng, containerID, t)
  63. // Give some time to the process to start
  64. containerWaitTimeout(eng, containerID, t)
  65. if !containerRunning(eng, containerID, t) {
  66. t.Errorf("Container should be running")
  67. }
  68. req, err := http.NewRequest("POST", "/containers/"+containerID+"/restart?t=1", bytes.NewReader([]byte{}))
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. r := httptest.NewRecorder()
  73. server.ServeRequest(eng, api.APIVERSION, r, req)
  74. assertHttpNotError(r, t)
  75. if r.Code != http.StatusNoContent {
  76. t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
  77. }
  78. // Give some time to the process to restart
  79. containerWaitTimeout(eng, containerID, t)
  80. if !containerRunning(eng, containerID, t) {
  81. t.Fatalf("Container should be running")
  82. }
  83. containerKill(eng, containerID, t)
  84. }
  85. func TestPostContainersStart(t *testing.T) {
  86. eng := NewTestEngine(t)
  87. defer mkDaemonFromEngine(eng, t).Nuke()
  88. containerID := createTestContainer(
  89. eng,
  90. &runconfig.Config{
  91. Image: unitTestImageID,
  92. Cmd: runconfig.NewCommand("/bin/cat"),
  93. OpenStdin: true,
  94. },
  95. t,
  96. )
  97. hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{})
  98. req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
  99. if err != nil {
  100. t.Fatal(err)
  101. }
  102. req.Header.Set("Content-Type", "application/json")
  103. r := httptest.NewRecorder()
  104. server.ServeRequest(eng, api.APIVERSION, r, req)
  105. assertHttpNotError(r, t)
  106. if r.Code != http.StatusNoContent {
  107. t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
  108. }
  109. containerAssertExists(eng, containerID, t)
  110. req, err = http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. req.Header.Set("Content-Type", "application/json")
  115. r = httptest.NewRecorder()
  116. server.ServeRequest(eng, api.APIVERSION, r, req)
  117. // Starting an already started container should return a 304
  118. assertHttpNotError(r, t)
  119. if r.Code != http.StatusNotModified {
  120. t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code)
  121. }
  122. containerAssertExists(eng, containerID, t)
  123. containerKill(eng, containerID, t)
  124. }
  125. func TestPostContainersStop(t *testing.T) {
  126. eng := NewTestEngine(t)
  127. defer mkDaemonFromEngine(eng, t).Nuke()
  128. containerID := createTestContainer(eng,
  129. &runconfig.Config{
  130. Image: unitTestImageID,
  131. Cmd: runconfig.NewCommand("/bin/top"),
  132. OpenStdin: true,
  133. },
  134. t,
  135. )
  136. startContainer(eng, containerID, t)
  137. // Give some time to the process to start
  138. containerWaitTimeout(eng, containerID, t)
  139. if !containerRunning(eng, containerID, t) {
  140. t.Errorf("Container should be running")
  141. }
  142. // Note: as it is a POST request, it requires a body.
  143. req, err := http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{}))
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. r := httptest.NewRecorder()
  148. server.ServeRequest(eng, api.APIVERSION, r, req)
  149. assertHttpNotError(r, t)
  150. if r.Code != http.StatusNoContent {
  151. t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
  152. }
  153. if containerRunning(eng, containerID, t) {
  154. t.Fatalf("The container hasn't been stopped")
  155. }
  156. req, err = http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{}))
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. r = httptest.NewRecorder()
  161. server.ServeRequest(eng, api.APIVERSION, r, req)
  162. // Stopping an already stopper container should return a 304
  163. assertHttpNotError(r, t)
  164. if r.Code != http.StatusNotModified {
  165. t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code)
  166. }
  167. }
  168. func TestPostContainersWait(t *testing.T) {
  169. eng := NewTestEngine(t)
  170. defer mkDaemonFromEngine(eng, t).Nuke()
  171. containerID := createTestContainer(eng,
  172. &runconfig.Config{
  173. Image: unitTestImageID,
  174. Cmd: runconfig.NewCommand("/bin/sleep", "1"),
  175. OpenStdin: true,
  176. },
  177. t,
  178. )
  179. startContainer(eng, containerID, t)
  180. setTimeout(t, "Wait timed out", 3*time.Second, func() {
  181. r := httptest.NewRecorder()
  182. req, err := http.NewRequest("POST", "/containers/"+containerID+"/wait", bytes.NewReader([]byte{}))
  183. if err != nil {
  184. t.Fatal(err)
  185. }
  186. server.ServeRequest(eng, api.APIVERSION, r, req)
  187. assertHttpNotError(r, t)
  188. var apiWait engine.Env
  189. if err := apiWait.Decode(r.Body); err != nil {
  190. t.Fatal(err)
  191. }
  192. if apiWait.GetInt("StatusCode") != 0 {
  193. t.Fatalf("Non zero exit code for sleep: %d\n", apiWait.GetInt("StatusCode"))
  194. }
  195. })
  196. if containerRunning(eng, containerID, t) {
  197. t.Fatalf("The container should be stopped after wait")
  198. }
  199. }
  200. func TestPostContainersAttach(t *testing.T) {
  201. eng := NewTestEngine(t)
  202. defer mkDaemonFromEngine(eng, t).Nuke()
  203. containerID := createTestContainer(eng,
  204. &runconfig.Config{
  205. Image: unitTestImageID,
  206. Cmd: runconfig.NewCommand("/bin/cat"),
  207. OpenStdin: true,
  208. },
  209. t,
  210. )
  211. // Start the process
  212. startContainer(eng, containerID, t)
  213. stdin, stdinPipe := io.Pipe()
  214. stdout, stdoutPipe := io.Pipe()
  215. // Try to avoid the timeout in destroy. Best effort, don't check error
  216. defer func() {
  217. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  218. containerKill(eng, containerID, t)
  219. }()
  220. // Attach to it
  221. c1 := make(chan struct{})
  222. go func() {
  223. defer close(c1)
  224. r := &hijackTester{
  225. ResponseRecorder: httptest.NewRecorder(),
  226. in: stdin,
  227. out: stdoutPipe,
  228. }
  229. req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
  230. if err != nil {
  231. t.Fatal(err)
  232. }
  233. server.ServeRequest(eng, api.APIVERSION, r, req)
  234. assertHttpNotError(r.ResponseRecorder, t)
  235. }()
  236. // Acknowledge hijack
  237. setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
  238. stdout.Read([]byte{})
  239. stdout.Read(make([]byte, 4096))
  240. })
  241. setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
  242. if err := assertPipe("hello\n", string([]byte{1, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil {
  243. t.Fatal(err)
  244. }
  245. })
  246. // Close pipes (client disconnects)
  247. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  248. t.Fatal(err)
  249. }
  250. // Wait for attach to finish, the client disconnected, therefore, Attach finished his job
  251. setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() {
  252. <-c1
  253. })
  254. // We closed stdin, expect /bin/cat to still be running
  255. // Wait a little bit to make sure container.monitor() did his thing
  256. containerWaitTimeout(eng, containerID, t)
  257. // Try to avoid the timeout in destroy. Best effort, don't check error
  258. cStdin, _ := containerAttach(eng, containerID, t)
  259. cStdin.Close()
  260. containerWait(eng, containerID, t)
  261. }
  262. func TestPostContainersAttachStderr(t *testing.T) {
  263. eng := NewTestEngine(t)
  264. defer mkDaemonFromEngine(eng, t).Nuke()
  265. containerID := createTestContainer(eng,
  266. &runconfig.Config{
  267. Image: unitTestImageID,
  268. Cmd: runconfig.NewCommand("/bin/sh", "-c", "/bin/cat >&2"),
  269. OpenStdin: true,
  270. },
  271. t,
  272. )
  273. // Start the process
  274. startContainer(eng, containerID, t)
  275. stdin, stdinPipe := io.Pipe()
  276. stdout, stdoutPipe := io.Pipe()
  277. // Try to avoid the timeout in destroy. Best effort, don't check error
  278. defer func() {
  279. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  280. containerKill(eng, containerID, t)
  281. }()
  282. // Attach to it
  283. c1 := make(chan struct{})
  284. go func() {
  285. defer close(c1)
  286. r := &hijackTester{
  287. ResponseRecorder: httptest.NewRecorder(),
  288. in: stdin,
  289. out: stdoutPipe,
  290. }
  291. req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. server.ServeRequest(eng, api.APIVERSION, r, req)
  296. assertHttpNotError(r.ResponseRecorder, t)
  297. }()
  298. // Acknowledge hijack
  299. setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
  300. stdout.Read([]byte{})
  301. stdout.Read(make([]byte, 4096))
  302. })
  303. setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
  304. if err := assertPipe("hello\n", string([]byte{2, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil {
  305. t.Fatal(err)
  306. }
  307. })
  308. // Close pipes (client disconnects)
  309. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  310. t.Fatal(err)
  311. }
  312. // Wait for attach to finish, the client disconnected, therefore, Attach finished his job
  313. setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() {
  314. <-c1
  315. })
  316. // We closed stdin, expect /bin/cat to still be running
  317. // Wait a little bit to make sure container.monitor() did his thing
  318. containerWaitTimeout(eng, containerID, t)
  319. // Try to avoid the timeout in destroy. Best effort, don't check error
  320. cStdin, _ := containerAttach(eng, containerID, t)
  321. cStdin.Close()
  322. containerWait(eng, containerID, t)
  323. }
  324. func TestOptionsRoute(t *testing.T) {
  325. eng := NewTestEngine(t)
  326. defer mkDaemonFromEngine(eng, t).Nuke()
  327. r := httptest.NewRecorder()
  328. req, err := http.NewRequest("OPTIONS", "/", nil)
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. server.ServeRequest(eng, api.APIVERSION, r, req)
  333. assertHttpNotError(r, t)
  334. if r.Code != http.StatusOK {
  335. t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
  336. }
  337. }
  338. func TestGetEnabledCors(t *testing.T) {
  339. eng := NewTestEngine(t)
  340. defer mkDaemonFromEngine(eng, t).Nuke()
  341. r := httptest.NewRecorder()
  342. req, err := http.NewRequest("GET", "/version", nil)
  343. if err != nil {
  344. t.Fatal(err)
  345. }
  346. server.ServeRequest(eng, api.APIVERSION, r, req)
  347. assertHttpNotError(r, t)
  348. if r.Code != http.StatusOK {
  349. t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
  350. }
  351. allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
  352. allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
  353. allowMethods := r.Header().Get("Access-Control-Allow-Methods")
  354. if allowOrigin != "*" {
  355. t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
  356. }
  357. if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" {
  358. t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth\", %s found.", allowHeaders)
  359. }
  360. if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" {
  361. t.Errorf("Expected header Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods)
  362. }
  363. }
  364. func TestDeleteImages(t *testing.T) {
  365. eng := NewTestEngine(t)
  366. //we expect errors, so we disable stderr
  367. eng.Stderr = ioutil.Discard
  368. defer mkDaemonFromEngine(eng, t).Nuke()
  369. initialImages := getImages(eng, t, true, "")
  370. d := getDaemon(eng)
  371. if err := d.Repositories().Tag("test", "test", unitTestImageName, true); err != nil {
  372. t.Fatal(err)
  373. }
  374. images := getImages(eng, t, true, "")
  375. if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 {
  376. t.Errorf("Expected %d images, %d found", len(initialImages[0].RepoTags)+1, len(images[0].RepoTags))
  377. }
  378. req, err := http.NewRequest("DELETE", "/images/"+unitTestImageID, nil)
  379. if err != nil {
  380. t.Fatal(err)
  381. }
  382. r := httptest.NewRecorder()
  383. server.ServeRequest(eng, api.APIVERSION, r, req)
  384. if r.Code != http.StatusConflict {
  385. t.Fatalf("Expected http status 409-conflict, got %v", r.Code)
  386. }
  387. req2, err := http.NewRequest("DELETE", "/images/test:test", nil)
  388. if err != nil {
  389. t.Fatal(err)
  390. }
  391. r2 := httptest.NewRecorder()
  392. server.ServeRequest(eng, api.APIVERSION, r2, req2)
  393. assertHttpNotError(r2, t)
  394. if r2.Code != http.StatusOK {
  395. t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
  396. }
  397. delImages := []types.ImageDelete{}
  398. err = json.Unmarshal(r2.Body.Bytes(), &delImages)
  399. if err != nil {
  400. t.Fatal(err)
  401. }
  402. if len(delImages) != 1 {
  403. t.Fatalf("Expected %d event (untagged), got %d", 1, len(delImages))
  404. }
  405. images = getImages(eng, t, false, "")
  406. if len(images) != len(initialImages) {
  407. t.Errorf("Expected %d image, %d found", len(initialImages), len(images))
  408. }
  409. }
  410. func TestPostContainersCopy(t *testing.T) {
  411. eng := NewTestEngine(t)
  412. defer mkDaemonFromEngine(eng, t).Nuke()
  413. // Create a container and remove a file
  414. containerID := createTestContainer(eng,
  415. &runconfig.Config{
  416. Image: unitTestImageID,
  417. Cmd: runconfig.NewCommand("touch", "/test.txt"),
  418. },
  419. t,
  420. )
  421. containerRun(eng, containerID, t)
  422. r := httptest.NewRecorder()
  423. var copyData engine.Env
  424. copyData.Set("Resource", "/test.txt")
  425. copyData.Set("HostPath", ".")
  426. jsonData := bytes.NewBuffer(nil)
  427. if err := copyData.Encode(jsonData); err != nil {
  428. t.Fatal(err)
  429. }
  430. req, err := http.NewRequest("POST", "/containers/"+containerID+"/copy", jsonData)
  431. if err != nil {
  432. t.Fatal(err)
  433. }
  434. req.Header.Add("Content-Type", "application/json")
  435. server.ServeRequest(eng, api.APIVERSION, r, req)
  436. assertHttpNotError(r, t)
  437. if r.Code != http.StatusOK {
  438. t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
  439. }
  440. found := false
  441. for tarReader := tar.NewReader(r.Body); ; {
  442. h, err := tarReader.Next()
  443. if err != nil {
  444. if err == io.EOF {
  445. break
  446. }
  447. t.Fatal(err)
  448. }
  449. if h.Name == "test.txt" {
  450. found = true
  451. break
  452. }
  453. }
  454. if !found {
  455. t.Fatalf("The created test file has not been found in the copied output")
  456. }
  457. }
  458. func TestPostContainersCopyWhenContainerNotFound(t *testing.T) {
  459. eng := NewTestEngine(t)
  460. defer mkDaemonFromEngine(eng, t).Nuke()
  461. r := httptest.NewRecorder()
  462. var copyData engine.Env
  463. copyData.Set("Resource", "/test.txt")
  464. copyData.Set("HostPath", ".")
  465. jsonData := bytes.NewBuffer(nil)
  466. if err := copyData.Encode(jsonData); err != nil {
  467. t.Fatal(err)
  468. }
  469. req, err := http.NewRequest("POST", "/containers/id_not_found/copy", jsonData)
  470. if err != nil {
  471. t.Fatal(err)
  472. }
  473. req.Header.Add("Content-Type", "application/json")
  474. server.ServeRequest(eng, api.APIVERSION, r, req)
  475. if r.Code != http.StatusNotFound {
  476. t.Fatalf("404 expected for id_not_found Container, received %v", r.Code)
  477. }
  478. }
  479. // Regression test for https://github.com/docker/docker/issues/6231
  480. func TestConstainersStartChunkedEncodingHostConfig(t *testing.T) {
  481. eng := NewTestEngine(t)
  482. defer mkDaemonFromEngine(eng, t).Nuke()
  483. r := httptest.NewRecorder()
  484. var testData engine.Env
  485. testData.Set("Image", "docker-test-image")
  486. testData.SetAuto("Volumes", map[string]struct{}{"/foo": {}})
  487. testData.Set("Cmd", "true")
  488. jsonData := bytes.NewBuffer(nil)
  489. if err := testData.Encode(jsonData); err != nil {
  490. t.Fatal(err)
  491. }
  492. req, err := http.NewRequest("POST", "/containers/create?name=chunk_test", jsonData)
  493. if err != nil {
  494. t.Fatal(err)
  495. }
  496. req.Header.Add("Content-Type", "application/json")
  497. server.ServeRequest(eng, api.APIVERSION, r, req)
  498. assertHttpNotError(r, t)
  499. var testData2 engine.Env
  500. testData2.SetAuto("Binds", []string{"/tmp:/foo"})
  501. jsonData = bytes.NewBuffer(nil)
  502. if err := testData2.Encode(jsonData); err != nil {
  503. t.Fatal(err)
  504. }
  505. req, err = http.NewRequest("POST", "/containers/chunk_test/start", jsonData)
  506. if err != nil {
  507. t.Fatal(err)
  508. }
  509. req.Header.Add("Content-Type", "application/json")
  510. // This is a cheat to make the http request do chunked encoding
  511. // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
  512. // https://golang.org/src/pkg/net/http/request.go?s=11980:12172
  513. req.ContentLength = -1
  514. server.ServeRequest(eng, api.APIVERSION, r, req)
  515. assertHttpNotError(r, t)
  516. type config struct {
  517. HostConfig struct {
  518. Binds []string
  519. }
  520. }
  521. req, err = http.NewRequest("GET", "/containers/chunk_test/json", nil)
  522. if err != nil {
  523. t.Fatal(err)
  524. }
  525. r2 := httptest.NewRecorder()
  526. req.Header.Add("Content-Type", "application/json")
  527. server.ServeRequest(eng, api.APIVERSION, r2, req)
  528. assertHttpNotError(r, t)
  529. c := config{}
  530. json.Unmarshal(r2.Body.Bytes(), &c)
  531. if len(c.HostConfig.Binds) == 0 {
  532. t.Fatal("Chunked Encoding not handled")
  533. }
  534. if c.HostConfig.Binds[0] != "/tmp:/foo" {
  535. t.Fatal("Chunked encoding not properly handled, expected binds to be /tmp:/foo, got:", c.HostConfig.Binds[0])
  536. }
  537. }
  538. // Mocked types for tests
  539. type NopConn struct {
  540. io.ReadCloser
  541. io.Writer
  542. }
  543. func (c *NopConn) LocalAddr() net.Addr { return nil }
  544. func (c *NopConn) RemoteAddr() net.Addr { return nil }
  545. func (c *NopConn) SetDeadline(t time.Time) error { return nil }
  546. func (c *NopConn) SetReadDeadline(t time.Time) error { return nil }
  547. func (c *NopConn) SetWriteDeadline(t time.Time) error { return nil }
  548. type hijackTester struct {
  549. *httptest.ResponseRecorder
  550. in io.ReadCloser
  551. out io.Writer
  552. }
  553. func (t *hijackTester) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  554. bufrw := bufio.NewReadWriter(bufio.NewReader(t.in), bufio.NewWriter(t.out))
  555. conn := &NopConn{
  556. ReadCloser: t.in,
  557. Writer: t.out,
  558. }
  559. return conn, bufrw, nil
  560. }