From 2655954c2dd9d92c2394b04e4262543174c4c038 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 4 Dec 2015 13:55:15 -0800 Subject: [PATCH 1/5] Add own reference package wrapper Signed-off-by: Tonis Tiigi --- api/client/build.go | 9 ++-- api/client/commit.go | 6 +-- api/client/create.go | 23 +++++----- api/client/images.go | 6 +-- api/client/import.go | 2 +- api/client/pull.go | 9 ++-- api/client/push.go | 6 +-- api/client/tag.go | 7 ++- api/client/trust.go | 2 +- api/server/router/local/image.go | 13 +++--- daemon/commit.go | 2 +- daemon/daemon.go | 37 ++++++++------- daemon/daemon_unix.go | 4 +- daemon/daemon_windows.go | 7 ++- daemon/daemonbuilder/builder.go | 6 +-- daemon/errors.go | 7 ++- daemon/events/filter.go | 2 +- daemon/image_delete.go | 19 ++++---- daemon/images.go | 12 ++--- daemon/import.go | 4 +- distribution/pull.go | 7 ++- distribution/pull_v1.go | 8 ++-- distribution/pull_v2.go | 20 ++++---- distribution/pull_v2_test.go | 4 +- distribution/push.go | 9 ++-- distribution/push_v1.go | 13 +++--- distribution/push_v2.go | 19 ++++---- distribution/registry_unit_test.go | 2 +- image/tarexport/load.go | 6 +-- image/tarexport/save.go | 13 +++--- image/tarexport/tarexport.go | 8 ++-- migrate/v1/migratev1.go | 15 +++--- migrate/v1/migratev1_test.go | 8 ++-- reference/reference.go | 60 ++++++++++++++++++++++++ {tag => reference}/store.go | 73 +++++++++++++++--------------- {tag => reference}/store_test.go | 41 ++++++++--------- registry/config.go | 9 ++-- registry/registry_mock_test.go | 2 +- registry/registry_test.go | 2 +- registry/service.go | 2 +- registry/service_v1.go | 2 +- registry/service_v2.go | 2 +- registry/session.go | 2 +- registry/types.go | 2 +- 44 files changed, 278 insertions(+), 234 deletions(-) create mode 100644 reference/reference.go rename {tag => reference}/store.go (76%) rename {tag => reference}/store_test.go (89%) diff --git a/api/client/build.go b/api/client/build.go index d0930e11f1..ec47c6e49c 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -13,7 +13,6 @@ import ( "runtime" "strings" - "github.com/docker/distribution/reference" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/builder/dockerignore" @@ -29,8 +28,8 @@ import ( "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/urlutil" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - tagpkg "github.com/docker/docker/tag" "github.com/docker/docker/utils" "github.com/docker/go-units" ) @@ -532,11 +531,11 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(reference.Name digested := false switch ref.(type) { - case reference.Tagged: - case reference.Digested: + case reference.NamedTagged: + case reference.Canonical: digested = true default: - ref, err = reference.WithTag(ref, tagpkg.DefaultTag) + ref, err = reference.WithTag(ref, reference.DefaultTag) if err != nil { return nil, nil, err } diff --git a/api/client/commit.go b/api/client/commit.go index b75520b243..9209cec22c 100644 --- a/api/client/commit.go +++ b/api/client/commit.go @@ -5,11 +5,11 @@ import ( "errors" "fmt" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" ) @@ -51,9 +51,9 @@ func (cli *DockerCli) CmdCommit(args ...string) error { repositoryName = ref.Name() switch x := ref.(type) { - case reference.Digested: + case reference.Canonical: return errors.New("cannot commit to digest reference") - case reference.Tagged: + case reference.NamedTagged: tag = x.Tag() } } diff --git a/api/client/create.go b/api/client/create.go index 857ef90aac..60d1838e28 100644 --- a/api/client/create.go +++ b/api/client/create.go @@ -5,14 +5,13 @@ import ( "io" "os" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/client/lib" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/jsonmessage" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" - tagpkg "github.com/docker/docker/tag" ) func (cli *DockerCli) pullImage(image string) error { @@ -27,13 +26,13 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { var tag string switch x := ref.(type) { - case reference.Digested: + case reference.Canonical: tag = x.Digest().String() - case reference.Tagged: + case reference.NamedTagged: tag = x.Tag() default: // pull only the image tagged 'latest' if no tag was specified - tag = tagpkg.DefaultTag + tag = reference.DefaultTag } // Resolve the Repository name from fqn to RepositoryInfo @@ -99,13 +98,13 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc return nil, err } - isDigested := false + isCanonical := false switch ref.(type) { - case reference.Tagged: - case reference.Digested: - isDigested = true + case reference.NamedTagged: + case reference.Canonical: + isCanonical = true default: - ref, err = reference.WithTag(ref, tagpkg.DefaultTag) + ref, err = reference.WithTag(ref, reference.DefaultTag) if err != nil { return nil, err } @@ -113,7 +112,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc var trustedRef reference.Canonical - if isTrusted() && !isDigested { + if isTrusted() && !isCanonical { var err error trustedRef, err = cli.trustedReference(ref.(reference.NamedTagged)) if err != nil { @@ -133,7 +132,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } - if trustedRef != nil && !isDigested { + if trustedRef != nil && !isCanonical { if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil { return nil, err } diff --git a/api/client/images.go b/api/client/images.go index 346c761bbb..d2d04f8131 100644 --- a/api/client/images.go +++ b/api/client/images.go @@ -6,13 +6,13 @@ import ( "text/tabwriter" "time" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/reference" "github.com/docker/go-units" ) @@ -98,9 +98,9 @@ func (cli *DockerCli) CmdImages(args ...string) error { repo = ref.Name() switch x := ref.(type) { - case reference.Digested: + case reference.Canonical: digest = x.Digest().String() - case reference.Tagged: + case reference.NamedTagged: tag = x.Tag() } } diff --git a/api/client/import.go b/api/client/import.go index 2e9be55f57..1b829c4374 100644 --- a/api/client/import.go +++ b/api/client/import.go @@ -5,13 +5,13 @@ import ( "io" "os" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/jsonmessage" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/urlutil" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" ) diff --git a/api/client/pull.go b/api/client/pull.go index 0144d99e7f..43cbc9e253 100644 --- a/api/client/pull.go +++ b/api/client/pull.go @@ -4,14 +4,13 @@ import ( "errors" "fmt" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/client/lib" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/jsonmessage" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - tagpkg "github.com/docker/docker/tag" ) var errTagCantBeUsed = errors.New("tag can't be used with --all-tags/-a") @@ -35,19 +34,19 @@ func (cli *DockerCli) CmdPull(args ...string) error { var tag string switch x := distributionRef.(type) { - case reference.Digested: + case reference.Canonical: if *allTags { return errTagCantBeUsed } tag = x.Digest().String() - case reference.Tagged: + case reference.NamedTagged: if *allTags { return errTagCantBeUsed } tag = x.Tag() default: if !*allTags { - tag = tagpkg.DefaultTag + tag = reference.DefaultTag distributionRef, err = reference.WithTag(distributionRef, tag) if err != nil { return err diff --git a/api/client/push.go b/api/client/push.go index 5bfd82d390..cc78c3c5b3 100644 --- a/api/client/push.go +++ b/api/client/push.go @@ -4,12 +4,12 @@ import ( "errors" "io" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/client/lib" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/jsonmessage" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" ) @@ -30,9 +30,9 @@ func (cli *DockerCli) CmdPush(args ...string) error { var tag string switch x := ref.(type) { - case reference.Digested: + case reference.Canonical: return errors.New("cannot push a digest reference") - case reference.Tagged: + case reference.NamedTagged: tag = x.Tag() } diff --git a/api/client/tag.go b/api/client/tag.go index 385a155c58..203d64421f 100644 --- a/api/client/tag.go +++ b/api/client/tag.go @@ -3,10 +3,10 @@ package client import ( "errors" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" ) @@ -25,13 +25,12 @@ func (cli *DockerCli) CmdTag(args ...string) error { return err } - _, isDigested := ref.(reference.Digested) - if isDigested { + if _, isCanonical := ref.(reference.Canonical); isCanonical { return errors.New("refusing to create a tag with a digest reference") } tag := "" - tagged, isTagged := ref.(reference.Tagged) + tagged, isTagged := ref.(reference.NamedTagged) if isTagged { tag = tagged.Tag() } diff --git a/api/client/trust.go b/api/client/trust.go index 14d52c48ae..9a18628b28 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -19,7 +19,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/client/lib" @@ -30,6 +29,7 @@ import ( "github.com/docker/docker/pkg/ioutils" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/tlsconfig" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/notary/client" "github.com/docker/notary/passphrase" diff --git a/api/server/router/local/image.go b/api/server/router/local/image.go index ce3ec3cd5d..15273ac276 100644 --- a/api/server/router/local/image.go +++ b/api/server/router/local/image.go @@ -12,7 +12,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/builder" @@ -25,8 +24,8 @@ import ( "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/ulimit" + "github.com/docker/docker/reference" "github.com/docker/docker/runconfig" - tagpkg "github.com/docker/docker/tag" "github.com/docker/docker/utils" "golang.org/x/net/context" ) @@ -156,7 +155,7 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r } switch newRef.(type) { - case reference.Digested: + case reference.Canonical: return errors.New("cannot import digest reference") } @@ -498,12 +497,12 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) { return nil, err } - if _, isDigested := ref.(reference.Digested); isDigested { - return nil, errors.New("build tag cannot be a digest") + if _, isCanonical := ref.(reference.Canonical); isCanonical { + return nil, errors.New("build tag cannot contain a digest") } - if _, isTagged := ref.(reference.Tagged); !isTagged { - ref, err = reference.WithTag(ref, tagpkg.DefaultTag) + if _, isTagged := ref.(reference.NamedTagged); !isTagged { + ref, err = reference.WithTag(ref, reference.DefaultTag) } nameWithTag := ref.String() diff --git a/daemon/commit.go b/daemon/commit.go index 1d3d738959..3e5744c0c2 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/container" "github.com/docker/docker/dockerversion" @@ -15,6 +14,7 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/reference" "github.com/docker/docker/runconfig" ) diff --git a/daemon/daemon.go b/daemon/daemon.go index 2e38e6838c..73dc8d5b1b 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -19,7 +19,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" @@ -58,9 +57,9 @@ import ( "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" - "github.com/docker/docker/tag" "github.com/docker/docker/utils" volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" @@ -138,7 +137,7 @@ type Daemon struct { repository string containers *contStore execCommands *exec.Store - tagStore tag.Store + referenceStore reference.Store downloadManager *xfer.LayerDownloadManager uploadManager *xfer.LayerUploadManager distributionMetadataStore dmetadata.Store @@ -789,16 +788,16 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo eventsService := events.New() - tagStore, err := tag.NewTagStore(filepath.Join(imageRoot, "repositories.json")) + referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json")) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err) } - if err := restoreCustomImage(d.driver, d.imageStore, d.layerStore, tagStore); err != nil { + if err := restoreCustomImage(d.driver, d.imageStore, d.layerStore, referenceStore); err != nil { return nil, fmt.Errorf("Couldn't restore custom images: %s", err) } - if err := v1.Migrate(config.Root, d.driver.String(), d.layerStore, d.imageStore, tagStore, distributionMetadataStore); err != nil { + if err := v1.Migrate(config.Root, d.driver.String(), d.layerStore, d.imageStore, referenceStore, distributionMetadataStore); err != nil { return nil, err } @@ -848,7 +847,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d.repository = daemonRepo d.containers = &contStore{s: make(map[string]*container.Container)} d.execCommands = exec.NewStore() - d.tagStore = tagStore + d.referenceStore = referenceStore d.distributionMetadataStore = distributionMetadataStore d.trustKey = trustKey d.idIndex = truncindex.NewTruncIndex([]string{}) @@ -1045,7 +1044,7 @@ func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error { return err } newTag = registry.NormalizeLocalReference(newTag) - if err := daemon.tagStore.AddTag(newTag, imageID, true); err != nil { + if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil { return err } daemon.EventsService.Log("tag", newTag.String(), "") @@ -1091,7 +1090,7 @@ func (daemon *Daemon) PullImage(ref reference.Named, metaHeaders map[string][]st EventsService: daemon.EventsService, MetadataStore: daemon.distributionMetadataStore, ImageStore: daemon.imageStore, - TagStore: daemon.tagStore, + ReferenceStore: daemon.referenceStore, DownloadManager: daemon.downloadManager, } @@ -1107,7 +1106,7 @@ func (daemon *Daemon) PullImage(ref reference.Named, metaHeaders map[string][]st // the same tag are exported. names is the set of tags to export, and // outStream is the writer which the images are written to. func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { - imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore) + imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore) return imageExporter.Save(names, outStream) } @@ -1135,7 +1134,7 @@ func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]st MetadataStore: daemon.distributionMetadataStore, LayerStore: daemon.layerStore, ImageStore: daemon.imageStore, - TagStore: daemon.tagStore, + ReferenceStore: daemon.referenceStore, TrustKey: daemon.trustKey, UploadManager: daemon.uploadManager, } @@ -1154,14 +1153,14 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { return nil, fmt.Errorf("No such image: %s", name) } - refs := daemon.tagStore.References(img.ID()) + refs := daemon.referenceStore.References(img.ID()) repoTags := []string{} repoDigests := []string{} for _, ref := range refs { switch ref.(type) { - case reference.Tagged: + case reference.NamedTagged: repoTags = append(repoTags, ref.String()) - case reference.Digested: + case reference.Canonical: repoDigests = append(repoDigests, ref.String()) } } @@ -1215,7 +1214,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { // complement of ImageExport. The input stream is an uncompressed tar // ball containing images and metadata. func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer) error { - imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore) + imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore) return imageExporter.Load(inTar, outStream) } @@ -1271,7 +1270,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) { h.ID = id.String() var tags []string - for _, r := range daemon.tagStore.References(id) { + for _, r := range daemon.referenceStore.References(id) { if _, ok := r.(reference.NamedTagged); ok { tags = append(tags, r.String()) } @@ -1303,12 +1302,12 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) { // Treat it as a possible tag or digest reference if ref, err := reference.ParseNamed(refOrID); err == nil { ref = registry.NormalizeLocalReference(ref) - if id, err := daemon.tagStore.Get(ref); err == nil { + if id, err := daemon.referenceStore.Get(ref); err == nil { return id, nil } - if tagged, ok := ref.(reference.Tagged); ok { + if tagged, ok := ref.(reference.NamedTagged); ok { if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil { - for _, namedRef := range daemon.tagStore.References(id) { + for _, namedRef := range daemon.referenceStore.References(id) { if namedRef.Name() == ref.Name() { return id, nil } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 1fc0a1b493..753bffe1e2 100755 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -22,8 +22,8 @@ import ( "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/sysinfo" + "github.com/docker/docker/reference" "github.com/docker/docker/runconfig" - "github.com/docker/docker/tag" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/drivers/bridge" @@ -694,7 +694,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container daemon.Unmount(container) } -func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, ts tag.Store) error { +func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, rs reference.Store) error { // Unix has no custom images to register return nil } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index bd35a2ee95..4ef30eb927 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -9,13 +9,12 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/docker/container" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/tag" + "github.com/docker/docker/reference" // register the windows graph driver "github.com/docker/docker/daemon/graphdriver/windows" "github.com/docker/docker/pkg/system" @@ -153,7 +152,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container } } -func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, ts tag.Store) error { +func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, rs reference.Store) error { if wd, ok := driver.(*windows.Driver); ok { imageInfos, err := wd.GetCustomImageInfos() if err != nil { @@ -206,7 +205,7 @@ func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Stor return err } - if err := ts.AddTag(ref, id, true); err != nil { + if err := rs.AddTag(ref, id, true); err != nil { return err } diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go index 6da9896301..09bab93a4a 100644 --- a/daemon/daemonbuilder/builder.go +++ b/daemon/daemonbuilder/builder.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/builder" @@ -20,6 +19,7 @@ import ( "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/urlutil" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" ) @@ -42,8 +42,8 @@ func (d Docker) Pull(name string) (*image.Image, error) { return nil, err } switch ref.(type) { - case reference.Tagged: - case reference.Digested: + case reference.NamedTagged: + case reference.Canonical: default: ref, err = reference.WithTag(ref, "latest") if err != nil { diff --git a/daemon/errors.go b/daemon/errors.go index 8ab411c0f0..8fff66b9ff 100644 --- a/daemon/errors.go +++ b/daemon/errors.go @@ -3,9 +3,8 @@ package daemon import ( "strings" - "github.com/docker/distribution/reference" derr "github.com/docker/docker/errors" - tagpkg "github.com/docker/docker/tag" + "github.com/docker/docker/reference" ) func (d *Daemon) imageNotExistToErrcode(err error) error { @@ -13,12 +12,12 @@ func (d *Daemon) imageNotExistToErrcode(err error) error { if strings.Contains(dne.RefOrID, "@") { return derr.ErrorCodeNoSuchImageHash.WithArgs(dne.RefOrID) } - tag := tagpkg.DefaultTag + tag := reference.DefaultTag ref, err := reference.ParseNamed(dne.RefOrID) if err != nil { return derr.ErrorCodeNoSuchImageTag.WithArgs(dne.RefOrID, tag) } - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { tag = tagged.Tag() } return derr.ErrorCodeNoSuchImageTag.WithArgs(ref.Name(), tag) diff --git a/daemon/events/filter.go b/daemon/events/filter.go index fa751d54b5..084346cfff 100644 --- a/daemon/events/filter.go +++ b/daemon/events/filter.go @@ -1,9 +1,9 @@ package events import ( - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/jsonmessage" + "github.com/docker/docker/reference" ) // Filter can filter out docker events from a stream diff --git a/daemon/image_delete.go b/daemon/image_delete.go index e2503c01d6..6d8b7b1bac 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -4,13 +4,12 @@ import ( "fmt" "strings" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" - tagpkg "github.com/docker/docker/tag" + "github.com/docker/docker/reference" ) // ImageDelete deletes the image referenced by the given imageRef from this @@ -58,7 +57,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I return nil, daemon.imageNotExistToErrcode(err) } - repoRefs := daemon.tagStore.References(imgID) + repoRefs := daemon.referenceStore.References(imgID) var removedRepositoryRef bool if !isImageIDPrefix(imgID.String(), imageRef) { @@ -151,11 +150,11 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai // tag is used. Returns the resolved image reference and an error. func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) { switch ref.(type) { - case reference.Tagged: - case reference.Digested: + case reference.NamedTagged: + case reference.Canonical: default: var err error - ref, err = reference.WithTag(ref, tagpkg.DefaultTag) + ref, err = reference.WithTag(ref, reference.DefaultTag) if err != nil { return nil, err } @@ -164,7 +163,7 @@ func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, erro // Ignore the boolean value returned, as far as we're concerned, this // is an idempotent operation and it's okay if the reference didn't // exist in the first place. - _, err := daemon.tagStore.Delete(ref) + _, err := daemon.referenceStore.Delete(ref) return ref, err } @@ -175,7 +174,7 @@ func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, erro // daemon's event service. An "Untagged" types.ImageDelete is added to the // given list of records. func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDelete) error { - imageRefs := daemon.tagStore.References(imgID) + imageRefs := daemon.referenceStore.References(imgID) for _, imageRef := range imageRefs { parsedRef, err := daemon.removeImageRef(imageRef) @@ -325,7 +324,7 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteConflict { // Check if any repository tags/digest reference this image. - if len(daemon.tagStore.References(imgID)) > 0 { + if len(daemon.referenceStore.References(imgID)) > 0 { return &imageDeleteConflict{ imgID: imgID, message: "image is referenced in one or more repositories", @@ -355,5 +354,5 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteC // that there are no repository references to the given image and it has no // child images. func (daemon *Daemon) imageIsDangling(imgID image.ID) bool { - return !(len(daemon.tagStore.References(imgID)) > 0 || len(daemon.imageStore.Children(imgID)) > 0) + return !(len(daemon.referenceStore.References(imgID)) > 0 || len(daemon.imageStore.Children(imgID)) > 0) } diff --git a/daemon/images.go b/daemon/images.go index 9220400271..a5ed6b18f3 100644 --- a/daemon/images.go +++ b/daemon/images.go @@ -5,11 +5,11 @@ import ( "path" "sort" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/image" "github.com/docker/docker/layer" + "github.com/docker/docker/reference" ) var acceptedImageFilterTags = map[string]bool{ @@ -68,9 +68,9 @@ func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Imag var filterTagged bool if filter != "" { - filterRef, err := reference.Parse(filter) + filterRef, err := reference.ParseNamed(filter) if err == nil { // parse error means wildcard repo - if _, ok := filterRef.(reference.Tagged); ok { + if _, ok := filterRef.(reference.NamedTagged); ok { filterTagged = true } } @@ -105,7 +105,7 @@ func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Imag newImage := newImage(img, size) - for _, ref := range daemon.tagStore.References(id) { + for _, ref := range daemon.referenceStore.References(id) { if filter != "" { // filter by tag/repo name if filterTagged { // filter by tag, require full ref match if ref.String() != filter { @@ -115,10 +115,10 @@ func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Imag continue } } - if _, ok := ref.(reference.Digested); ok { + if _, ok := ref.(reference.Canonical); ok { newImage.RepoDigests = append(newImage.RepoDigests, ref.String()) } - if _, ok := ref.(reference.Tagged); ok { + if _, ok := ref.(reference.NamedTagged); ok { newImage.RepoTags = append(newImage.RepoTags, ref.String()) } } diff --git a/daemon/import.go b/daemon/import.go index 75010e346d..f09ab672aa 100644 --- a/daemon/import.go +++ b/daemon/import.go @@ -8,13 +8,13 @@ import ( "runtime" "time" - "github.com/docker/distribution/reference" "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/httputils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" + "github.com/docker/docker/reference" "github.com/docker/docker/runconfig" ) @@ -90,7 +90,7 @@ func (daemon *Daemon) ImportImage(src string, newRef reference.Named, msg string return err } - // FIXME: connect with commit code and call tagstore directly + // FIXME: connect with commit code and call refstore directly if newRef != nil { if err := daemon.TagImage(newRef, id.String()); err != nil { return err diff --git a/distribution/pull.go b/distribution/pull.go index 9180de5084..e9408d5620 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -6,15 +6,14 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/events" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/docker/docker/tag" "golang.org/x/net/context" ) @@ -39,8 +38,8 @@ type ImagePullConfig struct { MetadataStore metadata.Store // ImageStore manages images. ImageStore image.Store - // TagStore manages tags. - TagStore tag.Store + // ReferenceStore manages tags. + ReferenceStore reference.Store // DownloadManager manages concurrent pulls. DownloadManager *xfer.LayerDownloadManager } diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go index 9bc229a83a..0a04216583 100644 --- a/distribution/pull_v1.go +++ b/distribution/pull_v1.go @@ -11,7 +11,6 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" @@ -21,6 +20,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "golang.org/x/net/context" ) @@ -34,7 +34,7 @@ type v1Puller struct { } func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool, err error) { - if _, isDigested := ref.(reference.Digested); isDigested { + if _, isCanonical := ref.(reference.Canonical); isCanonical { // Allowing fallback, because HTTPS v1 is before HTTP v2 return true, registry.ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")} } @@ -84,7 +84,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro logrus.Debugf("Retrieving the tag list") var tagsList map[string]string - tagged, isTagged := ref.(reference.Tagged) + tagged, isTagged := ref.(reference.NamedTagged) if !isTagged { tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName) } else { @@ -250,7 +250,7 @@ func (p *v1Puller) pullImage(ctx context.Context, v1ID, endpoint string, localNa return err } - if err := p.config.TagStore.AddTag(localNameRef, imageID, true); err != nil { + if err := p.config.ReferenceStore.AddTag(localNameRef, imageID, true); err != nil { return err } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 50244608f1..f9c47087bd 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -13,7 +13,6 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest/schema1" - "github.com/docker/distribution/reference" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" @@ -22,6 +21,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "golang.org/x/net/context" ) @@ -55,13 +55,13 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var refs []reference.Named taggedName := p.repoInfo.LocalName - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag()) if err != nil { return err } refs = []reference.Named{taggedName} - } else if digested, isDigested := ref.(reference.Digested); isDigested { + } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest()) if err != nil { return err @@ -197,9 +197,9 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) { func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) { tagOrDigest := "" - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { tagOrDigest = tagged.Tag() - } else if digested, isDigested := ref.(reference.Digested); isDigested { + } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { tagOrDigest = digested.Digest().String() } else { return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String()) @@ -300,27 +300,27 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String()) } - oldTagImageID, err := p.config.TagStore.Get(ref) + oldTagImageID, err := p.config.ReferenceStore.Get(ref) if err == nil && oldTagImageID == imageID { return false, nil } if canonical, ok := ref.(reference.Canonical); ok { - if err = p.config.TagStore.AddDigest(canonical, imageID, true); err != nil { + if err = p.config.ReferenceStore.AddDigest(canonical, imageID, true); err != nil { return false, err } - } else if err = p.config.TagStore.AddTag(ref, imageID, true); err != nil { + } else if err = p.config.ReferenceStore.AddTag(ref, imageID, true); err != nil { return false, err } return true, nil } -func verifyManifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) { +func verifyManifest(signedManifest *schema1.SignedManifest, ref reference.Named) (m *schema1.Manifest, err error) { // If pull by digest, then verify the manifest digest. NOTE: It is // important to do this first, before any other content validation. If the // digest cannot be verified, don't even bother with those other things. - if digested, isDigested := ref.(reference.Digested); isDigested { + if digested, isCanonical := ref.(reference.Canonical); isCanonical { verifier, err := digest.NewDigestVerifier(digested.Digest()) if err != nil { return nil, err diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index 2647911408..87356aee33 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -9,7 +9,7 @@ import ( "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest/schema1" - "github.com/docker/distribution/reference" + "github.com/docker/docker/reference" ) // TestFixManifestLayers checks that fixManifestLayers removes a duplicate @@ -104,7 +104,7 @@ func TestFixManifestLayersBadParent(t *testing.T) { // TestValidateManifest verifies the validateManifest function func TestValidateManifest(t *testing.T) { - expectedDigest, err := reference.Parse("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd") + expectedDigest, err := reference.ParseNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd") if err != nil { t.Fatal("could not parse reference") } diff --git a/distribution/push.go b/distribution/push.go index c9aef91375..956532a148 100644 --- a/distribution/push.go +++ b/distribution/push.go @@ -8,7 +8,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/events" "github.com/docker/docker/distribution/metadata" @@ -16,8 +15,8 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/docker/docker/tag" "github.com/docker/libtrust" "golang.org/x/net/context" ) @@ -45,8 +44,8 @@ type ImagePushConfig struct { LayerStore layer.Store // ImageStore manages images. ImageStore image.Store - // TagStore manages tags. - TagStore tag.Store + // ReferenceStore manages tags. + ReferenceStore reference.Store // TrustKey is the private key for legacy signatures. This is typically // an ephemeral key, since these signatures are no longer verified. TrustKey libtrust.PrivateKey @@ -112,7 +111,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.CanonicalName.String()) - associations := imagePushConfig.TagStore.ReferencesByName(repoInfo.LocalName) + associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.LocalName) if len(associations) == 0 { return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName) } diff --git a/distribution/push_v1.go b/distribution/push_v1.go index 155bbc8647..012216a68e 100644 --- a/distribution/push_v1.go +++ b/distribution/push_v1.go @@ -6,7 +6,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/image" @@ -15,6 +14,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "golang.org/x/net/context" ) @@ -141,16 +141,15 @@ func (p *v1Pusher) getImageList() (imageList []v1Image, tagsByImage map[image.ID tagsByImage = make(map[image.ID][]string) // Ignore digest references - _, isDigested := p.ref.(reference.Digested) - if isDigested { + if _, isCanonical := p.ref.(reference.Canonical); isCanonical { return } - tagged, isTagged := p.ref.(reference.Tagged) + tagged, isTagged := p.ref.(reference.NamedTagged) if isTagged { // Push a specific tag var imgID image.ID - imgID, err = p.config.TagStore.Get(p.ref) + imgID, err = p.config.ReferenceStore.Get(p.ref) if err != nil { return } @@ -168,9 +167,9 @@ func (p *v1Pusher) getImageList() (imageList []v1Image, tagsByImage map[image.ID imagesSeen := make(map[image.ID]struct{}) dependenciesSeen := make(map[layer.ChainID]*v1DependencyImage) - associations := p.config.TagStore.ReferencesByName(p.ref) + associations := p.config.ReferenceStore.ReferencesByName(p.ref) for _, association := range associations { - if tagged, isTagged = association.Ref.(reference.Tagged); !isTagged { + if tagged, isTagged = association.Ref.(reference.NamedTagged); !isTagged { // Ignore digest references. continue } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index 4f725eb60a..f687518fd1 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -13,7 +13,6 @@ import ( "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest/schema1" - "github.com/docker/distribution/reference" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" @@ -22,8 +21,8 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/docker/docker/tag" "golang.org/x/net/context" ) @@ -55,14 +54,14 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) { localName := p.repoInfo.LocalName.Name() - var associations []tag.Association - if _, isTagged := p.ref.(reference.Tagged); isTagged { - imageID, err := p.config.TagStore.Get(p.ref) + var associations []reference.Association + if _, isTagged := p.ref.(reference.NamedTagged); isTagged { + imageID, err := p.config.ReferenceStore.Get(p.ref) if err != nil { return false, fmt.Errorf("tag does not exist: %s", p.ref.String()) } - associations = []tag.Association{ + associations = []reference.Association{ { Ref: p.ref, ImageID: imageID, @@ -70,7 +69,7 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) { } } else { // Pull all tags - associations = p.config.TagStore.ReferencesByName(p.ref) + associations = p.config.ReferenceStore.ReferencesByName(p.ref) } if err != nil { return false, fmt.Errorf("error getting tags for %s: %s", localName, err) @@ -88,7 +87,7 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) { return false, nil } -func (p *v2Pusher) pushV2Tag(ctx context.Context, association tag.Association) error { +func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Association) error { ref := association.Ref logrus.Debugf("Pushing repository: %s", ref.String()) @@ -146,7 +145,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association tag.Association) e } var tag string - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { tag = tagged.Tag() } m, err := CreateV2Manifest(p.repo.Name(), tag, img, fsLayers) @@ -165,7 +164,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association tag.Association) e return err } if manifestDigest != "" { - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { // NOTE: do not change this format without first changing the trust client // code. This information is used to determine what was pushed and should be signed. progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", tagged.Tag(), manifestDigest, manifestSize) diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 45e4840112..f6543bf837 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -8,10 +8,10 @@ import ( "testing" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/docker/utils" "golang.org/x/net/context" diff --git a/image/tarexport/load.go b/image/tarexport/load.go index 695ddba3ee..a32b9863c9 100644 --- a/image/tarexport/load.go +++ b/image/tarexport/load.go @@ -9,13 +9,13 @@ import ( "path/filepath" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/docker/image" "github.com/docker/docker/image/v1" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/symlink" + "github.com/docker/docker/reference" ) func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { @@ -124,11 +124,11 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Lay } func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, outStream io.Writer) error { - if prevID, err := l.ts.Get(ref); err == nil && prevID != imgID { + if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID { fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", ref.String(), string(prevID)) // todo: this message is wrong in case of multiple tags } - if err := l.ts.AddTag(ref, imgID, true); err != nil { + if err := l.rs.AddTag(ref, imgID, true); err != nil { return err } return nil diff --git a/image/tarexport/save.go b/image/tarexport/save.go index f16a149c06..8626e95523 100644 --- a/image/tarexport/save.go +++ b/image/tarexport/save.go @@ -10,13 +10,12 @@ import ( "time" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/image" "github.com/docker/docker/image/v1" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/docker/docker/tag" ) type imageDescriptor struct { @@ -50,13 +49,13 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, if ref != nil { var tagged reference.NamedTagged - if _, ok := ref.(reference.Digested); ok { + if _, ok := ref.(reference.Canonical); ok { return } var ok bool if tagged, ok = ref.(reference.NamedTagged); !ok { var err error - if tagged, err = reference.WithTag(ref, tag.DefaultTag); err != nil { + if tagged, err = reference.WithTag(ref, reference.DefaultTag); err != nil { return } } @@ -84,9 +83,9 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, addAssoc(imgID, nil) continue } - if _, ok := ref.(reference.Digested); !ok { + if _, ok := ref.(reference.Canonical); !ok { if _, ok := ref.(reference.NamedTagged); !ok { - assocs := l.ts.ReferencesByName(ref) + assocs := l.rs.ReferencesByName(ref) for _, assoc := range assocs { addAssoc(assoc.ImageID, assoc.Ref) } @@ -101,7 +100,7 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, } } var imgID image.ID - if imgID, err = l.ts.Get(ref); err != nil { + if imgID, err = l.rs.Get(ref); err != nil { return nil, err } addAssoc(imgID, ref) diff --git a/image/tarexport/tarexport.go b/image/tarexport/tarexport.go index 3369d665c9..cc8cdc8533 100644 --- a/image/tarexport/tarexport.go +++ b/image/tarexport/tarexport.go @@ -3,7 +3,7 @@ package tarexport import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/tag" + "github.com/docker/docker/reference" ) const ( @@ -23,14 +23,14 @@ type manifestItem struct { type tarexporter struct { is image.Store ls layer.Store - ts tag.Store + rs reference.Store } // NewTarExporter returns new ImageExporter for tar packages -func NewTarExporter(is image.Store, ls layer.Store, ts tag.Store) image.Exporter { +func NewTarExporter(is image.Store, ls layer.Store, rs reference.Store) image.Exporter { return &tarexporter{ is: is, ls: ls, - ts: ts, + rs: rs, } } diff --git a/migrate/v1/migratev1.go b/migrate/v1/migratev1.go index 14f6ce4ba5..a9110bca9c 100644 --- a/migrate/v1/migratev1.go +++ b/migrate/v1/migratev1.go @@ -11,12 +11,11 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/image" imagev1 "github.com/docker/docker/image/v1" "github.com/docker/docker/layer" - "github.com/docker/docker/tag" + "github.com/docker/docker/reference" ) type graphIDRegistrar interface { @@ -46,7 +45,7 @@ var ( // Migrate takes an old graph directory and transforms the metadata into the // new format. -func Migrate(root, driverName string, ls layer.Store, is image.Store, ts tag.Store, ms metadata.Store) error { +func Migrate(root, driverName string, ls layer.Store, is image.Store, rs reference.Store, ms metadata.Store) error { mappings := make(map[string]image.ID) if registrar, ok := ls.(graphIDRegistrar); !ok { @@ -61,7 +60,7 @@ func Migrate(root, driverName string, ls layer.Store, is image.Store, ts tag.Sto return err } - if err := migrateTags(root, driverName, ts, mappings); err != nil { + if err := migrateRefs(root, driverName, rs, mappings); err != nil { return err } @@ -189,12 +188,12 @@ func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMapp return nil } -type tagAdder interface { +type refAdder interface { AddTag(ref reference.Named, id image.ID, force bool) error AddDigest(ref reference.Canonical, id image.ID, force bool) error } -func migrateTags(root, driverName string, ts tagAdder, mappings map[string]image.ID) error { +func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image.ID) error { migrationFile := filepath.Join(root, migrationTagsFileName) if _, err := os.Lstat(migrationFile); !os.IsNotExist(err) { return err @@ -232,7 +231,7 @@ func migrateTags(root, driverName string, ts tagAdder, mappings map[string]image logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err) continue } - if err := ts.AddDigest(canonical, strongID, false); err != nil { + if err := rs.AddDigest(canonical, strongID, false); err != nil { logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err) } } else { @@ -241,7 +240,7 @@ func migrateTags(root, driverName string, ts tagAdder, mappings map[string]image logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err) continue } - if err := ts.AddTag(tagRef, strongID, false); err != nil { + if err := rs.AddTag(tagRef, strongID, false); err != nil { logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err) } } diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go index ef5c1a951b..067d04c382 100644 --- a/migrate/v1/migratev1_test.go +++ b/migrate/v1/migratev1_test.go @@ -13,13 +13,13 @@ import ( "testing" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/image" "github.com/docker/docker/layer" + "github.com/docker/docker/reference" ) -func TestMigrateTags(t *testing.T) { +func TestMigrateRefs(t *testing.T) { tmpdir, err := ioutil.TempDir("", "migrate-tags") if err != nil { t.Fatal(err) @@ -29,7 +29,7 @@ func TestMigrateTags(t *testing.T) { ioutil.WriteFile(filepath.Join(tmpdir, "repositories-generic"), []byte(`{"Repositories":{"busybox":{"latest":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108","sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108"},"registry":{"2":"5d165b8e4b203685301c815e95663231691d383fd5e3d3185d1ce3f8dddead3d","latest":"8d5547a9f329b1d3f93198cd661fb5117e5a96b721c5cf9a2c389e7dd4877128"}}}`), 0600) ta := &mockTagAdder{} - err = migrateTags(tmpdir, "generic", ta, map[string]image.ID{ + err = migrateRefs(tmpdir, "generic", ta, map[string]image.ID{ "5d165b8e4b203685301c815e95663231691d383fd5e3d3185d1ce3f8dddead3d": image.ID("sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"), "b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"), "abcdef3434c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:56434342345ae68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"), @@ -50,7 +50,7 @@ func TestMigrateTags(t *testing.T) { // second migration is no-op ioutil.WriteFile(filepath.Join(tmpdir, "repositories-generic"), []byte(`{"Repositories":{"busybox":{"latest":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108"`), 0600) - err = migrateTags(tmpdir, "generic", ta, map[string]image.ID{ + err = migrateRefs(tmpdir, "generic", ta, map[string]image.ID{ "b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"), }) if err != nil { diff --git a/reference/reference.go b/reference/reference.go new file mode 100644 index 0000000000..70196a14d2 --- /dev/null +++ b/reference/reference.go @@ -0,0 +1,60 @@ +package reference + +import ( + "github.com/docker/distribution/digest" + distreference "github.com/docker/distribution/reference" +) + +// Reference is an opaque object reference identifier that may include +// modifiers such as a hostname, name, tag, and digest. +type Reference interface { + // String returns the full reference + String() string +} + +// Named is an object with a full name +type Named interface { + Reference + Name() string +} + +// NamedTagged is an object including a name and tag. +type NamedTagged interface { + Named + Tag() string +} + +// Canonical reference is an object with a fully unique +// name including a name with hostname and digest +type Canonical interface { + Named + Digest() digest.Digest +} + +// ParseNamed parses s and returns a syntactically valid reference implementing +// the Named interface. The reference must have a name, otherwise an error is +// returned. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: ParseNamed will not handle short digests. +func ParseNamed(s string) (Named, error) { + // todo: docker specific validation + return distreference.ParseNamed(s) +} + +// WithName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +func WithName(name string) (Named, error) { + return distreference.WithName(name) +} + +// WithTag combines the name from "name" and the tag from "tag" to form a +// reference incorporating both the name and the tag. +func WithTag(name Named, tag string) (NamedTagged, error) { + return distreference.WithTag(name, tag) +} + +// WithDigest combines the name from "name" and the digest from "digest" to form +// a reference incorporating both the name and the digest. +func WithDigest(name Named, digest digest.Digest) (Canonical, error) { + return distreference.WithDigest(name, digest) +} diff --git a/tag/store.go b/reference/store.go similarity index 76% rename from tag/store.go rename to reference/store.go index f09db74b53..660c9e2019 100644 --- a/tag/store.go +++ b/reference/store.go @@ -1,4 +1,4 @@ -package tag +package reference import ( "encoding/json" @@ -11,7 +11,6 @@ import ( "sync" "github.com/docker/distribution/digest" - "github.com/docker/distribution/reference" "github.com/docker/docker/image" ) @@ -26,18 +25,18 @@ var ( // An Association is a tuple associating a reference with an image ID. type Association struct { - Ref reference.Named + Ref Named ImageID image.ID } // Store provides the set of methods which can operate on a tag store. type Store interface { - References(id image.ID) []reference.Named - ReferencesByName(ref reference.Named) []Association - AddTag(ref reference.Named, id image.ID, force bool) error - AddDigest(ref reference.Canonical, id image.ID, force bool) error - Delete(ref reference.Named) (bool, error) - Get(ref reference.Named) (image.ID, error) + References(id image.ID) []Named + ReferencesByName(ref Named) []Association + AddTag(ref Named, id image.ID, force bool) error + AddDigest(ref Canonical, id image.ID, force bool) error + Delete(ref Named) (bool, error) + Get(ref Named) (image.ID, error) } type store struct { @@ -49,14 +48,14 @@ type store struct { Repositories map[string]repository // referencesByIDCache is a cache of references indexed by ID, to speed // up References. - referencesByIDCache map[image.ID]map[string]reference.Named + referencesByIDCache map[image.ID]map[string]Named } // Repository maps tags to image IDs. The key is a a stringified Reference, // including the repository name. type repository map[string]image.ID -type lexicalRefs []reference.Named +type lexicalRefs []Named func (a lexicalRefs) Len() int { return len(a) } func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] } @@ -68,22 +67,22 @@ func (a lexicalAssociations) Len() int { return len(a) } func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() } -func defaultTagIfNameOnly(ref reference.Named) reference.Named { +func defaultTagIfNameOnly(ref Named) Named { switch ref.(type) { - case reference.Tagged: + case NamedTagged: return ref - case reference.Digested: + case Canonical: return ref default: // Should never fail - ref, _ = reference.WithTag(ref, DefaultTag) + ref, _ = WithTag(ref, DefaultTag) return ref } } -// NewTagStore creates a new tag store, tied to a file path where the set of -// tags is serialized in JSON format. -func NewTagStore(jsonPath string) (Store, error) { +// NewReferenceStore creates a new reference store, tied to a file path where +// the set of references are serialized in JSON format. +func NewReferenceStore(jsonPath string) (Store, error) { abspath, err := filepath.Abs(jsonPath) if err != nil { return nil, err @@ -92,7 +91,7 @@ func NewTagStore(jsonPath string) (Store, error) { store := &store{ jsonPath: abspath, Repositories: make(map[string]repository), - referencesByIDCache: make(map[image.ID]map[string]reference.Named), + referencesByIDCache: make(map[image.ID]map[string]Named), } // Load the json file if it exists, otherwise create it. if err := store.reload(); os.IsNotExist(err) { @@ -105,21 +104,21 @@ func NewTagStore(jsonPath string) (Store, error) { return store, nil } -// Add adds a tag to the store. If force is set to true, existing +// AddTag adds a tag reference to the store. If force is set to true, existing // references can be overwritten. This only works for tags, not digests. -func (store *store) AddTag(ref reference.Named, id image.ID, force bool) error { - if _, isDigested := ref.(reference.Digested); isDigested { +func (store *store) AddTag(ref Named, id image.ID, force bool) error { + if _, isCanonical := ref.(Canonical); isCanonical { return errors.New("refusing to create a tag with a digest reference") } return store.addReference(defaultTagIfNameOnly(ref), id, force) } -// Add adds a digest reference to the store. -func (store *store) AddDigest(ref reference.Canonical, id image.ID, force bool) error { +// AddDigest adds a digest reference to the store. +func (store *store) AddDigest(ref Canonical, id image.ID, force bool) error { return store.addReference(ref, id, force) } -func (store *store) addReference(ref reference.Named, id image.ID, force bool) error { +func (store *store) addReference(ref Named, id image.ID, force bool) error { if ref.Name() == string(digest.Canonical) { return errors.New("refusing to create an ambiguous tag using digest algorithm as name") } @@ -138,7 +137,7 @@ func (store *store) addReference(ref reference.Named, id image.ID, force bool) e if exists { // force only works for tags - if digested, isDigest := ref.(reference.Digested); isDigest { + if digested, isDigest := ref.(Canonical); isDigest { return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String()) } @@ -156,7 +155,7 @@ func (store *store) addReference(ref reference.Named, id image.ID, force bool) e repository[refStr] = id if store.referencesByIDCache[id] == nil { - store.referencesByIDCache[id] = make(map[string]reference.Named) + store.referencesByIDCache[id] = make(map[string]Named) } store.referencesByIDCache[id][refStr] = ref @@ -165,7 +164,7 @@ func (store *store) addReference(ref reference.Named, id image.ID, force bool) e // Delete deletes a reference from the store. It returns true if a deletion // happened, or false otherwise. -func (store *store) Delete(ref reference.Named) (bool, error) { +func (store *store) Delete(ref Named) (bool, error) { ref = defaultTagIfNameOnly(ref) store.mu.Lock() @@ -196,8 +195,8 @@ func (store *store) Delete(ref reference.Named) (bool, error) { return false, ErrDoesNotExist } -// Get retrieves an item from the store by reference. -func (store *store) Get(ref reference.Named) (image.ID, error) { +// Get retrieves an item from the store by +func (store *store) Get(ref Named) (image.ID, error) { ref = defaultTagIfNameOnly(ref) store.mu.RLock() @@ -218,15 +217,15 @@ func (store *store) Get(ref reference.Named) (image.ID, error) { // References returns a slice of references to the given image ID. The slice // will be nil if there are no references to this image ID. -func (store *store) References(id image.ID) []reference.Named { +func (store *store) References(id image.ID) []Named { store.mu.RLock() defer store.mu.RUnlock() // Convert the internal map to an array for two reasons: - // 1) We must not return a mutable reference. + // 1) We must not return a mutable // 2) It would be ugly to expose the extraneous map keys to callers. - var references []reference.Named + var references []Named for _, ref := range store.referencesByIDCache[id] { references = append(references, ref) } @@ -239,7 +238,7 @@ func (store *store) References(id image.ID) []reference.Named { // ReferencesByName returns the references for a given repository name. // If there are no references known for this repository name, // ReferencesByName returns nil. -func (store *store) ReferencesByName(ref reference.Named) []Association { +func (store *store) ReferencesByName(ref Named) []Association { store.mu.RLock() defer store.mu.RUnlock() @@ -250,7 +249,7 @@ func (store *store) ReferencesByName(ref reference.Named) []Association { var associations []Association for refStr, refID := range repository { - ref, err := reference.ParseNamed(refStr) + ref, err := ParseNamed(refStr) if err != nil { // Should never happen return nil @@ -299,13 +298,13 @@ func (store *store) reload() error { for _, repository := range store.Repositories { for refStr, refID := range repository { - ref, err := reference.ParseNamed(refStr) + ref, err := ParseNamed(refStr) if err != nil { // Should never happen continue } if store.referencesByIDCache[refID] == nil { - store.referencesByIDCache[refID] = make(map[string]reference.Named) + store.referencesByIDCache[refID] = make(map[string]Named) } store.referencesByIDCache[refID][refStr] = ref } diff --git a/tag/store_test.go b/reference/store_test.go similarity index 89% rename from tag/store_test.go rename to reference/store_test.go index 80a36bf84c..a877c55f58 100644 --- a/tag/store_test.go +++ b/reference/store_test.go @@ -1,4 +1,4 @@ -package tag +package reference import ( "bytes" @@ -8,7 +8,6 @@ import ( "strings" "testing" - "github.com/docker/distribution/reference" "github.com/docker/docker/image" ) @@ -40,13 +39,13 @@ func TestLoad(t *testing.T) { } jsonFile.Close() - store, err := NewTagStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name()) if err != nil { t.Fatalf("error creating tag store: %v", err) } for refStr, expectedID := range saveLoadTestCases { - ref, err := reference.ParseNamed(refStr) + ref, err := ParseNamed(refStr) if err != nil { t.Fatalf("failed to parse reference: %v", err) } @@ -69,17 +68,17 @@ func TestSave(t *testing.T) { jsonFile.Close() defer os.RemoveAll(jsonFile.Name()) - store, err := NewTagStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name()) if err != nil { t.Fatalf("error creating tag store: %v", err) } for refStr, id := range saveLoadTestCases { - ref, err := reference.ParseNamed(refStr) + ref, err := ParseNamed(refStr) if err != nil { t.Fatalf("failed to parse reference: %v", err) } - if canonical, ok := ref.(reference.Canonical); ok { + if canonical, ok := ref.(Canonical); ok { err = store.AddDigest(canonical, id, false) if err != nil { t.Fatalf("could not add digest reference %s: %v", refStr, err) @@ -111,7 +110,7 @@ func TestAddDeleteGet(t *testing.T) { jsonFile.Close() defer os.RemoveAll(jsonFile.Name()) - store, err := NewTagStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name()) if err != nil { t.Fatalf("error creating tag store: %v", err) } @@ -121,7 +120,7 @@ func TestAddDeleteGet(t *testing.T) { testImageID3 := image.ID("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e") // Try adding a reference with no tag or digest - nameOnly, err := reference.WithName("username/repo") + nameOnly, err := WithName("username/repo") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -130,7 +129,7 @@ func TestAddDeleteGet(t *testing.T) { } // Add a few references - ref1, err := reference.ParseNamed("username/repo1:latest") + ref1, err := ParseNamed("username/repo1:latest") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -138,7 +137,7 @@ func TestAddDeleteGet(t *testing.T) { t.Fatalf("error adding to store: %v", err) } - ref2, err := reference.ParseNamed("username/repo1:old") + ref2, err := ParseNamed("username/repo1:old") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -146,7 +145,7 @@ func TestAddDeleteGet(t *testing.T) { t.Fatalf("error adding to store: %v", err) } - ref3, err := reference.ParseNamed("username/repo1:alias") + ref3, err := ParseNamed("username/repo1:alias") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -154,7 +153,7 @@ func TestAddDeleteGet(t *testing.T) { t.Fatalf("error adding to store: %v", err) } - ref4, err := reference.ParseNamed("username/repo2:latest") + ref4, err := ParseNamed("username/repo2:latest") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -162,11 +161,11 @@ func TestAddDeleteGet(t *testing.T) { t.Fatalf("error adding to store: %v", err) } - ref5, err := reference.ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c") + ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c") if err != nil { t.Fatalf("could not parse reference: %v", err) } - if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil { + if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil { t.Fatalf("error adding to store: %v", err) } @@ -229,7 +228,7 @@ func TestAddDeleteGet(t *testing.T) { } // Get should return ErrDoesNotExist for a nonexistent repo - nonExistRepo, err := reference.ParseNamed("username/nonexistrepo:latest") + nonExistRepo, err := ParseNamed("username/nonexistrepo:latest") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -238,7 +237,7 @@ func TestAddDeleteGet(t *testing.T) { } // Get should return ErrDoesNotExist for a nonexistent tag - nonExistTag, err := reference.ParseNamed("username/repo1:nonexist") + nonExistTag, err := ParseNamed("username/repo1:nonexist") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -264,7 +263,7 @@ func TestAddDeleteGet(t *testing.T) { } // Check ReferencesByName - repoName, err := reference.WithName("username/repo1") + repoName, err := WithName("username/repo1") if err != nil { t.Fatalf("could not parse reference: %v", err) } @@ -328,14 +327,14 @@ func TestInvalidTags(t *testing.T) { tmpDir, err := ioutil.TempDir("", "tag-store-test") defer os.RemoveAll(tmpDir) - store, err := NewTagStore(filepath.Join(tmpDir, "repositories.json")) + store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json")) if err != nil { t.Fatalf("error creating tag store: %v", err) } id := image.ID("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6") // sha256 as repo name - ref, err := reference.ParseNamed("sha256:abc") + ref, err := ParseNamed("sha256:abc") if err != nil { t.Fatal(err) } @@ -345,7 +344,7 @@ func TestInvalidTags(t *testing.T) { } // setting digest as a tag - ref, err = reference.ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6") + ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6") if err != nil { t.Fatal(err) } diff --git a/registry/config.go b/registry/config.go index 2eeba140e4..45caf57891 100644 --- a/registry/config.go +++ b/registry/config.go @@ -7,11 +7,12 @@ import ( "net/url" "strings" - "github.com/docker/distribution/reference" + distreference "github.com/docker/distribution/reference" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/image/v1" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/reference" ) // Options holds command line options. @@ -269,7 +270,7 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string { // splitReposName breaks a reposName into an index name and remote name func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) { var remoteNameStr string - indexName, remoteNameStr = reference.SplitHostname(reposName) + indexName, remoteNameStr = distreference.SplitHostname(reposName) if indexName == "" || (!strings.Contains(indexName, ".") && !strings.Contains(indexName, ":") && indexName != "localhost") { // This is a Docker Index repos (ex: samalba/hipache or ubuntu) @@ -405,13 +406,13 @@ func localNameFromRemote(indexName string, remoteName reference.Named) (referenc // error. func NormalizeLocalReference(ref reference.Named) reference.Named { localName := NormalizeLocalName(ref) - if tagged, isTagged := ref.(reference.Tagged); isTagged { + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { newRef, err := reference.WithTag(localName, tagged.Tag()) if err != nil { return ref } return newRef - } else if digested, isDigested := ref.(reference.Digested); isDigested { + } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { newRef, err := reference.WithDigest(localName, digested.Digest()) if err != nil { return ref diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index f45de5c89c..017d08bbe5 100644 --- a/registry/registry_mock_test.go +++ b/registry/registry_mock_test.go @@ -15,9 +15,9 @@ import ( "testing" "time" - "github.com/docker/distribution/reference" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/opts" + "github.com/docker/docker/reference" "github.com/gorilla/mux" "github.com/Sirupsen/logrus" diff --git a/registry/registry_test.go b/registry/registry_test.go index 7e35244165..31ae2d5fbb 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -8,10 +8,10 @@ import ( "strings" "testing" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/reference" ) var ( diff --git a/registry/service.go b/registry/service.go index b826f11731..eb5cd6bfdd 100644 --- a/registry/service.go +++ b/registry/service.go @@ -6,10 +6,10 @@ import ( "net/url" "strings" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/reference" ) // Service is a registry service. It tracks configuration data such as a list diff --git a/registry/service_v1.go b/registry/service_v1.go index 5fdc1ececf..3b3cc780f3 100644 --- a/registry/service_v1.go +++ b/registry/service_v1.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/docker/distribution/reference" "github.com/docker/docker/pkg/tlsconfig" + "github.com/docker/docker/reference" ) func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) { diff --git a/registry/service_v2.go b/registry/service_v2.go index 56a3d2eeed..3a2c32a5d9 100644 --- a/registry/service_v2.go +++ b/registry/service_v2.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" "github.com/docker/docker/pkg/tlsconfig" + "github.com/docker/docker/reference" ) func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) { diff --git a/registry/session.go b/registry/session.go index d09babd403..a1206206f6 100644 --- a/registry/session.go +++ b/registry/session.go @@ -19,13 +19,13 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/pkg/httputils" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/tarsum" + "github.com/docker/docker/reference" ) var ( diff --git a/registry/types.go b/registry/types.go index 03657820e5..939f44b14c 100644 --- a/registry/types.go +++ b/registry/types.go @@ -1,8 +1,8 @@ package registry import ( - "github.com/docker/distribution/reference" registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/reference" ) // RepositoryData tracks the image list, list of endpoints, and list of tokens From ffded61dad76e4c6530359d879afe83907e409a7 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 11 Dec 2015 11:00:13 -0800 Subject: [PATCH 2/5] Update Named reference with validation of conversions Signed-off-by: Tonis Tiigi --- api/client/build.go | 6 +- api/client/commit.go | 4 - api/client/import.go | 7 +- api/client/tag.go | 6 - api/client/trust.go | 18 +- daemon/daemon.go | 2 - distribution/pull.go | 10 +- distribution/pull_v1.go | 37 ++-- distribution/pull_v2.go | 22 +-- distribution/push.go | 14 +- distribution/push_v1.go | 10 +- distribution/push_v2.go | 8 +- distribution/registry.go | 13 +- distribution/registry_unit_test.go | 6 +- image/tarexport/save.go | 2 - reference/reference.go | 138 +++++++++++++-- reference/reference_test.go | 275 +++++++++++++++++++++++++++++ reference/store.go | 3 - registry/config.go | 178 +------------------ registry/registry_mock_test.go | 3 - registry/registry_test.go | 263 ++++++++------------------- registry/service_v1.go | 2 +- registry/service_v2.go | 2 +- registry/session.go | 20 +-- registry/types.go | 10 +- 25 files changed, 550 insertions(+), 509 deletions(-) create mode 100644 reference/reference_test.go diff --git a/api/client/build.go b/api/client/build.go index ec47c6e49c..f592ba915e 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -260,15 +260,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // validateTag checks if the given image name can be resolved. func validateTag(rawRepo string) (string, error) { - ref, err := reference.ParseNamed(rawRepo) + _, err := reference.ParseNamed(rawRepo) if err != nil { return "", err } - if err := registry.ValidateRepositoryName(ref); err != nil { - return "", err - } - return rawRepo, nil } diff --git a/api/client/commit.go b/api/client/commit.go index 9209cec22c..e7550dc27b 100644 --- a/api/client/commit.go +++ b/api/client/commit.go @@ -10,7 +10,6 @@ import ( "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/reference" - "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" ) @@ -44,9 +43,6 @@ func (cli *DockerCli) CmdCommit(args ...string) error { if err != nil { return err } - if err := registry.ValidateRepositoryName(ref); err != nil { - return err - } repositoryName = ref.Name() diff --git a/api/client/import.go b/api/client/import.go index 1b829c4374..7d0fdb870c 100644 --- a/api/client/import.go +++ b/api/client/import.go @@ -12,7 +12,6 @@ import ( flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/reference" - "github.com/docker/docker/registry" ) // CmdImport creates an empty filesystem image, imports the contents of the tarball into the image, and optionally tags the image. @@ -45,11 +44,7 @@ func (cli *DockerCli) CmdImport(args ...string) error { if repository != "" { //Check if the given image name can be resolved - ref, err := reference.ParseNamed(repository) - if err != nil { - return err - } - if err := registry.ValidateRepositoryName(ref); err != nil { + if _, err := reference.ParseNamed(repository); err != nil { return err } } diff --git a/api/client/tag.go b/api/client/tag.go index 203d64421f..b169194b96 100644 --- a/api/client/tag.go +++ b/api/client/tag.go @@ -7,7 +7,6 @@ import ( Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/reference" - "github.com/docker/docker/registry" ) // CmdTag tags an image into a repository. @@ -35,11 +34,6 @@ func (cli *DockerCli) CmdTag(args ...string) error { tag = tagged.Tag() } - //Check if the given image name can be resolved - if err := registry.ValidateRepositoryName(ref); err != nil { - return err - } - options := types.ImageTagOptions{ ImageID: cmd.Arg(0), RepositoryName: ref.Name(), diff --git a/api/client/trust.go b/api/client/trust.go index 9a18628b28..a609cc8c8d 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -167,12 +167,12 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut } creds := simpleCredentialStore{auth: authConfig} - tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName.Name(), "push", "pull") + tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), "push", "pull") basicHandler := auth.NewBasicHandler(creds) modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))) tr := transport.NewTransport(base, modifiers...) - return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName.Name(), server, tr, cli.getPassphraseRetriever()) + return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.FullName(), server, tr, cli.getPassphraseRetriever()) } func convertTarget(t client.Target) (target, error) { @@ -298,7 +298,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr for _, tgt := range targets { t, err := convertTarget(*tgt) if err != nil { - fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.LocalName) + fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name()) continue } refs = append(refs, t) @@ -321,19 +321,19 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr if displayTag != "" { displayTag = ":" + displayTag } - fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest) + fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest) - if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil { + if err := cli.imagePullPrivileged(authConfig, repoInfo.Name(), r.digest.String(), requestPrivilege); err != nil { return err } // If reference is not trusted, tag by trusted reference if !r.reference.HasDigest() { - tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String()) + tagged, err := reference.WithTag(repoInfo, r.reference.String()) if err != nil { return err } - trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest) + trustedRef, err := reference.WithDigest(repoInfo, r.digest) if err := cli.tagTrusted(trustedRef, tagged); err != nil { return err @@ -384,7 +384,7 @@ func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) { func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig types.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error { streamOut, targetChan := targetStream(cli.out) - reqError := cli.imagePushPrivileged(authConfig, repoInfo.LocalName.Name(), tag, streamOut, requestPrivilege) + reqError := cli.imagePushPrivileged(authConfig, repoInfo.Name(), tag, streamOut, requestPrivilege) // Close stream channel to finish target parsing if err := streamOut.Close(); err != nil { @@ -455,7 +455,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, if err := repo.Initialize(rootKeyID); err != nil { return notaryError(err) } - fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.CanonicalName) + fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName()) return notaryError(repo.Publish()) } diff --git a/daemon/daemon.go b/daemon/daemon.go index 73dc8d5b1b..681c1c61a2 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1043,7 +1043,6 @@ func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error { if err != nil { return err } - newTag = registry.NormalizeLocalReference(newTag) if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil { return err } @@ -1301,7 +1300,6 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) { // Treat it as a possible tag or digest reference if ref, err := reference.ParseNamed(refOrID); err == nil { - ref = registry.NormalizeLocalReference(ref) if id, err := daemon.referenceStore.Get(ref); err == nil { return id, nil } diff --git a/distribution/pull.go b/distribution/pull.go index e9408d5620..456288286a 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -87,17 +87,15 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo } // makes sure name is not empty or `scratch` - if err := validateRepoName(repoInfo.LocalName.Name()); err != nil { + if err := validateRepoName(repoInfo.Name()); err != nil { return err } - endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.CanonicalName) + endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo) if err != nil { return err } - localName := registry.NormalizeLocalReference(ref) - var ( // use a slice to append the error strings and return a joined string to caller errors []string @@ -112,7 +110,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo discardNoSupportErrors bool ) for _, endpoint := range endpoints { - logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version) + logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version) puller, err := newPuller(endpoint, repoInfo, imagePullConfig) if err != nil { @@ -148,7 +146,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo } } - imagePullConfig.EventsService.Log("pull", localName.String(), "") + imagePullConfig.EventsService.Log("pull", ref.String(), "") return nil } diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go index 0a04216583..36d5d4852f 100644 --- a/distribution/pull_v1.go +++ b/distribution/pull_v1.go @@ -65,18 +65,18 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool // TODO(dmcgowan): Check if should fallback return false, err } - progress.Message(p.config.ProgressOutput, "", p.repoInfo.CanonicalName.Name()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.") + progress.Message(p.config.ProgressOutput, "", p.repoInfo.FullName()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.") return false, nil } func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error { - progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.CanonicalName.Name()) + progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName()) - repoData, err := p.session.GetRepositoryData(p.repoInfo.RemoteName) + repoData, err := p.session.GetRepositoryData(p.repoInfo) if err != nil { if strings.Contains(err.Error(), "HTTP code: 404") { - return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName.Name()) + return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName()) } // Unexpected HTTP error return err @@ -86,13 +86,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro var tagsList map[string]string tagged, isTagged := ref.(reference.NamedTagged) if !isTagged { - tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName) + tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo) } else { var tagID string tagsList = make(map[string]string) - tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, tagged.Tag()) + tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag()) if err == registry.ErrRepoNotFound { - return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.CanonicalName.Name()) + return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName()) } tagsList[tagged.Tag()] = tagID } @@ -121,14 +121,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro } } - localNameRef := p.repoInfo.LocalName - if isTagged { - localNameRef, err = reference.WithTag(localNameRef, tagged.Tag()) - if err != nil { - localNameRef = p.repoInfo.LocalName - } - } - writeStatus(localNameRef.String(), p.config.ProgressOutput, layersDownloaded) + writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded) return nil } @@ -138,7 +131,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit return nil } - localNameRef, err := reference.WithTag(p.repoInfo.LocalName, img.Tag) + localNameRef, err := reference.WithTag(p.repoInfo, img.Tag) if err != nil { retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag) logrus.Debug(retErr.Error()) @@ -149,15 +142,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit return err } - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName.Name()) + progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName()) success := false var lastErr error for _, ep := range p.repoInfo.Index.Mirrors { ep += "v1/" - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep)) + progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep)) if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { // Don't report errors when pulling from mirrors. - logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err) + logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err) continue } success = true @@ -165,12 +158,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit } if !success { for _, ep := range repoData.Endpoints { - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep) + progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep) if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { // It's not ideal that only the last error is returned, it would be better to concatenate the errors. // As the error is also given to the output stream the user will see the error. lastErr = err - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err) + progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err) continue } success = true @@ -178,7 +171,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit } } if !success { - err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.CanonicalName.Name(), lastErr) + err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr) progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error()) return err } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index f9c47087bd..d00ddc6c90 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -54,19 +54,11 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var refs []reference.Named - taggedName := p.repoInfo.LocalName - if tagged, isTagged := ref.(reference.NamedTagged); isTagged { - taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag()) - if err != nil { - return err - } - refs = []reference.Named{taggedName} - } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { - taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest()) - if err != nil { - return err - } - refs = []reference.Named{taggedName} + taggedName := ref + if _, isTagged := ref.(reference.NamedTagged); isTagged { + refs = []reference.Named{ref} + } else if _, isCanonical := ref.(reference.Canonical); isCanonical { + refs = []reference.Named{ref} } else { manSvc, err := p.repo.Manifests(ctx) if err != nil { @@ -81,7 +73,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e // This probably becomes a lot nicer after the manifest // refactor... for _, tag := range tags { - tagRef, err := reference.WithTag(p.repoInfo.LocalName, tag) + tagRef, err := reference.WithTag(ref, tag) if err != nil { return err } @@ -291,7 +283,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat return false, err } - manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo.LocalName.Name()) + manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo) if err != nil { return false, err } diff --git a/distribution/push.go b/distribution/push.go index 956532a148..9a72963610 100644 --- a/distribution/push.go +++ b/distribution/push.go @@ -104,21 +104,21 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo return err } - endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.CanonicalName) + endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo) if err != nil { return err } - progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.CanonicalName.String()) + progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName()) - associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.LocalName) + associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo) if len(associations) == 0 { - return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName) + return fmt.Errorf("Repository does not exist: %s", repoInfo.Name()) } var lastErr error for _, endpoint := range endpoints { - logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version) + logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version) pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig) if err != nil { @@ -143,12 +143,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo } - imagePushConfig.EventsService.Log("push", repoInfo.LocalName.Name(), "") + imagePushConfig.EventsService.Log("push", repoInfo.Name(), "") return nil } if lastErr == nil { - lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName) + lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName()) } return lastErr } diff --git a/distribution/push_v1.go b/distribution/push_v1.go index 012216a68e..3b71364065 100644 --- a/distribution/push_v1.go +++ b/distribution/push_v1.go @@ -351,8 +351,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima } if topImage, isTopImage := img.(*v1TopImage); isTopImage { for _, tag := range tags[topImage.imageID] { - progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName.Name()+"/tags/"+tag) - if err := p.session.PushRegistryTag(p.repoInfo.RemoteName, v1ID, tag, endpoint); err != nil { + progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag) + if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil { return err } } @@ -381,18 +381,18 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error { // Register all the images in a repository with the registry // If an image is not in this list it will not be associated with the repository - repoData, err := p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, false, nil) + repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil) if err != nil { return err } - progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.CanonicalName.String()) + progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.FullName()) // push the repository to each of the endpoints only if it does not exist. for _, endpoint := range repoData.Endpoints { if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil { return err } } - _, err = p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, true, repoData.Endpoints) + _, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints) return err } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index f687518fd1..efea1926d1 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -52,8 +52,6 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) { return true, err } - localName := p.repoInfo.LocalName.Name() - var associations []reference.Association if _, isTagged := p.ref.(reference.NamedTagged); isTagged { imageID, err := p.config.ReferenceStore.Get(p.ref) @@ -72,10 +70,10 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) { associations = p.config.ReferenceStore.ReferencesByName(p.ref) } if err != nil { - return false, fmt.Errorf("error getting tags for %s: %s", localName, err) + return false, fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err) } if len(associations) == 0 { - return false, fmt.Errorf("no tags to push for %s", localName) + return false, fmt.Errorf("no tags to push for %s", p.repoInfo.Name()) } for _, association := range associations { @@ -159,7 +157,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat return err } - manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) + manifestDigest, manifestSize, err := digestFromManifest(signed, ref) if err != nil { return err } diff --git a/distribution/registry.go b/distribution/registry.go index f46d38237c..300daf6ef5 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -19,6 +19,7 @@ import ( "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/types" "github.com/docker/docker/distribution/xfer" + "github.com/docker/docker/reference" "github.com/docker/docker/registry" "golang.org/x/net/context" ) @@ -37,10 +38,10 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) { func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (distribution.Repository, error) { ctx := context.Background() - repoName := repoInfo.CanonicalName + repoName := repoInfo.FullName() // If endpoint does not support CanonicalName, use the RemoteName instead if endpoint.TrimHostname { - repoName = repoInfo.RemoteName + repoName = repoInfo.RemoteName() } // TODO(dmcgowan): Call close idle connections when complete, use keep alive @@ -99,16 +100,16 @@ func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEnd modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) } else { creds := dumbCredentialStore{auth: authConfig} - tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName.Name(), actions...) + tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...) basicHandler := auth.NewBasicHandler(creds) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) } tr := transport.NewTransport(base, modifiers...) - return client.NewRepository(ctx, repoName.Name(), endpoint.URL, tr) + return client.NewRepository(ctx, repoName, endpoint.URL, tr) } -func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Digest, int, error) { +func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) { payload, err := m.Payload() if err != nil { // If this failed, the signatures section was corrupted @@ -117,7 +118,7 @@ func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Dig } manifestDigest, err := digest.FromBytes(payload) if err != nil { - logrus.Infof("Could not compute manifest digest for %s:%s : %v", localName, m.Tag, err) + logrus.Infof("Could not compute manifest digest for %s:%s : %v", name.Name(), m.Tag, err) } return manifestDigest, len(payload), nil } diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index f6543bf837..ca8fbe3970 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -58,16 +58,14 @@ func TestTokenPassThru(t *testing.T) { } n, _ := reference.ParseNamed("testremotename") repoInfo := ®istry.RepositoryInfo{ + Named: n, Index: ®istrytypes.IndexInfo{ Name: "testrepo", Mirrors: nil, Secure: false, Official: false, }, - RemoteName: n, - LocalName: n, - CanonicalName: n, - Official: false, + Official: false, } imagePullConfig := &ImagePullConfig{ MetaHeaders: http.Header{}, diff --git a/image/tarexport/save.go b/image/tarexport/save.go index 8626e95523..c67009e16a 100644 --- a/image/tarexport/save.go +++ b/image/tarexport/save.go @@ -15,7 +15,6 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/reference" - "github.com/docker/docker/registry" ) type imageDescriptor struct { @@ -74,7 +73,6 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, if err != nil { return nil, err } - ref = registry.NormalizeLocalReference(ref) if ref.Name() == string(digest.Canonical) { imgID, err := l.is.Search(name) if err != nil { diff --git a/reference/reference.go b/reference/reference.go index 70196a14d2..d2d733587a 100644 --- a/reference/reference.go +++ b/reference/reference.go @@ -1,21 +1,37 @@ package reference import ( + "fmt" + "strings" + "github.com/docker/distribution/digest" distreference "github.com/docker/distribution/reference" + "github.com/docker/docker/image/v1" ) -// Reference is an opaque object reference identifier that may include -// modifiers such as a hostname, name, tag, and digest. -type Reference interface { - // String returns the full reference - String() string -} +const ( + // DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified + DefaultTag = "latest" + // DefaultHostname is the default built-in hostname + DefaultHostname = "docker.io" + // LegacyDefaultHostname is automatically converted to DefaultHostname + LegacyDefaultHostname = "index.docker.io" + // DefaultRepoPrefix is the prefix used for default repositories in default host + DefaultRepoPrefix = "library/" +) // Named is an object with a full name type Named interface { - Reference + // Name returns normalized repository name, like "ubuntu". Name() string + // String returns full reference, like "ubuntu@sha256:abcdef..." + String() string + // FullName returns full repository name with hostname, like "docker.io/library/ubuntu" + FullName() string + // Hostname returns hostname for the reference, like "docker.io" + Hostname() string + // RemoteName returns the repository component of the full name, like "library/ubuntu" + RemoteName() string } // NamedTagged is an object including a name and tag. @@ -35,26 +51,122 @@ type Canonical interface { // the Named interface. The reference must have a name, otherwise an error is // returned. // If an error was encountered it is returned, along with a nil Reference. -// NOTE: ParseNamed will not handle short digests. func ParseNamed(s string) (Named, error) { - // todo: docker specific validation - return distreference.ParseNamed(s) + named, err := distreference.ParseNamed(s) + if err != nil { + return nil, err + } + r, err := WithName(named.Name()) + if err != nil { + return nil, err + } + if canonical, isCanonical := named.(distreference.Canonical); isCanonical { + return WithDigest(r, canonical.Digest()) + } + if tagged, isTagged := named.(distreference.NamedTagged); isTagged { + return WithTag(r, tagged.Tag()) + } + return r, nil } // WithName returns a named object representing the given string. If the input // is invalid ErrReferenceInvalidFormat will be returned. func WithName(name string) (Named, error) { - return distreference.WithName(name) + name = normalize(name) + if err := validateName(name); err != nil { + return nil, err + } + r, err := distreference.WithName(name) + if err != nil { + return nil, err + } + return &namedRef{r}, nil } // WithTag combines the name from "name" and the tag from "tag" to form a // reference incorporating both the name and the tag. func WithTag(name Named, tag string) (NamedTagged, error) { - return distreference.WithTag(name, tag) + r, err := distreference.WithTag(name, tag) + if err != nil { + return nil, err + } + return &taggedRef{namedRef{r}}, nil } // WithDigest combines the name from "name" and the digest from "digest" to form // a reference incorporating both the name and the digest. func WithDigest(name Named, digest digest.Digest) (Canonical, error) { - return distreference.WithDigest(name, digest) + r, err := distreference.WithDigest(name, digest) + if err != nil { + return nil, err + } + return &canonicalRef{namedRef{r}}, nil +} + +type namedRef struct { + distreference.Named +} +type taggedRef struct { + namedRef +} +type canonicalRef struct { + namedRef +} + +func (r *namedRef) FullName() string { + hostname, remoteName := splitHostname(r.Name()) + return hostname + "/" + remoteName +} +func (r *namedRef) Hostname() string { + hostname, _ := splitHostname(r.Name()) + return hostname +} +func (r *namedRef) RemoteName() string { + _, remoteName := splitHostname(r.Name()) + return remoteName +} +func (r *taggedRef) Tag() string { + return r.namedRef.Named.(distreference.NamedTagged).Tag() +} +func (r *canonicalRef) Digest() digest.Digest { + return r.namedRef.Named.(distreference.Canonical).Digest() +} + +// splitHostname splits a repository name to hostname and remotename string. +// If no valid hostname is found, the default hostname is used. Repository name +// needs to be already validated before. +func splitHostname(name string) (hostname, remoteName string) { + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { + hostname, remoteName = DefaultHostname, name + } else { + hostname, remoteName = name[:i], name[i+1:] + } + if hostname == LegacyDefaultHostname { + hostname = DefaultHostname + } + if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') { + remoteName = DefaultRepoPrefix + remoteName + } + return +} + +// normalize returns a repository name in its normalized form, meaning it +// will not contain default hostname nor library/ prefix for official images. +func normalize(name string) string { + host, remoteName := splitHostname(name) + if host == DefaultHostname { + if strings.HasPrefix(remoteName, DefaultRepoPrefix) { + return strings.TrimPrefix(remoteName, DefaultRepoPrefix) + } + return remoteName + } + return name +} + +func validateName(name string) error { + if err := v1.ValidateID(name); err == nil { + return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name) + } + return nil } diff --git a/reference/reference_test.go b/reference/reference_test.go new file mode 100644 index 0000000000..ff35ba3da2 --- /dev/null +++ b/reference/reference_test.go @@ -0,0 +1,275 @@ +package reference + +import ( + "testing" + + "github.com/docker/distribution/digest" +) + +func TestValidateReferenceName(t *testing.T) { + validRepoNames := []string{ + "docker/docker", + "library/debian", + "debian", + "docker.io/docker/docker", + "docker.io/library/debian", + "docker.io/debian", + "index.docker.io/docker/docker", + "index.docker.io/library/debian", + "index.docker.io/debian", + "127.0.0.1:5000/docker/docker", + "127.0.0.1:5000/library/debian", + "127.0.0.1:5000/debian", + "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", + } + invalidRepoNames := []string{ + "https://github.com/docker/docker", + "docker/Docker", + "-docker", + "-docker/docker", + "-docker.io/docker/docker", + "docker///docker", + "docker.io/docker/Docker", + "docker.io/docker///docker", + "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", + "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", + } + + for _, name := range invalidRepoNames { + _, err := ParseNamed(name) + if err == nil { + t.Fatalf("Expected invalid repo name for %q", name) + } + } + + for _, name := range validRepoNames { + _, err := ParseNamed(name) + if err != nil { + t.Fatalf("Error parsing repo name %s, got: %q", name, err) + } + } +} + +func TestValidateRemoteName(t *testing.T) { + validRepositoryNames := []string{ + // Sanity check. + "docker/docker", + + // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). + "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", + + // Allow embedded hyphens. + "docker-rules/docker", + + // Allow multiple hyphens as well. + "docker---rules/docker", + + //Username doc and image name docker being tested. + "doc/docker", + + // single character names are now allowed. + "d/docker", + "jess/t", + + // Consecutive underscores. + "dock__er/docker", + } + for _, repositoryName := range validRepositoryNames { + _, err := ParseNamed(repositoryName) + if err != nil { + t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) + } + } + + invalidRepositoryNames := []string{ + // Disallow capital letters. + "docker/Docker", + + // Only allow one slash. + "docker///docker", + + // Disallow 64-character hexadecimal. + "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", + + // Disallow leading and trailing hyphens in namespace. + "-docker/docker", + "docker-/docker", + "-docker-/docker", + + // Don't allow underscores everywhere (as opposed to hyphens). + "____/____", + + "_docker/_docker", + + // Disallow consecutive periods. + "dock..er/docker", + "dock_.er/docker", + "dock-.er/docker", + + // No repository. + "docker/", + + //namespace too long + "this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker", + } + for _, repositoryName := range invalidRepositoryNames { + if _, err := ParseNamed(repositoryName); err == nil { + t.Errorf("Repository name should be invalid: %v", repositoryName) + } + } +} + +func TestParseRepositoryInfo(t *testing.T) { + type tcase struct { + RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string + } + + tcases := []tcase{ + { + RemoteName: "fooo/bar", + NormalizedName: "fooo/bar", + FullName: "docker.io/fooo/bar", + AmbiguousName: "index.docker.io/fooo/bar", + Hostname: "docker.io", + }, + { + RemoteName: "library/ubuntu", + NormalizedName: "ubuntu", + FullName: "docker.io/library/ubuntu", + AmbiguousName: "library/ubuntu", + Hostname: "docker.io", + }, + { + RemoteName: "nonlibrary/ubuntu", + NormalizedName: "nonlibrary/ubuntu", + FullName: "docker.io/nonlibrary/ubuntu", + AmbiguousName: "", + Hostname: "docker.io", + }, + { + RemoteName: "other/library", + NormalizedName: "other/library", + FullName: "docker.io/other/library", + AmbiguousName: "", + Hostname: "docker.io", + }, + { + RemoteName: "private/moonbase", + NormalizedName: "127.0.0.1:8000/private/moonbase", + FullName: "127.0.0.1:8000/private/moonbase", + AmbiguousName: "", + Hostname: "127.0.0.1:8000", + }, + { + RemoteName: "privatebase", + NormalizedName: "127.0.0.1:8000/privatebase", + FullName: "127.0.0.1:8000/privatebase", + AmbiguousName: "", + Hostname: "127.0.0.1:8000", + }, + { + RemoteName: "private/moonbase", + NormalizedName: "example.com/private/moonbase", + FullName: "example.com/private/moonbase", + AmbiguousName: "", + Hostname: "example.com", + }, + { + RemoteName: "privatebase", + NormalizedName: "example.com/privatebase", + FullName: "example.com/privatebase", + AmbiguousName: "", + Hostname: "example.com", + }, + { + RemoteName: "private/moonbase", + NormalizedName: "example.com:8000/private/moonbase", + FullName: "example.com:8000/private/moonbase", + AmbiguousName: "", + Hostname: "example.com:8000", + }, + { + RemoteName: "privatebasee", + NormalizedName: "example.com:8000/privatebasee", + FullName: "example.com:8000/privatebasee", + AmbiguousName: "", + Hostname: "example.com:8000", + }, + { + RemoteName: "library/ubuntu-12.04-base", + NormalizedName: "ubuntu-12.04-base", + FullName: "docker.io/library/ubuntu-12.04-base", + AmbiguousName: "index.docker.io/library/ubuntu-12.04-base", + Hostname: "docker.io", + }, + } + + for _, tcase := range tcases { + refStrings := []string{tcase.NormalizedName, tcase.FullName} + if tcase.AmbiguousName != "" { + refStrings = append(refStrings, tcase.AmbiguousName) + } + + var refs []Named + for _, r := range refStrings { + named, err := ParseNamed(r) + if err != nil { + t.Fatal(err) + } + refs = append(refs, named) + named, err = WithName(r) + if err != nil { + t.Fatal(err) + } + refs = append(refs, named) + } + + for _, r := range refs { + if expected, actual := tcase.NormalizedName, r.Name(); expected != actual { + t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual) + } + if expected, actual := tcase.FullName, r.FullName(); expected != actual { + t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual) + } + if expected, actual := tcase.Hostname, r.Hostname(); expected != actual { + t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual) + } + if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual { + t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual) + } + + } + } +} + +func TestParseReferenceWithTagAndDigest(t *testing.T) { + ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa") + if err != nil { + t.Fatal(err) + } + if _, isTagged := ref.(NamedTagged); isTagged { + t.Fatalf("Reference from %q should not support tag", ref) + } + if _, isCanonical := ref.(Canonical); !isCanonical { + t.Fatalf("Reference from %q should not support digest", ref) + } + if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected { + t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual) + } +} + +func TestInvalidReferenceComponents(t *testing.T) { + if _, err := WithName("-foo"); err == nil { + t.Fatal("Expected WithName to detect invalid name") + } + ref, err := WithName("busybox") + if err != nil { + t.Fatal(err) + } + if _, err := WithTag(ref, "-foo"); err == nil { + t.Fatal("Expected WithName to detect invalid tag") + } + if _, err := WithDigest(ref, digest.Digest("foo")); err == nil { + t.Fatal("Expected WithName to detect invalid digest") + } +} diff --git a/reference/store.go b/reference/store.go index 660c9e2019..85608f7e87 100644 --- a/reference/store.go +++ b/reference/store.go @@ -14,9 +14,6 @@ import ( "github.com/docker/docker/image" ) -// DefaultTag defines the default tag used when performing images related actions and no tag string is specified -const DefaultTag = "latest" - var ( // ErrDoesNotExist is returned if a reference is not found in the // store. diff --git a/registry/config.go b/registry/config.go index 45caf57891..ca7beec45a 100644 --- a/registry/config.go +++ b/registry/config.go @@ -7,9 +7,7 @@ import ( "net/url" "strings" - distreference "github.com/docker/distribution/reference" registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/image/v1" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/reference" @@ -182,28 +180,15 @@ func ValidateMirror(val string) (string, error) { // ValidateIndexName validates an index name. func ValidateIndexName(val string) (string, error) { - // 'index.docker.io' => 'docker.io' - if val == "index."+IndexName { - val = IndexName + if val == reference.LegacyDefaultHostname { + val = reference.DefaultHostname } if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") { return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val) } - // *TODO: Check if valid hostname[:port]/ip[:port]? return val, nil } -func validateRemoteName(remoteName reference.Named) error { - remoteNameStr := remoteName.Name() - if !strings.Contains(remoteNameStr, "/") { - // the repository name must not be a valid image ID - if err := v1.ValidateID(remoteNameStr); err == nil { - return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName) - } - } - return nil -} - func validateNoSchema(reposName string) error { if strings.Contains(reposName, "://") { // It cannot contain a scheme! @@ -212,29 +197,6 @@ func validateNoSchema(reposName string) error { return nil } -// ValidateRepositoryName validates a repository name -func ValidateRepositoryName(reposName reference.Named) error { - _, _, err := loadRepositoryName(reposName) - return err -} - -// loadRepositoryName returns the repo name splitted into index name -// and remote repo name. It returns an error if the name is not valid. -func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) { - if err := validateNoSchema(reposName.Name()); err != nil { - return "", nil, err - } - indexName, remoteName, err := splitReposName(reposName) - - if indexName, err = ValidateIndexName(indexName); err != nil { - return "", nil, err - } - if err = validateRemoteName(remoteName); err != nil { - return "", nil, err - } - return indexName, remoteName, nil -} - // newIndexInfo returns IndexInfo configuration from indexName func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) { var err error @@ -267,75 +229,14 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string { return index.Name } -// splitReposName breaks a reposName into an index name and remote name -func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) { - var remoteNameStr string - indexName, remoteNameStr = distreference.SplitHostname(reposName) - if indexName == "" || (!strings.Contains(indexName, ".") && - !strings.Contains(indexName, ":") && indexName != "localhost") { - // This is a Docker Index repos (ex: samalba/hipache or ubuntu) - // 'docker.io' - indexName = IndexName - remoteName = reposName - } else { - remoteName, err = reference.WithName(remoteNameStr) - } - return -} - // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo -func newRepositoryInfo(config *registrytypes.ServiceConfig, reposName reference.Named) (*RepositoryInfo, error) { - if err := validateNoSchema(reposName.Name()); err != nil { - return nil, err - } - - repoInfo := &RepositoryInfo{} - var ( - indexName string - err error - ) - - indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName) +func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) { + index, err := newIndexInfo(config, name.Hostname()) if err != nil { return nil, err } - - repoInfo.Index, err = newIndexInfo(config, indexName) - if err != nil { - return nil, err - } - - if repoInfo.Index.Official { - repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName) - if err != nil { - return nil, err - } - repoInfo.RemoteName = repoInfo.LocalName - - // If the normalized name does not contain a '/' (e.g. "foo") - // then it is an official repo. - if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 { - repoInfo.Official = true - // Fix up remote name for official repos. - repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name()) - if err != nil { - return nil, err - } - } - - repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name()) - if err != nil { - return nil, err - } - } else { - repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName) - if err != nil { - return nil, err - } - repoInfo.CanonicalName = repoInfo.LocalName - } - - return repoInfo, nil + official := !strings.ContainsRune(name.Name(), '/') + return &RepositoryInfo{name, index, official}, nil } // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but @@ -354,70 +255,3 @@ func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) { } return indexInfo, nil } - -// NormalizeLocalName transforms a repository name into a normalized LocalName -// Passes through the name without transformation on error (image id, etc) -// It does not use the repository info because we don't want to load -// the repository index and do request over the network. -func NormalizeLocalName(name reference.Named) reference.Named { - indexName, remoteName, err := loadRepositoryName(name) - if err != nil { - return name - } - - var officialIndex bool - // Return any configured index info, first. - if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok { - officialIndex = index.Official - } - - if officialIndex { - localName, err := normalizeLibraryRepoName(remoteName) - if err != nil { - return name - } - return localName - } - localName, err := localNameFromRemote(indexName, remoteName) - if err != nil { - return name - } - return localName -} - -// normalizeLibraryRepoName removes the library prefix from -// the repository name for official repos. -func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) { - if strings.HasPrefix(name.Name(), "library/") { - // If pull "library/foo", it's stored locally under "foo" - return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1]) - } - return name, nil -} - -// localNameFromRemote combines the index name and the repo remote name -// to generate a repo local name. -func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) { - return reference.WithName(indexName + "/" + remoteName.Name()) -} - -// NormalizeLocalReference transforms a reference to use a normalized LocalName -// for the name poriton. Passes through the reference without transformation on -// error. -func NormalizeLocalReference(ref reference.Named) reference.Named { - localName := NormalizeLocalName(ref) - if tagged, isTagged := ref.(reference.NamedTagged); isTagged { - newRef, err := reference.WithTag(localName, tagged.Tag()) - if err != nil { - return ref - } - return newRef - } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { - newRef, err := reference.WithDigest(localName, digested.Digest()) - if err != nil { - return ref - } - return newRef - } - return localName -} diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index 017d08bbe5..be04e34689 100644 --- a/registry/registry_mock_test.go +++ b/registry/registry_mock_test.go @@ -356,7 +356,6 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) { apiError(w, "Could not parse repository", 400) return } - repositoryName = NormalizeLocalName(repositoryName) tags, exists := testRepositories[repositoryName.String()] if !exists { apiError(w, "Repository not found", 404) @@ -380,7 +379,6 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) { apiError(w, "Could not parse repository", 400) return } - repositoryName = NormalizeLocalName(repositoryName) tagName := vars["tag"] tags, exists := testRepositories[repositoryName.String()] if !exists { @@ -405,7 +403,6 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) { apiError(w, "Could not parse repository", 400) return } - repositoryName = NormalizeLocalName(repositoryName) tagName := vars["tag"] tags, exists := testRepositories[repositoryName.String()] if !exists { diff --git a/registry/registry_test.go b/registry/registry_test.go index 31ae2d5fbb..46d2818fb7 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -307,71 +307,24 @@ func TestPushImageLayerRegistry(t *testing.T) { } } -func TestValidateRepositoryName(t *testing.T) { - validRepoNames := []string{ - "docker/docker", - "library/debian", - "debian", - "docker.io/docker/docker", - "docker.io/library/debian", - "docker.io/debian", - "index.docker.io/docker/docker", - "index.docker.io/library/debian", - "index.docker.io/debian", - "127.0.0.1:5000/docker/docker", - "127.0.0.1:5000/library/debian", - "127.0.0.1:5000/debian", - "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", - } - invalidRepoNames := []string{ - "https://github.com/docker/docker", - "docker/Docker", - "-docker", - "-docker/docker", - "-docker.io/docker/docker", - "docker///docker", - "docker.io/docker/Docker", - "docker.io/docker///docker", - "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - } - - for _, name := range invalidRepoNames { - named, err := reference.WithName(name) - if err == nil { - err := ValidateRepositoryName(named) - assertNotEqual(t, err, nil, "Expected invalid repo name: "+name) - } - } - - for _, name := range validRepoNames { - named, err := reference.WithName(name) - if err != nil { - t.Fatalf("could not parse valid name: %s", name) - } - err = ValidateRepositoryName(named) - assertEqual(t, err, nil, "Expected valid repo name: "+name) - } -} - func TestParseRepositoryInfo(t *testing.T) { - withName := func(name string) reference.Named { - named, err := reference.WithName(name) - if err != nil { - t.Fatalf("could not parse reference %s", name) - } - return named + type staticRepositoryInfo struct { + Index *registrytypes.IndexInfo + RemoteName string + CanonicalName string + LocalName string + Official bool } - expectedRepoInfos := map[string]RepositoryInfo{ + expectedRepoInfos := map[string]staticRepositoryInfo{ "fooo/bar": { Index: ®istrytypes.IndexInfo{ Name: IndexName, Official: true, }, - RemoteName: withName("fooo/bar"), - LocalName: withName("fooo/bar"), - CanonicalName: withName("docker.io/fooo/bar"), + RemoteName: "fooo/bar", + LocalName: "fooo/bar", + CanonicalName: "docker.io/fooo/bar", Official: false, }, "library/ubuntu": { @@ -379,9 +332,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("library/ubuntu"), - LocalName: withName("ubuntu"), - CanonicalName: withName("docker.io/library/ubuntu"), + RemoteName: "library/ubuntu", + LocalName: "ubuntu", + CanonicalName: "docker.io/library/ubuntu", Official: true, }, "nonlibrary/ubuntu": { @@ -389,9 +342,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("nonlibrary/ubuntu"), - LocalName: withName("nonlibrary/ubuntu"), - CanonicalName: withName("docker.io/nonlibrary/ubuntu"), + RemoteName: "nonlibrary/ubuntu", + LocalName: "nonlibrary/ubuntu", + CanonicalName: "docker.io/nonlibrary/ubuntu", Official: false, }, "ubuntu": { @@ -399,9 +352,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("library/ubuntu"), - LocalName: withName("ubuntu"), - CanonicalName: withName("docker.io/library/ubuntu"), + RemoteName: "library/ubuntu", + LocalName: "ubuntu", + CanonicalName: "docker.io/library/ubuntu", Official: true, }, "other/library": { @@ -409,9 +362,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("other/library"), - LocalName: withName("other/library"), - CanonicalName: withName("docker.io/other/library"), + RemoteName: "other/library", + LocalName: "other/library", + CanonicalName: "docker.io/other/library", Official: false, }, "127.0.0.1:8000/private/moonbase": { @@ -419,9 +372,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "127.0.0.1:8000", Official: false, }, - RemoteName: withName("private/moonbase"), - LocalName: withName("127.0.0.1:8000/private/moonbase"), - CanonicalName: withName("127.0.0.1:8000/private/moonbase"), + RemoteName: "private/moonbase", + LocalName: "127.0.0.1:8000/private/moonbase", + CanonicalName: "127.0.0.1:8000/private/moonbase", Official: false, }, "127.0.0.1:8000/privatebase": { @@ -429,9 +382,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "127.0.0.1:8000", Official: false, }, - RemoteName: withName("privatebase"), - LocalName: withName("127.0.0.1:8000/privatebase"), - CanonicalName: withName("127.0.0.1:8000/privatebase"), + RemoteName: "privatebase", + LocalName: "127.0.0.1:8000/privatebase", + CanonicalName: "127.0.0.1:8000/privatebase", Official: false, }, "localhost:8000/private/moonbase": { @@ -439,9 +392,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "localhost:8000", Official: false, }, - RemoteName: withName("private/moonbase"), - LocalName: withName("localhost:8000/private/moonbase"), - CanonicalName: withName("localhost:8000/private/moonbase"), + RemoteName: "private/moonbase", + LocalName: "localhost:8000/private/moonbase", + CanonicalName: "localhost:8000/private/moonbase", Official: false, }, "localhost:8000/privatebase": { @@ -449,9 +402,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "localhost:8000", Official: false, }, - RemoteName: withName("privatebase"), - LocalName: withName("localhost:8000/privatebase"), - CanonicalName: withName("localhost:8000/privatebase"), + RemoteName: "privatebase", + LocalName: "localhost:8000/privatebase", + CanonicalName: "localhost:8000/privatebase", Official: false, }, "example.com/private/moonbase": { @@ -459,9 +412,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "example.com", Official: false, }, - RemoteName: withName("private/moonbase"), - LocalName: withName("example.com/private/moonbase"), - CanonicalName: withName("example.com/private/moonbase"), + RemoteName: "private/moonbase", + LocalName: "example.com/private/moonbase", + CanonicalName: "example.com/private/moonbase", Official: false, }, "example.com/privatebase": { @@ -469,9 +422,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "example.com", Official: false, }, - RemoteName: withName("privatebase"), - LocalName: withName("example.com/privatebase"), - CanonicalName: withName("example.com/privatebase"), + RemoteName: "privatebase", + LocalName: "example.com/privatebase", + CanonicalName: "example.com/privatebase", Official: false, }, "example.com:8000/private/moonbase": { @@ -479,9 +432,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "example.com:8000", Official: false, }, - RemoteName: withName("private/moonbase"), - LocalName: withName("example.com:8000/private/moonbase"), - CanonicalName: withName("example.com:8000/private/moonbase"), + RemoteName: "private/moonbase", + LocalName: "example.com:8000/private/moonbase", + CanonicalName: "example.com:8000/private/moonbase", Official: false, }, "example.com:8000/privatebase": { @@ -489,9 +442,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "example.com:8000", Official: false, }, - RemoteName: withName("privatebase"), - LocalName: withName("example.com:8000/privatebase"), - CanonicalName: withName("example.com:8000/privatebase"), + RemoteName: "privatebase", + LocalName: "example.com:8000/privatebase", + CanonicalName: "example.com:8000/privatebase", Official: false, }, "localhost/private/moonbase": { @@ -499,9 +452,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "localhost", Official: false, }, - RemoteName: withName("private/moonbase"), - LocalName: withName("localhost/private/moonbase"), - CanonicalName: withName("localhost/private/moonbase"), + RemoteName: "private/moonbase", + LocalName: "localhost/private/moonbase", + CanonicalName: "localhost/private/moonbase", Official: false, }, "localhost/privatebase": { @@ -509,9 +462,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: "localhost", Official: false, }, - RemoteName: withName("privatebase"), - LocalName: withName("localhost/privatebase"), - CanonicalName: withName("localhost/privatebase"), + RemoteName: "privatebase", + LocalName: "localhost/privatebase", + CanonicalName: "localhost/privatebase", Official: false, }, IndexName + "/public/moonbase": { @@ -519,9 +472,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("public/moonbase"), - LocalName: withName("public/moonbase"), - CanonicalName: withName("docker.io/public/moonbase"), + RemoteName: "public/moonbase", + LocalName: "public/moonbase", + CanonicalName: "docker.io/public/moonbase", Official: false, }, "index." + IndexName + "/public/moonbase": { @@ -529,9 +482,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("public/moonbase"), - LocalName: withName("public/moonbase"), - CanonicalName: withName("docker.io/public/moonbase"), + RemoteName: "public/moonbase", + LocalName: "public/moonbase", + CanonicalName: "docker.io/public/moonbase", Official: false, }, "ubuntu-12.04-base": { @@ -539,9 +492,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("library/ubuntu-12.04-base"), - LocalName: withName("ubuntu-12.04-base"), - CanonicalName: withName("docker.io/library/ubuntu-12.04-base"), + RemoteName: "library/ubuntu-12.04-base", + LocalName: "ubuntu-12.04-base", + CanonicalName: "docker.io/library/ubuntu-12.04-base", Official: true, }, IndexName + "/ubuntu-12.04-base": { @@ -549,9 +502,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("library/ubuntu-12.04-base"), - LocalName: withName("ubuntu-12.04-base"), - CanonicalName: withName("docker.io/library/ubuntu-12.04-base"), + RemoteName: "library/ubuntu-12.04-base", + LocalName: "ubuntu-12.04-base", + CanonicalName: "docker.io/library/ubuntu-12.04-base", Official: true, }, "index." + IndexName + "/ubuntu-12.04-base": { @@ -559,9 +512,9 @@ func TestParseRepositoryInfo(t *testing.T) { Name: IndexName, Official: true, }, - RemoteName: withName("library/ubuntu-12.04-base"), - LocalName: withName("ubuntu-12.04-base"), - CanonicalName: withName("docker.io/library/ubuntu-12.04-base"), + RemoteName: "library/ubuntu-12.04-base", + LocalName: "ubuntu-12.04-base", + CanonicalName: "docker.io/library/ubuntu-12.04-base", Official: true, }, } @@ -577,9 +530,9 @@ func TestParseRepositoryInfo(t *testing.T) { t.Error(err) } else { checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName) - checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName) - checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName) - checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName) + checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName) + checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName) + checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName) checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName) checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName) } @@ -806,82 +759,6 @@ func TestSearchRepositories(t *testing.T) { assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars") } -func TestValidRemoteName(t *testing.T) { - validRepositoryNames := []string{ - // Sanity check. - "docker/docker", - - // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). - "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", - - // Allow embedded hyphens. - "docker-rules/docker", - - // Allow multiple hyphens as well. - "docker---rules/docker", - - //Username doc and image name docker being tested. - "doc/docker", - - // single character names are now allowed. - "d/docker", - "jess/t", - - // Consecutive underscores. - "dock__er/docker", - } - for _, repositoryName := range validRepositoryNames { - repositoryRef, err := reference.WithName(repositoryName) - if err != nil { - t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) - } - if err := validateRemoteName(repositoryRef); err != nil { - t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) - } - } - - invalidRepositoryNames := []string{ - // Disallow capital letters. - "docker/Docker", - - // Only allow one slash. - "docker///docker", - - // Disallow 64-character hexadecimal. - "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - - // Disallow leading and trailing hyphens in namespace. - "-docker/docker", - "docker-/docker", - "-docker-/docker", - - // Don't allow underscores everywhere (as opposed to hyphens). - "____/____", - - "_docker/_docker", - - // Disallow consecutive periods. - "dock..er/docker", - "dock_.er/docker", - "dock-.er/docker", - - // No repository. - "docker/", - - //namespace too long - "this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker", - } - for _, repositoryName := range invalidRepositoryNames { - repositoryRef, err := reference.ParseNamed(repositoryName) - if err != nil { - continue - } - if err := validateRemoteName(repositoryRef); err == nil { - t.Errorf("Repository name should be invalid: %v", repositoryName) - } - } -} - func TestTrustedLocation(t *testing.T) { for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { req, _ := http.NewRequest("GET", url, nil) diff --git a/registry/service_v1.go b/registry/service_v1.go index 3b3cc780f3..cd565bc43c 100644 --- a/registry/service_v1.go +++ b/registry/service_v1.go @@ -11,7 +11,7 @@ import ( func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) { var cfg = tlsconfig.ServerDefault tlsConfig := &cfg - nameString := repoName.Name() + nameString := repoName.FullName() if strings.HasPrefix(nameString, DefaultNamespace+"/") { endpoints = append(endpoints, APIEndpoint{ URL: DefaultV1Registry, diff --git a/registry/service_v2.go b/registry/service_v2.go index 3a2c32a5d9..8a8cd2600d 100644 --- a/registry/service_v2.go +++ b/registry/service_v2.go @@ -12,7 +12,7 @@ import ( func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) { var cfg = tlsconfig.ServerDefault tlsConfig := &cfg - nameString := repoName.Name() + nameString := repoName.FullName() if strings.HasPrefix(nameString, DefaultNamespace+"/") { // v2 mirrors for _, mirror := range s.Config.Mirrors { diff --git a/registry/session.go b/registry/session.go index a1206206f6..494b84bf5a 100644 --- a/registry/session.go +++ b/registry/session.go @@ -312,7 +312,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io // argument, and returns data from the first one that answers the query // successfully. func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) { - repository := repositoryRef.Name() + repository := repositoryRef.RemoteName() if strings.Count(repository, "/") == 0 { // This will be removed once the registry supports auto-resolution on @@ -350,7 +350,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name // the first one that answers the query successfully. It returns a map with // tag names as the keys and image IDs as the values. func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) { - repository := repositoryRef.Name() + repository := repositoryRef.RemoteName() if strings.Count(repository, "/") == 0 { // This will be removed once the registry supports auto-resolution on @@ -403,8 +403,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) { } // GetRepositoryData returns lists of images and endpoints for the repository -func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) { - repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name()) +func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) { + repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), name.RemoteName()) logrus.Debugf("[registry] Calling GET %s", repositoryTarget) @@ -438,7 +438,7 @@ func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, er if err != nil { logrus.Debugf("Error reading response body: %s", err) } - return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res) + return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res) } var endpoints []string @@ -593,7 +593,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error { // "jsonify" the string revision = "\"" + revision + "\"" - path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag) + path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag) req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision)) if err != nil { @@ -607,7 +607,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr } res.Body.Close() if res.StatusCode != 200 && res.StatusCode != 201 { - return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res) + return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res) } return nil } @@ -633,7 +633,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, if validate { suffix = "images" } - u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix) + u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.RemoteName(), suffix) logrus.Debugf("[registry] PUT %s", u) logrus.Debugf("Image list pushed to index:\n%s", imgListJSON) headers := map[string][]string{ @@ -671,7 +671,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, if err != nil { logrus.Debugf("Error reading response body: %s", err) } - return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res) + return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res) } tokens = res.Header["X-Docker-Token"] logrus.Debugf("Auth token: %v", tokens) @@ -689,7 +689,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, if err != nil { logrus.Debugf("Error reading response body: %s", err) } - return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res) + return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res) } } diff --git a/registry/types.go b/registry/types.go index 939f44b14c..da3eaacb3f 100644 --- a/registry/types.go +++ b/registry/types.go @@ -60,17 +60,9 @@ const ( // RepositoryInfo describes a repository type RepositoryInfo struct { + reference.Named // Index points to registry information Index *registrytypes.IndexInfo - // RemoteName is the remote name of the repository, such as - // "library/ubuntu-12.04-base" - RemoteName reference.Named - // LocalName is the local name of the repository, such as - // "ubuntu-12.04-base" - LocalName reference.Named - // CanonicalName is the canonical name of the repository, such as - // "docker.io/library/ubuntu-12.04-base" - CanonicalName reference.Named // Official indicates whether the repository is considered official. // If the registry is official, and the normalized name does not // contain a '/' (e.g. "foo"), then it is considered an official repo. From c1040b222c2520f8a0ebe14e81b5b7fe188e8dc6 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 9 Dec 2015 23:21:17 -0800 Subject: [PATCH 3/5] Remove unused repoinfo Signed-off-by: Tonis Tiigi --- api/client/build.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/api/client/build.go b/api/client/build.go index f592ba915e..2d1d0f2214 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -29,7 +29,6 @@ import ( "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/reference" - "github.com/docker/docker/registry" "github.com/docker/docker/utils" "github.com/docker/go-units" ) @@ -477,7 +476,6 @@ func (td *trustedDockerfile) Close() error { // resolvedTag records the repository, tag, and resolved digest reference // from a Dockerfile rewrite. type resolvedTag struct { - repoInfo *registry.RepositoryInfo digestRef reference.Canonical tagRef reference.NamedTagged } @@ -537,11 +535,6 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(reference.Name } } - repoInfo, err := registry.ParseRepositoryInfo(ref) - if err != nil { - return nil, nil, fmt.Errorf("unable to parse repository info %q: %v", ref.String(), err) - } - if !digested && isTrusted() { trustedRef, err := translator(ref.(reference.NamedTagged)) if err != nil { @@ -550,7 +543,6 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(reference.Name line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String())) resolvedTags = append(resolvedTags, &resolvedTag{ - repoInfo: repoInfo, digestRef: trustedRef, tagRef: ref.(reference.NamedTagged), }) From 15d84a3a48efa12ed8bdc500f28ca58a7b1d1083 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 9 Dec 2015 23:31:38 -0800 Subject: [PATCH 4/5] Improve reference parse errors Fixes #18093 Signed-off-by: Tonis Tiigi --- integration-cli/docker_cli_build_test.go | 2 +- integration-cli/docker_cli_create_test.go | 2 +- integration-cli/docker_cli_run_test.go | 4 ++-- integration-cli/docker_cli_tag_test.go | 6 +++--- reference/reference.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index b540af51c8..13001068f9 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -4614,7 +4614,7 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) { _, out, err := buildImageWithOut(name, "FROM scratch\nMAINTAINER quux\n", true) // if the error doesn't check for illegal tag name, or the image is built // then this should fail - if !strings.Contains(out, "invalid reference format") || strings.Contains(out, "Sending build context to Docker daemon") { + if !strings.Contains(out, "Error parsing reference") || strings.Contains(out, "Sending build context to Docker daemon") { c.Fatalf("failed to stop before building. Error: %s, Output: %s", err, out) } } diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go index cac5b92d5c..a2d47cf595 100644 --- a/integration-cli/docker_cli_create_test.go +++ b/integration-cli/docker_cli_create_test.go @@ -265,7 +265,7 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) { c.Fatalf("expected non-zero exit code; received %d", exit) } - if expected := "invalid reference format"; !strings.Contains(out, expected) { + if expected := "Error parsing reference"; !strings.Contains(out, expected) { c.Fatalf(`Expected %q in output; got: %s`, expected, out) } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 3bab96fb2f..399dce27cb 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -3758,8 +3758,8 @@ func (s *DockerSuite) TestRunInvalidReference(c *check.C) { c.Fatalf("expected non-zero exist code; received %d", exit) } - if !strings.Contains(out, "invalid reference format") { - c.Fatalf(`Expected "invalid reference format" in output; got: %s`, out) + if !strings.Contains(out, "Error parsing reference") { + c.Fatalf(`Expected "Error parsing reference" in output; got: %s`, out) } } diff --git a/integration-cli/docker_cli_tag_test.go b/integration-cli/docker_cli_tag_test.go index 7e27a750d2..de5d547ebe 100644 --- a/integration-cli/docker_cli_tag_test.go +++ b/integration-cli/docker_cli_tag_test.go @@ -99,17 +99,17 @@ func (s *DockerSuite) TestTagWithPrefixHyphen(c *check.C) { // test repository name begin with '-' out, _, err := dockerCmdWithError("tag", "busybox:latest", "-busybox:test") c.Assert(err, checker.NotNil, check.Commentf(out)) - c.Assert(out, checker.Contains, "invalid reference format", check.Commentf("tag a name begin with '-' should failed")) + c.Assert(out, checker.Contains, "Error parsing reference", check.Commentf("tag a name begin with '-' should failed")) // test namespace name begin with '-' out, _, err = dockerCmdWithError("tag", "busybox:latest", "-test/busybox:test") c.Assert(err, checker.NotNil, check.Commentf(out)) - c.Assert(out, checker.Contains, "invalid reference format", check.Commentf("tag a name begin with '-' should failed")) + c.Assert(out, checker.Contains, "Error parsing reference", check.Commentf("tag a name begin with '-' should failed")) // test index name begin with '-' out, _, err = dockerCmdWithError("tag", "busybox:latest", "-index:5000/busybox:test") c.Assert(err, checker.NotNil, check.Commentf(out)) - c.Assert(out, checker.Contains, "invalid reference format", check.Commentf("tag a name begin with '-' should failed")) + c.Assert(out, checker.Contains, "Error parsing reference", check.Commentf("tag a name begin with '-' should failed")) } // ensure tagging using official names works diff --git a/reference/reference.go b/reference/reference.go index d2d733587a..8e887e3637 100644 --- a/reference/reference.go +++ b/reference/reference.go @@ -54,7 +54,7 @@ type Canonical interface { func ParseNamed(s string) (Named, error) { named, err := distreference.ParseNamed(s) if err != nil { - return nil, err + return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag", s) } r, err := WithName(named.Name()) if err != nil { From eeb2d4c1adbe4e00f9fbcdc70f9ac31997968e1d Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 10 Dec 2015 11:01:34 -0800 Subject: [PATCH 5/5] Clean up reference type switches Signed-off-by: Tonis Tiigi --- api/client/build.go | 20 ++++---------------- api/client/create.go | 26 ++++++-------------------- api/client/import.go | 1 - api/client/pull.go | 25 ++++++++----------------- api/client/tag.go | 5 ++--- api/client/trust.go | 4 +++- api/server/router/local/image.go | 8 ++------ daemon/daemonbuilder/builder.go | 10 +--------- daemon/image_delete.go | 12 +----------- distribution/pull_v2.go | 7 ++----- image/tarexport/save.go | 26 ++++++++++++-------------- reference/reference.go | 19 +++++++++++++++++++ reference/store.go | 19 +++---------------- 13 files changed, 63 insertions(+), 119 deletions(-) diff --git a/api/client/build.go b/api/client/build.go index 2d1d0f2214..5d7b9f5da3 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -522,21 +522,9 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(reference.Name if err != nil { return nil, nil, err } - - digested := false - switch ref.(type) { - case reference.NamedTagged: - case reference.Canonical: - digested = true - default: - ref, err = reference.WithTag(ref, reference.DefaultTag) - if err != nil { - return nil, nil, err - } - } - - if !digested && isTrusted() { - trustedRef, err := translator(ref.(reference.NamedTagged)) + ref = reference.WithDefaultTag(ref) + if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() { + trustedRef, err := translator(ref) if err != nil { return nil, nil, err } @@ -544,7 +532,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(reference.Name line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String())) resolvedTags = append(resolvedTags, &resolvedTag{ digestRef: trustedRef, - tagRef: ref.(reference.NamedTagged), + tagRef: ref, }) } } diff --git a/api/client/create.go b/api/client/create.go index 60d1838e28..0336e86f33 100644 --- a/api/client/create.go +++ b/api/client/create.go @@ -25,14 +25,11 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { } var tag string - switch x := ref.(type) { + switch x := reference.WithDefaultTag(ref).(type) { case reference.Canonical: tag = x.Digest().String() case reference.NamedTagged: tag = x.Tag() - default: - // pull only the image tagged 'latest' if no tag was specified - tag = reference.DefaultTag } // Resolve the Repository name from fqn to RepositoryInfo @@ -97,24 +94,13 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc if err != nil { return nil, err } - - isCanonical := false - switch ref.(type) { - case reference.NamedTagged: - case reference.Canonical: - isCanonical = true - default: - ref, err = reference.WithTag(ref, reference.DefaultTag) - if err != nil { - return nil, err - } - } + ref = reference.WithDefaultTag(ref) var trustedRef reference.Canonical - if isTrusted() && !isCanonical { + if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() { var err error - trustedRef, err = cli.trustedReference(ref.(reference.NamedTagged)) + trustedRef, err = cli.trustedReference(ref) if err != nil { return nil, err } @@ -132,8 +118,8 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } - if trustedRef != nil && !isCanonical { - if err := cli.tagTrusted(trustedRef, ref.(reference.NamedTagged)); err != nil { + if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil { + if err := cli.tagTrusted(trustedRef, ref); err != nil { return nil, err } } diff --git a/api/client/import.go b/api/client/import.go index 7d0fdb870c..692566d72b 100644 --- a/api/client/import.go +++ b/api/client/import.go @@ -59,7 +59,6 @@ func (cli *DockerCli) CmdImport(args ...string) error { } defer file.Close() in = file - } options := types.ImageImportOptions{ diff --git a/api/client/pull.go b/api/client/pull.go index 43cbc9e253..361c341d06 100644 --- a/api/client/pull.go +++ b/api/client/pull.go @@ -13,8 +13,6 @@ import ( "github.com/docker/docker/registry" ) -var errTagCantBeUsed = errors.New("tag can't be used with --all-tags/-a") - // CmdPull pulls an image or a repository from the registry. // // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST] @@ -31,28 +29,21 @@ func (cli *DockerCli) CmdPull(args ...string) error { if err != nil { return err } + if *allTags && !reference.IsNameOnly(distributionRef) { + return errors.New("tag can't be used with --all-tags/-a") + } + + if !*allTags && reference.IsNameOnly(distributionRef) { + distributionRef = reference.WithDefaultTag(distributionRef) + fmt.Fprintf(cli.out, "Using default tag: %s\n", reference.DefaultTag) + } var tag string switch x := distributionRef.(type) { case reference.Canonical: - if *allTags { - return errTagCantBeUsed - } tag = x.Digest().String() case reference.NamedTagged: - if *allTags { - return errTagCantBeUsed - } tag = x.Tag() - default: - if !*allTags { - tag = reference.DefaultTag - distributionRef, err = reference.WithTag(distributionRef, tag) - if err != nil { - return err - } - fmt.Fprintf(cli.out, "Using default tag: %s\n", tag) - } } ref := registry.ParseReference(tag) diff --git a/api/client/tag.go b/api/client/tag.go index b169194b96..a1492666d0 100644 --- a/api/client/tag.go +++ b/api/client/tag.go @@ -28,9 +28,8 @@ func (cli *DockerCli) CmdTag(args ...string) error { return errors.New("refusing to create a tag with a digest reference") } - tag := "" - tagged, isTagged := ref.(reference.NamedTagged) - if isTagged { + var tag string + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { tag = tagged.Tag() } diff --git a/api/client/trust.go b/api/client/trust.go index a609cc8c8d..252b9b1260 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -334,9 +334,11 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr return err } trustedRef, err := reference.WithDigest(repoInfo, r.digest) + if err != nil { + return err + } if err := cli.tagTrusted(trustedRef, tagged); err != nil { return err - } } } diff --git a/api/server/router/local/image.go b/api/server/router/local/image.go index 15273ac276..c66a8d8f8b 100644 --- a/api/server/router/local/image.go +++ b/api/server/router/local/image.go @@ -154,8 +154,7 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r return err } - switch newRef.(type) { - case reference.Canonical: + if _, isCanonical := newRef.(reference.Canonical); isCanonical { return errors.New("cannot import digest reference") } @@ -496,15 +495,12 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) { if err != nil { return nil, err } + ref = reference.WithDefaultTag(ref) if _, isCanonical := ref.(reference.Canonical); isCanonical { return nil, errors.New("build tag cannot contain a digest") } - if _, isTagged := ref.(reference.NamedTagged); !isTagged { - ref, err = reference.WithTag(ref, reference.DefaultTag) - } - nameWithTag := ref.String() if _, exists := uniqNames[nameWithTag]; !exists { diff --git a/daemon/daemonbuilder/builder.go b/daemon/daemonbuilder/builder.go index 09bab93a4a..7de4ff2eab 100644 --- a/daemon/daemonbuilder/builder.go +++ b/daemon/daemonbuilder/builder.go @@ -41,15 +41,7 @@ func (d Docker) Pull(name string) (*image.Image, error) { if err != nil { return nil, err } - switch ref.(type) { - case reference.NamedTagged: - case reference.Canonical: - default: - ref, err = reference.WithTag(ref, "latest") - if err != nil { - return nil, err - } - } + ref = reference.WithDefaultTag(ref) pullRegistryAuth := &types.AuthConfig{} if len(d.AuthConfigs) > 0 { diff --git a/daemon/image_delete.go b/daemon/image_delete.go index 6d8b7b1bac..ab8217b5e1 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -149,17 +149,7 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai // optional tag or digest reference. If tag or digest is omitted, the default // tag is used. Returns the resolved image reference and an error. func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) { - switch ref.(type) { - case reference.NamedTagged: - case reference.Canonical: - default: - var err error - ref, err = reference.WithTag(ref, reference.DefaultTag) - if err != nil { - return nil, err - } - } - + ref = reference.WithDefaultTag(ref) // Ignore the boolean value returned, as far as we're concerned, this // is an idempotent operation and it's okay if the reference didn't // exist in the first place. diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index d00ddc6c90..8fe47e519f 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -54,10 +54,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var refs []reference.Named - taggedName := ref - if _, isTagged := ref.(reference.NamedTagged); isTagged { - refs = []reference.Named{ref} - } else if _, isCanonical := ref.(reference.Canonical); isCanonical { + if !reference.IsNameOnly(ref) { refs = []reference.Named{ref} } else { manSvc, err := p.repo.Manifests(ctx) @@ -92,7 +89,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e layersDownloaded = layersDownloaded || pulledNew } - writeStatus(taggedName.String(), p.config.ProgressOutput, layersDownloaded) + writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded) return nil } diff --git a/image/tarexport/save.go b/image/tarexport/save.go index c67009e16a..b7022ac5d8 100644 --- a/image/tarexport/save.go +++ b/image/tarexport/save.go @@ -81,21 +81,19 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, addAssoc(imgID, nil) continue } - if _, ok := ref.(reference.Canonical); !ok { - if _, ok := ref.(reference.NamedTagged); !ok { - assocs := l.rs.ReferencesByName(ref) - for _, assoc := range assocs { - addAssoc(assoc.ImageID, assoc.Ref) - } - if len(assocs) == 0 { - imgID, err := l.is.Search(name) - if err != nil { - return nil, err - } - addAssoc(imgID, nil) - } - continue + if reference.IsNameOnly(ref) { + assocs := l.rs.ReferencesByName(ref) + for _, assoc := range assocs { + addAssoc(assoc.ImageID, assoc.Ref) } + if len(assocs) == 0 { + imgID, err := l.is.Search(name) + if err != nil { + return nil, err + } + addAssoc(imgID, nil) + } + continue } var imgID image.ID if imgID, err = l.rs.Get(ref); err != nil { diff --git a/reference/reference.go b/reference/reference.go index 8e887e3637..1e3482475f 100644 --- a/reference/reference.go +++ b/reference/reference.go @@ -132,6 +132,25 @@ func (r *canonicalRef) Digest() digest.Digest { return r.namedRef.Named.(distreference.Canonical).Digest() } +// WithDefaultTag adds a default tag to a reference if it only has a repo name. +func WithDefaultTag(ref Named) Named { + if IsNameOnly(ref) { + ref, _ = WithTag(ref, DefaultTag) + } + return ref +} + +// IsNameOnly returns true if reference only contains a repo name. +func IsNameOnly(ref Named) bool { + if _, ok := ref.(NamedTagged); ok { + return false + } + if _, ok := ref.(Canonical); ok { + return false + } + return true +} + // splitHostname splits a repository name to hostname and remotename string. // If no valid hostname is found, the default hostname is used. Repository name // needs to be already validated before. diff --git a/reference/store.go b/reference/store.go index 85608f7e87..91c5c2aed2 100644 --- a/reference/store.go +++ b/reference/store.go @@ -64,19 +64,6 @@ func (a lexicalAssociations) Len() int { return len(a) } func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() } -func defaultTagIfNameOnly(ref Named) Named { - switch ref.(type) { - case NamedTagged: - return ref - case Canonical: - return ref - default: - // Should never fail - ref, _ = WithTag(ref, DefaultTag) - return ref - } -} - // NewReferenceStore creates a new reference store, tied to a file path where // the set of references are serialized in JSON format. func NewReferenceStore(jsonPath string) (Store, error) { @@ -107,7 +94,7 @@ func (store *store) AddTag(ref Named, id image.ID, force bool) error { if _, isCanonical := ref.(Canonical); isCanonical { return errors.New("refusing to create a tag with a digest reference") } - return store.addReference(defaultTagIfNameOnly(ref), id, force) + return store.addReference(WithDefaultTag(ref), id, force) } // AddDigest adds a digest reference to the store. @@ -162,7 +149,7 @@ func (store *store) addReference(ref Named, id image.ID, force bool) error { // Delete deletes a reference from the store. It returns true if a deletion // happened, or false otherwise. func (store *store) Delete(ref Named) (bool, error) { - ref = defaultTagIfNameOnly(ref) + ref = WithDefaultTag(ref) store.mu.Lock() defer store.mu.Unlock() @@ -194,7 +181,7 @@ func (store *store) Delete(ref Named) (bool, error) { // Get retrieves an item from the store by func (store *store) Get(ref Named) (image.ID, error) { - ref = defaultTagIfNameOnly(ref) + ref = WithDefaultTag(ref) store.mu.RLock() defer store.mu.RUnlock()