mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
client.ImagePush(): default to ":latest" instead of "all tags"
The `docker push` command up until docker v0.9.1 always pushed all tags of a given image, so `docker push foo/bar` would push (e.g.) all of `foo/bar:latest`, `foo:/bar:v1`, and `foo/bar:v1.0.0`. Pushing all tags of an image was not desirable in many case, so docker v0.10.0 enhanced `docker push` to optionally specify a tag to push (`docker push foo/bar:v1`) (see issue 3411 and PR 4948 (commite648a186d6
). This behavior exists up until today, and is confusing, because unlike other commands, `docker push` does not default to use the `:latest` tag when omitted, but instead makes it push "all tags of the image". `docker pull` had a similar behavior, but PR 7759 (9c08364a41
) changed the behavior to default to the `:latest` tag, and added a `--all-tags` flag to the CLI to optionally pull all images. This patch implements the API client changes to make `docker push` match the behavior of `docker pull`, and default to pull a single image, unless the `all` option is passed. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
2d467dc8d0
commit
d135dc242e
2 changed files with 58 additions and 39 deletions
|
@ -25,15 +25,14 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
|
||||||
return nil, errors.New("cannot push a digest reference")
|
return nil, errors.New("cannot push a digest reference")
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := ""
|
|
||||||
name := reference.FamiliarName(ref)
|
name := reference.FamiliarName(ref)
|
||||||
|
|
||||||
if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
|
|
||||||
tag = nameTaggedRef.Tag()
|
|
||||||
}
|
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("tag", tag)
|
if !options.All {
|
||||||
|
ref = reference.TagNameOnly(ref)
|
||||||
|
if tagged, ok := ref.(reference.Tagged); ok {
|
||||||
|
query.Set("tag", tagged.Tag())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
|
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
|
||||||
if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
|
if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
|
||||||
|
|
|
@ -131,50 +131,70 @@ func TestImagePushWithPrivilegedFuncNoError(t *testing.T) {
|
||||||
func TestImagePushWithoutErrors(t *testing.T) {
|
func TestImagePushWithoutErrors(t *testing.T) {
|
||||||
expectedOutput := "hello world"
|
expectedOutput := "hello world"
|
||||||
expectedURLFormat := "/images/%s/push"
|
expectedURLFormat := "/images/%s/push"
|
||||||
pullCases := []struct {
|
testCases := []struct {
|
||||||
|
all bool
|
||||||
reference string
|
reference string
|
||||||
expectedImage string
|
expectedImage string
|
||||||
expectedTag string
|
expectedTag string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
all: false,
|
||||||
|
reference: "myimage",
|
||||||
|
expectedImage: "myimage",
|
||||||
|
expectedTag: "latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
all: false,
|
||||||
|
reference: "myimage:tag",
|
||||||
|
expectedImage: "myimage",
|
||||||
|
expectedTag: "tag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
all: true,
|
||||||
reference: "myimage",
|
reference: "myimage",
|
||||||
expectedImage: "myimage",
|
expectedImage: "myimage",
|
||||||
expectedTag: "",
|
expectedTag: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reference: "myimage:tag",
|
all: true,
|
||||||
|
reference: "myimage:anything",
|
||||||
expectedImage: "myimage",
|
expectedImage: "myimage",
|
||||||
expectedTag: "tag",
|
expectedTag: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, pullCase := range pullCases {
|
for _, tc := range testCases {
|
||||||
client := &Client{
|
tc := tc
|
||||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
t.Run(fmt.Sprintf("%s,all-tags=%t", tc.reference, tc.all), func(t *testing.T) {
|
||||||
expectedURL := fmt.Sprintf(expectedURLFormat, pullCase.expectedImage)
|
client := &Client{
|
||||||
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||||
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
expectedURL := fmt.Sprintf(expectedURLFormat, tc.expectedImage)
|
||||||
}
|
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
||||||
query := req.URL.Query()
|
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
tag := query.Get("tag")
|
}
|
||||||
if tag != pullCase.expectedTag {
|
query := req.URL.Query()
|
||||||
return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", pullCase.expectedTag, tag)
|
tag := query.Get("tag")
|
||||||
}
|
if tag != tc.expectedTag {
|
||||||
return &http.Response{
|
return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", tc.expectedTag, tag)
|
||||||
StatusCode: http.StatusOK,
|
}
|
||||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(expectedOutput))),
|
return &http.Response{
|
||||||
}, nil
|
StatusCode: http.StatusOK,
|
||||||
}),
|
Body: ioutil.NopCloser(bytes.NewReader([]byte(expectedOutput))),
|
||||||
}
|
}, nil
|
||||||
resp, err := client.ImagePush(context.Background(), pullCase.reference, types.ImagePushOptions{})
|
}),
|
||||||
if err != nil {
|
}
|
||||||
t.Fatal(err)
|
resp, err := client.ImagePush(context.Background(), tc.reference, types.ImagePushOptions{
|
||||||
}
|
All: tc.all,
|
||||||
body, err := ioutil.ReadAll(resp)
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(body) != expectedOutput {
|
body, err := ioutil.ReadAll(resp)
|
||||||
t.Fatalf("expected '%s', got %s", expectedOutput, string(body))
|
if err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(body) != expectedOutput {
|
||||||
|
t.Fatalf("expected '%s', got %s", expectedOutput, string(body))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue