forked from TrueCloudLab/distribution
187 lines
5 KiB
Go
187 lines
5 KiB
Go
|
// Package reference provides a general type to represent any way of referencing images within the registry.
|
||
|
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
||
|
//
|
||
|
// Grammar
|
||
|
//
|
||
|
// reference := repository [ ":" tag ] [ "@" digest ]
|
||
|
//
|
||
|
// // repository.go
|
||
|
// repository := hostname ['/' component]+
|
||
|
// hostname := component [':' port-number]
|
||
|
// component := alpha-numeric [separator alpha-numeric]*
|
||
|
// alpha-numeric := /[a-zA-Z0-9]+/
|
||
|
// separator := /[._-]/
|
||
|
// port-number := /[0-9]+/
|
||
|
//
|
||
|
// // tag.go
|
||
|
// tag := /[\w][\w.-]{0,127}/
|
||
|
//
|
||
|
// // from the digest package
|
||
|
// digest := digest-algorithm ":" digest-hex
|
||
|
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
||
|
// digest-algorithm-separator := /[+.-_]/
|
||
|
// digest-algorithm-component := /[A-Za-z]/ /[A-Za-z0-9]*/
|
||
|
// digest-hex := /[A-Za-z0-9_-]+/ ; supports hex bytes or url safe base64
|
||
|
package reference
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"regexp"
|
||
|
|
||
|
"github.com/docker/distribution/digest"
|
||
|
)
|
||
|
|
||
|
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
||
|
var ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
||
|
|
||
|
// Reference abstracts types that reference images in a certain way.
|
||
|
type Reference interface {
|
||
|
// Repository returns the repository part of a reference
|
||
|
Repository() Repository
|
||
|
// String returns the entire reference, including the repository part
|
||
|
String() string
|
||
|
}
|
||
|
|
||
|
func parseHostname(s string) (hostname, tail string) {
|
||
|
tail = s
|
||
|
i := regexp.MustCompile(`^` + RepositoryNameHostnameRegexp.String()).FindStringIndex(s)
|
||
|
if i == nil {
|
||
|
return
|
||
|
}
|
||
|
return s[:i[1]], s[i[1]:]
|
||
|
}
|
||
|
|
||
|
func parseRepositoryName(s string) (repo, tail string) {
|
||
|
tail = s
|
||
|
i := regexp.MustCompile(`^/(?:` + RepositoryNameComponentRegexp.String() + `/)*` + RepositoryNameComponentRegexp.String()).FindStringIndex(s)
|
||
|
if i == nil {
|
||
|
return
|
||
|
}
|
||
|
return s[:i[1]], s[i[1]:]
|
||
|
}
|
||
|
|
||
|
func parseTag(s string) (tag Tag, tail string) {
|
||
|
tail = s
|
||
|
if len(s) == 0 || s[0] != ':' {
|
||
|
return
|
||
|
}
|
||
|
tag, err := NewTag(s[1:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
tail = s[len(tag)+1:]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func parseDigest(s string) (dgst digest.Digest, tail string) {
|
||
|
tail = s
|
||
|
if len(s) == 0 || s[0] != '@' {
|
||
|
return
|
||
|
}
|
||
|
dgst, err := digest.ParseDigest(s[1:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
tail = s[len(dgst)+1:]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Parse parses s and returns a syntactically valid Reference.
|
||
|
// If an error was encountered it is returned, along with a nil Reference.
|
||
|
func Parse(s string) (Reference, error) {
|
||
|
hostname, s := parseHostname(s)
|
||
|
name, s := parseRepositoryName(s)
|
||
|
repository := Repository{Hostname: hostname, Name: name}
|
||
|
if err := repository.Validate(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tag, s := parseTag(s)
|
||
|
dgst, s := parseDigest(s)
|
||
|
if len(s) > 0 {
|
||
|
return nil, ErrReferenceInvalidFormat
|
||
|
}
|
||
|
|
||
|
if dgst != "" {
|
||
|
return DigestReference{repository: repository, digest: dgst, tag: tag}, nil
|
||
|
}
|
||
|
if tag != "" {
|
||
|
return TagReference{repository: repository, tag: tag}, nil
|
||
|
}
|
||
|
return nil, ErrReferenceInvalidFormat
|
||
|
}
|
||
|
|
||
|
// DigestReference represents a reference of the form `repository@sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef`.
|
||
|
// Implements the Reference interface.
|
||
|
type DigestReference struct {
|
||
|
repository Repository
|
||
|
digest digest.Digest
|
||
|
tag Tag
|
||
|
}
|
||
|
|
||
|
// Repository returns the repository part.
|
||
|
func (r DigestReference) Repository() Repository { return r.repository }
|
||
|
|
||
|
// String returns the full string reference.
|
||
|
func (r DigestReference) String() string {
|
||
|
return r.repository.String() + "@" + string(r.digest)
|
||
|
}
|
||
|
|
||
|
// NewDigestReference returns an initialized DigestReference.
|
||
|
func NewDigestReference(canonicalRepository string, digest digest.Digest, optionalTag Tag) (DigestReference, error) {
|
||
|
ref := DigestReference{}
|
||
|
|
||
|
repo, err := NewRepository(canonicalRepository)
|
||
|
if err != nil {
|
||
|
return ref, err
|
||
|
}
|
||
|
ref.repository = repo
|
||
|
|
||
|
if err := digest.Validate(); err != nil {
|
||
|
return ref, err
|
||
|
}
|
||
|
ref.digest = digest
|
||
|
|
||
|
if len(optionalTag) > 0 {
|
||
|
if err := optionalTag.Validate(); err != nil {
|
||
|
return ref, err
|
||
|
}
|
||
|
ref.tag = optionalTag
|
||
|
}
|
||
|
|
||
|
return ref, err
|
||
|
}
|
||
|
|
||
|
// TagReference represents a reference of the form `repository:tag`.
|
||
|
// Implements the Reference interface.
|
||
|
type TagReference struct {
|
||
|
repository Repository
|
||
|
tag Tag
|
||
|
}
|
||
|
|
||
|
// Repository returns the repository part.
|
||
|
func (r TagReference) Repository() Repository { return r.repository }
|
||
|
|
||
|
// String returns the full string reference.
|
||
|
func (r TagReference) String() string {
|
||
|
return r.repository.String() + ":" + string(r.tag)
|
||
|
}
|
||
|
|
||
|
// NewTagReference returns an initialized TagReference.
|
||
|
func NewTagReference(canonicalRepository string, tagName string) (TagReference, error) {
|
||
|
ref := TagReference{}
|
||
|
|
||
|
repo, err := NewRepository(canonicalRepository)
|
||
|
if err != nil {
|
||
|
return ref, err
|
||
|
}
|
||
|
ref.repository = repo
|
||
|
|
||
|
tag, err := NewTag(tagName)
|
||
|
if err != nil {
|
||
|
return ref, err
|
||
|
}
|
||
|
ref.tag = tag
|
||
|
|
||
|
return ref, err
|
||
|
}
|