identifiers: use common package for identifier validation

A few days ago, we added validation for namespaces. We've decided to
expand these naming rules to include containers. To facilitate this, a
common package `identifiers` now provides a common validation area.
These rules will be extended to apply to task identifiers, snapshot keys
and other areas where user-provided identifiers may be used.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day
2017-06-23 16:17:56 -07:00
parent e69423f931
commit 70815af652
9 changed files with 119 additions and 136 deletions

View File

@@ -3,6 +3,7 @@ package namespaces
import (
"os"
"github.com/containerd/containerd/identifiers"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@@ -61,8 +62,8 @@ func NamespaceRequired(ctx context.Context) (string, error) {
return "", errNamespaceRequired
}
if err := Validate(namespace); err != nil {
return "", err
if err := identifiers.Validate(namespace); err != nil {
return "", errors.Wrap(err, "namespace validation")
}
return namespace, nil

View File

@@ -1,52 +0,0 @@
package namespaces
import (
"regexp"
"github.com/pkg/errors"
)
const (
label = `[a-z][a-z0-9]+(?:[-]+[a-z0-9]+)*`
)
func reGroup(s string) string {
return `(?:` + s + `)`
}
func reAnchor(s string) string {
return `^` + s + `$`
}
var (
// namespaceRe validates that a namespace matches valid namespaces.
//
// Rules for domains, defined in RFC 1035, section 2.3.1, are used for
// namespaces.
namespaceRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*"))
errNamespaceInvalid = errors.Errorf("invalid namespace, must match %v", namespaceRe)
)
// IsNamespacesValid return true if the error was due to an invalid namespace
// name.
func IsNamespaceInvalid(err error) bool {
return errors.Cause(err) == errNamespaceInvalid
}
// Validate return nil if the string s is a valid namespace name.
//
// Namespaces must be valid domain names according to RFC 1035, section 2.3.1.
// To enforce case insensitvity, all characters must be lower case.
//
// In general, namespaces that pass this validation, should be safe for use as
// a domain name or filesystem path component.
//
// Typically, this function is used through NamespacesRequired, rather than
// directly.
func Validate(s string) error {
if !namespaceRe.MatchString(s) {
return errors.Wrapf(errNamespaceInvalid, "namespace %q", s)
}
return nil
}

View File

@@ -1,79 +0,0 @@
package namespaces
import (
"testing"
"github.com/pkg/errors"
)
func TestValidNamespaces(t *testing.T) {
for _, testcase := range []struct {
input string
err error
}{
{
input: "default",
},
{
input: "default-default",
},
{
input: "default--default",
},
{
input: "containerd.io",
},
{
input: "foo.boo",
},
{
input: "swarmkit.docker.io",
},
{
input: "zn--e9.org", // or something like it!
},
{
input: ".foo..foo",
err: errNamespaceInvalid,
},
{
input: "foo/foo",
err: errNamespaceInvalid,
},
{
input: "foo/..",
err: errNamespaceInvalid,
},
{
input: "foo..foo",
err: errNamespaceInvalid,
},
{
input: "foo.-boo",
err: errNamespaceInvalid,
},
{
input: "-foo.boo",
err: errNamespaceInvalid,
},
{
input: "foo.boo-",
err: errNamespaceInvalid,
},
{
input: "foo_foo.boo_underscores", // boo-urns?
err: errNamespaceInvalid,
},
} {
t.Run(testcase.input, func(t *testing.T) {
if err := Validate(testcase.input); errors.Cause(err) != testcase.err {
t.Log(errors.Cause(err), testcase.err)
if testcase.err == nil {
t.Fatalf("unexpected error: %v != nil", err)
} else {
t.Fatalf("expected error %v to be %v", err, testcase.err)
}
}
})
}
}