add extra ref parsing, validation

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>
pull/5279/head
Josh Dolitsky 7 years ago
parent a32f8ebb37
commit 24eeca741e

@ -17,11 +17,18 @@ limitations under the License.
package registry // import "k8s.io/helm/pkg/registry"
import (
"errors"
"regexp"
"strings"
"github.com/containerd/containerd/reference"
)
var (
validPortRegEx = regexp.MustCompile("^([1-9]\\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$") // adapted from https://stackoverflow.com/a/12968117
tooManyColonsError = errors.New("ref may only contain a single colon character (:) unless specifying a port number")
)
type (
// Reference defines the main components of a reference specification
Reference struct {
@ -35,11 +42,67 @@ func ParseReference(s string) (*Reference, error) {
if err != nil {
return nil, err
}
// convert to our custom type and make necessary mods
ref := Reference{&spec}
ref.fix()
// ensure the reference is valid
err = ref.validate()
if err != nil {
return nil, err
}
return &ref, nil
}
// Repo returns a reference's repo minus the hostname
func (ref *Reference) Repo() string {
return strings.TrimPrefix(strings.TrimPrefix(ref.Locator, ref.Hostname()), "/")
// fix modifies references that were potentially not parsed properly
func (spec *Reference) fix() {
spec.fixNoTag()
}
// fixNoTag is a fix for ref strings such as "mychart:1.0.0", which result in missing tag (object)
func (spec *Reference) fixNoTag() {
if spec.Object == "" {
parts := strings.Split(spec.Locator, ":")
numParts := len(parts)
if 0 < numParts {
lastIndex := numParts - 1
lastPart := parts[lastIndex]
if !strings.Contains(lastPart, "/") {
spec.Locator = strings.Join(parts[:lastIndex], ":")
spec.Object = lastPart
}
}
}
}
// validate makes sure the ref meets our criteria
func (spec *Reference) validate() error {
return spec.validateColons()
}
// validateColons verifies the ref only contains one colon max
// (or two, there might be a port number specified i.e. :5000)
func (spec *Reference) validateColons() error {
if strings.Contains(spec.Object, ":") {
return tooManyColonsError
}
locParts := strings.Split(spec.Locator, ":")
locLastIndex := len(locParts) - 1
if 1 < locLastIndex {
return tooManyColonsError
}
if 0 < locLastIndex {
port := strings.Split(locParts[locLastIndex], "/")[0]
if !isValidPort(port) {
return tooManyColonsError
}
}
return nil
}
// isValidPort returns whether or not a string looks like a valid port
func isValidPort(s string) bool {
return validPortRegEx.MatchString(s)
}

@ -25,25 +25,53 @@ import (
func TestReference(t *testing.T) {
is := assert.New(t)
// bad ref
// bad refs
s := ""
_, err := ParseReference(s)
is.Error(err)
is.Error(err, "empty ref")
s = "my:bad:ref"
_, err = ParseReference(s)
is.Error(err, "ref contains too many colons (2)")
s = "my:really:bad:ref"
_, err = ParseReference(s)
is.Error(err, "ref contains too many colons (3)")
// good refs
s = "localhost:5000/mychart:latest"
s = "mychart:1.5.0"
ref, err := ParseReference(s)
is.NoError(err)
is.Equal("localhost:5000", ref.Hostname())
is.Equal("mychart", ref.Repo())
is.Equal("mychart", ref.Locator)
is.Equal("1.5.0", ref.Object)
s = "myrepo/mychart:1.5.0"
ref, err = ParseReference(s)
is.NoError(err)
is.Equal("myrepo/mychart", ref.Locator)
is.Equal("1.5.0", ref.Object)
s = "mychart:5001:1.5.0"
ref, err = ParseReference(s)
is.NoError(err)
is.Equal("mychart:5001", ref.Locator)
is.Equal("1.5.0", ref.Object)
s = "myrepo:5001/mychart:1.5.0"
ref, err = ParseReference(s)
is.NoError(err)
is.Equal("myrepo:5001/mychart", ref.Locator)
is.Equal("1.5.0", ref.Object)
s = "localhost:5000/mychart:latest"
ref, err = ParseReference(s)
is.NoError(err)
is.Equal("localhost:5000/mychart", ref.Locator)
is.Equal("latest", ref.Object)
s = "my.host.com/my/nested/repo:1.2.3"
ref, err = ParseReference(s)
is.NoError(err)
is.Equal("my.host.com", ref.Hostname())
is.Equal("my/nested/repo", ref.Repo())
is.Equal("my.host.com/my/nested/repo", ref.Locator)
is.Equal("1.2.3", ref.Object)
}

Loading…
Cancel
Save