Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -260,15 +260,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 260 | 260 |
|
| 261 | 261 |
// validateTag checks if the given image name can be resolved. |
| 262 | 262 |
func validateTag(rawRepo string) (string, error) {
|
| 263 |
- ref, err := reference.ParseNamed(rawRepo) |
|
| 263 |
+ _, err := reference.ParseNamed(rawRepo) |
|
| 264 | 264 |
if err != nil {
|
| 265 | 265 |
return "", err |
| 266 | 266 |
} |
| 267 | 267 |
|
| 268 |
- if err := registry.ValidateRepositoryName(ref); err != nil {
|
|
| 269 |
- return "", err |
|
| 270 |
- } |
|
| 271 |
- |
|
| 272 | 268 |
return rawRepo, nil |
| 273 | 269 |
} |
| 274 | 270 |
|
| ... | ... |
@@ -10,7 +10,6 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/opts" |
| 11 | 11 |
flag "github.com/docker/docker/pkg/mflag" |
| 12 | 12 |
"github.com/docker/docker/reference" |
| 13 |
- "github.com/docker/docker/registry" |
|
| 14 | 13 |
"github.com/docker/docker/runconfig" |
| 15 | 14 |
) |
| 16 | 15 |
|
| ... | ... |
@@ -44,9 +43,6 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
| 44 | 44 |
if err != nil {
|
| 45 | 45 |
return err |
| 46 | 46 |
} |
| 47 |
- if err := registry.ValidateRepositoryName(ref); err != nil {
|
|
| 48 |
- return err |
|
| 49 |
- } |
|
| 50 | 47 |
|
| 51 | 48 |
repositoryName = ref.Name() |
| 52 | 49 |
|
| ... | ... |
@@ -12,7 +12,6 @@ import ( |
| 12 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 13 | 13 |
"github.com/docker/docker/pkg/urlutil" |
| 14 | 14 |
"github.com/docker/docker/reference" |
| 15 |
- "github.com/docker/docker/registry" |
|
| 16 | 15 |
) |
| 17 | 16 |
|
| 18 | 17 |
// 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 {
|
| 45 | 45 |
|
| 46 | 46 |
if repository != "" {
|
| 47 | 47 |
//Check if the given image name can be resolved |
| 48 |
- ref, err := reference.ParseNamed(repository) |
|
| 49 |
- if err != nil {
|
|
| 50 |
- return err |
|
| 51 |
- } |
|
| 52 |
- if err := registry.ValidateRepositoryName(ref); err != nil {
|
|
| 48 |
+ if _, err := reference.ParseNamed(repository); err != nil {
|
|
| 53 | 49 |
return err |
| 54 | 50 |
} |
| 55 | 51 |
} |
| ... | ... |
@@ -7,7 +7,6 @@ import ( |
| 7 | 7 |
Cli "github.com/docker/docker/cli" |
| 8 | 8 |
flag "github.com/docker/docker/pkg/mflag" |
| 9 | 9 |
"github.com/docker/docker/reference" |
| 10 |
- "github.com/docker/docker/registry" |
|
| 11 | 10 |
) |
| 12 | 11 |
|
| 13 | 12 |
// CmdTag tags an image into a repository. |
| ... | ... |
@@ -35,11 +34,6 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
| 35 | 35 |
tag = tagged.Tag() |
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
- //Check if the given image name can be resolved |
|
| 39 |
- if err := registry.ValidateRepositoryName(ref); err != nil {
|
|
| 40 |
- return err |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 | 38 |
options := types.ImageTagOptions{
|
| 44 | 39 |
ImageID: cmd.Arg(0), |
| 45 | 40 |
RepositoryName: ref.Name(), |
| ... | ... |
@@ -167,12 +167,12 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut |
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
creds := simpleCredentialStore{auth: authConfig}
|
| 170 |
- tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName.Name(), "push", "pull") |
|
| 170 |
+ tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), "push", "pull") |
|
| 171 | 171 |
basicHandler := auth.NewBasicHandler(creds) |
| 172 | 172 |
modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))) |
| 173 | 173 |
tr := transport.NewTransport(base, modifiers...) |
| 174 | 174 |
|
| 175 |
- return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName.Name(), server, tr, cli.getPassphraseRetriever()) |
|
| 175 |
+ return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.FullName(), server, tr, cli.getPassphraseRetriever()) |
|
| 176 | 176 |
} |
| 177 | 177 |
|
| 178 | 178 |
func convertTarget(t client.Target) (target, error) {
|
| ... | ... |
@@ -298,7 +298,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr |
| 298 | 298 |
for _, tgt := range targets {
|
| 299 | 299 |
t, err := convertTarget(*tgt) |
| 300 | 300 |
if err != nil {
|
| 301 |
- fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.LocalName) |
|
| 301 |
+ fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name()) |
|
| 302 | 302 |
continue |
| 303 | 303 |
} |
| 304 | 304 |
refs = append(refs, t) |
| ... | ... |
@@ -321,19 +321,19 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr |
| 321 | 321 |
if displayTag != "" {
|
| 322 | 322 |
displayTag = ":" + displayTag |
| 323 | 323 |
} |
| 324 |
- fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest) |
|
| 324 |
+ fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest) |
|
| 325 | 325 |
|
| 326 |
- if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil {
|
|
| 326 |
+ if err := cli.imagePullPrivileged(authConfig, repoInfo.Name(), r.digest.String(), requestPrivilege); err != nil {
|
|
| 327 | 327 |
return err |
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 | 330 |
// If reference is not trusted, tag by trusted reference |
| 331 | 331 |
if !r.reference.HasDigest() {
|
| 332 |
- tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String()) |
|
| 332 |
+ tagged, err := reference.WithTag(repoInfo, r.reference.String()) |
|
| 333 | 333 |
if err != nil {
|
| 334 | 334 |
return err |
| 335 | 335 |
} |
| 336 |
- trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest) |
|
| 336 |
+ trustedRef, err := reference.WithDigest(repoInfo, r.digest) |
|
| 337 | 337 |
if err := cli.tagTrusted(trustedRef, tagged); err != nil {
|
| 338 | 338 |
return err |
| 339 | 339 |
|
| ... | ... |
@@ -384,7 +384,7 @@ func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
|
| 384 | 384 |
func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig types.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error {
|
| 385 | 385 |
streamOut, targetChan := targetStream(cli.out) |
| 386 | 386 |
|
| 387 |
- reqError := cli.imagePushPrivileged(authConfig, repoInfo.LocalName.Name(), tag, streamOut, requestPrivilege) |
|
| 387 |
+ reqError := cli.imagePushPrivileged(authConfig, repoInfo.Name(), tag, streamOut, requestPrivilege) |
|
| 388 | 388 |
|
| 389 | 389 |
// Close stream channel to finish target parsing |
| 390 | 390 |
if err := streamOut.Close(); err != nil {
|
| ... | ... |
@@ -455,7 +455,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, |
| 455 | 455 |
if err := repo.Initialize(rootKeyID); err != nil {
|
| 456 | 456 |
return notaryError(err) |
| 457 | 457 |
} |
| 458 |
- fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.CanonicalName) |
|
| 458 |
+ fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName()) |
|
| 459 | 459 |
|
| 460 | 460 |
return notaryError(repo.Publish()) |
| 461 | 461 |
} |
| ... | ... |
@@ -1043,7 +1043,6 @@ func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error {
|
| 1043 | 1043 |
if err != nil {
|
| 1044 | 1044 |
return err |
| 1045 | 1045 |
} |
| 1046 |
- newTag = registry.NormalizeLocalReference(newTag) |
|
| 1047 | 1046 |
if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil {
|
| 1048 | 1047 |
return err |
| 1049 | 1048 |
} |
| ... | ... |
@@ -1301,7 +1300,6 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
| 1301 | 1301 |
|
| 1302 | 1302 |
// Treat it as a possible tag or digest reference |
| 1303 | 1303 |
if ref, err := reference.ParseNamed(refOrID); err == nil {
|
| 1304 |
- ref = registry.NormalizeLocalReference(ref) |
|
| 1305 | 1304 |
if id, err := daemon.referenceStore.Get(ref); err == nil {
|
| 1306 | 1305 |
return id, nil |
| 1307 | 1306 |
} |
| ... | ... |
@@ -87,17 +87,15 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 | 89 |
// makes sure name is not empty or `scratch` |
| 90 |
- if err := validateRepoName(repoInfo.LocalName.Name()); err != nil {
|
|
| 90 |
+ if err := validateRepoName(repoInfo.Name()); err != nil {
|
|
| 91 | 91 |
return err |
| 92 | 92 |
} |
| 93 | 93 |
|
| 94 |
- endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.CanonicalName) |
|
| 94 |
+ endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo) |
|
| 95 | 95 |
if err != nil {
|
| 96 | 96 |
return err |
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 |
- localName := registry.NormalizeLocalReference(ref) |
|
| 100 |
- |
|
| 101 | 99 |
var ( |
| 102 | 100 |
// use a slice to append the error strings and return a joined string to caller |
| 103 | 101 |
errors []string |
| ... | ... |
@@ -112,7 +110,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 112 | 112 |
discardNoSupportErrors bool |
| 113 | 113 |
) |
| 114 | 114 |
for _, endpoint := range endpoints {
|
| 115 |
- logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version)
|
|
| 115 |
+ logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
|
|
| 116 | 116 |
|
| 117 | 117 |
puller, err := newPuller(endpoint, repoInfo, imagePullConfig) |
| 118 | 118 |
if err != nil {
|
| ... | ... |
@@ -148,7 +146,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 148 | 148 |
} |
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 |
- imagePullConfig.EventsService.Log("pull", localName.String(), "")
|
|
| 151 |
+ imagePullConfig.EventsService.Log("pull", ref.String(), "")
|
|
| 152 | 152 |
return nil |
| 153 | 153 |
} |
| 154 | 154 |
|
| ... | ... |
@@ -65,18 +65,18 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool |
| 65 | 65 |
// TODO(dmcgowan): Check if should fallback |
| 66 | 66 |
return false, err |
| 67 | 67 |
} |
| 68 |
- 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.") |
|
| 68 |
+ 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.") |
|
| 69 | 69 |
|
| 70 | 70 |
return false, nil |
| 71 | 71 |
} |
| 72 | 72 |
|
| 73 | 73 |
func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
|
| 74 |
- progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.CanonicalName.Name()) |
|
| 74 |
+ progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName()) |
|
| 75 | 75 |
|
| 76 |
- repoData, err := p.session.GetRepositoryData(p.repoInfo.RemoteName) |
|
| 76 |
+ repoData, err := p.session.GetRepositoryData(p.repoInfo) |
|
| 77 | 77 |
if err != nil {
|
| 78 | 78 |
if strings.Contains(err.Error(), "HTTP code: 404") {
|
| 79 |
- return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName.Name())
|
|
| 79 |
+ return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
|
|
| 80 | 80 |
} |
| 81 | 81 |
// Unexpected HTTP error |
| 82 | 82 |
return err |
| ... | ... |
@@ -86,13 +86,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro |
| 86 | 86 |
var tagsList map[string]string |
| 87 | 87 |
tagged, isTagged := ref.(reference.NamedTagged) |
| 88 | 88 |
if !isTagged {
|
| 89 |
- tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName) |
|
| 89 |
+ tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo) |
|
| 90 | 90 |
} else {
|
| 91 | 91 |
var tagID string |
| 92 | 92 |
tagsList = make(map[string]string) |
| 93 |
- tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, tagged.Tag()) |
|
| 93 |
+ tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag()) |
|
| 94 | 94 |
if err == registry.ErrRepoNotFound {
|
| 95 |
- return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.CanonicalName.Name())
|
|
| 95 |
+ return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
|
|
| 96 | 96 |
} |
| 97 | 97 |
tagsList[tagged.Tag()] = tagID |
| 98 | 98 |
} |
| ... | ... |
@@ -121,14 +121,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro |
| 121 | 121 |
} |
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 |
- localNameRef := p.repoInfo.LocalName |
|
| 125 |
- if isTagged {
|
|
| 126 |
- localNameRef, err = reference.WithTag(localNameRef, tagged.Tag()) |
|
| 127 |
- if err != nil {
|
|
| 128 |
- localNameRef = p.repoInfo.LocalName |
|
| 129 |
- } |
|
| 130 |
- } |
|
| 131 |
- writeStatus(localNameRef.String(), p.config.ProgressOutput, layersDownloaded) |
|
| 124 |
+ writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded) |
|
| 132 | 125 |
return nil |
| 133 | 126 |
} |
| 134 | 127 |
|
| ... | ... |
@@ -138,7 +131,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 138 | 138 |
return nil |
| 139 | 139 |
} |
| 140 | 140 |
|
| 141 |
- localNameRef, err := reference.WithTag(p.repoInfo.LocalName, img.Tag) |
|
| 141 |
+ localNameRef, err := reference.WithTag(p.repoInfo, img.Tag) |
|
| 142 | 142 |
if err != nil {
|
| 143 | 143 |
retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
|
| 144 | 144 |
logrus.Debug(retErr.Error()) |
| ... | ... |
@@ -149,15 +142,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 149 | 149 |
return err |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName.Name()) |
|
| 152 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName()) |
|
| 153 | 153 |
success := false |
| 154 | 154 |
var lastErr error |
| 155 | 155 |
for _, ep := range p.repoInfo.Index.Mirrors {
|
| 156 | 156 |
ep += "v1/" |
| 157 |
- 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))
|
|
| 157 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
|
|
| 158 | 158 |
if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
|
| 159 | 159 |
// Don't report errors when pulling from mirrors. |
| 160 |
- logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err)
|
|
| 160 |
+ logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
|
|
| 161 | 161 |
continue |
| 162 | 162 |
} |
| 163 | 163 |
success = true |
| ... | ... |
@@ -165,12 +158,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 165 | 165 |
} |
| 166 | 166 |
if !success {
|
| 167 | 167 |
for _, ep := range repoData.Endpoints {
|
| 168 |
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep) |
|
| 168 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep) |
|
| 169 | 169 |
if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
|
| 170 | 170 |
// It's not ideal that only the last error is returned, it would be better to concatenate the errors. |
| 171 | 171 |
// As the error is also given to the output stream the user will see the error. |
| 172 | 172 |
lastErr = err |
| 173 |
- 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) |
|
| 173 |
+ 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) |
|
| 174 | 174 |
continue |
| 175 | 175 |
} |
| 176 | 176 |
success = true |
| ... | ... |
@@ -178,7 +171,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 178 | 178 |
} |
| 179 | 179 |
} |
| 180 | 180 |
if !success {
|
| 181 |
- err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.CanonicalName.Name(), lastErr)
|
|
| 181 |
+ err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
|
|
| 182 | 182 |
progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error()) |
| 183 | 183 |
return err |
| 184 | 184 |
} |
| ... | ... |
@@ -54,19 +54,11 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool |
| 54 | 54 |
|
| 55 | 55 |
func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) {
|
| 56 | 56 |
var refs []reference.Named |
| 57 |
- taggedName := p.repoInfo.LocalName |
|
| 58 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 59 |
- taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag()) |
|
| 60 |
- if err != nil {
|
|
| 61 |
- return err |
|
| 62 |
- } |
|
| 63 |
- refs = []reference.Named{taggedName}
|
|
| 64 |
- } else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 65 |
- taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest()) |
|
| 66 |
- if err != nil {
|
|
| 67 |
- return err |
|
| 68 |
- } |
|
| 69 |
- refs = []reference.Named{taggedName}
|
|
| 57 |
+ taggedName := ref |
|
| 58 |
+ if _, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 59 |
+ refs = []reference.Named{ref}
|
|
| 60 |
+ } else if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 61 |
+ refs = []reference.Named{ref}
|
|
| 70 | 62 |
} else {
|
| 71 | 63 |
manSvc, err := p.repo.Manifests(ctx) |
| 72 | 64 |
if err != nil {
|
| ... | ... |
@@ -81,7 +73,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e |
| 81 | 81 |
// This probably becomes a lot nicer after the manifest |
| 82 | 82 |
// refactor... |
| 83 | 83 |
for _, tag := range tags {
|
| 84 |
- tagRef, err := reference.WithTag(p.repoInfo.LocalName, tag) |
|
| 84 |
+ tagRef, err := reference.WithTag(ref, tag) |
|
| 85 | 85 |
if err != nil {
|
| 86 | 86 |
return err |
| 87 | 87 |
} |
| ... | ... |
@@ -291,7 +283,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat |
| 291 | 291 |
return false, err |
| 292 | 292 |
} |
| 293 | 293 |
|
| 294 |
- manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo.LocalName.Name()) |
|
| 294 |
+ manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo) |
|
| 295 | 295 |
if err != nil {
|
| 296 | 296 |
return false, err |
| 297 | 297 |
} |
| ... | ... |
@@ -104,21 +104,21 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo |
| 104 | 104 |
return err |
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 |
- endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.CanonicalName) |
|
| 107 |
+ endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo) |
|
| 108 | 108 |
if err != nil {
|
| 109 | 109 |
return err |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
- progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.CanonicalName.String()) |
|
| 112 |
+ progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName()) |
|
| 113 | 113 |
|
| 114 |
- associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.LocalName) |
|
| 114 |
+ associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo) |
|
| 115 | 115 |
if len(associations) == 0 {
|
| 116 |
- return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
|
|
| 116 |
+ return fmt.Errorf("Repository does not exist: %s", repoInfo.Name())
|
|
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 | 119 |
var lastErr error |
| 120 | 120 |
for _, endpoint := range endpoints {
|
| 121 |
- logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
|
|
| 121 |
+ logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
|
|
| 122 | 122 |
|
| 123 | 123 |
pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig) |
| 124 | 124 |
if err != nil {
|
| ... | ... |
@@ -143,12 +143,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo |
| 143 | 143 |
|
| 144 | 144 |
} |
| 145 | 145 |
|
| 146 |
- imagePushConfig.EventsService.Log("push", repoInfo.LocalName.Name(), "")
|
|
| 146 |
+ imagePushConfig.EventsService.Log("push", repoInfo.Name(), "")
|
|
| 147 | 147 |
return nil |
| 148 | 148 |
} |
| 149 | 149 |
|
| 150 | 150 |
if lastErr == nil {
|
| 151 |
- lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
|
|
| 151 |
+ lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
|
|
| 152 | 152 |
} |
| 153 | 153 |
return lastErr |
| 154 | 154 |
} |
| ... | ... |
@@ -351,8 +351,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima |
| 351 | 351 |
} |
| 352 | 352 |
if topImage, isTopImage := img.(*v1TopImage); isTopImage {
|
| 353 | 353 |
for _, tag := range tags[topImage.imageID] {
|
| 354 |
- progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName.Name()+"/tags/"+tag)
|
|
| 355 |
- if err := p.session.PushRegistryTag(p.repoInfo.RemoteName, v1ID, tag, endpoint); err != nil {
|
|
| 354 |
+ progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
|
|
| 355 |
+ if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
|
|
| 356 | 356 |
return err |
| 357 | 357 |
} |
| 358 | 358 |
} |
| ... | ... |
@@ -381,18 +381,18 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
|
| 381 | 381 |
|
| 382 | 382 |
// Register all the images in a repository with the registry |
| 383 | 383 |
// If an image is not in this list it will not be associated with the repository |
| 384 |
- repoData, err := p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, false, nil) |
|
| 384 |
+ repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil) |
|
| 385 | 385 |
if err != nil {
|
| 386 | 386 |
return err |
| 387 | 387 |
} |
| 388 |
- progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.CanonicalName.String()) |
|
| 388 |
+ progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.FullName()) |
|
| 389 | 389 |
// push the repository to each of the endpoints only if it does not exist. |
| 390 | 390 |
for _, endpoint := range repoData.Endpoints {
|
| 391 | 391 |
if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil {
|
| 392 | 392 |
return err |
| 393 | 393 |
} |
| 394 | 394 |
} |
| 395 |
- _, err = p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, true, repoData.Endpoints) |
|
| 395 |
+ _, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints) |
|
| 396 | 396 |
return err |
| 397 | 397 |
} |
| 398 | 398 |
|
| ... | ... |
@@ -52,8 +52,6 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
|
| 52 | 52 |
return true, err |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 |
- localName := p.repoInfo.LocalName.Name() |
|
| 56 |
- |
|
| 57 | 55 |
var associations []reference.Association |
| 58 | 56 |
if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
|
| 59 | 57 |
imageID, err := p.config.ReferenceStore.Get(p.ref) |
| ... | ... |
@@ -72,10 +70,10 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
|
| 72 | 72 |
associations = p.config.ReferenceStore.ReferencesByName(p.ref) |
| 73 | 73 |
} |
| 74 | 74 |
if err != nil {
|
| 75 |
- return false, fmt.Errorf("error getting tags for %s: %s", localName, err)
|
|
| 75 |
+ return false, fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err)
|
|
| 76 | 76 |
} |
| 77 | 77 |
if len(associations) == 0 {
|
| 78 |
- return false, fmt.Errorf("no tags to push for %s", localName)
|
|
| 78 |
+ return false, fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
|
|
| 79 | 79 |
} |
| 80 | 80 |
|
| 81 | 81 |
for _, association := range associations {
|
| ... | ... |
@@ -159,7 +157,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat |
| 159 | 159 |
return err |
| 160 | 160 |
} |
| 161 | 161 |
|
| 162 |
- manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) |
|
| 162 |
+ manifestDigest, manifestSize, err := digestFromManifest(signed, ref) |
|
| 163 | 163 |
if err != nil {
|
| 164 | 164 |
return err |
| 165 | 165 |
} |
| ... | ... |
@@ -19,6 +19,7 @@ import ( |
| 19 | 19 |
"github.com/docker/distribution/registry/client/transport" |
| 20 | 20 |
"github.com/docker/docker/api/types" |
| 21 | 21 |
"github.com/docker/docker/distribution/xfer" |
| 22 |
+ "github.com/docker/docker/reference" |
|
| 22 | 23 |
"github.com/docker/docker/registry" |
| 23 | 24 |
"golang.org/x/net/context" |
| 24 | 25 |
) |
| ... | ... |
@@ -37,10 +38,10 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) {
|
| 37 | 37 |
func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (distribution.Repository, error) {
|
| 38 | 38 |
ctx := context.Background() |
| 39 | 39 |
|
| 40 |
- repoName := repoInfo.CanonicalName |
|
| 40 |
+ repoName := repoInfo.FullName() |
|
| 41 | 41 |
// If endpoint does not support CanonicalName, use the RemoteName instead |
| 42 | 42 |
if endpoint.TrimHostname {
|
| 43 |
- repoName = repoInfo.RemoteName |
|
| 43 |
+ repoName = repoInfo.RemoteName() |
|
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 | 46 |
// TODO(dmcgowan): Call close idle connections when complete, use keep alive |
| ... | ... |
@@ -99,16 +100,16 @@ func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEnd |
| 99 | 99 |
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) |
| 100 | 100 |
} else {
|
| 101 | 101 |
creds := dumbCredentialStore{auth: authConfig}
|
| 102 |
- tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName.Name(), actions...) |
|
| 102 |
+ tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...) |
|
| 103 | 103 |
basicHandler := auth.NewBasicHandler(creds) |
| 104 | 104 |
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) |
| 105 | 105 |
} |
| 106 | 106 |
tr := transport.NewTransport(base, modifiers...) |
| 107 | 107 |
|
| 108 |
- return client.NewRepository(ctx, repoName.Name(), endpoint.URL, tr) |
|
| 108 |
+ return client.NewRepository(ctx, repoName, endpoint.URL, tr) |
|
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 |
-func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Digest, int, error) {
|
|
| 111 |
+func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) {
|
|
| 112 | 112 |
payload, err := m.Payload() |
| 113 | 113 |
if err != nil {
|
| 114 | 114 |
// If this failed, the signatures section was corrupted |
| ... | ... |
@@ -117,7 +118,7 @@ func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Dig |
| 117 | 117 |
} |
| 118 | 118 |
manifestDigest, err := digest.FromBytes(payload) |
| 119 | 119 |
if err != nil {
|
| 120 |
- logrus.Infof("Could not compute manifest digest for %s:%s : %v", localName, m.Tag, err)
|
|
| 120 |
+ logrus.Infof("Could not compute manifest digest for %s:%s : %v", name.Name(), m.Tag, err)
|
|
| 121 | 121 |
} |
| 122 | 122 |
return manifestDigest, len(payload), nil |
| 123 | 123 |
} |
| ... | ... |
@@ -58,16 +58,14 @@ func TestTokenPassThru(t *testing.T) {
|
| 58 | 58 |
} |
| 59 | 59 |
n, _ := reference.ParseNamed("testremotename")
|
| 60 | 60 |
repoInfo := ®istry.RepositoryInfo{
|
| 61 |
+ Named: n, |
|
| 61 | 62 |
Index: ®istrytypes.IndexInfo{
|
| 62 | 63 |
Name: "testrepo", |
| 63 | 64 |
Mirrors: nil, |
| 64 | 65 |
Secure: false, |
| 65 | 66 |
Official: false, |
| 66 | 67 |
}, |
| 67 |
- RemoteName: n, |
|
| 68 |
- LocalName: n, |
|
| 69 |
- CanonicalName: n, |
|
| 70 |
- Official: false, |
|
| 68 |
+ Official: false, |
|
| 71 | 69 |
} |
| 72 | 70 |
imagePullConfig := &ImagePullConfig{
|
| 73 | 71 |
MetaHeaders: http.Header{},
|
| ... | ... |
@@ -15,7 +15,6 @@ import ( |
| 15 | 15 |
"github.com/docker/docker/layer" |
| 16 | 16 |
"github.com/docker/docker/pkg/archive" |
| 17 | 17 |
"github.com/docker/docker/reference" |
| 18 |
- "github.com/docker/docker/registry" |
|
| 19 | 18 |
) |
| 20 | 19 |
|
| 21 | 20 |
type imageDescriptor struct {
|
| ... | ... |
@@ -74,7 +73,6 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, |
| 74 | 74 |
if err != nil {
|
| 75 | 75 |
return nil, err |
| 76 | 76 |
} |
| 77 |
- ref = registry.NormalizeLocalReference(ref) |
|
| 78 | 77 |
if ref.Name() == string(digest.Canonical) {
|
| 79 | 78 |
imgID, err := l.is.Search(name) |
| 80 | 79 |
if err != nil {
|
| ... | ... |
@@ -1,21 +1,37 @@ |
| 1 | 1 |
package reference |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 4 | 7 |
"github.com/docker/distribution/digest" |
| 5 | 8 |
distreference "github.com/docker/distribution/reference" |
| 9 |
+ "github.com/docker/docker/image/v1" |
|
| 6 | 10 |
) |
| 7 | 11 |
|
| 8 |
-// Reference is an opaque object reference identifier that may include |
|
| 9 |
-// modifiers such as a hostname, name, tag, and digest. |
|
| 10 |
-type Reference interface {
|
|
| 11 |
- // String returns the full reference |
|
| 12 |
- String() string |
|
| 13 |
-} |
|
| 12 |
+const ( |
|
| 13 |
+ // DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified |
|
| 14 |
+ DefaultTag = "latest" |
|
| 15 |
+ // DefaultHostname is the default built-in hostname |
|
| 16 |
+ DefaultHostname = "docker.io" |
|
| 17 |
+ // LegacyDefaultHostname is automatically converted to DefaultHostname |
|
| 18 |
+ LegacyDefaultHostname = "index.docker.io" |
|
| 19 |
+ // DefaultRepoPrefix is the prefix used for default repositories in default host |
|
| 20 |
+ DefaultRepoPrefix = "library/" |
|
| 21 |
+) |
|
| 14 | 22 |
|
| 15 | 23 |
// Named is an object with a full name |
| 16 | 24 |
type Named interface {
|
| 17 |
- Reference |
|
| 25 |
+ // Name returns normalized repository name, like "ubuntu". |
|
| 18 | 26 |
Name() string |
| 27 |
+ // String returns full reference, like "ubuntu@sha256:abcdef..." |
|
| 28 |
+ String() string |
|
| 29 |
+ // FullName returns full repository name with hostname, like "docker.io/library/ubuntu" |
|
| 30 |
+ FullName() string |
|
| 31 |
+ // Hostname returns hostname for the reference, like "docker.io" |
|
| 32 |
+ Hostname() string |
|
| 33 |
+ // RemoteName returns the repository component of the full name, like "library/ubuntu" |
|
| 34 |
+ RemoteName() string |
|
| 19 | 35 |
} |
| 20 | 36 |
|
| 21 | 37 |
// NamedTagged is an object including a name and tag. |
| ... | ... |
@@ -35,26 +51,122 @@ type Canonical interface {
|
| 35 | 35 |
// the Named interface. The reference must have a name, otherwise an error is |
| 36 | 36 |
// returned. |
| 37 | 37 |
// If an error was encountered it is returned, along with a nil Reference. |
| 38 |
-// NOTE: ParseNamed will not handle short digests. |
|
| 39 | 38 |
func ParseNamed(s string) (Named, error) {
|
| 40 |
- // todo: docker specific validation |
|
| 41 |
- return distreference.ParseNamed(s) |
|
| 39 |
+ named, err := distreference.ParseNamed(s) |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ return nil, err |
|
| 42 |
+ } |
|
| 43 |
+ r, err := WithName(named.Name()) |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return nil, err |
|
| 46 |
+ } |
|
| 47 |
+ if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
|
|
| 48 |
+ return WithDigest(r, canonical.Digest()) |
|
| 49 |
+ } |
|
| 50 |
+ if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
|
|
| 51 |
+ return WithTag(r, tagged.Tag()) |
|
| 52 |
+ } |
|
| 53 |
+ return r, nil |
|
| 42 | 54 |
} |
| 43 | 55 |
|
| 44 | 56 |
// WithName returns a named object representing the given string. If the input |
| 45 | 57 |
// is invalid ErrReferenceInvalidFormat will be returned. |
| 46 | 58 |
func WithName(name string) (Named, error) {
|
| 47 |
- return distreference.WithName(name) |
|
| 59 |
+ name = normalize(name) |
|
| 60 |
+ if err := validateName(name); err != nil {
|
|
| 61 |
+ return nil, err |
|
| 62 |
+ } |
|
| 63 |
+ r, err := distreference.WithName(name) |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ return nil, err |
|
| 66 |
+ } |
|
| 67 |
+ return &namedRef{r}, nil
|
|
| 48 | 68 |
} |
| 49 | 69 |
|
| 50 | 70 |
// WithTag combines the name from "name" and the tag from "tag" to form a |
| 51 | 71 |
// reference incorporating both the name and the tag. |
| 52 | 72 |
func WithTag(name Named, tag string) (NamedTagged, error) {
|
| 53 |
- return distreference.WithTag(name, tag) |
|
| 73 |
+ r, err := distreference.WithTag(name, tag) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return nil, err |
|
| 76 |
+ } |
|
| 77 |
+ return &taggedRef{namedRef{r}}, nil
|
|
| 54 | 78 |
} |
| 55 | 79 |
|
| 56 | 80 |
// WithDigest combines the name from "name" and the digest from "digest" to form |
| 57 | 81 |
// a reference incorporating both the name and the digest. |
| 58 | 82 |
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
| 59 |
- return distreference.WithDigest(name, digest) |
|
| 83 |
+ r, err := distreference.WithDigest(name, digest) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return nil, err |
|
| 86 |
+ } |
|
| 87 |
+ return &canonicalRef{namedRef{r}}, nil
|
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+type namedRef struct {
|
|
| 91 |
+ distreference.Named |
|
| 92 |
+} |
|
| 93 |
+type taggedRef struct {
|
|
| 94 |
+ namedRef |
|
| 95 |
+} |
|
| 96 |
+type canonicalRef struct {
|
|
| 97 |
+ namedRef |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (r *namedRef) FullName() string {
|
|
| 101 |
+ hostname, remoteName := splitHostname(r.Name()) |
|
| 102 |
+ return hostname + "/" + remoteName |
|
| 103 |
+} |
|
| 104 |
+func (r *namedRef) Hostname() string {
|
|
| 105 |
+ hostname, _ := splitHostname(r.Name()) |
|
| 106 |
+ return hostname |
|
| 107 |
+} |
|
| 108 |
+func (r *namedRef) RemoteName() string {
|
|
| 109 |
+ _, remoteName := splitHostname(r.Name()) |
|
| 110 |
+ return remoteName |
|
| 111 |
+} |
|
| 112 |
+func (r *taggedRef) Tag() string {
|
|
| 113 |
+ return r.namedRef.Named.(distreference.NamedTagged).Tag() |
|
| 114 |
+} |
|
| 115 |
+func (r *canonicalRef) Digest() digest.Digest {
|
|
| 116 |
+ return r.namedRef.Named.(distreference.Canonical).Digest() |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// splitHostname splits a repository name to hostname and remotename string. |
|
| 120 |
+// If no valid hostname is found, the default hostname is used. Repository name |
|
| 121 |
+// needs to be already validated before. |
|
| 122 |
+func splitHostname(name string) (hostname, remoteName string) {
|
|
| 123 |
+ i := strings.IndexRune(name, '/') |
|
| 124 |
+ if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
|
| 125 |
+ hostname, remoteName = DefaultHostname, name |
|
| 126 |
+ } else {
|
|
| 127 |
+ hostname, remoteName = name[:i], name[i+1:] |
|
| 128 |
+ } |
|
| 129 |
+ if hostname == LegacyDefaultHostname {
|
|
| 130 |
+ hostname = DefaultHostname |
|
| 131 |
+ } |
|
| 132 |
+ if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
|
|
| 133 |
+ remoteName = DefaultRepoPrefix + remoteName |
|
| 134 |
+ } |
|
| 135 |
+ return |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+// normalize returns a repository name in its normalized form, meaning it |
|
| 139 |
+// will not contain default hostname nor library/ prefix for official images. |
|
| 140 |
+func normalize(name string) string {
|
|
| 141 |
+ host, remoteName := splitHostname(name) |
|
| 142 |
+ if host == DefaultHostname {
|
|
| 143 |
+ if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
|
|
| 144 |
+ return strings.TrimPrefix(remoteName, DefaultRepoPrefix) |
|
| 145 |
+ } |
|
| 146 |
+ return remoteName |
|
| 147 |
+ } |
|
| 148 |
+ return name |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func validateName(name string) error {
|
|
| 152 |
+ if err := v1.ValidateID(name); err == nil {
|
|
| 153 |
+ return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
|
|
| 154 |
+ } |
|
| 155 |
+ return nil |
|
| 60 | 156 |
} |
| 61 | 157 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,275 @@ |
| 0 |
+package reference |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/distribution/digest" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestValidateReferenceName(t *testing.T) {
|
|
| 9 |
+ validRepoNames := []string{
|
|
| 10 |
+ "docker/docker", |
|
| 11 |
+ "library/debian", |
|
| 12 |
+ "debian", |
|
| 13 |
+ "docker.io/docker/docker", |
|
| 14 |
+ "docker.io/library/debian", |
|
| 15 |
+ "docker.io/debian", |
|
| 16 |
+ "index.docker.io/docker/docker", |
|
| 17 |
+ "index.docker.io/library/debian", |
|
| 18 |
+ "index.docker.io/debian", |
|
| 19 |
+ "127.0.0.1:5000/docker/docker", |
|
| 20 |
+ "127.0.0.1:5000/library/debian", |
|
| 21 |
+ "127.0.0.1:5000/debian", |
|
| 22 |
+ "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 23 |
+ } |
|
| 24 |
+ invalidRepoNames := []string{
|
|
| 25 |
+ "https://github.com/docker/docker", |
|
| 26 |
+ "docker/Docker", |
|
| 27 |
+ "-docker", |
|
| 28 |
+ "-docker/docker", |
|
| 29 |
+ "-docker.io/docker/docker", |
|
| 30 |
+ "docker///docker", |
|
| 31 |
+ "docker.io/docker/Docker", |
|
| 32 |
+ "docker.io/docker///docker", |
|
| 33 |
+ "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 34 |
+ "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ for _, name := range invalidRepoNames {
|
|
| 38 |
+ _, err := ParseNamed(name) |
|
| 39 |
+ if err == nil {
|
|
| 40 |
+ t.Fatalf("Expected invalid repo name for %q", name)
|
|
| 41 |
+ } |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ for _, name := range validRepoNames {
|
|
| 45 |
+ _, err := ParseNamed(name) |
|
| 46 |
+ if err != nil {
|
|
| 47 |
+ t.Fatalf("Error parsing repo name %s, got: %q", name, err)
|
|
| 48 |
+ } |
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func TestValidateRemoteName(t *testing.T) {
|
|
| 53 |
+ validRepositoryNames := []string{
|
|
| 54 |
+ // Sanity check. |
|
| 55 |
+ "docker/docker", |
|
| 56 |
+ |
|
| 57 |
+ // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). |
|
| 58 |
+ "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 59 |
+ |
|
| 60 |
+ // Allow embedded hyphens. |
|
| 61 |
+ "docker-rules/docker", |
|
| 62 |
+ |
|
| 63 |
+ // Allow multiple hyphens as well. |
|
| 64 |
+ "docker---rules/docker", |
|
| 65 |
+ |
|
| 66 |
+ //Username doc and image name docker being tested. |
|
| 67 |
+ "doc/docker", |
|
| 68 |
+ |
|
| 69 |
+ // single character names are now allowed. |
|
| 70 |
+ "d/docker", |
|
| 71 |
+ "jess/t", |
|
| 72 |
+ |
|
| 73 |
+ // Consecutive underscores. |
|
| 74 |
+ "dock__er/docker", |
|
| 75 |
+ } |
|
| 76 |
+ for _, repositoryName := range validRepositoryNames {
|
|
| 77 |
+ _, err := ParseNamed(repositoryName) |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ invalidRepositoryNames := []string{
|
|
| 84 |
+ // Disallow capital letters. |
|
| 85 |
+ "docker/Docker", |
|
| 86 |
+ |
|
| 87 |
+ // Only allow one slash. |
|
| 88 |
+ "docker///docker", |
|
| 89 |
+ |
|
| 90 |
+ // Disallow 64-character hexadecimal. |
|
| 91 |
+ "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 92 |
+ |
|
| 93 |
+ // Disallow leading and trailing hyphens in namespace. |
|
| 94 |
+ "-docker/docker", |
|
| 95 |
+ "docker-/docker", |
|
| 96 |
+ "-docker-/docker", |
|
| 97 |
+ |
|
| 98 |
+ // Don't allow underscores everywhere (as opposed to hyphens). |
|
| 99 |
+ "____/____", |
|
| 100 |
+ |
|
| 101 |
+ "_docker/_docker", |
|
| 102 |
+ |
|
| 103 |
+ // Disallow consecutive periods. |
|
| 104 |
+ "dock..er/docker", |
|
| 105 |
+ "dock_.er/docker", |
|
| 106 |
+ "dock-.er/docker", |
|
| 107 |
+ |
|
| 108 |
+ // No repository. |
|
| 109 |
+ "docker/", |
|
| 110 |
+ |
|
| 111 |
+ //namespace too long |
|
| 112 |
+ "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", |
|
| 113 |
+ } |
|
| 114 |
+ for _, repositoryName := range invalidRepositoryNames {
|
|
| 115 |
+ if _, err := ParseNamed(repositoryName); err == nil {
|
|
| 116 |
+ t.Errorf("Repository name should be invalid: %v", repositoryName)
|
|
| 117 |
+ } |
|
| 118 |
+ } |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+func TestParseRepositoryInfo(t *testing.T) {
|
|
| 122 |
+ type tcase struct {
|
|
| 123 |
+ RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string |
|
| 124 |
+ } |
|
| 125 |
+ |
|
| 126 |
+ tcases := []tcase{
|
|
| 127 |
+ {
|
|
| 128 |
+ RemoteName: "fooo/bar", |
|
| 129 |
+ NormalizedName: "fooo/bar", |
|
| 130 |
+ FullName: "docker.io/fooo/bar", |
|
| 131 |
+ AmbiguousName: "index.docker.io/fooo/bar", |
|
| 132 |
+ Hostname: "docker.io", |
|
| 133 |
+ }, |
|
| 134 |
+ {
|
|
| 135 |
+ RemoteName: "library/ubuntu", |
|
| 136 |
+ NormalizedName: "ubuntu", |
|
| 137 |
+ FullName: "docker.io/library/ubuntu", |
|
| 138 |
+ AmbiguousName: "library/ubuntu", |
|
| 139 |
+ Hostname: "docker.io", |
|
| 140 |
+ }, |
|
| 141 |
+ {
|
|
| 142 |
+ RemoteName: "nonlibrary/ubuntu", |
|
| 143 |
+ NormalizedName: "nonlibrary/ubuntu", |
|
| 144 |
+ FullName: "docker.io/nonlibrary/ubuntu", |
|
| 145 |
+ AmbiguousName: "", |
|
| 146 |
+ Hostname: "docker.io", |
|
| 147 |
+ }, |
|
| 148 |
+ {
|
|
| 149 |
+ RemoteName: "other/library", |
|
| 150 |
+ NormalizedName: "other/library", |
|
| 151 |
+ FullName: "docker.io/other/library", |
|
| 152 |
+ AmbiguousName: "", |
|
| 153 |
+ Hostname: "docker.io", |
|
| 154 |
+ }, |
|
| 155 |
+ {
|
|
| 156 |
+ RemoteName: "private/moonbase", |
|
| 157 |
+ NormalizedName: "127.0.0.1:8000/private/moonbase", |
|
| 158 |
+ FullName: "127.0.0.1:8000/private/moonbase", |
|
| 159 |
+ AmbiguousName: "", |
|
| 160 |
+ Hostname: "127.0.0.1:8000", |
|
| 161 |
+ }, |
|
| 162 |
+ {
|
|
| 163 |
+ RemoteName: "privatebase", |
|
| 164 |
+ NormalizedName: "127.0.0.1:8000/privatebase", |
|
| 165 |
+ FullName: "127.0.0.1:8000/privatebase", |
|
| 166 |
+ AmbiguousName: "", |
|
| 167 |
+ Hostname: "127.0.0.1:8000", |
|
| 168 |
+ }, |
|
| 169 |
+ {
|
|
| 170 |
+ RemoteName: "private/moonbase", |
|
| 171 |
+ NormalizedName: "example.com/private/moonbase", |
|
| 172 |
+ FullName: "example.com/private/moonbase", |
|
| 173 |
+ AmbiguousName: "", |
|
| 174 |
+ Hostname: "example.com", |
|
| 175 |
+ }, |
|
| 176 |
+ {
|
|
| 177 |
+ RemoteName: "privatebase", |
|
| 178 |
+ NormalizedName: "example.com/privatebase", |
|
| 179 |
+ FullName: "example.com/privatebase", |
|
| 180 |
+ AmbiguousName: "", |
|
| 181 |
+ Hostname: "example.com", |
|
| 182 |
+ }, |
|
| 183 |
+ {
|
|
| 184 |
+ RemoteName: "private/moonbase", |
|
| 185 |
+ NormalizedName: "example.com:8000/private/moonbase", |
|
| 186 |
+ FullName: "example.com:8000/private/moonbase", |
|
| 187 |
+ AmbiguousName: "", |
|
| 188 |
+ Hostname: "example.com:8000", |
|
| 189 |
+ }, |
|
| 190 |
+ {
|
|
| 191 |
+ RemoteName: "privatebasee", |
|
| 192 |
+ NormalizedName: "example.com:8000/privatebasee", |
|
| 193 |
+ FullName: "example.com:8000/privatebasee", |
|
| 194 |
+ AmbiguousName: "", |
|
| 195 |
+ Hostname: "example.com:8000", |
|
| 196 |
+ }, |
|
| 197 |
+ {
|
|
| 198 |
+ RemoteName: "library/ubuntu-12.04-base", |
|
| 199 |
+ NormalizedName: "ubuntu-12.04-base", |
|
| 200 |
+ FullName: "docker.io/library/ubuntu-12.04-base", |
|
| 201 |
+ AmbiguousName: "index.docker.io/library/ubuntu-12.04-base", |
|
| 202 |
+ Hostname: "docker.io", |
|
| 203 |
+ }, |
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 206 |
+ for _, tcase := range tcases {
|
|
| 207 |
+ refStrings := []string{tcase.NormalizedName, tcase.FullName}
|
|
| 208 |
+ if tcase.AmbiguousName != "" {
|
|
| 209 |
+ refStrings = append(refStrings, tcase.AmbiguousName) |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ var refs []Named |
|
| 213 |
+ for _, r := range refStrings {
|
|
| 214 |
+ named, err := ParseNamed(r) |
|
| 215 |
+ if err != nil {
|
|
| 216 |
+ t.Fatal(err) |
|
| 217 |
+ } |
|
| 218 |
+ refs = append(refs, named) |
|
| 219 |
+ named, err = WithName(r) |
|
| 220 |
+ if err != nil {
|
|
| 221 |
+ t.Fatal(err) |
|
| 222 |
+ } |
|
| 223 |
+ refs = append(refs, named) |
|
| 224 |
+ } |
|
| 225 |
+ |
|
| 226 |
+ for _, r := range refs {
|
|
| 227 |
+ if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
|
|
| 228 |
+ t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
|
| 229 |
+ } |
|
| 230 |
+ if expected, actual := tcase.FullName, r.FullName(); expected != actual {
|
|
| 231 |
+ t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
|
| 232 |
+ } |
|
| 233 |
+ if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
|
|
| 234 |
+ t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
|
|
| 235 |
+ } |
|
| 236 |
+ if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
|
|
| 237 |
+ t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
|
|
| 238 |
+ } |
|
| 239 |
+ |
|
| 240 |
+ } |
|
| 241 |
+ } |
|
| 242 |
+} |
|
| 243 |
+ |
|
| 244 |
+func TestParseReferenceWithTagAndDigest(t *testing.T) {
|
|
| 245 |
+ ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
|
|
| 246 |
+ if err != nil {
|
|
| 247 |
+ t.Fatal(err) |
|
| 248 |
+ } |
|
| 249 |
+ if _, isTagged := ref.(NamedTagged); isTagged {
|
|
| 250 |
+ t.Fatalf("Reference from %q should not support tag", ref)
|
|
| 251 |
+ } |
|
| 252 |
+ if _, isCanonical := ref.(Canonical); !isCanonical {
|
|
| 253 |
+ t.Fatalf("Reference from %q should not support digest", ref)
|
|
| 254 |
+ } |
|
| 255 |
+ if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
|
|
| 256 |
+ t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
|
| 257 |
+ } |
|
| 258 |
+} |
|
| 259 |
+ |
|
| 260 |
+func TestInvalidReferenceComponents(t *testing.T) {
|
|
| 261 |
+ if _, err := WithName("-foo"); err == nil {
|
|
| 262 |
+ t.Fatal("Expected WithName to detect invalid name")
|
|
| 263 |
+ } |
|
| 264 |
+ ref, err := WithName("busybox")
|
|
| 265 |
+ if err != nil {
|
|
| 266 |
+ t.Fatal(err) |
|
| 267 |
+ } |
|
| 268 |
+ if _, err := WithTag(ref, "-foo"); err == nil {
|
|
| 269 |
+ t.Fatal("Expected WithName to detect invalid tag")
|
|
| 270 |
+ } |
|
| 271 |
+ if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
|
|
| 272 |
+ t.Fatal("Expected WithName to detect invalid digest")
|
|
| 273 |
+ } |
|
| 274 |
+} |
| ... | ... |
@@ -14,9 +14,6 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/image" |
| 15 | 15 |
) |
| 16 | 16 |
|
| 17 |
-// DefaultTag defines the default tag used when performing images related actions and no tag string is specified |
|
| 18 |
-const DefaultTag = "latest" |
|
| 19 |
- |
|
| 20 | 17 |
var ( |
| 21 | 18 |
// ErrDoesNotExist is returned if a reference is not found in the |
| 22 | 19 |
// store. |
| ... | ... |
@@ -7,9 +7,7 @@ import ( |
| 7 | 7 |
"net/url" |
| 8 | 8 |
"strings" |
| 9 | 9 |
|
| 10 |
- distreference "github.com/docker/distribution/reference" |
|
| 11 | 10 |
registrytypes "github.com/docker/docker/api/types/registry" |
| 12 |
- "github.com/docker/docker/image/v1" |
|
| 13 | 11 |
"github.com/docker/docker/opts" |
| 14 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 15 | 13 |
"github.com/docker/docker/reference" |
| ... | ... |
@@ -182,28 +180,15 @@ func ValidateMirror(val string) (string, error) {
|
| 182 | 182 |
|
| 183 | 183 |
// ValidateIndexName validates an index name. |
| 184 | 184 |
func ValidateIndexName(val string) (string, error) {
|
| 185 |
- // 'index.docker.io' => 'docker.io' |
|
| 186 |
- if val == "index."+IndexName {
|
|
| 187 |
- val = IndexName |
|
| 185 |
+ if val == reference.LegacyDefaultHostname {
|
|
| 186 |
+ val = reference.DefaultHostname |
|
| 188 | 187 |
} |
| 189 | 188 |
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
|
| 190 | 189 |
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
|
| 191 | 190 |
} |
| 192 |
- // *TODO: Check if valid hostname[:port]/ip[:port]? |
|
| 193 | 191 |
return val, nil |
| 194 | 192 |
} |
| 195 | 193 |
|
| 196 |
-func validateRemoteName(remoteName reference.Named) error {
|
|
| 197 |
- remoteNameStr := remoteName.Name() |
|
| 198 |
- if !strings.Contains(remoteNameStr, "/") {
|
|
| 199 |
- // the repository name must not be a valid image ID |
|
| 200 |
- if err := v1.ValidateID(remoteNameStr); err == nil {
|
|
| 201 |
- return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
|
|
| 202 |
- } |
|
| 203 |
- } |
|
| 204 |
- return nil |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 | 194 |
func validateNoSchema(reposName string) error {
|
| 208 | 195 |
if strings.Contains(reposName, "://") {
|
| 209 | 196 |
// It cannot contain a scheme! |
| ... | ... |
@@ -212,29 +197,6 @@ func validateNoSchema(reposName string) error {
|
| 212 | 212 |
return nil |
| 213 | 213 |
} |
| 214 | 214 |
|
| 215 |
-// ValidateRepositoryName validates a repository name |
|
| 216 |
-func ValidateRepositoryName(reposName reference.Named) error {
|
|
| 217 |
- _, _, err := loadRepositoryName(reposName) |
|
| 218 |
- return err |
|
| 219 |
-} |
|
| 220 |
- |
|
| 221 |
-// loadRepositoryName returns the repo name splitted into index name |
|
| 222 |
-// and remote repo name. It returns an error if the name is not valid. |
|
| 223 |
-func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
|
|
| 224 |
- if err := validateNoSchema(reposName.Name()); err != nil {
|
|
| 225 |
- return "", nil, err |
|
| 226 |
- } |
|
| 227 |
- indexName, remoteName, err := splitReposName(reposName) |
|
| 228 |
- |
|
| 229 |
- if indexName, err = ValidateIndexName(indexName); err != nil {
|
|
| 230 |
- return "", nil, err |
|
| 231 |
- } |
|
| 232 |
- if err = validateRemoteName(remoteName); err != nil {
|
|
| 233 |
- return "", nil, err |
|
| 234 |
- } |
|
| 235 |
- return indexName, remoteName, nil |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 | 215 |
// newIndexInfo returns IndexInfo configuration from indexName |
| 239 | 216 |
func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) {
|
| 240 | 217 |
var err error |
| ... | ... |
@@ -267,75 +229,14 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
|
| 267 | 267 |
return index.Name |
| 268 | 268 |
} |
| 269 | 269 |
|
| 270 |
-// splitReposName breaks a reposName into an index name and remote name |
|
| 271 |
-func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
|
|
| 272 |
- var remoteNameStr string |
|
| 273 |
- indexName, remoteNameStr = distreference.SplitHostname(reposName) |
|
| 274 |
- if indexName == "" || (!strings.Contains(indexName, ".") && |
|
| 275 |
- !strings.Contains(indexName, ":") && indexName != "localhost") {
|
|
| 276 |
- // This is a Docker Index repos (ex: samalba/hipache or ubuntu) |
|
| 277 |
- // 'docker.io' |
|
| 278 |
- indexName = IndexName |
|
| 279 |
- remoteName = reposName |
|
| 280 |
- } else {
|
|
| 281 |
- remoteName, err = reference.WithName(remoteNameStr) |
|
| 282 |
- } |
|
| 283 |
- return |
|
| 284 |
-} |
|
| 285 |
- |
|
| 286 | 270 |
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo |
| 287 |
-func newRepositoryInfo(config *registrytypes.ServiceConfig, reposName reference.Named) (*RepositoryInfo, error) {
|
|
| 288 |
- if err := validateNoSchema(reposName.Name()); err != nil {
|
|
| 289 |
- return nil, err |
|
| 290 |
- } |
|
| 291 |
- |
|
| 292 |
- repoInfo := &RepositoryInfo{}
|
|
| 293 |
- var ( |
|
| 294 |
- indexName string |
|
| 295 |
- err error |
|
| 296 |
- ) |
|
| 297 |
- |
|
| 298 |
- indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName) |
|
| 299 |
- if err != nil {
|
|
| 300 |
- return nil, err |
|
| 301 |
- } |
|
| 302 |
- |
|
| 303 |
- repoInfo.Index, err = newIndexInfo(config, indexName) |
|
| 271 |
+func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) {
|
|
| 272 |
+ index, err := newIndexInfo(config, name.Hostname()) |
|
| 304 | 273 |
if err != nil {
|
| 305 | 274 |
return nil, err |
| 306 | 275 |
} |
| 307 |
- |
|
| 308 |
- if repoInfo.Index.Official {
|
|
| 309 |
- repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName) |
|
| 310 |
- if err != nil {
|
|
| 311 |
- return nil, err |
|
| 312 |
- } |
|
| 313 |
- repoInfo.RemoteName = repoInfo.LocalName |
|
| 314 |
- |
|
| 315 |
- // If the normalized name does not contain a '/' (e.g. "foo") |
|
| 316 |
- // then it is an official repo. |
|
| 317 |
- if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
|
|
| 318 |
- repoInfo.Official = true |
|
| 319 |
- // Fix up remote name for official repos. |
|
| 320 |
- repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
|
|
| 321 |
- if err != nil {
|
|
| 322 |
- return nil, err |
|
| 323 |
- } |
|
| 324 |
- } |
|
| 325 |
- |
|
| 326 |
- repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
|
|
| 327 |
- if err != nil {
|
|
| 328 |
- return nil, err |
|
| 329 |
- } |
|
| 330 |
- } else {
|
|
| 331 |
- repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName) |
|
| 332 |
- if err != nil {
|
|
| 333 |
- return nil, err |
|
| 334 |
- } |
|
| 335 |
- repoInfo.CanonicalName = repoInfo.LocalName |
|
| 336 |
- } |
|
| 337 |
- |
|
| 338 |
- return repoInfo, nil |
|
| 276 |
+ official := !strings.ContainsRune(name.Name(), '/') |
|
| 277 |
+ return &RepositoryInfo{name, index, official}, nil
|
|
| 339 | 278 |
} |
| 340 | 279 |
|
| 341 | 280 |
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but |
| ... | ... |
@@ -354,70 +255,3 @@ func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
|
| 354 | 354 |
} |
| 355 | 355 |
return indexInfo, nil |
| 356 | 356 |
} |
| 357 |
- |
|
| 358 |
-// NormalizeLocalName transforms a repository name into a normalized LocalName |
|
| 359 |
-// Passes through the name without transformation on error (image id, etc) |
|
| 360 |
-// It does not use the repository info because we don't want to load |
|
| 361 |
-// the repository index and do request over the network. |
|
| 362 |
-func NormalizeLocalName(name reference.Named) reference.Named {
|
|
| 363 |
- indexName, remoteName, err := loadRepositoryName(name) |
|
| 364 |
- if err != nil {
|
|
| 365 |
- return name |
|
| 366 |
- } |
|
| 367 |
- |
|
| 368 |
- var officialIndex bool |
|
| 369 |
- // Return any configured index info, first. |
|
| 370 |
- if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
|
|
| 371 |
- officialIndex = index.Official |
|
| 372 |
- } |
|
| 373 |
- |
|
| 374 |
- if officialIndex {
|
|
| 375 |
- localName, err := normalizeLibraryRepoName(remoteName) |
|
| 376 |
- if err != nil {
|
|
| 377 |
- return name |
|
| 378 |
- } |
|
| 379 |
- return localName |
|
| 380 |
- } |
|
| 381 |
- localName, err := localNameFromRemote(indexName, remoteName) |
|
| 382 |
- if err != nil {
|
|
| 383 |
- return name |
|
| 384 |
- } |
|
| 385 |
- return localName |
|
| 386 |
-} |
|
| 387 |
- |
|
| 388 |
-// normalizeLibraryRepoName removes the library prefix from |
|
| 389 |
-// the repository name for official repos. |
|
| 390 |
-func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
|
|
| 391 |
- if strings.HasPrefix(name.Name(), "library/") {
|
|
| 392 |
- // If pull "library/foo", it's stored locally under "foo" |
|
| 393 |
- return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1]) |
|
| 394 |
- } |
|
| 395 |
- return name, nil |
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-// localNameFromRemote combines the index name and the repo remote name |
|
| 399 |
-// to generate a repo local name. |
|
| 400 |
-func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) {
|
|
| 401 |
- return reference.WithName(indexName + "/" + remoteName.Name()) |
|
| 402 |
-} |
|
| 403 |
- |
|
| 404 |
-// NormalizeLocalReference transforms a reference to use a normalized LocalName |
|
| 405 |
-// for the name poriton. Passes through the reference without transformation on |
|
| 406 |
-// error. |
|
| 407 |
-func NormalizeLocalReference(ref reference.Named) reference.Named {
|
|
| 408 |
- localName := NormalizeLocalName(ref) |
|
| 409 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 410 |
- newRef, err := reference.WithTag(localName, tagged.Tag()) |
|
| 411 |
- if err != nil {
|
|
| 412 |
- return ref |
|
| 413 |
- } |
|
| 414 |
- return newRef |
|
| 415 |
- } else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 416 |
- newRef, err := reference.WithDigest(localName, digested.Digest()) |
|
| 417 |
- if err != nil {
|
|
| 418 |
- return ref |
|
| 419 |
- } |
|
| 420 |
- return newRef |
|
| 421 |
- } |
|
| 422 |
- return localName |
|
| 423 |
-} |
| ... | ... |
@@ -356,7 +356,6 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
|
| 356 | 356 |
apiError(w, "Could not parse repository", 400) |
| 357 | 357 |
return |
| 358 | 358 |
} |
| 359 |
- repositoryName = NormalizeLocalName(repositoryName) |
|
| 360 | 359 |
tags, exists := testRepositories[repositoryName.String()] |
| 361 | 360 |
if !exists {
|
| 362 | 361 |
apiError(w, "Repository not found", 404) |
| ... | ... |
@@ -380,7 +379,6 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
|
| 380 | 380 |
apiError(w, "Could not parse repository", 400) |
| 381 | 381 |
return |
| 382 | 382 |
} |
| 383 |
- repositoryName = NormalizeLocalName(repositoryName) |
|
| 384 | 383 |
tagName := vars["tag"] |
| 385 | 384 |
tags, exists := testRepositories[repositoryName.String()] |
| 386 | 385 |
if !exists {
|
| ... | ... |
@@ -405,7 +403,6 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
|
| 405 | 405 |
apiError(w, "Could not parse repository", 400) |
| 406 | 406 |
return |
| 407 | 407 |
} |
| 408 |
- repositoryName = NormalizeLocalName(repositoryName) |
|
| 409 | 408 |
tagName := vars["tag"] |
| 410 | 409 |
tags, exists := testRepositories[repositoryName.String()] |
| 411 | 410 |
if !exists {
|
| ... | ... |
@@ -307,71 +307,24 @@ func TestPushImageLayerRegistry(t *testing.T) {
|
| 307 | 307 |
} |
| 308 | 308 |
} |
| 309 | 309 |
|
| 310 |
-func TestValidateRepositoryName(t *testing.T) {
|
|
| 311 |
- validRepoNames := []string{
|
|
| 312 |
- "docker/docker", |
|
| 313 |
- "library/debian", |
|
| 314 |
- "debian", |
|
| 315 |
- "docker.io/docker/docker", |
|
| 316 |
- "docker.io/library/debian", |
|
| 317 |
- "docker.io/debian", |
|
| 318 |
- "index.docker.io/docker/docker", |
|
| 319 |
- "index.docker.io/library/debian", |
|
| 320 |
- "index.docker.io/debian", |
|
| 321 |
- "127.0.0.1:5000/docker/docker", |
|
| 322 |
- "127.0.0.1:5000/library/debian", |
|
| 323 |
- "127.0.0.1:5000/debian", |
|
| 324 |
- "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 325 |
- } |
|
| 326 |
- invalidRepoNames := []string{
|
|
| 327 |
- "https://github.com/docker/docker", |
|
| 328 |
- "docker/Docker", |
|
| 329 |
- "-docker", |
|
| 330 |
- "-docker/docker", |
|
| 331 |
- "-docker.io/docker/docker", |
|
| 332 |
- "docker///docker", |
|
| 333 |
- "docker.io/docker/Docker", |
|
| 334 |
- "docker.io/docker///docker", |
|
| 335 |
- "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 336 |
- "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 337 |
- } |
|
| 338 |
- |
|
| 339 |
- for _, name := range invalidRepoNames {
|
|
| 340 |
- named, err := reference.WithName(name) |
|
| 341 |
- if err == nil {
|
|
| 342 |
- err := ValidateRepositoryName(named) |
|
| 343 |
- assertNotEqual(t, err, nil, "Expected invalid repo name: "+name) |
|
| 344 |
- } |
|
| 345 |
- } |
|
| 346 |
- |
|
| 347 |
- for _, name := range validRepoNames {
|
|
| 348 |
- named, err := reference.WithName(name) |
|
| 349 |
- if err != nil {
|
|
| 350 |
- t.Fatalf("could not parse valid name: %s", name)
|
|
| 351 |
- } |
|
| 352 |
- err = ValidateRepositoryName(named) |
|
| 353 |
- assertEqual(t, err, nil, "Expected valid repo name: "+name) |
|
| 354 |
- } |
|
| 355 |
-} |
|
| 356 |
- |
|
| 357 | 310 |
func TestParseRepositoryInfo(t *testing.T) {
|
| 358 |
- withName := func(name string) reference.Named {
|
|
| 359 |
- named, err := reference.WithName(name) |
|
| 360 |
- if err != nil {
|
|
| 361 |
- t.Fatalf("could not parse reference %s", name)
|
|
| 362 |
- } |
|
| 363 |
- return named |
|
| 311 |
+ type staticRepositoryInfo struct {
|
|
| 312 |
+ Index *registrytypes.IndexInfo |
|
| 313 |
+ RemoteName string |
|
| 314 |
+ CanonicalName string |
|
| 315 |
+ LocalName string |
|
| 316 |
+ Official bool |
|
| 364 | 317 |
} |
| 365 | 318 |
|
| 366 |
- expectedRepoInfos := map[string]RepositoryInfo{
|
|
| 319 |
+ expectedRepoInfos := map[string]staticRepositoryInfo{
|
|
| 367 | 320 |
"fooo/bar": {
|
| 368 | 321 |
Index: ®istrytypes.IndexInfo{
|
| 369 | 322 |
Name: IndexName, |
| 370 | 323 |
Official: true, |
| 371 | 324 |
}, |
| 372 |
- RemoteName: withName("fooo/bar"),
|
|
| 373 |
- LocalName: withName("fooo/bar"),
|
|
| 374 |
- CanonicalName: withName("docker.io/fooo/bar"),
|
|
| 325 |
+ RemoteName: "fooo/bar", |
|
| 326 |
+ LocalName: "fooo/bar", |
|
| 327 |
+ CanonicalName: "docker.io/fooo/bar", |
|
| 375 | 328 |
Official: false, |
| 376 | 329 |
}, |
| 377 | 330 |
"library/ubuntu": {
|
| ... | ... |
@@ -379,9 +332,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 379 | 379 |
Name: IndexName, |
| 380 | 380 |
Official: true, |
| 381 | 381 |
}, |
| 382 |
- RemoteName: withName("library/ubuntu"),
|
|
| 383 |
- LocalName: withName("ubuntu"),
|
|
| 384 |
- CanonicalName: withName("docker.io/library/ubuntu"),
|
|
| 382 |
+ RemoteName: "library/ubuntu", |
|
| 383 |
+ LocalName: "ubuntu", |
|
| 384 |
+ CanonicalName: "docker.io/library/ubuntu", |
|
| 385 | 385 |
Official: true, |
| 386 | 386 |
}, |
| 387 | 387 |
"nonlibrary/ubuntu": {
|
| ... | ... |
@@ -389,9 +342,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 389 | 389 |
Name: IndexName, |
| 390 | 390 |
Official: true, |
| 391 | 391 |
}, |
| 392 |
- RemoteName: withName("nonlibrary/ubuntu"),
|
|
| 393 |
- LocalName: withName("nonlibrary/ubuntu"),
|
|
| 394 |
- CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
|
|
| 392 |
+ RemoteName: "nonlibrary/ubuntu", |
|
| 393 |
+ LocalName: "nonlibrary/ubuntu", |
|
| 394 |
+ CanonicalName: "docker.io/nonlibrary/ubuntu", |
|
| 395 | 395 |
Official: false, |
| 396 | 396 |
}, |
| 397 | 397 |
"ubuntu": {
|
| ... | ... |
@@ -399,9 +352,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 399 | 399 |
Name: IndexName, |
| 400 | 400 |
Official: true, |
| 401 | 401 |
}, |
| 402 |
- RemoteName: withName("library/ubuntu"),
|
|
| 403 |
- LocalName: withName("ubuntu"),
|
|
| 404 |
- CanonicalName: withName("docker.io/library/ubuntu"),
|
|
| 402 |
+ RemoteName: "library/ubuntu", |
|
| 403 |
+ LocalName: "ubuntu", |
|
| 404 |
+ CanonicalName: "docker.io/library/ubuntu", |
|
| 405 | 405 |
Official: true, |
| 406 | 406 |
}, |
| 407 | 407 |
"other/library": {
|
| ... | ... |
@@ -409,9 +362,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 409 | 409 |
Name: IndexName, |
| 410 | 410 |
Official: true, |
| 411 | 411 |
}, |
| 412 |
- RemoteName: withName("other/library"),
|
|
| 413 |
- LocalName: withName("other/library"),
|
|
| 414 |
- CanonicalName: withName("docker.io/other/library"),
|
|
| 412 |
+ RemoteName: "other/library", |
|
| 413 |
+ LocalName: "other/library", |
|
| 414 |
+ CanonicalName: "docker.io/other/library", |
|
| 415 | 415 |
Official: false, |
| 416 | 416 |
}, |
| 417 | 417 |
"127.0.0.1:8000/private/moonbase": {
|
| ... | ... |
@@ -419,9 +372,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 419 | 419 |
Name: "127.0.0.1:8000", |
| 420 | 420 |
Official: false, |
| 421 | 421 |
}, |
| 422 |
- RemoteName: withName("private/moonbase"),
|
|
| 423 |
- LocalName: withName("127.0.0.1:8000/private/moonbase"),
|
|
| 424 |
- CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
|
|
| 422 |
+ RemoteName: "private/moonbase", |
|
| 423 |
+ LocalName: "127.0.0.1:8000/private/moonbase", |
|
| 424 |
+ CanonicalName: "127.0.0.1:8000/private/moonbase", |
|
| 425 | 425 |
Official: false, |
| 426 | 426 |
}, |
| 427 | 427 |
"127.0.0.1:8000/privatebase": {
|
| ... | ... |
@@ -429,9 +382,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 429 | 429 |
Name: "127.0.0.1:8000", |
| 430 | 430 |
Official: false, |
| 431 | 431 |
}, |
| 432 |
- RemoteName: withName("privatebase"),
|
|
| 433 |
- LocalName: withName("127.0.0.1:8000/privatebase"),
|
|
| 434 |
- CanonicalName: withName("127.0.0.1:8000/privatebase"),
|
|
| 432 |
+ RemoteName: "privatebase", |
|
| 433 |
+ LocalName: "127.0.0.1:8000/privatebase", |
|
| 434 |
+ CanonicalName: "127.0.0.1:8000/privatebase", |
|
| 435 | 435 |
Official: false, |
| 436 | 436 |
}, |
| 437 | 437 |
"localhost:8000/private/moonbase": {
|
| ... | ... |
@@ -439,9 +392,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 439 | 439 |
Name: "localhost:8000", |
| 440 | 440 |
Official: false, |
| 441 | 441 |
}, |
| 442 |
- RemoteName: withName("private/moonbase"),
|
|
| 443 |
- LocalName: withName("localhost:8000/private/moonbase"),
|
|
| 444 |
- CanonicalName: withName("localhost:8000/private/moonbase"),
|
|
| 442 |
+ RemoteName: "private/moonbase", |
|
| 443 |
+ LocalName: "localhost:8000/private/moonbase", |
|
| 444 |
+ CanonicalName: "localhost:8000/private/moonbase", |
|
| 445 | 445 |
Official: false, |
| 446 | 446 |
}, |
| 447 | 447 |
"localhost:8000/privatebase": {
|
| ... | ... |
@@ -449,9 +402,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 449 | 449 |
Name: "localhost:8000", |
| 450 | 450 |
Official: false, |
| 451 | 451 |
}, |
| 452 |
- RemoteName: withName("privatebase"),
|
|
| 453 |
- LocalName: withName("localhost:8000/privatebase"),
|
|
| 454 |
- CanonicalName: withName("localhost:8000/privatebase"),
|
|
| 452 |
+ RemoteName: "privatebase", |
|
| 453 |
+ LocalName: "localhost:8000/privatebase", |
|
| 454 |
+ CanonicalName: "localhost:8000/privatebase", |
|
| 455 | 455 |
Official: false, |
| 456 | 456 |
}, |
| 457 | 457 |
"example.com/private/moonbase": {
|
| ... | ... |
@@ -459,9 +412,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 459 | 459 |
Name: "example.com", |
| 460 | 460 |
Official: false, |
| 461 | 461 |
}, |
| 462 |
- RemoteName: withName("private/moonbase"),
|
|
| 463 |
- LocalName: withName("example.com/private/moonbase"),
|
|
| 464 |
- CanonicalName: withName("example.com/private/moonbase"),
|
|
| 462 |
+ RemoteName: "private/moonbase", |
|
| 463 |
+ LocalName: "example.com/private/moonbase", |
|
| 464 |
+ CanonicalName: "example.com/private/moonbase", |
|
| 465 | 465 |
Official: false, |
| 466 | 466 |
}, |
| 467 | 467 |
"example.com/privatebase": {
|
| ... | ... |
@@ -469,9 +422,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 469 | 469 |
Name: "example.com", |
| 470 | 470 |
Official: false, |
| 471 | 471 |
}, |
| 472 |
- RemoteName: withName("privatebase"),
|
|
| 473 |
- LocalName: withName("example.com/privatebase"),
|
|
| 474 |
- CanonicalName: withName("example.com/privatebase"),
|
|
| 472 |
+ RemoteName: "privatebase", |
|
| 473 |
+ LocalName: "example.com/privatebase", |
|
| 474 |
+ CanonicalName: "example.com/privatebase", |
|
| 475 | 475 |
Official: false, |
| 476 | 476 |
}, |
| 477 | 477 |
"example.com:8000/private/moonbase": {
|
| ... | ... |
@@ -479,9 +432,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 479 | 479 |
Name: "example.com:8000", |
| 480 | 480 |
Official: false, |
| 481 | 481 |
}, |
| 482 |
- RemoteName: withName("private/moonbase"),
|
|
| 483 |
- LocalName: withName("example.com:8000/private/moonbase"),
|
|
| 484 |
- CanonicalName: withName("example.com:8000/private/moonbase"),
|
|
| 482 |
+ RemoteName: "private/moonbase", |
|
| 483 |
+ LocalName: "example.com:8000/private/moonbase", |
|
| 484 |
+ CanonicalName: "example.com:8000/private/moonbase", |
|
| 485 | 485 |
Official: false, |
| 486 | 486 |
}, |
| 487 | 487 |
"example.com:8000/privatebase": {
|
| ... | ... |
@@ -489,9 +442,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 489 | 489 |
Name: "example.com:8000", |
| 490 | 490 |
Official: false, |
| 491 | 491 |
}, |
| 492 |
- RemoteName: withName("privatebase"),
|
|
| 493 |
- LocalName: withName("example.com:8000/privatebase"),
|
|
| 494 |
- CanonicalName: withName("example.com:8000/privatebase"),
|
|
| 492 |
+ RemoteName: "privatebase", |
|
| 493 |
+ LocalName: "example.com:8000/privatebase", |
|
| 494 |
+ CanonicalName: "example.com:8000/privatebase", |
|
| 495 | 495 |
Official: false, |
| 496 | 496 |
}, |
| 497 | 497 |
"localhost/private/moonbase": {
|
| ... | ... |
@@ -499,9 +452,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 499 | 499 |
Name: "localhost", |
| 500 | 500 |
Official: false, |
| 501 | 501 |
}, |
| 502 |
- RemoteName: withName("private/moonbase"),
|
|
| 503 |
- LocalName: withName("localhost/private/moonbase"),
|
|
| 504 |
- CanonicalName: withName("localhost/private/moonbase"),
|
|
| 502 |
+ RemoteName: "private/moonbase", |
|
| 503 |
+ LocalName: "localhost/private/moonbase", |
|
| 504 |
+ CanonicalName: "localhost/private/moonbase", |
|
| 505 | 505 |
Official: false, |
| 506 | 506 |
}, |
| 507 | 507 |
"localhost/privatebase": {
|
| ... | ... |
@@ -509,9 +462,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 509 | 509 |
Name: "localhost", |
| 510 | 510 |
Official: false, |
| 511 | 511 |
}, |
| 512 |
- RemoteName: withName("privatebase"),
|
|
| 513 |
- LocalName: withName("localhost/privatebase"),
|
|
| 514 |
- CanonicalName: withName("localhost/privatebase"),
|
|
| 512 |
+ RemoteName: "privatebase", |
|
| 513 |
+ LocalName: "localhost/privatebase", |
|
| 514 |
+ CanonicalName: "localhost/privatebase", |
|
| 515 | 515 |
Official: false, |
| 516 | 516 |
}, |
| 517 | 517 |
IndexName + "/public/moonbase": {
|
| ... | ... |
@@ -519,9 +472,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 519 | 519 |
Name: IndexName, |
| 520 | 520 |
Official: true, |
| 521 | 521 |
}, |
| 522 |
- RemoteName: withName("public/moonbase"),
|
|
| 523 |
- LocalName: withName("public/moonbase"),
|
|
| 524 |
- CanonicalName: withName("docker.io/public/moonbase"),
|
|
| 522 |
+ RemoteName: "public/moonbase", |
|
| 523 |
+ LocalName: "public/moonbase", |
|
| 524 |
+ CanonicalName: "docker.io/public/moonbase", |
|
| 525 | 525 |
Official: false, |
| 526 | 526 |
}, |
| 527 | 527 |
"index." + IndexName + "/public/moonbase": {
|
| ... | ... |
@@ -529,9 +482,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 529 | 529 |
Name: IndexName, |
| 530 | 530 |
Official: true, |
| 531 | 531 |
}, |
| 532 |
- RemoteName: withName("public/moonbase"),
|
|
| 533 |
- LocalName: withName("public/moonbase"),
|
|
| 534 |
- CanonicalName: withName("docker.io/public/moonbase"),
|
|
| 532 |
+ RemoteName: "public/moonbase", |
|
| 533 |
+ LocalName: "public/moonbase", |
|
| 534 |
+ CanonicalName: "docker.io/public/moonbase", |
|
| 535 | 535 |
Official: false, |
| 536 | 536 |
}, |
| 537 | 537 |
"ubuntu-12.04-base": {
|
| ... | ... |
@@ -539,9 +492,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 539 | 539 |
Name: IndexName, |
| 540 | 540 |
Official: true, |
| 541 | 541 |
}, |
| 542 |
- RemoteName: withName("library/ubuntu-12.04-base"),
|
|
| 543 |
- LocalName: withName("ubuntu-12.04-base"),
|
|
| 544 |
- CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
|
| 542 |
+ RemoteName: "library/ubuntu-12.04-base", |
|
| 543 |
+ LocalName: "ubuntu-12.04-base", |
|
| 544 |
+ CanonicalName: "docker.io/library/ubuntu-12.04-base", |
|
| 545 | 545 |
Official: true, |
| 546 | 546 |
}, |
| 547 | 547 |
IndexName + "/ubuntu-12.04-base": {
|
| ... | ... |
@@ -549,9 +502,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 549 | 549 |
Name: IndexName, |
| 550 | 550 |
Official: true, |
| 551 | 551 |
}, |
| 552 |
- RemoteName: withName("library/ubuntu-12.04-base"),
|
|
| 553 |
- LocalName: withName("ubuntu-12.04-base"),
|
|
| 554 |
- CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
|
| 552 |
+ RemoteName: "library/ubuntu-12.04-base", |
|
| 553 |
+ LocalName: "ubuntu-12.04-base", |
|
| 554 |
+ CanonicalName: "docker.io/library/ubuntu-12.04-base", |
|
| 555 | 555 |
Official: true, |
| 556 | 556 |
}, |
| 557 | 557 |
"index." + IndexName + "/ubuntu-12.04-base": {
|
| ... | ... |
@@ -559,9 +512,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 559 | 559 |
Name: IndexName, |
| 560 | 560 |
Official: true, |
| 561 | 561 |
}, |
| 562 |
- RemoteName: withName("library/ubuntu-12.04-base"),
|
|
| 563 |
- LocalName: withName("ubuntu-12.04-base"),
|
|
| 564 |
- CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
|
| 562 |
+ RemoteName: "library/ubuntu-12.04-base", |
|
| 563 |
+ LocalName: "ubuntu-12.04-base", |
|
| 564 |
+ CanonicalName: "docker.io/library/ubuntu-12.04-base", |
|
| 565 | 565 |
Official: true, |
| 566 | 566 |
}, |
| 567 | 567 |
} |
| ... | ... |
@@ -577,9 +530,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 577 | 577 |
t.Error(err) |
| 578 | 578 |
} else {
|
| 579 | 579 |
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName) |
| 580 |
- checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName) |
|
| 581 |
- checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName) |
|
| 582 |
- checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName) |
|
| 580 |
+ checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName) |
|
| 581 |
+ checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName) |
|
| 582 |
+ checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName) |
|
| 583 | 583 |
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName) |
| 584 | 584 |
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName) |
| 585 | 585 |
} |
| ... | ... |
@@ -806,82 +759,6 @@ func TestSearchRepositories(t *testing.T) {
|
| 806 | 806 |
assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars") |
| 807 | 807 |
} |
| 808 | 808 |
|
| 809 |
-func TestValidRemoteName(t *testing.T) {
|
|
| 810 |
- validRepositoryNames := []string{
|
|
| 811 |
- // Sanity check. |
|
| 812 |
- "docker/docker", |
|
| 813 |
- |
|
| 814 |
- // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). |
|
| 815 |
- "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 816 |
- |
|
| 817 |
- // Allow embedded hyphens. |
|
| 818 |
- "docker-rules/docker", |
|
| 819 |
- |
|
| 820 |
- // Allow multiple hyphens as well. |
|
| 821 |
- "docker---rules/docker", |
|
| 822 |
- |
|
| 823 |
- //Username doc and image name docker being tested. |
|
| 824 |
- "doc/docker", |
|
| 825 |
- |
|
| 826 |
- // single character names are now allowed. |
|
| 827 |
- "d/docker", |
|
| 828 |
- "jess/t", |
|
| 829 |
- |
|
| 830 |
- // Consecutive underscores. |
|
| 831 |
- "dock__er/docker", |
|
| 832 |
- } |
|
| 833 |
- for _, repositoryName := range validRepositoryNames {
|
|
| 834 |
- repositoryRef, err := reference.WithName(repositoryName) |
|
| 835 |
- if err != nil {
|
|
| 836 |
- t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
| 837 |
- } |
|
| 838 |
- if err := validateRemoteName(repositoryRef); err != nil {
|
|
| 839 |
- t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
| 840 |
- } |
|
| 841 |
- } |
|
| 842 |
- |
|
| 843 |
- invalidRepositoryNames := []string{
|
|
| 844 |
- // Disallow capital letters. |
|
| 845 |
- "docker/Docker", |
|
| 846 |
- |
|
| 847 |
- // Only allow one slash. |
|
| 848 |
- "docker///docker", |
|
| 849 |
- |
|
| 850 |
- // Disallow 64-character hexadecimal. |
|
| 851 |
- "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 852 |
- |
|
| 853 |
- // Disallow leading and trailing hyphens in namespace. |
|
| 854 |
- "-docker/docker", |
|
| 855 |
- "docker-/docker", |
|
| 856 |
- "-docker-/docker", |
|
| 857 |
- |
|
| 858 |
- // Don't allow underscores everywhere (as opposed to hyphens). |
|
| 859 |
- "____/____", |
|
| 860 |
- |
|
| 861 |
- "_docker/_docker", |
|
| 862 |
- |
|
| 863 |
- // Disallow consecutive periods. |
|
| 864 |
- "dock..er/docker", |
|
| 865 |
- "dock_.er/docker", |
|
| 866 |
- "dock-.er/docker", |
|
| 867 |
- |
|
| 868 |
- // No repository. |
|
| 869 |
- "docker/", |
|
| 870 |
- |
|
| 871 |
- //namespace too long |
|
| 872 |
- "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", |
|
| 873 |
- } |
|
| 874 |
- for _, repositoryName := range invalidRepositoryNames {
|
|
| 875 |
- repositoryRef, err := reference.ParseNamed(repositoryName) |
|
| 876 |
- if err != nil {
|
|
| 877 |
- continue |
|
| 878 |
- } |
|
| 879 |
- if err := validateRemoteName(repositoryRef); err == nil {
|
|
| 880 |
- t.Errorf("Repository name should be invalid: %v", repositoryName)
|
|
| 881 |
- } |
|
| 882 |
- } |
|
| 883 |
-} |
|
| 884 |
- |
|
| 885 | 809 |
func TestTrustedLocation(t *testing.T) {
|
| 886 | 810 |
for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
|
| 887 | 811 |
req, _ := http.NewRequest("GET", url, nil)
|
| ... | ... |
@@ -11,7 +11,7 @@ import ( |
| 11 | 11 |
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
| 12 | 12 |
var cfg = tlsconfig.ServerDefault |
| 13 | 13 |
tlsConfig := &cfg |
| 14 |
- nameString := repoName.Name() |
|
| 14 |
+ nameString := repoName.FullName() |
|
| 15 | 15 |
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
| 16 | 16 |
endpoints = append(endpoints, APIEndpoint{
|
| 17 | 17 |
URL: DefaultV1Registry, |
| ... | ... |
@@ -12,7 +12,7 @@ import ( |
| 12 | 12 |
func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
| 13 | 13 |
var cfg = tlsconfig.ServerDefault |
| 14 | 14 |
tlsConfig := &cfg |
| 15 |
- nameString := repoName.Name() |
|
| 15 |
+ nameString := repoName.FullName() |
|
| 16 | 16 |
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
| 17 | 17 |
// v2 mirrors |
| 18 | 18 |
for _, mirror := range s.Config.Mirrors {
|
| ... | ... |
@@ -312,7 +312,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io |
| 312 | 312 |
// argument, and returns data from the first one that answers the query |
| 313 | 313 |
// successfully. |
| 314 | 314 |
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
|
| 315 |
- repository := repositoryRef.Name() |
|
| 315 |
+ repository := repositoryRef.RemoteName() |
|
| 316 | 316 |
|
| 317 | 317 |
if strings.Count(repository, "/") == 0 {
|
| 318 | 318 |
// This will be removed once the registry supports auto-resolution on |
| ... | ... |
@@ -350,7 +350,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name |
| 350 | 350 |
// the first one that answers the query successfully. It returns a map with |
| 351 | 351 |
// tag names as the keys and image IDs as the values. |
| 352 | 352 |
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
|
| 353 |
- repository := repositoryRef.Name() |
|
| 353 |
+ repository := repositoryRef.RemoteName() |
|
| 354 | 354 |
|
| 355 | 355 |
if strings.Count(repository, "/") == 0 {
|
| 356 | 356 |
// This will be removed once the registry supports auto-resolution on |
| ... | ... |
@@ -403,8 +403,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
|
| 403 | 403 |
} |
| 404 | 404 |
|
| 405 | 405 |
// GetRepositoryData returns lists of images and endpoints for the repository |
| 406 |
-func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
|
|
| 407 |
- repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
|
|
| 406 |
+func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
|
|
| 407 |
+ repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), name.RemoteName())
|
|
| 408 | 408 |
|
| 409 | 409 |
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
|
| 410 | 410 |
|
| ... | ... |
@@ -438,7 +438,7 @@ func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, er |
| 438 | 438 |
if err != nil {
|
| 439 | 439 |
logrus.Debugf("Error reading response body: %s", err)
|
| 440 | 440 |
} |
| 441 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
|
| 441 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
|
|
| 442 | 442 |
} |
| 443 | 443 |
|
| 444 | 444 |
var endpoints []string |
| ... | ... |
@@ -593,7 +593,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry |
| 593 | 593 |
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
|
| 594 | 594 |
// "jsonify" the string |
| 595 | 595 |
revision = "\"" + revision + "\"" |
| 596 |
- path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
|
|
| 596 |
+ path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
|
|
| 597 | 597 |
|
| 598 | 598 |
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
|
| 599 | 599 |
if err != nil {
|
| ... | ... |
@@ -607,7 +607,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr |
| 607 | 607 |
} |
| 608 | 608 |
res.Body.Close() |
| 609 | 609 |
if res.StatusCode != 200 && res.StatusCode != 201 {
|
| 610 |
- return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
|
|
| 610 |
+ return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
|
|
| 611 | 611 |
} |
| 612 | 612 |
return nil |
| 613 | 613 |
} |
| ... | ... |
@@ -633,7 +633,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 633 | 633 |
if validate {
|
| 634 | 634 |
suffix = "images" |
| 635 | 635 |
} |
| 636 |
- u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
|
|
| 636 |
+ u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.RemoteName(), suffix)
|
|
| 637 | 637 |
logrus.Debugf("[registry] PUT %s", u)
|
| 638 | 638 |
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
| 639 | 639 |
headers := map[string][]string{
|
| ... | ... |
@@ -671,7 +671,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 671 | 671 |
if err != nil {
|
| 672 | 672 |
logrus.Debugf("Error reading response body: %s", err)
|
| 673 | 673 |
} |
| 674 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
|
| 674 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
|
| 675 | 675 |
} |
| 676 | 676 |
tokens = res.Header["X-Docker-Token"] |
| 677 | 677 |
logrus.Debugf("Auth token: %v", tokens)
|
| ... | ... |
@@ -689,7 +689,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 689 | 689 |
if err != nil {
|
| 690 | 690 |
logrus.Debugf("Error reading response body: %s", err)
|
| 691 | 691 |
} |
| 692 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
|
| 692 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
|
| 693 | 693 |
} |
| 694 | 694 |
} |
| 695 | 695 |
|
| ... | ... |
@@ -60,17 +60,9 @@ const ( |
| 60 | 60 |
|
| 61 | 61 |
// RepositoryInfo describes a repository |
| 62 | 62 |
type RepositoryInfo struct {
|
| 63 |
+ reference.Named |
|
| 63 | 64 |
// Index points to registry information |
| 64 | 65 |
Index *registrytypes.IndexInfo |
| 65 |
- // RemoteName is the remote name of the repository, such as |
|
| 66 |
- // "library/ubuntu-12.04-base" |
|
| 67 |
- RemoteName reference.Named |
|
| 68 |
- // LocalName is the local name of the repository, such as |
|
| 69 |
- // "ubuntu-12.04-base" |
|
| 70 |
- LocalName reference.Named |
|
| 71 |
- // CanonicalName is the canonical name of the repository, such as |
|
| 72 |
- // "docker.io/library/ubuntu-12.04-base" |
|
| 73 |
- CanonicalName reference.Named |
|
| 74 | 66 |
// Official indicates whether the repository is considered official. |
| 75 | 67 |
// If the registry is official, and the normalized name does not |
| 76 | 68 |
// contain a '/' (e.g. "foo"), then it is considered an official repo. |