image_push_test.go 5.9 KB

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