api_test.go 21 KB

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