forked from TrueCloudLab/distribution
Update Named reference with validation of conversions
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
9b8f1a0895
commit
46683f6192
7 changed files with 89 additions and 389 deletions
178
docs/config.go
178
docs/config.go
|
@ -7,9 +7,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
distreference "github.com/docker/distribution/reference"
|
|
||||||
registrytypes "github.com/docker/docker/api/types/registry"
|
registrytypes "github.com/docker/docker/api/types/registry"
|
||||||
"github.com/docker/docker/image/v1"
|
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
|
@ -182,28 +180,15 @@ func ValidateMirror(val string) (string, error) {
|
||||||
|
|
||||||
// ValidateIndexName validates an index name.
|
// ValidateIndexName validates an index name.
|
||||||
func ValidateIndexName(val string) (string, error) {
|
func ValidateIndexName(val string) (string, error) {
|
||||||
// 'index.docker.io' => 'docker.io'
|
if val == reference.LegacyDefaultHostname {
|
||||||
if val == "index."+IndexName {
|
val = reference.DefaultHostname
|
||||||
val = IndexName
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
|
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
|
||||||
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
|
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
|
||||||
}
|
}
|
||||||
// *TODO: Check if valid hostname[:port]/ip[:port]?
|
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateRemoteName(remoteName reference.Named) error {
|
|
||||||
remoteNameStr := remoteName.Name()
|
|
||||||
if !strings.Contains(remoteNameStr, "/") {
|
|
||||||
// the repository name must not be a valid image ID
|
|
||||||
if err := v1.ValidateID(remoteNameStr); err == nil {
|
|
||||||
return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateNoSchema(reposName string) error {
|
func validateNoSchema(reposName string) error {
|
||||||
if strings.Contains(reposName, "://") {
|
if strings.Contains(reposName, "://") {
|
||||||
// It cannot contain a scheme!
|
// It cannot contain a scheme!
|
||||||
|
@ -212,29 +197,6 @@ func validateNoSchema(reposName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRepositoryName validates a repository name
|
|
||||||
func ValidateRepositoryName(reposName reference.Named) error {
|
|
||||||
_, _, err := loadRepositoryName(reposName)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadRepositoryName returns the repo name splitted into index name
|
|
||||||
// and remote repo name. It returns an error if the name is not valid.
|
|
||||||
func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
|
|
||||||
if err := validateNoSchema(reposName.Name()); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
indexName, remoteName, err := splitReposName(reposName)
|
|
||||||
|
|
||||||
if indexName, err = ValidateIndexName(indexName); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
if err = validateRemoteName(remoteName); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
return indexName, remoteName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newIndexInfo returns IndexInfo configuration from indexName
|
// newIndexInfo returns IndexInfo configuration from indexName
|
||||||
func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) {
|
func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -267,75 +229,14 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
|
||||||
return index.Name
|
return index.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitReposName breaks a reposName into an index name and remote name
|
|
||||||
func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
|
|
||||||
var remoteNameStr string
|
|
||||||
indexName, remoteNameStr = distreference.SplitHostname(reposName)
|
|
||||||
if indexName == "" || (!strings.Contains(indexName, ".") &&
|
|
||||||
!strings.Contains(indexName, ":") && indexName != "localhost") {
|
|
||||||
// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
|
|
||||||
// 'docker.io'
|
|
||||||
indexName = IndexName
|
|
||||||
remoteName = reposName
|
|
||||||
} else {
|
|
||||||
remoteName, err = reference.WithName(remoteNameStr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
|
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
|
||||||
func newRepositoryInfo(config *registrytypes.ServiceConfig, reposName reference.Named) (*RepositoryInfo, error) {
|
func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) {
|
||||||
if err := validateNoSchema(reposName.Name()); err != nil {
|
index, err := newIndexInfo(config, name.Hostname())
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
repoInfo := &RepositoryInfo{}
|
|
||||||
var (
|
|
||||||
indexName string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
official := !strings.ContainsRune(name.Name(), '/')
|
||||||
repoInfo.Index, err = newIndexInfo(config, indexName)
|
return &RepositoryInfo{name, index, official}, nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if repoInfo.Index.Official {
|
|
||||||
repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repoInfo.RemoteName = repoInfo.LocalName
|
|
||||||
|
|
||||||
// If the normalized name does not contain a '/' (e.g. "foo")
|
|
||||||
// then it is an official repo.
|
|
||||||
if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
|
|
||||||
repoInfo.Official = true
|
|
||||||
// Fix up remote name for official repos.
|
|
||||||
repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repoInfo.CanonicalName = repoInfo.LocalName
|
|
||||||
}
|
|
||||||
|
|
||||||
return repoInfo, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
|
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
|
||||||
|
@ -354,70 +255,3 @@ func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
|
||||||
}
|
}
|
||||||
return indexInfo, nil
|
return indexInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizeLocalName transforms a repository name into a normalized LocalName
|
|
||||||
// Passes through the name without transformation on error (image id, etc)
|
|
||||||
// It does not use the repository info because we don't want to load
|
|
||||||
// the repository index and do request over the network.
|
|
||||||
func NormalizeLocalName(name reference.Named) reference.Named {
|
|
||||||
indexName, remoteName, err := loadRepositoryName(name)
|
|
||||||
if err != nil {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
var officialIndex bool
|
|
||||||
// Return any configured index info, first.
|
|
||||||
if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
|
|
||||||
officialIndex = index.Official
|
|
||||||
}
|
|
||||||
|
|
||||||
if officialIndex {
|
|
||||||
localName, err := normalizeLibraryRepoName(remoteName)
|
|
||||||
if err != nil {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return localName
|
|
||||||
}
|
|
||||||
localName, err := localNameFromRemote(indexName, remoteName)
|
|
||||||
if err != nil {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return localName
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalizeLibraryRepoName removes the library prefix from
|
|
||||||
// the repository name for official repos.
|
|
||||||
func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
|
|
||||||
if strings.HasPrefix(name.Name(), "library/") {
|
|
||||||
// If pull "library/foo", it's stored locally under "foo"
|
|
||||||
return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
|
|
||||||
}
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// localNameFromRemote combines the index name and the repo remote name
|
|
||||||
// to generate a repo local name.
|
|
||||||
func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) {
|
|
||||||
return reference.WithName(indexName + "/" + remoteName.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeLocalReference transforms a reference to use a normalized LocalName
|
|
||||||
// for the name poriton. Passes through the reference without transformation on
|
|
||||||
// error.
|
|
||||||
func NormalizeLocalReference(ref reference.Named) reference.Named {
|
|
||||||
localName := NormalizeLocalName(ref)
|
|
||||||
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
||||||
newRef, err := reference.WithTag(localName, tagged.Tag())
|
|
||||||
if err != nil {
|
|
||||||
return ref
|
|
||||||
}
|
|
||||||
return newRef
|
|
||||||
} else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
|
|
||||||
newRef, err := reference.WithDigest(localName, digested.Digest())
|
|
||||||
if err != nil {
|
|
||||||
return ref
|
|
||||||
}
|
|
||||||
return newRef
|
|
||||||
}
|
|
||||||
return localName
|
|
||||||
}
|
|
||||||
|
|
|
@ -356,7 +356,6 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
|
||||||
apiError(w, "Could not parse repository", 400)
|
apiError(w, "Could not parse repository", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
repositoryName = NormalizeLocalName(repositoryName)
|
|
||||||
tags, exists := testRepositories[repositoryName.String()]
|
tags, exists := testRepositories[repositoryName.String()]
|
||||||
if !exists {
|
if !exists {
|
||||||
apiError(w, "Repository not found", 404)
|
apiError(w, "Repository not found", 404)
|
||||||
|
@ -380,7 +379,6 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
|
||||||
apiError(w, "Could not parse repository", 400)
|
apiError(w, "Could not parse repository", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
repositoryName = NormalizeLocalName(repositoryName)
|
|
||||||
tagName := vars["tag"]
|
tagName := vars["tag"]
|
||||||
tags, exists := testRepositories[repositoryName.String()]
|
tags, exists := testRepositories[repositoryName.String()]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
@ -405,7 +403,6 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
|
||||||
apiError(w, "Could not parse repository", 400)
|
apiError(w, "Could not parse repository", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
repositoryName = NormalizeLocalName(repositoryName)
|
|
||||||
tagName := vars["tag"]
|
tagName := vars["tag"]
|
||||||
tags, exists := testRepositories[repositoryName.String()]
|
tags, exists := testRepositories[repositoryName.String()]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|
|
@ -307,71 +307,24 @@ func TestPushImageLayerRegistry(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateRepositoryName(t *testing.T) {
|
|
||||||
validRepoNames := []string{
|
|
||||||
"docker/docker",
|
|
||||||
"library/debian",
|
|
||||||
"debian",
|
|
||||||
"docker.io/docker/docker",
|
|
||||||
"docker.io/library/debian",
|
|
||||||
"docker.io/debian",
|
|
||||||
"index.docker.io/docker/docker",
|
|
||||||
"index.docker.io/library/debian",
|
|
||||||
"index.docker.io/debian",
|
|
||||||
"127.0.0.1:5000/docker/docker",
|
|
||||||
"127.0.0.1:5000/library/debian",
|
|
||||||
"127.0.0.1:5000/debian",
|
|
||||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
|
||||||
}
|
|
||||||
invalidRepoNames := []string{
|
|
||||||
"https://github.com/docker/docker",
|
|
||||||
"docker/Docker",
|
|
||||||
"-docker",
|
|
||||||
"-docker/docker",
|
|
||||||
"-docker.io/docker/docker",
|
|
||||||
"docker///docker",
|
|
||||||
"docker.io/docker/Docker",
|
|
||||||
"docker.io/docker///docker",
|
|
||||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
|
||||||
"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range invalidRepoNames {
|
|
||||||
named, err := reference.WithName(name)
|
|
||||||
if err == nil {
|
|
||||||
err := ValidateRepositoryName(named)
|
|
||||||
assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range validRepoNames {
|
|
||||||
named, err := reference.WithName(name)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("could not parse valid name: %s", name)
|
|
||||||
}
|
|
||||||
err = ValidateRepositoryName(named)
|
|
||||||
assertEqual(t, err, nil, "Expected valid repo name: "+name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseRepositoryInfo(t *testing.T) {
|
func TestParseRepositoryInfo(t *testing.T) {
|
||||||
withName := func(name string) reference.Named {
|
type staticRepositoryInfo struct {
|
||||||
named, err := reference.WithName(name)
|
Index *registrytypes.IndexInfo
|
||||||
if err != nil {
|
RemoteName string
|
||||||
t.Fatalf("could not parse reference %s", name)
|
CanonicalName string
|
||||||
}
|
LocalName string
|
||||||
return named
|
Official bool
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRepoInfos := map[string]RepositoryInfo{
|
expectedRepoInfos := map[string]staticRepositoryInfo{
|
||||||
"fooo/bar": {
|
"fooo/bar": {
|
||||||
Index: ®istrytypes.IndexInfo{
|
Index: ®istrytypes.IndexInfo{
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("fooo/bar"),
|
RemoteName: "fooo/bar",
|
||||||
LocalName: withName("fooo/bar"),
|
LocalName: "fooo/bar",
|
||||||
CanonicalName: withName("docker.io/fooo/bar"),
|
CanonicalName: "docker.io/fooo/bar",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"library/ubuntu": {
|
"library/ubuntu": {
|
||||||
|
@ -379,9 +332,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("library/ubuntu"),
|
RemoteName: "library/ubuntu",
|
||||||
LocalName: withName("ubuntu"),
|
LocalName: "ubuntu",
|
||||||
CanonicalName: withName("docker.io/library/ubuntu"),
|
CanonicalName: "docker.io/library/ubuntu",
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
"nonlibrary/ubuntu": {
|
"nonlibrary/ubuntu": {
|
||||||
|
@ -389,9 +342,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("nonlibrary/ubuntu"),
|
RemoteName: "nonlibrary/ubuntu",
|
||||||
LocalName: withName("nonlibrary/ubuntu"),
|
LocalName: "nonlibrary/ubuntu",
|
||||||
CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
|
CanonicalName: "docker.io/nonlibrary/ubuntu",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"ubuntu": {
|
"ubuntu": {
|
||||||
|
@ -399,9 +352,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("library/ubuntu"),
|
RemoteName: "library/ubuntu",
|
||||||
LocalName: withName("ubuntu"),
|
LocalName: "ubuntu",
|
||||||
CanonicalName: withName("docker.io/library/ubuntu"),
|
CanonicalName: "docker.io/library/ubuntu",
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
"other/library": {
|
"other/library": {
|
||||||
|
@ -409,9 +362,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("other/library"),
|
RemoteName: "other/library",
|
||||||
LocalName: withName("other/library"),
|
LocalName: "other/library",
|
||||||
CanonicalName: withName("docker.io/other/library"),
|
CanonicalName: "docker.io/other/library",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"127.0.0.1:8000/private/moonbase": {
|
"127.0.0.1:8000/private/moonbase": {
|
||||||
|
@ -419,9 +372,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "127.0.0.1:8000",
|
Name: "127.0.0.1:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("private/moonbase"),
|
RemoteName: "private/moonbase",
|
||||||
LocalName: withName("127.0.0.1:8000/private/moonbase"),
|
LocalName: "127.0.0.1:8000/private/moonbase",
|
||||||
CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
|
CanonicalName: "127.0.0.1:8000/private/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"127.0.0.1:8000/privatebase": {
|
"127.0.0.1:8000/privatebase": {
|
||||||
|
@ -429,9 +382,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "127.0.0.1:8000",
|
Name: "127.0.0.1:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("privatebase"),
|
RemoteName: "privatebase",
|
||||||
LocalName: withName("127.0.0.1:8000/privatebase"),
|
LocalName: "127.0.0.1:8000/privatebase",
|
||||||
CanonicalName: withName("127.0.0.1:8000/privatebase"),
|
CanonicalName: "127.0.0.1:8000/privatebase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"localhost:8000/private/moonbase": {
|
"localhost:8000/private/moonbase": {
|
||||||
|
@ -439,9 +392,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "localhost:8000",
|
Name: "localhost:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("private/moonbase"),
|
RemoteName: "private/moonbase",
|
||||||
LocalName: withName("localhost:8000/private/moonbase"),
|
LocalName: "localhost:8000/private/moonbase",
|
||||||
CanonicalName: withName("localhost:8000/private/moonbase"),
|
CanonicalName: "localhost:8000/private/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"localhost:8000/privatebase": {
|
"localhost:8000/privatebase": {
|
||||||
|
@ -449,9 +402,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "localhost:8000",
|
Name: "localhost:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("privatebase"),
|
RemoteName: "privatebase",
|
||||||
LocalName: withName("localhost:8000/privatebase"),
|
LocalName: "localhost:8000/privatebase",
|
||||||
CanonicalName: withName("localhost:8000/privatebase"),
|
CanonicalName: "localhost:8000/privatebase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"example.com/private/moonbase": {
|
"example.com/private/moonbase": {
|
||||||
|
@ -459,9 +412,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "example.com",
|
Name: "example.com",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("private/moonbase"),
|
RemoteName: "private/moonbase",
|
||||||
LocalName: withName("example.com/private/moonbase"),
|
LocalName: "example.com/private/moonbase",
|
||||||
CanonicalName: withName("example.com/private/moonbase"),
|
CanonicalName: "example.com/private/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"example.com/privatebase": {
|
"example.com/privatebase": {
|
||||||
|
@ -469,9 +422,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "example.com",
|
Name: "example.com",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("privatebase"),
|
RemoteName: "privatebase",
|
||||||
LocalName: withName("example.com/privatebase"),
|
LocalName: "example.com/privatebase",
|
||||||
CanonicalName: withName("example.com/privatebase"),
|
CanonicalName: "example.com/privatebase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"example.com:8000/private/moonbase": {
|
"example.com:8000/private/moonbase": {
|
||||||
|
@ -479,9 +432,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "example.com:8000",
|
Name: "example.com:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("private/moonbase"),
|
RemoteName: "private/moonbase",
|
||||||
LocalName: withName("example.com:8000/private/moonbase"),
|
LocalName: "example.com:8000/private/moonbase",
|
||||||
CanonicalName: withName("example.com:8000/private/moonbase"),
|
CanonicalName: "example.com:8000/private/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"example.com:8000/privatebase": {
|
"example.com:8000/privatebase": {
|
||||||
|
@ -489,9 +442,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "example.com:8000",
|
Name: "example.com:8000",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("privatebase"),
|
RemoteName: "privatebase",
|
||||||
LocalName: withName("example.com:8000/privatebase"),
|
LocalName: "example.com:8000/privatebase",
|
||||||
CanonicalName: withName("example.com:8000/privatebase"),
|
CanonicalName: "example.com:8000/privatebase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"localhost/private/moonbase": {
|
"localhost/private/moonbase": {
|
||||||
|
@ -499,9 +452,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "localhost",
|
Name: "localhost",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("private/moonbase"),
|
RemoteName: "private/moonbase",
|
||||||
LocalName: withName("localhost/private/moonbase"),
|
LocalName: "localhost/private/moonbase",
|
||||||
CanonicalName: withName("localhost/private/moonbase"),
|
CanonicalName: "localhost/private/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"localhost/privatebase": {
|
"localhost/privatebase": {
|
||||||
|
@ -509,9 +462,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: "localhost",
|
Name: "localhost",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
RemoteName: withName("privatebase"),
|
RemoteName: "privatebase",
|
||||||
LocalName: withName("localhost/privatebase"),
|
LocalName: "localhost/privatebase",
|
||||||
CanonicalName: withName("localhost/privatebase"),
|
CanonicalName: "localhost/privatebase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
IndexName + "/public/moonbase": {
|
IndexName + "/public/moonbase": {
|
||||||
|
@ -519,9 +472,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("public/moonbase"),
|
RemoteName: "public/moonbase",
|
||||||
LocalName: withName("public/moonbase"),
|
LocalName: "public/moonbase",
|
||||||
CanonicalName: withName("docker.io/public/moonbase"),
|
CanonicalName: "docker.io/public/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"index." + IndexName + "/public/moonbase": {
|
"index." + IndexName + "/public/moonbase": {
|
||||||
|
@ -529,9 +482,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("public/moonbase"),
|
RemoteName: "public/moonbase",
|
||||||
LocalName: withName("public/moonbase"),
|
LocalName: "public/moonbase",
|
||||||
CanonicalName: withName("docker.io/public/moonbase"),
|
CanonicalName: "docker.io/public/moonbase",
|
||||||
Official: false,
|
Official: false,
|
||||||
},
|
},
|
||||||
"ubuntu-12.04-base": {
|
"ubuntu-12.04-base": {
|
||||||
|
@ -539,9 +492,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
RemoteName: "library/ubuntu-12.04-base",
|
||||||
LocalName: withName("ubuntu-12.04-base"),
|
LocalName: "ubuntu-12.04-base",
|
||||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
IndexName + "/ubuntu-12.04-base": {
|
IndexName + "/ubuntu-12.04-base": {
|
||||||
|
@ -549,9 +502,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
RemoteName: "library/ubuntu-12.04-base",
|
||||||
LocalName: withName("ubuntu-12.04-base"),
|
LocalName: "ubuntu-12.04-base",
|
||||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
"index." + IndexName + "/ubuntu-12.04-base": {
|
"index." + IndexName + "/ubuntu-12.04-base": {
|
||||||
|
@ -559,9 +512,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
Name: IndexName,
|
Name: IndexName,
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
RemoteName: "library/ubuntu-12.04-base",
|
||||||
LocalName: withName("ubuntu-12.04-base"),
|
LocalName: "ubuntu-12.04-base",
|
||||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||||
Official: true,
|
Official: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -577,9 +530,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else {
|
} else {
|
||||||
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
|
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
|
||||||
checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName)
|
checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
|
||||||
checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName)
|
checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
|
||||||
checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName)
|
checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
|
||||||
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
|
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
|
||||||
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
|
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
|
||||||
}
|
}
|
||||||
|
@ -806,82 +759,6 @@ func TestSearchRepositories(t *testing.T) {
|
||||||
assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
|
assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidRemoteName(t *testing.T) {
|
|
||||||
validRepositoryNames := []string{
|
|
||||||
// Sanity check.
|
|
||||||
"docker/docker",
|
|
||||||
|
|
||||||
// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
|
|
||||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
|
||||||
|
|
||||||
// Allow embedded hyphens.
|
|
||||||
"docker-rules/docker",
|
|
||||||
|
|
||||||
// Allow multiple hyphens as well.
|
|
||||||
"docker---rules/docker",
|
|
||||||
|
|
||||||
//Username doc and image name docker being tested.
|
|
||||||
"doc/docker",
|
|
||||||
|
|
||||||
// single character names are now allowed.
|
|
||||||
"d/docker",
|
|
||||||
"jess/t",
|
|
||||||
|
|
||||||
// Consecutive underscores.
|
|
||||||
"dock__er/docker",
|
|
||||||
}
|
|
||||||
for _, repositoryName := range validRepositoryNames {
|
|
||||||
repositoryRef, err := reference.WithName(repositoryName)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
||||||
}
|
|
||||||
if err := validateRemoteName(repositoryRef); err != nil {
|
|
||||||
t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidRepositoryNames := []string{
|
|
||||||
// Disallow capital letters.
|
|
||||||
"docker/Docker",
|
|
||||||
|
|
||||||
// Only allow one slash.
|
|
||||||
"docker///docker",
|
|
||||||
|
|
||||||
// Disallow 64-character hexadecimal.
|
|
||||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
|
||||||
|
|
||||||
// Disallow leading and trailing hyphens in namespace.
|
|
||||||
"-docker/docker",
|
|
||||||
"docker-/docker",
|
|
||||||
"-docker-/docker",
|
|
||||||
|
|
||||||
// Don't allow underscores everywhere (as opposed to hyphens).
|
|
||||||
"____/____",
|
|
||||||
|
|
||||||
"_docker/_docker",
|
|
||||||
|
|
||||||
// Disallow consecutive periods.
|
|
||||||
"dock..er/docker",
|
|
||||||
"dock_.er/docker",
|
|
||||||
"dock-.er/docker",
|
|
||||||
|
|
||||||
// No repository.
|
|
||||||
"docker/",
|
|
||||||
|
|
||||||
//namespace too long
|
|
||||||
"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
|
|
||||||
}
|
|
||||||
for _, repositoryName := range invalidRepositoryNames {
|
|
||||||
repositoryRef, err := reference.ParseNamed(repositoryName)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := validateRemoteName(repositoryRef); err == nil {
|
|
||||||
t.Errorf("Repository name should be invalid: %v", repositoryName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrustedLocation(t *testing.T) {
|
func TestTrustedLocation(t *testing.T) {
|
||||||
for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
|
for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
|
||||||
req, _ := http.NewRequest("GET", url, nil)
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||||
var cfg = tlsconfig.ServerDefault
|
var cfg = tlsconfig.ServerDefault
|
||||||
tlsConfig := &cfg
|
tlsConfig := &cfg
|
||||||
nameString := repoName.Name()
|
nameString := repoName.FullName()
|
||||||
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
||||||
endpoints = append(endpoints, APIEndpoint{
|
endpoints = append(endpoints, APIEndpoint{
|
||||||
URL: DefaultV1Registry,
|
URL: DefaultV1Registry,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||||
var cfg = tlsconfig.ServerDefault
|
var cfg = tlsconfig.ServerDefault
|
||||||
tlsConfig := &cfg
|
tlsConfig := &cfg
|
||||||
nameString := repoName.Name()
|
nameString := repoName.FullName()
|
||||||
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
||||||
// v2 mirrors
|
// v2 mirrors
|
||||||
for _, mirror := range s.Config.Mirrors {
|
for _, mirror := range s.Config.Mirrors {
|
||||||
|
|
|
@ -312,7 +312,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
|
||||||
// argument, and returns data from the first one that answers the query
|
// argument, and returns data from the first one that answers the query
|
||||||
// successfully.
|
// successfully.
|
||||||
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
|
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
|
||||||
repository := repositoryRef.Name()
|
repository := repositoryRef.RemoteName()
|
||||||
|
|
||||||
if strings.Count(repository, "/") == 0 {
|
if strings.Count(repository, "/") == 0 {
|
||||||
// This will be removed once the registry supports auto-resolution on
|
// This will be removed once the registry supports auto-resolution on
|
||||||
|
@ -350,7 +350,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name
|
||||||
// the first one that answers the query successfully. It returns a map with
|
// the first one that answers the query successfully. It returns a map with
|
||||||
// tag names as the keys and image IDs as the values.
|
// tag names as the keys and image IDs as the values.
|
||||||
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
|
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
|
||||||
repository := repositoryRef.Name()
|
repository := repositoryRef.RemoteName()
|
||||||
|
|
||||||
if strings.Count(repository, "/") == 0 {
|
if strings.Count(repository, "/") == 0 {
|
||||||
// This will be removed once the registry supports auto-resolution on
|
// This will be removed once the registry supports auto-resolution on
|
||||||
|
@ -403,8 +403,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepositoryData returns lists of images and endpoints for the repository
|
// GetRepositoryData returns lists of images and endpoints for the repository
|
||||||
func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
|
func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
|
||||||
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
|
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), name.RemoteName())
|
||||||
|
|
||||||
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
|
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoints []string
|
var endpoints []string
|
||||||
|
@ -593,7 +593,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
|
||||||
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
|
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
|
||||||
// "jsonify" the string
|
// "jsonify" the string
|
||||||
revision = "\"" + revision + "\""
|
revision = "\"" + revision + "\""
|
||||||
path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
|
path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
|
||||||
|
|
||||||
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
|
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -607,7 +607,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if res.StatusCode != 200 && res.StatusCode != 201 {
|
if res.StatusCode != 200 && res.StatusCode != 201 {
|
||||||
return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
|
return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -633,7 +633,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
|
||||||
if validate {
|
if validate {
|
||||||
suffix = "images"
|
suffix = "images"
|
||||||
}
|
}
|
||||||
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
|
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.RemoteName(), suffix)
|
||||||
logrus.Debugf("[registry] PUT %s", u)
|
logrus.Debugf("[registry] PUT %s", u)
|
||||||
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
||||||
headers := map[string][]string{
|
headers := map[string][]string{
|
||||||
|
@ -671,7 +671,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
||||||
}
|
}
|
||||||
tokens = res.Header["X-Docker-Token"]
|
tokens = res.Header["X-Docker-Token"]
|
||||||
logrus.Debugf("Auth token: %v", tokens)
|
logrus.Debugf("Auth token: %v", tokens)
|
||||||
|
@ -689,7 +689,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error reading response body: %s", err)
|
logrus.Debugf("Error reading response body: %s", err)
|
||||||
}
|
}
|
||||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,17 +60,9 @@ const (
|
||||||
|
|
||||||
// RepositoryInfo describes a repository
|
// RepositoryInfo describes a repository
|
||||||
type RepositoryInfo struct {
|
type RepositoryInfo struct {
|
||||||
|
reference.Named
|
||||||
// Index points to registry information
|
// Index points to registry information
|
||||||
Index *registrytypes.IndexInfo
|
Index *registrytypes.IndexInfo
|
||||||
// RemoteName is the remote name of the repository, such as
|
|
||||||
// "library/ubuntu-12.04-base"
|
|
||||||
RemoteName reference.Named
|
|
||||||
// LocalName is the local name of the repository, such as
|
|
||||||
// "ubuntu-12.04-base"
|
|
||||||
LocalName reference.Named
|
|
||||||
// CanonicalName is the canonical name of the repository, such as
|
|
||||||
// "docker.io/library/ubuntu-12.04-base"
|
|
||||||
CanonicalName reference.Named
|
|
||||||
// Official indicates whether the repository is considered official.
|
// Official indicates whether the repository is considered official.
|
||||||
// If the registry is official, and the normalized name does not
|
// If the registry is official, and the normalized name does not
|
||||||
// contain a '/' (e.g. "foo"), then it is considered an official repo.
|
// contain a '/' (e.g. "foo"), then it is considered an official repo.
|
||||||
|
|
Loading…
Reference in a new issue