reference: use distribution reference and remove fork
| ... | ... |
@@ -7,13 +7,12 @@ import ( |
| 7 | 7 |
"strconv" |
| 8 | 8 |
"strings" |
| 9 | 9 |
|
| 10 |
- distreference "github.com/docker/distribution/reference" |
|
| 10 |
+ "github.com/docker/distribution/reference" |
|
| 11 | 11 |
"github.com/docker/docker/api/server/httputils" |
| 12 | 12 |
"github.com/docker/docker/api/types" |
| 13 | 13 |
"github.com/docker/docker/api/types/filters" |
| 14 | 14 |
"github.com/docker/docker/pkg/ioutils" |
| 15 | 15 |
"github.com/docker/docker/pkg/streamformatter" |
| 16 |
- "github.com/docker/docker/reference" |
|
| 17 | 16 |
"github.com/pkg/errors" |
| 18 | 17 |
"golang.org/x/net/context" |
| 19 | 18 |
) |
| ... | ... |
@@ -47,39 +46,27 @@ func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig) |
| 47 | 47 |
// be returned. |
| 48 | 48 |
func parseRemoteRef(remote string) (reference.Named, string, error) {
|
| 49 | 49 |
// Parse remote reference, supporting remotes with name and tag |
| 50 |
- // NOTE: Using distribution reference to handle references |
|
| 51 |
- // containing both a name and digest |
|
| 52 |
- remoteRef, err := distreference.ParseNamed(remote) |
|
| 50 |
+ remoteRef, err := reference.ParseNormalizedNamed(remote) |
|
| 53 | 51 |
if err != nil {
|
| 54 | 52 |
return nil, "", err |
| 55 | 53 |
} |
| 56 | 54 |
|
| 57 |
- var tag string |
|
| 58 |
- if t, ok := remoteRef.(distreference.Tagged); ok {
|
|
| 59 |
- tag = t.Tag() |
|
| 55 |
+ type canonicalWithTag interface {
|
|
| 56 |
+ reference.Canonical |
|
| 57 |
+ Tag() string |
|
| 60 | 58 |
} |
| 61 | 59 |
|
| 62 |
- // Convert distribution reference to docker reference |
|
| 63 |
- // TODO: remove when docker reference changes reconciled upstream |
|
| 64 |
- ref, err := reference.WithName(remoteRef.Name()) |
|
| 65 |
- if err != nil {
|
|
| 66 |
- return nil, "", err |
|
| 67 |
- } |
|
| 68 |
- if d, ok := remoteRef.(distreference.Digested); ok {
|
|
| 69 |
- ref, err = reference.WithDigest(ref, d.Digest()) |
|
| 70 |
- if err != nil {
|
|
| 71 |
- return nil, "", err |
|
| 72 |
- } |
|
| 73 |
- } else if tag != "" {
|
|
| 74 |
- ref, err = reference.WithTag(ref, tag) |
|
| 60 |
+ if canonical, ok := remoteRef.(canonicalWithTag); ok {
|
|
| 61 |
+ remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest()) |
|
| 75 | 62 |
if err != nil {
|
| 76 | 63 |
return nil, "", err |
| 77 | 64 |
} |
| 78 |
- } else {
|
|
| 79 |
- ref = reference.WithDefaultTag(ref) |
|
| 65 |
+ return remoteRef, canonical.Tag(), nil |
|
| 80 | 66 |
} |
| 81 | 67 |
|
| 82 |
- return ref, tag, nil |
|
| 68 |
+ remoteRef = reference.TagNameOnly(remoteRef) |
|
| 69 |
+ |
|
| 70 |
+ return remoteRef, "", nil |
|
| 83 | 71 |
} |
| 84 | 72 |
|
| 85 | 73 |
func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| ... | ... |
@@ -188,24 +175,24 @@ func getName(ref reference.Named, tag, name string) (string, error) {
|
| 188 | 188 |
if err != nil {
|
| 189 | 189 |
return "", err |
| 190 | 190 |
} |
| 191 |
- name = nt.String() |
|
| 191 |
+ name = reference.FamiliarString(nt) |
|
| 192 | 192 |
} else {
|
| 193 |
- name = reference.WithDefaultTag(trimmed).String() |
|
| 193 |
+ name = reference.FamiliarString(reference.TagNameOnly(trimmed)) |
|
| 194 | 194 |
} |
| 195 | 195 |
} else {
|
| 196 |
- name = ref.String() |
|
| 196 |
+ name = reference.FamiliarString(ref) |
|
| 197 | 197 |
} |
| 198 | 198 |
} else {
|
| 199 |
- localRef, err := reference.ParseNamed(name) |
|
| 199 |
+ localRef, err := reference.ParseNormalizedNamed(name) |
|
| 200 | 200 |
if err != nil {
|
| 201 | 201 |
return "", err |
| 202 | 202 |
} |
| 203 | 203 |
if _, ok := localRef.(reference.Canonical); ok {
|
| 204 | 204 |
return "", errors.New("cannot use digest in plugin tag")
|
| 205 | 205 |
} |
| 206 |
- if distreference.IsNameOnly(localRef) {
|
|
| 206 |
+ if reference.IsNameOnly(localRef) {
|
|
| 207 | 207 |
// TODO: log change in name to out stream |
| 208 |
- name = reference.WithDefaultTag(localRef).String() |
|
| 208 |
+ name = reference.FamiliarString(reference.TagNameOnly(localRef)) |
|
| 209 | 209 |
} |
| 210 | 210 |
} |
| 211 | 211 |
return name, nil |
| 212 | 212 |
deleted file mode 100644 |
| ... | ... |
@@ -1,34 +0,0 @@ |
| 1 |
-package reference |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- distreference "github.com/docker/distribution/reference" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-// Parse parses the given references and returns the repository and |
|
| 8 |
-// tag (if present) from it. If there is an error during parsing, it will |
|
| 9 |
-// return an error. |
|
| 10 |
-func Parse(ref string) (string, string, error) {
|
|
| 11 |
- distributionRef, err := distreference.ParseNamed(ref) |
|
| 12 |
- if err != nil {
|
|
| 13 |
- return "", "", err |
|
| 14 |
- } |
|
| 15 |
- |
|
| 16 |
- tag := GetTagFromNamedRef(distributionRef) |
|
| 17 |
- return distributionRef.Name(), tag, nil |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-// GetTagFromNamedRef returns a tag from the specified reference. |
|
| 21 |
-// This function is necessary as long as the docker "server" api makes the distinction between repository |
|
| 22 |
-// and tags. |
|
| 23 |
-func GetTagFromNamedRef(ref distreference.Named) string {
|
|
| 24 |
- var tag string |
|
| 25 |
- switch x := ref.(type) {
|
|
| 26 |
- case distreference.Digested: |
|
| 27 |
- tag = x.Digest().String() |
|
| 28 |
- case distreference.NamedTagged: |
|
| 29 |
- tag = x.Tag() |
|
| 30 |
- default: |
|
| 31 |
- tag = "latest" |
|
| 32 |
- } |
|
| 33 |
- return tag |
|
| 34 |
-} |
| 35 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,73 +0,0 @@ |
| 1 |
-package reference |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- _ "crypto/sha256" |
|
| 5 |
- "testing" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func TestParse(t *testing.T) {
|
|
| 9 |
- testCases := []struct {
|
|
| 10 |
- ref string |
|
| 11 |
- expectedName string |
|
| 12 |
- expectedTag string |
|
| 13 |
- expectedError bool |
|
| 14 |
- }{
|
|
| 15 |
- {
|
|
| 16 |
- ref: "", |
|
| 17 |
- expectedName: "", |
|
| 18 |
- expectedTag: "", |
|
| 19 |
- expectedError: true, |
|
| 20 |
- }, |
|
| 21 |
- {
|
|
| 22 |
- ref: "repository", |
|
| 23 |
- expectedName: "repository", |
|
| 24 |
- expectedTag: "latest", |
|
| 25 |
- expectedError: false, |
|
| 26 |
- }, |
|
| 27 |
- {
|
|
| 28 |
- ref: "repository:tag", |
|
| 29 |
- expectedName: "repository", |
|
| 30 |
- expectedTag: "tag", |
|
| 31 |
- expectedError: false, |
|
| 32 |
- }, |
|
| 33 |
- {
|
|
| 34 |
- ref: "test.com/repository", |
|
| 35 |
- expectedName: "test.com/repository", |
|
| 36 |
- expectedTag: "latest", |
|
| 37 |
- expectedError: false, |
|
| 38 |
- }, |
|
| 39 |
- {
|
|
| 40 |
- ref: "test.com:5000/test/repository", |
|
| 41 |
- expectedName: "test.com:5000/test/repository", |
|
| 42 |
- expectedTag: "latest", |
|
| 43 |
- expectedError: false, |
|
| 44 |
- }, |
|
| 45 |
- {
|
|
| 46 |
- ref: "test.com:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|
| 47 |
- expectedName: "test.com:5000/repo", |
|
| 48 |
- expectedTag: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|
| 49 |
- expectedError: false, |
|
| 50 |
- }, |
|
| 51 |
- {
|
|
| 52 |
- ref: "test.com:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|
| 53 |
- expectedName: "test.com:5000/repo", |
|
| 54 |
- expectedTag: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|
| 55 |
- expectedError: false, |
|
| 56 |
- }, |
|
| 57 |
- } |
|
| 58 |
- |
|
| 59 |
- for _, c := range testCases {
|
|
| 60 |
- name, tag, err := Parse(c.ref) |
|
| 61 |
- if err != nil && c.expectedError {
|
|
| 62 |
- continue |
|
| 63 |
- } else if err != nil {
|
|
| 64 |
- t.Fatalf("error with %s: %s", c.ref, err.Error())
|
|
| 65 |
- } |
|
| 66 |
- if name != c.expectedName {
|
|
| 67 |
- t.Fatalf("expected name %s, got %s", c.expectedName, name)
|
|
| 68 |
- } |
|
| 69 |
- if tag != c.expectedTag {
|
|
| 70 |
- t.Fatalf("expected tag %s, got %s", c.expectedTag, tag)
|
|
| 71 |
- } |
|
| 72 |
- } |
|
| 73 |
-} |
| ... | ... |
@@ -9,11 +9,11 @@ import ( |
| 9 | 9 |
"os" |
| 10 | 10 |
"time" |
| 11 | 11 |
|
| 12 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 13 |
"github.com/docker/docker/api/types" |
| 13 | 14 |
"github.com/docker/docker/api/types/backend" |
| 14 | 15 |
"github.com/docker/docker/api/types/container" |
| 15 | 16 |
"github.com/docker/docker/image" |
| 16 |
- "github.com/docker/docker/reference" |
|
| 17 | 17 |
"golang.org/x/net/context" |
| 18 | 18 |
) |
| 19 | 19 |
|
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
|
| 13 | 13 |
"github.com/Sirupsen/logrus" |
| 14 |
+ "github.com/docker/distribution/reference" |
|
| 14 | 15 |
apierrors "github.com/docker/docker/api/errors" |
| 15 | 16 |
"github.com/docker/docker/api/types" |
| 16 | 17 |
"github.com/docker/docker/api/types/backend" |
| ... | ... |
@@ -19,7 +20,6 @@ import ( |
| 19 | 19 |
"github.com/docker/docker/builder/dockerfile/parser" |
| 20 | 20 |
"github.com/docker/docker/image" |
| 21 | 21 |
"github.com/docker/docker/pkg/stringid" |
| 22 |
- "github.com/docker/docker/reference" |
|
| 23 | 22 |
perrors "github.com/pkg/errors" |
| 24 | 23 |
"golang.org/x/net/context" |
| 25 | 24 |
) |
| ... | ... |
@@ -178,23 +178,16 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
|
| 178 | 178 |
continue |
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
- ref, err := reference.ParseNamed(repo) |
|
| 181 |
+ ref, err := reference.ParseNormalizedNamed(repo) |
|
| 182 | 182 |
if err != nil {
|
| 183 | 183 |
return nil, err |
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 |
- ref = reference.WithDefaultTag(ref) |
|
| 187 |
- |
|
| 188 | 186 |
if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
| 189 | 187 |
return nil, errors.New("build tag cannot contain a digest")
|
| 190 | 188 |
} |
| 191 | 189 |
|
| 192 |
- if _, isTagged := ref.(reference.NamedTagged); !isTagged {
|
|
| 193 |
- ref, err = reference.WithTag(ref, reference.DefaultTag) |
|
| 194 |
- if err != nil {
|
|
| 195 |
- return nil, err |
|
| 196 |
- } |
|
| 197 |
- } |
|
| 190 |
+ ref = reference.TagNameOnly(ref) |
|
| 198 | 191 |
|
| 199 | 192 |
nameWithTag := ref.String() |
| 200 | 193 |
|
| ... | ... |
@@ -168,11 +168,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config * |
| 168 | 168 |
return nil, err |
| 169 | 169 |
} |
| 170 | 170 |
if named, ok := ref.(reference.Named); ok {
|
| 171 |
- if reference.IsNameOnly(named) {
|
|
| 172 |
- namedRef = reference.EnsureTagged(named) |
|
| 173 |
- } else {
|
|
| 174 |
- namedRef = named |
|
| 175 |
- } |
|
| 171 |
+ namedRef = reference.TagNameOnly(named) |
|
| 176 | 172 |
|
| 177 | 173 |
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
|
| 178 | 174 |
var err error |
| ... | ... |
@@ -94,12 +94,12 @@ func (ctx *DiskUsageContext) Write() {
|
| 94 | 94 |
tag := "<none>" |
| 95 | 95 |
if len(i.RepoTags) > 0 && !isDangling(*i) {
|
| 96 | 96 |
// Only show the first tag |
| 97 |
- ref, err := reference.ParseNamed(i.RepoTags[0]) |
|
| 97 |
+ ref, err := reference.ParseNormalizedNamed(i.RepoTags[0]) |
|
| 98 | 98 |
if err != nil {
|
| 99 | 99 |
continue |
| 100 | 100 |
} |
| 101 | 101 |
if nt, ok := ref.(reference.NamedTagged); ok {
|
| 102 |
- repo = ref.Name() |
|
| 102 |
+ repo = reference.FamiliarName(ref) |
|
| 103 | 103 |
tag = nt.Tag() |
| 104 | 104 |
} |
| 105 | 105 |
} |
| ... | ... |
@@ -94,7 +94,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC |
| 94 | 94 |
repoTags := map[string][]string{}
|
| 95 | 95 |
repoDigests := map[string][]string{}
|
| 96 | 96 |
|
| 97 |
- for _, refString := range append(image.RepoTags) {
|
|
| 97 |
+ for _, refString := range image.RepoTags {
|
|
| 98 | 98 |
ref, err := reference.ParseNormalizedNamed(refString) |
| 99 | 99 |
if err != nil {
|
| 100 | 100 |
continue |
| ... | ... |
@@ -104,7 +104,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC |
| 104 | 104 |
repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag()) |
| 105 | 105 |
} |
| 106 | 106 |
} |
| 107 |
- for _, refString := range append(image.RepoDigests) {
|
|
| 107 |
+ for _, refString := range image.RepoDigests {
|
|
| 108 | 108 |
ref, err := reference.ParseNormalizedNamed(refString) |
| 109 | 109 |
if err != nil {
|
| 110 | 110 |
continue |
| ... | ... |
@@ -5,7 +5,7 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 |
- distreference "github.com/docker/distribution/reference" |
|
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 9 | 9 |
mounttypes "github.com/docker/docker/api/types/mount" |
| 10 | 10 |
"github.com/docker/docker/api/types/swarm" |
| 11 | 11 |
"github.com/docker/docker/cli/command/inspect" |
| ... | ... |
@@ -409,11 +409,12 @@ func (c *serviceContext) Replicas() string {
|
| 409 | 409 |
func (c *serviceContext) Image() string {
|
| 410 | 410 |
c.AddHeader(imageHeader) |
| 411 | 411 |
image := c.service.Spec.TaskTemplate.ContainerSpec.Image |
| 412 |
- if ref, err := distreference.ParseNamed(image); err == nil {
|
|
| 413 |
- // update image string for display |
|
| 414 |
- namedTagged, ok := ref.(distreference.NamedTagged) |
|
| 415 |
- if ok {
|
|
| 416 |
- image = namedTagged.Name() + ":" + namedTagged.Tag() |
|
| 412 |
+ if ref, err := reference.ParseNormalizedNamed(image); err == nil {
|
|
| 413 |
+ // update image string for display, (strips any digest) |
|
| 414 |
+ if nt, ok := ref.(reference.NamedTagged); ok {
|
|
| 415 |
+ if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
|
|
| 416 |
+ image = reference.FamiliarString(namedTagged) |
|
| 417 |
+ } |
|
| 417 | 418 |
} |
| 418 | 419 |
} |
| 419 | 420 |
|
| ... | ... |
@@ -397,9 +397,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator |
| 397 | 397 |
if err != nil {
|
| 398 | 398 |
return nil, nil, err |
| 399 | 399 |
} |
| 400 |
- if reference.IsNameOnly(ref) {
|
|
| 401 |
- ref = reference.EnsureTagged(ref) |
|
| 402 |
- } |
|
| 400 |
+ ref = reference.TagNameOnly(ref) |
|
| 403 | 401 |
if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
|
| 404 | 402 |
trustedRef, err := translator(ctx, ref) |
| 405 | 403 |
if err != nil {
|
| ... | ... |
@@ -42,7 +42,6 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
|
| 45 |
- var distributionRef reference.Named |
|
| 46 | 45 |
distributionRef, err := reference.ParseNormalizedNamed(opts.remote) |
| 47 | 46 |
if err != nil {
|
| 48 | 47 |
return err |
| ... | ... |
@@ -52,9 +51,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
|
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 | 54 |
if !opts.all && reference.IsNameOnly(distributionRef) {
|
| 55 |
- taggedRef := reference.EnsureTagged(distributionRef) |
|
| 56 |
- fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag()) |
|
| 57 |
- distributionRef = taggedRef |
|
| 55 |
+ distributionRef = reference.TagNameOnly(distributionRef) |
|
| 56 |
+ if tagged, ok := distributionRef.(reference.Tagged); ok {
|
|
| 57 |
+ fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag()) |
|
| 58 |
+ } |
|
| 58 | 59 |
} |
| 59 | 60 |
|
| 60 | 61 |
// Resolve the Repository name from fqn to RepositoryInfo |
| ... | ... |
@@ -129,15 +129,15 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI |
| 129 | 129 |
|
| 130 | 130 |
// Initialize the notary repository with a remotely managed snapshot key |
| 131 | 131 |
if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
|
| 132 |
- return trust.NotaryError(repoInfo.FullName(), err) |
|
| 132 |
+ return trust.NotaryError(repoInfo.Name.Name(), err) |
|
| 133 | 133 |
} |
| 134 |
- fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.FullName()) |
|
| 134 |
+ fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.Name.Name()) |
|
| 135 | 135 |
err = repo.AddTarget(target, data.CanonicalTargetsRole) |
| 136 | 136 |
case nil: |
| 137 | 137 |
// already initialized and we have successfully downloaded the latest metadata |
| 138 | 138 |
err = addTargetToAllSignableRoles(repo, target) |
| 139 | 139 |
default: |
| 140 |
- return trust.NotaryError(repoInfo.FullName(), err) |
|
| 140 |
+ return trust.NotaryError(repoInfo.Name.Name(), err) |
|
| 141 | 141 |
} |
| 142 | 142 |
|
| 143 | 143 |
if err == nil {
|
| ... | ... |
@@ -145,11 +145,11 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI |
| 145 | 145 |
} |
| 146 | 146 |
|
| 147 | 147 |
if err != nil {
|
| 148 |
- fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error()) |
|
| 149 |
- return trust.NotaryError(repoInfo.FullName(), err) |
|
| 148 |
+ fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error()) |
|
| 149 |
+ return trust.NotaryError(repoInfo.Name.Name(), err) |
|
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
- fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.FullName(), tag) |
|
| 152 |
+ fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.Name.Name(), tag) |
|
| 153 | 153 |
return nil |
| 154 | 154 |
} |
| 155 | 155 |
|
| ... | ... |
@@ -342,12 +342,12 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference |
| 342 | 342 |
|
| 343 | 343 |
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) |
| 344 | 344 |
if err != nil {
|
| 345 |
- return nil, trust.NotaryError(repoInfo.FullName(), err) |
|
| 345 |
+ return nil, trust.NotaryError(repoInfo.Name.Name(), err) |
|
| 346 | 346 |
} |
| 347 | 347 |
// Only list tags in the top level targets role or the releases delegation role - ignore |
| 348 | 348 |
// all other delegation roles |
| 349 | 349 |
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
|
| 350 |
- return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
|
|
| 350 |
+ return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", ref.Tag()))
|
|
| 351 | 351 |
} |
| 352 | 352 |
r, err := convertTarget(t.Target) |
| 353 | 353 |
if err != nil {
|
| ... | ... |
@@ -7,7 +7,6 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/distribution/reference" |
| 9 | 9 |
"github.com/docker/docker/api/types" |
| 10 |
- registrytypes "github.com/docker/docker/api/types/registry" |
|
| 11 | 10 |
"github.com/docker/docker/cli" |
| 12 | 11 |
"github.com/docker/docker/cli/command" |
| 13 | 12 |
"github.com/docker/docker/cli/command/image" |
| ... | ... |
@@ -54,20 +53,6 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 54 | 54 |
return cmd |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
-func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
|
|
| 58 |
- named, err := reference.ParseNormalizedNamed(ref.Name()) |
|
| 59 |
- if err != nil {
|
|
| 60 |
- return nil, err |
|
| 61 |
- } |
|
| 62 |
- |
|
| 63 |
- repoInfo, err := registry.ParseRepositoryInfo(named) |
|
| 64 |
- if err != nil {
|
|
| 65 |
- return nil, err |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- return repoInfo.Index, nil |
|
| 69 |
-} |
|
| 70 |
- |
|
| 71 | 57 |
type pluginRegistryService struct {
|
| 72 | 58 |
registry.Service |
| 73 | 59 |
} |
| ... | ... |
@@ -104,9 +89,10 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu |
| 104 | 104 |
|
| 105 | 105 |
_, isCanonical := ref.(reference.Canonical) |
| 106 | 106 |
if command.IsTrusted() && !isCanonical {
|
| 107 |
+ ref = reference.TagNameOnly(ref) |
|
| 107 | 108 |
nt, ok := ref.(reference.NamedTagged) |
| 108 | 109 |
if !ok {
|
| 109 |
- nt = reference.EnsureTagged(ref) |
|
| 110 |
+ return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
|
|
| 110 | 111 |
} |
| 111 | 112 |
|
| 112 | 113 |
ctx := context.Background() |
| ... | ... |
@@ -148,7 +134,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
|
| 148 | 148 |
if _, ok := aref.(reference.Canonical); ok {
|
| 149 | 149 |
return fmt.Errorf("invalid name: %s", opts.localName)
|
| 150 | 150 |
} |
| 151 |
- localName = reference.FamiliarString(reference.EnsureTagged(aref)) |
|
| 151 |
+ localName = reference.FamiliarString(reference.TagNameOnly(aref)) |
|
| 152 | 152 |
} |
| 153 | 153 |
|
| 154 | 154 |
ctx := context.Background() |
| ... | ... |
@@ -40,10 +40,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
|
| 40 | 40 |
return fmt.Errorf("invalid name: %s", name)
|
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
- taggedRef, ok := named.(reference.NamedTagged) |
|
| 44 |
- if !ok {
|
|
| 45 |
- taggedRef = reference.EnsureTagged(named) |
|
| 46 |
- } |
|
| 43 |
+ named = reference.TagNameOnly(named) |
|
| 47 | 44 |
|
| 48 | 45 |
ctx := context.Background() |
| 49 | 46 |
|
| ... | ... |
@@ -58,7 +55,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
|
| 58 | 58 |
return err |
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
- responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth) |
|
| 61 |
+ responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(named), encodedAuth) |
|
| 62 | 62 |
if err != nil {
|
| 63 | 63 |
return err |
| 64 | 64 |
} |
| ... | ... |
@@ -5,10 +5,10 @@ import ( |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"strings" |
| 7 | 7 |
|
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/docker/cli" |
| 9 | 10 |
"github.com/docker/docker/cli/command" |
| 10 | 11 |
"github.com/docker/docker/pkg/jsonmessage" |
| 11 |
- "github.com/docker/docker/reference" |
|
| 12 | 12 |
"github.com/pkg/errors" |
| 13 | 13 |
"github.com/spf13/cobra" |
| 14 | 14 |
) |
| ... | ... |
@@ -49,19 +49,19 @@ func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
|
| 49 | 49 |
if opts.remote == "" {
|
| 50 | 50 |
opts.remote = p.PluginReference |
| 51 | 51 |
} |
| 52 |
- remote, err := reference.ParseNamed(opts.remote) |
|
| 52 |
+ remote, err := reference.ParseNormalizedNamed(opts.remote) |
|
| 53 | 53 |
if err != nil {
|
| 54 | 54 |
return errors.Wrap(err, "error parsing remote upgrade image reference") |
| 55 | 55 |
} |
| 56 |
- remote = reference.WithDefaultTag(remote) |
|
| 56 |
+ remote = reference.TagNameOnly(remote) |
|
| 57 | 57 |
|
| 58 |
- old, err := reference.ParseNamed(p.PluginReference) |
|
| 58 |
+ old, err := reference.ParseNormalizedNamed(p.PluginReference) |
|
| 59 | 59 |
if err != nil {
|
| 60 | 60 |
return errors.Wrap(err, "error parsing current image reference") |
| 61 | 61 |
} |
| 62 |
- old = reference.WithDefaultTag(old) |
|
| 62 |
+ old = reference.TagNameOnly(old) |
|
| 63 | 63 |
|
| 64 |
- fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, old, remote) |
|
| 64 |
+ fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote)) |
|
| 65 | 65 |
if !opts.skipRemoteCheck && remote.String() != old.String() {
|
| 66 | 66 |
if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
|
| 67 | 67 |
return errors.New("canceling upgrade request")
|
| ... | ... |
@@ -33,10 +33,12 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv |
| 33 | 33 |
namedRef, ok := ref.(reference.Named) |
| 34 | 34 |
if !ok {
|
| 35 | 35 |
return errors.New("failed to resolve image digest using content trust: reference is not named")
|
| 36 |
- |
|
| 37 | 36 |
} |
| 38 |
- |
|
| 39 |
- taggedRef := reference.EnsureTagged(namedRef) |
|
| 37 |
+ namedRef = reference.TagNameOnly(namedRef) |
|
| 38 |
+ taggedRef, ok := namedRef.(reference.NamedTagged) |
|
| 39 |
+ if !ok {
|
|
| 40 |
+ return errors.New("failed to resolve image digest using content trust: reference is not tagged")
|
|
| 41 |
+ } |
|
| 40 | 42 |
|
| 41 | 43 |
resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef) |
| 42 | 44 |
if err != nil {
|
| ... | ... |
@@ -65,12 +67,12 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer |
| 65 | 65 |
|
| 66 | 66 |
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) |
| 67 | 67 |
if err != nil {
|
| 68 |
- return nil, trust.NotaryError(repoInfo.FullName(), err) |
|
| 68 |
+ return nil, trust.NotaryError(repoInfo.Name.Name(), err) |
|
| 69 | 69 |
} |
| 70 | 70 |
// Only get the tag if it's in the top level targets role or the releases delegation role |
| 71 | 71 |
// ignore it if it's in any other delegation roles |
| 72 | 72 |
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
|
| 73 |
- return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
|
|
| 73 |
+ return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
|
|
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
logrus.Debugf("retrieving target for %s role\n", t.Role)
|
| ... | ... |
@@ -10,7 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"golang.org/x/net/context" |
| 12 | 12 |
|
| 13 |
- distreference "github.com/docker/distribution/reference" |
|
| 13 |
+ "github.com/docker/distribution/reference" |
|
| 14 | 14 |
"github.com/docker/docker/api/types/swarm" |
| 15 | 15 |
"github.com/docker/docker/cli/command" |
| 16 | 16 |
"github.com/docker/docker/cli/command/idresolver" |
| ... | ... |
@@ -129,13 +129,15 @@ func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idr |
| 129 | 129 |
|
| 130 | 130 |
image := task.Spec.ContainerSpec.Image |
| 131 | 131 |
if !noTrunc {
|
| 132 |
- ref, err := distreference.ParseNamed(image) |
|
| 132 |
+ ref, err := reference.ParseNormalizedNamed(image) |
|
| 133 | 133 |
if err == nil {
|
| 134 |
- // update image string for display |
|
| 135 |
- namedTagged, ok := ref.(distreference.NamedTagged) |
|
| 136 |
- if ok {
|
|
| 137 |
- image = namedTagged.Name() + ":" + namedTagged.Tag() |
|
| 134 |
+ // update image string for display, (strips any digest) |
|
| 135 |
+ if nt, ok := ref.(reference.NamedTagged); ok {
|
|
| 136 |
+ if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
|
|
| 137 |
+ image = reference.FamiliarString(namedTagged) |
|
| 138 |
+ } |
|
| 138 | 139 |
} |
| 140 |
+ |
|
| 139 | 141 |
} |
| 140 | 142 |
} |
| 141 | 143 |
|
| ... | ... |
@@ -148,7 +148,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI |
| 148 | 148 |
} |
| 149 | 149 |
|
| 150 | 150 |
scope := auth.RepositoryScope{
|
| 151 |
- Repository: repoInfo.FullName(), |
|
| 151 |
+ Repository: repoInfo.Name.Name(), |
|
| 152 | 152 |
Actions: actions, |
| 153 | 153 |
Class: repoInfo.Class, |
| 154 | 154 |
} |
| ... | ... |
@@ -166,7 +166,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI |
| 166 | 166 |
|
| 167 | 167 |
return client.NewNotaryRepository( |
| 168 | 168 |
trustDirectory(), |
| 169 |
- repoInfo.FullName(), |
|
| 169 |
+ repoInfo.Name.Name(), |
|
| 170 | 170 |
server, |
| 171 | 171 |
tr, |
| 172 | 172 |
getPassphraseRetriever(streams), |
| ... | ... |
@@ -5,9 +5,8 @@ import ( |
| 5 | 5 |
"errors" |
| 6 | 6 |
"net/url" |
| 7 | 7 |
|
| 8 |
- distreference "github.com/docker/distribution/reference" |
|
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 9 | 9 |
"github.com/docker/docker/api/types" |
| 10 |
- "github.com/docker/docker/api/types/reference" |
|
| 11 | 10 |
"golang.org/x/net/context" |
| 12 | 11 |
) |
| 13 | 12 |
|
| ... | ... |
@@ -15,17 +14,20 @@ import ( |
| 15 | 15 |
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
|
| 16 | 16 |
var repository, tag string |
| 17 | 17 |
if options.Reference != "" {
|
| 18 |
- distributionRef, err := distreference.ParseNamed(options.Reference) |
|
| 18 |
+ ref, err := reference.ParseNormalizedNamed(options.Reference) |
|
| 19 | 19 |
if err != nil {
|
| 20 | 20 |
return types.IDResponse{}, err
|
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
|
|
| 23 |
+ if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 24 | 24 |
return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference")
|
| 25 | 25 |
} |
| 26 |
+ ref = reference.TagNameOnly(ref) |
|
| 26 | 27 |
|
| 27 |
- tag = reference.GetTagFromNamedRef(distributionRef) |
|
| 28 |
- repository = distributionRef.Name() |
|
| 28 |
+ if tagged, ok := ref.(reference.Tagged); ok {
|
|
| 29 |
+ tag = tagged.Tag() |
|
| 30 |
+ } |
|
| 31 |
+ repository = reference.FamiliarName(ref) |
|
| 29 | 32 |
} |
| 30 | 33 |
|
| 31 | 34 |
query := url.Values{}
|
| ... | ... |
@@ -6,21 +6,21 @@ import ( |
| 6 | 6 |
|
| 7 | 7 |
"golang.org/x/net/context" |
| 8 | 8 |
|
| 9 |
+ "github.com/docker/distribution/reference" |
|
| 9 | 10 |
"github.com/docker/docker/api/types" |
| 10 |
- "github.com/docker/docker/api/types/reference" |
|
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
// ImageCreate creates a new image based in the parent options. |
| 14 | 14 |
// It returns the JSON content in the response body. |
| 15 | 15 |
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
|
| 16 |
- repository, tag, err := reference.Parse(parentReference) |
|
| 16 |
+ ref, err := reference.ParseNormalizedNamed(parentReference) |
|
| 17 | 17 |
if err != nil {
|
| 18 | 18 |
return nil, err |
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 | 21 |
query := url.Values{}
|
| 22 |
- query.Set("fromImage", repository)
|
|
| 23 |
- query.Set("tag", tag)
|
|
| 22 |
+ query.Set("fromImage", reference.FamiliarName(ref))
|
|
| 23 |
+ query.Set("tag", getAPITagFromNamedRef(ref))
|
|
| 24 | 24 |
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) |
| 25 | 25 |
if err != nil {
|
| 26 | 26 |
return nil, err |
| ... | ... |
@@ -15,7 +15,7 @@ import ( |
| 15 | 15 |
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
|
| 16 | 16 |
if ref != "" {
|
| 17 | 17 |
//Check if the given image name can be resolved |
| 18 |
- if _, err := reference.ParseNamed(ref); err != nil {
|
|
| 18 |
+ if _, err := reference.ParseNormalizedNamed(ref); err != nil {
|
|
| 19 | 19 |
return nil, err |
| 20 | 20 |
} |
| 21 | 21 |
} |
| ... | ... |
@@ -7,8 +7,8 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"golang.org/x/net/context" |
| 9 | 9 |
|
| 10 |
+ "github.com/docker/distribution/reference" |
|
| 10 | 11 |
"github.com/docker/docker/api/types" |
| 11 |
- "github.com/docker/docker/api/types/reference" |
|
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
// ImagePull requests the docker host to pull an image from a remote registry. |
| ... | ... |
@@ -19,16 +19,16 @@ import ( |
| 19 | 19 |
// FIXME(vdemeester): there is currently used in a few way in docker/docker |
| 20 | 20 |
// - if not in trusted content, ref is used to pass the whole reference, and tag is empty |
| 21 | 21 |
// - if in trusted content, ref is used to pass the reference name, and tag for the digest |
| 22 |
-func (cli *Client) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
|
|
| 23 |
- repository, tag, err := reference.Parse(ref) |
|
| 22 |
+func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) {
|
|
| 23 |
+ ref, err := reference.ParseNormalizedNamed(refStr) |
|
| 24 | 24 |
if err != nil {
|
| 25 | 25 |
return nil, err |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 | 28 |
query := url.Values{}
|
| 29 |
- query.Set("fromImage", repository)
|
|
| 30 |
- if tag != "" && !options.All {
|
|
| 31 |
- query.Set("tag", tag)
|
|
| 29 |
+ query.Set("fromImage", reference.FamiliarName(ref))
|
|
| 30 |
+ if !options.All {
|
|
| 31 |
+ query.Set("tag", getAPITagFromNamedRef(ref))
|
|
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 | 34 |
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) |
| ... | ... |
@@ -44,3 +44,18 @@ func (cli *Client) ImagePull(ctx context.Context, ref string, options types.Imag |
| 44 | 44 |
} |
| 45 | 45 |
return resp.body, nil |
| 46 | 46 |
} |
| 47 |
+ |
|
| 48 |
+// getAPITagFromNamedRef returns a tag from the specified reference. |
|
| 49 |
+// This function is necessary as long as the docker "server" api expects |
|
| 50 |
+// digests to be sent as tags and makes a distinction between the name |
|
| 51 |
+// and tag/digest part of a reference. |
|
| 52 |
+func getAPITagFromNamedRef(ref reference.Named) string {
|
|
| 53 |
+ if digested, ok := ref.(reference.Digested); ok {
|
|
| 54 |
+ return digested.Digest().String() |
|
| 55 |
+ } |
|
| 56 |
+ ref = reference.TagNameOnly(ref) |
|
| 57 |
+ if tagged, ok := ref.(reference.Tagged); ok {
|
|
| 58 |
+ return tagged.Tag() |
|
| 59 |
+ } |
|
| 60 |
+ return "" |
|
| 61 |
+} |
| ... | ... |
@@ -21,7 +21,7 @@ func TestImagePullReferenceParseError(t *testing.T) {
|
| 21 | 21 |
} |
| 22 | 22 |
// An empty reference is an invalid reference |
| 23 | 23 |
_, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
|
| 24 |
- if err == nil || err.Error() != "repository name must have at least one component" {
|
|
| 24 |
+ if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
|
|
| 25 | 25 |
t.Fatalf("expected an error, got %v", err)
|
| 26 | 26 |
} |
| 27 | 27 |
} |
| ... | ... |
@@ -8,7 +8,7 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
"golang.org/x/net/context" |
| 10 | 10 |
|
| 11 |
- distreference "github.com/docker/distribution/reference" |
|
| 11 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 12 |
"github.com/docker/docker/api/types" |
| 13 | 13 |
) |
| 14 | 14 |
|
| ... | ... |
@@ -16,31 +16,33 @@ import ( |
| 16 | 16 |
// It executes the privileged function if the operation is unauthorized |
| 17 | 17 |
// and it tries one more time. |
| 18 | 18 |
// It's up to the caller to handle the io.ReadCloser and close it properly. |
| 19 |
-func (cli *Client) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
|
|
| 20 |
- distributionRef, err := distreference.ParseNamed(ref) |
|
| 19 |
+func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) {
|
|
| 20 |
+ ref, err := reference.ParseNormalizedNamed(image) |
|
| 21 | 21 |
if err != nil {
|
| 22 | 22 |
return nil, err |
| 23 | 23 |
} |
| 24 | 24 |
|
| 25 |
- if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
|
|
| 25 |
+ if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 26 | 26 |
return nil, errors.New("cannot push a digest reference")
|
| 27 | 27 |
} |
| 28 | 28 |
|
| 29 |
- var tag = "" |
|
| 30 |
- if nameTaggedRef, isNamedTagged := distributionRef.(distreference.NamedTagged); isNamedTagged {
|
|
| 29 |
+ tag := "" |
|
| 30 |
+ name := reference.FamiliarName(ref) |
|
| 31 |
+ |
|
| 32 |
+ if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
|
|
| 31 | 33 |
tag = nameTaggedRef.Tag() |
| 32 | 34 |
} |
| 33 | 35 |
|
| 34 | 36 |
query := url.Values{}
|
| 35 | 37 |
query.Set("tag", tag)
|
| 36 | 38 |
|
| 37 |
- resp, err := cli.tryImagePush(ctx, distributionRef.Name(), query, options.RegistryAuth) |
|
| 39 |
+ resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth) |
|
| 38 | 40 |
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
|
| 39 | 41 |
newAuthHeader, privilegeErr := options.PrivilegeFunc() |
| 40 | 42 |
if privilegeErr != nil {
|
| 41 | 43 |
return nil, privilegeErr |
| 42 | 44 |
} |
| 43 |
- resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader) |
|
| 45 |
+ resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader) |
|
| 44 | 46 |
} |
| 45 | 47 |
if err != nil {
|
| 46 | 48 |
return nil, err |
| ... | ... |
@@ -21,7 +21,7 @@ func TestImagePushReferenceError(t *testing.T) {
|
| 21 | 21 |
} |
| 22 | 22 |
// An empty reference is an invalid reference |
| 23 | 23 |
_, err := client.ImagePush(context.Background(), "", types.ImagePushOptions{})
|
| 24 |
- if err == nil || err.Error() != "repository name must have at least one component" {
|
|
| 24 |
+ if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
|
|
| 25 | 25 |
t.Fatalf("expected an error, got %v", err)
|
| 26 | 26 |
} |
| 27 | 27 |
// An canonical reference cannot be pushed |
| ... | ... |
@@ -3,32 +3,33 @@ package client |
| 3 | 3 |
import ( |
| 4 | 4 |
"net/url" |
| 5 | 5 |
|
| 6 |
- distreference "github.com/docker/distribution/reference" |
|
| 7 |
- "github.com/docker/docker/api/types/reference" |
|
| 6 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 7 |
"github.com/pkg/errors" |
| 9 | 8 |
"golang.org/x/net/context" |
| 10 | 9 |
) |
| 11 | 10 |
|
| 12 | 11 |
// ImageTag tags an image in the docker host |
| 13 | 12 |
func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
|
| 14 |
- if _, err := distreference.ParseNamed(source); err != nil {
|
|
| 13 |
+ if _, err := reference.ParseNormalizedNamed(source); err != nil {
|
|
| 15 | 14 |
return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source) |
| 16 | 15 |
} |
| 17 | 16 |
|
| 18 |
- distributionRef, err := distreference.ParseNamed(target) |
|
| 17 |
+ ref, err := reference.ParseNormalizedNamed(target) |
|
| 19 | 18 |
if err != nil {
|
| 20 | 19 |
return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target) |
| 21 | 20 |
} |
| 22 | 21 |
|
| 23 |
- if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
|
|
| 22 |
+ if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 24 | 23 |
return errors.New("refusing to create a tag with a digest reference")
|
| 25 | 24 |
} |
| 26 | 25 |
|
| 27 |
- tag := reference.GetTagFromNamedRef(distributionRef) |
|
| 26 |
+ ref = reference.TagNameOnly(ref) |
|
| 28 | 27 |
|
| 29 | 28 |
query := url.Values{}
|
| 30 |
- query.Set("repo", distributionRef.Name())
|
|
| 31 |
- query.Set("tag", tag)
|
|
| 29 |
+ query.Set("repo", reference.FamiliarName(ref))
|
|
| 30 |
+ if tagged, ok := ref.(reference.Tagged); ok {
|
|
| 31 |
+ query.Set("tag", tagged.Tag())
|
|
| 32 |
+ } |
|
| 32 | 33 |
|
| 33 | 34 |
resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil) |
| 34 | 35 |
ensureReaderClosed(resp) |
| ... | ... |
@@ -15,7 +15,7 @@ import ( |
| 15 | 15 |
// PluginInstall installs a plugin |
| 16 | 16 |
func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
|
| 17 | 17 |
query := url.Values{}
|
| 18 |
- if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
|
|
| 18 |
+ if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
|
|
| 19 | 19 |
return nil, errors.Wrap(err, "invalid remote reference") |
| 20 | 20 |
} |
| 21 | 21 |
query.Set("remote", options.RemoteRef)
|
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
// PluginUpgrade upgrades a plugin |
| 15 | 15 |
func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
|
| 16 | 16 |
query := url.Values{}
|
| 17 |
- if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
|
|
| 17 |
+ if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
|
|
| 18 | 18 |
return nil, errors.Wrap(err, "invalid remote reference") |
| 19 | 19 |
} |
| 20 | 20 |
query.Set("remote", options.RemoteRef)
|
| ... | ... |
@@ -52,7 +52,7 @@ import ( |
| 52 | 52 |
"time" |
| 53 | 53 |
|
| 54 | 54 |
"github.com/Sirupsen/logrus" |
| 55 |
- distreference "github.com/docker/distribution/reference" |
|
| 55 |
+ "github.com/docker/distribution/reference" |
|
| 56 | 56 |
apierrors "github.com/docker/docker/api/errors" |
| 57 | 57 |
apitypes "github.com/docker/docker/api/types" |
| 58 | 58 |
"github.com/docker/docker/api/types/backend" |
| ... | ... |
@@ -66,13 +66,11 @@ import ( |
| 66 | 66 |
"github.com/docker/docker/pkg/ioutils" |
| 67 | 67 |
"github.com/docker/docker/pkg/signal" |
| 68 | 68 |
"github.com/docker/docker/pkg/stdcopy" |
| 69 |
- "github.com/docker/docker/reference" |
|
| 70 | 69 |
"github.com/docker/docker/runconfig" |
| 71 | 70 |
swarmapi "github.com/docker/swarmkit/api" |
| 72 | 71 |
"github.com/docker/swarmkit/manager/encryption" |
| 73 | 72 |
swarmnode "github.com/docker/swarmkit/node" |
| 74 | 73 |
gogotypes "github.com/gogo/protobuf/types" |
| 75 |
- "github.com/opencontainers/go-digest" |
|
| 76 | 74 |
"github.com/pkg/errors" |
| 77 | 75 |
"golang.org/x/net/context" |
| 78 | 76 |
) |
| ... | ... |
@@ -829,50 +827,46 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv |
| 829 | 829 |
} |
| 830 | 830 |
|
| 831 | 831 |
// imageWithDigestString takes an image such as name or name:tag |
| 832 |
-// and returns the image pinned to a digest, such as name@sha256:34234... |
|
| 833 |
-// Due to the difference between the docker/docker/reference, and the |
|
| 834 |
-// docker/distribution/reference packages, we're parsing the image twice. |
|
| 835 |
-// As the two packages converge, this function should be simplified. |
|
| 836 |
-// TODO(nishanttotla): After the packages converge, the function must |
|
| 837 |
-// convert distreference.Named -> distreference.Canonical, and the logic simplified. |
|
| 832 |
+// and returns the image pinned to a digest, such as name@sha256:34234 |
|
| 838 | 833 |
func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
|
| 839 |
- if _, err := digest.Parse(image); err == nil {
|
|
| 840 |
- return "", errors.New("image reference is an image ID")
|
|
| 841 |
- } |
|
| 842 |
- ref, err := distreference.ParseNamed(image) |
|
| 834 |
+ ref, err := reference.ParseAnyReference(image) |
|
| 843 | 835 |
if err != nil {
|
| 844 | 836 |
return "", err |
| 845 | 837 |
} |
| 846 |
- // only query registry if not a canonical reference (i.e. with digest) |
|
| 847 |
- if _, ok := ref.(distreference.Canonical); !ok {
|
|
| 848 |
- // create a docker/docker/reference Named object because GetRepository needs it |
|
| 849 |
- dockerRef, err := reference.ParseNamed(image) |
|
| 850 |
- if err != nil {
|
|
| 851 |
- return "", err |
|
| 838 |
+ namedRef, ok := ref.(reference.Named) |
|
| 839 |
+ if !ok {
|
|
| 840 |
+ if _, ok := ref.(reference.Digested); ok {
|
|
| 841 |
+ return "", errors.New("image reference is an image ID")
|
|
| 852 | 842 |
} |
| 853 |
- dockerRef = reference.WithDefaultTag(dockerRef) |
|
| 854 |
- namedTaggedRef, ok := dockerRef.(reference.NamedTagged) |
|
| 843 |
+ return "", errors.Errorf("unknown image reference format: %s", image)
|
|
| 844 |
+ } |
|
| 845 |
+ // only query registry if not a canonical reference (i.e. with digest) |
|
| 846 |
+ if _, ok := namedRef.(reference.Canonical); !ok {
|
|
| 847 |
+ namedRef = reference.TagNameOnly(namedRef) |
|
| 848 |
+ |
|
| 849 |
+ taggedRef, ok := namedRef.(reference.NamedTagged) |
|
| 855 | 850 |
if !ok {
|
| 856 |
- return "", errors.New("unable to cast image to NamedTagged reference object")
|
|
| 851 |
+ return "", errors.Errorf("image reference not tagged: %s", image)
|
|
| 857 | 852 |
} |
| 858 | 853 |
|
| 859 |
- repo, _, err := c.config.Backend.GetRepository(ctx, namedTaggedRef, authConfig) |
|
| 854 |
+ repo, _, err := c.config.Backend.GetRepository(ctx, taggedRef, authConfig) |
|
| 860 | 855 |
if err != nil {
|
| 861 | 856 |
return "", err |
| 862 | 857 |
} |
| 863 |
- dscrptr, err := repo.Tags(ctx).Get(ctx, namedTaggedRef.Tag()) |
|
| 858 |
+ dscrptr, err := repo.Tags(ctx).Get(ctx, taggedRef.Tag()) |
|
| 864 | 859 |
if err != nil {
|
| 865 | 860 |
return "", err |
| 866 | 861 |
} |
| 867 | 862 |
|
| 868 |
- namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest) |
|
| 863 |
+ namedDigestedRef, err := reference.WithDigest(taggedRef, dscrptr.Digest) |
|
| 869 | 864 |
if err != nil {
|
| 870 | 865 |
return "", err |
| 871 | 866 |
} |
| 872 |
- return namedDigestedRef.String(), nil |
|
| 867 |
+ // return familiar form until interface updated to return type |
|
| 868 |
+ return reference.FamiliarString(namedDigestedRef), nil |
|
| 873 | 869 |
} |
| 874 | 870 |
// reference already contains a digest, so just return it |
| 875 |
- return ref.String(), nil |
|
| 871 |
+ return reference.FamiliarString(ref), nil |
|
| 876 | 872 |
} |
| 877 | 873 |
|
| 878 | 874 |
// CreateService creates a new service in a managed swarm cluster. |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"time" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/docker/distribution" |
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/docker/api/types" |
| 9 | 10 |
"github.com/docker/docker/api/types/backend" |
| 10 | 11 |
"github.com/docker/docker/api/types/container" |
| ... | ... |
@@ -14,7 +15,6 @@ import ( |
| 14 | 14 |
swarmtypes "github.com/docker/docker/api/types/swarm" |
| 15 | 15 |
clustertypes "github.com/docker/docker/daemon/cluster/provider" |
| 16 | 16 |
"github.com/docker/docker/plugin" |
| 17 |
- "github.com/docker/docker/reference" |
|
| 18 | 17 |
"github.com/docker/libnetwork" |
| 19 | 18 |
"github.com/docker/libnetwork/cluster" |
| 20 | 19 |
networktypes "github.com/docker/libnetwork/types" |
| ... | ... |
@@ -12,13 +12,13 @@ import ( |
| 12 | 12 |
"time" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/Sirupsen/logrus" |
| 15 |
+ "github.com/docker/distribution/reference" |
|
| 15 | 16 |
"github.com/docker/docker/api/types" |
| 16 | 17 |
"github.com/docker/docker/api/types/backend" |
| 17 | 18 |
containertypes "github.com/docker/docker/api/types/container" |
| 18 | 19 |
"github.com/docker/docker/api/types/events" |
| 19 | 20 |
"github.com/docker/docker/daemon/cluster/convert" |
| 20 | 21 |
executorpkg "github.com/docker/docker/daemon/cluster/executor" |
| 21 |
- "github.com/docker/docker/reference" |
|
| 22 | 22 |
"github.com/docker/libnetwork" |
| 23 | 23 |
"github.com/docker/swarmkit/agent/exec" |
| 24 | 24 |
"github.com/docker/swarmkit/api" |
| ... | ... |
@@ -61,7 +61,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
|
| 61 | 61 |
|
| 62 | 62 |
// Skip pulling if the image is referenced by digest and already |
| 63 | 63 |
// exists locally. |
| 64 |
- named, err := reference.ParseNamed(spec.Image) |
|
| 64 |
+ named, err := reference.ParseNormalizedNamed(spec.Image) |
|
| 65 | 65 |
if err == nil {
|
| 66 | 66 |
if _, ok := named.(reference.Canonical); ok {
|
| 67 | 67 |
_, err := c.backend.LookupImage(spec.Image) |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"github.com/Sirupsen/logrus" |
| 12 | 12 |
|
| 13 |
+ "github.com/docker/distribution/reference" |
|
| 13 | 14 |
"github.com/docker/docker/api/types" |
| 14 | 15 |
enginecontainer "github.com/docker/docker/api/types/container" |
| 15 | 16 |
"github.com/docker/docker/api/types/events" |
| ... | ... |
@@ -18,7 +19,6 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/api/types/network" |
| 19 | 19 |
volumetypes "github.com/docker/docker/api/types/volume" |
| 20 | 20 |
clustertypes "github.com/docker/docker/daemon/cluster/provider" |
| 21 |
- "github.com/docker/docker/reference" |
|
| 22 | 21 |
"github.com/docker/go-connections/nat" |
| 23 | 22 |
"github.com/docker/swarmkit/agent/exec" |
| 24 | 23 |
"github.com/docker/swarmkit/api" |
| ... | ... |
@@ -132,11 +132,11 @@ func (c *containerConfig) name() string {
|
| 132 | 132 |
|
| 133 | 133 |
func (c *containerConfig) image() string {
|
| 134 | 134 |
raw := c.spec().Image |
| 135 |
- ref, err := reference.ParseNamed(raw) |
|
| 135 |
+ ref, err := reference.ParseNormalizedNamed(raw) |
|
| 136 | 136 |
if err != nil {
|
| 137 | 137 |
return raw |
| 138 | 138 |
} |
| 139 |
- return reference.WithDefaultTag(ref).String() |
|
| 139 |
+ return reference.FamiliarString(reference.TagNameOnly(ref)) |
|
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 | 142 |
func (c *containerConfig) portBindings() nat.PortMap {
|
| ... | ... |
@@ -2,12 +2,12 @@ package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
- "fmt" |
|
| 6 | 5 |
"io" |
| 7 | 6 |
"runtime" |
| 8 | 7 |
"strings" |
| 9 | 8 |
"time" |
| 10 | 9 |
|
| 10 |
+ "github.com/docker/distribution/reference" |
|
| 11 | 11 |
"github.com/docker/docker/api/types/backend" |
| 12 | 12 |
containertypes "github.com/docker/docker/api/types/container" |
| 13 | 13 |
"github.com/docker/docker/builder/dockerfile" |
| ... | ... |
@@ -16,7 +16,7 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/image" |
| 17 | 17 |
"github.com/docker/docker/layer" |
| 18 | 18 |
"github.com/docker/docker/pkg/ioutils" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 19 |
+ "github.com/pkg/errors" |
|
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 | 22 |
// merge merges two Config, the image container configuration (defaults values), |
| ... | ... |
@@ -128,7 +128,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str |
| 128 | 128 |
|
| 129 | 129 |
// It is not possible to commit a running container on Windows and on Solaris. |
| 130 | 130 |
if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() {
|
| 131 |
- return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS)
|
|
| 131 |
+ return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
|
|
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 | 134 |
if c.Pause && !container.IsPaused() {
|
| ... | ... |
@@ -228,10 +228,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str |
| 228 | 228 |
|
| 229 | 229 |
imageRef := "" |
| 230 | 230 |
if c.Repo != "" {
|
| 231 |
- newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer |
|
| 231 |
+ newTag, err := reference.ParseNormalizedNamed(c.Repo) // todo: should move this to API layer |
|
| 232 | 232 |
if err != nil {
|
| 233 | 233 |
return "", err |
| 234 | 234 |
} |
| 235 |
+ if !reference.IsNameOnly(newTag) {
|
|
| 236 |
+ return "", errors.Errorf("unexpected repository name: %s", c.Repo)
|
|
| 237 |
+ } |
|
| 235 | 238 |
if c.Tag != "" {
|
| 236 | 239 |
if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
|
| 237 | 240 |
return "", err |
| ... | ... |
@@ -240,7 +243,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str |
| 240 | 240 |
if err := daemon.TagImageWithReference(id, newTag); err != nil {
|
| 241 | 241 |
return "", err |
| 242 | 242 |
} |
| 243 |
- imageRef = newTag.String() |
|
| 243 |
+ imageRef = reference.FamiliarString(newTag) |
|
| 244 | 244 |
} |
| 245 | 245 |
|
| 246 | 246 |
attributes := map[string]string{
|
| ... | ... |
@@ -46,7 +46,7 @@ import ( |
| 46 | 46 |
"github.com/docker/docker/pkg/system" |
| 47 | 47 |
"github.com/docker/docker/pkg/truncindex" |
| 48 | 48 |
"github.com/docker/docker/plugin" |
| 49 |
- "github.com/docker/docker/reference" |
|
| 49 |
+ refstore "github.com/docker/docker/reference" |
|
| 50 | 50 |
"github.com/docker/docker/registry" |
| 51 | 51 |
"github.com/docker/docker/runconfig" |
| 52 | 52 |
volumedrivers "github.com/docker/docker/volume/drivers" |
| ... | ... |
@@ -76,7 +76,7 @@ type Daemon struct {
|
| 76 | 76 |
repository string |
| 77 | 77 |
containers container.Store |
| 78 | 78 |
execCommands *exec.Store |
| 79 |
- referenceStore reference.Store |
|
| 79 |
+ referenceStore refstore.Store |
|
| 80 | 80 |
downloadManager *xfer.LayerDownloadManager |
| 81 | 81 |
uploadManager *xfer.LayerUploadManager |
| 82 | 82 |
distributionMetadataStore dmetadata.Store |
| ... | ... |
@@ -637,7 +637,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot |
| 637 | 637 |
|
| 638 | 638 |
eventsService := events.New() |
| 639 | 639 |
|
| 640 |
- referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json")) |
|
| 640 |
+ referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json")) |
|
| 641 | 641 |
if err != nil {
|
| 642 | 642 |
return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
|
| 643 | 643 |
} |
| ... | ... |
@@ -16,7 +16,7 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/pkg/idtools" |
| 17 | 17 |
"github.com/docker/docker/pkg/parsers/kernel" |
| 18 | 18 |
"github.com/docker/docker/pkg/sysinfo" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 19 |
+ refstore "github.com/docker/docker/reference" |
|
| 20 | 20 |
"github.com/docker/libnetwork" |
| 21 | 21 |
nwconfig "github.com/docker/libnetwork/config" |
| 22 | 22 |
"github.com/docker/libnetwork/drivers/solaris/bridge" |
| ... | ... |
@@ -491,7 +491,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container |
| 491 | 491 |
return daemon.Unmount(container) |
| 492 | 492 |
} |
| 493 | 493 |
|
| 494 |
-func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
|
|
| 494 |
+func restoreCustomImage(is image.Store, ls layer.Store, rs refstore.Store) error {
|
|
| 495 | 495 |
// Solaris has no custom images to register |
| 496 | 496 |
return nil |
| 497 | 497 |
} |
| ... | ... |
@@ -2,29 +2,13 @@ package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "strings" |
|
| 6 | 5 |
|
| 7 | 6 |
"github.com/docker/docker/api/errors" |
| 8 |
- "github.com/docker/docker/reference" |
|
| 9 | 7 |
) |
| 10 | 8 |
|
| 11 | 9 |
func (d *Daemon) imageNotExistToErrcode(err error) error {
|
| 12 | 10 |
if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
|
| 13 |
- if strings.Contains(dne.RefOrID, "@") {
|
|
| 14 |
- e := fmt.Errorf("No such image: %s", dne.RefOrID)
|
|
| 15 |
- return errors.NewRequestNotFoundError(e) |
|
| 16 |
- } |
|
| 17 |
- tag := reference.DefaultTag |
|
| 18 |
- ref, err := reference.ParseNamed(dne.RefOrID) |
|
| 19 |
- if err != nil {
|
|
| 20 |
- e := fmt.Errorf("No such image: %s:%s", dne.RefOrID, tag)
|
|
| 21 |
- return errors.NewRequestNotFoundError(e) |
|
| 22 |
- } |
|
| 23 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 24 |
- tag = tagged.Tag() |
|
| 25 |
- } |
|
| 26 |
- e := fmt.Errorf("No such image: %s:%s", ref.Name(), tag)
|
|
| 27 |
- return errors.NewRequestNotFoundError(e) |
|
| 11 |
+ return errors.NewRequestNotFoundError(dne) |
|
| 28 | 12 |
} |
| 29 | 13 |
return err |
| 30 | 14 |
} |
| ... | ... |
@@ -1,9 +1,9 @@ |
| 1 | 1 |
package events |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "github.com/docker/distribution/reference" |
|
| 4 | 5 |
"github.com/docker/docker/api/types/events" |
| 5 | 6 |
"github.com/docker/docker/api/types/filters" |
| 6 |
- "github.com/docker/docker/reference" |
|
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 | 9 |
// Filter can filter out docker events from a stream |
| ... | ... |
@@ -102,9 +102,9 @@ func (ef *Filter) matchImage(ev events.Message) bool {
|
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 | 104 |
func stripTag(image string) string {
|
| 105 |
- ref, err := reference.ParseNamed(image) |
|
| 105 |
+ ref, err := reference.ParseNormalizedNamed(image) |
|
| 106 | 106 |
if err != nil {
|
| 107 | 107 |
return image |
| 108 | 108 |
} |
| 109 |
- return ref.Name() |
|
| 109 |
+ return reference.FamiliarName(ref) |
|
| 110 | 110 |
} |
| ... | ... |
@@ -3,45 +3,55 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
+ "github.com/docker/distribution/reference" |
|
| 6 | 7 |
"github.com/docker/docker/builder" |
| 7 | 8 |
"github.com/docker/docker/image" |
| 8 | 9 |
"github.com/docker/docker/pkg/stringid" |
| 9 |
- "github.com/docker/docker/reference" |
|
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 | 12 |
// ErrImageDoesNotExist is error returned when no image can be found for a reference. |
| 13 | 13 |
type ErrImageDoesNotExist struct {
|
| 14 |
- RefOrID string |
|
| 14 |
+ ref reference.Reference |
|
| 15 | 15 |
} |
| 16 | 16 |
|
| 17 | 17 |
func (e ErrImageDoesNotExist) Error() string {
|
| 18 |
- return fmt.Sprintf("no such id: %s", e.RefOrID)
|
|
| 18 |
+ ref := e.ref |
|
| 19 |
+ if named, ok := ref.(reference.Named); ok {
|
|
| 20 |
+ ref = reference.TagNameOnly(named) |
|
| 21 |
+ } |
|
| 22 |
+ return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
|
|
| 19 | 23 |
} |
| 20 | 24 |
|
| 21 | 25 |
// GetImageID returns an image ID corresponding to the image referred to by |
| 22 | 26 |
// refOrID. |
| 23 | 27 |
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
| 24 |
- id, ref, err := reference.ParseIDOrReference(refOrID) |
|
| 28 |
+ ref, err := reference.ParseAnyReference(refOrID) |
|
| 25 | 29 |
if err != nil {
|
| 26 | 30 |
return "", err |
| 27 | 31 |
} |
| 28 |
- if id != "" {
|
|
| 29 |
- if _, err := daemon.imageStore.Get(image.IDFromDigest(id)); err != nil {
|
|
| 30 |
- return "", ErrImageDoesNotExist{refOrID}
|
|
| 32 |
+ namedRef, ok := ref.(reference.Named) |
|
| 33 |
+ if !ok {
|
|
| 34 |
+ digested, ok := ref.(reference.Digested) |
|
| 35 |
+ if !ok {
|
|
| 36 |
+ return "", ErrImageDoesNotExist{ref}
|
|
| 31 | 37 |
} |
| 32 |
- return image.IDFromDigest(id), nil |
|
| 38 |
+ id := image.IDFromDigest(digested.Digest()) |
|
| 39 |
+ if _, err := daemon.imageStore.Get(id); err != nil {
|
|
| 40 |
+ return "", ErrImageDoesNotExist{ref}
|
|
| 41 |
+ } |
|
| 42 |
+ return id, nil |
|
| 33 | 43 |
} |
| 34 | 44 |
|
| 35 |
- if id, err := daemon.referenceStore.Get(ref); err == nil {
|
|
| 45 |
+ if id, err := daemon.referenceStore.Get(namedRef); err == nil {
|
|
| 36 | 46 |
return image.IDFromDigest(id), nil |
| 37 | 47 |
} |
| 38 | 48 |
|
| 39 | 49 |
// deprecated: repo:shortid https://github.com/docker/docker/pull/799 |
| 40 |
- if tagged, ok := ref.(reference.NamedTagged); ok {
|
|
| 50 |
+ if tagged, ok := namedRef.(reference.Tagged); ok {
|
|
| 41 | 51 |
if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
|
| 42 | 52 |
if id, err := daemon.imageStore.Search(tag); err == nil {
|
| 43 |
- for _, namedRef := range daemon.referenceStore.References(id.Digest()) {
|
|
| 44 |
- if namedRef.Name() == ref.Name() {
|
|
| 53 |
+ for _, storeRef := range daemon.referenceStore.References(id.Digest()) {
|
|
| 54 |
+ if storeRef.Name() == namedRef.Name() {
|
|
| 45 | 55 |
return id, nil |
| 46 | 56 |
} |
| 47 | 57 |
} |
| ... | ... |
@@ -54,7 +64,7 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
| 54 | 54 |
return id, nil |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
- return "", ErrImageDoesNotExist{refOrID}
|
|
| 57 |
+ return "", ErrImageDoesNotExist{ref}
|
|
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 | 60 |
// GetImage returns an image corresponding to the image referred to by refOrID. |
| ... | ... |
@@ -5,12 +5,12 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/docker/api/errors" |
| 9 | 10 |
"github.com/docker/docker/api/types" |
| 10 | 11 |
"github.com/docker/docker/container" |
| 11 | 12 |
"github.com/docker/docker/image" |
| 12 | 13 |
"github.com/docker/docker/pkg/stringid" |
| 13 |
- "github.com/docker/docker/reference" |
|
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 | 16 |
type conflictType int |
| ... | ... |
@@ -89,7 +89,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I |
| 89 | 89 |
} |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 |
- parsedRef, err := reference.ParseNamed(imageRef) |
|
| 92 |
+ parsedRef, err := reference.ParseNormalizedNamed(imageRef) |
|
| 93 | 93 |
if err != nil {
|
| 94 | 94 |
return nil, err |
| 95 | 95 |
} |
| ... | ... |
@@ -99,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I |
| 99 | 99 |
return nil, err |
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 |
- untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
|
|
| 102 |
+ untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
|
|
| 103 | 103 |
|
| 104 | 104 |
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") |
| 105 | 105 |
records = append(records, untaggedRecord) |
| ... | ... |
@@ -126,7 +126,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I |
| 126 | 126 |
return records, err |
| 127 | 127 |
} |
| 128 | 128 |
|
| 129 |
- untaggedRecord := types.ImageDeleteResponseItem{Untagged: repoRef.String()}
|
|
| 129 |
+ untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(repoRef)}
|
|
| 130 | 130 |
records = append(records, untaggedRecord) |
| 131 | 131 |
} else {
|
| 132 | 132 |
remainingRefs = append(remainingRefs, repoRef) |
| ... | ... |
@@ -162,7 +162,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I |
| 162 | 162 |
return nil, err |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
- untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
|
|
| 165 |
+ untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
|
|
| 166 | 166 |
|
| 167 | 167 |
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") |
| 168 | 168 |
records = append(records, untaggedRecord) |
| ... | ... |
@@ -232,7 +232,8 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai |
| 232 | 232 |
// optional tag or digest reference. If tag or digest is omitted, the default |
| 233 | 233 |
// tag is used. Returns the resolved image reference and an error. |
| 234 | 234 |
func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
|
| 235 |
- ref = reference.WithDefaultTag(ref) |
|
| 235 |
+ ref = reference.TagNameOnly(ref) |
|
| 236 |
+ |
|
| 236 | 237 |
// Ignore the boolean value returned, as far as we're concerned, this |
| 237 | 238 |
// is an idempotent operation and it's okay if the reference didn't |
| 238 | 239 |
// exist in the first place. |
| ... | ... |
@@ -255,7 +256,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty |
| 255 | 255 |
return err |
| 256 | 256 |
} |
| 257 | 257 |
|
| 258 |
- untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
|
|
| 258 |
+ untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
|
|
| 259 | 259 |
|
| 260 | 260 |
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") |
| 261 | 261 |
*records = append(*records, untaggedRecord) |
| ... | ... |
@@ -4,9 +4,9 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"time" |
| 6 | 6 |
|
| 7 |
+ "github.com/docker/distribution/reference" |
|
| 7 | 8 |
"github.com/docker/docker/api/types/image" |
| 8 | 9 |
"github.com/docker/docker/layer" |
| 9 |
- "github.com/docker/docker/reference" |
|
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 | 12 |
// ImageHistory returns a slice of ImageHistory structures for the specified image |
| ... | ... |
@@ -64,7 +64,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e |
| 64 | 64 |
var tags []string |
| 65 | 65 |
for _, r := range daemon.referenceStore.References(id.Digest()) {
|
| 66 | 66 |
if _, ok := r.(reference.NamedTagged); ok {
|
| 67 |
- tags = append(tags, r.String()) |
|
| 67 |
+ tags = append(tags, reference.FamiliarString(r)) |
|
| 68 | 68 |
} |
| 69 | 69 |
} |
| 70 | 70 |
|
| ... | ... |
@@ -3,9 +3,9 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"time" |
| 5 | 5 |
|
| 6 |
+ "github.com/docker/distribution/reference" |
|
| 6 | 7 |
"github.com/docker/docker/api/types" |
| 7 | 8 |
"github.com/docker/docker/layer" |
| 8 |
- "github.com/docker/docker/reference" |
|
| 9 | 9 |
"github.com/pkg/errors" |
| 10 | 10 |
) |
| 11 | 11 |
|
| ... | ... |
@@ -23,9 +23,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
|
| 23 | 23 |
for _, ref := range refs {
|
| 24 | 24 |
switch ref.(type) {
|
| 25 | 25 |
case reference.NamedTagged: |
| 26 |
- repoTags = append(repoTags, ref.String()) |
|
| 26 |
+ repoTags = append(repoTags, reference.FamiliarString(ref)) |
|
| 27 | 27 |
case reference.Canonical: |
| 28 |
- repoDigests = append(repoDigests, ref.String()) |
|
| 28 |
+ repoDigests = append(repoDigests, reference.FamiliarString(ref)) |
|
| 29 | 29 |
} |
| 30 | 30 |
} |
| 31 | 31 |
|
| ... | ... |
@@ -5,12 +5,12 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
|
| 7 | 7 |
dist "github.com/docker/distribution" |
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/docker/api/types" |
| 9 | 10 |
"github.com/docker/docker/builder" |
| 10 | 11 |
"github.com/docker/docker/distribution" |
| 11 | 12 |
progressutils "github.com/docker/docker/distribution/utils" |
| 12 | 13 |
"github.com/docker/docker/pkg/progress" |
| 13 |
- "github.com/docker/docker/reference" |
|
| 14 | 14 |
"github.com/docker/docker/registry" |
| 15 | 15 |
"github.com/opencontainers/go-digest" |
| 16 | 16 |
"golang.org/x/net/context" |
| ... | ... |
@@ -24,7 +24,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead |
| 24 | 24 |
// compatibility. |
| 25 | 25 |
image = strings.TrimSuffix(image, ":") |
| 26 | 26 |
|
| 27 |
- ref, err := reference.ParseNamed(image) |
|
| 27 |
+ ref, err := reference.ParseNormalizedNamed(image) |
|
| 28 | 28 |
if err != nil {
|
| 29 | 29 |
return err |
| 30 | 30 |
} |
| ... | ... |
@@ -48,11 +48,11 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead |
| 48 | 48 |
|
| 49 | 49 |
// PullOnBuild tells Docker to pull image referenced by `name`. |
| 50 | 50 |
func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
|
| 51 |
- ref, err := reference.ParseNamed(name) |
|
| 51 |
+ ref, err := reference.ParseNormalizedNamed(name) |
|
| 52 | 52 |
if err != nil {
|
| 53 | 53 |
return nil, err |
| 54 | 54 |
} |
| 55 |
- ref = reference.WithDefaultTag(ref) |
|
| 55 |
+ ref = reference.TagNameOnly(ref) |
|
| 56 | 56 |
|
| 57 | 57 |
pullRegistryAuth := &types.AuthConfig{}
|
| 58 | 58 |
if len(authConfigs) > 0 {
|
| ... | ... |
@@ -118,12 +118,12 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagg |
| 118 | 118 |
return nil, false, err |
| 119 | 119 |
} |
| 120 | 120 |
// makes sure name is not empty or `scratch` |
| 121 |
- if err := distribution.ValidateRepoName(repoInfo.Name()); err != nil {
|
|
| 121 |
+ if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
|
| 122 | 122 |
return nil, false, err |
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 | 125 |
// get endpoints |
| 126 |
- endpoints, err := daemon.RegistryService.LookupPullEndpoints(repoInfo.Hostname()) |
|
| 126 |
+ endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) |
|
| 127 | 127 |
if err != nil {
|
| 128 | 128 |
return nil, false, err |
| 129 | 129 |
} |
| ... | ... |
@@ -4,17 +4,17 @@ import ( |
| 4 | 4 |
"io" |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/distribution/manifest/schema2" |
| 7 |
+ "github.com/docker/distribution/reference" |
|
| 7 | 8 |
"github.com/docker/docker/api/types" |
| 8 | 9 |
"github.com/docker/docker/distribution" |
| 9 | 10 |
progressutils "github.com/docker/docker/distribution/utils" |
| 10 | 11 |
"github.com/docker/docker/pkg/progress" |
| 11 |
- "github.com/docker/docker/reference" |
|
| 12 | 12 |
"golang.org/x/net/context" |
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 | 15 |
// PushImage initiates a push operation on the repository named localName. |
| 16 | 16 |
func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
| 17 |
- ref, err := reference.ParseNamed(image) |
|
| 17 |
+ ref, err := reference.ParseNormalizedNamed(image) |
|
| 18 | 18 |
if err != nil {
|
| 19 | 19 |
return err |
| 20 | 20 |
} |
| ... | ... |
@@ -1,8 +1,8 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "github.com/docker/distribution/reference" |
|
| 4 | 5 |
"github.com/docker/docker/image" |
| 5 |
- "github.com/docker/docker/reference" |
|
| 6 | 6 |
) |
| 7 | 7 |
|
| 8 | 8 |
// TagImage creates the tag specified by newTag, pointing to the image named |
| ... | ... |
@@ -13,12 +13,12 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
|
| 13 | 13 |
return err |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
- newTag, err := reference.WithName(repository) |
|
| 16 |
+ newTag, err := reference.ParseNormalizedNamed(repository) |
|
| 17 | 17 |
if err != nil {
|
| 18 | 18 |
return err |
| 19 | 19 |
} |
| 20 | 20 |
if tag != "" {
|
| 21 |
- if newTag, err = reference.WithTag(newTag, tag); err != nil {
|
|
| 21 |
+ if newTag, err = reference.WithTag(reference.TrimNamed(newTag), tag); err != nil {
|
|
| 22 | 22 |
return err |
| 23 | 23 |
} |
| 24 | 24 |
} |
| ... | ... |
@@ -32,6 +32,6 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.N |
| 32 | 32 |
return err |
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 |
- daemon.LogImageEvent(imageID.String(), newTag.String(), "tag") |
|
| 35 |
+ daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag") |
|
| 36 | 36 |
return nil |
| 37 | 37 |
} |
| ... | ... |
@@ -135,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs |
| 135 | 135 |
var found bool |
| 136 | 136 |
var matchErr error |
| 137 | 137 |
for _, pattern := range imageFilters.Get("reference") {
|
| 138 |
- found, matchErr = reference.Match(pattern, ref) |
|
| 138 |
+ found, matchErr = reference.FamiliarMatch(pattern, ref) |
|
| 139 | 139 |
if matchErr != nil {
|
| 140 | 140 |
return nil, matchErr |
| 141 | 141 |
} |
| ... | ... |
@@ -145,10 +145,10 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs |
| 145 | 145 |
} |
| 146 | 146 |
} |
| 147 | 147 |
if _, ok := ref.(reference.Canonical); ok {
|
| 148 |
- newImage.RepoDigests = append(newImage.RepoDigests, ref.String()) |
|
| 148 |
+ newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref)) |
|
| 149 | 149 |
} |
| 150 | 150 |
if _, ok := ref.(reference.NamedTagged); ok {
|
| 151 |
- newImage.RepoTags = append(newImage.RepoTags, ref.String()) |
|
| 151 |
+ newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref)) |
|
| 152 | 152 |
} |
| 153 | 153 |
} |
| 154 | 154 |
if newImage.RepoDigests == nil && newImage.RepoTags == nil {
|
| ... | ... |
@@ -2,13 +2,13 @@ package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
- "errors" |
|
| 6 | 5 |
"io" |
| 7 | 6 |
"net/http" |
| 8 | 7 |
"net/url" |
| 9 | 8 |
"runtime" |
| 10 | 9 |
"time" |
| 11 | 10 |
|
| 11 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 12 |
"github.com/docker/docker/api/types/container" |
| 13 | 13 |
"github.com/docker/docker/builder/dockerfile" |
| 14 | 14 |
"github.com/docker/docker/dockerversion" |
| ... | ... |
@@ -18,7 +18,7 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/pkg/httputils" |
| 19 | 19 |
"github.com/docker/docker/pkg/progress" |
| 20 | 20 |
"github.com/docker/docker/pkg/streamformatter" |
| 21 |
- "github.com/docker/docker/reference" |
|
| 21 |
+ "github.com/pkg/errors" |
|
| 22 | 22 |
) |
| 23 | 23 |
|
| 24 | 24 |
// ImportImage imports an image, getting the archived layer data either from |
| ... | ... |
@@ -35,11 +35,10 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string |
| 35 | 35 |
|
| 36 | 36 |
if repository != "" {
|
| 37 | 37 |
var err error |
| 38 |
- newRef, err = reference.ParseNamed(repository) |
|
| 38 |
+ newRef, err = reference.ParseNormalizedNamed(repository) |
|
| 39 | 39 |
if err != nil {
|
| 40 | 40 |
return err |
| 41 | 41 |
} |
| 42 |
- |
|
| 43 | 42 |
if _, isCanonical := newRef.(reference.Canonical); isCanonical {
|
| 44 | 43 |
return errors.New("cannot import digest reference")
|
| 45 | 44 |
} |
| ... | ... |
@@ -6,13 +6,13 @@ import ( |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 | 8 |
"github.com/Sirupsen/logrus" |
| 9 |
+ "github.com/docker/distribution/reference" |
|
| 9 | 10 |
"github.com/docker/docker/api/types" |
| 10 | 11 |
"github.com/docker/docker/api/types/filters" |
| 11 | 12 |
timetypes "github.com/docker/docker/api/types/time" |
| 12 | 13 |
"github.com/docker/docker/image" |
| 13 | 14 |
"github.com/docker/docker/layer" |
| 14 | 15 |
"github.com/docker/docker/pkg/directory" |
| 15 |
- "github.com/docker/docker/reference" |
|
| 16 | 16 |
"github.com/docker/docker/runconfig" |
| 17 | 17 |
"github.com/docker/docker/volume" |
| 18 | 18 |
"github.com/docker/libnetwork" |
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/image" |
| 15 | 15 |
"github.com/docker/docker/layer" |
| 16 | 16 |
"github.com/docker/docker/pkg/progress" |
| 17 |
- "github.com/docker/docker/reference" |
|
| 17 |
+ refstore "github.com/docker/docker/reference" |
|
| 18 | 18 |
"github.com/docker/docker/registry" |
| 19 | 19 |
"github.com/docker/libtrust" |
| 20 | 20 |
"github.com/opencontainers/go-digest" |
| ... | ... |
@@ -44,7 +44,7 @@ type Config struct {
|
| 44 | 44 |
ImageStore ImageConfigStore |
| 45 | 45 |
// ReferenceStore manages tags. This value is optional, when excluded |
| 46 | 46 |
// content will not be tagged. |
| 47 |
- ReferenceStore reference.Store |
|
| 47 |
+ ReferenceStore refstore.Store |
|
| 48 | 48 |
// RequireSchema2 ensures that only schema2 manifests are used. |
| 49 | 49 |
RequireSchema2 bool |
| 50 | 50 |
} |
| ... | ... |
@@ -7,12 +7,12 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/Sirupsen/logrus" |
| 9 | 9 |
"github.com/docker/distribution" |
| 10 |
+ "github.com/docker/distribution/reference" |
|
| 10 | 11 |
"github.com/docker/distribution/registry/api/errcode" |
| 11 | 12 |
"github.com/docker/distribution/registry/api/v2" |
| 12 | 13 |
"github.com/docker/distribution/registry/client" |
| 13 | 14 |
"github.com/docker/distribution/registry/client/auth" |
| 14 | 15 |
"github.com/docker/docker/distribution/xfer" |
| 15 |
- "github.com/docker/docker/reference" |
|
| 16 | 16 |
"github.com/pkg/errors" |
| 17 | 17 |
) |
| 18 | 18 |
|
| ... | ... |
@@ -78,11 +78,11 @@ func TranslatePullError(err error, ref reference.Named) error {
|
| 78 | 78 |
switch v.Code {
|
| 79 | 79 |
case errcode.ErrorCodeDenied: |
| 80 | 80 |
// ErrorCodeDenied is used when access to the repository was denied |
| 81 |
- newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name())
|
|
| 81 |
+ newErr = errors.Errorf("repository %s not found: does not exist or no pull access", reference.FamiliarName(ref))
|
|
| 82 | 82 |
case v2.ErrorCodeManifestUnknown: |
| 83 |
- newErr = errors.Errorf("manifest for %s not found", ref.String())
|
|
| 83 |
+ newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
|
|
| 84 | 84 |
case v2.ErrorCodeNameUnknown: |
| 85 |
- newErr = errors.Errorf("repository %s not found", ref.Name())
|
|
| 85 |
+ newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
|
|
| 86 | 86 |
} |
| 87 | 87 |
if newErr != nil {
|
| 88 | 88 |
logrus.Infof("Translating %q to %q", err, newErr)
|
| ... | ... |
@@ -1,14 +1,14 @@ |
| 1 | 1 |
package distribution |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "errors" |
|
| 5 | 4 |
"fmt" |
| 6 | 5 |
|
| 7 | 6 |
"github.com/Sirupsen/logrus" |
| 7 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 8 |
"github.com/docker/docker/api" |
| 9 | 9 |
"github.com/docker/docker/distribution/metadata" |
| 10 | 10 |
"github.com/docker/docker/pkg/progress" |
| 11 |
- "github.com/docker/docker/reference" |
|
| 11 |
+ refstore "github.com/docker/docker/reference" |
|
| 12 | 12 |
"github.com/docker/docker/registry" |
| 13 | 13 |
"github.com/opencontainers/go-digest" |
| 14 | 14 |
"golang.org/x/net/context" |
| ... | ... |
@@ -56,12 +56,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 56 | 56 |
return err |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
- // makes sure name is not empty or `scratch` |
|
| 60 |
- if err := ValidateRepoName(repoInfo.Name()); err != nil {
|
|
| 59 |
+ // makes sure name is not `scratch` |
|
| 60 |
+ if err := ValidateRepoName(repoInfo.Name); err != nil {
|
|
| 61 | 61 |
return err |
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 |
- endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.Hostname()) |
|
| 64 |
+ endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) |
|
| 65 | 65 |
if err != nil {
|
| 66 | 66 |
return err |
| 67 | 67 |
} |
| ... | ... |
@@ -105,7 +105,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 105 | 105 |
} |
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 |
- logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
|
|
| 108 |
+ logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version)
|
|
| 109 | 109 |
|
| 110 | 110 |
puller, err := newPuller(endpoint, repoInfo, imagePullConfig) |
| 111 | 111 |
if err != nil {
|
| ... | ... |
@@ -147,12 +147,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
| 147 | 147 |
return TranslatePullError(err, ref) |
| 148 | 148 |
} |
| 149 | 149 |
|
| 150 |
- imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull") |
|
| 150 |
+ imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull") |
|
| 151 | 151 |
return nil |
| 152 | 152 |
} |
| 153 | 153 |
|
| 154 | 154 |
if lastErr == nil {
|
| 155 |
- lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
|
|
| 155 |
+ lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref))
|
|
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 | 158 |
return TranslatePullError(lastErr, ref) |
| ... | ... |
@@ -171,17 +171,14 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool |
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
// ValidateRepoName validates the name of a repository. |
| 174 |
-func ValidateRepoName(name string) error {
|
|
| 175 |
- if name == "" {
|
|
| 176 |
- return errors.New("Repository name can't be empty")
|
|
| 177 |
- } |
|
| 178 |
- if name == api.NoBaseImageSpecifier {
|
|
| 174 |
+func ValidateRepoName(name reference.Named) error {
|
|
| 175 |
+ if reference.FamiliarName(name) == api.NoBaseImageSpecifier {
|
|
| 179 | 176 |
return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
|
| 180 | 177 |
} |
| 181 | 178 |
return nil |
| 182 | 179 |
} |
| 183 | 180 |
|
| 184 |
-func addDigestReference(store reference.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
|
|
| 181 |
+func addDigestReference(store refstore.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
|
|
| 185 | 182 |
dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst) |
| 186 | 183 |
if err != nil {
|
| 187 | 184 |
return err |
| ... | ... |
@@ -193,7 +190,7 @@ func addDigestReference(store reference.Store, ref reference.Named, dgst digest. |
| 193 | 193 |
logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id)
|
| 194 | 194 |
} |
| 195 | 195 |
return nil |
| 196 |
- } else if err != reference.ErrDoesNotExist {
|
|
| 196 |
+ } else if err != refstore.ErrDoesNotExist {
|
|
| 197 | 197 |
return err |
| 198 | 198 |
} |
| 199 | 199 |
|
| ... | ... |
@@ -12,6 +12,7 @@ import ( |
| 12 | 12 |
"time" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/Sirupsen/logrus" |
| 15 |
+ "github.com/docker/distribution/reference" |
|
| 15 | 16 |
"github.com/docker/distribution/registry/client/transport" |
| 16 | 17 |
"github.com/docker/docker/distribution/metadata" |
| 17 | 18 |
"github.com/docker/docker/distribution/xfer" |
| ... | ... |
@@ -22,7 +23,6 @@ import ( |
| 22 | 22 |
"github.com/docker/docker/pkg/ioutils" |
| 23 | 23 |
"github.com/docker/docker/pkg/progress" |
| 24 | 24 |
"github.com/docker/docker/pkg/stringid" |
| 25 |
- "github.com/docker/docker/reference" |
|
| 26 | 25 |
"github.com/docker/docker/registry" |
| 27 | 26 |
"golang.org/x/net/context" |
| 28 | 27 |
) |
| ... | ... |
@@ -67,23 +67,23 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
|
| 67 | 67 |
// TODO(dmcgowan): Check if should fallback |
| 68 | 68 |
return err |
| 69 | 69 |
} |
| 70 |
- 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.") |
|
| 70 |
+ progress.Message(p.config.ProgressOutput, "", p.repoInfo.Name.Name()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.") |
|
| 71 | 71 |
|
| 72 | 72 |
return nil |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
|
| 76 |
- progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName()) |
|
| 76 |
+ progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name()) |
|
| 77 | 77 |
|
| 78 | 78 |
tagged, isTagged := ref.(reference.NamedTagged) |
| 79 | 79 |
|
| 80 |
- repoData, err := p.session.GetRepositoryData(p.repoInfo) |
|
| 80 |
+ repoData, err := p.session.GetRepositoryData(p.repoInfo.Name) |
|
| 81 | 81 |
if err != nil {
|
| 82 | 82 |
if strings.Contains(err.Error(), "HTTP code: 404") {
|
| 83 | 83 |
if isTagged {
|
| 84 |
- return fmt.Errorf("Error: image %s:%s not found", p.repoInfo.RemoteName(), tagged.Tag())
|
|
| 84 |
+ return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag())
|
|
| 85 | 85 |
} |
| 86 |
- return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
|
|
| 86 |
+ return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name))
|
|
| 87 | 87 |
} |
| 88 | 88 |
// Unexpected HTTP error |
| 89 | 89 |
return err |
| ... | ... |
@@ -92,13 +92,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro |
| 92 | 92 |
logrus.Debug("Retrieving the tag list")
|
| 93 | 93 |
var tagsList map[string]string |
| 94 | 94 |
if !isTagged {
|
| 95 |
- tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo) |
|
| 95 |
+ tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name) |
|
| 96 | 96 |
} else {
|
| 97 | 97 |
var tagID string |
| 98 | 98 |
tagsList = make(map[string]string) |
| 99 |
- tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag()) |
|
| 99 |
+ tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag()) |
|
| 100 | 100 |
if err == registry.ErrRepoNotFound {
|
| 101 |
- return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
|
|
| 101 |
+ return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name())
|
|
| 102 | 102 |
} |
| 103 | 103 |
tagsList[tagged.Tag()] = tagID |
| 104 | 104 |
} |
| ... | ... |
@@ -127,7 +127,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro |
| 127 | 127 |
} |
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 |
- writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded) |
|
| 130 |
+ writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded) |
|
| 131 | 131 |
return nil |
| 132 | 132 |
} |
| 133 | 133 |
|
| ... | ... |
@@ -137,7 +137,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 137 | 137 |
return nil |
| 138 | 138 |
} |
| 139 | 139 |
|
| 140 |
- localNameRef, err := reference.WithTag(p.repoInfo, img.Tag) |
|
| 140 |
+ localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag) |
|
| 141 | 141 |
if err != nil {
|
| 142 | 142 |
retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
|
| 143 | 143 |
logrus.Debug(retErr.Error()) |
| ... | ... |
@@ -148,15 +148,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 148 | 148 |
return err |
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 |
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName()) |
|
| 151 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name()) |
|
| 152 | 152 |
success := false |
| 153 | 153 |
var lastErr error |
| 154 | 154 |
for _, ep := range p.repoInfo.Index.Mirrors {
|
| 155 | 155 |
ep += "v1/" |
| 156 |
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
|
|
| 156 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.Name.Name(), ep))
|
|
| 157 | 157 |
if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
|
| 158 | 158 |
// Don't report errors when pulling from mirrors. |
| 159 |
- logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
|
|
| 159 |
+ logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
|
|
| 160 | 160 |
continue |
| 161 | 161 |
} |
| 162 | 162 |
success = true |
| ... | ... |
@@ -164,12 +164,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 164 | 164 |
} |
| 165 | 165 |
if !success {
|
| 166 | 166 |
for _, ep := range repoData.Endpoints {
|
| 167 |
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep) |
|
| 167 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), ep) |
|
| 168 | 168 |
if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
|
| 169 | 169 |
// It's not ideal that only the last error is returned, it would be better to concatenate the errors. |
| 170 | 170 |
// As the error is also given to the output stream the user will see the error. |
| 171 | 171 |
lastErr = err |
| 172 |
- 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) |
|
| 172 |
+ progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err) |
|
| 173 | 173 |
continue |
| 174 | 174 |
} |
| 175 | 175 |
success = true |
| ... | ... |
@@ -177,7 +177,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit |
| 177 | 177 |
} |
| 178 | 178 |
} |
| 179 | 179 |
if !success {
|
| 180 |
- err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
|
|
| 180 |
+ err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr)
|
|
| 181 | 181 |
progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error()) |
| 182 | 182 |
return err |
| 183 | 183 |
} |
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
"github.com/docker/distribution/manifest/manifestlist" |
| 16 | 16 |
"github.com/docker/distribution/manifest/schema1" |
| 17 | 17 |
"github.com/docker/distribution/manifest/schema2" |
| 18 |
+ "github.com/docker/distribution/reference" |
|
| 18 | 19 |
"github.com/docker/distribution/registry/api/errcode" |
| 19 | 20 |
"github.com/docker/distribution/registry/client/auth" |
| 20 | 21 |
"github.com/docker/distribution/registry/client/transport" |
| ... | ... |
@@ -26,7 +27,7 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/pkg/ioutils" |
| 27 | 27 |
"github.com/docker/docker/pkg/progress" |
| 28 | 28 |
"github.com/docker/docker/pkg/stringid" |
| 29 |
- "github.com/docker/docker/reference" |
|
| 29 |
+ refstore "github.com/docker/docker/reference" |
|
| 30 | 30 |
"github.com/docker/docker/registry" |
| 31 | 31 |
"github.com/opencontainers/go-digest" |
| 32 | 32 |
"golang.org/x/net/context" |
| ... | ... |
@@ -124,7 +125,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e |
| 124 | 124 |
} |
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 |
- writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded) |
|
| 127 |
+ writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded) |
|
| 128 | 128 |
|
| 129 | 129 |
return nil |
| 130 | 130 |
} |
| ... | ... |
@@ -317,7 +318,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error {
|
| 317 | 317 |
|
| 318 | 318 |
func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
|
| 319 | 319 |
// Cache mapping from this layer's DiffID to the blobsum |
| 320 |
- ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
|
|
| 320 |
+ ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
|
|
| 321 | 321 |
} |
| 322 | 322 |
|
| 323 | 323 |
func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
|
| ... | ... |
@@ -343,7 +344,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat |
| 343 | 343 |
} |
| 344 | 344 |
tagOrDigest = digested.Digest().String() |
| 345 | 345 |
} else {
|
| 346 |
- return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
|
|
| 346 |
+ return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
|
|
| 347 | 347 |
} |
| 348 | 348 |
|
| 349 | 349 |
if manifest == nil {
|
| ... | ... |
@@ -371,8 +372,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat |
| 371 | 371 |
// the other side speaks the v2 protocol. |
| 372 | 372 |
p.confirmedV2 = true |
| 373 | 373 |
|
| 374 |
- logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
|
|
| 375 |
- progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Named().Name()) |
|
| 374 |
+ logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
|
|
| 375 |
+ progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named())) |
|
| 376 | 376 |
|
| 377 | 377 |
var ( |
| 378 | 378 |
id digest.Digest |
| ... | ... |
@@ -410,7 +411,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat |
| 410 | 410 |
if oldTagID == id {
|
| 411 | 411 |
return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id) |
| 412 | 412 |
} |
| 413 |
- } else if err != reference.ErrDoesNotExist {
|
|
| 413 |
+ } else if err != refstore.ErrDoesNotExist {
|
|
| 414 | 414 |
return false, err |
| 415 | 415 |
} |
| 416 | 416 |
|
| ... | ... |
@@ -802,13 +803,13 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference |
| 802 | 802 |
m = &signedManifest.Manifest |
| 803 | 803 |
|
| 804 | 804 |
if m.SchemaVersion != 1 {
|
| 805 |
- return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
|
|
| 805 |
+ return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
|
|
| 806 | 806 |
} |
| 807 | 807 |
if len(m.FSLayers) != len(m.History) {
|
| 808 |
- return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
|
|
| 808 |
+ return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
|
|
| 809 | 809 |
} |
| 810 | 810 |
if len(m.FSLayers) == 0 {
|
| 811 |
- return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
|
|
| 811 |
+ return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
|
|
| 812 | 812 |
} |
| 813 | 813 |
return m, nil |
| 814 | 814 |
} |
| ... | ... |
@@ -9,7 +9,7 @@ import ( |
| 9 | 9 |
"testing" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/distribution/manifest/schema1" |
| 12 |
- "github.com/docker/docker/reference" |
|
| 12 |
+ "github.com/docker/distribution/reference" |
|
| 13 | 13 |
"github.com/opencontainers/go-digest" |
| 14 | 14 |
) |
| 15 | 15 |
|
| ... | ... |
@@ -113,7 +113,7 @@ func TestValidateManifest(t *testing.T) {
|
| 113 | 113 |
if runtime.GOOS == "windows" {
|
| 114 | 114 |
t.Skip("Needs fixing on Windows")
|
| 115 | 115 |
} |
| 116 |
- expectedDigest, err := reference.ParseNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
|
|
| 116 |
+ expectedDigest, err := reference.ParseNormalizedNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
|
|
| 117 | 117 |
if err != nil {
|
| 118 | 118 |
t.Fatal("could not parse reference")
|
| 119 | 119 |
} |
| ... | ... |
@@ -7,9 +7,9 @@ import ( |
| 7 | 7 |
"io" |
| 8 | 8 |
|
| 9 | 9 |
"github.com/Sirupsen/logrus" |
| 10 |
+ "github.com/docker/distribution/reference" |
|
| 10 | 11 |
"github.com/docker/docker/distribution/metadata" |
| 11 | 12 |
"github.com/docker/docker/pkg/progress" |
| 12 |
- "github.com/docker/docker/reference" |
|
| 13 | 13 |
"github.com/docker/docker/registry" |
| 14 | 14 |
"golang.org/x/net/context" |
| 15 | 15 |
) |
| ... | ... |
@@ -64,16 +64,16 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo |
| 64 | 64 |
return err |
| 65 | 65 |
} |
| 66 | 66 |
|
| 67 |
- endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.Hostname()) |
|
| 67 |
+ endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name)) |
|
| 68 | 68 |
if err != nil {
|
| 69 | 69 |
return err |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
- progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName()) |
|
| 72 |
+ progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.Name.Name()) |
|
| 73 | 73 |
|
| 74 |
- associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo) |
|
| 74 |
+ associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.Name) |
|
| 75 | 75 |
if len(associations) == 0 {
|
| 76 |
- return fmt.Errorf("An image does not exist locally with the tag: %s", repoInfo.Name())
|
|
| 76 |
+ return fmt.Errorf("An image does not exist locally with the tag: %s", reference.FamiliarName(repoInfo.Name))
|
|
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 | 79 |
var ( |
| ... | ... |
@@ -106,7 +106,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo |
| 106 | 106 |
} |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
- logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
|
|
| 109 |
+ logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name.Name(), endpoint.URL, endpoint.Version)
|
|
| 110 | 110 |
|
| 111 | 111 |
pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig) |
| 112 | 112 |
if err != nil {
|
| ... | ... |
@@ -135,12 +135,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo |
| 135 | 135 |
return err |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
- imagePushConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "push") |
|
| 138 |
+ imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push") |
|
| 139 | 139 |
return nil |
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 | 142 |
if lastErr == nil {
|
| 143 |
- lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
|
|
| 143 |
+ lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.Name.Name())
|
|
| 144 | 144 |
} |
| 145 | 145 |
return lastErr |
| 146 | 146 |
} |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"sync" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/Sirupsen/logrus" |
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/distribution/registry/client/transport" |
| 9 | 10 |
"github.com/docker/docker/distribution/metadata" |
| 10 | 11 |
"github.com/docker/docker/dockerversion" |
| ... | ... |
@@ -14,7 +15,6 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/pkg/ioutils" |
| 15 | 15 |
"github.com/docker/docker/pkg/progress" |
| 16 | 16 |
"github.com/docker/docker/pkg/stringid" |
| 17 |
- "github.com/docker/docker/reference" |
|
| 18 | 17 |
"github.com/docker/docker/registry" |
| 19 | 18 |
"github.com/opencontainers/go-digest" |
| 20 | 19 |
"golang.org/x/net/context" |
| ... | ... |
@@ -356,8 +356,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima |
| 356 | 356 |
} |
| 357 | 357 |
if topImage, isTopImage := img.(*v1TopImage); isTopImage {
|
| 358 | 358 |
for _, tag := range tags[topImage.imageID] {
|
| 359 |
- progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
|
|
| 360 |
- if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
|
|
| 359 |
+ progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag)
|
|
| 360 |
+ if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil {
|
|
| 361 | 361 |
return err |
| 362 | 362 |
} |
| 363 | 363 |
} |
| ... | ... |
@@ -385,7 +385,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
|
| 385 | 385 |
|
| 386 | 386 |
// Register all the images in a repository with the registry |
| 387 | 387 |
// If an image is not in this list it will not be associated with the repository |
| 388 |
- repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil) |
|
| 388 |
+ repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil) |
|
| 389 | 389 |
if err != nil {
|
| 390 | 390 |
return err |
| 391 | 391 |
} |
| ... | ... |
@@ -395,7 +395,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
|
| 395 | 395 |
return err |
| 396 | 396 |
} |
| 397 | 397 |
} |
| 398 |
- _, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints) |
|
| 398 |
+ _, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints) |
|
| 399 | 399 |
return err |
| 400 | 400 |
} |
| 401 | 401 |
|
| ... | ... |
@@ -15,7 +15,7 @@ import ( |
| 15 | 15 |
"github.com/docker/distribution" |
| 16 | 16 |
"github.com/docker/distribution/manifest/schema1" |
| 17 | 17 |
"github.com/docker/distribution/manifest/schema2" |
| 18 |
- distreference "github.com/docker/distribution/reference" |
|
| 18 |
+ "github.com/docker/distribution/reference" |
|
| 19 | 19 |
"github.com/docker/distribution/registry/client" |
| 20 | 20 |
apitypes "github.com/docker/docker/api/types" |
| 21 | 21 |
"github.com/docker/docker/distribution/metadata" |
| ... | ... |
@@ -24,7 +24,6 @@ import ( |
| 24 | 24 |
"github.com/docker/docker/pkg/ioutils" |
| 25 | 25 |
"github.com/docker/docker/pkg/progress" |
| 26 | 26 |
"github.com/docker/docker/pkg/stringid" |
| 27 |
- "github.com/docker/docker/reference" |
|
| 28 | 27 |
"github.com/docker/docker/registry" |
| 29 | 28 |
"github.com/opencontainers/go-digest" |
| 30 | 29 |
) |
| ... | ... |
@@ -83,7 +82,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
|
| 83 | 83 |
if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
|
| 84 | 84 |
imageID, err := p.config.ReferenceStore.Get(p.ref) |
| 85 | 85 |
if err != nil {
|
| 86 |
- return fmt.Errorf("tag does not exist: %s", p.ref.String())
|
|
| 86 |
+ return fmt.Errorf("tag does not exist: %s", reference.FamiliarString(p.ref))
|
|
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 | 89 |
return p.pushV2Tag(ctx, namedTagged, imageID) |
| ... | ... |
@@ -105,23 +104,23 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
|
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 | 107 |
if pushed == 0 {
|
| 108 |
- return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
|
|
| 108 |
+ return fmt.Errorf("no tags to push for %s", reference.FamiliarName(p.repoInfo.Name))
|
|
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 | 111 |
return nil |
| 112 | 112 |
} |
| 113 | 113 |
|
| 114 | 114 |
func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error {
|
| 115 |
- logrus.Debugf("Pushing repository: %s", ref.String())
|
|
| 115 |
+ logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref))
|
|
| 116 | 116 |
|
| 117 | 117 |
imgConfig, err := p.config.ImageStore.Get(id) |
| 118 | 118 |
if err != nil {
|
| 119 |
- return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
|
|
| 119 |
+ return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
|
|
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 | 122 |
rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig) |
| 123 | 123 |
if err != nil {
|
| 124 |
- return fmt.Errorf("unable to get rootfs for image %s: %s", ref.String(), err)
|
|
| 124 |
+ return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
|
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
l, err := p.config.LayerStore.Get(rootfs.ChainID()) |
| ... | ... |
@@ -140,7 +139,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id |
| 140 | 140 |
descriptorTemplate := v2PushDescriptor{
|
| 141 | 141 |
v2MetadataService: p.v2MetadataService, |
| 142 | 142 |
hmacKey: hmacKey, |
| 143 |
- repoInfo: p.repoInfo, |
|
| 143 |
+ repoInfo: p.repoInfo.Name, |
|
| 144 | 144 |
ref: p.ref, |
| 145 | 145 |
repo: p.repo, |
| 146 | 146 |
pushState: &p.pushState, |
| ... | ... |
@@ -181,7 +180,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id |
| 181 | 181 |
|
| 182 | 182 |
logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
|
| 183 | 183 |
|
| 184 |
- manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag()) |
|
| 184 |
+ manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag()) |
|
| 185 | 185 |
if err != nil {
|
| 186 | 186 |
return err |
| 187 | 187 |
} |
| ... | ... |
@@ -248,7 +247,7 @@ type v2PushDescriptor struct {
|
| 248 | 248 |
} |
| 249 | 249 |
|
| 250 | 250 |
func (pd *v2PushDescriptor) Key() string {
|
| 251 |
- return "v2push:" + pd.ref.FullName() + " " + pd.layer.DiffID().String() |
|
| 251 |
+ return "v2push:" + pd.ref.Name() + " " + pd.layer.DiffID().String() |
|
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 | 254 |
func (pd *v2PushDescriptor) ID() string {
|
| ... | ... |
@@ -304,23 +303,22 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. |
| 304 | 304 |
createOpts := []distribution.BlobCreateOption{}
|
| 305 | 305 |
|
| 306 | 306 |
if len(mountCandidate.SourceRepository) > 0 {
|
| 307 |
- namedRef, err := reference.WithName(mountCandidate.SourceRepository) |
|
| 307 |
+ namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository) |
|
| 308 | 308 |
if err != nil {
|
| 309 |
- logrus.Errorf("failed to parse source repository reference %v: %v", namedRef.String(), err)
|
|
| 309 |
+ logrus.Errorf("failed to parse source repository reference %v: %v", reference.FamiliarString(namedRef), err)
|
|
| 310 | 310 |
pd.v2MetadataService.Remove(mountCandidate) |
| 311 | 311 |
continue |
| 312 | 312 |
} |
| 313 | 313 |
|
| 314 |
- // TODO (brianbland): We need to construct a reference where the Name is |
|
| 315 |
- // only the full remote name, so clean this up when distribution has a |
|
| 316 |
- // richer reference package |
|
| 317 |
- remoteRef, err := distreference.WithName(namedRef.RemoteName()) |
|
| 314 |
+ // Candidates are always under same domain, create remote reference |
|
| 315 |
+ // with only path to set mount from with |
|
| 316 |
+ remoteRef, err := reference.WithName(reference.Path(namedRef)) |
|
| 318 | 317 |
if err != nil {
|
| 319 |
- logrus.Errorf("failed to make remote reference out of %q: %v", namedRef.RemoteName(), namedRef.RemoteName())
|
|
| 318 |
+ logrus.Errorf("failed to make remote reference out of %q: %v", reference.Path(namedRef), err)
|
|
| 320 | 319 |
continue |
| 321 | 320 |
} |
| 322 | 321 |
|
| 323 |
- canonicalRef, err := distreference.WithDigest(distreference.TrimNamed(remoteRef), mountCandidate.Digest) |
|
| 322 |
+ canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest) |
|
| 324 | 323 |
if err != nil {
|
| 325 | 324 |
logrus.Errorf("failed to make canonical reference: %v", err)
|
| 326 | 325 |
continue |
| ... | ... |
@@ -347,7 +345,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. |
| 347 | 347 |
// Cache mapping from this layer's DiffID to the blobsum |
| 348 | 348 |
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
|
| 349 | 349 |
Digest: err.Descriptor.Digest, |
| 350 |
- SourceRepository: pd.repoInfo.FullName(), |
|
| 350 |
+ SourceRepository: pd.repoInfo.Name(), |
|
| 351 | 351 |
}); err != nil {
|
| 352 | 352 |
return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
|
| 353 | 353 |
} |
| ... | ... |
@@ -455,7 +453,7 @@ func (pd *v2PushDescriptor) uploadUsingSession( |
| 455 | 455 |
// Cache mapping from this layer's DiffID to the blobsum |
| 456 | 456 |
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
|
| 457 | 457 |
Digest: pushDigest, |
| 458 |
- SourceRepository: pd.repoInfo.FullName(), |
|
| 458 |
+ SourceRepository: pd.repoInfo.Name(), |
|
| 459 | 459 |
}); err != nil {
|
| 460 | 460 |
return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
|
| 461 | 461 |
} |
| ... | ... |
@@ -490,7 +488,7 @@ func (pd *v2PushDescriptor) layerAlreadyExists( |
| 490 | 490 |
// filter the metadata |
| 491 | 491 |
candidates := []metadata.V2Metadata{}
|
| 492 | 492 |
for _, meta := range v2Metadata {
|
| 493 |
- if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.FullName() {
|
|
| 493 |
+ if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.Name() {
|
|
| 494 | 494 |
continue |
| 495 | 495 |
} |
| 496 | 496 |
candidates = append(candidates, meta) |
| ... | ... |
@@ -521,16 +519,16 @@ func (pd *v2PushDescriptor) layerAlreadyExists( |
| 521 | 521 |
attempts: |
| 522 | 522 |
for _, dgst := range layerDigests {
|
| 523 | 523 |
meta := digestToMetadata[dgst] |
| 524 |
- logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
|
|
| 524 |
+ logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
|
|
| 525 | 525 |
desc, err = pd.repo.Blobs(ctx).Stat(ctx, dgst) |
| 526 | 526 |
pd.checkedDigests[meta.Digest] = struct{}{}
|
| 527 | 527 |
switch err {
|
| 528 | 528 |
case nil: |
| 529 |
- if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.FullName() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
|
|
| 529 |
+ if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.Name() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
|
|
| 530 | 530 |
// cache mapping from this layer's DiffID to the blobsum |
| 531 | 531 |
if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
|
| 532 | 532 |
Digest: desc.Digest, |
| 533 |
- SourceRepository: pd.repoInfo.FullName(), |
|
| 533 |
+ SourceRepository: pd.repoInfo.Name(), |
|
| 534 | 534 |
}); err != nil {
|
| 535 | 535 |
return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
|
| 536 | 536 |
} |
| ... | ... |
@@ -539,12 +537,12 @@ attempts: |
| 539 | 539 |
exists = true |
| 540 | 540 |
break attempts |
| 541 | 541 |
case distribution.ErrBlobUnknown: |
| 542 |
- if meta.SourceRepository == pd.repoInfo.FullName() {
|
|
| 542 |
+ if meta.SourceRepository == pd.repoInfo.Name() {
|
|
| 543 | 543 |
// remove the mapping to the target repository |
| 544 | 544 |
pd.v2MetadataService.Remove(*meta) |
| 545 | 545 |
} |
| 546 | 546 |
default: |
| 547 |
- logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
|
|
| 547 |
+ logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
|
|
| 548 | 548 |
} |
| 549 | 549 |
} |
| 550 | 550 |
|
| ... | ... |
@@ -598,11 +596,11 @@ func getRepositoryMountCandidates( |
| 598 | 598 |
candidates := []metadata.V2Metadata{}
|
| 599 | 599 |
for _, meta := range v2Metadata {
|
| 600 | 600 |
sourceRepo, err := reference.ParseNamed(meta.SourceRepository) |
| 601 |
- if err != nil || repoInfo.Hostname() != sourceRepo.Hostname() {
|
|
| 601 |
+ if err != nil || reference.Domain(repoInfo) != reference.Domain(sourceRepo) {
|
|
| 602 | 602 |
continue |
| 603 | 603 |
} |
| 604 | 604 |
// target repository is not a viable candidate |
| 605 |
- if meta.SourceRepository == repoInfo.FullName() {
|
|
| 605 |
+ if meta.SourceRepository == repoInfo.Name() {
|
|
| 606 | 606 |
continue |
| 607 | 607 |
} |
| 608 | 608 |
candidates = append(candidates, meta) |
| ... | ... |
@@ -653,7 +651,7 @@ func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, ma |
| 653 | 653 |
sort.Stable(byLikeness{
|
| 654 | 654 |
arr: marr, |
| 655 | 655 |
hmacKey: hmacKey, |
| 656 |
- pathComponents: getPathComponents(repoInfo.FullName()), |
|
| 656 |
+ pathComponents: getPathComponents(repoInfo.Name()), |
|
| 657 | 657 |
}) |
| 658 | 658 |
} |
| 659 | 659 |
|
| ... | ... |
@@ -670,11 +668,6 @@ func numOfMatchingPathComponents(pth string, matchComponents []string) int {
|
| 670 | 670 |
} |
| 671 | 671 |
|
| 672 | 672 |
func getPathComponents(path string) []string {
|
| 673 |
- // make sure to add docker.io/ prefix to the path |
|
| 674 |
- named, err := reference.ParseNamed(path) |
|
| 675 |
- if err == nil {
|
|
| 676 |
- path = named.FullName() |
|
| 677 |
- } |
|
| 678 | 673 |
return strings.Split(path, "/") |
| 679 | 674 |
} |
| 680 | 675 |
|
| ... | ... |
@@ -8,11 +8,10 @@ import ( |
| 8 | 8 |
"github.com/docker/distribution" |
| 9 | 9 |
"github.com/docker/distribution/context" |
| 10 | 10 |
"github.com/docker/distribution/manifest/schema2" |
| 11 |
- distreference "github.com/docker/distribution/reference" |
|
| 11 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 12 |
"github.com/docker/docker/distribution/metadata" |
| 13 | 13 |
"github.com/docker/docker/layer" |
| 14 | 14 |
"github.com/docker/docker/pkg/progress" |
| 15 |
- "github.com/docker/docker/reference" |
|
| 16 | 15 |
"github.com/opencontainers/go-digest" |
| 17 | 16 |
) |
| 18 | 17 |
|
| ... | ... |
@@ -43,8 +42,8 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
|
| 43 | 43 |
name: "one item matching", |
| 44 | 44 |
targetRepo: "busybox", |
| 45 | 45 |
maxCandidates: -1, |
| 46 |
- metadata: []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
|
|
| 47 |
- candidates: []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
|
|
| 46 |
+ metadata: []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
|
|
| 47 |
+ candidates: []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
|
|
| 48 | 48 |
}, |
| 49 | 49 |
{
|
| 50 | 50 |
name: "allow missing SourceRepository", |
| ... | ... |
@@ -63,13 +62,13 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
|
| 63 | 63 |
maxCandidates: -1, |
| 64 | 64 |
metadata: []metadata.V2Metadata{
|
| 65 | 65 |
{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
|
| 66 |
- {Digest: digest.Digest("3"), SourceRepository: "user/bar"},
|
|
| 67 |
- {Digest: digest.Digest("2"), SourceRepository: "app"},
|
|
| 66 |
+ {Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
|
|
| 67 |
+ {Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
|
|
| 68 | 68 |
}, |
| 69 | 69 |
candidates: []metadata.V2Metadata{
|
| 70 |
- {Digest: digest.Digest("3"), SourceRepository: "user/bar"},
|
|
| 70 |
+ {Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
|
|
| 71 | 71 |
{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
|
| 72 |
- {Digest: digest.Digest("2"), SourceRepository: "app"},
|
|
| 72 |
+ {Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
|
|
| 73 | 73 |
}, |
| 74 | 74 |
}, |
| 75 | 75 |
{
|
| ... | ... |
@@ -78,10 +77,10 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
|
| 78 | 78 |
targetRepo: "127.0.0.1/foo/bar", |
| 79 | 79 |
maxCandidates: -1, |
| 80 | 80 |
metadata: []metadata.V2Metadata{
|
| 81 |
- taggedMetadata("hash", "1", "hello-world"),
|
|
| 81 |
+ taggedMetadata("hash", "1", "docker.io/library/hello-world"),
|
|
| 82 | 82 |
taggedMetadata("efgh", "2", "127.0.0.1/hello-world"),
|
| 83 |
- taggedMetadata("abcd", "3", "busybox"),
|
|
| 84 |
- taggedMetadata("hash", "4", "busybox"),
|
|
| 83 |
+ taggedMetadata("abcd", "3", "docker.io/library/busybox"),
|
|
| 84 |
+ taggedMetadata("hash", "4", "docker.io/library/busybox"),
|
|
| 85 | 85 |
taggedMetadata("hash", "5", "127.0.0.1/foo"),
|
| 86 | 86 |
taggedMetadata("hash", "6", "127.0.0.1/bar"),
|
| 87 | 87 |
taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
|
| ... | ... |
@@ -105,23 +104,25 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
|
| 105 | 105 |
targetRepo: "user/app", |
| 106 | 106 |
maxCandidates: 3, |
| 107 | 107 |
metadata: []metadata.V2Metadata{
|
| 108 |
- taggedMetadata("abcd", "1", "user/app1"),
|
|
| 109 |
- taggedMetadata("abcd", "2", "user/app/base"),
|
|
| 110 |
- taggedMetadata("hash", "3", "user/app"),
|
|
| 108 |
+ taggedMetadata("abcd", "1", "docker.io/user/app1"),
|
|
| 109 |
+ taggedMetadata("abcd", "2", "docker.io/user/app/base"),
|
|
| 110 |
+ taggedMetadata("hash", "3", "docker.io/user/app"),
|
|
| 111 | 111 |
taggedMetadata("abcd", "4", "127.0.0.1/user/app"),
|
| 112 |
- taggedMetadata("hash", "5", "user/foo"),
|
|
| 113 |
- taggedMetadata("hash", "6", "app/bar"),
|
|
| 112 |
+ taggedMetadata("hash", "5", "docker.io/user/foo"),
|
|
| 113 |
+ taggedMetadata("hash", "6", "docker.io/app/bar"),
|
|
| 114 | 114 |
}, |
| 115 | 115 |
candidates: []metadata.V2Metadata{
|
| 116 | 116 |
// first by matching hash |
| 117 |
- taggedMetadata("abcd", "2", "user/app/base"),
|
|
| 118 |
- taggedMetadata("abcd", "1", "user/app1"),
|
|
| 117 |
+ taggedMetadata("abcd", "2", "docker.io/user/app/base"),
|
|
| 118 |
+ taggedMetadata("abcd", "1", "docker.io/user/app1"),
|
|
| 119 | 119 |
// then by longest matching prefix |
| 120 |
- taggedMetadata("hash", "3", "user/app"),
|
|
| 120 |
+ // "docker.io/usr/app" is excluded since candidates must |
|
| 121 |
+ // be from a different repository |
|
| 122 |
+ taggedMetadata("hash", "5", "docker.io/user/foo"),
|
|
| 121 | 123 |
}, |
| 122 | 124 |
}, |
| 123 | 125 |
} {
|
| 124 |
- repoInfo, err := reference.ParseNamed(tc.targetRepo) |
|
| 126 |
+ repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo) |
|
| 125 | 127 |
if err != nil {
|
| 126 | 128 |
t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
|
| 127 | 129 |
} |
| ... | ... |
@@ -204,10 +205,13 @@ func TestLayerAlreadyExists(t *testing.T) {
|
| 204 | 204 |
{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
|
| 205 | 205 |
{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
|
| 206 | 206 |
{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
|
| 207 |
- {Digest: digest.Digest("plum"), SourceRepository: "busybox"},
|
|
| 207 |
+ {Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
|
|
| 208 | 208 |
{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
|
| 209 | 209 |
}, |
| 210 |
- expectedRequests: []string{"plum", "pear", "apple", "orange", "banana"},
|
|
| 210 |
+ expectedRequests: []string{"plum", "apple", "pear", "orange", "banana"},
|
|
| 211 |
+ expectedRemovals: []metadata.V2Metadata{
|
|
| 212 |
+ {Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
|
|
| 213 |
+ }, |
|
| 211 | 214 |
}, |
| 212 | 215 |
{
|
| 213 | 216 |
name: "find existing blob", |
| ... | ... |
@@ -374,7 +378,7 @@ func TestLayerAlreadyExists(t *testing.T) {
|
| 374 | 374 |
}, |
| 375 | 375 |
}, |
| 376 | 376 |
} {
|
| 377 |
- repoInfo, err := reference.ParseNamed(tc.targetRepo) |
|
| 377 |
+ repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo) |
|
| 378 | 378 |
if err != nil {
|
| 379 | 379 |
t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
|
| 380 | 380 |
} |
| ... | ... |
@@ -476,7 +480,7 @@ type mockRepo struct {
|
| 476 | 476 |
|
| 477 | 477 |
var _ distribution.Repository = &mockRepo{}
|
| 478 | 478 |
|
| 479 |
-func (m *mockRepo) Named() distreference.Named {
|
|
| 479 |
+func (m *mockRepo) Named() reference.Named {
|
|
| 480 | 480 |
m.t.Fatalf("Named() not implemented")
|
| 481 | 481 |
return nil |
| 482 | 482 |
} |
| ... | ... |
@@ -8,7 +8,7 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
"github.com/docker/distribution" |
| 10 | 10 |
"github.com/docker/distribution/manifest/schema2" |
| 11 |
- distreference "github.com/docker/distribution/reference" |
|
| 11 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 12 |
"github.com/docker/distribution/registry/client" |
| 13 | 13 |
"github.com/docker/distribution/registry/client/auth" |
| 14 | 14 |
"github.com/docker/distribution/registry/client/transport" |
| ... | ... |
@@ -55,10 +55,10 @@ func init() {
|
| 55 | 55 |
// providing timeout settings and authentication support, and also verifies the |
| 56 | 56 |
// remote API version. |
| 57 | 57 |
func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
|
| 58 |
- repoName := repoInfo.FullName() |
|
| 58 |
+ repoName := repoInfo.Name.Name() |
|
| 59 | 59 |
// If endpoint does not support CanonicalName, use the RemoteName instead |
| 60 | 60 |
if endpoint.TrimHostname {
|
| 61 |
- repoName = repoInfo.RemoteName() |
|
| 61 |
+ repoName = reference.Path(repoInfo.Name) |
|
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 | 64 |
direct := &net.Dialer{
|
| ... | ... |
@@ -122,7 +122,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end |
| 122 | 122 |
} |
| 123 | 123 |
tr := transport.NewTransport(base, modifiers...) |
| 124 | 124 |
|
| 125 |
- repoNameRef, err := distreference.ParseNamed(repoName) |
|
| 125 |
+ repoNameRef, err := reference.WithName(repoName) |
|
| 126 | 126 |
if err != nil {
|
| 127 | 127 |
return nil, foundVersion, fallbackError{
|
| 128 | 128 |
err: err, |
| ... | ... |
@@ -12,11 +12,11 @@ import ( |
| 12 | 12 |
"testing" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/Sirupsen/logrus" |
| 15 |
+ "github.com/docker/distribution/reference" |
|
| 15 | 16 |
"github.com/docker/docker/api/types" |
| 16 | 17 |
registrytypes "github.com/docker/docker/api/types/registry" |
| 17 | 18 |
"github.com/docker/docker/pkg/archive" |
| 18 | 19 |
"github.com/docker/docker/pkg/stringid" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 20 | 20 |
"github.com/docker/docker/registry" |
| 21 | 21 |
"golang.org/x/net/context" |
| 22 | 22 |
) |
| ... | ... |
@@ -61,9 +61,9 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
|
| 61 | 61 |
TrimHostname: false, |
| 62 | 62 |
TLSConfig: nil, |
| 63 | 63 |
} |
| 64 |
- n, _ := reference.ParseNamed("testremotename")
|
|
| 64 |
+ n, _ := reference.ParseNormalizedNamed("testremotename")
|
|
| 65 | 65 |
repoInfo := ®istry.RepositoryInfo{
|
| 66 |
- Named: n, |
|
| 66 |
+ Name: n, |
|
| 67 | 67 |
Index: ®istrytypes.IndexInfo{
|
| 68 | 68 |
Name: "testrepo", |
| 69 | 69 |
Mirrors: nil, |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
"github.com/Sirupsen/logrus" |
| 13 | 13 |
"github.com/docker/distribution" |
| 14 |
+ "github.com/docker/distribution/reference" |
|
| 14 | 15 |
"github.com/docker/docker/image" |
| 15 | 16 |
"github.com/docker/docker/image/v1" |
| 16 | 17 |
"github.com/docker/docker/layer" |
| ... | ... |
@@ -21,7 +22,6 @@ import ( |
| 21 | 21 |
"github.com/docker/docker/pkg/stringid" |
| 22 | 22 |
"github.com/docker/docker/pkg/symlink" |
| 23 | 23 |
"github.com/docker/docker/pkg/system" |
| 24 |
- "github.com/docker/docker/reference" |
|
| 25 | 24 |
"github.com/opencontainers/go-digest" |
| 26 | 25 |
) |
| 27 | 26 |
|
| ... | ... |
@@ -117,7 +117,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) |
| 117 | 117 |
|
| 118 | 118 |
imageRefCount = 0 |
| 119 | 119 |
for _, repoTag := range m.RepoTags {
|
| 120 |
- named, err := reference.ParseNamed(repoTag) |
|
| 120 |
+ named, err := reference.ParseNormalizedNamed(repoTag) |
|
| 121 | 121 |
if err != nil {
|
| 122 | 122 |
return err |
| 123 | 123 |
} |
| ... | ... |
@@ -126,7 +126,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) |
| 126 | 126 |
return fmt.Errorf("invalid tag %q", repoTag)
|
| 127 | 127 |
} |
| 128 | 128 |
l.setLoadedTag(ref, imgID.Digest(), outStream) |
| 129 |
- outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", ref)))
|
|
| 129 |
+ outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", reference.FamiliarString(ref))))
|
|
| 130 | 130 |
imageRefCount++ |
| 131 | 131 |
} |
| 132 | 132 |
|
| ... | ... |
@@ -201,7 +201,7 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, |
| 201 | 201 |
|
| 202 | 202 |
func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
|
| 203 | 203 |
if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
|
| 204 |
- fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", ref.String(), string(prevID)) // todo: this message is wrong in case of multiple tags |
|
| 204 |
+ fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", reference.FamiliarString(ref), string(prevID)) // todo: this message is wrong in case of multiple tags |
|
| 205 | 205 |
} |
| 206 | 206 |
|
| 207 | 207 |
if err := l.rs.AddTag(ref, imgID, true); err != nil {
|
| ... | ... |
@@ -249,7 +249,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut |
| 249 | 249 |
if !ok {
|
| 250 | 250 |
return fmt.Errorf("invalid target ID: %v", oldID)
|
| 251 | 251 |
} |
| 252 |
- named, err := reference.WithName(name) |
|
| 252 |
+ named, err := reference.ParseNormalizedNamed(name) |
|
| 253 | 253 |
if err != nil {
|
| 254 | 254 |
return err |
| 255 | 255 |
} |
| ... | ... |
@@ -10,12 +10,12 @@ import ( |
| 10 | 10 |
"time" |
| 11 | 11 |
|
| 12 | 12 |
"github.com/docker/distribution" |
| 13 |
+ "github.com/docker/distribution/reference" |
|
| 13 | 14 |
"github.com/docker/docker/image" |
| 14 | 15 |
"github.com/docker/docker/image/v1" |
| 15 | 16 |
"github.com/docker/docker/layer" |
| 16 | 17 |
"github.com/docker/docker/pkg/archive" |
| 17 | 18 |
"github.com/docker/docker/pkg/system" |
| 18 |
- "github.com/docker/docker/reference" |
|
| 19 | 19 |
"github.com/opencontainers/go-digest" |
| 20 | 20 |
"github.com/pkg/errors" |
| 21 | 21 |
) |
| ... | ... |
@@ -51,16 +51,12 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, |
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 | 53 |
if ref != nil {
|
| 54 |
- var tagged reference.NamedTagged |
|
| 55 | 54 |
if _, ok := ref.(reference.Canonical); ok {
|
| 56 | 55 |
return |
| 57 | 56 |
} |
| 58 |
- var ok bool |
|
| 59 |
- if tagged, ok = ref.(reference.NamedTagged); !ok {
|
|
| 60 |
- var err error |
|
| 61 |
- if tagged, err = reference.WithTag(ref, reference.DefaultTag); err != nil {
|
|
| 62 |
- return |
|
| 63 |
- } |
|
| 57 |
+ tagged, ok := reference.TagNameOnly(ref).(reference.NamedTagged) |
|
| 58 |
+ if !ok {
|
|
| 59 |
+ return |
|
| 64 | 60 |
} |
| 65 | 61 |
|
| 66 | 62 |
for _, t := range imgDescr[id].refs {
|
| ... | ... |
@@ -73,19 +69,26 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
for _, name := range names {
|
| 76 |
- id, ref, err := reference.ParseIDOrReference(name) |
|
| 76 |
+ ref, err := reference.ParseAnyReference(name) |
|
| 77 | 77 |
if err != nil {
|
| 78 | 78 |
return nil, err |
| 79 | 79 |
} |
| 80 |
- if id != "" {
|
|
| 81 |
- _, err := l.is.Get(image.IDFromDigest(id)) |
|
| 82 |
- if err != nil {
|
|
| 83 |
- return nil, err |
|
| 80 |
+ namedRef, ok := ref.(reference.Named) |
|
| 81 |
+ if !ok {
|
|
| 82 |
+ // Check if digest ID reference |
|
| 83 |
+ if digested, ok := ref.(reference.Digested); ok {
|
|
| 84 |
+ id := image.IDFromDigest(digested.Digest()) |
|
| 85 |
+ _, err := l.is.Get(id) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return nil, err |
|
| 88 |
+ } |
|
| 89 |
+ addAssoc(id, nil) |
|
| 90 |
+ continue |
|
| 84 | 91 |
} |
| 85 |
- addAssoc(image.IDFromDigest(id), nil) |
|
| 86 |
- continue |
|
| 92 |
+ return nil, errors.Errorf("invalid reference: %v", name)
|
|
| 87 | 93 |
} |
| 88 |
- if ref.Name() == string(digest.Canonical) {
|
|
| 94 |
+ |
|
| 95 |
+ if reference.FamiliarName(namedRef) == string(digest.Canonical) {
|
|
| 89 | 96 |
imgID, err := l.is.Search(name) |
| 90 | 97 |
if err != nil {
|
| 91 | 98 |
return nil, err |
| ... | ... |
@@ -93,8 +96,8 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, |
| 93 | 93 |
addAssoc(imgID, nil) |
| 94 | 94 |
continue |
| 95 | 95 |
} |
| 96 |
- if reference.IsNameOnly(ref) {
|
|
| 97 |
- assocs := l.rs.ReferencesByName(ref) |
|
| 96 |
+ if reference.IsNameOnly(namedRef) {
|
|
| 97 |
+ assocs := l.rs.ReferencesByName(namedRef) |
|
| 98 | 98 |
for _, assoc := range assocs {
|
| 99 | 99 |
addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref) |
| 100 | 100 |
} |
| ... | ... |
@@ -107,11 +110,11 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, |
| 107 | 107 |
} |
| 108 | 108 |
continue |
| 109 | 109 |
} |
| 110 |
- id, err = l.rs.Get(ref) |
|
| 110 |
+ id, err := l.rs.Get(namedRef) |
|
| 111 | 111 |
if err != nil {
|
| 112 | 112 |
return nil, err |
| 113 | 113 |
} |
| 114 |
- addAssoc(image.IDFromDigest(id), ref) |
|
| 114 |
+ addAssoc(image.IDFromDigest(id), namedRef) |
|
| 115 | 115 |
|
| 116 | 116 |
} |
| 117 | 117 |
return imgDescr, nil |
| ... | ... |
@@ -144,11 +147,12 @@ func (s *saveSession) save(outStream io.Writer) error {
|
| 144 | 144 |
var layers []string |
| 145 | 145 |
|
| 146 | 146 |
for _, ref := range imageDescr.refs {
|
| 147 |
- if _, ok := reposLegacy[ref.Name()]; !ok {
|
|
| 148 |
- reposLegacy[ref.Name()] = make(map[string]string) |
|
| 147 |
+ familiarName := reference.FamiliarName(ref) |
|
| 148 |
+ if _, ok := reposLegacy[familiarName]; !ok {
|
|
| 149 |
+ reposLegacy[familiarName] = make(map[string]string) |
|
| 149 | 150 |
} |
| 150 |
- reposLegacy[ref.Name()][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1] |
|
| 151 |
- repoTags = append(repoTags, ref.String()) |
|
| 151 |
+ reposLegacy[familiarName][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1] |
|
| 152 |
+ repoTags = append(repoTags, reference.FamiliarString(ref)) |
|
| 152 | 153 |
} |
| 153 | 154 |
|
| 154 | 155 |
for _, l := range imageDescr.layers {
|
| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"github.com/docker/distribution" |
| 5 | 5 |
"github.com/docker/docker/image" |
| 6 | 6 |
"github.com/docker/docker/layer" |
| 7 |
- "github.com/docker/docker/reference" |
|
| 7 |
+ refstore "github.com/docker/docker/reference" |
|
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 | 10 |
const ( |
| ... | ... |
@@ -26,7 +26,7 @@ type manifestItem struct {
|
| 26 | 26 |
type tarexporter struct {
|
| 27 | 27 |
is image.Store |
| 28 | 28 |
ls layer.Store |
| 29 |
- rs reference.Store |
|
| 29 |
+ rs refstore.Store |
|
| 30 | 30 |
loggerImgEvent LogImageEvent |
| 31 | 31 |
} |
| 32 | 32 |
|
| ... | ... |
@@ -37,7 +37,7 @@ type LogImageEvent interface {
|
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 | 39 |
// NewTarExporter returns new Exporter for tar packages |
| 40 |
-func NewTarExporter(is image.Store, ls layer.Store, rs reference.Store, loggerImgEvent LogImageEvent) image.Exporter {
|
|
| 40 |
+func NewTarExporter(is image.Store, ls layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
|
|
| 41 | 41 |
return &tarexporter{
|
| 42 | 42 |
is: is, |
| 43 | 43 |
ls: ls, |
| ... | ... |
@@ -335,7 +335,7 @@ func (s *DockerSuite) TestImagesFormat(c *check.C) {
|
| 335 | 335 |
expected := []string{"myimage", "myimage"}
|
| 336 | 336 |
var names []string |
| 337 | 337 |
names = append(names, lines...) |
| 338 |
- c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
|
|
| 338 |
+ c.Assert(names, checker.DeepEquals, expected, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
|
|
| 339 | 339 |
} |
| 340 | 340 |
|
| 341 | 341 |
// ImagesDefaultFormatAndQuiet |
| ... | ... |
@@ -14,12 +14,13 @@ import ( |
| 14 | 14 |
"encoding/json" |
| 15 | 15 |
|
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 |
+ "github.com/docker/distribution/reference" |
|
| 17 | 18 |
"github.com/docker/docker/distribution/metadata" |
| 18 | 19 |
"github.com/docker/docker/image" |
| 19 | 20 |
imagev1 "github.com/docker/docker/image/v1" |
| 20 | 21 |
"github.com/docker/docker/layer" |
| 21 | 22 |
"github.com/docker/docker/pkg/ioutils" |
| 22 |
- "github.com/docker/docker/reference" |
|
| 23 |
+ refstore "github.com/docker/docker/reference" |
|
| 23 | 24 |
"github.com/opencontainers/go-digest" |
| 24 | 25 |
) |
| 25 | 26 |
|
| ... | ... |
@@ -56,7 +57,7 @@ var ( |
| 56 | 56 |
|
| 57 | 57 |
// Migrate takes an old graph directory and transforms the metadata into the |
| 58 | 58 |
// new format. |
| 59 |
-func Migrate(root, driverName string, ls layer.Store, is image.Store, rs reference.Store, ms metadata.Store) error {
|
|
| 59 |
+func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error {
|
|
| 60 | 60 |
graphDir := filepath.Join(root, graphDirName) |
| 61 | 61 |
if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
|
| 62 | 62 |
return nil |
| ... | ... |
@@ -322,11 +323,15 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image |
| 322 | 322 |
for name, repo := range repos.Repositories {
|
| 323 | 323 |
for tag, id := range repo {
|
| 324 | 324 |
if strongID, exists := mappings[id]; exists {
|
| 325 |
- ref, err := reference.WithName(name) |
|
| 325 |
+ ref, err := reference.ParseNormalizedNamed(name) |
|
| 326 | 326 |
if err != nil {
|
| 327 | 327 |
logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
|
| 328 | 328 |
continue |
| 329 | 329 |
} |
| 330 |
+ if !reference.IsNameOnly(ref) {
|
|
| 331 |
+ logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name)
|
|
| 332 |
+ continue |
|
| 333 |
+ } |
|
| 330 | 334 |
if dgst, err := digest.Parse(tag); err == nil {
|
| 331 | 335 |
canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst) |
| 332 | 336 |
if err != nil {
|
| ... | ... |
@@ -334,7 +339,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image |
| 334 | 334 |
continue |
| 335 | 335 |
} |
| 336 | 336 |
if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil {
|
| 337 |
- logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err)
|
|
| 337 |
+ logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
|
|
| 338 | 338 |
} |
| 339 | 339 |
} else {
|
| 340 | 340 |
tagRef, err := reference.WithTag(ref, tag) |
| ... | ... |
@@ -343,7 +348,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image |
| 343 | 343 |
continue |
| 344 | 344 |
} |
| 345 | 345 |
if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil {
|
| 346 |
- logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
|
|
| 346 |
+ logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
|
|
| 347 | 347 |
} |
| 348 | 348 |
} |
| 349 | 349 |
logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)
|
| ... | ... |
@@ -13,10 +13,10 @@ import ( |
| 13 | 13 |
"runtime" |
| 14 | 14 |
"testing" |
| 15 | 15 |
|
| 16 |
+ "github.com/docker/distribution/reference" |
|
| 16 | 17 |
"github.com/docker/docker/distribution/metadata" |
| 17 | 18 |
"github.com/docker/docker/image" |
| 18 | 19 |
"github.com/docker/docker/layer" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 20 | 20 |
"github.com/opencontainers/go-digest" |
| 21 | 21 |
) |
| 22 | 22 |
|
| ... | ... |
@@ -40,9 +40,9 @@ func TestMigrateRefs(t *testing.T) {
|
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 | 42 |
expected := map[string]string{
|
| 43 |
- "busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", |
|
| 44 |
- "busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", |
|
| 45 |
- "registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", |
|
| 43 |
+ "docker.io/library/busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", |
|
| 44 |
+ "docker.io/library/busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", |
|
| 45 |
+ "docker.io/library/registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", |
|
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 | 48 |
if !reflect.DeepEqual(expected, ta.refs) {
|
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
|
| 19 | 19 |
"github.com/Sirupsen/logrus" |
| 20 | 20 |
"github.com/docker/distribution/manifest/schema2" |
| 21 |
+ "github.com/docker/distribution/reference" |
|
| 21 | 22 |
"github.com/docker/docker/api/types" |
| 22 | 23 |
"github.com/docker/docker/api/types/filters" |
| 23 | 24 |
"github.com/docker/docker/distribution" |
| ... | ... |
@@ -30,7 +31,7 @@ import ( |
| 30 | 30 |
"github.com/docker/docker/pkg/pools" |
| 31 | 31 |
"github.com/docker/docker/pkg/progress" |
| 32 | 32 |
"github.com/docker/docker/plugin/v2" |
| 33 |
- "github.com/docker/docker/reference" |
|
| 33 |
+ refstore "github.com/docker/docker/reference" |
|
| 34 | 34 |
"github.com/opencontainers/go-digest" |
| 35 | 35 |
"github.com/pkg/errors" |
| 36 | 36 |
"golang.org/x/net/context" |
| ... | ... |
@@ -232,11 +233,11 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string |
| 232 | 232 |
defer pm.muGC.RUnlock() |
| 233 | 233 |
|
| 234 | 234 |
// revalidate because Pull is public |
| 235 |
- nameref, err := reference.ParseNamed(name) |
|
| 235 |
+ nameref, err := reference.ParseNormalizedNamed(name) |
|
| 236 | 236 |
if err != nil {
|
| 237 | 237 |
return errors.Wrapf(err, "failed to parse %q", name) |
| 238 | 238 |
} |
| 239 |
- name = reference.WithDefaultTag(nameref).String() |
|
| 239 |
+ name = reference.FamiliarString(reference.TagNameOnly(nameref)) |
|
| 240 | 240 |
|
| 241 | 241 |
tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs") |
| 242 | 242 |
defer os.RemoveAll(tmpRootFSDir) |
| ... | ... |
@@ -277,11 +278,11 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m |
| 277 | 277 |
defer pm.muGC.RUnlock() |
| 278 | 278 |
|
| 279 | 279 |
// revalidate because Pull is public |
| 280 |
- nameref, err := reference.ParseNamed(name) |
|
| 280 |
+ nameref, err := reference.ParseNormalizedNamed(name) |
|
| 281 | 281 |
if err != nil {
|
| 282 | 282 |
return errors.Wrapf(err, "failed to parse %q", name) |
| 283 | 283 |
} |
| 284 |
- name = reference.WithDefaultTag(nameref).String() |
|
| 284 |
+ name = reference.FamiliarString(reference.TagNameOnly(nameref)) |
|
| 285 | 285 |
|
| 286 | 286 |
if err := pm.config.Store.validateName(name); err != nil {
|
| 287 | 287 |
return err |
| ... | ... |
@@ -370,7 +371,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header |
| 370 | 370 |
return err |
| 371 | 371 |
} |
| 372 | 372 |
|
| 373 |
- ref, err := reference.ParseNamed(p.Name()) |
|
| 373 |
+ ref, err := reference.ParseNormalizedNamed(p.Name()) |
|
| 374 | 374 |
if err != nil {
|
| 375 | 375 |
return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name()) |
| 376 | 376 |
} |
| ... | ... |
@@ -448,8 +449,8 @@ func (r *pluginReference) References(id digest.Digest) []reference.Named {
|
| 448 | 448 |
return []reference.Named{r.name}
|
| 449 | 449 |
} |
| 450 | 450 |
|
| 451 |
-func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Association {
|
|
| 452 |
- return []reference.Association{
|
|
| 451 |
+func (r *pluginReference) ReferencesByName(ref reference.Named) []refstore.Association {
|
|
| 452 |
+ return []refstore.Association{
|
|
| 453 | 453 |
{
|
| 454 | 454 |
Ref: r.name, |
| 455 | 455 |
ID: r.pluginID, |
| ... | ... |
@@ -459,7 +460,7 @@ func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Asso |
| 459 | 459 |
|
| 460 | 460 |
func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) {
|
| 461 | 461 |
if r.name.String() != ref.String() {
|
| 462 |
- return digest.Digest(""), reference.ErrDoesNotExist
|
|
| 462 |
+ return digest.Digest(""), refstore.ErrDoesNotExist
|
|
| 463 | 463 |
} |
| 464 | 464 |
return r.pluginID, nil |
| 465 | 465 |
} |
| ... | ... |
@@ -664,15 +665,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, |
| 664 | 664 |
pm.muGC.RLock() |
| 665 | 665 |
defer pm.muGC.RUnlock() |
| 666 | 666 |
|
| 667 |
- ref, err := reference.ParseNamed(options.RepoName) |
|
| 667 |
+ ref, err := reference.ParseNormalizedNamed(options.RepoName) |
|
| 668 | 668 |
if err != nil {
|
| 669 | 669 |
return errors.Wrapf(err, "failed to parse reference %v", options.RepoName) |
| 670 | 670 |
} |
| 671 | 671 |
if _, ok := ref.(reference.Canonical); ok {
|
| 672 | 672 |
return errors.Errorf("canonical references are not permitted")
|
| 673 | 673 |
} |
| 674 |
- taggedRef := reference.WithDefaultTag(ref) |
|
| 675 |
- name := taggedRef.String() |
|
| 674 |
+ name := reference.FamiliarString(reference.TagNameOnly(ref)) |
|
| 676 | 675 |
|
| 677 | 676 |
if err := pm.config.Store.validateName(name); err != nil { // fast check, real check is in createPlugin()
|
| 678 | 677 |
return err |
| ... | ... |
@@ -754,7 +754,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, |
| 754 | 754 |
if err != nil {
|
| 755 | 755 |
return err |
| 756 | 756 |
} |
| 757 |
- p.PluginObj.PluginReference = taggedRef.String() |
|
| 757 |
+ p.PluginObj.PluginReference = name |
|
| 758 | 758 |
|
| 759 | 759 |
pm.config.LogPluginEvent(p.PluginObj.ID, name, "create") |
| 760 | 760 |
|
| ... | ... |
@@ -5,10 +5,10 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/Sirupsen/logrus" |
| 8 |
+ "github.com/docker/distribution/reference" |
|
| 8 | 9 |
"github.com/docker/docker/pkg/plugingetter" |
| 9 | 10 |
"github.com/docker/docker/pkg/plugins" |
| 10 | 11 |
"github.com/docker/docker/plugin/v2" |
| 11 |
- "github.com/docker/docker/reference" |
|
| 12 | 12 |
"github.com/pkg/errors" |
| 13 | 13 |
) |
| 14 | 14 |
|
| ... | ... |
@@ -230,19 +230,19 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
|
| 230 | 230 |
return idOrName, nil |
| 231 | 231 |
} |
| 232 | 232 |
|
| 233 |
- ref, err := reference.ParseNamed(idOrName) |
|
| 233 |
+ ref, err := reference.ParseNormalizedNamed(idOrName) |
|
| 234 | 234 |
if err != nil {
|
| 235 | 235 |
return "", errors.WithStack(ErrNotFound(idOrName)) |
| 236 | 236 |
} |
| 237 | 237 |
if _, ok := ref.(reference.Canonical); ok {
|
| 238 |
- logrus.Warnf("canonical references cannot be resolved: %v", ref.String())
|
|
| 238 |
+ logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
|
|
| 239 | 239 |
return "", errors.WithStack(ErrNotFound(idOrName)) |
| 240 | 240 |
} |
| 241 | 241 |
|
| 242 |
- fullRef := reference.WithDefaultTag(ref) |
|
| 242 |
+ ref = reference.TagNameOnly(ref) |
|
| 243 | 243 |
|
| 244 | 244 |
for _, p := range ps.plugins {
|
| 245 |
- if p.PluginObj.Name == fullRef.String() {
|
|
| 245 |
+ if p.PluginObj.Name == reference.FamiliarString(ref) {
|
|
| 246 | 246 |
return p.PluginObj.ID, nil |
| 247 | 247 |
} |
| 248 | 248 |
} |
| 249 | 249 |
deleted file mode 100644 |
| ... | ... |
@@ -1,194 +0,0 @@ |
| 1 |
-package reference |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- |
|
| 6 |
- distreference "github.com/docker/distribution/reference" |
|
| 7 |
- "github.com/docker/docker/pkg/stringid" |
|
| 8 |
- "github.com/opencontainers/go-digest" |
|
| 9 |
- "github.com/pkg/errors" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 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 |
-) |
|
| 22 |
- |
|
| 23 |
-// Named is an object with a full name |
|
| 24 |
-type Named interface {
|
|
| 25 |
- // Name returns normalized repository name, like "ubuntu". |
|
| 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 |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-// NamedTagged is an object including a name and tag. |
|
| 38 |
-type NamedTagged interface {
|
|
| 39 |
- Named |
|
| 40 |
- Tag() string |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-// Canonical reference is an object with a fully unique |
|
| 44 |
-// name including a name with hostname and digest |
|
| 45 |
-type Canonical interface {
|
|
| 46 |
- Named |
|
| 47 |
- Digest() digest.Digest |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-// ParseNamed parses s and returns a syntactically valid reference implementing |
|
| 51 |
-// the Named interface. The reference must have a name, otherwise an error is |
|
| 52 |
-// returned. |
|
| 53 |
-// If an error was encountered it is returned, along with a nil Reference. |
|
| 54 |
-func ParseNamed(s string) (Named, error) {
|
|
| 55 |
- named, err := distreference.ParseNormalizedNamed(s) |
|
| 56 |
- if err != nil {
|
|
| 57 |
- return nil, errors.Wrapf(err, "failed to parse reference %q", s) |
|
| 58 |
- } |
|
| 59 |
- if err := validateName(distreference.FamiliarName(named)); err != nil {
|
|
| 60 |
- return nil, err |
|
| 61 |
- } |
|
| 62 |
- |
|
| 63 |
- // Ensure returned reference cannot have tag and digest |
|
| 64 |
- if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
|
|
| 65 |
- r, err := distreference.WithDigest(distreference.TrimNamed(named), canonical.Digest()) |
|
| 66 |
- if err != nil {
|
|
| 67 |
- return nil, err |
|
| 68 |
- } |
|
| 69 |
- return &canonicalRef{namedRef{r}}, nil
|
|
| 70 |
- } |
|
| 71 |
- if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
|
|
| 72 |
- r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag()) |
|
| 73 |
- if err != nil {
|
|
| 74 |
- return nil, err |
|
| 75 |
- } |
|
| 76 |
- return &taggedRef{namedRef{r}}, nil
|
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- return &namedRef{named}, nil
|
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// TrimNamed removes any tag or digest from the named reference |
|
| 83 |
-func TrimNamed(ref Named) Named {
|
|
| 84 |
- return &namedRef{distreference.TrimNamed(ref)}
|
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-// WithName returns a named object representing the given string. If the input |
|
| 88 |
-// is invalid ErrReferenceInvalidFormat will be returned. |
|
| 89 |
-func WithName(name string) (Named, error) {
|
|
| 90 |
- r, err := distreference.ParseNormalizedNamed(name) |
|
| 91 |
- if err != nil {
|
|
| 92 |
- return nil, err |
|
| 93 |
- } |
|
| 94 |
- if err := validateName(distreference.FamiliarName(r)); err != nil {
|
|
| 95 |
- return nil, err |
|
| 96 |
- } |
|
| 97 |
- if !distreference.IsNameOnly(r) {
|
|
| 98 |
- return nil, distreference.ErrReferenceInvalidFormat |
|
| 99 |
- } |
|
| 100 |
- return &namedRef{r}, nil
|
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-// WithTag combines the name from "name" and the tag from "tag" to form a |
|
| 104 |
-// reference incorporating both the name and the tag. |
|
| 105 |
-func WithTag(name Named, tag string) (NamedTagged, error) {
|
|
| 106 |
- r, err := distreference.WithTag(name, tag) |
|
| 107 |
- if err != nil {
|
|
| 108 |
- return nil, err |
|
| 109 |
- } |
|
| 110 |
- return &taggedRef{namedRef{r}}, nil
|
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-// WithDigest combines the name from "name" and the digest from "digest" to form |
|
| 114 |
-// a reference incorporating both the name and the digest. |
|
| 115 |
-func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
|
| 116 |
- r, err := distreference.WithDigest(name, digest) |
|
| 117 |
- if err != nil {
|
|
| 118 |
- return nil, err |
|
| 119 |
- } |
|
| 120 |
- return &canonicalRef{namedRef{r}}, nil
|
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-type namedRef struct {
|
|
| 124 |
- distreference.Named |
|
| 125 |
-} |
|
| 126 |
-type taggedRef struct {
|
|
| 127 |
- namedRef |
|
| 128 |
-} |
|
| 129 |
-type canonicalRef struct {
|
|
| 130 |
- namedRef |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func (r *namedRef) Name() string {
|
|
| 134 |
- return distreference.FamiliarName(r.Named) |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-func (r *namedRef) String() string {
|
|
| 138 |
- return distreference.FamiliarString(r.Named) |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func (r *namedRef) FullName() string {
|
|
| 142 |
- return r.Named.Name() |
|
| 143 |
-} |
|
| 144 |
-func (r *namedRef) Hostname() string {
|
|
| 145 |
- return distreference.Domain(r.Named) |
|
| 146 |
-} |
|
| 147 |
-func (r *namedRef) RemoteName() string {
|
|
| 148 |
- return distreference.Path(r.Named) |
|
| 149 |
-} |
|
| 150 |
-func (r *taggedRef) Tag() string {
|
|
| 151 |
- return r.namedRef.Named.(distreference.NamedTagged).Tag() |
|
| 152 |
-} |
|
| 153 |
-func (r *canonicalRef) Digest() digest.Digest {
|
|
| 154 |
- return r.namedRef.Named.(distreference.Canonical).Digest() |
|
| 155 |
-} |
|
| 156 |
- |
|
| 157 |
-// WithDefaultTag adds a default tag to a reference if it only has a repo name. |
|
| 158 |
-func WithDefaultTag(ref Named) Named {
|
|
| 159 |
- if IsNameOnly(ref) {
|
|
| 160 |
- ref, _ = WithTag(ref, DefaultTag) |
|
| 161 |
- } |
|
| 162 |
- return ref |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-// IsNameOnly returns true if reference only contains a repo name. |
|
| 166 |
-func IsNameOnly(ref Named) bool {
|
|
| 167 |
- if _, ok := ref.(NamedTagged); ok {
|
|
| 168 |
- return false |
|
| 169 |
- } |
|
| 170 |
- if _, ok := ref.(Canonical); ok {
|
|
| 171 |
- return false |
|
| 172 |
- } |
|
| 173 |
- return true |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-// ParseIDOrReference parses string for an image ID or a reference. ID can be |
|
| 177 |
-// without a default prefix. |
|
| 178 |
-func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
|
|
| 179 |
- if err := stringid.ValidateID(idOrRef); err == nil {
|
|
| 180 |
- idOrRef = "sha256:" + idOrRef |
|
| 181 |
- } |
|
| 182 |
- if dgst, err := digest.Parse(idOrRef); err == nil {
|
|
| 183 |
- return dgst, nil, nil |
|
| 184 |
- } |
|
| 185 |
- ref, err := ParseNamed(idOrRef) |
|
| 186 |
- return "", ref, err |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-func validateName(name string) error {
|
|
| 190 |
- if err := stringid.ValidateID(name); err == nil {
|
|
| 191 |
- return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
|
|
| 192 |
- } |
|
| 193 |
- return nil |
|
| 194 |
-} |
| 195 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,275 +0,0 @@ |
| 1 |
-package reference |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
- |
|
| 6 |
- "github.com/opencontainers/go-digest" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func TestValidateReferenceName(t *testing.T) {
|
|
| 10 |
- validRepoNames := []string{
|
|
| 11 |
- "docker/docker", |
|
| 12 |
- "library/debian", |
|
| 13 |
- "debian", |
|
| 14 |
- "docker.io/docker/docker", |
|
| 15 |
- "docker.io/library/debian", |
|
| 16 |
- "docker.io/debian", |
|
| 17 |
- "index.docker.io/docker/docker", |
|
| 18 |
- "index.docker.io/library/debian", |
|
| 19 |
- "index.docker.io/debian", |
|
| 20 |
- "127.0.0.1:5000/docker/docker", |
|
| 21 |
- "127.0.0.1:5000/library/debian", |
|
| 22 |
- "127.0.0.1:5000/debian", |
|
| 23 |
- "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 24 |
- } |
|
| 25 |
- invalidRepoNames := []string{
|
|
| 26 |
- "https://github.com/docker/docker", |
|
| 27 |
- "docker/Docker", |
|
| 28 |
- "-docker", |
|
| 29 |
- "-docker/docker", |
|
| 30 |
- "-docker.io/docker/docker", |
|
| 31 |
- "docker///docker", |
|
| 32 |
- "docker.io/docker/Docker", |
|
| 33 |
- "docker.io/docker///docker", |
|
| 34 |
- "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 35 |
- "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 36 |
- } |
|
| 37 |
- |
|
| 38 |
- for _, name := range invalidRepoNames {
|
|
| 39 |
- _, err := ParseNamed(name) |
|
| 40 |
- if err == nil {
|
|
| 41 |
- t.Fatalf("Expected invalid repo name for %q", name)
|
|
| 42 |
- } |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- for _, name := range validRepoNames {
|
|
| 46 |
- _, err := ParseNamed(name) |
|
| 47 |
- if err != nil {
|
|
| 48 |
- t.Fatalf("Error parsing repo name %s, got: %q", name, err)
|
|
| 49 |
- } |
|
| 50 |
- } |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-func TestValidateRemoteName(t *testing.T) {
|
|
| 54 |
- validRepositoryNames := []string{
|
|
| 55 |
- // Sanity check. |
|
| 56 |
- "docker/docker", |
|
| 57 |
- |
|
| 58 |
- // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). |
|
| 59 |
- "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", |
|
| 60 |
- |
|
| 61 |
- // Allow embedded hyphens. |
|
| 62 |
- "docker-rules/docker", |
|
| 63 |
- |
|
| 64 |
- // Allow multiple hyphens as well. |
|
| 65 |
- "docker---rules/docker", |
|
| 66 |
- |
|
| 67 |
- //Username doc and image name docker being tested. |
|
| 68 |
- "doc/docker", |
|
| 69 |
- |
|
| 70 |
- // single character names are now allowed. |
|
| 71 |
- "d/docker", |
|
| 72 |
- "jess/t", |
|
| 73 |
- |
|
| 74 |
- // Consecutive underscores. |
|
| 75 |
- "dock__er/docker", |
|
| 76 |
- } |
|
| 77 |
- for _, repositoryName := range validRepositoryNames {
|
|
| 78 |
- _, err := ParseNamed(repositoryName) |
|
| 79 |
- if err != nil {
|
|
| 80 |
- t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
| 81 |
- } |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- invalidRepositoryNames := []string{
|
|
| 85 |
- // Disallow capital letters. |
|
| 86 |
- "docker/Docker", |
|
| 87 |
- |
|
| 88 |
- // Only allow one slash. |
|
| 89 |
- "docker///docker", |
|
| 90 |
- |
|
| 91 |
- // Disallow 64-character hexadecimal. |
|
| 92 |
- "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", |
|
| 93 |
- |
|
| 94 |
- // Disallow leading and trailing hyphens in namespace. |
|
| 95 |
- "-docker/docker", |
|
| 96 |
- "docker-/docker", |
|
| 97 |
- "-docker-/docker", |
|
| 98 |
- |
|
| 99 |
- // Don't allow underscores everywhere (as opposed to hyphens). |
|
| 100 |
- "____/____", |
|
| 101 |
- |
|
| 102 |
- "_docker/_docker", |
|
| 103 |
- |
|
| 104 |
- // Disallow consecutive periods. |
|
| 105 |
- "dock..er/docker", |
|
| 106 |
- "dock_.er/docker", |
|
| 107 |
- "dock-.er/docker", |
|
| 108 |
- |
|
| 109 |
- // No repository. |
|
| 110 |
- "docker/", |
|
| 111 |
- |
|
| 112 |
- //namespace too long |
|
| 113 |
- "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", |
|
| 114 |
- } |
|
| 115 |
- for _, repositoryName := range invalidRepositoryNames {
|
|
| 116 |
- if _, err := ParseNamed(repositoryName); err == nil {
|
|
| 117 |
- t.Errorf("Repository name should be invalid: %v", repositoryName)
|
|
| 118 |
- } |
|
| 119 |
- } |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-func TestParseRepositoryInfo(t *testing.T) {
|
|
| 123 |
- type tcase struct {
|
|
| 124 |
- RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string |
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- tcases := []tcase{
|
|
| 128 |
- {
|
|
| 129 |
- RemoteName: "fooo/bar", |
|
| 130 |
- NormalizedName: "fooo/bar", |
|
| 131 |
- FullName: "docker.io/fooo/bar", |
|
| 132 |
- AmbiguousName: "index.docker.io/fooo/bar", |
|
| 133 |
- Hostname: "docker.io", |
|
| 134 |
- }, |
|
| 135 |
- {
|
|
| 136 |
- RemoteName: "library/ubuntu", |
|
| 137 |
- NormalizedName: "ubuntu", |
|
| 138 |
- FullName: "docker.io/library/ubuntu", |
|
| 139 |
- AmbiguousName: "library/ubuntu", |
|
| 140 |
- Hostname: "docker.io", |
|
| 141 |
- }, |
|
| 142 |
- {
|
|
| 143 |
- RemoteName: "nonlibrary/ubuntu", |
|
| 144 |
- NormalizedName: "nonlibrary/ubuntu", |
|
| 145 |
- FullName: "docker.io/nonlibrary/ubuntu", |
|
| 146 |
- AmbiguousName: "", |
|
| 147 |
- Hostname: "docker.io", |
|
| 148 |
- }, |
|
| 149 |
- {
|
|
| 150 |
- RemoteName: "other/library", |
|
| 151 |
- NormalizedName: "other/library", |
|
| 152 |
- FullName: "docker.io/other/library", |
|
| 153 |
- AmbiguousName: "", |
|
| 154 |
- Hostname: "docker.io", |
|
| 155 |
- }, |
|
| 156 |
- {
|
|
| 157 |
- RemoteName: "private/moonbase", |
|
| 158 |
- NormalizedName: "127.0.0.1:8000/private/moonbase", |
|
| 159 |
- FullName: "127.0.0.1:8000/private/moonbase", |
|
| 160 |
- AmbiguousName: "", |
|
| 161 |
- Hostname: "127.0.0.1:8000", |
|
| 162 |
- }, |
|
| 163 |
- {
|
|
| 164 |
- RemoteName: "privatebase", |
|
| 165 |
- NormalizedName: "127.0.0.1:8000/privatebase", |
|
| 166 |
- FullName: "127.0.0.1:8000/privatebase", |
|
| 167 |
- AmbiguousName: "", |
|
| 168 |
- Hostname: "127.0.0.1:8000", |
|
| 169 |
- }, |
|
| 170 |
- {
|
|
| 171 |
- RemoteName: "private/moonbase", |
|
| 172 |
- NormalizedName: "example.com/private/moonbase", |
|
| 173 |
- FullName: "example.com/private/moonbase", |
|
| 174 |
- AmbiguousName: "", |
|
| 175 |
- Hostname: "example.com", |
|
| 176 |
- }, |
|
| 177 |
- {
|
|
| 178 |
- RemoteName: "privatebase", |
|
| 179 |
- NormalizedName: "example.com/privatebase", |
|
| 180 |
- FullName: "example.com/privatebase", |
|
| 181 |
- AmbiguousName: "", |
|
| 182 |
- Hostname: "example.com", |
|
| 183 |
- }, |
|
| 184 |
- {
|
|
| 185 |
- RemoteName: "private/moonbase", |
|
| 186 |
- NormalizedName: "example.com:8000/private/moonbase", |
|
| 187 |
- FullName: "example.com:8000/private/moonbase", |
|
| 188 |
- AmbiguousName: "", |
|
| 189 |
- Hostname: "example.com:8000", |
|
| 190 |
- }, |
|
| 191 |
- {
|
|
| 192 |
- RemoteName: "privatebasee", |
|
| 193 |
- NormalizedName: "example.com:8000/privatebasee", |
|
| 194 |
- FullName: "example.com:8000/privatebasee", |
|
| 195 |
- AmbiguousName: "", |
|
| 196 |
- Hostname: "example.com:8000", |
|
| 197 |
- }, |
|
| 198 |
- {
|
|
| 199 |
- RemoteName: "library/ubuntu-12.04-base", |
|
| 200 |
- NormalizedName: "ubuntu-12.04-base", |
|
| 201 |
- FullName: "docker.io/library/ubuntu-12.04-base", |
|
| 202 |
- AmbiguousName: "index.docker.io/library/ubuntu-12.04-base", |
|
| 203 |
- Hostname: "docker.io", |
|
| 204 |
- }, |
|
| 205 |
- } |
|
| 206 |
- |
|
| 207 |
- for _, tcase := range tcases {
|
|
| 208 |
- refStrings := []string{tcase.NormalizedName, tcase.FullName}
|
|
| 209 |
- if tcase.AmbiguousName != "" {
|
|
| 210 |
- refStrings = append(refStrings, tcase.AmbiguousName) |
|
| 211 |
- } |
|
| 212 |
- |
|
| 213 |
- var refs []Named |
|
| 214 |
- for _, r := range refStrings {
|
|
| 215 |
- named, err := ParseNamed(r) |
|
| 216 |
- if err != nil {
|
|
| 217 |
- t.Fatal(err) |
|
| 218 |
- } |
|
| 219 |
- refs = append(refs, named) |
|
| 220 |
- named, err = WithName(r) |
|
| 221 |
- if err != nil {
|
|
| 222 |
- t.Fatal(err) |
|
| 223 |
- } |
|
| 224 |
- refs = append(refs, named) |
|
| 225 |
- } |
|
| 226 |
- |
|
| 227 |
- for _, r := range refs {
|
|
| 228 |
- if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
|
|
| 229 |
- t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
|
| 230 |
- } |
|
| 231 |
- if expected, actual := tcase.FullName, r.FullName(); expected != actual {
|
|
| 232 |
- t.Fatalf("Invalid fullName for %q. Expected %q, got %q", r, expected, actual)
|
|
| 233 |
- } |
|
| 234 |
- if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
|
|
| 235 |
- t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
|
|
| 236 |
- } |
|
| 237 |
- if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
|
|
| 238 |
- t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
|
|
| 239 |
- } |
|
| 240 |
- |
|
| 241 |
- } |
|
| 242 |
- } |
|
| 243 |
-} |
|
| 244 |
- |
|
| 245 |
-func TestParseReferenceWithTagAndDigest(t *testing.T) {
|
|
| 246 |
- ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
|
|
| 247 |
- if err != nil {
|
|
| 248 |
- t.Fatal(err) |
|
| 249 |
- } |
|
| 250 |
- if _, isTagged := ref.(NamedTagged); isTagged {
|
|
| 251 |
- t.Fatalf("Reference from %q should not support tag", ref)
|
|
| 252 |
- } |
|
| 253 |
- if _, isCanonical := ref.(Canonical); !isCanonical {
|
|
| 254 |
- t.Fatalf("Reference from %q should not support digest", ref)
|
|
| 255 |
- } |
|
| 256 |
- if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
|
|
| 257 |
- t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
|
| 258 |
- } |
|
| 259 |
-} |
|
| 260 |
- |
|
| 261 |
-func TestInvalidReferenceComponents(t *testing.T) {
|
|
| 262 |
- if _, err := WithName("-foo"); err == nil {
|
|
| 263 |
- t.Fatal("Expected WithName to detect invalid name")
|
|
| 264 |
- } |
|
| 265 |
- ref, err := WithName("busybox")
|
|
| 266 |
- if err != nil {
|
|
| 267 |
- t.Fatal(err) |
|
| 268 |
- } |
|
| 269 |
- if _, err := WithTag(ref, "-foo"); err == nil {
|
|
| 270 |
- t.Fatal("Expected WithName to detect invalid tag")
|
|
| 271 |
- } |
|
| 272 |
- if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
|
|
| 273 |
- t.Fatal("Expected WithName to detect invalid digest")
|
|
| 274 |
- } |
|
| 275 |
-} |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"sort" |
| 10 | 10 |
"sync" |
| 11 | 11 |
|
| 12 |
+ "github.com/docker/distribution/reference" |
|
| 12 | 13 |
"github.com/docker/docker/pkg/ioutils" |
| 13 | 14 |
"github.com/opencontainers/go-digest" |
| 14 | 15 |
) |
| ... | ... |
@@ -21,18 +22,18 @@ var ( |
| 21 | 21 |
|
| 22 | 22 |
// An Association is a tuple associating a reference with an image ID. |
| 23 | 23 |
type Association struct {
|
| 24 |
- Ref Named |
|
| 24 |
+ Ref reference.Named |
|
| 25 | 25 |
ID digest.Digest |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 | 28 |
// Store provides the set of methods which can operate on a tag store. |
| 29 | 29 |
type Store interface {
|
| 30 |
- References(id digest.Digest) []Named |
|
| 31 |
- ReferencesByName(ref Named) []Association |
|
| 32 |
- AddTag(ref Named, id digest.Digest, force bool) error |
|
| 33 |
- AddDigest(ref Canonical, id digest.Digest, force bool) error |
|
| 34 |
- Delete(ref Named) (bool, error) |
|
| 35 |
- Get(ref Named) (digest.Digest, error) |
|
| 30 |
+ References(id digest.Digest) []reference.Named |
|
| 31 |
+ ReferencesByName(ref reference.Named) []Association |
|
| 32 |
+ AddTag(ref reference.Named, id digest.Digest, force bool) error |
|
| 33 |
+ AddDigest(ref reference.Canonical, id digest.Digest, force bool) error |
|
| 34 |
+ Delete(ref reference.Named) (bool, error) |
|
| 35 |
+ Get(ref reference.Named) (digest.Digest, error) |
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
type store struct {
|
| ... | ... |
@@ -44,24 +45,28 @@ type store struct {
|
| 44 | 44 |
Repositories map[string]repository |
| 45 | 45 |
// referencesByIDCache is a cache of references indexed by ID, to speed |
| 46 | 46 |
// up References. |
| 47 |
- referencesByIDCache map[digest.Digest]map[string]Named |
|
| 47 |
+ referencesByIDCache map[digest.Digest]map[string]reference.Named |
|
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 | 50 |
// Repository maps tags to digests. The key is a stringified Reference, |
| 51 | 51 |
// including the repository name. |
| 52 | 52 |
type repository map[string]digest.Digest |
| 53 | 53 |
|
| 54 |
-type lexicalRefs []Named |
|
| 54 |
+type lexicalRefs []reference.Named |
|
| 55 | 55 |
|
| 56 |
-func (a lexicalRefs) Len() int { return len(a) }
|
|
| 57 |
-func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
| 58 |
-func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() }
|
|
| 56 |
+func (a lexicalRefs) Len() int { return len(a) }
|
|
| 57 |
+func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
| 58 |
+func (a lexicalRefs) Less(i, j int) bool {
|
|
| 59 |
+ return a[i].String() < a[j].String() |
|
| 60 |
+} |
|
| 59 | 61 |
|
| 60 | 62 |
type lexicalAssociations []Association |
| 61 | 63 |
|
| 62 |
-func (a lexicalAssociations) Len() int { return len(a) }
|
|
| 63 |
-func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
| 64 |
-func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() }
|
|
| 64 |
+func (a lexicalAssociations) Len() int { return len(a) }
|
|
| 65 |
+func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
| 66 |
+func (a lexicalAssociations) Less(i, j int) bool {
|
|
| 67 |
+ return a[i].Ref.String() < a[j].Ref.String() |
|
| 68 |
+} |
|
| 65 | 69 |
|
| 66 | 70 |
// NewReferenceStore creates a new reference store, tied to a file path where |
| 67 | 71 |
// the set of references are serialized in JSON format. |
| ... | ... |
@@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) {
|
| 74 | 74 |
store := &store{
|
| 75 | 75 |
jsonPath: abspath, |
| 76 | 76 |
Repositories: make(map[string]repository), |
| 77 |
- referencesByIDCache: make(map[digest.Digest]map[string]Named), |
|
| 77 |
+ referencesByIDCache: make(map[digest.Digest]map[string]reference.Named), |
|
| 78 | 78 |
} |
| 79 | 79 |
// Load the json file if it exists, otherwise create it. |
| 80 | 80 |
if err := store.reload(); os.IsNotExist(err) {
|
| ... | ... |
@@ -89,43 +94,45 @@ func NewReferenceStore(jsonPath string) (Store, error) {
|
| 89 | 89 |
|
| 90 | 90 |
// AddTag adds a tag reference to the store. If force is set to true, existing |
| 91 | 91 |
// references can be overwritten. This only works for tags, not digests. |
| 92 |
-func (store *store) AddTag(ref Named, id digest.Digest, force bool) error {
|
|
| 93 |
- if _, isCanonical := ref.(Canonical); isCanonical {
|
|
| 92 |
+func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error {
|
|
| 93 |
+ if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
| 94 | 94 |
return errors.New("refusing to create a tag with a digest reference")
|
| 95 | 95 |
} |
| 96 |
- return store.addReference(WithDefaultTag(ref), id, force) |
|
| 96 |
+ return store.addReference(reference.TagNameOnly(ref), id, force) |
|
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 | 99 |
// AddDigest adds a digest reference to the store. |
| 100 |
-func (store *store) AddDigest(ref Canonical, id digest.Digest, force bool) error {
|
|
| 100 |
+func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error {
|
|
| 101 | 101 |
return store.addReference(ref, id, force) |
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 |
-func (store *store) addReference(ref Named, id digest.Digest, force bool) error {
|
|
| 105 |
- if ref.Name() == string(digest.Canonical) {
|
|
| 104 |
+func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
|
|
| 105 |
+ refName := reference.FamiliarName(ref) |
|
| 106 |
+ refStr := reference.FamiliarString(ref) |
|
| 107 |
+ |
|
| 108 |
+ if refName == string(digest.Canonical) {
|
|
| 106 | 109 |
return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
|
| 107 | 110 |
} |
| 108 | 111 |
|
| 109 | 112 |
store.mu.Lock() |
| 110 | 113 |
defer store.mu.Unlock() |
| 111 | 114 |
|
| 112 |
- repository, exists := store.Repositories[ref.Name()] |
|
| 115 |
+ repository, exists := store.Repositories[refName] |
|
| 113 | 116 |
if !exists || repository == nil {
|
| 114 | 117 |
repository = make(map[string]digest.Digest) |
| 115 |
- store.Repositories[ref.Name()] = repository |
|
| 118 |
+ store.Repositories[refName] = repository |
|
| 116 | 119 |
} |
| 117 | 120 |
|
| 118 |
- refStr := ref.String() |
|
| 119 | 121 |
oldID, exists := repository[refStr] |
| 120 | 122 |
|
| 121 | 123 |
if exists {
|
| 122 | 124 |
// force only works for tags |
| 123 |
- if digested, isDigest := ref.(Canonical); isDigest {
|
|
| 125 |
+ if digested, isDigest := ref.(reference.Canonical); isDigest {
|
|
| 124 | 126 |
return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
|
| 125 | 127 |
} |
| 126 | 128 |
|
| 127 | 129 |
if !force {
|
| 128 |
- return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
|
|
| 130 |
+ return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", refStr, oldID.String())
|
|
| 129 | 131 |
} |
| 130 | 132 |
|
| 131 | 133 |
if store.referencesByIDCache[oldID] != nil {
|
| ... | ... |
@@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error |
| 138 | 138 |
|
| 139 | 139 |
repository[refStr] = id |
| 140 | 140 |
if store.referencesByIDCache[id] == nil {
|
| 141 |
- store.referencesByIDCache[id] = make(map[string]Named) |
|
| 141 |
+ store.referencesByIDCache[id] = make(map[string]reference.Named) |
|
| 142 | 142 |
} |
| 143 | 143 |
store.referencesByIDCache[id][refStr] = ref |
| 144 | 144 |
|
| ... | ... |
@@ -147,24 +154,24 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error |
| 147 | 147 |
|
| 148 | 148 |
// Delete deletes a reference from the store. It returns true if a deletion |
| 149 | 149 |
// happened, or false otherwise. |
| 150 |
-func (store *store) Delete(ref Named) (bool, error) {
|
|
| 151 |
- ref = WithDefaultTag(ref) |
|
| 150 |
+func (store *store) Delete(ref reference.Named) (bool, error) {
|
|
| 151 |
+ ref = reference.TagNameOnly(ref) |
|
| 152 |
+ |
|
| 153 |
+ refName := reference.FamiliarName(ref) |
|
| 154 |
+ refStr := reference.FamiliarString(ref) |
|
| 152 | 155 |
|
| 153 | 156 |
store.mu.Lock() |
| 154 | 157 |
defer store.mu.Unlock() |
| 155 | 158 |
|
| 156 |
- repoName := ref.Name() |
|
| 157 |
- |
|
| 158 |
- repository, exists := store.Repositories[repoName] |
|
| 159 |
+ repository, exists := store.Repositories[refName] |
|
| 159 | 160 |
if !exists {
|
| 160 | 161 |
return false, ErrDoesNotExist |
| 161 | 162 |
} |
| 162 | 163 |
|
| 163 |
- refStr := ref.String() |
|
| 164 | 164 |
if id, exists := repository[refStr]; exists {
|
| 165 | 165 |
delete(repository, refStr) |
| 166 | 166 |
if len(repository) == 0 {
|
| 167 |
- delete(store.Repositories, repoName) |
|
| 167 |
+ delete(store.Repositories, refName) |
|
| 168 | 168 |
} |
| 169 | 169 |
if store.referencesByIDCache[id] != nil {
|
| 170 | 170 |
delete(store.referencesByIDCache[id], refStr) |
| ... | ... |
@@ -179,18 +186,34 @@ func (store *store) Delete(ref Named) (bool, error) {
|
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 | 181 |
// Get retrieves an item from the store by reference |
| 182 |
-func (store *store) Get(ref Named) (digest.Digest, error) {
|
|
| 183 |
- ref = WithDefaultTag(ref) |
|
| 182 |
+func (store *store) Get(ref reference.Named) (digest.Digest, error) {
|
|
| 183 |
+ if canonical, ok := ref.(reference.Canonical); ok {
|
|
| 184 |
+ // If reference contains both tag and digest, only |
|
| 185 |
+ // lookup by digest as it takes precendent over |
|
| 186 |
+ // tag, until tag/digest combos are stored. |
|
| 187 |
+ if _, ok := ref.(reference.Tagged); ok {
|
|
| 188 |
+ var err error |
|
| 189 |
+ ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest()) |
|
| 190 |
+ if err != nil {
|
|
| 191 |
+ return "", err |
|
| 192 |
+ } |
|
| 193 |
+ } |
|
| 194 |
+ } else {
|
|
| 195 |
+ ref = reference.TagNameOnly(ref) |
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ refName := reference.FamiliarName(ref) |
|
| 199 |
+ refStr := reference.FamiliarString(ref) |
|
| 184 | 200 |
|
| 185 | 201 |
store.mu.RLock() |
| 186 | 202 |
defer store.mu.RUnlock() |
| 187 | 203 |
|
| 188 |
- repository, exists := store.Repositories[ref.Name()] |
|
| 204 |
+ repository, exists := store.Repositories[refName] |
|
| 189 | 205 |
if !exists || repository == nil {
|
| 190 | 206 |
return "", ErrDoesNotExist |
| 191 | 207 |
} |
| 192 | 208 |
|
| 193 |
- id, exists := repository[ref.String()] |
|
| 209 |
+ id, exists := repository[refStr] |
|
| 194 | 210 |
if !exists {
|
| 195 | 211 |
return "", ErrDoesNotExist |
| 196 | 212 |
} |
| ... | ... |
@@ -200,7 +223,7 @@ func (store *store) Get(ref Named) (digest.Digest, error) {
|
| 200 | 200 |
|
| 201 | 201 |
// References returns a slice of references to the given ID. The slice |
| 202 | 202 |
// will be nil if there are no references to this ID. |
| 203 |
-func (store *store) References(id digest.Digest) []Named {
|
|
| 203 |
+func (store *store) References(id digest.Digest) []reference.Named {
|
|
| 204 | 204 |
store.mu.RLock() |
| 205 | 205 |
defer store.mu.RUnlock() |
| 206 | 206 |
|
| ... | ... |
@@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named {
|
| 208 | 208 |
// 1) We must not return a mutable |
| 209 | 209 |
// 2) It would be ugly to expose the extraneous map keys to callers. |
| 210 | 210 |
|
| 211 |
- var references []Named |
|
| 211 |
+ var references []reference.Named |
|
| 212 | 212 |
for _, ref := range store.referencesByIDCache[id] {
|
| 213 | 213 |
references = append(references, ref) |
| 214 | 214 |
} |
| ... | ... |
@@ -221,18 +244,20 @@ func (store *store) References(id digest.Digest) []Named {
|
| 221 | 221 |
// ReferencesByName returns the references for a given repository name. |
| 222 | 222 |
// If there are no references known for this repository name, |
| 223 | 223 |
// ReferencesByName returns nil. |
| 224 |
-func (store *store) ReferencesByName(ref Named) []Association {
|
|
| 224 |
+func (store *store) ReferencesByName(ref reference.Named) []Association {
|
|
| 225 |
+ refName := reference.FamiliarName(ref) |
|
| 226 |
+ |
|
| 225 | 227 |
store.mu.RLock() |
| 226 | 228 |
defer store.mu.RUnlock() |
| 227 | 229 |
|
| 228 |
- repository, exists := store.Repositories[ref.Name()] |
|
| 230 |
+ repository, exists := store.Repositories[refName] |
|
| 229 | 231 |
if !exists {
|
| 230 | 232 |
return nil |
| 231 | 233 |
} |
| 232 | 234 |
|
| 233 | 235 |
var associations []Association |
| 234 | 236 |
for refStr, refID := range repository {
|
| 235 |
- ref, err := ParseNamed(refStr) |
|
| 237 |
+ ref, err := reference.ParseNormalizedNamed(refStr) |
|
| 236 | 238 |
if err != nil {
|
| 237 | 239 |
// Should never happen |
| 238 | 240 |
return nil |
| ... | ... |
@@ -270,13 +295,13 @@ func (store *store) reload() error {
|
| 270 | 270 |
|
| 271 | 271 |
for _, repository := range store.Repositories {
|
| 272 | 272 |
for refStr, refID := range repository {
|
| 273 |
- ref, err := ParseNamed(refStr) |
|
| 273 |
+ ref, err := reference.ParseNormalizedNamed(refStr) |
|
| 274 | 274 |
if err != nil {
|
| 275 | 275 |
// Should never happen |
| 276 | 276 |
continue |
| 277 | 277 |
} |
| 278 | 278 |
if store.referencesByIDCache[refID] == nil {
|
| 279 |
- store.referencesByIDCache[refID] = make(map[string]Named) |
|
| 279 |
+ store.referencesByIDCache[refID] = make(map[string]reference.Named) |
|
| 280 | 280 |
} |
| 281 | 281 |
store.referencesByIDCache[refID][refStr] = ref |
| 282 | 282 |
} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"strings" |
| 9 | 9 |
"testing" |
| 10 | 10 |
|
| 11 |
+ "github.com/docker/distribution/reference" |
|
| 11 | 12 |
"github.com/opencontainers/go-digest" |
| 12 | 13 |
) |
| 13 | 14 |
|
| ... | ... |
@@ -45,7 +46,7 @@ func TestLoad(t *testing.T) {
|
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 | 47 |
for refStr, expectedID := range saveLoadTestCases {
|
| 48 |
- ref, err := ParseNamed(refStr) |
|
| 48 |
+ ref, err := reference.ParseNormalizedNamed(refStr) |
|
| 49 | 49 |
if err != nil {
|
| 50 | 50 |
t.Fatalf("failed to parse reference: %v", err)
|
| 51 | 51 |
} |
| ... | ... |
@@ -74,11 +75,11 @@ func TestSave(t *testing.T) {
|
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
for refStr, id := range saveLoadTestCases {
|
| 77 |
- ref, err := ParseNamed(refStr) |
|
| 77 |
+ ref, err := reference.ParseNormalizedNamed(refStr) |
|
| 78 | 78 |
if err != nil {
|
| 79 | 79 |
t.Fatalf("failed to parse reference: %v", err)
|
| 80 | 80 |
} |
| 81 |
- if canonical, ok := ref.(Canonical); ok {
|
|
| 81 |
+ if canonical, ok := ref.(reference.Canonical); ok {
|
|
| 82 | 82 |
err = store.AddDigest(canonical, id, false) |
| 83 | 83 |
if err != nil {
|
| 84 | 84 |
t.Fatalf("could not add digest reference %s: %v", refStr, err)
|
| ... | ... |
@@ -120,7 +121,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 120 | 120 |
testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
|
| 121 | 121 |
|
| 122 | 122 |
// Try adding a reference with no tag or digest |
| 123 |
- nameOnly, err := WithName("username/repo")
|
|
| 123 |
+ nameOnly, err := reference.ParseNormalizedNamed("username/repo")
|
|
| 124 | 124 |
if err != nil {
|
| 125 | 125 |
t.Fatalf("could not parse reference: %v", err)
|
| 126 | 126 |
} |
| ... | ... |
@@ -129,7 +130,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 | 131 |
// Add a few references |
| 132 |
- ref1, err := ParseNamed("username/repo1:latest")
|
|
| 132 |
+ ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
|
|
| 133 | 133 |
if err != nil {
|
| 134 | 134 |
t.Fatalf("could not parse reference: %v", err)
|
| 135 | 135 |
} |
| ... | ... |
@@ -137,7 +138,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 137 | 137 |
t.Fatalf("error adding to store: %v", err)
|
| 138 | 138 |
} |
| 139 | 139 |
|
| 140 |
- ref2, err := ParseNamed("username/repo1:old")
|
|
| 140 |
+ ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
|
|
| 141 | 141 |
if err != nil {
|
| 142 | 142 |
t.Fatalf("could not parse reference: %v", err)
|
| 143 | 143 |
} |
| ... | ... |
@@ -145,7 +146,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 145 | 145 |
t.Fatalf("error adding to store: %v", err)
|
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
- ref3, err := ParseNamed("username/repo1:alias")
|
|
| 148 |
+ ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
|
|
| 149 | 149 |
if err != nil {
|
| 150 | 150 |
t.Fatalf("could not parse reference: %v", err)
|
| 151 | 151 |
} |
| ... | ... |
@@ -153,7 +154,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 153 | 153 |
t.Fatalf("error adding to store: %v", err)
|
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
- ref4, err := ParseNamed("username/repo2:latest")
|
|
| 156 |
+ ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
|
|
| 157 | 157 |
if err != nil {
|
| 158 | 158 |
t.Fatalf("could not parse reference: %v", err)
|
| 159 | 159 |
} |
| ... | ... |
@@ -161,11 +162,11 @@ func TestAddDeleteGet(t *testing.T) {
|
| 161 | 161 |
t.Fatalf("error adding to store: %v", err)
|
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 |
- ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
|
|
| 164 |
+ ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
|
|
| 165 | 165 |
if err != nil {
|
| 166 | 166 |
t.Fatalf("could not parse reference: %v", err)
|
| 167 | 167 |
} |
| 168 |
- if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
|
|
| 168 |
+ if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
|
|
| 169 | 169 |
t.Fatalf("error adding to store: %v", err)
|
| 170 | 170 |
} |
| 171 | 171 |
|
| ... | ... |
@@ -228,7 +229,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 228 | 228 |
} |
| 229 | 229 |
|
| 230 | 230 |
// Get should return ErrDoesNotExist for a nonexistent repo |
| 231 |
- nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
|
|
| 231 |
+ nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
|
|
| 232 | 232 |
if err != nil {
|
| 233 | 233 |
t.Fatalf("could not parse reference: %v", err)
|
| 234 | 234 |
} |
| ... | ... |
@@ -237,7 +238,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 | 239 |
// Get should return ErrDoesNotExist for a nonexistent tag |
| 240 |
- nonExistTag, err := ParseNamed("username/repo1:nonexist")
|
|
| 240 |
+ nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
|
|
| 241 | 241 |
if err != nil {
|
| 242 | 242 |
t.Fatalf("could not parse reference: %v", err)
|
| 243 | 243 |
} |
| ... | ... |
@@ -263,7 +264,7 @@ func TestAddDeleteGet(t *testing.T) {
|
| 263 | 263 |
} |
| 264 | 264 |
|
| 265 | 265 |
// Check ReferencesByName |
| 266 |
- repoName, err := WithName("username/repo1")
|
|
| 266 |
+ repoName, err := reference.ParseNormalizedNamed("username/repo1")
|
|
| 267 | 267 |
if err != nil {
|
| 268 | 268 |
t.Fatalf("could not parse reference: %v", err)
|
| 269 | 269 |
} |
| ... | ... |
@@ -334,7 +335,7 @@ func TestInvalidTags(t *testing.T) {
|
| 334 | 334 |
id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
|
| 335 | 335 |
|
| 336 | 336 |
// sha256 as repo name |
| 337 |
- ref, err := ParseNamed("sha256:abc")
|
|
| 337 |
+ ref, err := reference.ParseNormalizedNamed("sha256:abc")
|
|
| 338 | 338 |
if err != nil {
|
| 339 | 339 |
t.Fatal(err) |
| 340 | 340 |
} |
| ... | ... |
@@ -344,7 +345,7 @@ func TestInvalidTags(t *testing.T) {
|
| 344 | 344 |
} |
| 345 | 345 |
|
| 346 | 346 |
// setting digest as a tag |
| 347 |
- ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
|
|
| 347 |
+ ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
|
|
| 348 | 348 |
if err != nil {
|
| 349 | 349 |
t.Fatal(err) |
| 350 | 350 |
} |
| ... | ... |
@@ -10,7 +10,6 @@ import ( |
| 10 | 10 |
"github.com/docker/distribution/reference" |
| 11 | 11 |
registrytypes "github.com/docker/docker/api/types/registry" |
| 12 | 12 |
"github.com/docker/docker/opts" |
| 13 |
- forkedref "github.com/docker/docker/reference" |
|
| 14 | 13 |
"github.com/pkg/errors" |
| 15 | 14 |
"github.com/spf13/pflag" |
| 16 | 15 |
) |
| ... | ... |
@@ -271,8 +270,9 @@ func ValidateMirror(val string) (string, error) {
|
| 271 | 271 |
|
| 272 | 272 |
// ValidateIndexName validates an index name. |
| 273 | 273 |
func ValidateIndexName(val string) (string, error) {
|
| 274 |
- if val == forkedref.LegacyDefaultHostname {
|
|
| 275 |
- val = forkedref.DefaultHostname |
|
| 274 |
+ // TODO: upstream this to check to reference package |
|
| 275 |
+ if val == "index.docker.io" {
|
|
| 276 |
+ val = "docker.io" |
|
| 276 | 277 |
} |
| 277 | 278 |
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
|
| 278 | 279 |
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
|
| ... | ... |
@@ -328,13 +328,8 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository |
| 328 | 328 |
} |
| 329 | 329 |
official := !strings.ContainsRune(reference.FamiliarName(name), '/') |
| 330 | 330 |
|
| 331 |
- // TODO: remove used of forked reference package |
|
| 332 |
- nameref, err := forkedref.ParseNamed(name.String()) |
|
| 333 |
- if err != nil {
|
|
| 334 |
- return nil, err |
|
| 335 |
- } |
|
| 336 | 331 |
return &RepositoryInfo{
|
| 337 |
- Named: nameref, |
|
| 332 |
+ Name: reference.TrimNamed(name), |
|
| 338 | 333 |
Index: index, |
| 339 | 334 |
Official: official, |
| 340 | 335 |
}, nil |
| ... | ... |
@@ -14,7 +14,6 @@ import ( |
| 14 | 14 |
"github.com/docker/distribution/registry/client/transport" |
| 15 | 15 |
"github.com/docker/docker/api/types" |
| 16 | 16 |
registrytypes "github.com/docker/docker/api/types/registry" |
| 17 |
- forkedref "github.com/docker/docker/reference" |
|
| 18 | 17 |
) |
| 19 | 18 |
|
| 20 | 19 |
var ( |
| ... | ... |
@@ -202,7 +201,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
|
| 202 | 202 |
|
| 203 | 203 |
func TestGetRemoteTag(t *testing.T) {
|
| 204 | 204 |
r := spawnTestRegistrySession(t) |
| 205 |
- repoRef, err := forkedref.ParseNamed(REPO) |
|
| 205 |
+ repoRef, err := reference.ParseNormalizedNamed(REPO) |
|
| 206 | 206 |
if err != nil {
|
| 207 | 207 |
t.Fatal(err) |
| 208 | 208 |
} |
| ... | ... |
@@ -212,7 +211,7 @@ func TestGetRemoteTag(t *testing.T) {
|
| 212 | 212 |
} |
| 213 | 213 |
assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID) |
| 214 | 214 |
|
| 215 |
- bazRef, err := forkedref.ParseNamed("foo42/baz")
|
|
| 215 |
+ bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
|
|
| 216 | 216 |
if err != nil {
|
| 217 | 217 |
t.Fatal(err) |
| 218 | 218 |
} |
| ... | ... |
@@ -224,7 +223,7 @@ func TestGetRemoteTag(t *testing.T) {
|
| 224 | 224 |
|
| 225 | 225 |
func TestGetRemoteTags(t *testing.T) {
|
| 226 | 226 |
r := spawnTestRegistrySession(t) |
| 227 |
- repoRef, err := forkedref.ParseNamed(REPO) |
|
| 227 |
+ repoRef, err := reference.ParseNormalizedNamed(REPO) |
|
| 228 | 228 |
if err != nil {
|
| 229 | 229 |
t.Fatal(err) |
| 230 | 230 |
} |
| ... | ... |
@@ -236,7 +235,7 @@ func TestGetRemoteTags(t *testing.T) {
|
| 236 | 236 |
assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID) |
| 237 | 237 |
assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID) |
| 238 | 238 |
|
| 239 |
- bazRef, err := forkedref.ParseNamed("foo42/baz")
|
|
| 239 |
+ bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
|
|
| 240 | 240 |
if err != nil {
|
| 241 | 241 |
t.Fatal(err) |
| 242 | 242 |
} |
| ... | ... |
@@ -253,7 +252,7 @@ func TestGetRepositoryData(t *testing.T) {
|
| 253 | 253 |
t.Fatal(err) |
| 254 | 254 |
} |
| 255 | 255 |
host := "http://" + parsedURL.Host + "/v1/" |
| 256 |
- repoRef, err := forkedref.ParseNamed(REPO) |
|
| 256 |
+ repoRef, err := reference.ParseNormalizedNamed(REPO) |
|
| 257 | 257 |
if err != nil {
|
| 258 | 258 |
t.Fatal(err) |
| 259 | 259 |
} |
| ... | ... |
@@ -516,9 +515,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
| 516 | 516 |
t.Error(err) |
| 517 | 517 |
} else {
|
| 518 | 518 |
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName) |
| 519 |
- checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName) |
|
| 520 |
- checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName) |
|
| 521 |
- checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName) |
|
| 519 |
+ checkEqual(t, reference.Path(repoInfo.Name), expectedRepoInfo.RemoteName, reposName) |
|
| 520 |
+ checkEqual(t, reference.FamiliarName(repoInfo.Name), expectedRepoInfo.LocalName, reposName) |
|
| 521 |
+ checkEqual(t, repoInfo.Name.Name(), expectedRepoInfo.CanonicalName, reposName) |
|
| 522 | 522 |
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName) |
| 523 | 523 |
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName) |
| 524 | 524 |
} |
| ... | ... |
@@ -689,7 +688,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
| 689 | 689 |
|
| 690 | 690 |
func TestPushRegistryTag(t *testing.T) {
|
| 691 | 691 |
r := spawnTestRegistrySession(t) |
| 692 |
- repoRef, err := forkedref.ParseNamed(REPO) |
|
| 692 |
+ repoRef, err := reference.ParseNormalizedNamed(REPO) |
|
| 693 | 693 |
if err != nil {
|
| 694 | 694 |
t.Fatal(err) |
| 695 | 695 |
} |
| ... | ... |
@@ -711,7 +710,7 @@ func TestPushImageJSONIndex(t *testing.T) {
|
| 711 | 711 |
Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2", |
| 712 | 712 |
}, |
| 713 | 713 |
} |
| 714 |
- repoRef, err := forkedref.ParseNamed(REPO) |
|
| 714 |
+ repoRef, err := reference.ParseNormalizedNamed(REPO) |
|
| 715 | 715 |
if err != nil {
|
| 716 | 716 |
t.Fatal(err) |
| 717 | 717 |
} |
| ... | ... |
@@ -19,6 +19,7 @@ import ( |
| 19 | 19 |
"strings" |
| 20 | 20 |
|
| 21 | 21 |
"github.com/Sirupsen/logrus" |
| 22 |
+ "github.com/docker/distribution/reference" |
|
| 22 | 23 |
"github.com/docker/distribution/registry/api/errcode" |
| 23 | 24 |
"github.com/docker/docker/api/types" |
| 24 | 25 |
registrytypes "github.com/docker/docker/api/types/registry" |
| ... | ... |
@@ -26,7 +27,6 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/pkg/ioutils" |
| 27 | 27 |
"github.com/docker/docker/pkg/stringid" |
| 28 | 28 |
"github.com/docker/docker/pkg/tarsum" |
| 29 |
- "github.com/docker/docker/reference" |
|
| 30 | 29 |
) |
| 31 | 30 |
|
| 32 | 31 |
var ( |
| ... | ... |
@@ -324,7 +324,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io |
| 324 | 324 |
// argument, and returns data from the first one that answers the query |
| 325 | 325 |
// successfully. |
| 326 | 326 |
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
|
| 327 |
- repository := repositoryRef.RemoteName() |
|
| 327 |
+ repository := reference.Path(repositoryRef) |
|
| 328 | 328 |
|
| 329 | 329 |
if strings.Count(repository, "/") == 0 {
|
| 330 | 330 |
// This will be removed once the registry supports auto-resolution on |
| ... | ... |
@@ -362,7 +362,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name |
| 362 | 362 |
// the first one that answers the query successfully. It returns a map with |
| 363 | 363 |
// tag names as the keys and image IDs as the values. |
| 364 | 364 |
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
|
| 365 |
- repository := repositoryRef.RemoteName() |
|
| 365 |
+ repository := reference.Path(repositoryRef) |
|
| 366 | 366 |
|
| 367 | 367 |
if strings.Count(repository, "/") == 0 {
|
| 368 | 368 |
// This will be removed once the registry supports auto-resolution on |
| ... | ... |
@@ -416,7 +416,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
|
| 416 | 416 |
|
| 417 | 417 |
// GetRepositoryData returns lists of images and endpoints for the repository |
| 418 | 418 |
func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
|
| 419 |
- repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), name.RemoteName())
|
|
| 419 |
+ repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), reference.Path(name))
|
|
| 420 | 420 |
|
| 421 | 421 |
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
|
| 422 | 422 |
|
| ... | ... |
@@ -450,7 +450,7 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro |
| 450 | 450 |
if err != nil {
|
| 451 | 451 |
logrus.Debugf("Error reading response body: %s", err)
|
| 452 | 452 |
} |
| 453 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
|
|
| 453 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
|
|
| 454 | 454 |
} |
| 455 | 455 |
|
| 456 | 456 |
var endpoints []string |
| ... | ... |
@@ -605,7 +605,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry |
| 605 | 605 |
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
|
| 606 | 606 |
// "jsonify" the string |
| 607 | 607 |
revision = "\"" + revision + "\"" |
| 608 |
- path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
|
|
| 608 |
+ path := fmt.Sprintf("repositories/%s/tags/%s", reference.Path(remote), tag)
|
|
| 609 | 609 |
|
| 610 | 610 |
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
|
| 611 | 611 |
if err != nil {
|
| ... | ... |
@@ -619,7 +619,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr |
| 619 | 619 |
} |
| 620 | 620 |
res.Body.Close() |
| 621 | 621 |
if res.StatusCode != 200 && res.StatusCode != 201 {
|
| 622 |
- return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
|
|
| 622 |
+ return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
|
|
| 623 | 623 |
} |
| 624 | 624 |
return nil |
| 625 | 625 |
} |
| ... | ... |
@@ -645,7 +645,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 645 | 645 |
if validate {
|
| 646 | 646 |
suffix = "images" |
| 647 | 647 |
} |
| 648 |
- u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), remote.RemoteName(), suffix)
|
|
| 648 |
+ u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), reference.Path(remote), suffix)
|
|
| 649 | 649 |
logrus.Debugf("[registry] PUT %s", u)
|
| 650 | 650 |
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
| 651 | 651 |
headers := map[string][]string{
|
| ... | ... |
@@ -683,7 +683,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 683 | 683 |
if err != nil {
|
| 684 | 684 |
logrus.Debugf("Error reading response body: %s", err)
|
| 685 | 685 |
} |
| 686 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
|
| 686 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
|
| 687 | 687 |
} |
| 688 | 688 |
tokens = res.Header["X-Docker-Token"] |
| 689 | 689 |
logrus.Debugf("Auth token: %v", tokens)
|
| ... | ... |
@@ -701,7 +701,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, |
| 701 | 701 |
if err != nil {
|
| 702 | 702 |
logrus.Debugf("Error reading response body: %s", err)
|
| 703 | 703 |
} |
| 704 |
- return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
|
| 704 |
+ return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
|
|
| 705 | 705 |
} |
| 706 | 706 |
} |
| 707 | 707 |
|
| ... | ... |
@@ -1,8 +1,8 @@ |
| 1 | 1 |
package registry |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "github.com/docker/distribution/reference" |
|
| 4 | 5 |
registrytypes "github.com/docker/docker/api/types/registry" |
| 5 |
- "github.com/docker/docker/reference" |
|
| 6 | 6 |
) |
| 7 | 7 |
|
| 8 | 8 |
// RepositoryData tracks the image list, list of endpoints for a repository |
| ... | ... |
@@ -57,7 +57,7 @@ var apiVersions = map[APIVersion]string{
|
| 57 | 57 |
|
| 58 | 58 |
// RepositoryInfo describes a repository |
| 59 | 59 |
type RepositoryInfo struct {
|
| 60 |
- reference.Named |
|
| 60 |
+ Name reference.Named |
|
| 61 | 61 |
// Index points to registry information |
| 62 | 62 |
Index *registrytypes.IndexInfo |
| 63 | 63 |
// Official indicates whether the repository is considered official. |
| ... | ... |
@@ -43,7 +43,7 @@ github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 |
| 43 | 43 |
github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 |
| 44 | 44 |
|
| 45 | 45 |
# get graph and distribution packages |
| 46 |
-github.com/docker/distribution 129ad8ea0c3760d878b34cffdb9c3be874a7b2f7 |
|
| 46 |
+github.com/docker/distribution 545102ea07aa9796f189d82f606b7c27d7aa3ed3 |
|
| 47 | 47 |
github.com/vbatts/tar-split v0.10.1 |
| 48 | 48 |
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb |
| 49 | 49 |
|
| ... | ... |
@@ -102,7 +102,7 @@ github.com/docker/containerd 78fb8f45890a601e0fd9051cf9f9f74923e950fd |
| 102 | 102 |
github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 |
| 103 | 103 |
|
| 104 | 104 |
# cluster |
| 105 |
-github.com/docker/swarmkit 78ae345f449ac69aa741c762df7e5f0020f70275 |
|
| 105 |
+github.com/docker/swarmkit 3ca4775ba4a5519e2225c3337c7db8901ec39d26 |
|
| 106 | 106 |
github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9 |
| 107 | 107 |
github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2 |
| 108 | 108 |
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a |
| ... | ... |
@@ -22,8 +22,8 @@ type Manifest interface {
|
| 22 | 22 |
References() []Descriptor |
| 23 | 23 |
|
| 24 | 24 |
// Payload provides the serialized format of the manifest, in addition to |
| 25 |
- // the mediatype. |
|
| 26 |
- Payload() (mediatype string, payload []byte, err error) |
|
| 25 |
+ // the media type. |
|
| 26 |
+ Payload() (mediaType string, payload []byte, err error) |
|
| 27 | 27 |
} |
| 28 | 28 |
|
| 29 | 29 |
// ManifestBuilder creates a manifest allowing one to include dependencies. |
| ... | ... |
@@ -94,20 +94,20 @@ var mappings = make(map[string]UnmarshalFunc, 0) |
| 94 | 94 |
func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
|
| 95 | 95 |
// Need to look up by the actual media type, not the raw contents of |
| 96 | 96 |
// the header. Strip semicolons and anything following them. |
| 97 |
- var mediatype string |
|
| 97 |
+ var mediaType string |
|
| 98 | 98 |
if ctHeader != "" {
|
| 99 | 99 |
var err error |
| 100 |
- mediatype, _, err = mime.ParseMediaType(ctHeader) |
|
| 100 |
+ mediaType, _, err = mime.ParseMediaType(ctHeader) |
|
| 101 | 101 |
if err != nil {
|
| 102 | 102 |
return nil, Descriptor{}, err
|
| 103 | 103 |
} |
| 104 | 104 |
} |
| 105 | 105 |
|
| 106 |
- unmarshalFunc, ok := mappings[mediatype] |
|
| 106 |
+ unmarshalFunc, ok := mappings[mediaType] |
|
| 107 | 107 |
if !ok {
|
| 108 | 108 |
unmarshalFunc, ok = mappings[""] |
| 109 | 109 |
if !ok {
|
| 110 |
- return nil, Descriptor{}, fmt.Errorf("unsupported manifest mediatype and no default available: %s", mediatype)
|
|
| 110 |
+ return nil, Descriptor{}, fmt.Errorf("unsupported manifest media type and no default available: %s", mediaType)
|
|
| 111 | 111 |
} |
| 112 | 112 |
} |
| 113 | 113 |
|
| ... | ... |
@@ -116,10 +116,10 @@ func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) |
| 116 | 116 |
|
| 117 | 117 |
// RegisterManifestSchema registers an UnmarshalFunc for a given schema type. This |
| 118 | 118 |
// should be called from specific |
| 119 |
-func RegisterManifestSchema(mediatype string, u UnmarshalFunc) error {
|
|
| 120 |
- if _, ok := mappings[mediatype]; ok {
|
|
| 121 |
- return fmt.Errorf("manifest mediatype registration would overwrite existing: %s", mediatype)
|
|
| 119 |
+func RegisterManifestSchema(mediaType string, u UnmarshalFunc) error {
|
|
| 120 |
+ if _, ok := mappings[mediaType]; ok {
|
|
| 121 |
+ return fmt.Errorf("manifest media type registration would overwrite existing: %s", mediaType)
|
|
| 122 | 122 |
} |
| 123 |
- mappings[mediatype] = u |
|
| 123 |
+ mappings[mediaType] = u |
|
| 124 | 124 |
return nil |
| 125 | 125 |
} |
| ... | ... |
@@ -1,5 +1,7 @@ |
| 1 | 1 |
package reference |
| 2 | 2 |
|
| 3 |
+import "path" |
|
| 4 |
+ |
|
| 3 | 5 |
// IsNameOnly returns true if reference only contains a repo name. |
| 4 | 6 |
func IsNameOnly(ref Named) bool {
|
| 5 | 7 |
if _, ok := ref.(NamedTagged); ok {
|
| ... | ... |
@@ -14,7 +16,7 @@ func IsNameOnly(ref Named) bool {
|
| 14 | 14 |
// FamiliarName returns the familiar name string |
| 15 | 15 |
// for the given named, familiarizing if needed. |
| 16 | 16 |
func FamiliarName(ref Named) string {
|
| 17 |
- if nn, ok := ref.(NormalizedNamed); ok {
|
|
| 17 |
+ if nn, ok := ref.(normalizedNamed); ok {
|
|
| 18 | 18 |
return nn.Familiar().Name() |
| 19 | 19 |
} |
| 20 | 20 |
return ref.Name() |
| ... | ... |
@@ -23,8 +25,18 @@ func FamiliarName(ref Named) string {
|
| 23 | 23 |
// FamiliarString returns the familiar string representation |
| 24 | 24 |
// for the given reference, familiarizing if needed. |
| 25 | 25 |
func FamiliarString(ref Reference) string {
|
| 26 |
- if nn, ok := ref.(NormalizedNamed); ok {
|
|
| 26 |
+ if nn, ok := ref.(normalizedNamed); ok {
|
|
| 27 | 27 |
return nn.Familiar().String() |
| 28 | 28 |
} |
| 29 | 29 |
return ref.String() |
| 30 | 30 |
} |
| 31 |
+ |
|
| 32 |
+// FamiliarMatch reports whether ref matches the specified pattern. |
|
| 33 |
+// See https://godoc.org/path#Match for supported patterns. |
|
| 34 |
+func FamiliarMatch(pattern string, ref Reference) (bool, error) {
|
|
| 35 |
+ matched, err := path.Match(pattern, FamiliarString(ref)) |
|
| 36 |
+ if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
|
| 37 |
+ matched, _ = path.Match(pattern, FamiliarName(namedRef)) |
|
| 38 |
+ } |
|
| 39 |
+ return matched, err |
|
| 40 |
+} |
| ... | ... |
@@ -12,16 +12,16 @@ import ( |
| 12 | 12 |
var ( |
| 13 | 13 |
legacyDefaultDomain = "index.docker.io" |
| 14 | 14 |
defaultDomain = "docker.io" |
| 15 |
- defaultRepoPrefix = "library/" |
|
| 15 |
+ officialRepoName = "library" |
|
| 16 | 16 |
defaultTag = "latest" |
| 17 | 17 |
) |
| 18 | 18 |
|
| 19 |
-// NormalizedNamed represents a name which has been |
|
| 19 |
+// normalizedNamed represents a name which has been |
|
| 20 | 20 |
// normalized and has a familiar form. A familiar name |
| 21 | 21 |
// is what is used in Docker UI. An example normalized |
| 22 | 22 |
// name is "docker.io/library/ubuntu" and corresponding |
| 23 | 23 |
// familiar name of "ubuntu". |
| 24 |
-type NormalizedNamed interface {
|
|
| 24 |
+type normalizedNamed interface {
|
|
| 25 | 25 |
Named |
| 26 | 26 |
Familiar() Named |
| 27 | 27 |
} |
| ... | ... |
@@ -30,7 +30,7 @@ type NormalizedNamed interface {
|
| 30 | 30 |
// transforming a familiar name from Docker UI to a fully |
| 31 | 31 |
// qualified reference. If the value may be an identifier |
| 32 | 32 |
// use ParseAnyReference. |
| 33 |
-func ParseNormalizedNamed(s string) (NormalizedNamed, error) {
|
|
| 33 |
+func ParseNormalizedNamed(s string) (Named, error) {
|
|
| 34 | 34 |
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
| 35 | 35 |
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
| 36 | 36 |
} |
| ... | ... |
@@ -49,7 +49,7 @@ func ParseNormalizedNamed(s string) (NormalizedNamed, error) {
|
| 49 | 49 |
if err != nil {
|
| 50 | 50 |
return nil, err |
| 51 | 51 |
} |
| 52 |
- named, isNamed := ref.(NormalizedNamed) |
|
| 52 |
+ named, isNamed := ref.(Named) |
|
| 53 | 53 |
if !isNamed {
|
| 54 | 54 |
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
| 55 | 55 |
} |
| ... | ... |
@@ -70,7 +70,7 @@ func splitDockerDomain(name string) (domain, remainder string) {
|
| 70 | 70 |
domain = defaultDomain |
| 71 | 71 |
} |
| 72 | 72 |
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
| 73 |
- remainder = defaultRepoPrefix + remainder |
|
| 73 |
+ remainder = officialRepoName + "/" + remainder |
|
| 74 | 74 |
} |
| 75 | 75 |
return |
| 76 | 76 |
} |
| ... | ... |
@@ -89,7 +89,10 @@ func familiarizeName(named namedRepository) repository {
|
| 89 | 89 |
|
| 90 | 90 |
if repo.domain == defaultDomain {
|
| 91 | 91 |
repo.domain = "" |
| 92 |
- repo.path = strings.TrimPrefix(repo.path, defaultRepoPrefix) |
|
| 92 |
+ // Handle official repositories which have the pattern "library/<official repo name>" |
|
| 93 |
+ if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
|
|
| 94 |
+ repo.path = split[1] |
|
| 95 |
+ } |
|
| 93 | 96 |
} |
| 94 | 97 |
return repo |
| 95 | 98 |
} |
| ... | ... |
@@ -120,11 +123,10 @@ func (c canonicalReference) Familiar() Named {
|
| 120 | 120 |
} |
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 |
-// EnsureTagged adds the default tag "latest" to a reference if it only has |
|
| 123 |
+// TagNameOnly adds the default tag "latest" to a reference if it only has |
|
| 124 | 124 |
// a repo name. |
| 125 |
-func EnsureTagged(ref Named) NamedTagged {
|
|
| 126 |
- namedTagged, ok := ref.(NamedTagged) |
|
| 127 |
- if !ok {
|
|
| 125 |
+func TagNameOnly(ref Named) Named {
|
|
| 126 |
+ if IsNameOnly(ref) {
|
|
| 128 | 127 |
namedTagged, err := WithTag(ref, defaultTag) |
| 129 | 128 |
if err != nil {
|
| 130 | 129 |
// Default tag must be valid, to create a NamedTagged |
| ... | ... |
@@ -134,7 +136,7 @@ func EnsureTagged(ref Named) NamedTagged {
|
| 134 | 134 |
} |
| 135 | 135 |
return namedTagged |
| 136 | 136 |
} |
| 137 |
- return namedTagged |
|
| 137 |
+ return ref |
|
| 138 | 138 |
} |
| 139 | 139 |
|
| 140 | 140 |
// ParseAnyReference parses a reference string as a possible identifier, |
| ... | ... |
@@ -20,14 +20,13 @@ |
| 20 | 20 |
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ |
| 21 | 21 |
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
| 22 | 22 |
// |
| 23 |
-// identifier := /[a-f0-9]{64}/
|
|
| 24 |
-// short-identifier := /[a-f0-9]{6,64}/
|
|
| 23 |
+// identifier := /[a-f0-9]{64}/
|
|
| 24 |
+// short-identifier := /[a-f0-9]{6,64}/
|
|
| 25 | 25 |
package reference |
| 26 | 26 |
|
| 27 | 27 |
import ( |
| 28 | 28 |
"errors" |
| 29 | 29 |
"fmt" |
| 30 |
- "path" |
|
| 31 | 30 |
"strings" |
| 32 | 31 |
|
| 33 | 32 |
"github.com/opencontainers/go-digest" |
| ... | ... |
@@ -56,6 +55,9 @@ var ( |
| 56 | 56 |
|
| 57 | 57 |
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. |
| 58 | 58 |
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
| 59 |
+ |
|
| 60 |
+ // ErrNameNotCanonical is returned when a name is not canonical. |
|
| 61 |
+ ErrNameNotCanonical = errors.New("repository name must be canonical")
|
|
| 59 | 62 |
) |
| 60 | 63 |
|
| 61 | 64 |
// Reference is an opaque object reference identifier that may include |
| ... | ... |
@@ -232,18 +234,17 @@ func Parse(s string) (Reference, error) {
|
| 232 | 232 |
} |
| 233 | 233 |
|
| 234 | 234 |
// ParseNamed parses s and returns a syntactically valid reference implementing |
| 235 |
-// the Named interface. The reference must have a name, otherwise an error is |
|
| 236 |
-// returned. |
|
| 235 |
+// the Named interface. The reference must have a name and be in the canonical |
|
| 236 |
+// form, otherwise an error is returned. |
|
| 237 | 237 |
// If an error was encountered it is returned, along with a nil Reference. |
| 238 | 238 |
// NOTE: ParseNamed will not handle short digests. |
| 239 | 239 |
func ParseNamed(s string) (Named, error) {
|
| 240 |
- ref, err := Parse(s) |
|
| 240 |
+ named, err := ParseNormalizedNamed(s) |
|
| 241 | 241 |
if err != nil {
|
| 242 | 242 |
return nil, err |
| 243 | 243 |
} |
| 244 |
- named, isNamed := ref.(Named) |
|
| 245 |
- if !isNamed {
|
|
| 246 |
- return nil, fmt.Errorf("reference %s has no name", ref.String())
|
|
| 244 |
+ if named.String() != s {
|
|
| 245 |
+ return nil, ErrNameNotCanonical |
|
| 247 | 246 |
} |
| 248 | 247 |
return named, nil |
| 249 | 248 |
} |
| ... | ... |
@@ -317,16 +318,6 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
| 317 | 317 |
}, nil |
| 318 | 318 |
} |
| 319 | 319 |
|
| 320 |
-// Match reports whether ref matches the specified pattern. |
|
| 321 |
-// See https://godoc.org/path#Match for supported patterns. |
|
| 322 |
-func Match(pattern string, ref Reference) (bool, error) {
|
|
| 323 |
- matched, err := path.Match(pattern, ref.String()) |
|
| 324 |
- if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
|
| 325 |
- matched, _ = path.Match(pattern, namedRef.Name()) |
|
| 326 |
- } |
|
| 327 |
- return matched, err |
|
| 328 |
-} |
|
| 329 |
- |
|
| 330 | 320 |
// TrimNamed removes any tag or digest from the named reference. |
| 331 | 321 |
func TrimNamed(ref Named) Named {
|
| 332 | 322 |
domain, path := SplitHostname(ref) |
| ... | ... |
@@ -408,7 +399,7 @@ func (r repository) Path() string {
|
| 408 | 408 |
type digestReference digest.Digest |
| 409 | 409 |
|
| 410 | 410 |
func (d digestReference) String() string {
|
| 411 |
- return d.String() |
|
| 411 |
+ return digest.Digest(d).String() |
|
| 412 | 412 |
} |
| 413 | 413 |
|
| 414 | 414 |
func (d digestReference) Digest() digest.Digest {
|
| ... | ... |
@@ -35,7 +35,7 @@ type Namespace interface {
|
| 35 | 35 |
// reference. |
| 36 | 36 |
Repository(ctx context.Context, name reference.Named) (Repository, error) |
| 37 | 37 |
|
| 38 |
- // Repositories fills 'repos' with a lexigraphically sorted catalog of repositories |
|
| 38 |
+ // Repositories fills 'repos' with a lexicographically sorted catalog of repositories |
|
| 39 | 39 |
// up to the size of 'repos' and returns the value 'n' for the number of entries |
| 40 | 40 |
// which were filled. 'last' contains an offset in the catalog, and 'err' will be |
| 41 | 41 |
// set to io.EOF if there are no more entries to obtain. |
| ... | ... |
@@ -26,7 +26,7 @@ func NewInMemoryBlobDescriptorCacheProvider() cache.BlobDescriptorCacheProvider |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 | 28 |
func (imbdcp *inMemoryBlobDescriptorCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) {
|
| 29 |
- if _, err := reference.ParseNamed(repo); err != nil {
|
|
| 29 |
+ if _, err := reference.ParseNormalizedNamed(repo); err != nil {
|
|
| 30 | 30 |
return nil, err |
| 31 | 31 |
} |
| 32 | 32 |
|
| ... | ... |
@@ -39,6 +39,7 @@ type Agent struct {
|
| 39 | 39 |
ready chan struct{}
|
| 40 | 40 |
leaving chan struct{}
|
| 41 | 41 |
leaveOnce sync.Once |
| 42 |
+ left chan struct{} // closed after "run" processes "leaving" and will no longer accept new assignments
|
|
| 42 | 43 |
stopped chan struct{} // requests shutdown
|
| 43 | 44 |
stopOnce sync.Once // only allow stop to be called once |
| 44 | 45 |
closed chan struct{} // only closed in run
|
| ... | ... |
@@ -56,6 +57,7 @@ func New(config *Config) (*Agent, error) {
|
| 56 | 56 |
sessionq: make(chan sessionOperation), |
| 57 | 57 |
started: make(chan struct{}),
|
| 58 | 58 |
leaving: make(chan struct{}),
|
| 59 |
+ left: make(chan struct{}),
|
|
| 59 | 60 |
stopped: make(chan struct{}),
|
| 60 | 61 |
closed: make(chan struct{}),
|
| 61 | 62 |
ready: make(chan struct{}),
|
| ... | ... |
@@ -96,6 +98,16 @@ func (a *Agent) Leave(ctx context.Context) error {
|
| 96 | 96 |
close(a.leaving) |
| 97 | 97 |
}) |
| 98 | 98 |
|
| 99 |
+ // Do not call Wait until we have confirmed that the agent is no longer |
|
| 100 |
+ // accepting assignments. Starting a worker might race with Wait. |
|
| 101 |
+ select {
|
|
| 102 |
+ case <-a.left: |
|
| 103 |
+ case <-a.closed: |
|
| 104 |
+ return ErrClosed |
|
| 105 |
+ case <-ctx.Done(): |
|
| 106 |
+ return ctx.Err() |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 99 | 109 |
// agent could be closed while Leave is in progress |
| 100 | 110 |
var err error |
| 101 | 111 |
ch := make(chan struct{})
|
| ... | ... |
@@ -215,6 +227,8 @@ func (a *Agent) run(ctx context.Context) {
|
| 215 | 215 |
if err := a.worker.Assign(ctx, nil); err != nil {
|
| 216 | 216 |
log.G(ctx).WithError(err).Error("failed removing all assignments")
|
| 217 | 217 |
} |
| 218 |
+ |
|
| 219 |
+ close(a.left) |
|
| 218 | 220 |
case msg := <-session.assignments: |
| 219 | 221 |
// if we have left, accept no more assignments |
| 220 | 222 |
if leaving == nil {
|
| ... | ... |
@@ -104,17 +104,21 @@ func Resolve(ctx context.Context, task *api.Task, executor Executor) (Controller |
| 104 | 104 |
|
| 105 | 105 |
// depending on the tasks state, a failed controller resolution has varying |
| 106 | 106 |
// impact. The following expresses that impact. |
| 107 |
- if task.Status.State < api.TaskStateStarting {
|
|
| 108 |
- if err != nil {
|
|
| 109 |
- // before the task has been started, we consider it a rejection. |
|
| 110 |
- status.Message = "resolving controller failed" |
|
| 111 |
- status.Err = err.Error() |
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ status.Message = "resolving controller failed" |
|
| 109 |
+ status.Err = err.Error() |
|
| 110 |
+ // before the task has been started, we consider it a rejection. |
|
| 111 |
+ // if task is running, consider the task has failed |
|
| 112 |
+ // otherwise keep the existing state |
|
| 113 |
+ if task.Status.State < api.TaskStateStarting {
|
|
| 112 | 114 |
status.State = api.TaskStateRejected |
| 113 |
- } else if task.Status.State < api.TaskStateAccepted {
|
|
| 114 |
- // we always want to proceed to accepted when we resolve the contoller |
|
| 115 |
- status.Message = "accepted" |
|
| 116 |
- status.State = api.TaskStateAccepted |
|
| 115 |
+ } else if task.Status.State <= api.TaskStateRunning {
|
|
| 116 |
+ status.State = api.TaskStateFailed |
|
| 117 | 117 |
} |
| 118 |
+ } else if task.Status.State < api.TaskStateAccepted {
|
|
| 119 |
+ // we always want to proceed to accepted when we resolve the controller |
|
| 120 |
+ status.Message = "accepted" |
|
| 121 |
+ status.State = api.TaskStateAccepted |
|
| 118 | 122 |
} |
| 119 | 123 |
|
| 120 | 124 |
return ctlr, status, err |
| ... | ... |
@@ -30,7 +30,7 @@ service NodeCA {
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
message NodeCertificateStatusRequest {
|
| 33 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 33 |
+ string node_id = 1; |
|
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 | 36 |
message NodeCertificateStatusResponse {
|
| ... | ... |
@@ -54,7 +54,7 @@ message IssueNodeCertificateRequest {
|
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 | 56 |
message IssueNodeCertificateResponse {
|
| 57 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 57 |
+ string node_id = 1; |
|
| 58 | 58 |
NodeSpec.Membership node_membership = 2; |
| 59 | 59 |
} |
| 60 | 60 |
|
| ... | ... |
@@ -119,7 +119,7 @@ service Control {
|
| 119 | 119 |
} |
| 120 | 120 |
|
| 121 | 121 |
message GetNodeRequest {
|
| 122 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 122 |
+ string node_id = 1; |
|
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 | 125 |
message GetNodeResponse {
|
| ... | ... |
@@ -129,7 +129,7 @@ message GetNodeResponse {
|
| 129 | 129 |
message ListNodesRequest {
|
| 130 | 130 |
message Filters {
|
| 131 | 131 |
repeated string names = 1; |
| 132 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 132 |
+ repeated string id_prefixes = 2; |
|
| 133 | 133 |
map<string, string> labels = 3; |
| 134 | 134 |
repeated NodeSpec.Membership memberships = 4; |
| 135 | 135 |
repeated NodeRole roles = 5; |
| ... | ... |
@@ -148,7 +148,7 @@ message ListNodesResponse {
|
| 148 | 148 |
// to request a new availability for a node, such as PAUSE. Invalid updates |
| 149 | 149 |
// will be denied and cause an error. |
| 150 | 150 |
message UpdateNodeRequest {
|
| 151 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 151 |
+ string node_id = 1; |
|
| 152 | 152 |
Version node_version = 2; |
| 153 | 153 |
NodeSpec spec = 3; |
| 154 | 154 |
} |
| ... | ... |
@@ -159,7 +159,7 @@ message UpdateNodeResponse {
|
| 159 | 159 |
|
| 160 | 160 |
// RemoveNodeRequest requests to delete the specified node from store. |
| 161 | 161 |
message RemoveNodeRequest {
|
| 162 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 162 |
+ string node_id = 1; |
|
| 163 | 163 |
bool force = 2; |
| 164 | 164 |
} |
| 165 | 165 |
|
| ... | ... |
@@ -167,7 +167,7 @@ message RemoveNodeResponse {
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
message GetTaskRequest {
|
| 170 |
- string task_id = 1 [(gogoproto.customname) = "TaskID"]; |
|
| 170 |
+ string task_id = 1; |
|
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
message GetTaskResponse {
|
| ... | ... |
@@ -175,7 +175,7 @@ message GetTaskResponse {
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
message RemoveTaskRequest {
|
| 178 |
- string task_id = 1 [(gogoproto.customname) = "TaskID"]; |
|
| 178 |
+ string task_id = 1; |
|
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 | 181 |
message RemoveTaskResponse {
|
| ... | ... |
@@ -184,10 +184,10 @@ message RemoveTaskResponse {
|
| 184 | 184 |
message ListTasksRequest {
|
| 185 | 185 |
message Filters {
|
| 186 | 186 |
repeated string names = 1; |
| 187 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 187 |
+ repeated string id_prefixes = 2; |
|
| 188 | 188 |
map<string, string> labels = 3; |
| 189 |
- repeated string service_ids = 4 [(gogoproto.customname) = "ServiceIDs"]; |
|
| 190 |
- repeated string node_ids = 5 [(gogoproto.customname) = "NodeIDs"]; |
|
| 189 |
+ repeated string service_ids = 4; |
|
| 190 |
+ repeated string node_ids = 5; |
|
| 191 | 191 |
repeated docker.swarmkit.v1.TaskState desired_states = 6; |
| 192 | 192 |
// NamePrefixes matches all objects with the given prefixes |
| 193 | 193 |
repeated string name_prefixes = 7; |
| ... | ... |
@@ -209,7 +209,7 @@ message CreateServiceResponse {
|
| 209 | 209 |
} |
| 210 | 210 |
|
| 211 | 211 |
message GetServiceRequest {
|
| 212 |
- string service_id = 1 [(gogoproto.customname) = "ServiceID"]; |
|
| 212 |
+ string service_id = 1; |
|
| 213 | 213 |
} |
| 214 | 214 |
|
| 215 | 215 |
message GetServiceResponse {
|
| ... | ... |
@@ -217,7 +217,7 @@ message GetServiceResponse {
|
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 | 219 |
message UpdateServiceRequest {
|
| 220 |
- string service_id = 1 [(gogoproto.customname) = "ServiceID"]; |
|
| 220 |
+ string service_id = 1; |
|
| 221 | 221 |
Version service_version = 2; |
| 222 | 222 |
ServiceSpec spec = 3; |
| 223 | 223 |
} |
| ... | ... |
@@ -227,7 +227,7 @@ message UpdateServiceResponse {
|
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 | 229 |
message RemoveServiceRequest {
|
| 230 |
- string service_id = 1 [(gogoproto.customname) = "ServiceID"]; |
|
| 230 |
+ string service_id = 1; |
|
| 231 | 231 |
} |
| 232 | 232 |
|
| 233 | 233 |
message RemoveServiceResponse {
|
| ... | ... |
@@ -236,7 +236,7 @@ message RemoveServiceResponse {
|
| 236 | 236 |
message ListServicesRequest {
|
| 237 | 237 |
message Filters {
|
| 238 | 238 |
repeated string names = 1; |
| 239 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 239 |
+ repeated string id_prefixes = 2; |
|
| 240 | 240 |
map<string, string> labels = 3; |
| 241 | 241 |
// NamePrefixes matches all objects with the given prefixes |
| 242 | 242 |
repeated string name_prefixes = 4; |
| ... | ... |
@@ -259,7 +259,7 @@ message CreateNetworkResponse {
|
| 259 | 259 |
|
| 260 | 260 |
message GetNetworkRequest {
|
| 261 | 261 |
string name = 1; |
| 262 |
- string network_id = 2 [(gogoproto.customname) = "NetworkID"]; |
|
| 262 |
+ string network_id = 2; |
|
| 263 | 263 |
} |
| 264 | 264 |
|
| 265 | 265 |
message GetNetworkResponse {
|
| ... | ... |
@@ -268,7 +268,7 @@ message GetNetworkResponse {
|
| 268 | 268 |
|
| 269 | 269 |
message RemoveNetworkRequest {
|
| 270 | 270 |
string name = 1; |
| 271 |
- string network_id = 2 [(gogoproto.customname) = "NetworkID"]; |
|
| 271 |
+ string network_id = 2; |
|
| 272 | 272 |
} |
| 273 | 273 |
|
| 274 | 274 |
message RemoveNetworkResponse {}
|
| ... | ... |
@@ -276,7 +276,7 @@ message RemoveNetworkResponse {}
|
| 276 | 276 |
message ListNetworksRequest {
|
| 277 | 277 |
message Filters {
|
| 278 | 278 |
repeated string names = 1; |
| 279 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 279 |
+ repeated string id_prefixes = 2; |
|
| 280 | 280 |
map<string, string> labels = 3; |
| 281 | 281 |
// NamePrefixes matches all objects with the given prefixes |
| 282 | 282 |
repeated string name_prefixes = 4; |
| ... | ... |
@@ -290,7 +290,7 @@ message ListNetworksResponse {
|
| 290 | 290 |
} |
| 291 | 291 |
|
| 292 | 292 |
message GetClusterRequest {
|
| 293 |
- string cluster_id = 1 [(gogoproto.customname) = "ClusterID"]; |
|
| 293 |
+ string cluster_id = 1; |
|
| 294 | 294 |
} |
| 295 | 295 |
|
| 296 | 296 |
message GetClusterResponse {
|
| ... | ... |
@@ -300,7 +300,7 @@ message GetClusterResponse {
|
| 300 | 300 |
message ListClustersRequest {
|
| 301 | 301 |
message Filters {
|
| 302 | 302 |
repeated string names = 1; |
| 303 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 303 |
+ repeated string id_prefixes = 2; |
|
| 304 | 304 |
map<string, string> labels = 3; |
| 305 | 305 |
// NamePrefixes matches all objects with the given prefixes |
| 306 | 306 |
repeated string name_prefixes = 4; |
| ... | ... |
@@ -328,7 +328,7 @@ message KeyRotation {
|
| 328 | 328 |
|
| 329 | 329 |
message UpdateClusterRequest {
|
| 330 | 330 |
// ClusterID is the cluster ID to update. |
| 331 |
- string cluster_id = 1 [(gogoproto.customname) = "ClusterID"]; |
|
| 331 |
+ string cluster_id = 1; |
|
| 332 | 332 |
|
| 333 | 333 |
// ClusterVersion is the version of the cluster being updated. |
| 334 | 334 |
Version cluster_version = 2; |
| ... | ... |
@@ -346,7 +346,7 @@ message UpdateClusterResponse {
|
| 346 | 346 |
|
| 347 | 347 |
// GetSecretRequest is the request to get a `Secret` object given a secret id. |
| 348 | 348 |
message GetSecretRequest {
|
| 349 |
- string secret_id = 1 [(gogoproto.customname) = "SecretID"]; |
|
| 349 |
+ string secret_id = 1; |
|
| 350 | 350 |
} |
| 351 | 351 |
|
| 352 | 352 |
// GetSecretResponse contains the Secret corresponding to the id in |
| ... | ... |
@@ -358,7 +358,7 @@ message GetSecretResponse {
|
| 358 | 358 |
|
| 359 | 359 |
message UpdateSecretRequest {
|
| 360 | 360 |
// SecretID is the secret ID to update. |
| 361 |
- string secret_id = 1 [(gogoproto.customname) = "SecretID"]; |
|
| 361 |
+ string secret_id = 1; |
|
| 362 | 362 |
|
| 363 | 363 |
// SecretVersion is the version of the secret being updated. |
| 364 | 364 |
Version secret_version = 2; |
| ... | ... |
@@ -378,7 +378,7 @@ message UpdateSecretResponse {
|
| 378 | 378 |
message ListSecretsRequest {
|
| 379 | 379 |
message Filters {
|
| 380 | 380 |
repeated string names = 1; |
| 381 |
- repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; |
|
| 381 |
+ repeated string id_prefixes = 2; |
|
| 382 | 382 |
map<string, string> labels = 3; |
| 383 | 383 |
repeated string name_prefixes = 4; |
| 384 | 384 |
} |
| ... | ... |
@@ -410,7 +410,7 @@ message CreateSecretResponse {
|
| 410 | 410 |
// RemoveSecretRequest contains the ID of the secret that should be removed. This |
| 411 | 411 |
// removes all versions of the secret. |
| 412 | 412 |
message RemoveSecretRequest {
|
| 413 |
- string secret_id = 1 [(gogoproto.customname) = "SecretID"]; |
|
| 413 |
+ string secret_id = 1; |
|
| 414 | 414 |
} |
| 415 | 415 |
|
| 416 | 416 |
// RemoveSecretResponse is an empty object indicating the successful removal of |
| ... | ... |
@@ -66,7 +66,7 @@ message SessionRequest {
|
| 66 | 66 |
// SessionID is empty or invalid, a new SessionID will be assigned. |
| 67 | 67 |
// |
| 68 | 68 |
// See SessionMessage.SessionID for details. |
| 69 |
- string session_id = 2 [(gogoproto.customname) = "SessionID"]; |
|
| 69 |
+ string session_id = 2; |
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
// SessionMessage instructs an agent on various actions as part of the current |
| ... | ... |
@@ -115,7 +115,7 @@ message SessionMessage {
|
| 115 | 115 |
// We considered placing this field in a GRPC header. Because this is a |
| 116 | 116 |
// critical feature of the protocol, we thought it should be represented |
| 117 | 117 |
// directly in the RPC message set. |
| 118 |
- string session_id = 1 [(gogoproto.customname) = "SessionID"]; |
|
| 118 |
+ string session_id = 1; |
|
| 119 | 119 |
|
| 120 | 120 |
// Node identifies the registering node. |
| 121 | 121 |
Node node = 2; |
| ... | ... |
@@ -130,7 +130,7 @@ message SessionMessage {
|
| 130 | 130 |
|
| 131 | 131 |
// HeartbeatRequest provides identifying properties for a single heartbeat. |
| 132 | 132 |
message HeartbeatRequest {
|
| 133 |
- string session_id = 1 [(gogoproto.customname) = "SessionID"]; |
|
| 133 |
+ string session_id = 1; |
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
message HeartbeatResponse {
|
| ... | ... |
@@ -142,10 +142,10 @@ message HeartbeatResponse {
|
| 142 | 142 |
message UpdateTaskStatusRequest {
|
| 143 | 143 |
// Tasks should contain all statuses for running tasks. Only the status |
| 144 | 144 |
// field must be set. The spec is not required. |
| 145 |
- string session_id = 1 [(gogoproto.customname) = "SessionID"]; |
|
| 145 |
+ string session_id = 1; |
|
| 146 | 146 |
|
| 147 | 147 |
message TaskStatusUpdate {
|
| 148 |
- string task_id = 1 [(gogoproto.customname) = "TaskID"]; |
|
| 148 |
+ string task_id = 1; |
|
| 149 | 149 |
TaskStatus status = 2; |
| 150 | 150 |
} |
| 151 | 151 |
|
| ... | ... |
@@ -157,7 +157,7 @@ message UpdateTaskStatusResponse{
|
| 157 | 157 |
} |
| 158 | 158 |
|
| 159 | 159 |
message TasksRequest {
|
| 160 |
- string session_id = 1 [(gogoproto.customname) = "SessionID"]; |
|
| 160 |
+ string session_id = 1; |
|
| 161 | 161 |
} |
| 162 | 162 |
|
| 163 | 163 |
message TasksMessage {
|
| ... | ... |
@@ -167,7 +167,7 @@ message TasksMessage {
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
message AssignmentsRequest {
|
| 170 |
- string session_id = 1 [(gogoproto.customname) = "SessionID"]; |
|
| 170 |
+ string session_id = 1; |
|
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
message Assignment {
|
| ... | ... |
@@ -54,16 +54,16 @@ message LogSubscriptionOptions {
|
| 54 | 54 |
// possible. For example, if they want to listen to all the tasks of a service, |
| 55 | 55 |
// they should use the service id, rather than specifying the individual tasks. |
| 56 | 56 |
message LogSelector {
|
| 57 |
- repeated string service_ids = 1 [(gogoproto.customname) = "ServiceIDs"]; |
|
| 58 |
- repeated string node_ids = 2 [(gogoproto.customname) = "NodeIDs"]; |
|
| 59 |
- repeated string task_ids = 3 [(gogoproto.customname) = "TaskIDs"]; |
|
| 57 |
+ repeated string service_ids = 1; |
|
| 58 |
+ repeated string node_ids = 2; |
|
| 59 |
+ repeated string task_ids = 3; |
|
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 | 62 |
// LogContext marks the context from which a log message was generated. |
| 63 | 63 |
message LogContext {
|
| 64 |
- string service_id = 1 [(gogoproto.customname) = "ServiceID"]; |
|
| 65 |
- string node_id = 2 [(gogoproto.customname) = "NodeID"]; |
|
| 66 |
- string task_id = 3 [(gogoproto.customname) = "TaskID"]; |
|
| 64 |
+ string service_id = 1; |
|
| 65 |
+ string node_id = 2; |
|
| 66 |
+ string task_id = 3; |
|
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 | 69 |
// LogMessage |
| ... | ... |
@@ -147,7 +147,7 @@ message ListenSubscriptionsRequest { }
|
| 147 | 147 |
// If Options.Follow == false, the worker should end the subscription on its own. |
| 148 | 148 |
message SubscriptionMessage {
|
| 149 | 149 |
// ID identifies the subscription. |
| 150 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 150 |
+ string id = 1; |
|
| 151 | 151 |
|
| 152 | 152 |
// Selector defines which sources should be sent for the subscription. |
| 153 | 153 |
LogSelector selector = 2; |
| ... | ... |
@@ -163,7 +163,7 @@ message SubscriptionMessage {
|
| 163 | 163 |
message PublishLogsMessage {
|
| 164 | 164 |
// SubscriptionID identifies which subscription the set of messages should |
| 165 | 165 |
// be sent to. We can think of this as a "mail box" for the subscription. |
| 166 |
- string subscription_id = 1 [(gogoproto.customname) = "SubscriptionID"]; |
|
| 166 |
+ string subscription_id = 1; |
|
| 167 | 167 |
|
| 168 | 168 |
// Messages is the log message for publishing. |
| 169 | 169 |
repeated LogMessage messages = 2 [(gogoproto.nullable) = false]; |
| ... | ... |
@@ -25,7 +25,7 @@ message Meta {
|
| 25 | 25 |
// Node provides the internal node state as seen by the cluster. |
| 26 | 26 |
message Node {
|
| 27 | 27 |
// ID specifies the identity of the node. |
| 28 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 28 |
+ string id = 1; |
|
| 29 | 29 |
|
| 30 | 30 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 31 | 31 |
|
| ... | ... |
@@ -63,7 +63,7 @@ message Node {
|
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 | 65 |
message Service {
|
| 66 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 66 |
+ string id = 1; |
|
| 67 | 67 |
|
| 68 | 68 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 69 | 69 |
|
| ... | ... |
@@ -101,7 +101,7 @@ message Endpoint {
|
| 101 | 101 |
// and the IP addresses the target service will be made available under. |
| 102 | 102 |
message VirtualIP {
|
| 103 | 103 |
// NetworkID for which this endpoint attachment was created. |
| 104 |
- string network_id = 1 [(gogoproto.customname) = "NetworkID"]; |
|
| 104 |
+ string network_id = 1; |
|
| 105 | 105 |
|
| 106 | 106 |
// A virtual IP is used to address this service in IP |
| 107 | 107 |
// layer that the client can use to send requests to |
| ... | ... |
@@ -123,7 +123,7 @@ message Endpoint {
|
| 123 | 123 |
// immutable and idempotent. Once it is dispatched to a node, it will not be |
| 124 | 124 |
// dispatched to another node. |
| 125 | 125 |
message Task {
|
| 126 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 126 |
+ string id = 1; |
|
| 127 | 127 |
|
| 128 | 128 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 129 | 129 |
|
| ... | ... |
@@ -133,7 +133,7 @@ message Task {
|
| 133 | 133 |
|
| 134 | 134 |
// ServiceID indicates the service under which this task is orchestrated. This |
| 135 | 135 |
// should almost always be set. |
| 136 |
- string service_id = 4 [(gogoproto.customname) = "ServiceID"]; |
|
| 136 |
+ string service_id = 4; |
|
| 137 | 137 |
|
| 138 | 138 |
// Slot is the service slot number for a task. |
| 139 | 139 |
// For example, if a replicated service has replicas = 2, there will be a |
| ... | ... |
@@ -142,7 +142,7 @@ message Task {
|
| 142 | 142 |
|
| 143 | 143 |
// NodeID indicates the node to which the task is assigned. If this field |
| 144 | 144 |
// is empty or not set, the task is unassigned. |
| 145 |
- string node_id = 6 [(gogoproto.customname) = "NodeID"]; |
|
| 145 |
+ string node_id = 6; |
|
| 146 | 146 |
|
| 147 | 147 |
// Annotations defines the names and labels for the runtime, as set by |
| 148 | 148 |
// the cluster manager. |
| ... | ... |
@@ -204,7 +204,7 @@ message NetworkAttachment {
|
| 204 | 204 |
} |
| 205 | 205 |
|
| 206 | 206 |
message Network {
|
| 207 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 207 |
+ string id = 1; |
|
| 208 | 208 |
|
| 209 | 209 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 210 | 210 |
|
| ... | ... |
@@ -220,7 +220,7 @@ message Network {
|
| 220 | 220 |
|
| 221 | 221 |
// Cluster provides global cluster settings. |
| 222 | 222 |
message Cluster {
|
| 223 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 223 |
+ string id = 1; |
|
| 224 | 224 |
|
| 225 | 225 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 226 | 226 |
|
| ... | ... |
@@ -256,7 +256,7 @@ message Cluster {
|
| 256 | 256 |
// information that is generated from the secret data in the `spec`, such as |
| 257 | 257 |
// the digest and size of the secret data. |
| 258 | 258 |
message Secret {
|
| 259 |
- string id = 1 [(gogoproto.customname) = "ID"]; |
|
| 259 |
+ string id = 1; |
|
| 260 | 260 |
|
| 261 | 261 |
Meta meta = 2 [(gogoproto.nullable) = false]; |
| 262 | 262 |
|
| ... | ... |
@@ -40,10 +40,10 @@ service RaftMembership {
|
| 40 | 40 |
message RaftMember {
|
| 41 | 41 |
// RaftID specifies the internal ID used by the manager in a raft context, it can never be modified |
| 42 | 42 |
// and is used only for information purposes |
| 43 |
- uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; |
|
| 43 |
+ uint64 raft_id = 1; |
|
| 44 | 44 |
|
| 45 | 45 |
// NodeID is the node's ID. |
| 46 |
- string node_id = 2 [(gogoproto.customname) = "NodeID"]; |
|
| 46 |
+ string node_id = 2; |
|
| 47 | 47 |
|
| 48 | 48 |
// Addr specifies the address of the member |
| 49 | 49 |
string addr = 3; |
| ... | ... |
@@ -59,7 +59,7 @@ message JoinRequest {
|
| 59 | 59 |
|
| 60 | 60 |
message JoinResponse {
|
| 61 | 61 |
// RaftID is the ID assigned to the new member. |
| 62 |
- uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; |
|
| 62 |
+ uint64 raft_id = 1; |
|
| 63 | 63 |
|
| 64 | 64 |
// Members is the membership set of the cluster. |
| 65 | 65 |
repeated RaftMember members = 2; |
| ... | ... |
@@ -84,7 +84,7 @@ message ProcessRaftMessageResponse {}
|
| 84 | 84 |
|
| 85 | 85 |
message ResolveAddressRequest {
|
| 86 | 86 |
// raft_id is the ID to resolve to an address. |
| 87 |
- uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; |
|
| 87 |
+ uint64 raft_id = 1; |
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
message ResolveAddressResponse {
|
| ... | ... |
@@ -96,7 +96,7 @@ message ResolveAddressResponse {
|
| 96 | 96 |
// over the raft backend with a request ID to track when the |
| 97 | 97 |
// action is effectively applied |
| 98 | 98 |
message InternalRaftRequest {
|
| 99 |
- uint64 id = 1 [(gogoproto.customname) = "ID"]; |
|
| 99 |
+ uint64 id = 1; |
|
| 100 | 100 |
|
| 101 | 101 |
repeated StoreAction action = 2; |
| 102 | 102 |
} |
| ... | ... |
@@ -20,15 +20,15 @@ service ResourceAllocator {
|
| 20 | 20 |
|
| 21 | 21 |
message AttachNetworkRequest {
|
| 22 | 22 |
NetworkAttachmentConfig config = 1; |
| 23 |
- string container_id = 2 [(gogoproto.customname) = "ContainerID"]; |
|
| 23 |
+ string container_id = 2; |
|
| 24 | 24 |
} |
| 25 | 25 |
|
| 26 | 26 |
message AttachNetworkResponse {
|
| 27 |
- string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"]; |
|
| 27 |
+ string attachment_id = 1; |
|
| 28 | 28 |
} |
| 29 | 29 |
|
| 30 | 30 |
message DetachNetworkRequest {
|
| 31 |
- string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"]; |
|
| 31 |
+ string attachment_id = 1; |
|
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 | 34 |
message DetachNetworkResponse {}
|
| ... | ... |
@@ -130,7 +130,7 @@ message TaskSpec {
|
| 130 | 130 |
message NetworkAttachmentSpec {
|
| 131 | 131 |
// ContainerID spcifies a unique ID of the container for which |
| 132 | 132 |
// this attachment is for. |
| 133 |
- string container_id = 1 [(gogoproto.customname) = "ContainerID"]; |
|
| 133 |
+ string container_id = 1; |
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
|
| ... | ... |
@@ -410,7 +410,7 @@ enum TaskState {
|
| 410 | 410 |
|
| 411 | 411 |
// Container specific status. |
| 412 | 412 |
message ContainerStatus {
|
| 413 |
- string container_id = 1 [(gogoproto.customname) = "ContainerID"]; |
|
| 413 |
+ string container_id = 1; |
|
| 414 | 414 |
|
| 415 | 415 |
int32 pid = 2 [(gogoproto.customname) = "PID"]; |
| 416 | 416 |
int32 exit_code = 3; |
| ... | ... |
@@ -574,7 +574,7 @@ message IPAMOptions {
|
| 574 | 574 |
|
| 575 | 575 |
// Peer should be used anywhere where we are describing a remote peer. |
| 576 | 576 |
message Peer {
|
| 577 |
- string node_id = 1 [(gogoproto.customname) = "NodeID"]; |
|
| 577 |
+ string node_id = 1; |
|
| 578 | 578 |
string addr = 2; |
| 579 | 579 |
} |
| 580 | 580 |
|
| ... | ... |
@@ -787,7 +787,7 @@ message EncryptionKey {
|
| 787 | 787 |
message ManagerStatus {
|
| 788 | 788 |
// RaftID specifies the internal ID used by the manager in a raft context, it can never be modified |
| 789 | 789 |
// and is used only for information purposes |
| 790 |
- uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; |
|
| 790 |
+ uint64 raft_id = 1; |
|
| 791 | 791 |
|
| 792 | 792 |
// Addr is the address advertised to raft. |
| 793 | 793 |
string addr = 2; |
| ... | ... |
@@ -805,7 +805,7 @@ message SecretReference {
|
| 805 | 805 |
// SecretID represents the ID of the specific Secret that we're |
| 806 | 806 |
// referencing. This identifier exists so that SecretReferences don't leak |
| 807 | 807 |
// any information about the secret contents. |
| 808 |
- string secret_id = 1 [(gogoproto.customname) = "SecretID"]; |
|
| 808 |
+ string secret_id = 1; |
|
| 809 | 809 |
|
| 810 | 810 |
// SecretName is the name of the secret that this references, but this is just provided for |
| 811 | 811 |
// lookup/display purposes. The secret in the reference will be identified by its ID. |
| ... | ... |
@@ -151,6 +151,25 @@ func (rca *RootCA) IssueAndSaveNewCertificates(kw KeyWriter, cn, ou, org string) |
| 151 | 151 |
return &tlsKeyPair, nil |
| 152 | 152 |
} |
| 153 | 153 |
|
| 154 |
+// Normally we can just call cert.Verify(opts), but since we actually want more information about |
|
| 155 |
+// whether a certificate is not yet valid or expired, we also need to perform the expiry checks ourselves. |
|
| 156 |
+func verifyCertificate(cert *x509.Certificate, opts x509.VerifyOptions, allowExpired bool) error {
|
|
| 157 |
+ _, err := cert.Verify(opts) |
|
| 158 |
+ if invalidErr, ok := err.(x509.CertificateInvalidError); ok && invalidErr.Reason == x509.Expired {
|
|
| 159 |
+ now := time.Now().UTC() |
|
| 160 |
+ if now.Before(cert.NotBefore) {
|
|
| 161 |
+ return errors.Wrapf(err, "certificate not valid before %s, and it is currently %s", |
|
| 162 |
+ cert.NotBefore.UTC().Format(time.RFC1123), now.Format(time.RFC1123)) |
|
| 163 |
+ } |
|
| 164 |
+ if allowExpired {
|
|
| 165 |
+ return nil |
|
| 166 |
+ } |
|
| 167 |
+ return errors.Wrapf(err, "certificate expires at %s, and it is currently %s", |
|
| 168 |
+ cert.NotAfter.UTC().Format(time.RFC1123), now.Format(time.RFC1123)) |
|
| 169 |
+ } |
|
| 170 |
+ return err |
|
| 171 |
+} |
|
| 172 |
+ |
|
| 154 | 173 |
// RequestAndSaveNewCertificates gets new certificates issued, either by signing them locally if a signer is |
| 155 | 174 |
// available, or by requesting them from the remote server at remoteAddr. |
| 156 | 175 |
func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWriter, config CertificateRequestConfig) (*tls.Certificate, error) {
|
| ... | ... |
@@ -199,7 +218,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit |
| 199 | 199 |
Roots: rca.Pool, |
| 200 | 200 |
} |
| 201 | 201 |
// Check to see if this certificate was signed by our CA, and isn't expired |
| 202 |
- if _, err := X509Cert.Verify(opts); err != nil {
|
|
| 202 |
+ if err := verifyCertificate(X509Cert, opts, false); err != nil {
|
|
| 203 | 203 |
return nil, err |
| 204 | 204 |
} |
| 205 | 205 |
|
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
|
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 | 17 |
cfconfig "github.com/cloudflare/cfssl/config" |
| 18 |
+ events "github.com/docker/go-events" |
|
| 18 | 19 |
"github.com/docker/swarmkit/api" |
| 19 | 20 |
"github.com/docker/swarmkit/connectionbroker" |
| 20 | 21 |
"github.com/docker/swarmkit/identity" |
| ... | ... |
@@ -50,6 +51,13 @@ const ( |
| 50 | 50 |
base36DigestLen = 50 |
| 51 | 51 |
) |
| 52 | 52 |
|
| 53 |
+// RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired |
|
| 54 |
+var RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{
|
|
| 55 |
+ Base: time.Second * 5, |
|
| 56 |
+ Factor: time.Minute, |
|
| 57 |
+ Max: 1 * time.Hour, |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 53 | 60 |
// SecurityConfig is used to represent a node's security configuration. It includes information about |
| 54 | 61 |
// the RootCA and ServerTLSCreds/ClientTLSCreds transport authenticators to be used for MTLS |
| 55 | 62 |
type SecurityConfig struct {
|
| ... | ... |
@@ -189,7 +197,7 @@ func GenerateJoinToken(rootCA *RootCA) string {
|
| 189 | 189 |
|
| 190 | 190 |
func getCAHashFromToken(token string) (digest.Digest, error) {
|
| 191 | 191 |
split := strings.Split(token, "-") |
| 192 |
- if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" {
|
|
| 192 |
+ if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" || len(split[2]) != base36DigestLen || len(split[3]) != maxGeneratedSecretLength {
|
|
| 193 | 193 |
return "", errors.New("invalid join token")
|
| 194 | 194 |
} |
| 195 | 195 |
|
| ... | ... |
@@ -242,7 +250,7 @@ func DownloadRootCA(ctx context.Context, paths CertPaths, token string, connBrok |
| 242 | 242 |
|
| 243 | 243 |
// LoadSecurityConfig loads TLS credentials from disk, or returns an error if |
| 244 | 244 |
// these credentials do not exist or are unusable. |
| 245 |
-func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter) (*SecurityConfig, error) {
|
|
| 245 |
+func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter, allowExpired bool) (*SecurityConfig, error) {
|
|
| 246 | 246 |
ctx = log.WithModule(ctx, "tls") |
| 247 | 247 |
|
| 248 | 248 |
// At this point we've successfully loaded the CA details from disk, or |
| ... | ... |
@@ -273,7 +281,7 @@ func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter) |
| 273 | 273 |
} |
| 274 | 274 |
|
| 275 | 275 |
// Check to see if this certificate was signed by our CA, and isn't expired |
| 276 |
- if _, err := X509Cert.Verify(opts); err != nil {
|
|
| 276 |
+ if err := verifyCertificate(X509Cert, opts, allowExpired); err != nil {
|
|
| 277 | 277 |
return nil, err |
| 278 | 278 |
} |
| 279 | 279 |
|
| ... | ... |
@@ -447,6 +455,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti |
| 447 | 447 |
|
| 448 | 448 |
go func() {
|
| 449 | 449 |
var retry time.Duration |
| 450 |
+ expBackoff := events.NewExponentialBackoff(RenewTLSExponentialBackoff) |
|
| 450 | 451 |
defer close(updates) |
| 451 | 452 |
for {
|
| 452 | 453 |
ctx = log.WithModule(ctx, "tls") |
| ... | ... |
@@ -472,18 +481,12 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti |
| 472 | 472 |
return |
| 473 | 473 |
} |
| 474 | 474 |
} else {
|
| 475 |
- // If we have an expired certificate, we let's stick with the starting default in |
|
| 476 |
- // the hope that this is a temporary clock skew. |
|
| 475 |
+ // If we have an expired certificate, try to renew immediately: the hope that this is a temporary clock skew, or |
|
| 476 |
+ // we can issue our own TLS certs. |
|
| 477 | 477 |
if validUntil.Before(time.Now()) {
|
| 478 |
- log.WithError(err).Errorf("failed to create a new client TLS config")
|
|
| 479 |
- |
|
| 480 |
- select {
|
|
| 481 |
- case updates <- CertificateUpdate{Err: errors.New("TLS certificate is expired")}:
|
|
| 482 |
- case <-ctx.Done(): |
|
| 483 |
- log.Info("shutting down certificate renewal routine")
|
|
| 484 |
- return |
|
| 485 |
- } |
|
| 486 |
- |
|
| 478 |
+ log.Warn("the current TLS certificate is expired, so an attempt to renew it will be made immediately")
|
|
| 479 |
+ // retry immediately(ish) with exponential backoff |
|
| 480 |
+ retry = expBackoff.Proceed(nil) |
|
| 487 | 481 |
} else {
|
| 488 | 482 |
// Random retry time between 50% and 80% of the total time to expiration |
| 489 | 483 |
retry = calculateRandomExpiry(validFrom, validUntil) |
| ... | ... |
@@ -492,7 +495,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti |
| 492 | 492 |
|
| 493 | 493 |
log.WithFields(logrus.Fields{
|
| 494 | 494 |
"time": time.Now().Add(retry), |
| 495 |
- }).Debugf("next certificate renewal scheduled")
|
|
| 495 |
+ }).Debugf("next certificate renewal scheduled for %v from now", retry)
|
|
| 496 | 496 |
|
| 497 | 497 |
select {
|
| 498 | 498 |
case <-time.After(retry): |
| ... | ... |
@@ -508,8 +511,10 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti |
| 508 | 508 |
var certUpdate CertificateUpdate |
| 509 | 509 |
if err := RenewTLSConfigNow(ctx, s, connBroker); err != nil {
|
| 510 | 510 |
certUpdate.Err = err |
| 511 |
+ expBackoff.Failure(nil, nil) |
|
| 511 | 512 |
} else {
|
| 512 | 513 |
certUpdate.Role = s.ClientTLSCreds.Role() |
| 514 |
+ expBackoff = events.NewExponentialBackoff(RenewTLSExponentialBackoff) |
|
| 513 | 515 |
} |
| 514 | 516 |
|
| 515 | 517 |
select {
|
| ... | ... |
@@ -110,7 +110,7 @@ func validateContainerSpec(container *api.ContainerSpec) error {
|
| 110 | 110 |
return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: image reference must be provided") |
| 111 | 111 |
} |
| 112 | 112 |
|
| 113 |
- if _, err := reference.ParseNamed(container.Image); err != nil {
|
|
| 113 |
+ if _, err := reference.ParseNormalizedNamed(container.Image); err != nil {
|
|
| 114 | 114 |
return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: %q is not a valid repository/tag", container.Image) |
| 115 | 115 |
} |
| 116 | 116 |
|
| ... | ... |
@@ -275,6 +275,21 @@ func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error |
| 275 | 275 |
return nil |
| 276 | 276 |
} |
| 277 | 277 |
|
| 278 |
+func validateMode(s *api.ServiceSpec) error {
|
|
| 279 |
+ m := s.GetMode() |
|
| 280 |
+ switch m.(type) {
|
|
| 281 |
+ case *api.ServiceSpec_Replicated: |
|
| 282 |
+ if int64(m.(*api.ServiceSpec_Replicated).Replicated.Replicas) < 0 {
|
|
| 283 |
+ return grpc.Errorf(codes.InvalidArgument, "Number of replicas must be non-negative") |
|
| 284 |
+ } |
|
| 285 |
+ case *api.ServiceSpec_Global: |
|
| 286 |
+ default: |
|
| 287 |
+ return grpc.Errorf(codes.InvalidArgument, "Unrecognized service mode") |
|
| 288 |
+ } |
|
| 289 |
+ |
|
| 290 |
+ return nil |
|
| 291 |
+} |
|
| 292 |
+ |
|
| 278 | 293 |
func validateServiceSpec(spec *api.ServiceSpec) error {
|
| 279 | 294 |
if spec == nil {
|
| 280 | 295 |
return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) |
| ... | ... |
@@ -291,6 +306,9 @@ func validateServiceSpec(spec *api.ServiceSpec) error {
|
| 291 | 291 |
if err := validateEndpointSpec(spec.Endpoint); err != nil {
|
| 292 | 292 |
return err |
| 293 | 293 |
} |
| 294 |
+ if err := validateMode(spec); err != nil {
|
|
| 295 |
+ return err |
|
| 296 |
+ } |
|
| 294 | 297 |
// Check to see if the Secret Reference portion of the spec is valid |
| 295 | 298 |
if err := validateSecretRefsSpec(spec); err != nil {
|
| 296 | 299 |
return err |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package logbroker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "context" |
|
| 5 | 4 |
"fmt" |
| 6 | 5 |
"strings" |
| 7 | 6 |
"sync" |
| ... | ... |
@@ -12,6 +11,7 @@ import ( |
| 12 | 12 |
"github.com/docker/swarmkit/manager/state" |
| 13 | 13 |
"github.com/docker/swarmkit/manager/state/store" |
| 14 | 14 |
"github.com/docker/swarmkit/watch" |
| 15 |
+ "golang.org/x/net/context" |
|
| 15 | 16 |
) |
| 16 | 17 |
|
| 17 | 18 |
type subscription struct {
|
| ... | ... |
@@ -102,15 +102,15 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
|
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 | 104 |
deploy := service.Spec.GetMode().(*api.ServiceSpec_Replicated) |
| 105 |
- specifiedSlots := int(deploy.Replicated.Replicas) |
|
| 105 |
+ specifiedSlots := deploy.Replicated.Replicas |
|
| 106 | 106 |
|
| 107 | 107 |
switch {
|
| 108 |
- case specifiedSlots > numSlots: |
|
| 108 |
+ case specifiedSlots > uint64(numSlots): |
|
| 109 | 109 |
log.G(ctx).Debugf("Service %s was scaled up from %d to %d instances", service.ID, numSlots, specifiedSlots)
|
| 110 | 110 |
// Update all current tasks then add missing tasks |
| 111 | 111 |
r.updater.Update(ctx, r.cluster, service, slotsSlice) |
| 112 | 112 |
_, err = r.store.Batch(func(batch *store.Batch) error {
|
| 113 |
- r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-numSlots) |
|
| 113 |
+ r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-uint64(numSlots)) |
|
| 114 | 114 |
r.deleteTasksMap(ctx, batch, deadSlots) |
| 115 | 115 |
return nil |
| 116 | 116 |
}) |
| ... | ... |
@@ -118,7 +118,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
|
| 118 | 118 |
log.G(ctx).WithError(err).Errorf("reconcile batch failed")
|
| 119 | 119 |
} |
| 120 | 120 |
|
| 121 |
- case specifiedSlots < numSlots: |
|
| 121 |
+ case specifiedSlots < uint64(numSlots): |
|
| 122 | 122 |
// Update up to N tasks then remove the extra |
| 123 | 123 |
log.G(ctx).Debugf("Service %s was scaled down from %d to %d instances", service.ID, numSlots, specifiedSlots)
|
| 124 | 124 |
|
| ... | ... |
@@ -165,7 +165,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
|
| 165 | 165 |
log.G(ctx).WithError(err).Errorf("reconcile batch failed")
|
| 166 | 166 |
} |
| 167 | 167 |
|
| 168 |
- case specifiedSlots == numSlots: |
|
| 168 |
+ case specifiedSlots == uint64(numSlots): |
|
| 169 | 169 |
_, err = r.store.Batch(func(batch *store.Batch) error {
|
| 170 | 170 |
r.deleteTasksMap(ctx, batch, deadSlots) |
| 171 | 171 |
return nil |
| ... | ... |
@@ -178,9 +178,9 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
|
| 178 | 178 |
} |
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
-func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count int) {
|
|
| 181 |
+func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count uint64) {
|
|
| 182 | 182 |
slot := uint64(0) |
| 183 |
- for i := 0; i < count; i++ {
|
|
| 183 |
+ for i := uint64(0); i < count; i++ {
|
|
| 184 | 184 |
// Find a slot number that is missing a running task |
| 185 | 185 |
for {
|
| 186 | 186 |
slot++ |
| ... | ... |
@@ -1193,9 +1193,13 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa |
| 1193 | 1193 |
ctx, cancel := n.WithContext(ctx) |
| 1194 | 1194 |
defer cancel() |
| 1195 | 1195 |
|
| 1196 |
- if err := n.reportNewAddress(ctx, msg.Message.From); err != nil {
|
|
| 1197 |
- log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From)
|
|
| 1198 |
- } |
|
| 1196 |
+ // TODO(aaronl): Address changes are temporarily disabled. |
|
| 1197 |
+ // See https://github.com/docker/docker/issues/30455. |
|
| 1198 |
+ // This should be reenabled in the future with additional |
|
| 1199 |
+ // safeguards (perhaps storing multiple addresses per node). |
|
| 1200 |
+ //if err := n.reportNewAddress(ctx, msg.Message.From); err != nil {
|
|
| 1201 |
+ // log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From)
|
|
| 1202 |
+ //} |
|
| 1199 | 1203 |
|
| 1200 | 1204 |
// Reject vote requests from unreachable peers |
| 1201 | 1205 |
if msg.Message.Type == raftpb.MsgVote {
|
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package storage |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "context" |
|
| 5 | 4 |
"io" |
| 6 | 5 |
"io/ioutil" |
| 7 | 6 |
"os" |
| ... | ... |
@@ -15,6 +14,7 @@ import ( |
| 15 | 15 |
"github.com/docker/swarmkit/log" |
| 16 | 16 |
"github.com/docker/swarmkit/manager/encryption" |
| 17 | 17 |
"github.com/pkg/errors" |
| 18 |
+ "golang.org/x/net/context" |
|
| 18 | 19 |
) |
| 19 | 20 |
|
| 20 | 21 |
// This package wraps the github.com/coreos/etcd/wal package, and encrypts |
| ... | ... |
@@ -568,7 +568,8 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro |
| 568 | 568 |
return nil, err |
| 569 | 569 |
} |
| 570 | 570 |
if err == nil {
|
| 571 |
- securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw) |
|
| 571 |
+ // if forcing a new cluster, we allow the certificates to be expired - a new set will be generated |
|
| 572 |
+ securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster) |
|
| 572 | 573 |
if err != nil {
|
| 573 | 574 |
_, isInvalidKEK := errors.Cause(err).(ca.ErrInvalidKEK) |
| 574 | 575 |
if isInvalidKEK {
|
| ... | ... |
@@ -606,7 +607,7 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro |
| 606 | 606 |
// - We wait for CreateSecurityConfig to finish since we need a certificate to operate. |
| 607 | 607 |
|
| 608 | 608 |
// Attempt to load certificate from disk |
| 609 |
- securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw) |
|
| 609 |
+ securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster) |
|
| 610 | 610 |
if err == nil {
|
| 611 | 611 |
log.G(ctx).WithFields(logrus.Fields{
|
| 612 | 612 |
"node.id": securityConfig.ClientTLSCreds.NodeID(), |