mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Update Named reference with validation of conversions
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
		
							parent
							
								
									2655954c2d
								
							
						
					
					
						commit
						ffded61dad
					
				
					 25 changed files with 550 additions and 509 deletions
				
			
		| 
						 | 
				
			
			@ -260,15 +260,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 | 
			
		|||
 | 
			
		||||
// validateTag checks if the given image name can be resolved.
 | 
			
		||||
func validateTag(rawRepo string) (string, error) {
 | 
			
		||||
	ref, err := reference.ParseNamed(rawRepo)
 | 
			
		||||
	_, err := reference.ParseNamed(rawRepo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := registry.ValidateRepositoryName(ref); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rawRepo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@ import (
 | 
			
		|||
	"github.com/docker/docker/opts"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
	"github.com/docker/docker/registry"
 | 
			
		||||
	"github.com/docker/docker/runconfig"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +43,6 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := registry.ValidateRepositoryName(ref); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repositoryName = ref.Name()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,6 @@ import (
 | 
			
		|||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/urlutil"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
	"github.com/docker/docker/registry"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdImport creates an empty filesystem image, imports the contents of the tarball into the image, and optionally tags the image.
 | 
			
		||||
| 
						 | 
				
			
			@ -45,11 +44,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
 | 
			
		|||
 | 
			
		||||
	if repository != "" {
 | 
			
		||||
		//Check if the given image name can be resolved
 | 
			
		||||
		ref, err := reference.ParseNamed(repository)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := registry.ValidateRepositoryName(ref); err != nil {
 | 
			
		||||
		if _, err := reference.ParseNamed(repository); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ import (
 | 
			
		|||
	Cli "github.com/docker/docker/cli"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
	"github.com/docker/docker/registry"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdTag tags an image into a repository.
 | 
			
		||||
| 
						 | 
				
			
			@ -35,11 +34,6 @@ func (cli *DockerCli) CmdTag(args ...string) error {
 | 
			
		|||
		tag = tagged.Tag()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Check if the given image name can be resolved
 | 
			
		||||
	if err := registry.ValidateRepositoryName(ref); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options := types.ImageTagOptions{
 | 
			
		||||
		ImageID:        cmd.Arg(0),
 | 
			
		||||
		RepositoryName: ref.Name(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,12 +167,12 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	creds := simpleCredentialStore{auth: authConfig}
 | 
			
		||||
	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName.Name(), "push", "pull")
 | 
			
		||||
	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), "push", "pull")
 | 
			
		||||
	basicHandler := auth.NewBasicHandler(creds)
 | 
			
		||||
	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
 | 
			
		||||
	tr := transport.NewTransport(base, modifiers...)
 | 
			
		||||
 | 
			
		||||
	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName.Name(), server, tr, cli.getPassphraseRetriever())
 | 
			
		||||
	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.FullName(), server, tr, cli.getPassphraseRetriever())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertTarget(t client.Target) (target, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +298,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 | 
			
		|||
		for _, tgt := range targets {
 | 
			
		||||
			t, err := convertTarget(*tgt)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.LocalName)
 | 
			
		||||
				fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			refs = append(refs, t)
 | 
			
		||||
| 
						 | 
				
			
			@ -321,19 +321,19 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
 | 
			
		|||
		if displayTag != "" {
 | 
			
		||||
			displayTag = ":" + displayTag
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest)
 | 
			
		||||
		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest)
 | 
			
		||||
 | 
			
		||||
		if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil {
 | 
			
		||||
		if err := cli.imagePullPrivileged(authConfig, repoInfo.Name(), r.digest.String(), requestPrivilege); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If reference is not trusted, tag by trusted reference
 | 
			
		||||
		if !r.reference.HasDigest() {
 | 
			
		||||
			tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String())
 | 
			
		||||
			tagged, err := reference.WithTag(repoInfo, r.reference.String())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest)
 | 
			
		||||
			trustedRef, err := reference.WithDigest(repoInfo, r.digest)
 | 
			
		||||
			if err := cli.tagTrusted(trustedRef, tagged); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +384,7 @@ func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
 | 
			
		|||
func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig types.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error {
 | 
			
		||||
	streamOut, targetChan := targetStream(cli.out)
 | 
			
		||||
 | 
			
		||||
	reqError := cli.imagePushPrivileged(authConfig, repoInfo.LocalName.Name(), tag, streamOut, requestPrivilege)
 | 
			
		||||
	reqError := cli.imagePushPrivileged(authConfig, repoInfo.Name(), tag, streamOut, requestPrivilege)
 | 
			
		||||
 | 
			
		||||
	// Close stream channel to finish target parsing
 | 
			
		||||
	if err := streamOut.Close(); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +455,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
 | 
			
		|||
	if err := repo.Initialize(rootKeyID); err != nil {
 | 
			
		||||
		return notaryError(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.CanonicalName)
 | 
			
		||||
	fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName())
 | 
			
		||||
 | 
			
		||||
	return notaryError(repo.Publish())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1043,7 +1043,6 @@ func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	newTag = registry.NormalizeLocalReference(newTag)
 | 
			
		||||
	if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1301,7 +1300,6 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
 | 
			
		|||
 | 
			
		||||
	// Treat it as a possible tag or digest reference
 | 
			
		||||
	if ref, err := reference.ParseNamed(refOrID); err == nil {
 | 
			
		||||
		ref = registry.NormalizeLocalReference(ref)
 | 
			
		||||
		if id, err := daemon.referenceStore.Get(ref); err == nil {
 | 
			
		||||
			return id, nil
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,17 +87,15 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// makes sure name is not empty or `scratch`
 | 
			
		||||
	if err := validateRepoName(repoInfo.LocalName.Name()); err != nil {
 | 
			
		||||
	if err := validateRepoName(repoInfo.Name()); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.CanonicalName)
 | 
			
		||||
	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localName := registry.NormalizeLocalReference(ref)
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		// use a slice to append the error strings and return a joined string to caller
 | 
			
		||||
		errors []string
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +110,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 | 
			
		|||
		discardNoSupportErrors bool
 | 
			
		||||
	)
 | 
			
		||||
	for _, endpoint := range endpoints {
 | 
			
		||||
		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version)
 | 
			
		||||
		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
 | 
			
		||||
 | 
			
		||||
		puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +146,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		imagePullConfig.EventsService.Log("pull", localName.String(), "")
 | 
			
		||||
		imagePullConfig.EventsService.Log("pull", ref.String(), "")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,18 +65,18 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool
 | 
			
		|||
		// TODO(dmcgowan): Check if should fallback
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", p.repoInfo.CanonicalName.Name()+": this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.")
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", p.repoInfo.FullName()+": this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.")
 | 
			
		||||
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.CanonicalName.Name())
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName())
 | 
			
		||||
 | 
			
		||||
	repoData, err := p.session.GetRepositoryData(p.repoInfo.RemoteName)
 | 
			
		||||
	repoData, err := p.session.GetRepositoryData(p.repoInfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "HTTP code: 404") {
 | 
			
		||||
			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName.Name())
 | 
			
		||||
			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
 | 
			
		||||
		}
 | 
			
		||||
		// Unexpected HTTP error
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -86,13 +86,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
 | 
			
		|||
	var tagsList map[string]string
 | 
			
		||||
	tagged, isTagged := ref.(reference.NamedTagged)
 | 
			
		||||
	if !isTagged {
 | 
			
		||||
		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName)
 | 
			
		||||
		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
 | 
			
		||||
	} else {
 | 
			
		||||
		var tagID string
 | 
			
		||||
		tagsList = make(map[string]string)
 | 
			
		||||
		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, tagged.Tag())
 | 
			
		||||
		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag())
 | 
			
		||||
		if err == registry.ErrRepoNotFound {
 | 
			
		||||
			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.CanonicalName.Name())
 | 
			
		||||
			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
 | 
			
		||||
		}
 | 
			
		||||
		tagsList[tagged.Tag()] = tagID
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -121,14 +121,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localNameRef := p.repoInfo.LocalName
 | 
			
		||||
	if isTagged {
 | 
			
		||||
		localNameRef, err = reference.WithTag(localNameRef, tagged.Tag())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			localNameRef = p.repoInfo.LocalName
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	writeStatus(localNameRef.String(), p.config.ProgressOutput, layersDownloaded)
 | 
			
		||||
	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +131,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localNameRef, err := reference.WithTag(p.repoInfo.LocalName, img.Tag)
 | 
			
		||||
	localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
 | 
			
		||||
		logrus.Debug(retErr.Error())
 | 
			
		||||
| 
						 | 
				
			
			@ -149,15 +142,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName.Name())
 | 
			
		||||
	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName())
 | 
			
		||||
	success := false
 | 
			
		||||
	var lastErr error
 | 
			
		||||
	for _, ep := range p.repoInfo.Index.Mirrors {
 | 
			
		||||
		ep += "v1/"
 | 
			
		||||
		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep))
 | 
			
		||||
		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
 | 
			
		||||
		if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 | 
			
		||||
			// Don't report errors when pulling from mirrors.
 | 
			
		||||
			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err)
 | 
			
		||||
			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		success = true
 | 
			
		||||
