remote_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package remotecontext
  2. import (
  3. "bytes"
  4. "io"
  5. "io/ioutil"
  6. "net/http"
  7. "net/http/httptest"
  8. "net/url"
  9. "testing"
  10. "github.com/docker/docker/builder"
  11. "github.com/docker/docker/internal/testutil"
  12. "github.com/docker/docker/pkg/archive"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. )
  16. var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} //xz magic
  17. func TestSelectAcceptableMIME(t *testing.T) {
  18. validMimeStrings := []string{
  19. "application/x-bzip2",
  20. "application/bzip2",
  21. "application/gzip",
  22. "application/x-gzip",
  23. "application/x-xz",
  24. "application/xz",
  25. "application/tar",
  26. "application/x-tar",
  27. "application/octet-stream",
  28. "text/plain",
  29. }
  30. invalidMimeStrings := []string{
  31. "",
  32. "application/octet",
  33. "application/json",
  34. }
  35. for _, m := range invalidMimeStrings {
  36. if len(selectAcceptableMIME(m)) > 0 {
  37. t.Fatalf("Should not have accepted %q", m)
  38. }
  39. }
  40. for _, m := range validMimeStrings {
  41. if str := selectAcceptableMIME(m); str == "" {
  42. t.Fatalf("Should have accepted %q", m)
  43. }
  44. }
  45. }
  46. func TestInspectEmptyResponse(t *testing.T) {
  47. ct := "application/octet-stream"
  48. br := ioutil.NopCloser(bytes.NewReader([]byte("")))
  49. contentType, bReader, err := inspectResponse(ct, br, 0)
  50. if err == nil {
  51. t.Fatal("Should have generated an error for an empty response")
  52. }
  53. if contentType != "application/octet-stream" {
  54. t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
  55. }
  56. body, err := ioutil.ReadAll(bReader)
  57. if err != nil {
  58. t.Fatal(err)
  59. }
  60. if len(body) != 0 {
  61. t.Fatal("response body should remain empty")
  62. }
  63. }
  64. func TestInspectResponseBinary(t *testing.T) {
  65. ct := "application/octet-stream"
  66. br := ioutil.NopCloser(bytes.NewReader(binaryContext))
  67. contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext)))
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. if contentType != "application/octet-stream" {
  72. t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
  73. }
  74. body, err := ioutil.ReadAll(bReader)
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. if len(body) != len(binaryContext) {
  79. t.Fatalf("Wrong response size %d, should be == len(binaryContext)", len(body))
  80. }
  81. for i := range body {
  82. if body[i] != binaryContext[i] {
  83. t.Fatalf("Corrupted response body at byte index %d", i)
  84. }
  85. }
  86. }
  87. func TestResponseUnsupportedContentType(t *testing.T) {
  88. content := []byte(dockerfileContents)
  89. ct := "application/json"
  90. br := ioutil.NopCloser(bytes.NewReader(content))
  91. contentType, bReader, err := inspectResponse(ct, br, int64(len(dockerfileContents)))
  92. if err == nil {
  93. t.Fatal("Should have returned an error on content-type 'application/json'")
  94. }
  95. if contentType != ct {
  96. t.Fatalf("Should not have altered content-type: orig: %s, altered: %s", ct, contentType)
  97. }
  98. body, err := ioutil.ReadAll(bReader)
  99. if err != nil {
  100. t.Fatal(err)
  101. }
  102. if string(body) != dockerfileContents {
  103. t.Fatalf("Corrupted response body %s", body)
  104. }
  105. }
  106. func TestInspectResponseTextSimple(t *testing.T) {
  107. content := []byte(dockerfileContents)
  108. ct := "text/plain"
  109. br := ioutil.NopCloser(bytes.NewReader(content))
  110. contentType, bReader, err := inspectResponse(ct, br, int64(len(content)))
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. if contentType != "text/plain" {
  115. t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
  116. }
  117. body, err := ioutil.ReadAll(bReader)
  118. if err != nil {
  119. t.Fatal(err)
  120. }
  121. if string(body) != dockerfileContents {
  122. t.Fatalf("Corrupted response body %s", body)
  123. }
  124. }
  125. func TestInspectResponseEmptyContentType(t *testing.T) {
  126. content := []byte(dockerfileContents)
  127. br := ioutil.NopCloser(bytes.NewReader(content))
  128. contentType, bodyReader, err := inspectResponse("", br, int64(len(content)))
  129. if err != nil {
  130. t.Fatal(err)
  131. }
  132. if contentType != "text/plain" {
  133. t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
  134. }
  135. body, err := ioutil.ReadAll(bodyReader)
  136. if err != nil {
  137. t.Fatal(err)
  138. }
  139. if string(body) != dockerfileContents {
  140. t.Fatalf("Corrupted response body %s", body)
  141. }
  142. }
  143. func TestUnknownContentLength(t *testing.T) {
  144. content := []byte(dockerfileContents)
  145. ct := "text/plain"
  146. br := ioutil.NopCloser(bytes.NewReader(content))
  147. contentType, bReader, err := inspectResponse(ct, br, -1)
  148. if err != nil {
  149. t.Fatal(err)
  150. }
  151. if contentType != "text/plain" {
  152. t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
  153. }
  154. body, err := ioutil.ReadAll(bReader)
  155. if err != nil {
  156. t.Fatal(err)
  157. }
  158. if string(body) != dockerfileContents {
  159. t.Fatalf("Corrupted response body %s", body)
  160. }
  161. }
  162. func TestMakeRemoteContext(t *testing.T) {
  163. contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test")
  164. defer cleanup()
  165. createTestTempFile(t, contextDir, builder.DefaultDockerfileName, dockerfileContents, 0777)
  166. mux := http.NewServeMux()
  167. server := httptest.NewServer(mux)
  168. serverURL, _ := url.Parse(server.URL)
  169. serverURL.Path = "/" + builder.DefaultDockerfileName
  170. remoteURL := serverURL.String()
  171. mux.Handle("/", http.FileServer(http.Dir(contextDir)))
  172. remoteContext, err := MakeRemoteContext(remoteURL, map[string]func(io.ReadCloser) (io.ReadCloser, error){
  173. mimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) {
  174. dockerfile, err := ioutil.ReadAll(rc)
  175. if err != nil {
  176. return nil, err
  177. }
  178. r, err := archive.Generate(builder.DefaultDockerfileName, string(dockerfile))
  179. if err != nil {
  180. return nil, err
  181. }
  182. return ioutil.NopCloser(r), nil
  183. },
  184. })
  185. if err != nil {
  186. t.Fatalf("Error when executing DetectContextFromRemoteURL: %s", err)
  187. }
  188. if remoteContext == nil {
  189. t.Fatal("Remote context should not be nil")
  190. }
  191. h, err := remoteContext.Hash(builder.DefaultDockerfileName)
  192. if err != nil {
  193. t.Fatalf("failed to compute hash %s", err)
  194. }
  195. if expected, actual := "7b6b6b66bee9e2102fbdc2228be6c980a2a23adf371962a37286a49f7de0f7cc", h; expected != actual {
  196. t.Fatalf("There should be file named %s %s in fileInfoSums", expected, actual)
  197. }
  198. }
  199. func TestGetWithStatusError(t *testing.T) {
  200. var testcases = []struct {
  201. err error
  202. statusCode int
  203. expectedErr string
  204. expectedBody string
  205. }{
  206. {
  207. statusCode: 200,
  208. expectedBody: "THE BODY",
  209. },
  210. {
  211. statusCode: 400,
  212. expectedErr: "with status 400 Bad Request: broke",
  213. expectedBody: "broke",
  214. },
  215. }
  216. for _, testcase := range testcases {
  217. ts := httptest.NewServer(
  218. http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  219. buffer := bytes.NewBufferString(testcase.expectedBody)
  220. w.WriteHeader(testcase.statusCode)
  221. w.Write(buffer.Bytes())
  222. }),
  223. )
  224. defer ts.Close()
  225. response, err := GetWithStatusError(ts.URL)
  226. if testcase.expectedErr == "" {
  227. require.NoError(t, err)
  228. body, err := readBody(response.Body)
  229. require.NoError(t, err)
  230. assert.Contains(t, string(body), testcase.expectedBody)
  231. } else {
  232. testutil.ErrorContains(t, err, testcase.expectedErr)
  233. }
  234. }
  235. }
  236. func readBody(b io.ReadCloser) ([]byte, error) {
  237. defer b.Close()
  238. return ioutil.ReadAll(b)
  239. }