Merge pull request #3798 from estesp/common-indentifiers

Common identifiers validation
This commit is contained in:
Michael Crosby 2019-11-05 07:08:02 -05:00 committed by GitHub
commit 356702098d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 11 additions and 162 deletions

View File

@ -225,7 +225,7 @@ func validateTopic(topic string) error {
}
func validateEnvelope(envelope *events.Envelope) error {
if err := namespaces.Validate(envelope.Namespace); err != nil {
if err := identifiers.Validate(envelope.Namespace); err != nil {
return errors.Wrapf(err, "event envelope has invalid namespace")
}

View File

@ -42,13 +42,13 @@ var (
identifierRe = regexp.MustCompile(reAnchor(alphanum + reGroup(separators+reGroup(alphanum)) + "*"))
)
// Validate return nil if the string s is a valid identifier.
// Validate returns nil if the string s is a valid identifier.
//
// identifiers must be valid domain names according to RFC 1035, section 2.3.1. To
// enforce case insensitivity, all characters must be lower case.
// identifiers are similar to the domain name rules according to RFC 1035, section 2.3.1. However
// rules in this package are relaxed to allow numerals to follow period (".") and mixed case is
// allowed.
//
// In general, identifiers that pass this validation, should be safe for use as
// a domain names or filesystem path component.
// In general identifiers that pass this validation should be safe for use as filesystem path components.
func Validate(s string) error {
if len(s) == 0 {
return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier must not be empty")

View File

@ -34,6 +34,7 @@ func TestValidIdentifiers(t *testing.T) {
"swarmkit.docker.io",
"0912341234",
"task.0.0123456789",
"container.system-75-f19a.00",
"underscores_are_allowed",
strings.Repeat("a", maxLength),
} {

View File

@ -20,6 +20,7 @@ import (
"context"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/identifiers"
l "github.com/containerd/containerd/labels"
"github.com/containerd/containerd/namespaces"
"github.com/pkg/errors"
@ -41,7 +42,7 @@ func (s *namespaceStore) Create(ctx context.Context, namespace string, labels ma
return err
}
if err := namespaces.Validate(namespace); err != nil {
if err := identifiers.Validate(namespace); err != nil {
return err
}

View File

@ -21,6 +21,7 @@ import (
"os"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/identifiers"
"github.com/pkg/errors"
)
@ -70,7 +71,7 @@ func NamespaceRequired(ctx context.Context) (string, error) {
if !ok || namespace == "" {
return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace is required")
}
if err := Validate(namespace); err != nil {
if err := identifiers.Validate(namespace); err != nil {
return "", errors.Wrap(err, "namespace validation")
}
return namespace, nil

View File

@ -1,83 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package namespaces provides tools for working with namespaces across
// containerd.
//
// Namespaces collect resources such as containers and images, into a unique
// identifier space. This means that two applications can use the same
// identifiers and not conflict while using containerd.
//
// This package can be used to ensure that client and server functions
// correctly store the namespace on the context.
package namespaces
import (
"regexp"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
)
const (
maxLength = 76
alpha = `[A-Za-z]`
alphanum = `[A-Za-z0-9]+`
label = alpha + alphanum + `(:?[-]+` + alpha + alphanum + `)*`
)
var (
// namespaceRe validates that a namespace matches valid identifiers.
//
// Rules for domains, defined in RFC 1035, section 2.3.1, are used for
// namespaces.
namespaceRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*"))
)
// Validate returns nil if the string s is a valid namespace.
//
// To allow such namespace identifiers to be used across various contexts
// safely, the character set has been restricted to that defined for domains in
// RFC 1035, section 2.3.1. This will make namespace identifiers safe for use
// across networks, filesystems and other media.
//
// The identifier specification departs from RFC 1035 in that it allows
// "labels" to start with number and only enforces a total length restriction
// of 76 characters.
//
// While the character set may be expanded in the future, namespace identifiers
// are guaranteed to be safely used as filesystem path components.
//
// For the most part, this doesn't need to be called directly when using the
// context-oriented functions.
func Validate(s string) error {
if len(s) > maxLength {
return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q greater than maximum length (%d characters)", s, maxLength)
}
if !namespaceRe.MatchString(s) {
return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q must match %v", s, namespaceRe)
}
return nil
}
func reGroup(s string) string {
return `(?:` + s + `)`
}
func reAnchor(s string) string {
return `^` + s + `$`
}

View File

@ -1,71 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package namespaces
import (
"strings"
"testing"
"github.com/containerd/containerd/errdefs"
)
func TestValidNamespaces(t *testing.T) {
for _, input := range []string{
"default",
"Default",
t.Name(),
"default-default",
"default--default",
"containerd.io",
"foo.boo",
"swarmkit.docker.io",
"zn--e9.org", // or something like it!
strings.Repeat("a", maxLength),
} {
t.Run(input, func(t *testing.T) {
if err := Validate(input); err != nil {
t.Fatalf("unexpected error: %v != nil", err)
}
})
}
}
func TestInvalidNamespaces(t *testing.T) {
for _, input := range []string{
".foo..foo",
"foo/foo",
"foo/..",
"foo..foo",
"foo.-boo",
"foo.-boo.bar",
"-foo.boo",
"foo.boo-",
"foo_foo.boo_underscores", // boo-urns?
"0912341234",
"task.0.0123456789",
strings.Repeat("a", maxLength+1),
} {
t.Run(input, func(t *testing.T) {
if err := Validate(input); err == nil {
t.Fatal("expected invalid error")
} else if !errdefs.IsInvalidArgument(err) {
t.Fatal("error should be an invalid identifier error")
}
})
}
}