| 
						 | 
				
			
			@ -165,12 +158,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 | 
			
		|||
	}
 | 
			
		||||
	if !success {
 | 
			
		||||
		for _, ep := range repoData.Endpoints {
 | 
			
		||||
			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep)
 | 
			
		||||
			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep)
 | 
			
		||||
			if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 | 
			
		||||
				// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
 | 
			
		||||
				// As the error is also given to the output stream the user will see the error.
 | 
			
		||||
				lastErr = err
 | 
			
		||||
				progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err)
 | 
			
		||||
				progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			success = true
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +171,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !success {
 | 
			
		||||
		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.CanonicalName.Name(), lastErr)
 | 
			
		||||
		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
 | 
			
		||||
		progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,19 +54,11 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool
 | 
			
		|||
 | 
			
		||||
func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) {
 | 
			
		||||
	var refs []reference.Named
 | 
			
		||||
	taggedName := p.repoInfo.LocalName
 | 
			
		||||
	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
 | 
			
		||||
		taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		refs = []reference.Named{taggedName}
 | 
			
		||||
	} else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
 | 
			
		||||
		taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		refs = []reference.Named{taggedName}
 | 
			
		||||
	taggedName := ref
 | 
			
		||||
	if _, isTagged := ref.(reference.NamedTagged); isTagged {
 | 
			
		||||
		refs = []reference.Named{ref}
 | 
			
		||||
	} else if _, isCanonical := ref.(reference.Canonical); isCanonical {
 | 
			
		||||
		refs = []reference.Named{ref}
 | 
			
		||||
	} else {
 | 
			
		||||
		manSvc, err := p.repo.Manifests(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +73,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
 | 
			
		|||
		// This probably becomes a lot nicer after the manifest
 | 
			
		||||
		// refactor...
 | 
			
		||||
		for _, tag := range tags {
 | 
			
		||||
			tagRef, err := reference.WithTag(p.repoInfo.LocalName, tag)
 | 
			
		||||
			tagRef, err := reference.WithTag(ref, tag)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +283,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 | 
			
		|||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo.LocalName.Name())
 | 
			
		||||
	manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,21 +104,21 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.CanonicalName)
 | 
			
		||||
	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.CanonicalName.String())
 | 
			
		||||
	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName())
 | 
			
		||||
 | 
			
		||||
	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.LocalName)
 | 
			
		||||
	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo)
 | 
			
		||||
	if len(associations) == 0 {
 | 
			
		||||
		return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
 | 
			
		||||
		return fmt.Errorf("Repository does not exist: %s", repoInfo.Name())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var lastErr error
 | 
			
		||||
	for _, endpoint := range endpoints {
 | 
			
		||||
		logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
 | 
			
		||||
		logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
 | 
			
		||||
 | 
			
		||||
		pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -143,12 +143,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 | 
			
		|||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		imagePushConfig.EventsService.Log("push", repoInfo.LocalName.Name(), "")
 | 
			
		||||
		imagePushConfig.EventsService.Log("push", repoInfo.Name(), "")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lastErr == nil {
 | 
			
		||||
		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
 | 
			
		||||
		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
 | 
			
		||||
	}
 | 
			
		||||
	return lastErr
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -351,8 +351,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima
 | 
			
		|||
		}
 | 
			
		||||
		if topImage, isTopImage := img.(*v1TopImage); isTopImage {
 | 
			
		||||
			for _, tag := range tags[topImage.imageID] {
 | 
			
		||||
				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName.Name()+"/tags/"+tag)
 | 
			
		||||
				if err := p.session.PushRegistryTag(p.repoInfo.RemoteName, v1ID, tag, endpoint); err != nil {
 | 
			
		||||
				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
 | 
			
		||||
				if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -381,18 +381,18 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
 | 
			
		|||
 | 
			
		||||
	// Register all the images in a repository with the registry
 | 
			
		||||
	// If an image is not in this list it will not be associated with the repository
 | 
			
		||||
	repoData, err := p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, false, nil)
 | 
			
		||||
	repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.CanonicalName.String())
 | 
			
		||||
	progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.FullName())
 | 
			
		||||
	// push the repository to each of the endpoints only if it does not exist.
 | 
			
		||||
	for _, endpoint := range repoData.Endpoints {
 | 
			
		||||
		if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	_, err = p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, true, repoData.Endpoints)
 | 
			
		||||
	_, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,6 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
 | 
			
		|||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localName := p.repoInfo.LocalName.Name()
 | 
			
		||||
 | 
			
		||||
	var associations []reference.Association
 | 
			
		||||
	if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
 | 
			
		||||
		imageID, err := p.config.ReferenceStore.Get(p.ref)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,10 +70,10 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
 | 
			
		|||
		associations = p.config.ReferenceStore.ReferencesByName(p.ref)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, fmt.Errorf("error getting tags for %s: %s", localName, err)
 | 
			
		||||
		return false, fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(associations) == 0 {
 | 
			
		||||
		return false, fmt.Errorf("no tags to push for %s", localName)
 | 
			
		||||
		return false, fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, association := range associations {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +157,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name())
 | 
			
		||||
	manifestDigest, manifestSize, err := digestFromManifest(signed, ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@ import (
 | 
			
		|||
	"github.com/docker/distribution/registry/client/transport"
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/docker/docker/distribution/xfer"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
	"github.com/docker/docker/registry"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -37,10 +38,10 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) {
 | 
			
		|||
func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (distribution.Repository, error) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	repoName := repoInfo.CanonicalName
 | 
			
		||||
	repoName := repoInfo.FullName()
 | 
			
		||||
	// If endpoint does not support CanonicalName, use the RemoteName instead
 | 
			
		||||
	if endpoint.TrimHostname {
 | 
			
		||||
		repoName = repoInfo.RemoteName
 | 
			
		||||
		repoName = repoInfo.RemoteName()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(dmcgowan): Call close idle connections when complete, use keep alive
 | 
			
		||||
| 
						 | 
				
			
			@ -99,16 +100,16 @@ func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEnd
 | 
			
		|||
		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
 | 
			
		||||
	} else {
 | 
			
		||||
		creds := dumbCredentialStore{auth: authConfig}
 | 
			
		||||
		tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName.Name(), actions...)
 | 
			
		||||
		tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...)
 | 
			
		||||
		basicHandler := auth.NewBasicHandler(creds)
 | 
			
		||||
		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
 | 
			
		||||
	}
 | 
			
		||||
	tr := transport.NewTransport(base, modifiers...)
 | 
			
		||||
 | 
			
		||||
	return client.NewRepository(ctx, repoName.Name(), endpoint.URL, tr)
 | 
			
		||||
	return client.NewRepository(ctx, repoName, endpoint.URL, tr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Digest, int, error) {
 | 
			
		||||
func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) {
 | 
			
		||||
	payload, err := m.Payload()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// If this failed, the signatures section was corrupted
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +118,7 @@ func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Dig
 | 
			
		|||
	}
 | 
			
		||||
	manifestDigest, err := digest.FromBytes(payload)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Infof("Could not compute manifest digest for %s:%s : %v", localName, m.Tag, err)
 | 
			
		||||
		logrus.Infof("Could not compute manifest digest for %s:%s : %v", name.Name(), m.Tag, err)
 | 
			
		||||
	}
 | 
			
		||||
	return manifestDigest, len(payload), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,16 +58,14 @@ func TestTokenPassThru(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
	n, _ := reference.ParseNamed("testremotename")
 | 
			
		||||
	repoInfo := ®istry.RepositoryInfo{
 | 
			
		||||
		Named: n,
 | 
			
		||||
		Index: ®istrytypes.IndexInfo{
 | 
			
		||||
			Name:     "testrepo",
 | 
			
		||||
			Mirrors:  nil,
 | 
			
		||||
			Secure:   false,
 | 
			
		||||
			Official: false,
 | 
			
		||||
		},
 | 
			
		||||
		RemoteName:    n,
 | 
			
		||||
		LocalName:     n,
 | 
			
		||||
		CanonicalName: n,
 | 
			
		||||
		Official:      false,
 | 
			
		||||
		Official: false,
 | 
			
		||||
	}
 | 
			
		||||
	imagePullConfig := &ImagePullConfig{
 | 
			
		||||
		MetaHeaders: http.Header{},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,6 @@ import (
 | 
			
		|||
	"github.com/docker/docker/layer"
 | 
			
		||||
	"github.com/docker/docker/pkg/archive"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
	"github.com/docker/docker/registry"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type imageDescriptor struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +73,6 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		ref = registry.NormalizeLocalReference(ref)
 | 
			
		||||
		if ref.Name() == string(digest.Canonical) {
 | 
			
		||||
			imgID, err := l.is.Search(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +1,37 @@
 | 
			
		|||
package reference
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/distribution/digest"
 | 
			
		||||
	distreference "github.com/docker/distribution/reference"
 | 
			
		||||
	"github.com/docker/docker/image/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Reference is an opaque object reference identifier that may include
 | 
			
		||||
// modifiers such as a hostname, name, tag, and digest.
 | 
			
		||||
type Reference interface {
 | 
			
		||||
	// String returns the full reference
 | 
			
		||||
	String() string
 | 
			
		||||
}
 | 
			
		||||
const (
 | 
			
		||||
	// DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified
 | 
			
		||||
	DefaultTag = "latest"
 | 
			
		||||
	// DefaultHostname is the default built-in hostname
 | 
			
		||||
	DefaultHostname = "docker.io"
 | 
			
		||||
	// LegacyDefaultHostname is automatically converted to DefaultHostname
 | 
			
		||||
	LegacyDefaultHostname = "index.docker.io"
 | 
			
		||||
	// DefaultRepoPrefix is the prefix used for default repositories in default host
 | 
			
		||||
	DefaultRepoPrefix = "library/"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Named is an object with a full name
 | 
			
		||||
type Named interface {
 | 
			
		||||
	Reference
 | 
			
		||||
	// Name returns normalized repository name, like "ubuntu".
 | 
			
		||||
	Name() string
 | 
			
		||||
	// String returns full reference, like "ubuntu@sha256:abcdef..."
 | 
			
		||||
	String() string
 | 
			
		||||
	// FullName returns full repository name with hostname, like "docker.io/library/ubuntu"
 | 
			
		||||
	FullName() string
 | 
			
		||||
	// Hostname returns hostname for the reference, like "docker.io"
 | 
			
		||||
	Hostname() string
 | 
			
		||||
	// RemoteName returns the repository component of the full name, like "library/ubuntu"
 | 
			
		||||
	RemoteName() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NamedTagged is an object including a name and tag.
 | 
			
		||||
| 
						 | 
				
			
			@ -35,26 +51,122 @@ type Canonical interface {
 | 
			
		|||
// the Named interface. The reference must have a name, otherwise an error is
 | 
			
		||||
// returned.
 | 
			
		||||
// If an error was encountered it is returned, along with a nil Reference.
 | 
			
		||||
// NOTE: ParseNamed will not handle short digests.
 | 
			
		||||
func ParseNamed(s string) (Named, error) {
 | 
			
		||||
	// todo: docker specific validation
 | 
			
		||||
	return distreference.ParseNamed(s)
 | 
			
		||||
	named, err := distreference.ParseNamed(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	r, err := WithName(named.Name())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
 | 
			
		||||
		return WithDigest(r, canonical.Digest())
 | 
			
		||||
	}
 | 
			
		||||
	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
 | 
			
		||||
		return WithTag(r, tagged.Tag())
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithName returns a named object representing the given string. If the input
 | 
			
		||||
// is invalid ErrReferenceInvalidFormat will be returned.
 | 
			
		||||
func WithName(name string) (Named, error) {
 | 
			
		||||
	return distreference.WithName(name)
 | 
			
		||||
	name = normalize(name)
 | 
			
		||||
	if err := validateName(name); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	r, err := distreference.WithName(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &namedRef{r}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithTag combines the name from "name" and the tag from "tag" to form a
 | 
			
		||||
// reference incorporating both the name and the tag.
 | 
			
		||||
func WithTag(name Named, tag string) (NamedTagged, error) {
 | 
			
		||||
	return distreference.WithTag(name, tag)
 | 
			
		||||
	r, err := distreference.WithTag(name, tag)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &taggedRef{namedRef{r}}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDigest combines the name from "name" and the digest from "digest" to form
 | 
			
		||||
// a reference incorporating both the name and the digest.
 | 
			
		||||
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
 | 
			
		||||
	return distreference.WithDigest(name, digest)
 | 
			
		||||
	r, err := distreference.WithDigest(name, digest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &canonicalRef{namedRef{r}}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type namedRef struct {
 | 
			
		||||
	distreference.Named
 | 
			
		||||
}
 | 
			
		||||
type taggedRef struct {
 | 
			
		||||
	namedRef
 | 
			
		||||
}
 | 
			
		||||
type canonicalRef struct {
 | 
			
		||||
	namedRef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *namedRef) FullName() string {
 | 
			
		||||
	hostname, remoteName := splitHostname(r.Name())
 | 
			
		||||
	return hostname + "/" + remoteName
 | 
			
		||||
}
 | 
			
		||||
func (r *namedRef) Hostname() string {
 | 
			
		||||
	hostname, _ := splitHostname(r.Name())
 | 
			
		||||
	return hostname
 | 
			
		||||
}
 | 
			
		||||
func (r *namedRef) RemoteName() string {
 | 
			
		||||
	_, remoteName := splitHostname(r.Name())
 | 
			
		||||
	return remoteName
 | 
			
		||||
}
 | 
			
		||||
func (r *taggedRef) Tag() string {
 | 
			
		||||
	return r.namedRef.Named.(distreference.NamedTagged).Tag()
 | 
			
		||||
}
 | 
			
		||||
func (r *canonicalRef) Digest() digest.Digest {
 | 
			
		||||
	return r.namedRef.Named.(distreference.Canonical).Digest()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// splitHostname splits a repository name to hostname and remotename string.
 | 
			
		||||
// If no valid hostname is found, the default hostname is used. Repository name
 | 
			
		||||
// needs to be already validated before.
 | 
			
		||||
func splitHostname(name string) (hostname, remoteName string) {
 | 
			
		||||
	i := strings.IndexRune(name, '/')
 | 
			
		||||
	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
 | 
			
		||||
		hostname, remoteName = DefaultHostname, name
 | 
			
		||||
	} else {
 | 
			
		||||
		hostname, remoteName = name[:i], name[i+1:]
 | 
			
		||||
	}
 | 
			
		||||
	if hostname == LegacyDefaultHostname {
 | 
			
		||||
		hostname = DefaultHostname
 | 
			
		||||
	}
 | 
			
		||||
	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
 | 
			
		||||
		remoteName = DefaultRepoPrefix + remoteName
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// normalize returns a repository name in its normalized form, meaning it
 | 
			
		||||
// will not contain default hostname nor library/ prefix for official images.
 | 
			
		||||
func normalize(name string) string {
 | 
			
		||||
	host, remoteName := splitHostname(name)
 | 
			
		||||
	if host == DefaultHostname {
 | 
			
		||||
		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
 | 
			
		||||
			return strings.TrimPrefix(remoteName, DefaultRepoPrefix)
 | 
			
		||||
		}
 | 
			
		||||
		return remoteName
 | 
			
		||||
	}
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateName(name string) error {
 | 
			
		||||
	if err := v1.ValidateID(name); err == nil {
 | 
			
		||||
		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										275
									
								
								reference/reference_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								reference/reference_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,275 @@
 | 
			
		|||
package reference
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/distribution/digest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestValidateReferenceName(t *testing.T) {
 | 
			
		||||
	validRepoNames := []string{
 | 
			
		||||
		"docker/docker",
 | 
			
		||||
		"library/debian",
 | 
			
		||||
		"debian",
 | 
			
		||||
		"docker.io/docker/docker",
 | 
			
		||||
		"docker.io/library/debian",
 | 
			
		||||
		"docker.io/debian",
 | 
			
		||||
		"index.docker.io/docker/docker",
 | 
			
		||||
		"index.docker.io/library/debian",
 | 
			
		||||
		"index.docker.io/debian",
 | 
			
		||||
		"127.0.0.1:5000/docker/docker",
 | 
			
		||||
		"127.0.0.1:5000/library/debian",
 | 
			
		||||
		"127.0.0.1:5000/debian",
 | 
			
		||||
		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
 | 
			
		||||
	}
 | 
			
		||||
	invalidRepoNames := []string{
 | 
			
		||||
		"https://github.com/docker/docker",
 | 
			
		||||
		"docker/Docker",
 | 
			
		||||
		"-docker",
 | 
			
		||||
		"-docker/docker",
 | 
			
		||||
		"-docker.io/docker/docker",
 | 
			
		||||
		"docker///docker",
 | 
			
		||||
		"docker.io/docker/Docker",
 | 
			
		||||
		"docker.io/docker///docker",
 | 
			
		||||
		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range invalidRepoNames {
 | 
			
		||||
		_, err := ParseNamed(name)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			t.Fatalf("Expected invalid repo name for %q", name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range validRepoNames {
 | 
			
		||||
		_, err := ParseNamed(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("Error parsing repo name %s, got: %q", name, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidateRemoteName(t *testing.T) {
 | 
			
		||||
	validRepositoryNames := []string{
 | 
			
		||||
		// Sanity check.
 | 
			
		||||
		"docker/docker",
 | 
			
		||||
 | 
			
		||||
		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
 | 
			
		||||
		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
 | 
			
		||||
 | 
			
		||||
		// Allow embedded hyphens.
 | 
			
		||||
		"docker-rules/docker",
 | 
			
		||||
 | 
			
		||||
		// Allow multiple hyphens as well.
 | 
			
		||||
		"docker---rules/docker",
 | 
			
		||||
 | 
			
		||||
		//Username doc and image name docker being tested.
 | 
			
		||||
		"doc/docker",
 | 
			
		||||
 | 
			
		||||
		// single character names are now allowed.
 | 
			
		||||
		"d/docker",
 | 
			
		||||
		"jess/t",
 | 
			
		||||
 | 
			
		||||
		// Consecutive underscores.
 | 
			
		||||
		"dock__er/docker",
 | 
			
		||||
	}
 | 
			
		||||
	for _, repositoryName := range validRepositoryNames {
 | 
			
		||||
		_, err := ParseNamed(repositoryName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	invalidRepositoryNames := []string{
 | 
			
		||||
		// Disallow capital letters.
 | 
			
		||||
		"docker/Docker",
 | 
			
		||||
 | 
			
		||||
		// Only allow one slash.
 | 
			
		||||
		"docker///docker",
 | 
			
		||||
 | 
			
		||||
		// Disallow 64-character hexadecimal.
 | 
			
		||||
		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
 | 
			
		||||
		// Disallow leading and trailing hyphens in namespace.
 | 
			
		||||
		"-docker/docker",
 | 
			
		||||
		"docker-/docker",
 | 
			
		||||
		"-docker-/docker",
 | 
			
		||||
 | 
			
		||||
		// Don't allow underscores everywhere (as opposed to hyphens).
 | 
			
		||||
		"____/____",
 | 
			
		||||
 | 
			
		||||
		"_docker/_docker",
 | 
			
		||||
 | 
			
		||||
		// Disallow consecutive periods.
 | 
			
		||||
		"dock..er/docker",
 | 
			
		||||
		"dock_.er/docker",
 | 
			
		||||
		"dock-.er/docker",
 | 
			
		||||
 | 
			
		||||
		// No repository.
 | 
			
		||||
		"docker/",
 | 
			
		||||
 | 
			
		||||
		//namespace too long
 | 
			
		||||
		"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
 | 
			
		||||
	}
 | 
			
		||||
	for _, repositoryName := range invalidRepositoryNames {
 | 
			
		||||
		if _, err := ParseNamed(repositoryName); err == nil {
 | 
			
		||||
			t.Errorf("Repository name should be invalid: %v", repositoryName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		||||
	type tcase struct {
 | 
			
		||||
		RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tcases := []tcase{
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "fooo/bar",
 | 
			
		||||
			NormalizedName: "fooo/bar",
 | 
			
		||||
			FullName:       "docker.io/fooo/bar",
 | 
			
		||||
			AmbiguousName:  "index.docker.io/fooo/bar",
 | 
			
		||||
			Hostname:       "docker.io",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "library/ubuntu",
 | 
			
		||||
			NormalizedName: "ubuntu",
 | 
			
		||||
			FullName:       "docker.io/library/ubuntu",
 | 
			
		||||
			AmbiguousName:  "library/ubuntu",
 | 
			
		||||
			Hostname:       "docker.io",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "nonlibrary/ubuntu",
 | 
			
		||||
			NormalizedName: "nonlibrary/ubuntu",
 | 
			
		||||
			FullName:       "docker.io/nonlibrary/ubuntu",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "docker.io",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "other/library",
 | 
			
		||||
			NormalizedName: "other/library",
 | 
			
		||||
			FullName:       "docker.io/other/library",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "docker.io",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "private/moonbase",
 | 
			
		||||
			NormalizedName: "127.0.0.1:8000/private/moonbase",
 | 
			
		||||
			FullName:       "127.0.0.1:8000/private/moonbase",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "127.0.0.1:8000",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "privatebase",
 | 
			
		||||
			NormalizedName: "127.0.0.1:8000/privatebase",
 | 
			
		||||
			FullName:       "127.0.0.1:8000/privatebase",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "127.0.0.1:8000",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "private/moonbase",
 | 
			
		||||
			NormalizedName: "example.com/private/moonbase",
 | 
			
		||||
			FullName:       "example.com/private/moonbase",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "example.com",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "privatebase",
 | 
			
		||||
			NormalizedName: "example.com/privatebase",
 | 
			
		||||
			FullName:       "example.com/privatebase",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "example.com",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "private/moonbase",
 | 
			
		||||
			NormalizedName: "example.com:8000/private/moonbase",
 | 
			
		||||
			FullName:       "example.com:8000/private/moonbase",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "example.com:8000",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "privatebasee",
 | 
			
		||||
			NormalizedName: "example.com:8000/privatebasee",
 | 
			
		||||
			FullName:       "example.com:8000/privatebasee",
 | 
			
		||||
			AmbiguousName:  "",
 | 
			
		||||
			Hostname:       "example.com:8000",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			RemoteName:     "library/ubuntu-12.04-base",
 | 
			
		||||
			NormalizedName: "ubuntu-12.04-base",
 | 
			
		||||
			FullName:       "docker.io/library/ubuntu-12.04-base",
 | 
			
		||||
			AmbiguousName:  "index.docker.io/library/ubuntu-12.04-base",
 | 
			
		||||
			Hostname:       "docker.io",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tcase := range tcases {
 | 
			
		||||
		refStrings := []string{tcase.NormalizedName, tcase.FullName}
 | 
			
		||||
		if tcase.AmbiguousName != "" {
 | 
			
		||||
			refStrings = append(refStrings, tcase.AmbiguousName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var refs []Named
 | 
			
		||||
		for _, r := range refStrings {
 | 
			
		||||
			named, err := ParseNamed(r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			refs = append(refs, named)
 | 
			
		||||
			named, err = WithName(r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			refs = append(refs, named)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, r := range refs {
 | 
			
		||||
			if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
 | 
			
		||||
				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
 | 
			
		||||
			}
 | 
			
		||||
			if expected, actual := tcase.FullName, r.FullName(); expected != actual {
 | 
			
		||||
				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
 | 
			
		||||
			}
 | 
			
		||||
			if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
 | 
			
		||||
				t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
 | 
			
		||||
			}
 | 
			
		||||
			if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
 | 
			
		||||
				t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseReferenceWithTagAndDigest(t *testing.T) {
 | 
			
		||||
	ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, isTagged := ref.(NamedTagged); isTagged {
 | 
			
		||||
		t.Fatalf("Reference from %q should not support tag", ref)
 | 
			
		||||
	}
 | 
			
		||||
	if _, isCanonical := ref.(Canonical); !isCanonical {
 | 
			
		||||
		t.Fatalf("Reference from %q should not support digest", ref)
 | 
			
		||||
	}
 | 
			
		||||
	if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
 | 
			
		||||
		t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInvalidReferenceComponents(t *testing.T) {
 | 
			
		||||
	if _, err := WithName("-foo"); err == nil {
 | 
			
		||||
		t.Fatal("Expected WithName to detect invalid name")
 | 
			
		||||
	}
 | 
			
		||||
	ref, err := WithName("busybox")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := WithTag(ref, "-foo"); err == nil {
 | 
			
		||||
		t.Fatal("Expected WithName to detect invalid tag")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
 | 
			
		||||
		t.Fatal("Expected WithName to detect invalid digest")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -14,9 +14,6 @@ import (
 | 
			
		|||
	"github.com/docker/docker/image"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultTag defines the default tag used when performing images related actions and no tag string is specified
 | 
			
		||||
const DefaultTag = "latest"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrDoesNotExist is returned if a reference is not found in the
 | 
			
		||||
	// store.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,7 @@ import (
 | 
			
		|||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	distreference "github.com/docker/distribution/reference"
 | 
			
		||||
	registrytypes "github.com/docker/docker/api/types/registry"
 | 
			
		||||
	"github.com/docker/docker/image/v1"
 | 
			
		||||
	"github.com/docker/docker/opts"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/reference"
 | 
			
		||||
| 
						 | 
				
			
			@ -182,28 +180,15 @@ func ValidateMirror(val string) (string, error) {
 | 
			
		|||
 | 
			
		||||
// ValidateIndexName validates an index name.
 | 
			
		||||
func ValidateIndexName(val string) (string, error) {
 | 
			
		||||
	// 'index.docker.io' => 'docker.io'
 | 
			
		||||
	if val == "index."+IndexName {
 | 
			
		||||
		val = IndexName
 | 
			
		||||
	if val == reference.LegacyDefaultHostname {
 | 
			
		||||
		val = reference.DefaultHostname
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
 | 
			
		||||
		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
 | 
			
		||||
	}
 | 
			
		||||
	// *TODO: Check if valid hostname[:port]/ip[:port]?
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRemoteName(remoteName reference.Named) error {
 | 
			
		||||
	remoteNameStr := remoteName.Name()
 | 
			
		||||
	if !strings.Contains(remoteNameStr, "/") {
 | 
			
		||||
		// the repository name must not be a valid image ID
 | 
			
		||||
		if err := v1.ValidateID(remoteNameStr); err == nil {
 | 
			
		||||
			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateNoSchema(reposName string) error {
 | 
			
		||||
	if strings.Contains(reposName, "://") {
 | 
			
		||||
		// It cannot contain a scheme!
 | 
			
		||||
| 
						 | 
				
			
			@ -212,29 +197,6 @@ func validateNoSchema(reposName string) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateRepositoryName validates a repository name
 | 
			
		||||
func ValidateRepositoryName(reposName reference.Named) error {
 | 
			
		||||
	_, _, err := loadRepositoryName(reposName)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// loadRepositoryName returns the repo name splitted into index name
 | 
			
		||||
// and remote repo name. It returns an error if the name is not valid.
 | 
			
		||||
func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
 | 
			
		||||
	if err := validateNoSchema(reposName.Name()); err != nil {
 | 
			
		||||
		return "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
	indexName, remoteName, err := splitReposName(reposName)
 | 
			
		||||
 | 
			
		||||
	if indexName, err = ValidateIndexName(indexName); err != nil {
 | 
			
		||||
		return "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err = validateRemoteName(remoteName); err != nil {
 | 
			
		||||
		return "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return indexName, remoteName, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newIndexInfo returns IndexInfo configuration from indexName
 | 
			
		||||
func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
| 
						 | 
				
			
			@ -267,75 +229,14 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
 | 
			
		|||
	return index.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// splitReposName breaks a reposName into an index name and remote name
 | 
			
		||||
func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
 | 
			
		||||
	var remoteNameStr string
 | 
			
		||||
	indexName, remoteNameStr = distreference.SplitHostname(reposName)
 | 
			
		||||
	if indexName == "" || (!strings.Contains(indexName, ".") &&
 | 
			
		||||
		!strings.Contains(indexName, ":") && indexName != "localhost") {
 | 
			
		||||
		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
 | 
			
		||||
		// 'docker.io'
 | 
			
		||||
		indexName = IndexName
 | 
			
		||||
		remoteName = reposName
 | 
			
		||||
	} else {
 | 
			
		||||
		remoteName, err = reference.WithName(remoteNameStr)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
 | 
			
		||||
func newRepositoryInfo(config *registrytypes.ServiceConfig, reposName reference.Named) (*RepositoryInfo, error) {
 | 
			
		||||
	if err := validateNoSchema(reposName.Name()); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repoInfo := &RepositoryInfo{}
 | 
			
		||||
	var (
 | 
			
		||||
		indexName string
 | 
			
		||||
		err       error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
 | 
			
		||||
func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) {
 | 
			
		||||
	index, err := newIndexInfo(config, name.Hostname())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repoInfo.Index, err = newIndexInfo(config, indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if repoInfo.Index.Official {
 | 
			
		||||
		repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		repoInfo.RemoteName = repoInfo.LocalName
 | 
			
		||||
 | 
			
		||||
		// If the normalized name does not contain a '/' (e.g. "foo")
 | 
			
		||||
		// then it is an official repo.
 | 
			
		||||
		if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
 | 
			
		||||
			repoInfo.Official = true
 | 
			
		||||
			// Fix up remote name for official repos.
 | 
			
		||||
			repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		repoInfo.CanonicalName = repoInfo.LocalName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return repoInfo, nil
 | 
			
		||||
	official := !strings.ContainsRune(name.Name(), '/')
 | 
			
		||||
	return &RepositoryInfo{name, index, official}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
 | 
			
		||||
| 
						 | 
				
			
			@ -354,70 +255,3 @@ func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
 | 
			
		|||
	}
 | 
			
		||||
	return indexInfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeLocalName transforms a repository name into a normalized LocalName
 | 
			
		||||
// Passes through the name without transformation on error (image id, etc)
 | 
			
		||||
// It does not use the repository info because we don't want to load
 | 
			
		||||
// the repository index and do request over the network.
 | 
			
		||||
func NormalizeLocalName(name reference.Named) reference.Named {
 | 
			
		||||
	indexName, remoteName, err := loadRepositoryName(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var officialIndex bool
 | 
			
		||||
	// Return any configured index info, first.
 | 
			
		||||
	if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
 | 
			
		||||
		officialIndex = index.Official
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if officialIndex {
 | 
			
		||||
		localName, err := normalizeLibraryRepoName(remoteName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return name
 | 
			
		||||
		}
 | 
			
		||||
		return localName
 | 
			
		||||
	}
 | 
			
		||||
	localName, err := localNameFromRemote(indexName, remoteName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
	return localName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// normalizeLibraryRepoName removes the library prefix from
 | 
			
		||||
// the repository name for official repos.
 | 
			
		||||
func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
 | 
			
		||||
	if strings.HasPrefix(name.Name(), "library/") {
 | 
			
		||||
		// If pull "library/foo", it's stored locally under "foo"
 | 
			
		||||
		return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
 | 
			
		||||
	}
 | 
			
		||||
	return name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// localNameFromRemote combines the index name and the repo remote name
 | 
			
		||||
// to generate a repo local name.
 | 
			
		||||
func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) {
 | 
			
		||||
	return reference.WithName(indexName + "/" + remoteName.Name())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeLocalReference transforms a reference to use a normalized LocalName
 | 
			
		||||
// for the name poriton. Passes through the reference without transformation on
 | 
			
		||||
// error.
 | 
			
		||||
func NormalizeLocalReference(ref reference.Named) reference.Named {
 | 
			
		||||
	localName := NormalizeLocalName(ref)
 | 
			
		||||
	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
 | 
			
		||||
		newRef, err := reference.WithTag(localName, tagged.Tag())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return ref
 | 
			
		||||
		}
 | 
			
		||||
		return newRef
 | 
			
		||||
	} else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
 | 
			
		||||
		newRef, err := reference.WithDigest(localName, digested.Digest())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return ref
 | 
			
		||||
		}
 | 
			
		||||
		return newRef
 | 
			
		||||
	}
 | 
			
		||||
	return localName
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -356,7 +356,6 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
		apiError(w, "Could not parse repository", 400)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	repositoryName = NormalizeLocalName(repositoryName)
 | 
			
		||||
	tags, exists := testRepositories[repositoryName.String()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		apiError(w, "Repository not found", 404)
 | 
			
		||||
| 
						 | 
				
			
			@ -380,7 +379,6 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
		apiError(w, "Could not parse repository", 400)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	repositoryName = NormalizeLocalName(repositoryName)
 | 
			
		||||
	tagName := vars["tag"]
 | 
			
		||||
	tags, exists := testRepositories[repositoryName.String()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
| 
						 | 
				
			
			@ -405,7 +403,6 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
		apiError(w, "Could not parse repository", 400)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	repositoryName = NormalizeLocalName(repositoryName)
 | 
			
		||||
	tagName := vars["tag"]
 | 
			
		||||
	tags, exists := testRepositories[repositoryName.String()]
 | 
			
		||||
	if !exists {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -307,71 +307,24 @@ func TestPushImageLayerRegistry(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidateRepositoryName(t *testing.T) {
 | 
			
		||||
	validRepoNames := []string{
 | 
			
		||||
		"docker/docker",
 | 
			
		||||
		"library/debian",
 | 
			
		||||
		"debian",
 | 
			
		||||
		"docker.io/docker/docker",
 | 
			
		||||
		"docker.io/library/debian",
 | 
			
		||||
		"docker.io/debian",
 | 
			
		||||
		"index.docker.io/docker/docker",
 | 
			
		||||
		"index.docker.io/library/debian",
 | 
			
		||||
		"index.docker.io/debian",
 | 
			
		||||
		"127.0.0.1:5000/docker/docker",
 | 
			
		||||
		"127.0.0.1:5000/library/debian",
 | 
			
		||||
		"127.0.0.1:5000/debian",
 | 
			
		||||
		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
 | 
			
		||||
	}
 | 
			
		||||
	invalidRepoNames := []string{
 | 
			
		||||
		"https://github.com/docker/docker",
 | 
			
		||||
		"docker/Docker",
 | 
			
		||||
		"-docker",
 | 
			
		||||
		"-docker/docker",
 | 
			
		||||
		"-docker.io/docker/docker",
 | 
			
		||||
		"docker///docker",
 | 
			
		||||
		"docker.io/docker/Docker",
 | 
			
		||||
		"docker.io/docker///docker",
 | 
			
		||||
		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range invalidRepoNames {
 | 
			
		||||
		named, err := reference.WithName(name)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			err := ValidateRepositoryName(named)
 | 
			
		||||
			assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range validRepoNames {
 | 
			
		||||
		named, err := reference.WithName(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("could not parse valid name: %s", name)
 | 
			
		||||
		}
 | 
			
		||||
		err = ValidateRepositoryName(named)
 | 
			
		||||
		assertEqual(t, err, nil, "Expected valid repo name: "+name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		||||
	withName := func(name string) reference.Named {
 | 
			
		||||
		named, err := reference.WithName(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("could not parse reference %s", name)
 | 
			
		||||
		}
 | 
			
		||||
		return named
 | 
			
		||||
	type staticRepositoryInfo struct {
 | 
			
		||||
		Index         *registrytypes.IndexInfo
 | 
			
		||||
		RemoteName    string
 | 
			
		||||
		CanonicalName string
 | 
			
		||||
		LocalName     string
 | 
			
		||||
		Official      bool
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedRepoInfos := map[string]RepositoryInfo{
 | 
			
		||||
	expectedRepoInfos := map[string]staticRepositoryInfo{
 | 
			
		||||
		"fooo/bar": {
 | 
			
		||||
			Index: ®istrytypes.IndexInfo{
 | 
			
		||||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("fooo/bar"),
 | 
			
		||||
			LocalName:     withName("fooo/bar"),
 | 
			
		||||
			CanonicalName: withName("docker.io/fooo/bar"),
 | 
			
		||||
			RemoteName:    "fooo/bar",
 | 
			
		||||
			LocalName:     "fooo/bar",
 | 
			
		||||
			CanonicalName: "docker.io/fooo/bar",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"library/ubuntu": {
 | 
			
		||||
| 
						 | 
				
			
			@ -379,9 +332,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("library/ubuntu"),
 | 
			
		||||
			LocalName:     withName("ubuntu"),
 | 
			
		||||
			CanonicalName: withName("docker.io/library/ubuntu"),
 | 
			
		||||
			RemoteName:    "library/ubuntu",
 | 
			
		||||
			LocalName:     "ubuntu",
 | 
			
		||||
			CanonicalName: "docker.io/library/ubuntu",
 | 
			
		||||
			Official:      true,
 | 
			
		||||
		},
 | 
			
		||||
		"nonlibrary/ubuntu": {
 | 
			
		||||
| 
						 | 
				
			
			@ -389,9 +342,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("nonlibrary/ubuntu"),
 | 
			
		||||
			LocalName:     withName("nonlibrary/ubuntu"),
 | 
			
		||||
			CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
 | 
			
		||||
			RemoteName:    "nonlibrary/ubuntu",
 | 
			
		||||
			LocalName:     "nonlibrary/ubuntu",
 | 
			
		||||
			CanonicalName: "docker.io/nonlibrary/ubuntu",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"ubuntu": {
 | 
			
		||||
| 
						 | 
				
			
			@ -399,9 +352,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("library/ubuntu"),
 | 
			
		||||
			LocalName:     withName("ubuntu"),
 | 
			
		||||
			CanonicalName: withName("docker.io/library/ubuntu"),
 | 
			
		||||
			RemoteName:    "library/ubuntu",
 | 
			
		||||
			LocalName:     "ubuntu",
 | 
			
		||||
			CanonicalName: "docker.io/library/ubuntu",
 | 
			
		||||
			Official:      true,
 | 
			
		||||
		},
 | 
			
		||||
		"other/library": {
 | 
			
		||||
| 
						 | 
				
			
			@ -409,9 +362,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("other/library"),
 | 
			
		||||
			LocalName:     withName("other/library"),
 | 
			
		||||
			CanonicalName: withName("docker.io/other/library"),
 | 
			
		||||
			RemoteName:    "other/library",
 | 
			
		||||
			LocalName:     "other/library",
 | 
			
		||||
			CanonicalName: "docker.io/other/library",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"127.0.0.1:8000/private/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -419,9 +372,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "127.0.0.1:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("private/moonbase"),
 | 
			
		||||
			LocalName:     withName("127.0.0.1:8000/private/moonbase"),
 | 
			
		||||
			CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
 | 
			
		||||
			RemoteName:    "private/moonbase",
 | 
			
		||||
			LocalName:     "127.0.0.1:8000/private/moonbase",
 | 
			
		||||
			CanonicalName: "127.0.0.1:8000/private/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"127.0.0.1:8000/privatebase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -429,9 +382,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "127.0.0.1:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("privatebase"),
 | 
			
		||||
			LocalName:     withName("127.0.0.1:8000/privatebase"),
 | 
			
		||||
			CanonicalName: withName("127.0.0.1:8000/privatebase"),
 | 
			
		||||
			RemoteName:    "privatebase",
 | 
			
		||||
			LocalName:     "127.0.0.1:8000/privatebase",
 | 
			
		||||
			CanonicalName: "127.0.0.1:8000/privatebase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"localhost:8000/private/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -439,9 +392,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "localhost:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("private/moonbase"),
 | 
			
		||||
			LocalName:     withName("localhost:8000/private/moonbase"),
 | 
			
		||||
			CanonicalName: withName("localhost:8000/private/moonbase"),
 | 
			
		||||
			RemoteName:    "private/moonbase",
 | 
			
		||||
			LocalName:     "localhost:8000/private/moonbase",
 | 
			
		||||
			CanonicalName: "localhost:8000/private/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"localhost:8000/privatebase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -449,9 +402,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "localhost:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("privatebase"),
 | 
			
		||||
			LocalName:     withName("localhost:8000/privatebase"),
 | 
			
		||||
			CanonicalName: withName("localhost:8000/privatebase"),
 | 
			
		||||
			RemoteName:    "privatebase",
 | 
			
		||||
			LocalName:     "localhost:8000/privatebase",
 | 
			
		||||
			CanonicalName: "localhost:8000/privatebase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"example.com/private/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -459,9 +412,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "example.com",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("private/moonbase"),
 | 
			
		||||
			LocalName:     withName("example.com/private/moonbase"),
 | 
			
		||||
			CanonicalName: withName("example.com/private/moonbase"),
 | 
			
		||||
			RemoteName:    "private/moonbase",
 | 
			
		||||
			LocalName:     "example.com/private/moonbase",
 | 
			
		||||
			CanonicalName: "example.com/private/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"example.com/privatebase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -469,9 +422,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "example.com",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("privatebase"),
 | 
			
		||||
			LocalName:     withName("example.com/privatebase"),
 | 
			
		||||
			CanonicalName: withName("example.com/privatebase"),
 | 
			
		||||
			RemoteName:    "privatebase",
 | 
			
		||||
			LocalName:     "example.com/privatebase",
 | 
			
		||||
			CanonicalName: "example.com/privatebase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"example.com:8000/private/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -479,9 +432,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "example.com:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("private/moonbase"),
 | 
			
		||||
			LocalName:     withName("example.com:8000/private/moonbase"),
 | 
			
		||||
			CanonicalName: withName("example.com:8000/private/moonbase"),
 | 
			
		||||
			RemoteName:    "private/moonbase",
 | 
			
		||||
			LocalName:     "example.com:8000/private/moonbase",
 | 
			
		||||
			CanonicalName: "example.com:8000/private/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"example.com:8000/privatebase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -489,9 +442,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "example.com:8000",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("privatebase"),
 | 
			
		||||
			LocalName:     withName("example.com:8000/privatebase"),
 | 
			
		||||
			CanonicalName: withName("example.com:8000/privatebase"),
 | 
			
		||||
			RemoteName:    "privatebase",
 | 
			
		||||
			LocalName:     "example.com:8000/privatebase",
 | 
			
		||||
			CanonicalName: "example.com:8000/privatebase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"localhost/private/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -499,9 +452,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "localhost",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("private/moonbase"),
 | 
			
		||||
			LocalName:     withName("localhost/private/moonbase"),
 | 
			
		||||
			CanonicalName: withName("localhost/private/moonbase"),
 | 
			
		||||
			RemoteName:    "private/moonbase",
 | 
			
		||||
			LocalName:     "localhost/private/moonbase",
 | 
			
		||||
			CanonicalName: "localhost/private/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"localhost/privatebase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -509,9 +462,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     "localhost",
 | 
			
		||||
				Official: false,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("privatebase"),
 | 
			
		||||
			LocalName:     withName("localhost/privatebase"),
 | 
			
		||||
			CanonicalName: withName("localhost/privatebase"),
 | 
			
		||||
			RemoteName:    "privatebase",
 | 
			
		||||
			LocalName:     "localhost/privatebase",
 | 
			
		||||
			CanonicalName: "localhost/privatebase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		IndexName + "/public/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -519,9 +472,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("public/moonbase"),
 | 
			
		||||
			LocalName:     withName("public/moonbase"),
 | 
			
		||||
			CanonicalName: withName("docker.io/public/moonbase"),
 | 
			
		||||
			RemoteName:    "public/moonbase",
 | 
			
		||||
			LocalName:     "public/moonbase",
 | 
			
		||||
			CanonicalName: "docker.io/public/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"index." + IndexName + "/public/moonbase": {
 | 
			
		||||
| 
						 | 
				
			
			@ -529,9 +482,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("public/moonbase"),
 | 
			
		||||
			LocalName:     withName("public/moonbase"),
 | 
			
		||||
			CanonicalName: withName("docker.io/public/moonbase"),
 | 
			
		||||
			RemoteName:    "public/moonbase",
 | 
			
		||||
			LocalName:     "public/moonbase",
 | 
			
		||||
			CanonicalName: "docker.io/public/moonbase",
 | 
			
		||||
			Official:      false,
 | 
			
		||||
		},
 | 
			
		||||
		"ubuntu-12.04-base": {
 | 
			
		||||
| 
						 | 
				
			
			@ -539,9 +492,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("library/ubuntu-12.04-base"),
 | 
			
		||||
			LocalName:     withName("ubuntu-12.04-base"),
 | 
			
		||||
			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
 | 
			
		||||
			RemoteName:    "library/ubuntu-12.04-base",
 | 
			
		||||
			LocalName:     "ubuntu-12.04-base",
 | 
			
		||||
			CanonicalName: "docker.io/library/ubuntu-12.04-base",
 | 
			
		||||
			Official:      true,
 | 
			
		||||
		},
 | 
			
		||||
		IndexName + "/ubuntu-12.04-base": {
 | 
			
		||||
| 
						 | 
				
			
			@ -549,9 +502,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("library/ubuntu-12.04-base"),
 | 
			
		||||
			LocalName:     withName("ubuntu-12.04-base"),
 | 
			
		||||
			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
 | 
			
		||||
			RemoteName:    "library/ubuntu-12.04-base",
 | 
			
		||||
			LocalName:     "ubuntu-12.04-base",
 | 
			
		||||
			CanonicalName: "docker.io/library/ubuntu-12.04-base",
 | 
			
		||||
			Official:      true,
 | 
			
		||||
		},
 | 
			
		||||
		"index." + IndexName + "/ubuntu-12.04-base": {
 | 
			
		||||
| 
						 | 
				
			
			@ -559,9 +512,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
				Name:     IndexName,
 | 
			
		||||
				Official: true,
 | 
			
		||||
			},
 | 
			
		||||
			RemoteName:    withName("library/ubuntu-12.04-base"),
 | 
			
		||||
			LocalName:     withName("ubuntu-12.04-base"),
 | 
			
		||||
			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
 | 
			
		||||
			RemoteName:    "library/ubuntu-12.04-base",
 | 
			
		||||
			LocalName:     "ubuntu-12.04-base",
 | 
			
		||||
			CanonicalName: "docker.io/library/ubuntu-12.04-base",
 | 
			
		||||
			Official:      true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -577,9 +530,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
			t.Error(err)
 | 
			
		||||
		} else {
 | 
			
		||||
			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
 | 
			
		||||
			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -806,82 +759,6 @@ func TestSearchRepositories(t *testing.T) {
 | 
			
		|||
	assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidRemoteName(t *testing.T) {
 | 
			
		||||
	validRepositoryNames := []string{
 | 
			
		||||
		// Sanity check.
 | 
			
		||||
		"docker/docker",
 | 
			
		||||
 | 
			
		||||
		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
 | 
			
		||||
		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
 | 
			
		||||
 | 
			
		||||
		// Allow embedded hyphens.
 | 
			
		||||
		"docker-rules/docker",
 | 
			
		||||
 | 
			
		||||
		// Allow multiple hyphens as well.
 | 
			
		||||
		"docker---rules/docker",
 | 
			
		||||
 | 
			
		||||
		//Username doc and image name docker being tested.
 | 
			
		||||
		"doc/docker",
 | 
			
		||||
 | 
			
		||||
		// single character names are now allowed.
 | 
			
		||||
		"d/docker",
 | 
			
		||||
		"jess/t",
 | 
			
		||||
 | 
			
		||||
		// Consecutive underscores.
 | 
			
		||||
		"dock__er/docker",
 | 
			
		||||
	}
 | 
			
		||||
	for _, repositoryName := range validRepositoryNames {
 | 
			
		||||
		repositoryRef, err := reference.WithName(repositoryName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := validateRemoteName(repositoryRef); err != nil {
 | 
			
		||||
			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	invalidRepositoryNames := []string{
 | 
			
		||||
		// Disallow capital letters.
 | 
			
		||||
		"docker/Docker",
 | 
			
		||||
 | 
			
		||||
		// Only allow one slash.
 | 
			
		||||
		"docker///docker",
 | 
			
		||||
 | 
			
		||||
		// Disallow 64-character hexadecimal.
 | 
			
		||||
		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
 | 
			
		||||
 | 
			
		||||
		// Disallow leading and trailing hyphens in namespace.
 | 
			
		||||
		"-docker/docker",
 | 
			
		||||
		"docker-/docker",
 | 
			
		||||
		"-docker-/docker",
 | 
			
		||||
 | 
			
		||||
		// Don't allow underscores everywhere (as opposed to hyphens).
 | 
			
		||||
		"____/____",
 | 
			
		||||
 | 
			
		||||
		"_docker/_docker",
 | 
			
		||||
 | 
			
		||||
		// Disallow consecutive periods.
 | 
			
		||||
		"dock..er/docker",
 | 
			
		||||
		"dock_.er/docker",
 | 
			
		||||
		"dock-.er/docker",
 | 
			
		||||
 | 
			
		||||
		// No repository.
 | 
			
		||||
		"docker/",
 | 
			
		||||
 | 
			
		||||
		//namespace too long
 | 
			
		||||
		"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
 | 
			
		||||
	}
 | 
			
		||||
	for _, repositoryName := range invalidRepositoryNames {
 | 
			
		||||
		repositoryRef, err := reference.ParseNamed(repositoryName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err := validateRemoteName(repositoryRef); err == nil {
 | 
			
		||||
			t.Errorf("Repository name should be invalid: %v", repositoryName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTrustedLocation(t *testing.T) {
 | 
			
		||||
	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
 | 
			
		||||
		req, _ := http.NewRequest("GET", url, nil)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import (
 | 
			
		|||
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
 | 
			
		||||
	var cfg = tlsconfig.ServerDefault
 | 
			
		||||
	tlsConfig := &cfg
 | 
			
		||||
	nameString := repoName.Name()
 | 
			
		||||
	nameString := repoName.FullName()
 | 
			
		||||
	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
 | 
			
		||||
		endpoints = append(endpoints, APIEndpoint{
 | 
			
		||||
			URL:          DefaultV1Registry,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ import (
 | 
			
		|||
func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
 | 
			
		||||
	var cfg = tlsconfig.ServerDefault
 | 
			
		||||
	tlsConfig := &cfg
 | 
			
		||||
	nameString := repoName.Name()
 | 
			
		||||
	nameString := repoName.FullName()
 | 
			
		||||
	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
 | 
			
		||||
		// v2 mirrors
 | 
			
		||||
		for _, mirror := range s.Config.Mirrors {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -312,7 +312,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
 | 
			
		|||
// argument, and returns data from the first one that answers the query
 | 
			
		||||
// successfully.
 | 
			
		||||
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
 | 
			
		||||
	repository := repositoryRef.Name()
 | 
			
		||||
	repository := repositoryRef.RemoteName()
 | 
			
		||||
 | 
			
		||||
	if strings.Count(repository, "/") == 0 {
 | 
			
		||||
		// This will be removed once the registry supports auto-resolution on
 | 
			
		||||
| 
						 | 
				
			
			@ -350,7 +350,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name
 | 
			
		|||
// the first one that answers the query successfully. It returns a map with
 | 
			
		||||
// tag names as the keys and image IDs as the values.
 | 
			
		||||
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
 | 
			
		||||
	repository := repositoryRef.Name()
 | 
			
		||||
	repository := repositoryRef.RemoteName()
 | 
			
		||||
 | 
			
		||||
	if strings.Count(repository, "/") == 0 {
 | 
			
		||||
		// This will be removed once the registry supports auto-resolution on
 | 
			
		||||
| 
						 | 
				
			
			@ -403,8 +403,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetRepositoryData returns lists of images and endpoints for the repository
 | 
			
		||||
func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
 | 
			
		||||
	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
 | 
			
		||||
func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
 | 
			
		||||
	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), name.RemoteName())
 | 
			
		||||
 | 
			
		||||
	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -438,7 +438,7 @@ func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, er
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			logrus.Debugf("Error reading response body: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
 | 
			
		||||
		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var endpoints []string
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +593,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
 | 
			
		|||
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
 | 
			
		||||
	// "jsonify" the string
 | 
			
		||||
	revision = "\"" + revision + "\""
 | 
			
		||||
	path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
 | 
			
		||||
	path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -607,7 +607,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
 | 
			
		|||
	}
 | 
			
		||||
	res.Body.Close()
 | 
			
		||||
	if res.StatusCode != 200 && res.StatusCode != 201 {
 | 
			
		||||
		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
 | 
			
		||||
		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -633,7 +633,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 | 
			
		|||
	if validate {
 | 
			
		||||
		suffix = "images"
 | 
			
		||||
	}
 | 
			
		||||
	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
 | 
			
		||||
	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.RemoteName(), suffix)
 | 
			
		||||
	logrus.Debugf("[registry] PUT %s", u)
 | 
			
		||||
	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
 | 
			
		||||
	headers := map[string][]string{
 | 
			
		||||
| 
						 | 
				
			
			@ -671,7 +671,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 | 
			
		|||
			if err != nil {
 | 
			
		||||
				logrus.Debugf("Error reading response body: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
 | 
			
		||||
			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
 | 
			
		||||
		}
 | 
			
		||||
		tokens = res.Header["X-Docker-Token"]
 | 
			
		||||
		logrus.Debugf("Auth token: %v", tokens)
 | 
			
		||||
| 
						 | 
				
			
			@ -689,7 +689,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 | 
			
		|||
			if err != nil {
 | 
			
		||||
				logrus.Debugf("Error reading response body: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
 | 
			
		||||
			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,17 +60,9 @@ const (
 | 
			
		|||
 | 
			
		||||
// RepositoryInfo describes a repository
 | 
			
		||||
type RepositoryInfo struct {
 | 
			
		||||
	reference.Named
 | 
			
		||||
	// Index points to registry information
 | 
			
		||||
	Index *registrytypes.IndexInfo
 | 
			
		||||
	// RemoteName is the remote name of the repository, such as
 | 
			
		||||
	// "library/ubuntu-12.04-base"
 | 
			
		||||
	RemoteName reference.Named
 | 
			
		||||
	// LocalName is the local name of the repository, such as
 | 
			
		||||
	// "ubuntu-12.04-base"
 | 
			
		||||
	LocalName reference.Named
 | 
			
		||||
	// CanonicalName is the canonical name of the repository, such as
 | 
			
		||||
	// "docker.io/library/ubuntu-12.04-base"
 | 
			
		||||
	CanonicalName reference.Named
 | 
			
		||||
	// Official indicates whether the repository is considered official.
 | 
			
		||||
	// If the registry is official, and the normalized name does not
 | 
			
		||||
	// contain a '/' (e.g. "foo"), then it is considered an official repo.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue