Use distribution reference

Remove forked reference package. Use normalized named values
everywhere and familiar functions to convert back to familiar
strings for UX and storage compatibility.

Enforce that the source repository in the distribution metadata
is always a normalized string, ignore invalid values which are not.
Update distribution tests to use normalized values.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2017-01-25 16:54:18 -08:00
parent 2bea87393b
commit 3a1279393f
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
78 changed files with 538 additions and 1117 deletions

View File

@ -4,9 +4,9 @@ import (
"io"
"net/http"
"github.com/docker/distribution/reference"
enginetypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/reference"
"golang.org/x/net/context"
)

View File

@ -7,13 +7,12 @@ import (
"strconv"
"strings"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@ -47,39 +46,27 @@ func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig)
// be returned.
func parseRemoteRef(remote string) (reference.Named, string, error) {
// Parse remote reference, supporting remotes with name and tag
// NOTE: Using distribution reference to handle references
// containing both a name and digest
remoteRef, err := distreference.ParseNamed(remote)
remoteRef, err := reference.ParseNormalizedNamed(remote)
if err != nil {
return nil, "", err
}
var tag string
if t, ok := remoteRef.(distreference.Tagged); ok {
tag = t.Tag()
type canonicalWithTag interface {
reference.Canonical
Tag() string
}
// Convert distribution reference to docker reference
// TODO: remove when docker reference changes reconciled upstream
ref, err := reference.WithName(remoteRef.Name())
if err != nil {
return nil, "", err
}
if d, ok := remoteRef.(distreference.Digested); ok {
ref, err = reference.WithDigest(ref, d.Digest())
if canonical, ok := remoteRef.(canonicalWithTag); ok {
remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
if err != nil {
return nil, "", err
}
} else if tag != "" {
ref, err = reference.WithTag(ref, tag)
if err != nil {
return nil, "", err
}
} else {
ref = reference.WithDefaultTag(ref)
return remoteRef, canonical.Tag(), nil
}
return ref, tag, nil
remoteRef = reference.TagNameOnly(remoteRef)
return remoteRef, "", nil
}
func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
@ -188,24 +175,24 @@ func getName(ref reference.Named, tag, name string) (string, error) {
if err != nil {
return "", err
}
name = nt.String()
name = reference.FamiliarString(nt)
} else {
name = reference.WithDefaultTag(trimmed).String()
name = reference.FamiliarString(reference.TagNameOnly(trimmed))
}
} else {
name = ref.String()
name = reference.FamiliarString(ref)
}
} else {
localRef, err := reference.ParseNamed(name)
localRef, err := reference.ParseNormalizedNamed(name)
if err != nil {
return "", err
}
if _, ok := localRef.(reference.Canonical); ok {
return "", errors.New("cannot use digest in plugin tag")
}
if distreference.IsNameOnly(localRef) {
if reference.IsNameOnly(localRef) {
// TODO: log change in name to out stream
name = reference.WithDefaultTag(localRef).String()
name = reference.FamiliarString(reference.TagNameOnly(localRef))
}
}
return name, nil

View File

@ -1,34 +0,0 @@
package reference
import (
distreference "github.com/docker/distribution/reference"
)
// Parse parses the given references and returns the repository and
// tag (if present) from it. If there is an error during parsing, it will
// return an error.
func Parse(ref string) (string, string, error) {
distributionRef, err := distreference.ParseNamed(ref)
if err != nil {
return "", "", err
}
tag := GetTagFromNamedRef(distributionRef)
return distributionRef.Name(), tag, nil
}
// GetTagFromNamedRef returns a tag from the specified reference.
// This function is necessary as long as the docker "server" api makes the distinction between repository
// and tags.
func GetTagFromNamedRef(ref distreference.Named) string {
var tag string
switch x := ref.(type) {
case distreference.Digested:
tag = x.Digest().String()
case distreference.NamedTagged:
tag = x.Tag()
default:
tag = "latest"
}
return tag
}

View File

@ -1,73 +0,0 @@
package reference
import (
_ "crypto/sha256"
"testing"
)
func TestParse(t *testing.T) {
testCases := []struct {
ref string
expectedName string
expectedTag string
expectedError bool
}{
{
ref: "",
expectedName: "",
expectedTag: "",
expectedError: true,
},
{
ref: "repository",
expectedName: "repository",
expectedTag: "latest",
expectedError: false,
},
{
ref: "repository:tag",
expectedName: "repository",
expectedTag: "tag",
expectedError: false,
},
{
ref: "test.com/repository",
expectedName: "test.com/repository",
expectedTag: "latest",
expectedError: false,
},
{
ref: "test.com:5000/test/repository",
expectedName: "test.com:5000/test/repository",
expectedTag: "latest",
expectedError: false,
},
{
ref: "test.com:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
expectedName: "test.com:5000/repo",
expectedTag: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
expectedError: false,
},
{
ref: "test.com:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
expectedName: "test.com:5000/repo",
expectedTag: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
expectedError: false,
},
}
for _, c := range testCases {
name, tag, err := Parse(c.ref)
if err != nil && c.expectedError {
continue
} else if err != nil {
t.Fatalf("error with %s: %s", c.ref, err.Error())
}
if name != c.expectedName {
t.Fatalf("expected name %s, got %s", c.expectedName, name)
}
if tag != c.expectedTag {
t.Fatalf("expected tag %s, got %s", c.expectedTag, tag)
}
}
}

View File

@ -9,11 +9,11 @@ import (
"os"
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/image"
"github.com/docker/docker/reference"
"golang.org/x/net/context"
)

View File

@ -11,6 +11,7 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
apierrors "github.com/docker/docker/api/errors"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
@ -19,7 +20,6 @@ import (
"github.com/docker/docker/builder/dockerfile/parser"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/reference"
perrors "github.com/pkg/errors"
"golang.org/x/net/context"
)
@ -176,23 +176,16 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
continue
}
ref, err := reference.ParseNamed(repo)
ref, err := reference.ParseNormalizedNamed(repo)
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)
if err != nil {
return nil, err
}
}
ref = reference.TagNameOnly(ref)
nameWithTag := ref.String()

View File

@ -168,11 +168,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
return nil, err
}
if named, ok := ref.(reference.Named); ok {
if reference.IsNameOnly(named) {
namedRef = reference.EnsureTagged(named)
} else {
namedRef = named
}
namedRef = reference.TagNameOnly(named)
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
var err error

View File

@ -94,12 +94,12 @@ func (ctx *DiskUsageContext) Write() {
tag := "<none>"
if len(i.RepoTags) > 0 && !isDangling(*i) {
// Only show the first tag
ref, err := reference.ParseNamed(i.RepoTags[0])
ref, err := reference.ParseNormalizedNamed(i.RepoTags[0])
if err != nil {
continue
}
if nt, ok := ref.(reference.NamedTagged); ok {
repo = ref.Name()
repo = reference.FamiliarName(ref)
tag = nt.Tag()
}
}

View File

@ -94,7 +94,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
repoTags := map[string][]string{}
repoDigests := map[string][]string{}
for _, refString := range append(image.RepoTags) {
for _, refString := range image.RepoTags {
ref, err := reference.ParseNormalizedNamed(refString)
if err != nil {
continue
@ -104,7 +104,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
}
}
for _, refString := range append(image.RepoDigests) {
for _, refString := range image.RepoDigests {
ref, err := reference.ParseNormalizedNamed(refString)
if err != nil {
continue

View File

@ -5,7 +5,7 @@ import (
"strings"
"time"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli/command/inspect"
@ -409,11 +409,12 @@ func (c *serviceContext) Replicas() string {
func (c *serviceContext) Image() string {
c.AddHeader(imageHeader)
image := c.service.Spec.TaskTemplate.ContainerSpec.Image
if ref, err := distreference.ParseNamed(image); err == nil {
// update image string for display
namedTagged, ok := ref.(distreference.NamedTagged)
if ok {
image = namedTagged.Name() + ":" + namedTagged.Tag()
if ref, err := reference.ParseNormalizedNamed(image); err == nil {
// update image string for display, (strips any digest)
if nt, ok := ref.(reference.NamedTagged); ok {
if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
image = reference.FamiliarString(namedTagged)
}
}
}

View File

@ -397,9 +397,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
if err != nil {
return nil, nil, err
}
if reference.IsNameOnly(ref) {
ref = reference.EnsureTagged(ref)
}
ref = reference.TagNameOnly(ref)
if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
trustedRef, err := translator(ctx, ref)
if err != nil {

View File

@ -42,7 +42,6 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
}
func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
var distributionRef reference.Named
distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
if err != nil {
return err
@ -52,9 +51,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
}
if !opts.all && reference.IsNameOnly(distributionRef) {
taggedRef := reference.EnsureTagged(distributionRef)
fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag())
distributionRef = taggedRef
distributionRef = reference.TagNameOnly(distributionRef)
if tagged, ok := distributionRef.(reference.Tagged); ok {
fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag())
}
}
// Resolve the Repository name from fqn to RepositoryInfo

View File

@ -129,15 +129,15 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
// Initialize the notary repository with a remotely managed snapshot key
if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
return trust.NotaryError(repoInfo.FullName(), err)
return trust.NotaryError(repoInfo.Name.Name(), err)
}
fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.FullName())
fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.Name.Name())
err = repo.AddTarget(target, data.CanonicalTargetsRole)
case nil:
// already initialized and we have successfully downloaded the latest metadata
err = addTargetToAllSignableRoles(repo, target)
default:
return trust.NotaryError(repoInfo.FullName(), err)
return trust.NotaryError(repoInfo.Name.Name(), err)
}
if err == nil {
@ -145,11 +145,11 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
}
if err != nil {
fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
return trust.NotaryError(repoInfo.FullName(), err)
fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error())
return trust.NotaryError(repoInfo.Name.Name(), err)
}
fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.Name.Name(), tag)
return nil
}
@ -342,12 +342,12 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
if err != nil {
return nil, trust.NotaryError(repoInfo.FullName(), err)
return nil, trust.NotaryError(repoInfo.Name.Name(), err)
}
// Only list tags in the top level targets role or the releases delegation role - ignore
// all other delegation roles
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", ref.Tag()))
}
r, err := convertTarget(t.Target)
if err != nil {

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/image"
@ -54,20 +53,6 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
named, err := reference.ParseNormalizedNamed(ref.Name())
if err != nil {
return nil, err
}
repoInfo, err := registry.ParseRepositoryInfo(named)
if err != nil {
return nil, err
}
return repoInfo.Index, nil
}
type pluginRegistryService struct {
registry.Service
}
@ -104,9 +89,10 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu
_, isCanonical := ref.(reference.Canonical)
if command.IsTrusted() && !isCanonical {
ref = reference.TagNameOnly(ref)
nt, ok := ref.(reference.NamedTagged)
if !ok {
nt = reference.EnsureTagged(ref)
return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
}
ctx := context.Background()
@ -148,7 +134,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
if _, ok := aref.(reference.Canonical); ok {
return fmt.Errorf("invalid name: %s", opts.localName)
}
localName = reference.FamiliarString(reference.EnsureTagged(aref))
localName = reference.FamiliarString(reference.TagNameOnly(aref))
}
ctx := context.Background()

