api_test.go 19 KB

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