mirror of
https://github.com/moby/moby.git
synced 2026-01-11 02:31:44 +00:00
client/image_tag: Wrap options and result
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
committed by
Austin Vazquez
parent
347693a580
commit
6819a9fc1e
@@ -115,7 +115,7 @@ type ImageAPIClient interface {
|
||||
ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
|
||||
ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) (ImageRemoveResult, error)
|
||||
ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
|
||||
ImageTag(ctx context.Context, image, ref string) error
|
||||
ImageTag(ctx context.Context, options ImageTagOptions) (ImageTagResult, error)
|
||||
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
|
||||
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (ImageInspectResult, error)
|
||||
|
||||
@@ -9,19 +9,29 @@ import (
|
||||
"github.com/distribution/reference"
|
||||
)
|
||||
|
||||
type ImageTagOptions struct {
|
||||
Source string
|
||||
Target string
|
||||
}
|
||||
|
||||
type ImageTagResult struct{}
|
||||
|
||||
// ImageTag tags an image in the docker host
|
||||
func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
|
||||
func (cli *Client) ImageTag(ctx context.Context, options ImageTagOptions) (ImageTagResult, error) {
|
||||
source := options.Source
|
||||
target := options.Target
|
||||
|
||||
if _, err := reference.ParseAnyReference(source); err != nil {
|
||||
return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
|
||||
return ImageTagResult{}, fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
|
||||
return ImageTagResult{}, fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
|
||||
}
|
||||
|
||||
if _, ok := ref.(reference.Digested); ok {
|
||||
return errors.New("refusing to create a tag with a digest reference")
|
||||
return ImageTagResult{}, errors.New("refusing to create a tag with a digest reference")
|
||||
}
|
||||
|
||||
ref = reference.TagNameOnly(ref)
|
||||
@@ -34,5 +44,5 @@ func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
|
||||
|
||||
resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return err
|
||||
return ImageTagResult{}, err
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestImageTagError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ImageTag(context.Background(), "image_id", "repo:tag")
|
||||
_, err = client.ImageTag(context.Background(), ImageTagOptions{Source: "image_id", Target: "repo:tag"})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestImageTagInvalidReference(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ImageTag(context.Background(), "image_id", "aa/asdf$$^/aa")
|
||||
_, err = client.ImageTag(context.Background(), ImageTagOptions{Source: "image_id", Target: "aa/asdf$$^/aa"})
|
||||
assert.Check(t, is.Error(err, `error parsing reference: "aa/asdf$$^/aa" is not a valid repository/tag: invalid reference format`))
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestImageTagInvalidSourceImageName(t *testing.T) {
|
||||
for _, repo := range invalidRepos {
|
||||
t.Run("invalidRepo/"+repo, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox", repo)
|
||||
_, err := client.ImageTag(ctx, ImageTagOptions{Source: "busybox", Target: repo})
|
||||
assert.Check(t, is.ErrorContains(err, "not a valid repository/tag"))
|
||||
})
|
||||
}
|
||||
@@ -53,26 +53,26 @@ func TestImageTagInvalidSourceImageName(t *testing.T) {
|
||||
for _, repotag := range invalidTags {
|
||||
t.Run("invalidTag/"+repotag, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox", repotag)
|
||||
_, err := client.ImageTag(ctx, ImageTagOptions{Source: "busybox", Target: repotag})
|
||||
assert.Check(t, is.ErrorContains(err, "not a valid repository/tag"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("test repository name begin with '-'", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox:latest", "-busybox:test")
|
||||
_, err := client.ImageTag(ctx, ImageTagOptions{Source: "busybox:latest", Target: "-busybox:test"})
|
||||
assert.Check(t, is.ErrorContains(err, "error parsing reference"))
|
||||
})
|
||||
|
||||
t.Run("test namespace name begin with '-'", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox:latest", "-test/busybox:test")
|
||||
_, err := client.ImageTag(ctx, ImageTagOptions{Source: "busybox:latest", Target: "-test/busybox:test"})
|
||||
assert.Check(t, is.ErrorContains(err, "error parsing reference"))
|
||||
})
|
||||
|
||||
t.Run("test index name begin with '-'", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox:latest", "-index:5000/busybox:test")
|
||||
_, err := client.ImageTag(ctx, ImageTagOptions{Source: "busybox:latest", Target: "-index:5000/busybox:test"})
|
||||
assert.Check(t, is.ErrorContains(err, "error parsing reference"))
|
||||
})
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func TestImageTagHexSource(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusOK, "OK")))
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ImageTag(context.Background(), "0d409d33b27e47423b049f7f863faa08655a8c901749c2b25b93ca67d01a470d", "repo:tag")
|
||||
_, err = client.ImageTag(context.Background(), ImageTagOptions{Source: "0d409d33b27e47423b049f7f863faa08655a8c901749c2b25b93ca67d01a470d", Target: "repo:tag"})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestImageTag(t *testing.T) {
|
||||
}, nil
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
err = client.ImageTag(context.Background(), "image_id", tagCase.reference)
|
||||
_, err = client.ImageTag(context.Background(), ImageTagOptions{Source: "image_id", Target: tagCase.reference})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *testing.T) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
||||
// tag the image to upload it to the private registry
|
||||
ctx := testutil.GetContext(c)
|
||||
err := apiClient.ImageTag(ctx, "busybox", repoName)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox", Target: repoName})
|
||||
assert.Check(c, err)
|
||||
// push the image to the registry
|
||||
rc, err := apiClient.ImagePush(ctx, repoName, client.ImagePushOptions{RegistryAuth: "{}"})
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestImageInspectUniqueRepoDigests(t *testing.T) {
|
||||
|
||||
for _, tag := range []string{"master", "newest"} {
|
||||
imgName := "busybox:" + tag
|
||||
err := apiClient.ImageTag(ctx, "busybox", imgName)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox", Target: imgName})
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
_, _ = apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{Force: true})
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestImagesFilterMultiReference(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, repoTag := range repoTags {
|
||||
err := apiClient.ImageTag(ctx, "busybox:latest", repoTag)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: repoTag})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestAPIImagesFilters(t *testing.T) {
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
for _, n := range []string{"utest:tag1", "utest/docker:tag2", "utest:5000/docker:tag3"} {
|
||||
err := apiClient.ImageTag(ctx, "busybox:latest", n)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: n})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,10 @@ func TestPruneLexographicalOrder(t *testing.T) {
|
||||
|
||||
tags := []string{"h", "a", "j", "o", "s", "q", "w", "e", "r", "t"}
|
||||
for _, tag := range tags {
|
||||
err = apiClient.ImageTag(ctx, id, "busybox:"+tag)
|
||||
_, err = apiClient.ImageTag(ctx, client.ImageTagOptions{Source: id, Target: "busybox:" + tag})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
err = apiClient.ImageTag(ctx, id, "busybox:z")
|
||||
_, err = apiClient.ImageTag(ctx, client.ImageTagOptions{Source: id, Target: "busybox:z"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = apiClient.ImageRemove(ctx, "busybox:latest", client.ImageRemoveOptions{Force: true})
|
||||
@@ -127,7 +127,8 @@ func TestPruneDontDeleteUsedImage(t *testing.T) {
|
||||
// busybox:other tag pointing to the same image.
|
||||
name: "two tags",
|
||||
prepare: func(t *testing.T, d *daemon.Daemon, apiClient *client.Client) error {
|
||||
return apiClient.ImageTag(ctx, "busybox:latest", "busybox:a")
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: "busybox:a"})
|
||||
return err
|
||||
},
|
||||
check: func(t *testing.T, apiClient *client.Client, pruned image.PruneReport) {
|
||||
if assert.Check(t, is.Len(pruned.ImagesDeleted, 1)) {
|
||||
|
||||
@@ -216,7 +216,8 @@ func TestImagePullKeepOldAsDangling(t *testing.T) {
|
||||
|
||||
t.Log(inspect1)
|
||||
|
||||
assert.NilError(t, apiClient.ImageTag(ctx, "busybox:latest", "alpine:latest"))
|
||||
_, err = apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: "alpine:latest"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = apiClient.ImageRemove(ctx, "busybox:latest", client.ImageRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestRemoveByDigest(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
err := apiClient.ImageTag(ctx, "busybox", "test-remove-by-digest:latest")
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox", Target: "test-remove-by-digest:latest"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
inspect, err := apiClient.ImageInspect(ctx, "test-remove-by-digest")
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
@@ -12,23 +13,23 @@ import (
|
||||
func TestTagUnprefixedRepoByNameOrName(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
client := testEnv.APIClient()
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
// By name
|
||||
err := client.ImageTag(ctx, "busybox:latest", "testfoobarbaz")
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: "testfoobarbaz"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// By ID
|
||||
insp, err := client.ImageInspect(ctx, "busybox")
|
||||
insp, err := apiClient.ImageInspect(ctx, "busybox")
|
||||
assert.NilError(t, err)
|
||||
err = client.ImageTag(ctx, insp.ID, "testfoobarbaz")
|
||||
_, err = apiClient.ImageTag(ctx, client.ImageTagOptions{Source: insp.ID, Target: "testfoobarbaz"})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestTagUsingDigestAlgorithmAsName(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
client := testEnv.APIClient()
|
||||
err := client.ImageTag(ctx, "busybox:latest", "sha256:sometag")
|
||||
apiClient := testEnv.APIClient()
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: "sha256:sometag"})
|
||||
assert.Check(t, is.ErrorContains(err, "refusing to create an ambiguous tag using digest algorithm as name"))
|
||||
}
|
||||
|
||||
@@ -36,14 +37,14 @@ func TestTagUsingDigestAlgorithmAsName(t *testing.T) {
|
||||
func TestTagValidPrefixedRepo(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
client := testEnv.APIClient()
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
validRepos := []string{"fooo/bar", "fooaa/test", "foooo:t", "HOSTNAME.DOMAIN.COM:443/foo/bar"}
|
||||
|
||||
for _, repo := range validRepos {
|
||||
t.Run(repo, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := client.ImageTag(ctx, "busybox", repo)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox", Target: repo})
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -52,9 +53,9 @@ func TestTagValidPrefixedRepo(t *testing.T) {
|
||||
// tag an image with an existed tag name without -f option should work
|
||||
func TestTagExistedNameWithoutForce(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
client := testEnv.APIClient()
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
err := client.ImageTag(ctx, "busybox:latest", "busybox:test")
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: "busybox:test"})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ func TestTagExistedNameWithoutForce(t *testing.T) {
|
||||
// ensure all tags result in the same name
|
||||
func TestTagOfficialNames(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
client := testEnv.APIClient()
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
names := []string{
|
||||
"docker.io/busybox",
|
||||
@@ -74,16 +75,16 @@ func TestTagOfficialNames(t *testing.T) {
|
||||
|
||||
for _, name := range names {
|
||||
t.Run("tag from busybox to "+name, func(t *testing.T) {
|
||||
err := client.ImageTag(ctx, "busybox", name+":latest")
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox", Target: name + ":latest"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// ensure we don't have multiple tag names.
|
||||
insp, err := client.ImageInspect(ctx, "busybox")
|
||||
insp, err := apiClient.ImageInspect(ctx, "busybox")
|
||||
assert.NilError(t, err)
|
||||
// TODO(vvoland): Not sure what's actually being tested here. Is is still doing anything useful?
|
||||
assert.Assert(t, !is.Contains(insp.RepoTags, name)().Success())
|
||||
|
||||
err = client.ImageTag(ctx, name+":latest", "test-tag-official-names/foobar:latest")
|
||||
_, err = apiClient.ImageTag(ctx, client.ImageTagOptions{Source: name + ":latest", Target: "test-tag-official-names/foobar:latest"})
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -92,14 +93,14 @@ func TestTagOfficialNames(t *testing.T) {
|
||||
// ensure tags can not match digests
|
||||
func TestTagMatchesDigest(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
client := testEnv.APIClient()
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
digest := "busybox@sha256:abcdef76720241213f5303bda7704ec4c2ef75613173910a56fb1b6e20251507"
|
||||
// test setting tag fails
|
||||
err := client.ImageTag(ctx, "busybox:latest", digest)
|
||||
_, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: "busybox:latest", Target: digest})
|
||||
assert.Check(t, is.ErrorContains(err, "refusing to create a tag with a digest reference"))
|
||||
|
||||
// check that no new image matches the digest
|
||||
_, err = client.ImageInspect(ctx, digest)
|
||||
_, err = apiClient.ImageInspect(ctx, digest)
|
||||
assert.Check(t, is.ErrorContains(err, fmt.Sprintf("No such image: %s", digest)))
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func FrozenImagesLinux(ctx context.Context, apiClient client.APIClient, images .
|
||||
|
||||
for _, img := range loadImages {
|
||||
if img.srcName != img.destName {
|
||||
if err := apiClient.ImageTag(ctx, img.srcName, img.destName); err != nil {
|
||||
if _, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: img.srcName, Target: img.destName}); err != nil {
|
||||
return errors.Wrapf(err, "failed to tag %s as %s", img.srcName, img.destName)
|
||||
}
|
||||
if _, err := apiClient.ImageRemove(ctx, img.srcName, client.ImageRemoveOptions{}); err != nil {
|
||||
@@ -171,7 +171,7 @@ func pullTagAndRemove(ctx context.Context, apiClient client.APIClient, ref strin
|
||||
return err
|
||||
}
|
||||
|
||||
if err := apiClient.ImageTag(ctx, ref, tag); err != nil {
|
||||
if _, err := apiClient.ImageTag(ctx, client.ImageTagOptions{Source: ref, Target: tag}); err != nil {
|
||||
return errors.Wrapf(err, "failed to tag %s as %s", ref, tag)
|
||||
}
|
||||
_, err = apiClient.ImageRemove(ctx, ref, client.ImageRemoveOptions{})
|
||||
|
||||
2
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
2
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
@@ -115,7 +115,7 @@ type ImageAPIClient interface {
|
||||
ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
|
||||
ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) (ImageRemoveResult, error)
|
||||
ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
|
||||
ImageTag(ctx context.Context, image, ref string) error
|
||||
ImageTag(ctx context.Context, options ImageTagOptions) (ImageTagResult, error)
|
||||
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
|
||||
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (ImageInspectResult, error)
|
||||
|
||||
20
vendor/github.com/moby/moby/client/image_tag.go
generated
vendored
20
vendor/github.com/moby/moby/client/image_tag.go
generated
vendored
@@ -9,19 +9,29 @@ import (
|
||||
"github.com/distribution/reference"
|
||||
)
|
||||
|
||||
type ImageTagOptions struct {
|
||||
Source string
|
||||
Target string
|
||||
}
|
||||
|
||||
type ImageTagResult struct{}
|
||||
|
||||
// ImageTag tags an image in the docker host
|
||||
func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
|
||||
func (cli *Client) ImageTag(ctx context.Context, options ImageTagOptions) (ImageTagResult, error) {
|
||||
source := options.Source
|
||||
target := options.Target
|
||||
|
||||
if _, err := reference.ParseAnyReference(source); err != nil {
|
||||
return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
|
||||
return ImageTagResult{}, fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
|
||||
return ImageTagResult{}, fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
|
||||
}
|
||||
|
||||
if _, ok := ref.(reference.Digested); ok {
|
||||
return errors.New("refusing to create a tag with a digest reference")
|
||||
return ImageTagResult{}, errors.New("refusing to create a tag with a digest reference")
|
||||
}
|
||||
|
||||
ref = reference.TagNameOnly(ref)
|
||||
@@ -34,5 +44,5 @@ func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
|
||||
|
||||
resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return err
|
||||
return ImageTagResult{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user