View File

@ -40,10 +40,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
return fmt.Errorf("invalid name: %s", name)
}
taggedRef, ok := named.(reference.NamedTagged)
if !ok {
taggedRef = reference.EnsureTagged(named)
}
named = reference.TagNameOnly(named)
ctx := context.Background()
@ -58,7 +55,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
return err
}
responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth)
responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(named), encodedAuth)
if err != nil {
return err
}

View File

@ -5,10 +5,10 @@ import (
"fmt"
"strings"
"github.com/docker/distribution/reference"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -49,19 +49,19 @@ func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
if opts.remote == "" {
opts.remote = p.PluginReference
}
remote, err := reference.ParseNamed(opts.remote)
remote, err := reference.ParseNormalizedNamed(opts.remote)
if err != nil {
return errors.Wrap(err, "error parsing remote upgrade image reference")
}
remote = reference.WithDefaultTag(remote)
remote = reference.TagNameOnly(remote)
old, err := reference.ParseNamed(p.PluginReference)
old, err := reference.ParseNormalizedNamed(p.PluginReference)
if err != nil {
return errors.Wrap(err, "error parsing current image reference")
}
old = reference.WithDefaultTag(old)
old = reference.TagNameOnly(old)
fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, old, remote)
fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
if !opts.skipRemoteCheck && remote.String() != old.String() {
if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
return errors.New("canceling upgrade request")

View File

@ -33,10 +33,12 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
namedRef, ok := ref.(reference.Named)
if !ok {
return errors.New("failed to resolve image digest using content trust: reference is not named")
}
taggedRef := reference.EnsureTagged(namedRef)
namedRef = reference.TagNameOnly(namedRef)
taggedRef, ok := namedRef.(reference.NamedTagged)
if !ok {
return errors.New("failed to resolve image digest using content trust: reference is not tagged")
}
resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
if err != nil {
@ -65,12 +67,12 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
if err != nil {
return nil, trust.NotaryError(repoInfo.FullName(), err)
return nil, trust.NotaryError(repoInfo.Name.Name(), err)
}
// Only get the tag if it's in the top level targets role or the releases delegation role
// ignore it if it's in any other delegation roles
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
}
logrus.Debugf("retrieving target for %s role\n", t.Role)

View File

@ -10,7 +10,7 @@ import (
"golang.org/x/net/context"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/idresolver"
@ -129,13 +129,15 @@ func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idr
image := task.Spec.ContainerSpec.Image
if !noTrunc {
ref, err := distreference.ParseNamed(image)
ref, err := reference.ParseNormalizedNamed(image)
if err == nil {
// update image string for display
namedTagged, ok := ref.(distreference.NamedTagged)
if ok {
image = namedTagged.Name() + ":" + namedTagged.Tag()
// update image string for display, (strips any digest)
if nt, ok := ref.(reference.NamedTagged); ok {
if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
image = reference.FamiliarString(namedTagged)
}
}
}
}

View File

@ -148,7 +148,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
}
scope := auth.RepositoryScope{
Repository: repoInfo.FullName(),
Repository: repoInfo.Name.Name(),
Actions: actions,
Class: repoInfo.Class,
}
@ -166,7 +166,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
return client.NewNotaryRepository(
trustDirectory(),
repoInfo.FullName(),
repoInfo.Name.Name(),
server,
tr,
getPassphraseRetriever(streams),

View File

@ -5,9 +5,8 @@ import (
"errors"
"net/url"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/reference"
"golang.org/x/net/context"
)
@ -15,17 +14,20 @@ import (
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
var repository, tag string
if options.Reference != "" {
distributionRef, err := distreference.ParseNamed(options.Reference)
ref, err := reference.ParseNormalizedNamed(options.Reference)
if err != nil {
return types.IDResponse{}, err
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
if _, isCanonical := ref.(reference.Canonical); isCanonical {
return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference")
}
ref = reference.TagNameOnly(ref)
tag = reference.GetTagFromNamedRef(distributionRef)
repository = distributionRef.Name()
if tagged, ok := ref.(reference.Tagged); ok {
tag = tagged.Tag()
}
repository = reference.FamiliarName(ref)
}
query := url.Values{}

View File

@ -6,21 +6,21 @@ import (
"golang.org/x/net/context"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/reference"
)
// ImageCreate creates a new image based in the parent options.
// It returns the JSON content in the response body.
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
repository, tag, err := reference.Parse(parentReference)
ref, err := reference.ParseNormalizedNamed(parentReference)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", repository)
query.Set("tag", tag)
query.Set("fromImage", reference.FamiliarName(ref))
query.Set("tag", getAPITagFromNamedRef(ref))
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if err != nil {
return nil, err

View File

@ -15,7 +15,7 @@ import (
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
if ref != "" {
//Check if the given image name can be resolved
if _, err := reference.ParseNamed(ref); err != nil {
if _, err := reference.ParseNormalizedNamed(ref); err != nil {
return nil, err
}
}

View File

@ -7,8 +7,8 @@ import (
"golang.org/x/net/context"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/reference"
)
// ImagePull requests the docker host to pull an image from a remote registry.
@ -19,16 +19,16 @@ import (
// FIXME(vdemeester): there is currently used in a few way in docker/docker
// - if not in trusted content, ref is used to pass the whole reference, and tag is empty
// - if in trusted content, ref is used to pass the reference name, and tag for the digest
func (cli *Client) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
repository, tag, err := reference.Parse(ref)
func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) {
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", repository)
if tag != "" && !options.All {
query.Set("tag", tag)
query.Set("fromImage", reference.FamiliarName(ref))
if !options.All {
query.Set("tag", getAPITagFromNamedRef(ref))
}
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
@ -44,3 +44,18 @@ func (cli *Client) ImagePull(ctx context.Context, ref string, options types.Imag
}
return resp.body, nil
}
// getAPITagFromNamedRef returns a tag from the specified reference.
// This function is necessary as long as the docker "server" api expects
// digests to be sent as tags and makes a distinction between the name
// and tag/digest part of a reference.
func getAPITagFromNamedRef(ref reference.Named) string {
if digested, ok := ref.(reference.Digested); ok {
return digested.Digest().String()
}
ref = reference.TagNameOnly(ref)
if tagged, ok := ref.(reference.Tagged); ok {
return tagged.Tag()
}
return ""
}

View File

@ -21,7 +21,7 @@ func TestImagePullReferenceParseError(t *testing.T) {
}
// An empty reference is an invalid reference
_, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
if err == nil || err.Error() != "repository name must have at least one component" {
if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
t.Fatalf("expected an error, got %v", err)
}
}

View File

@ -8,7 +8,7 @@ import (
"golang.org/x/net/context"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
)
@ -16,31 +16,33 @@ import (
// It executes the privileged function if the operation is unauthorized
// and it tries one more time.
// It's up to the caller to handle the io.ReadCloser and close it properly.
func (cli *Client) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
distributionRef, err := distreference.ParseNamed(ref)
func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) {
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return nil, err
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
if _, isCanonical := ref.(reference.Canonical); isCanonical {
return nil, errors.New("cannot push a digest reference")
}
var tag = ""
if nameTaggedRef, isNamedTagged := distributionRef.(distreference.NamedTagged); isNamedTagged {
tag := ""
name := reference.FamiliarName(ref)
if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
tag = nameTaggedRef.Tag()
}
query := url.Values{}
query.Set("tag", tag)
resp, err := cli.tryImagePush(ctx, distributionRef.Name(), query, options.RegistryAuth)
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil {
return nil, privilegeErr
}
resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader)
resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader)
}
if err != nil {
return nil, err

View File

@ -21,7 +21,7 @@ func TestImagePushReferenceError(t *testing.T) {
}
// An empty reference is an invalid reference
_, err := client.ImagePush(context.Background(), "", types.ImagePushOptions{})
if err == nil || err.Error() != "repository name must have at least one component" {
if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
t.Fatalf("expected an error, got %v", err)
}
// An canonical reference cannot be pushed

View File

@ -3,32 +3,33 @@ package client
import (
"net/url"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/reference"
"github.com/docker/distribution/reference"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
// ImageTag tags an image in the docker host
func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
if _, err := distreference.ParseNamed(source); err != nil {
if _, err := reference.ParseNormalizedNamed(source); err != nil {
return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
}
distributionRef, err := distreference.ParseNamed(target)
ref, err := reference.ParseNormalizedNamed(target)
if err != nil {
return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
}
if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
if _, isCanonical := ref.(reference.Canonical); isCanonical {
return errors.New("refusing to create a tag with a digest reference")
}
tag := reference.GetTagFromNamedRef(distributionRef)
ref = reference.TagNameOnly(ref)
query := url.Values{}
query.Set("repo", distributionRef.Name())
query.Set("tag", tag)
query.Set("repo", reference.FamiliarName(ref))
if tagged, ok := ref.(reference.Tagged); ok {
query.Set("tag", tagged.Tag())
}
resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
ensureReaderClosed(resp)

View File

@ -15,7 +15,7 @@ import (
// PluginInstall installs a plugin
func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
query := url.Values{}
if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
return nil, errors.Wrap(err, "invalid remote reference")
}
query.Set("remote", options.RemoteRef)

View File

@ -14,7 +14,7 @@ import (
// PluginUpgrade upgrades a plugin
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
query := url.Values{}
if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
return nil, errors.Wrap(err, "invalid remote reference")
}
query.Set("remote", options.RemoteRef)

View File

@ -52,7 +52,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
apierrors "github.com/docker/docker/api/errors"
apitypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
@ -66,13 +66,11 @@ import (
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/reference"
"github.com/docker/docker/runconfig"
swarmapi "github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/manager/encryption"
swarmnode "github.com/docker/swarmkit/node"
gogotypes "github.com/gogo/protobuf/types"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@ -829,50 +827,46 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
}
// imageWithDigestString takes an image such as name or name:tag
// and returns the image pinned to a digest, such as name@sha256:34234...
// Due to the difference between the docker/docker/reference, and the
// docker/distribution/reference packages, we're parsing the image twice.
// As the two packages converge, this function should be simplified.
// TODO(nishanttotla): After the packages converge, the function must
// convert distreference.Named -> distreference.Canonical, and the logic simplified.
// and returns the image pinned to a digest, such as name@sha256:34234
func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
if _, err := digest.Parse(image); err == nil {
return "", errors.New("image reference is an image ID")
}
ref, err := distreference.ParseNamed(image)
ref, err := reference.ParseAnyReference(image)
if err != nil {
return "", err
}
namedRef, ok := ref.(reference.Named)
if !ok {
if _, ok := ref.(reference.Digested); ok {
return "", errors.New("image reference is an image ID")
}
return "", errors.Errorf("unknown image reference format: %s", image)
}
// only query registry if not a canonical reference (i.e. with digest)
if _, ok := ref.(distreference.Canonical); !ok {
// create a docker/docker/reference Named object because GetRepository needs it
dockerRef, err := reference.ParseNamed(image)
if err != nil {
return "", err
}
dockerRef = reference.WithDefaultTag(dockerRef)
namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
if _, ok := namedRef.(reference.Canonical); !ok {
namedRef = reference.TagNameOnly(namedRef)
taggedRef, ok := namedRef.(reference.NamedTagged)
if !ok {
return "", errors.New("unable to cast image to NamedTagged reference object")
return "", errors.Errorf("image reference not tagged: %s", image)
}
repo, _, err := c.config.Backend.GetRepository(ctx, namedTaggedRef, authConfig)
repo, _, err := c.config.Backend.GetRepository(ctx, taggedRef, authConfig)
if err != nil {
return "", err
}
dscrptr, err := repo.Tags(ctx).Get(ctx, namedTaggedRef.Tag())
dscrptr, err := repo.Tags(ctx).Get(ctx, taggedRef.Tag())
if err != nil {
return "", err
}
namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
namedDigestedRef, err := reference.WithDigest(taggedRef, dscrptr.Digest)
if err != nil {
return "", err
}
return namedDigestedRef.String(), nil
// return familiar form until interface updated to return type
return reference.FamiliarString(namedDigestedRef), nil
}
// reference already contains a digest, so just return it
return ref.String(), nil
return reference.FamiliarString(ref), nil
}
// CreateService creates a new service in a managed swarm cluster.

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
@ -14,7 +15,6 @@ import (
swarmtypes "github.com/docker/docker/api/types/swarm"
clustertypes "github.com/docker/docker/daemon/cluster/provider"
"github.com/docker/docker/plugin"
"github.com/docker/docker/reference"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/cluster"
networktypes "github.com/docker/libnetwork/types"

View File

@ -11,13 +11,13 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/daemon/cluster/convert"
executorpkg "github.com/docker/docker/daemon/cluster/executor"
"github.com/docker/docker/reference"
"github.com/docker/libnetwork"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api"
@ -60,7 +60,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
// Skip pulling if the image is referenced by digest and already
// exists locally.
named, err := reference.ParseNamed(spec.Image)
named, err := reference.ParseNormalizedNamed(spec.Image)
if err == nil {
if _, ok := named.(reference.Canonical); ok {
_, err := c.backend.LookupImage(spec.Image)

View File

@ -10,6 +10,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
enginecontainer "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
@ -18,7 +19,6 @@ import (
"github.com/docker/docker/api/types/network"
volumetypes "github.com/docker/docker/api/types/volume"
clustertypes "github.com/docker/docker/daemon/cluster/provider"
"github.com/docker/docker/reference"
"github.com/docker/go-connections/nat"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api"
@ -132,11 +132,11 @@ func (c *containerConfig) name() string {
func (c *containerConfig) image() string {
raw := c.spec().Image
ref, err := reference.ParseNamed(raw)
ref, err := reference.ParseNormalizedNamed(raw)
if err != nil {
return raw
}
return reference.WithDefaultTag(ref).String()
return reference.FamiliarString(reference.TagNameOnly(ref))
}
func (c *containerConfig) portBindings() nat.PortMap {

View File

@ -2,12 +2,12 @@ package daemon
import (
"encoding/json"
"fmt"
"io"
"runtime"
"strings"
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/backend"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/builder/dockerfile"
@ -16,7 +16,7 @@ import (
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
)
// merge merges two Config, the image container configuration (defaults values),
@ -128,7 +128,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
// It is not possible to commit a running container on Windows and on Solaris.
if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() {
return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS)
return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
}
if c.Pause && !container.IsPaused() {
@ -228,10 +228,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
imageRef := ""
if c.Repo != "" {
newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
newTag, err := reference.ParseNormalizedNamed(c.Repo) // todo: should move this to API layer
if err != nil {
return "", err
}
if !reference.IsNameOnly(newTag) {
return "", errors.Errorf("unexpected repository name: %s", c.Repo)
}
if c.Tag != "" {
if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
return "", err
@ -240,7 +243,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
if err := daemon.TagImageWithReference(id, newTag); err != nil {
return "", err
}
imageRef = newTag.String()
imageRef = reference.FamiliarString(newTag)
}
attributes := map[string]string{

View File

@ -46,7 +46,7 @@ import (
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/plugin"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
volumedrivers "github.com/docker/docker/volume/drivers"
@ -76,7 +76,7 @@ type Daemon struct {
repository string
containers container.Store
execCommands *exec.Store
referenceStore reference.Store
referenceStore refstore.Store
downloadManager *xfer.LayerDownloadManager
uploadManager *xfer.LayerUploadManager
distributionMetadataStore dmetadata.Store
@ -637,7 +637,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
eventsService := events.New()
referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
}

View File

@ -16,7 +16,7 @@ import (
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/docker/libnetwork"
nwconfig "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/drivers/solaris/bridge"
@ -491,7 +491,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container
return daemon.Unmount(container)
}
func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
func restoreCustomImage(is image.Store, ls layer.Store, rs refstore.Store) error {
// Solaris has no custom images to register
return nil
}

View File

@ -2,29 +2,13 @@ package daemon
import (
"fmt"
"strings"
"github.com/docker/docker/api/errors"
"github.com/docker/docker/reference"
)
func (d *Daemon) imageNotExistToErrcode(err error) error {
if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
if strings.Contains(dne.RefOrID, "@") {
e := fmt.Errorf("No such image: %s", dne.RefOrID)
return errors.NewRequestNotFoundError(e)
}
tag := reference.DefaultTag
ref, err := reference.ParseNamed(dne.RefOrID)
if err != nil {
e := fmt.Errorf("No such image: %s:%s", dne.RefOrID, tag)
return errors.NewRequestNotFoundError(e)
}
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
tag = tagged.Tag()
}
e := fmt.Errorf("No such image: %s:%s", ref.Name(), tag)
return errors.NewRequestNotFoundError(e)
return errors.NewRequestNotFoundError(dne)
}
return err
}

View File

@ -1,9 +1,9 @@
package events
import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/reference"
)
// Filter can filter out docker events from a stream
@ -102,9 +102,9 @@ func (ef *Filter) matchImage(ev events.Message) bool {
}
func stripTag(image string) string {
ref, err := reference.ParseNamed(image)
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return image
}
return ref.Name()
return reference.FamiliarName(ref)
}

View File

@ -3,45 +3,55 @@ package daemon
import (
"fmt"
"github.com/docker/distribution/reference"
"github.com/docker/docker/builder"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/reference"
)
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
type ErrImageDoesNotExist struct {
RefOrID string
ref reference.Reference
}
func (e ErrImageDoesNotExist) Error() string {
return fmt.Sprintf("no such id: %s", e.RefOrID)
ref := e.ref
if named, ok := ref.(reference.Named); ok {
ref = reference.TagNameOnly(named)
}
return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
}
// GetImageID returns an image ID corresponding to the image referred to by
// refOrID.
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
id, ref, err := reference.ParseIDOrReference(refOrID)
ref, err := reference.ParseAnyReference(refOrID)
if err != nil {
return "", err
}
if id != "" {
if _, err := daemon.imageStore.Get(image.IDFromDigest(id)); err != nil {
return "", ErrImageDoesNotExist{refOrID}
namedRef, ok := ref.(reference.Named)
if !ok {
digested, ok := ref.(reference.Digested)
if !ok {
return "", ErrImageDoesNotExist{ref}
}
return image.IDFromDigest(id), nil
id := image.IDFromDigest(digested.Digest())
if _, err := daemon.imageStore.Get(id); err != nil {
return "", ErrImageDoesNotExist{ref}
}
return id, nil
}
if id, err := daemon.referenceStore.Get(ref); err == nil {
if id, err := daemon.referenceStore.Get(namedRef); err == nil {
return image.IDFromDigest(id), nil
}
// deprecated: repo:shortid https://github.com/docker/docker/pull/799
if tagged, ok := ref.(reference.NamedTagged); ok {
if tagged, ok := namedRef.(reference.Tagged); ok {
if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
if id, err := daemon.imageStore.Search(tag); err == nil {
for _, namedRef := range daemon.referenceStore.References(id.Digest()) {
if namedRef.Name() == ref.Name() {
for _, storeRef := range daemon.referenceStore.References(id.Digest()) {
if storeRef.Name() == namedRef.Name() {
return id, nil
}
}
@ -54,7 +64,7 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
return id, nil
}
return "", ErrImageDoesNotExist{refOrID}
return "", ErrImageDoesNotExist{ref}
}
// GetImage returns an image corresponding to the image referred to by refOrID.

View File

@ -5,12 +5,12 @@ import (
"strings"
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/errors"
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/reference"
)
type conflictType int
@ -89,7 +89,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
}
}
parsedRef, err := reference.ParseNamed(imageRef)
parsedRef, err := reference.ParseNormalizedNamed(imageRef)
if err != nil {
return nil, err
}
@ -99,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, err
}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord)
@ -126,7 +126,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return records, err
}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: repoRef.String()}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(repoRef)}
records = append(records, untaggedRecord)
} else {
remainingRefs = append(remainingRefs, repoRef)
@ -162,7 +162,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, err
}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord)
@ -232,7 +232,8 @@ 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) {
ref = reference.WithDefaultTag(ref)
ref = reference.TagNameOnly(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.
@ -255,7 +256,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty
return err
}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
*records = append(*records, untaggedRecord)

View File

@ -4,9 +4,9 @@ import (
"fmt"
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
)
// ImageHistory returns a slice of ImageHistory structures for the specified image
@ -64,7 +64,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
var tags []string
for _, r := range daemon.referenceStore.References(id.Digest()) {
if _, ok := r.(reference.NamedTagged); ok {
tags = append(tags, r.String())
tags = append(tags, reference.FamiliarString(r))
}
}

View File

@ -3,9 +3,9 @@ package daemon
import (
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
)
@ -23,9 +23,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
for _, ref := range refs {
switch ref.(type) {
case reference.NamedTagged:
repoTags = append(repoTags, ref.String())
repoTags = append(repoTags, reference.FamiliarString(ref))
case reference.Canonical:
repoDigests = append(repoDigests, ref.String())
repoDigests = append(repoDigests, reference.FamiliarString(ref))
}
}

View File

@ -5,12 +5,12 @@ import (
"strings"
dist "github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/builder"
"github.com/docker/docker/distribution"
progressutils "github.com/docker/docker/distribution/utils"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"golang.org/x/net/context"
@ -24,7 +24,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
// compatibility.
image = strings.TrimSuffix(image, ":")
ref, err := reference.ParseNamed(image)
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return err
}
@ -48,11 +48,11 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
// PullOnBuild tells Docker to pull image referenced by `name`.
func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
ref, err := reference.ParseNamed(name)
ref, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, err
}
ref = reference.WithDefaultTag(ref)
ref = reference.TagNameOnly(ref)
pullRegistryAuth := &types.AuthConfig{}
if len(authConfigs) > 0 {
@ -118,12 +118,12 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagg
return nil, false, err
}
// makes sure name is not empty or `scratch`
if err := distribution.ValidateRepoName(repoInfo.Name()); err != nil {
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
return nil, false, err
}
// get endpoints
endpoints, err := daemon.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
if err != nil {
return nil, false, err
}

View File

@ -4,17 +4,17 @@ import (
"io"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/distribution"
progressutils "github.com/docker/docker/distribution/utils"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
"golang.org/x/net/context"
)
// PushImage initiates a push operation on the repository named localName.
func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
ref, err := reference.ParseNamed(image)
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return err
}

View File

@ -1,8 +1,8 @@
package daemon
import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/image"
"github.com/docker/docker/reference"
)
// TagImage creates the tag specified by newTag, pointing to the image named
@ -13,12 +13,12 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
return err
}
newTag, err := reference.WithName(repository)
newTag, err := reference.ParseNormalizedNamed(repository)
if err != nil {
return err
}
if tag != "" {
if newTag, err = reference.WithTag(newTag, tag); err != nil {
if newTag, err = reference.WithTag(reference.TrimNamed(newTag), tag); err != nil {
return err
}
}
@ -32,6 +32,6 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.N
return err
}
daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
return nil
}

View File

@ -135,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
var found bool
var matchErr error
for _, pattern := range imageFilters.Get("reference") {
found, matchErr = reference.Match(pattern, ref)
found, matchErr = reference.FamiliarMatch(pattern, ref)
if matchErr != nil {
return nil, matchErr
}
@ -145,10 +145,10 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
}
}
if _, ok := ref.(reference.Canonical); ok {
newImage.RepoDigests = append(newImage.RepoDigests, ref.String())
newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
}
if _, ok := ref.(reference.NamedTagged); ok {
newImage.RepoTags = append(newImage.RepoTags, ref.String())
newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
}
}
if newImage.RepoDigests == nil && newImage.RepoTags == nil {

View File

@ -2,13 +2,13 @@ package daemon
import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"runtime"
"time"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/builder/dockerfile"
"github.com/docker/docker/dockerversion"
@ -18,7 +18,7 @@ import (
"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/pkg/errors"
)
// ImportImage imports an image, getting the archived layer data either from
@ -35,11 +35,10 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string
if repository != "" {
var err error
newRef, err = reference.ParseNamed(repository)
newRef, err = reference.ParseNormalizedNamed(repository)
if err != nil {
return err
}
if _, isCanonical := newRef.(reference.Canonical); isCanonical {
return errors.New("cannot import digest reference")
}

View File

@ -6,13 +6,13 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
timetypes "github.com/docker/docker/api/types/time"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/directory"
"github.com/docker/docker/reference"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume"
"github.com/docker/libnetwork"

View File

@ -14,7 +14,7 @@ import (
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/libtrust"
"github.com/opencontainers/go-digest"
@ -44,7 +44,7 @@ type Config struct {
ImageStore ImageConfigStore
// ReferenceStore manages tags. This value is optional, when excluded
// content will not be tagged.
ReferenceStore reference.Store
ReferenceStore refstore.Store
// RequireSchema2 ensures that only schema2 manifests are used.
RequireSchema2 bool
}

View File

@ -7,12 +7,12 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/client"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
)
@ -78,11 +78,11 @@ func TranslatePullError(err error, ref reference.Named) error {
switch v.Code {
case errcode.ErrorCodeDenied:
// ErrorCodeDenied is used when access to the repository was denied
newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name())
newErr = errors.Errorf("repository %s not found: does not exist or no pull access", reference.FamiliarName(ref))
case v2.ErrorCodeManifestUnknown:
newErr = errors.Errorf("manifest for %s not found", ref.String())
newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
case v2.ErrorCodeNameUnknown:
newErr = errors.Errorf("repository %s not found", ref.Name())
newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
}
if newErr != nil {
logrus.Infof("Translating %q to %q", err, newErr)

View File

@ -1,14 +1,14 @@
package distribution
import (
"errors"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"golang.org/x/net/context"
@ -56,12 +56,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
return err
}
// makes sure name is not empty or `scratch`
if err := ValidateRepoName(repoInfo.Name()); err != nil {
// makes sure name is not `scratch`
if err := ValidateRepoName(repoInfo.Name); err != nil {
return err
}
endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
if err != nil {
return err
}
@ -105,7 +105,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
}
}
logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version)
puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
if err != nil {
@ -147,12 +147,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
return TranslatePullError(err, ref)
}
imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
return nil
}
if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref))
}
return TranslatePullError(lastErr, ref)
@ -171,17 +171,14 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool
}
// ValidateRepoName validates the name of a repository.
func ValidateRepoName(name string) error {
if name == "" {
return errors.New("Repository name can't be empty")
}
if name == api.NoBaseImageSpecifier {
func ValidateRepoName(name reference.Named) error {
if reference.FamiliarName(name) == api.NoBaseImageSpecifier {
return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
}
return nil
}
func addDigestReference(store reference.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
func addDigestReference(store refstore.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
if err != nil {
return err
@ -193,7 +190,7 @@ func addDigestReference(store reference.Store, ref reference.Named, dgst digest.
logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id)
}
return nil
} else if err != reference.ErrDoesNotExist {
} else if err != refstore.ErrDoesNotExist {
return err
}

View File

@ -12,6 +12,7 @@ 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"
@ -22,7 +23,6 @@ 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"
)
@ -67,23 +67,23 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
// TODO(dmcgowan): Check if should fallback
return err
}
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.")
progress.Message(p.config.ProgressOutput, "", p.repoInfo.Name.Name()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.")
return nil
}
func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName())
progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name())
tagged, isTagged := ref.(reference.NamedTagged)
repoData, err := p.session.GetRepositoryData(p.repoInfo)
repoData, err := p.session.GetRepositoryData(p.repoInfo.Name)
if err != nil {
if strings.Contains(err.Error(), "HTTP code: 404") {
if isTagged {
return fmt.Errorf("Error: image %s:%s not found", p.repoInfo.RemoteName(), tagged.Tag())
return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag())
}
return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name))
}
// Unexpected HTTP error
return err
@ -92,13 +92,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
logrus.Debug("Retrieving the tag list")
var tagsList map[string]string
if !isTagged {
tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name)
} else {
var tagID string
tagsList = make(map[string]string)
tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag())
tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag())
if err == registry.ErrRepoNotFound {
return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name())
}
tagsList[tagged.Tag()] = tagID
}
@ -127,7 +127,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
}
}
writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
return nil
}
@ -137,7 +137,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
return nil
}
localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag)
if err != nil {
retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
logrus.Debug(retErr.Error())
@ -148,15 +148,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.FullName())
progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name())
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.FullName(), ep))
progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.Name.Name(), 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.FullName(), ep, err)
logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
continue
}
success = true
@ -164,12 +164,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.FullName(), ep)
progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), 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.FullName(), ep, err)
progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
continue
}
success = true
@ -177,7 +177,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.FullName(), lastErr)
err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr)
progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
return err
}

