forked from TrueCloudLab/distribution
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 <stephen.day@docker.com>
This commit is contained in:
parent
5ea13fc549
commit
a58848a0b7
3 changed files with 37 additions and 36 deletions
|
@ -6,19 +6,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(stevvooe): Move these definitions back to an exported package. While
|
// TODO(stevvooe): Move these definitions to the future "reference" package.
|
||||||
// they are used with v2 definitions, their relevance expands beyond.
|
// While they are used with v2 definitions, their relevance expands beyond.
|
||||||
// "distribution/names" is a candidate package.
|
|
||||||
|
|
||||||
const (
|
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
|
// RepositoryNameTotalLengthMax is the maximum total number of characters in
|
||||||
// a repository name
|
// a repository name
|
||||||
RepositoryNameTotalLengthMax = 255
|
RepositoryNameTotalLengthMax = 255
|
||||||
|
@ -40,17 +31,13 @@ var RepositoryNameRegexp = regexp.MustCompile(`(?:` + RepositoryNameComponentReg
|
||||||
// TagNameRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
// TagNameRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||||
var TagNameRegexp = regexp.MustCompile(`[\w][\w.-]{0,127}`)
|
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 (
|
var (
|
||||||
// ErrRepositoryNameComponentShort is returned when a repository name
|
// ErrRepositoryNameEmpty is returned for empty, invalid repository names.
|
||||||
// contains a component which is shorter than
|
ErrRepositoryNameEmpty = fmt.Errorf("repository name must have at least one component")
|
||||||
// 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)
|
|
||||||
|
|
||||||
// ErrRepositoryNameLong is returned when a repository name is longer than
|
// ErrRepositoryNameLong is returned when a repository name is longer than
|
||||||
// RepositoryNameTotalLengthMax
|
// RepositoryNameTotalLengthMax
|
||||||
|
@ -76,21 +63,17 @@ var (
|
||||||
// The result of the production, known as the "namespace", should be limited
|
// The result of the production, known as the "namespace", should be limited
|
||||||
// to 255 characters.
|
// to 255 characters.
|
||||||
func ValidateRepositoryName(name string) error {
|
func ValidateRepositoryName(name string) error {
|
||||||
|
if name == "" {
|
||||||
|
return ErrRepositoryNameEmpty
|
||||||
|
}
|
||||||
|
|
||||||
if len(name) > RepositoryNameTotalLengthMax {
|
if len(name) > RepositoryNameTotalLengthMax {
|
||||||
return ErrRepositoryNameLong
|
return ErrRepositoryNameLong
|
||||||
}
|
}
|
||||||
|
|
||||||
components := strings.Split(name, "/")
|
components := strings.Split(name, "/")
|
||||||
|
|
||||||
if len(components) < RepositoryNameMinComponents {
|
|
||||||
return ErrRepositoryNameMissingComponents
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, component := range components {
|
for _, component := range components {
|
||||||
if len(component) < RepositoryNameComponentMinLength {
|
|
||||||
return ErrRepositoryNameComponentShort
|
|
||||||
}
|
|
||||||
|
|
||||||
if !RepositoryNameComponentAnchoredRegexp.MatchString(component) {
|
if !RepositoryNameComponentAnchoredRegexp.MatchString(component) {
|
||||||
return ErrRepositoryNameComponentInvalid
|
return ErrRepositoryNameComponentInvalid
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -10,6 +11,10 @@ func TestRepositoryNameRegexp(t *testing.T) {
|
||||||
input string
|
input string
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
input: "",
|
||||||
|
err: ErrRepositoryNameEmpty,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: "short",
|
input: "short",
|
||||||
},
|
},
|
||||||
|
@ -30,11 +35,26 @@ func TestRepositoryNameRegexp(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "a/a/a/b/b",
|
input: "a/a/a/b/b",
|
||||||
err: ErrRepositoryNameComponentShort,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "a/a/a/a/",
|
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",
|
input: "foo.com/bar/baz",
|
||||||
|
@ -58,10 +78,6 @@ func TestRepositoryNameRegexp(t *testing.T) {
|
||||||
{
|
{
|
||||||
input: "a-a/a-a",
|
input: "a-a/a-a",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
input: "a",
|
|
||||||
err: ErrRepositoryNameComponentShort,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
input: "a-/a/a/a",
|
input: "a-/a/a/a",
|
||||||
err: ErrRepositoryNameComponentInvalid,
|
err: ErrRepositoryNameComponentInvalid,
|
||||||
|
@ -110,9 +126,8 @@ func TestRepositoryNameRegexp(t *testing.T) {
|
||||||
err: ErrRepositoryNameComponentInvalid,
|
err: ErrRepositoryNameComponentInvalid,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
failf := func(format string, v ...interface{}) {
|
failf := func(format string, v ...interface{}) {
|
||||||
t.Logf(testcase.input+": "+format, v...)
|
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,6 +263,7 @@ func checkTestRouter(t *testing.T, testCases []routeTestCase, prefix string, dee
|
||||||
}
|
}
|
||||||
|
|
||||||
if testcase.StatusCode != http.StatusOK {
|
if testcase.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
// We don't care about json response.
|
// We don't care about json response.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -291,6 +292,8 @@ func checkTestRouter(t *testing.T, testCases []routeTestCase, prefix string, dee
|
||||||
if deeplyEqual && !reflect.DeepEqual(actualRouteInfo, testcase) {
|
if deeplyEqual && !reflect.DeepEqual(actualRouteInfo, testcase) {
|
||||||
t.Fatalf("actual does not equal expected: %#v != %#v", actualRouteInfo, testcase)
|
t.Fatalf("actual does not equal expected: %#v != %#v", actualRouteInfo, testcase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue