From b54cc5ed474f938e0219214b56e639881747d028 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 10 Jul 2015 12:00:06 -0600 Subject: [PATCH] Allow single character repository names The main goal of this changeset is to allow repository name components to consist of a single character. The number of components allowed and the slash separation requirements have also been clarified. To go along with this simplification, errant constants and unneeded error types have been removed. Signed-off-by: Stephen J Day --- docs/spec/api.md | 15 ++++++++----- docs/spec/api.md.tmpl | 15 ++++++++----- registry/api/v2/names.go | 39 ++++++++++------------------------ registry/api/v2/names_test.go | 31 ++++++++++++++++++++------- registry/api/v2/routes_test.go | 3 +++ 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/docs/spec/api.md b/docs/spec/api.md index 5196d05c5..483f15f03 100644 --- a/docs/spec/api.md +++ b/docs/spec/api.md @@ -120,6 +120,12 @@ indicating what is different. Optionally, we may start marking parts of the specification to correspond with the versions enumerated here.
+
2.0.3
+
+
  • Allow repository name components to be one character.
  • +
  • Clarified that single component names are allowed.
  • +
    +
    2.0.2
  • Added section covering digest format.
  • @@ -174,12 +180,11 @@ path component is less than 30 characters. The V2 registry API does not enforce this. The rules for a repository name are as follows: 1. A repository name is broken up into _path components_. A component of a - repository name must be at least two lowercase, alpha-numeric characters, + repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it - must match the regular expression `[a-z0-9]+(?:[._-][a-z0-9]+)*` and the - matched result must be 2 or more characters in length. -2. The name of a repository must have at least two path components, separated - by a forward slash. + must match the regular expression `[a-z0-9]+(?:[._-][a-z0-9]+)*`. +2. If a repository name has two or more path components, they must be + separated by a forward slash ("/"). 3. The total length of a repository name, including slashes, must be less the 256 characters. diff --git a/docs/spec/api.md.tmpl b/docs/spec/api.md.tmpl index 878f0e4da..bc6d6f926 100644 --- a/docs/spec/api.md.tmpl +++ b/docs/spec/api.md.tmpl @@ -120,6 +120,12 @@ indicating what is different. Optionally, we may start marking parts of the specification to correspond with the versions enumerated here.
    +
    2.0.3
    +
    +
  • Allow repository name components to be one character.
  • +
  • Clarified that single component names are allowed.
  • +
    +
    2.0.2
  • Added section covering digest format.
  • @@ -174,12 +180,11 @@ path component is less than 30 characters. The V2 registry API does not enforce this. The rules for a repository name are as follows: 1. A repository name is broken up into _path components_. A component of a - repository name must be at least two lowercase, alpha-numeric characters, + repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it - must match the regular expression `[a-z0-9]+(?:[._-][a-z0-9]+)*` and the - matched result must be 2 or more characters in length. -2. The name of a repository must have at least two path components, separated - by a forward slash. + must match the regular expression `[a-z0-9]+(?:[._-][a-z0-9]+)*`. +2. If a repository name has two or more path components, they must be + separated by a forward slash ("/"). 3. The total length of a repository name, including slashes, must be less the 256 characters. diff --git a/registry/api/v2/names.go b/registry/api/v2/names.go index 19cb72a02..14b7ea60a 100644 --- a/registry/api/v2/names.go +++ b/registry/api/v2/names.go @@ -6,19 +6,10 @@ import ( "strings" ) -// TODO(stevvooe): Move these definitions back to an exported package. While -// they are used with v2 definitions, their relevance expands beyond. -// "distribution/names" is a candidate package. +// TODO(stevvooe): Move these definitions to the future "reference" package. +// While they are used with v2 definitions, their relevance expands beyond. const ( - // RepositoryNameComponentMinLength is the minimum number of characters in a - // single repository name slash-delimited component - RepositoryNameComponentMinLength = 2 - - // RepositoryNameMinComponents is the minimum number of slash-delimited - // components that a repository name must have - RepositoryNameMinComponents = 1 - // RepositoryNameTotalLengthMax is the maximum total number of characters in // a repository name RepositoryNameTotalLengthMax = 255 @@ -40,17 +31,13 @@ var RepositoryNameRegexp = regexp.MustCompile(`(?:` + RepositoryNameComponentReg // TagNameRegexp matches valid tag names. From docker/docker:graph/tags.go. var TagNameRegexp = regexp.MustCompile(`[\w][\w.-]{0,127}`) -// TODO(stevvooe): Contribute these exports back to core, so they are shared. +// TagNameAnchoredRegexp matches valid tag names, anchored at the start and +// end of the matched string. +var TagNameAnchoredRegexp = regexp.MustCompile("^" + TagNameRegexp.String() + "$") var ( - // ErrRepositoryNameComponentShort is returned when a repository name - // contains a component which is shorter than - // RepositoryNameComponentMinLength - ErrRepositoryNameComponentShort = fmt.Errorf("repository name component must be %v or more characters", RepositoryNameComponentMinLength) - - // ErrRepositoryNameMissingComponents is returned when a repository name - // contains fewer than RepositoryNameMinComponents components - ErrRepositoryNameMissingComponents = fmt.Errorf("repository name must have at least %v components", RepositoryNameMinComponents) + // ErrRepositoryNameEmpty is returned for empty, invalid repository names. + ErrRepositoryNameEmpty = fmt.Errorf("repository name must have at least one component") // ErrRepositoryNameLong is returned when a repository name is longer than // RepositoryNameTotalLengthMax @@ -76,21 +63,17 @@ var ( // The result of the production, known as the "namespace", should be limited // to 255 characters. func ValidateRepositoryName(name string) error { + if name == "" { + return ErrRepositoryNameEmpty + } + if len(name) > RepositoryNameTotalLengthMax { return ErrRepositoryNameLong } components := strings.Split(name, "/") - if len(components) < RepositoryNameMinComponents { - return ErrRepositoryNameMissingComponents - } - for _, component := range components { - if len(component) < RepositoryNameComponentMinLength { - return ErrRepositoryNameComponentShort - } - if !RepositoryNameComponentAnchoredRegexp.MatchString(component) { return ErrRepositoryNameComponentInvalid } diff --git a/registry/api/v2/names_test.go b/registry/api/v2/names_test.go index 0975fb7c8..51e0ba8b3 100644 --- a/registry/api/v2/names_test.go +++ b/registry/api/v2/names_test.go @@ -1,6 +1,7 @@ package v2 import ( + "strconv" "strings" "testing" ) @@ -10,6 +11,10 @@ func TestRepositoryNameRegexp(t *testing.T) { input string err error }{ + { + input: "", + err: ErrRepositoryNameEmpty, + }, { input: "short", }, @@ -30,11 +35,26 @@ func TestRepositoryNameRegexp(t *testing.T) { }, { input: "a/a/a/b/b", - err: ErrRepositoryNameComponentShort, }, { input: "a/a/a/a/", - err: ErrRepositoryNameComponentShort, + err: ErrRepositoryNameComponentInvalid, + }, + { + input: "a//a/a", + err: ErrRepositoryNameComponentInvalid, + }, + { + input: "a", + }, + { + input: "a/aa", + }, + { + input: "aa/a", + }, + { + input: "a/aa/a", }, { input: "foo.com/bar/baz", @@ -58,10 +78,6 @@ func TestRepositoryNameRegexp(t *testing.T) { { input: "a-a/a-a", }, - { - input: "a", - err: ErrRepositoryNameComponentShort, - }, { input: "a-/a/a/a", err: ErrRepositoryNameComponentInvalid, @@ -110,9 +126,8 @@ func TestRepositoryNameRegexp(t *testing.T) { err: ErrRepositoryNameComponentInvalid, }, } { - failf := func(format string, v ...interface{}) { - t.Logf(testcase.input+": "+format, v...) + t.Logf(strconv.Quote(testcase.input)+": "+format, v...) t.Fail() } diff --git a/registry/api/v2/routes_test.go b/registry/api/v2/routes_test.go index fb268336f..9fd29a4f5 100644 --- a/registry/api/v2/routes_test.go +++ b/registry/api/v2/routes_test.go @@ -263,6 +263,7 @@ func checkTestRouter(t *testing.T, testCases []routeTestCase, prefix string, dee } if testcase.StatusCode != http.StatusOK { + resp.Body.Close() // We don't care about json response. continue } @@ -291,6 +292,8 @@ func checkTestRouter(t *testing.T, testCases []routeTestCase, prefix string, dee if deeplyEqual && !reflect.DeepEqual(actualRouteInfo, testcase) { t.Fatalf("actual does not equal expected: %#v != %#v", actualRouteInfo, testcase) } + + resp.Body.Close() } }