image_push_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 TestImagePushReferenceError(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.ImagePush(context.Background(), "", types.ImagePushOptions{})
  24. if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
  25. t.Fatalf("expected an error, got %v", err)
  26. }
  27. // An canonical reference cannot be pushed
  28. _, err = client.ImagePush(context.Background(), "repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", types.ImagePushOptions{})
  29. if err == nil || err.Error() != "cannot push a digest reference" {
  30. t.Fatalf("expected an error, got %v", err)
  31. }
  32. }
  33. func TestImagePushAnyError(t *testing.T) {
  34. client := &Client{
  35. client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
  36. }
  37. _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{})
  38. assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
  39. }
  40. func TestImagePushStatusUnauthorizedError(t *testing.T) {
  41. client := &Client{
  42. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  43. }
  44. _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{})
  45. assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
  46. }
  47. func TestImagePushWithUnauthorizedErrorAndPrivilegeFuncError(t *testing.T) {
  48. client := &Client{
  49. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  50. }
  51. privilegeFunc := func() (string, error) {
  52. return "", fmt.Errorf("Error requesting privilege")
  53. }
  54. _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{
  55. PrivilegeFunc: privilegeFunc,
  56. })
  57. if err == nil || err.Error() != "Error requesting privilege" {
  58. t.Fatalf("expected an error requesting privilege, got %v", err)
  59. }
  60. }
  61. func TestImagePushWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing.T) {
  62. client := &Client{
  63. client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
  64. }
  65. privilegeFunc := func() (string, error) {
  66. return "a-auth-header", nil
  67. }
  68. _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{
  69. PrivilegeFunc: privilegeFunc,
  70. })
  71. assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
  72. }
  73. func TestImagePushWithPrivilegedFuncNoError(t *testing.T) {
  74. expectedURL := "/images/myimage/push"
  75. client := &Client{
  76. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  77. if !strings.HasPrefix(req.URL.Path, expectedURL) {
  78. return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
  79. }
  80. auth := req.Header.Get(registry.AuthHeader)
  81. if auth == "NotValid" {
  82. return &http.Response{
  83. StatusCode: http.StatusUnauthorized,
  84. Body: io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))),
  85. }, nil
  86. }
  87. if auth != "IAmValid" {
  88. return nil, fmt.Errorf("invalid auth header: expected %s, got %s", "IAmValid", auth)
  89. }
  90. query := req.URL.Query()
  91. tag := query.Get("tag")
  92. if tag != "tag" {
  93. return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", "tag", tag)
  94. }
  95. return &http.Response{
  96. StatusCode: http.StatusOK,
  97. Body: io.NopCloser(bytes.NewReader([]byte("hello world"))),
  98. }, nil
  99. }),
  100. }
  101. privilegeFunc := func() (string, error) {
  102. return "IAmValid", nil
  103. }
  104. resp, err := client.ImagePush(context.Background(), "myimage:tag", types.ImagePushOptions{
  105. RegistryAuth: "NotValid",
  106. PrivilegeFunc: privilegeFunc,
  107. })
  108. if err != nil {
  109. t.Fatal(err)
  110. }
  111. body, err := io.ReadAll(resp)
  112. if err != nil {
  113. t.Fatal(err)
  114. }
  115. if string(body) != "hello world" {
  116. t.Fatalf("expected 'hello world', got %s", string(body))
  117. }
  118. }
  119. func TestImagePushWithoutErrors(t *testing.T) {
  120. expectedOutput := "hello world"
  121. expectedURLFormat := "/images/%s/push"
  122. testCases := []struct {
  123. all bool
  124. reference string
  125. expectedImage string
  126. expectedTag string
  127. }{
  128. {
  129. all: false,
  130. reference: "myimage",
  131. expectedImage: "myimage",
  132. expectedTag: "latest",
  133. },
  134. {
  135. all: false,
  136. reference: "myimage:tag",
  137. expectedImage: "myimage",
  138. expectedTag: "tag",
  139. },
  140. {
  141. all: true,
  142. reference: "myimage",
  143. expectedImage: "myimage",
  144. expectedTag: "",
  145. },
  146. {
  147. all: true,
  148. reference: "myimage:anything",
  149. expectedImage: "myimage",
  150. expectedTag: "",
  151. },
  152. }
  153. for _, tc := range testCases {
  154. tc := tc
  155. t.Run(fmt.Sprintf("%s,all-tags=%t", tc.reference, tc.all), func(t *testing.T) {
  156. client := &Client{
  157. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  158. expectedURL := fmt.Sprintf(expectedURLFormat, tc.expectedImage)
  159. if !strings.HasPrefix(req.URL.Path, expectedURL) {
  160. return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
  161. }
  162. query := req.URL.Query()
  163. tag := query.Get("tag")
  164. if tag != tc.expectedTag {
  165. return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", tc.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.ImagePush(context.Background(), tc.reference, types.ImagePushOptions{
  174. All: tc.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. }
  188. }