View File

@ -15,6 +15,7 @@ import (
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/transport"
@ -26,7 +27,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"
refstore "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"golang.org/x/net/context"
@ -124,7 +125,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
}
}
writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
return nil
}
@ -317,7 +318,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error {
func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
// Cache mapping from this layer's DiffID to the blobsum
ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
}
func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
@ -343,7 +344,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
}
tagOrDigest = digested.Digest().String()
} else {
return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
}
if manifest == nil {
@ -371,8 +372,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
// the other side speaks the v2 protocol.
p.confirmedV2 = true
logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Named().Name())
logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
var (
id digest.Digest
@ -410,7 +411,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
if oldTagID == id {
return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
}
} else if err != reference.ErrDoesNotExist {
} else if err != refstore.ErrDoesNotExist {
return false, err
}
@ -802,13 +803,13 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference
m = &signedManifest.Manifest
if m.SchemaVersion != 1 {
return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
}
if len(m.FSLayers) != len(m.History) {
return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
}
if len(m.FSLayers) == 0 {
return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
}
return m, nil
}

View File

@ -9,7 +9,7 @@ import (
"testing"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/docker/reference"
"github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
)
@ -113,7 +113,7 @@ func TestValidateManifest(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Needs fixing on Windows")
}
expectedDigest, err := reference.ParseNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
expectedDigest, err := reference.ParseNormalizedNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
if err != nil {
t.Fatal("could not parse reference")
}

