image_pull_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package client // import "github.com/docker/docker/client"
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "testing"
  10. "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/api/types/registry"
  12. "github.com/docker/docker/errdefs"
  13. "gotest.tools/v3/assert"
  14. is "gotest.tools/v3/assert/cmp"
  15. )
  16. func TestImagePullReferenceParseError(t *testing.T) {
  17. client := &Client{
  18. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  19. return nil, nil
  20. }),
  21. }
  22. // An empty reference is an invalid reference
  23. _, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
  24. if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
  25. t.Fatalf("expected an error, got %v", err)
  26. }
  27. }
  28. func TestImagePullAnyError(t *testing.T) {
  29. client := &Client{
  30. client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
  31. }
  32. _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{})
  33. assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
  34. }
  35. func TestImagePullStatusUnauthorizedError(t *testing.T) {
  36. client := &Client{
  37. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  38. }
  39. _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{})
  40. assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
  41. }
  42. func TestImagePullWithUnauthorizedErrorAndPrivilegeFuncError(t *testing.T) {
  43. client := &Client{
  44. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  45. }
  46. privilegeFunc := func() (string, error) {
  47. return "", fmt.Errorf("Error requesting privilege")
  48. }
  49. _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{
  50. PrivilegeFunc: privilegeFunc,
  51. })
  52. if err == nil || err.Error() != "Error requesting privilege" {
  53. t.Fatalf("expected an error requesting privilege, got %v", err)
  54. }
  55. }
  56. func TestImagePullWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing.T) {
  57. client := &Client{
  58. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  59. }
  60. privilegeFunc := func() (string, error) {
  61. return "a-auth-header", nil
  62. }
  63. _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{
  64. PrivilegeFunc: privilegeFunc,
  65. })
  66. assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
  67. }
  68. func TestImagePullWithPrivilegedFuncNoError(t *testing.T) {
  69. expectedURL := "/images/create"
  70. client := &Client{
  71. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  72. if !strings.HasPrefix(req.URL.Path, expectedURL) {
  73. return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL)
  74. }
  75. auth := req.Header.Get(registry.AuthHeader)
  76. if auth == "NotValid" {
  77. return &http.Response{
  78. StatusCode: http.StatusUnauthorized,
  79. Body: io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))),
  80. }, nil
  81. }
  82. if auth != "IAmValid" {
  83. return nil, fmt.Errorf("invalid auth header: expected %s, got %s", "IAmValid", auth)
  84. }
  85. query := req.URL.Query()
  86. fromImage := query.Get("fromImage")
  87. if fromImage != "myimage" {
  88. return nil, fmt.Errorf("fromimage not set in URL query properly. Expected '%s', got %s", "myimage", fromImage)
  89. }
  90. tag := query.Get("tag")
  91. if tag != "latest" {
  92. return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", "latest", tag)
  93. }
  94. return &http.Response{
  95. StatusCode: http.StatusOK,
  96. Body: io.NopCloser(bytes.NewReader([]byte("hello world"))),
  97. }, nil
  98. }),
  99. }
  100. privilegeFunc := func() (string, error) {
  101. return "IAmValid", nil
  102. }
  103. resp, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{
  104. RegistryAuth: "NotValid",
  105. PrivilegeFunc: privilegeFunc,
  106. })
  107. if err != nil {
  108. t.Fatal(err)
  109. }
  110. body, err := io.ReadAll(resp)
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. if string(body) != "hello world" {
  115. t.Fatalf("expected 'hello world', got %s", string(body))
  116. }
  117. }
  118. func TestImagePullWithoutErrors(t *testing.T) {
  119. expectedURL := "/images/create"
  120. expectedOutput := "hello world"
  121. pullCases := []struct {
  122. all bool
  123. reference string
  124. expectedImage string
  125. expectedTag string
  126. }{
  127. {
  128. all: false,
  129. reference: "myimage",
  130. expectedImage: "myimage",
  131. expectedTag: "latest",
  132. },
  133. {
  134. all: false,
  135. reference: "myimage:tag",
  136. expectedImage: "myimage",
  137. expectedTag: "tag",
  138. },
  139. {
  140. all: true,
  141. reference: "myimage",
  142. expectedImage: "myimage",
  143. expectedTag: "",
  144. },
  145. {
  146. all: true,
  147. reference: "myimage:anything",
  148. expectedImage: "myimage",
  149. expectedTag: "",
  150. },
  151. }
  152. for _, pullCase := range pullCases {
  153. client := &Client{
  154. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  155. if !strings.HasPrefix(req.URL.Path, expectedURL) {
  156. return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
  157. }
  158. query := req.URL.Query()
  159. fromImage := query.Get("fromImage")
  160. if fromImage != pullCase.expectedImage {
  161. return nil, fmt.Errorf("fromimage not set in URL query properly. Expected '%s', got %s", pullCase.expectedImage, fromImage)
  162. }
  163. tag := query.Get("tag")
  164. if tag != pullCase.expectedTag {
  165. return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", pullCase.expectedTag, tag)
  166. }
  167. return &http.Response{
  168. StatusCode: http.StatusOK,
  169. Body: io.NopCloser(bytes.NewReader([]byte(expectedOutput))),
  170. }, nil
  171. }),
  172. }
  173. resp, err := client.ImagePull(context.Background(), pullCase.reference, types.ImagePullOptions{
  174. All: pullCase.all,
  175. })
  176. if err != nil {
  177. t.Fatal(err)
  178. }
  179. body, err := io.ReadAll(resp)
  180. if err != nil {
  181. t.Fatal(err)
  182. }
  183. if string(body) != expectedOutput {
  184. t.Fatalf("expected '%s', got %s", expectedOutput, string(body))
  185. }
  186. }
  187. }