View File

@ -7,9 +7,9 @@ import (
"io"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"golang.org/x/net/context"
)
@ -64,16 +64,16 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
return err
}
endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.Hostname())
endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name))
if err != nil {
return err
}
progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName())
progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.Name.Name())
associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo)
associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.Name)
if len(associations) == 0 {
return fmt.Errorf("An image does not exist locally with the tag: %s", repoInfo.Name())
return fmt.Errorf("An image does not exist locally with the tag: %s", reference.FamiliarName(repoInfo.Name))
}
var (
@ -106,7 +106,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
}
}
logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name.Name(), endpoint.URL, endpoint.Version)
pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
if err != nil {
@ -135,12 +135,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
return err
}
imagePushConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "push")
imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push")
return nil
}
if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.Name.Name())
}
return lastErr
}

View File

@ -5,6 +5,7 @@ import (
"sync"
"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/dockerversion"
@ -14,7 +15,6 @@ 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/opencontainers/go-digest"
"golang.org/x/net/context"
@ -356,8 +356,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()+"/tags/"+tag)
if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag)
if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil {
return err
}
}
@ -385,7 +385,7 @@ 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, imageIndex, false, nil)
repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil)
if err != nil {
return err
}
@ -395,7 +395,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
return err
}
}
_, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints)
_, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints)
return err
}

View File

@ -15,7 +15,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client"
apitypes "github.com/docker/docker/api/types"
"github.com/docker/docker/distribution/metadata"
@ -24,7 +24,6 @@ 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/opencontainers/go-digest"
)
@ -83,7 +82,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
imageID, err := p.config.ReferenceStore.Get(p.ref)
if err != nil {
return fmt.Errorf("tag does not exist: %s", p.ref.String())
return fmt.Errorf("tag does not exist: %s", reference.FamiliarString(p.ref))
}
return p.pushV2Tag(ctx, namedTagged, imageID)
@ -105,23 +104,23 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
}
if pushed == 0 {
return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
return fmt.Errorf("no tags to push for %s", reference.FamiliarName(p.repoInfo.Name))
}
return nil
}
func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error {
logrus.Debugf("Pushing repository: %s", ref.String())
logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref))
imgConfig, err := p.config.ImageStore.Get(id)
if err != nil {
return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
}
rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
if err != nil {
return fmt.Errorf("unable to get rootfs for image %s: %s", ref.String(), err)
return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
}
l, err := p.config.LayerStore.Get(rootfs.ChainID())
@ -140,7 +139,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
descriptorTemplate := v2PushDescriptor{
v2MetadataService: p.v2MetadataService,
hmacKey: hmacKey,
repoInfo: p.repoInfo,
repoInfo: p.repoInfo.Name,
ref: p.ref,
repo: p.repo,
pushState: &p.pushState,
@ -181,7 +180,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag())
manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
if err != nil {
return err
}
@ -248,7 +247,7 @@ type v2PushDescriptor struct {
}
func (pd *v2PushDescriptor) Key() string {
return "v2push:" + pd.ref.FullName() + " " + pd.layer.DiffID().String()
return "v2push:" + pd.ref.Name() + " " + pd.layer.DiffID().String()
}
func (pd *v2PushDescriptor) ID() string {
@ -304,23 +303,22 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
createOpts := []distribution.BlobCreateOption{}
if len(mountCandidate.SourceRepository) > 0 {
namedRef, err := reference.WithName(mountCandidate.SourceRepository)
namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository)
if err != nil {
logrus.Errorf("failed to parse source repository reference %v: %v", namedRef.String(), err)
logrus.Errorf("failed to parse source repository reference %v: %v", reference.FamiliarString(namedRef), err)
pd.v2MetadataService.Remove(mountCandidate)
continue
}
// TODO (brianbland): We need to construct a reference where the Name is
// only the full remote name, so clean this up when distribution has a
// richer reference package
remoteRef, err := distreference.WithName(namedRef.RemoteName())
// Candidates are always under same domain, create remote reference
// with only path to set mount from with
remoteRef, err := reference.WithName(reference.Path(namedRef))
if err != nil {
logrus.Errorf("failed to make remote reference out of %q: %v", namedRef.RemoteName(), namedRef.RemoteName())
logrus.Errorf("failed to make remote reference out of %q: %v", reference.Path(namedRef), err)
continue
}
canonicalRef, err := distreference.WithDigest(distreference.TrimNamed(remoteRef), mountCandidate.Digest)
canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest)
if err != nil {
logrus.Errorf("failed to make canonical reference: %v", err)
continue
@ -347,7 +345,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
// Cache mapping from this layer's DiffID to the blobsum
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
Digest: err.Descriptor.Digest,
SourceRepository: pd.repoInfo.FullName(),
SourceRepository: pd.repoInfo.Name(),
}); err != nil {
return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
}
@ -455,7 +453,7 @@ func (pd *v2PushDescriptor) uploadUsingSession(
// Cache mapping from this layer's DiffID to the blobsum
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
Digest: pushDigest,
SourceRepository: pd.repoInfo.FullName(),
SourceRepository: pd.repoInfo.Name(),
}); err != nil {
return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
}
@ -490,7 +488,7 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
// filter the metadata
candidates := []metadata.V2Metadata{}
for _, meta := range v2Metadata {
if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.FullName() {
if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.Name() {
continue
}
candidates = append(candidates, meta)
@ -521,16 +519,16 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
attempts:
for _, dgst := range layerDigests {
meta := digestToMetadata[dgst]
logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
desc, err = pd.repo.Blobs(ctx).Stat(ctx, dgst)
pd.checkedDigests[meta.Digest] = struct{}{}
switch err {
case nil:
if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.FullName() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.Name() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
// cache mapping from this layer's DiffID to the blobsum
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
Digest: desc.Digest,
SourceRepository: pd.repoInfo.FullName(),
SourceRepository: pd.repoInfo.Name(),
}); err != nil {
return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
}
@ -539,12 +537,12 @@ attempts:
exists = true
break attempts
case distribution.ErrBlobUnknown:
if meta.SourceRepository == pd.repoInfo.FullName() {
if meta.SourceRepository == pd.repoInfo.Name() {
// remove the mapping to the target repository
pd.v2MetadataService.Remove(*meta)
}
default:
logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
}
}
@ -598,11 +596,11 @@ func getRepositoryMountCandidates(
candidates := []metadata.V2Metadata{}
for _, meta := range v2Metadata {
sourceRepo, err := reference.ParseNamed(meta.SourceRepository)
if err != nil || repoInfo.Hostname() != sourceRepo.Hostname() {
if err != nil || reference.Domain(repoInfo) != reference.Domain(sourceRepo) {
continue
}
// target repository is not a viable candidate
if meta.SourceRepository == repoInfo.FullName() {
if meta.SourceRepository == repoInfo.Name() {
continue
}
candidates = append(candidates, meta)
@ -653,7 +651,7 @@ func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, ma
sort.Stable(byLikeness{
arr: marr,
hmacKey: hmacKey,
pathComponents: getPathComponents(repoInfo.FullName()),
pathComponents: getPathComponents(repoInfo.Name()),
})
}
@ -670,11 +668,6 @@ func numOfMatchingPathComponents(pth string, matchComponents []string) int {
}
func getPathComponents(path string) []string {
// make sure to add docker.io/ prefix to the path
named, err := reference.ParseNamed(path)
if err == nil {
path = named.FullName()
}
return strings.Split(path, "/")
}

View File

@ -8,11 +8,10 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/context"
"github.com/docker/distribution/manifest/schema2"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/reference"
"github.com/opencontainers/go-digest"
)
@ -43,8 +42,8 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
name: "one item matching",
targetRepo: "busybox",
maxCandidates: -1,
metadata: []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
candidates: []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
metadata: []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
candidates: []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
},
{
name: "allow missing SourceRepository",
@ -63,13 +62,13 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
maxCandidates: -1,
metadata: []metadata.V2Metadata{
{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
{Digest: digest.Digest("2"), SourceRepository: "app"},
{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
},
candidates: []metadata.V2Metadata{
{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
{Digest: digest.Digest("2"), SourceRepository: "app"},
{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
},
},
{
@ -78,10 +77,10 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
targetRepo: "127.0.0.1/foo/bar",
maxCandidates: -1,
metadata: []metadata.V2Metadata{
taggedMetadata("hash", "1", "hello-world"),
taggedMetadata("hash", "1", "docker.io/library/hello-world"),
taggedMetadata("efgh", "2", "127.0.0.1/hello-world"),
taggedMetadata("abcd", "3", "busybox"),
taggedMetadata("hash", "4", "busybox"),
taggedMetadata("abcd", "3", "docker.io/library/busybox"),
taggedMetadata("hash", "4", "docker.io/library/busybox"),
taggedMetadata("hash", "5", "127.0.0.1/foo"),
taggedMetadata("hash", "6", "127.0.0.1/bar"),
taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
@ -105,23 +104,25 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
targetRepo: "user/app",
maxCandidates: 3,
metadata: []metadata.V2Metadata{
taggedMetadata("abcd", "1", "user/app1"),
taggedMetadata("abcd", "2", "user/app/base"),
taggedMetadata("hash", "3", "user/app"),
taggedMetadata("abcd", "1", "docker.io/user/app1"),
taggedMetadata("abcd", "2", "docker.io/user/app/base"),
taggedMetadata("hash", "3", "docker.io/user/app"),
taggedMetadata("abcd", "4", "127.0.0.1/user/app"),
taggedMetadata("hash", "5", "user/foo"),
taggedMetadata("hash", "6", "app/bar"),
taggedMetadata("hash", "5", "docker.io/user/foo"),
taggedMetadata("hash", "6", "docker.io/app/bar"),
},
candidates: []metadata.V2Metadata{
// first by matching hash
taggedMetadata("abcd", "2", "user/app/base"),
taggedMetadata("abcd", "1", "user/app1"),
taggedMetadata("abcd", "2", "docker.io/user/app/base"),
taggedMetadata("abcd", "1", "docker.io/user/app1"),
// then by longest matching prefix
taggedMetadata("hash", "3", "user/app"),
// "docker.io/usr/app" is excluded since candidates must
// be from a different repository
taggedMetadata("hash", "5", "docker.io/user/foo"),
},
},
} {
repoInfo, err := reference.ParseNamed(tc.targetRepo)
repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
if err != nil {
t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
}
@ -204,10 +205,13 @@ func TestLayerAlreadyExists(t *testing.T) {
{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
{Digest: digest.Digest("plum"), SourceRepository: "busybox"},
{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
},
expectedRequests: []string{"plum", "pear", "apple", "orange", "banana"},
expectedRequests: []string{"plum", "apple", "pear", "orange", "banana"},
expectedRemovals: []metadata.V2Metadata{
{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
},
},
{
name: "find existing blob",
@ -374,7 +378,7 @@ func TestLayerAlreadyExists(t *testing.T) {
},
},
} {
repoInfo, err := reference.ParseNamed(tc.targetRepo)
repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
if err != nil {
t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
}
@ -476,7 +480,7 @@ type mockRepo struct {
var _ distribution.Repository = &mockRepo{}
func (m *mockRepo) Named() distreference.Named {
func (m *mockRepo) Named() reference.Named {
m.t.Fatalf("Named() not implemented")
return nil
}

View File

@ -8,7 +8,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/manifest/schema2"
distreference "github.com/docker/distribution/reference"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/transport"
@ -55,10 +55,10 @@ func init() {
// providing timeout settings and authentication support, and also verifies the
// remote API version.
func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
repoName := repoInfo.FullName()
repoName := repoInfo.Name.Name()
// If endpoint does not support CanonicalName, use the RemoteName instead
if endpoint.TrimHostname {
repoName = repoInfo.RemoteName()
repoName = reference.Path(repoInfo.Name)
}
direct := &net.Dialer{
@ -122,7 +122,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
}
tr := transport.NewTransport(base, modifiers...)
repoNameRef, err := distreference.ParseNamed(repoName)
repoNameRef, err := reference.WithName(repoName)
if err != nil {
return nil, foundVersion, fallbackError{
err: err,

View File

@ -12,11 +12,11 @@ import (
"testing"
"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/archive"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"golang.org/x/net/context"
)
@ -61,9 +61,9 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
TrimHostname: false,
TLSConfig: nil,
}
n, _ := reference.ParseNamed("testremotename")
n, _ := reference.ParseNormalizedNamed("testremotename")
repoInfo := &registry.RepositoryInfo{
Named: n,
Name: n,
Index: &registrytypes.IndexInfo{
Name: "testrepo",
Mirrors: nil,

View File

@ -11,6 +11,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/docker/docker/image"
"github.com/docker/docker/image/v1"
"github.com/docker/docker/layer"
@ -21,7 +22,6 @@ import (
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/reference"
"github.com/opencontainers/go-digest"
)
@ -117,7 +117,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
imageRefCount = 0
for _, repoTag := range m.RepoTags {
named, err := reference.ParseNamed(repoTag)
named, err := reference.ParseNormalizedNamed(repoTag)
if err != nil {
return err
}
@ -126,7 +126,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
return fmt.Errorf("invalid tag %q", repoTag)
}
l.setLoadedTag(ref, imgID.Digest(), outStream)
outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", ref)))
outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", reference.FamiliarString(ref))))
imageRefCount++
}
@ -201,7 +201,7 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
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
fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", reference.FamiliarString(ref), string(prevID)) // todo: this message is wrong in case of multiple tags
}
if err := l.rs.AddTag(ref, imgID, true); err != nil {
@ -249,7 +249,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut
if !ok {
return fmt.Errorf("invalid target ID: %v", oldID)
}
named, err := reference.WithName(name)
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return err
}

View File

@ -10,12 +10,12 @@ import (
"time"
"github.com/docker/distribution"
"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/system"
"github.com/docker/docker/reference"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
@ -51,16 +51,12 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
}
if ref != nil {
var tagged reference.NamedTagged
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, reference.DefaultTag); err != nil {
return
}
tagged, ok := reference.TagNameOnly(ref).(reference.NamedTagged)
if !ok {
return
}
for _, t := range imgDescr[id].refs {
@ -73,19 +69,26 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
}
for _, name := range names {
id, ref, err := reference.ParseIDOrReference(name)
ref, err := reference.ParseAnyReference(name)
if err != nil {
return nil, err
}
if id != "" {
_, err := l.is.Get(image.IDFromDigest(id))
if err != nil {
return nil, err
namedRef, ok := ref.(reference.Named)
if !ok {
// Check if digest ID reference
if digested, ok := ref.(reference.Digested); ok {
id := image.IDFromDigest(digested.Digest())
_, err := l.is.Get(id)
if err != nil {
return nil, err
}
addAssoc(id, nil)
continue
}
addAssoc(image.IDFromDigest(id), nil)
continue
return nil, errors.Errorf("invalid reference: %v", name)
}
if ref.Name() == string(digest.Canonical) {
if reference.FamiliarName(namedRef) == string(digest.Canonical) {
imgID, err := l.is.Search(name)
if err != nil {
return nil, err
@ -93,8 +96,8 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
addAssoc(imgID, nil)
continue
}
if reference.IsNameOnly(ref) {
assocs := l.rs.ReferencesByName(ref)
if reference.IsNameOnly(namedRef) {
assocs := l.rs.ReferencesByName(namedRef)
for _, assoc := range assocs {
addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref)
}
@ -107,11 +110,11 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
}
continue
}
id, err = l.rs.Get(ref)
id, err := l.rs.Get(namedRef)
if err != nil {
return nil, err
}
addAssoc(image.IDFromDigest(id), ref)
addAssoc(image.IDFromDigest(id), namedRef)
}
return imgDescr, nil
@ -144,11 +147,12 @@ func (s *saveSession) save(outStream io.Writer) error {
var layers []string
for _, ref := range imageDescr.refs {
if _, ok := reposLegacy[ref.Name()]; !ok {
reposLegacy[ref.Name()] = make(map[string]string)
familiarName := reference.FamiliarName(ref)
if _, ok := reposLegacy[familiarName]; !ok {
reposLegacy[familiarName] = make(map[string]string)
}
reposLegacy[ref.Name()][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
repoTags = append(repoTags, ref.String())
reposLegacy[familiarName][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
repoTags = append(repoTags, reference.FamiliarString(ref))
}
for _, l := range imageDescr.layers {

View File

@ -4,7 +4,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
)
const (
@ -26,7 +26,7 @@ type manifestItem struct {
type tarexporter struct {
is image.Store
ls layer.Store
rs reference.Store
rs refstore.Store
loggerImgEvent LogImageEvent
}
@ -37,7 +37,7 @@ type LogImageEvent interface {
}
// NewTarExporter returns new Exporter for tar packages
func NewTarExporter(is image.Store, ls layer.Store, rs reference.Store, loggerImgEvent LogImageEvent) image.Exporter {
func NewTarExporter(is image.Store, ls layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
return &tarexporter{
is: is,
ls: ls,

View File

@ -335,7 +335,7 @@ func (s *DockerSuite) TestImagesFormat(c *check.C) {
expected := []string{"myimage", "myimage"}
var names []string
names = append(names, lines...)
c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
c.Assert(names, checker.DeepEquals, expected, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
}
// ImagesDefaultFormatAndQuiet

View File

@ -14,12 +14,13 @@ import (
"encoding/json"
"github.com/Sirupsen/logrus"
"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/pkg/ioutils"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/opencontainers/go-digest"
)
@ -56,7 +57,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, rs reference.Store, ms metadata.Store) error {
func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error {
graphDir := filepath.Join(root, graphDirName)
if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
return nil
@ -322,11 +323,15 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
for name, repo := range repos.Repositories {
for tag, id := range repo {
if strongID, exists := mappings[id]; exists {
ref, err := reference.WithName(name)
ref, err := reference.ParseNormalizedNamed(name)
if err != nil {
logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
continue
}
if !reference.IsNameOnly(ref) {
logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name)
continue
}
if dgst, err := digest.Parse(tag); err == nil {
canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
if err != nil {
@ -334,7 +339,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
continue
}
if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil {
logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err)
logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
}
} else {
tagRef, err := reference.WithTag(ref, tag)
@ -343,7 +348,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
continue
}
if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil {
logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
}
}
logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)

View File

@ -13,10 +13,10 @@ import (
"runtime"
"testing"
"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"
"github.com/opencontainers/go-digest"
)
@ -40,9 +40,9 @@ func TestMigrateRefs(t *testing.T) {
}
expected := map[string]string{
"busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
"busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
"registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"docker.io/library/busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
"docker.io/library/busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
"docker.io/library/registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
}
if !reflect.DeepEqual(expected, ta.refs) {

View File

@ -18,6 +18,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/distribution"
@ -30,7 +31,7 @@ import (
"github.com/docker/docker/pkg/pools"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/plugin/v2"
"github.com/docker/docker/reference"
refstore "github.com/docker/docker/reference"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"golang.org/x/net/context"
@ -232,11 +233,11 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string
defer pm.muGC.RUnlock()
// revalidate because Pull is public
nameref, err := reference.ParseNamed(name)
nameref, err := reference.ParseNormalizedNamed(name)
if err != nil {
return errors.Wrapf(err, "failed to parse %q", name)
}
name = reference.WithDefaultTag(nameref).String()
name = reference.FamiliarString(reference.TagNameOnly(nameref))
tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
defer os.RemoveAll(tmpRootFSDir)
@ -277,11 +278,11 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m
defer pm.muGC.RUnlock()
// revalidate because Pull is public
nameref, err := reference.ParseNamed(name)
nameref, err := reference.ParseNormalizedNamed(name)
if err != nil {
return errors.Wrapf(err, "failed to parse %q", name)
}
name = reference.WithDefaultTag(nameref).String()
name = reference.FamiliarString(reference.TagNameOnly(nameref))
if err := pm.config.Store.validateName(name); err != nil {
return err
@ -370,7 +371,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header
return err
}
ref, err := reference.ParseNamed(p.Name())
ref, err := reference.ParseNormalizedNamed(p.Name())
if err != nil {
return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name())
}
@ -448,8 +449,8 @@ func (r *pluginReference) References(id digest.Digest) []reference.Named {
return []reference.Named{r.name}
}
func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Association {
return []reference.Association{
func (r *pluginReference) ReferencesByName(ref reference.Named) []refstore.Association {
return []refstore.Association{
{
Ref: r.name,
ID: r.pluginID,
@ -459,7 +460,7 @@ func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Asso
func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) {
if r.name.String() != ref.String() {
return digest.Digest(""), reference.ErrDoesNotExist
return digest.Digest(""), refstore.ErrDoesNotExist
}
return r.pluginID, nil
}
@ -664,15 +665,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
pm.muGC.RLock()
defer pm.muGC.RUnlock()
ref, err := reference.ParseNamed(options.RepoName)
ref, err := reference.ParseNormalizedNamed(options.RepoName)
if err != nil {
return errors.Wrapf(err, "failed to parse reference %v", options.RepoName)
}
if _, ok := ref.(reference.Canonical); ok {
return errors.Errorf("canonical references are not permitted")
}
taggedRef := reference.WithDefaultTag(ref)
name := taggedRef.String()
name := reference.FamiliarString(reference.TagNameOnly(ref))
if err := pm.config.Store.validateName(name); err != nil { // fast check, real check is in createPlugin()
return err
@ -754,7 +754,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
if err != nil {
return err
}
p.PluginObj.PluginReference = taggedRef.String()
p.PluginObj.PluginReference = name
pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")

View File

@ -7,9 +7,9 @@ import (
"io"
"net/http"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/reference"
"golang.org/x/net/context"
)

View File

@ -5,10 +5,10 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/plugingetter"
"github.com/docker/docker/pkg/plugins"
"github.com/docker/docker/plugin/v2"
"github.com/docker/docker/reference"
"github.com/pkg/errors"
)
@ -230,19 +230,19 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
return idOrName, nil
}
ref, err := reference.ParseNamed(idOrName)
ref, err := reference.ParseNormalizedNamed(idOrName)
if err != nil {
return "", errors.WithStack(ErrNotFound(idOrName))
}
if _, ok := ref.(reference.Canonical); ok {
logrus.Warnf("canonical references cannot be resolved: %v", ref.String())
logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
return "", errors.WithStack(ErrNotFound(idOrName))
}
fullRef := reference.WithDefaultTag(ref)
ref = reference.TagNameOnly(ref)
for _, p := range ps.plugins {
if p.PluginObj.Name == fullRef.String() {
if p.PluginObj.Name == reference.FamiliarString(ref) {
return p.PluginObj.ID, nil
}
}

View File

@ -1,194 +0,0 @@
package reference
import (
"fmt"
distreference "github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/stringid"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
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 {
// 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.
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.
func ParseNamed(s string) (Named, error) {
named, err := distreference.ParseNormalizedNamed(s)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse reference %q", s)
}
if err := validateName(distreference.FamiliarName(named)); err != nil {
return nil, err
}
// Ensure returned reference cannot have tag and digest
if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
r, err := distreference.WithDigest(distreference.TrimNamed(named), canonical.Digest())
if err != nil {
return nil, err
}
return &canonicalRef{namedRef{r}}, nil
}
if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag())
if err != nil {
return nil, err
}
return &taggedRef{namedRef{r}}, nil
}
return &namedRef{named}, nil
}
// TrimNamed removes any tag or digest from the named reference
func TrimNamed(ref Named) Named {
return &namedRef{distreference.TrimNamed(ref)}
}
// WithName returns a named object representing the given string. If the input
// is invalid ErrReferenceInvalidFormat will be returned.
func WithName(name string) (Named, error) {
r, err := distreference.ParseNormalizedNamed(name)
if err != nil {
return nil, err
}
if err := validateName(distreference.FamiliarName(r)); err != nil {
return nil, err
}
if !distreference.IsNameOnly(r) {
return nil, distreference.ErrReferenceInvalidFormat
}
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) {
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) {
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) Name() string {
return distreference.FamiliarName(r.Named)
}
func (r *namedRef) String() string {
return distreference.FamiliarString(r.Named)
}
func (r *namedRef) FullName() string {
return r.Named.Name()
}
func (r *namedRef) Hostname() string {
return distreference.Domain(r.Named)
}
func (r *namedRef) RemoteName() string {
return distreference.Path(r.Named)
}
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()
}
// 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
}
// ParseIDOrReference parses string for an image ID or a reference. ID can be
// without a default prefix.
func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
if err := stringid.ValidateID(idOrRef); err == nil {
idOrRef = "sha256:" + idOrRef
}
if dgst, err := digest.Parse(idOrRef); err == nil {
return dgst, nil, nil
}
ref, err := ParseNamed(idOrRef)
return "", ref, err
}
func validateName(name string) error {
if err := stringid.ValidateID(name); err == nil {
return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
}
return nil
}

View File

@ -1,275 +0,0 @@
package reference
import (
"testing"
"github.com/opencontainers/go-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 fullName 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")
}
}

View File

@ -9,6 +9,7 @@ import (
"sort"
"sync"
"github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/ioutils"
"github.com/opencontainers/go-digest"
)
@ -21,18 +22,18 @@ var (
// An Association is a tuple associating a reference with an image ID.
type Association struct {
Ref Named
Ref reference.Named
ID digest.Digest
}
// Store provides the set of methods which can operate on a tag store.
type Store interface {
References(id digest.Digest) []Named
ReferencesByName(ref Named) []Association
AddTag(ref Named, id digest.Digest, force bool) error
AddDigest(ref Canonical, id digest.Digest, force bool) error
Delete(ref Named) (bool, error)
Get(ref Named) (digest.Digest, error)
References(id digest.Digest) []reference.Named
ReferencesByName(ref reference.Named) []Association
AddTag(ref reference.Named, id digest.Digest, force bool) error
AddDigest(ref reference.Canonical, id digest.Digest, force bool) error
Delete(ref reference.Named) (bool, error)
Get(ref reference.Named) (digest.Digest, error)
}
type store struct {
@ -44,24 +45,28 @@ type store struct {
Repositories map[string]repository
// referencesByIDCache is a cache of references indexed by ID, to speed
// up References.
referencesByIDCache map[digest.Digest]map[string]Named
referencesByIDCache map[digest.Digest]map[string]reference.Named
}
// Repository maps tags to digests. The key is a stringified Reference,
// including the repository name.
type repository map[string]digest.Digest
type lexicalRefs []Named
type lexicalRefs []reference.Named
func (a lexicalRefs) Len() int { return len(a) }
func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() }
func (a lexicalRefs) Len() int { return len(a) }
func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lexicalRefs) Less(i, j int) bool {
return a[i].String() < a[j].String()
}
type lexicalAssociations []Association
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 (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()
}
// NewReferenceStore creates a new reference store, tied to a file path where
// the set of references are serialized in JSON format.
@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) {
store := &store{
jsonPath: abspath,
Repositories: make(map[string]repository),
referencesByIDCache: make(map[digest.Digest]map[string]Named),
referencesByIDCache: make(map[digest.Digest]map[string]reference.Named),
}
// Load the json file if it exists, otherwise create it.
if err := store.reload(); os.IsNotExist(err) {
@ -89,43 +94,45 @@ func NewReferenceStore(jsonPath string) (Store, error) {
// 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 Named, id digest.Digest, force bool) error {
if _, isCanonical := ref.(Canonical); isCanonical {
func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error {
if _, isCanonical := ref.(reference.Canonical); isCanonical {
return errors.New("refusing to create a tag with a digest reference")
}
return store.addReference(WithDefaultTag(ref), id, force)
return store.addReference(reference.TagNameOnly(ref), id, force)
}
// AddDigest adds a digest reference to the store.
func (store *store) AddDigest(ref Canonical, id digest.Digest, force bool) error {
func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error {
return store.addReference(ref, id, force)
}
func (store *store) addReference(ref Named, id digest.Digest, force bool) error {
if ref.Name() == string(digest.Canonical) {
func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
refName := reference.FamiliarName(ref)
refStr := reference.FamiliarString(ref)
if refName == string(digest.Canonical) {
return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
}
store.mu.Lock()
defer store.mu.Unlock()
repository, exists := store.Repositories[ref.Name()]
repository, exists := store.Repositories[refName]
if !exists || repository == nil {
repository = make(map[string]digest.Digest)
store.Repositories[ref.Name()] = repository
store.Repositories[refName] = repository
}
refStr := ref.String()
oldID, exists := repository[refStr]
if exists {
// force only works for tags
if digested, isDigest := ref.(Canonical); isDigest {
if digested, isDigest := ref.(reference.Canonical); isDigest {
return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
}
if !force {
return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", refStr, oldID.String())
}
if store.referencesByIDCache[oldID] != nil {
@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
repository[refStr] = id
if store.referencesByIDCache[id] == nil {
store.referencesByIDCache[id] = make(map[string]Named)
store.referencesByIDCache[id] = make(map[string]reference.Named)
}
store.referencesByIDCache[id][refStr] = ref
@ -147,24 +154,24 @@ func (store *store) addReference(ref Named, id digest.Digest, 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 = WithDefaultTag(ref)
func (store *store) Delete(ref reference.Named) (bool, error) {
ref = reference.TagNameOnly(ref)
refName := reference.FamiliarName(ref)
refStr := reference.FamiliarString(ref)
store.mu.Lock()
defer store.mu.Unlock()
repoName := ref.Name()
repository, exists := store.Repositories[repoName]
repository, exists := store.Repositories[refName]
if !exists {
return false, ErrDoesNotExist
}
refStr := ref.String()
if id, exists := repository[refStr]; exists {
delete(repository, refStr)
if len(repository) == 0 {
delete(store.Repositories, repoName)
delete(store.Repositories, refName)
}
if store.referencesByIDCache[id] != nil {
delete(store.referencesByIDCache[id], refStr)
@ -179,18 +186,34 @@ func (store *store) Delete(ref Named) (bool, error) {
}
// Get retrieves an item from the store by reference
func (store *store) Get(ref Named) (digest.Digest, error) {
ref = WithDefaultTag(ref)
func (store *store) Get(ref reference.Named) (digest.Digest, error) {
if canonical, ok := ref.(reference.Canonical); ok {
// If reference contains both tag and digest, only
// lookup by digest as it takes precendent over
// tag, until tag/digest combos are stored.
if _, ok := ref.(reference.Tagged); ok {
var err error
ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
if err != nil {
return "", err
}
}
} else {
ref = reference.TagNameOnly(ref)
}
refName := reference.FamiliarName(ref)
refStr := reference.FamiliarString(ref)
store.mu.RLock()
defer store.mu.RUnlock()
repository, exists := store.Repositories[ref.Name()]
repository, exists := store.Repositories[refName]
if !exists || repository == nil {
return "", ErrDoesNotExist
}
id, exists := repository[ref.String()]
id, exists := repository[refStr]
if !exists {
return "", ErrDoesNotExist
}
@ -200,7 +223,7 @@ func (store *store) Get(ref Named) (digest.Digest, error) {
// References returns a slice of references to the given ID. The slice
// will be nil if there are no references to this ID.
func (store *store) References(id digest.Digest) []Named {
func (store *store) References(id digest.Digest) []reference.Named {
store.mu.RLock()
defer store.mu.RUnlock()
@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named {
// 1) We must not return a mutable
// 2) It would be ugly to expose the extraneous map keys to callers.
var references []Named
var references []reference.Named
for _, ref := range store.referencesByIDCache[id] {
references = append(references, ref)
}
@ -221,18 +244,20 @@ func (store *store) References(id digest.Digest) []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 Named) []Association {
func (store *store) ReferencesByName(ref reference.Named) []Association {
refName := reference.FamiliarName(ref)
store.mu.RLock()
defer store.mu.RUnlock()
repository, exists := store.Repositories[ref.Name()]
repository, exists := store.Repositories[refName]
if !exists {
return nil
}
var associations []Association
for refStr, refID := range repository {
ref, err := ParseNamed(refStr)
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
// Should never happen
return nil
@ -270,13 +295,13 @@ func (store *store) reload() error {
for _, repository := range store.Repositories {
for refStr, refID := range repository {
ref, err := ParseNamed(refStr)
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
// Should never happen
continue
}
if store.referencesByIDCache[refID] == nil {
store.referencesByIDCache[refID] = make(map[string]Named)
store.referencesByIDCache[refID] = make(map[string]reference.Named)
}
store.referencesByIDCache[refID][refStr] = ref
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"testing"
"github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
)
@ -45,7 +46,7 @@ func TestLoad(t *testing.T) {
}
for refStr, expectedID := range saveLoadTestCases {
ref, err := ParseNamed(refStr)
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
t.Fatalf("failed to parse reference: %v", err)
}
@ -74,11 +75,11 @@ func TestSave(t *testing.T) {
}
for refStr, id := range saveLoadTestCases {
ref, err := ParseNamed(refStr)
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
t.Fatalf("failed to parse reference: %v", err)
}
if canonical, ok := ref.(Canonical); ok {
if canonical, ok := ref.(reference.Canonical); ok {
err = store.AddDigest(canonical, id, false)
if err != nil {
t.Fatalf("could not add digest reference %s: %v", refStr, err)
@ -120,7 +121,7 @@ func TestAddDeleteGet(t *testing.T) {
testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
// Try adding a reference with no tag or digest
nameOnly, err := WithName("username/repo")
nameOnly, err := reference.ParseNormalizedNamed("username/repo")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -129,7 +130,7 @@ func TestAddDeleteGet(t *testing.T) {
}
// Add a few references
ref1, err := ParseNamed("username/repo1:latest")
ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -137,7 +138,7 @@ func TestAddDeleteGet(t *testing.T) {
t.Fatalf("error adding to store: %v", err)
}
ref2, err := ParseNamed("username/repo1:old")
ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -145,7 +146,7 @@ func TestAddDeleteGet(t *testing.T) {
t.Fatalf("error adding to store: %v", err)
}
ref3, err := ParseNamed("username/repo1:alias")
ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -153,7 +154,7 @@ func TestAddDeleteGet(t *testing.T) {
t.Fatalf("error adding to store: %v", err)
}
ref4, err := ParseNamed("username/repo2:latest")
ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -161,11 +162,11 @@ func TestAddDeleteGet(t *testing.T) {
t.Fatalf("error adding to store: %v", err)
}
ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
t.Fatalf("error adding to store: %v", err)
}
@ -228,7 +229,7 @@ func TestAddDeleteGet(t *testing.T) {
}
// Get should return ErrDoesNotExist for a nonexistent repo
nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -237,7 +238,7 @@ func TestAddDeleteGet(t *testing.T) {
}
// Get should return ErrDoesNotExist for a nonexistent tag
nonExistTag, err := ParseNamed("username/repo1:nonexist")
nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -263,7 +264,7 @@ func TestAddDeleteGet(t *testing.T) {
}
// Check ReferencesByName
repoName, err := WithName("username/repo1")
repoName, err := reference.ParseNormalizedNamed("username/repo1")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
@ -334,7 +335,7 @@ func TestInvalidTags(t *testing.T) {
id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
// sha256 as repo name
ref, err := ParseNamed("sha256:abc")
ref, err := reference.ParseNormalizedNamed("sha256:abc")
if err != nil {
t.Fatal(err)
}
@ -344,7 +345,7 @@ func TestInvalidTags(t *testing.T) {
}
// setting digest as a tag
ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
if err != nil {
t.Fatal(err)
}

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/distribution/reference"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/opts"
forkedref "github.com/docker/docker/reference"
"github.com/pkg/errors"
"github.com/spf13/pflag"
)
@ -271,8 +270,9 @@ func ValidateMirror(val string) (string, error) {
// ValidateIndexName validates an index name.
func ValidateIndexName(val string) (string, error) {
if val == forkedref.LegacyDefaultHostname {
val = forkedref.DefaultHostname
// TODO: upstream this to check to reference package
if val == "index.docker.io" {
val = "docker.io"
}
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
@ -328,13 +328,8 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
}
official := !strings.ContainsRune(reference.FamiliarName(name), '/')
// TODO: remove used of forked reference package
nameref, err := forkedref.ParseNamed(name.String())
if err != nil {
return nil, err
}
return &RepositoryInfo{
Named: nameref,
Name: reference.TrimNamed(name),
Index: index,
Official: official,
}, nil

View File

@ -17,8 +17,8 @@ import (
"testing"
"time"
"github.com/docker/distribution/reference"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/reference"
"github.com/gorilla/mux"
"github.com/Sirupsen/logrus"

View File

@ -14,7 +14,6 @@ import (
"github.com/docker/distribution/registry/client/transport"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
forkedref "github.com/docker/docker/reference"
)
var (
@ -202,7 +201,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
func TestGetRemoteTag(t *testing.T) {
r := spawnTestRegistrySession(t)
repoRef, err := forkedref.ParseNamed(REPO)
repoRef, err := reference.ParseNormalizedNamed(REPO)
if err != nil {
t.Fatal(err)
}
@ -212,7 +211,7 @@ func TestGetRemoteTag(t *testing.T) {
}
assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
bazRef, err := forkedref.ParseNamed("foo42/baz")
bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
if err != nil {
t.Fatal(err)
}
@ -224,7 +223,7 @@ func TestGetRemoteTag(t *testing.T) {
func TestGetRemoteTags(t *testing.T) {
r := spawnTestRegistrySession(t)
repoRef, err := forkedref.ParseNamed(REPO)
repoRef, err := reference.ParseNormalizedNamed(REPO)
if err != nil {
t.Fatal(err)
}
@ -236,7 +235,7 @@ func TestGetRemoteTags(t *testing.T) {
assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
bazRef, err := forkedref.ParseNamed("foo42/baz")
bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
if err != nil {
t.Fatal(err)
}
@ -253,7 +252,7 @@ func TestGetRepositoryData(t *testing.T) {
t.Fatal(err)
}
host := "http://" + parsedURL.Host + "/v1/"
repoRef, err := forkedref.ParseNamed(REPO)
repoRef, err := reference.ParseNormalizedNamed(REPO)
if err != nil {
t.Fatal(err)
}
@ -516,9 +515,9 @@ func TestParseRepositoryInfo(t *testing.T) {
t.Error(err)
} else {
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
checkEqual(t, reference.Path(repoInfo.Name), expectedRepoInfo.RemoteName, reposName)
checkEqual(t, reference.FamiliarName(repoInfo.Name), expectedRepoInfo.LocalName, reposName)
checkEqual(t, repoInfo.Name.Name(), expectedRepoInfo.CanonicalName, reposName)
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
}
@ -689,7 +688,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
func TestPushRegistryTag(t *testing.T) {
r := spawnTestRegistrySession(t)
repoRef, err := forkedref.ParseNamed(REPO)
repoRef, err := reference.ParseNormalizedNamed(REPO)
if err != nil {
t.Fatal(err)
}
@ -711,7 +710,7 @@ func TestPushImageJSONIndex(t *testing.T) {
Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
},
}
repoRef, err := forkedref.ParseNamed(REPO)
repoRef, err := reference.ParseNormalizedNamed(REPO)
if err != nil {
t.Fatal(err)
}

View File

@ -19,6 +19,7 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
@ -26,7 +27,6 @@ import (
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/tarsum"
"github.com/docker/docker/reference"
)
var (
@ -324,7 +324,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.RemoteName()
repository := reference.Path(repositoryRef)
if strings.Count(repository, "/") == 0 {
// This will be removed once the registry supports auto-resolution on
@ -362,7 +362,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.RemoteName()
repository := reference.Path(repositoryRef)
if strings.Count(repository, "/") == 0 {
// This will be removed once the registry supports auto-resolution on
@ -416,7 +416,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
// GetRepositoryData returns lists of images and endpoints for the repository
func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), name.RemoteName())
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), reference.Path(name))
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
@ -450,7 +450,7 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro
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, name.RemoteName(), errBody), res)
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
}
var endpoints []string
@ -605,7 +605,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.RemoteName(), tag)
path := fmt.Sprintf("repositories/%s/tags/%s", reference.Path(remote), tag)
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
if err != nil {
@ -619,7 +619,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.RemoteName()), res)
return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
}
return nil
}
@ -645,7 +645,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
if validate {
suffix = "images"
}
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), remote.RemoteName(), suffix)
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), reference.Path(remote), suffix)
logrus.Debugf("[registry] PUT %s", u)
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
headers := map[string][]string{
@ -683,7 +683,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.RemoteName(), errBody), res)
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
}
tokens = res.Header["X-Docker-Token"]
logrus.Debugf("Auth token: %v", tokens)
@ -701,7 +701,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.RemoteName(), errBody), res)
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
}
}

View File

@ -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 for a repository
@ -57,7 +57,7 @@ var apiVersions = map[APIVersion]string{
// RepositoryInfo describes a repository
type RepositoryInfo struct {
reference.Named
Name reference.Named
// Index points to registry information
Index *registrytypes.IndexInfo
// Official indicates whether the repository is considered official.