Update daemon and docker core to use new content addressable storage
Add distribution package for managing pulls and pushes. This is based on the old code in the graph package, with major changes to work with the new image/layer model. Add v1 migration code. Update registry, api/*, and daemon packages to use the reference package's types where applicable. Update daemon package to use image/layer/tag stores instead of the graph package Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com> Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
dde006ba6b
commit
7efcb7496c
8 changed files with 336 additions and 196 deletions
163
docs/config.go
163
docs/config.go
|
@ -9,7 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/image/v1"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
@ -216,18 +216,15 @@ func ValidateIndexName(val string) (string, error) {
|
|||
return val, nil
|
||||
}
|
||||
|
||||
func validateRemoteName(remoteName string) error {
|
||||
|
||||
if !strings.Contains(remoteName, "/") {
|
||||
|
||||
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 := image.ValidateID(remoteName); err == nil {
|
||||
if err := v1.ValidateID(remoteNameStr); err == nil {
|
||||
return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := reference.WithName(remoteName)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNoSchema(reposName string) error {
|
||||
|
@ -239,27 +236,24 @@ func validateNoSchema(reposName string) error {
|
|||
}
|
||||
|
||||
// ValidateRepositoryName validates a repository name
|
||||
func ValidateRepositoryName(reposName string) error {
|
||||
_, _, err := loadRepositoryName(reposName, true)
|
||||
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 string, checkRemoteName bool) (string, string, error) {
|
||||
if err := validateNoSchema(reposName); err != nil {
|
||||
return "", "", err
|
||||
func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
|
||||
if err := validateNoSchema(reposName.Name()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
indexName, remoteName := splitReposName(reposName)
|
||||
indexName, remoteName, err := splitReposName(reposName)
|
||||
|
||||
var err error
|
||||
if indexName, err = ValidateIndexName(indexName); err != nil {
|
||||
return "", "", err
|
||||
return "", nil, err
|
||||
}
|
||||
if checkRemoteName {
|
||||
if err = validateRemoteName(remoteName); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if err = validateRemoteName(remoteName); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return indexName, remoteName, nil
|
||||
}
|
||||
|
@ -297,31 +291,36 @@ func (index *IndexInfo) GetAuthConfigKey() string {
|
|||
}
|
||||
|
||||
// splitReposName breaks a reposName into an index name and remote name
|
||||
func splitReposName(reposName string) (string, string) {
|
||||
nameParts := strings.SplitN(reposName, "/", 2)
|
||||
var indexName, remoteName string
|
||||
if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") &&
|
||||
!strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
|
||||
func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
|
||||
var remoteNameStr string
|
||||
indexName, remoteNameStr = reference.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 {
|
||||
indexName = nameParts[0]
|
||||
remoteName = nameParts[1]
|
||||
remoteName, err = reference.WithName(remoteNameStr)
|
||||
}
|
||||
return indexName, remoteName
|
||||
return
|
||||
}
|
||||
|
||||
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
|
||||
func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool) (*RepositoryInfo, error) {
|
||||
indexName, remoteName, err := loadRepositoryName(reposName, !bySearch)
|
||||
if err != nil {
|
||||
func (config *ServiceConfig) NewRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
|
||||
if err := validateNoSchema(reposName.Name()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoInfo := &RepositoryInfo{
|
||||
RemoteName: remoteName,
|
||||
repoInfo := &RepositoryInfo{}
|
||||
var (
|
||||
indexName string
|
||||
err error
|
||||
)
|
||||
|
||||
indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoInfo.Index, err = config.NewIndexInfo(indexName)
|
||||
|
@ -330,46 +329,47 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool)
|
|||
}
|
||||
|
||||
if repoInfo.Index.Official {
|
||||
normalizedName := normalizeLibraryRepoName(repoInfo.RemoteName)
|
||||
repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoInfo.RemoteName = repoInfo.LocalName
|
||||
|
||||
repoInfo.LocalName = normalizedName
|
||||
repoInfo.RemoteName = normalizedName
|
||||
// If the normalized name does not contain a '/' (e.g. "foo")
|
||||
// then it is an official repo.
|
||||
if strings.IndexRune(normalizedName, '/') == -1 {
|
||||
if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
|
||||
repoInfo.Official = true
|
||||
// Fix up remote name for official repos.
|
||||
repoInfo.RemoteName = "library/" + normalizedName
|
||||
repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
repoInfo.CanonicalName = "docker.io/" + repoInfo.RemoteName
|
||||
repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
repoInfo.LocalName = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
|
||||
repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoInfo.CanonicalName = repoInfo.LocalName
|
||||
|
||||
}
|
||||
|
||||
return repoInfo, nil
|
||||
}
|
||||
|
||||
// GetSearchTerm special-cases using local name for official index, and
|
||||
// remote name for private indexes.
|
||||
func (repoInfo *RepositoryInfo) GetSearchTerm() string {
|
||||
if repoInfo.Index.Official {
|
||||
return repoInfo.LocalName
|
||||
}
|
||||
return repoInfo.RemoteName
|
||||
}
|
||||
|
||||
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
|
||||
// lacks registry configuration.
|
||||
func ParseRepositoryInfo(reposName string) (*RepositoryInfo, error) {
|
||||
return emptyServiceConfig.NewRepositoryInfo(reposName, false)
|
||||
func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
|
||||
return emptyServiceConfig.NewRepositoryInfo(reposName)
|
||||
}
|
||||
|
||||
// ParseIndexInfo will use repository name to get back an indexInfo.
|
||||
func ParseIndexInfo(reposName string) (*IndexInfo, error) {
|
||||
indexName, _ := splitReposName(reposName)
|
||||
// ParseSearchIndexInfo will use repository name to get back an indexInfo.
|
||||
func ParseSearchIndexInfo(reposName string) (*IndexInfo, error) {
|
||||
indexName, _ := splitReposSearchTerm(reposName)
|
||||
|
||||
indexInfo, err := emptyServiceConfig.NewIndexInfo(indexName)
|
||||
if err != nil {
|
||||
|
@ -378,12 +378,12 @@ func ParseIndexInfo(reposName string) (*IndexInfo, error) {
|
|||
return indexInfo, nil
|
||||
}
|
||||
|
||||
// NormalizeLocalName transforms a repository name into a normalize LocalName
|
||||
// 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 string) string {
|
||||
indexName, remoteName, err := loadRepositoryName(name, true)
|
||||
func NormalizeLocalName(name reference.Named) reference.Named {
|
||||
indexName, remoteName, err := loadRepositoryName(name)
|
||||
if err != nil {
|
||||
return name
|
||||
}
|
||||
|
@ -395,23 +395,52 @@ func NormalizeLocalName(name string) string {
|
|||
}
|
||||
|
||||
if officialIndex {
|
||||
return normalizeLibraryRepoName(remoteName)
|
||||
localName, err := normalizeLibraryRepoName(remoteName)
|
||||
if err != nil {
|
||||
return name
|
||||
}
|
||||
return localName
|
||||
}
|
||||
return localNameFromRemote(indexName, remoteName)
|
||||
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 string) string {
|
||||
if strings.HasPrefix(name, "library/") {
|
||||
func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
|
||||
if strings.HasPrefix(name.Name(), "library/") {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
name = strings.SplitN(name, "/", 2)[1]
|
||||
return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
|
||||
}
|
||||
return name
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// localNameFromRemote combines the index name and the repo remote name
|
||||
// to generate a repo local name.
|
||||
func localNameFromRemote(indexName, remoteName string) string {
|
||||
return indexName + "/" + remoteName
|
||||
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.Tagged); isTagged {
|
||||
newRef, err := reference.WithTag(localName, tagged.Tag())
|
||||
if err != nil {
|
||||
return ref
|
||||
}
|
||||
return newRef
|
||||
} else if digested, isDigested := ref.(reference.Digested); isDigested {
|
||||
newRef, err := reference.WithDigest(localName, digested.Digest())
|
||||
if err != nil {
|
||||
return ref
|
||||
}
|
||||
return newRef
|
||||
}
|
||||
return localName
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
|
@ -349,15 +350,19 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
|
|||
if !requiresAuth(w, r) {
|
||||
return
|
||||
}
|
||||
repositoryName := mux.Vars(r)["repository"]
|
||||
repositoryName, err := reference.WithName(mux.Vars(r)["repository"])
|
||||
if err != nil {
|
||||
apiError(w, "Could not parse repository", 400)
|
||||
return
|
||||
}
|
||||
repositoryName = NormalizeLocalName(repositoryName)
|
||||
tags, exists := testRepositories[repositoryName]
|
||||
tags, exists := testRepositories[repositoryName.String()]
|
||||
if !exists {
|
||||
apiError(w, "Repository not found", 404)
|
||||
return
|
||||
}
|
||||
if r.Method == "DELETE" {
|
||||
delete(testRepositories, repositoryName)
|
||||
delete(testRepositories, repositoryName.String())
|
||||
writeResponse(w, true, 200)
|
||||
return
|
||||
}
|
||||
|
@ -369,10 +374,14 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
repositoryName := vars["repository"]
|
||||
repositoryName, err := reference.WithName(vars["repository"])
|
||||
if err != nil {
|
||||
apiError(w, "Could not parse repository", 400)
|
||||
return
|
||||
}
|
||||
repositoryName = NormalizeLocalName(repositoryName)
|
||||
tagName := vars["tag"]
|
||||
tags, exists := testRepositories[repositoryName]
|
||||
tags, exists := testRepositories[repositoryName.String()]
|
||||
if !exists {
|
||||
apiError(w, "Repository not found", 404)
|
||||
return
|
||||
|
@ -390,13 +399,17 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
repositoryName := vars["repository"]
|
||||
repositoryName, err := reference.WithName(vars["repository"])
|
||||
if err != nil {
|
||||
apiError(w, "Could not parse repository", 400)
|
||||
return
|
||||
}
|
||||
repositoryName = NormalizeLocalName(repositoryName)
|
||||
tagName := vars["tag"]
|
||||
tags, exists := testRepositories[repositoryName]
|
||||
tags, exists := testRepositories[repositoryName.String()]
|
||||
if !exists {
|
||||
tags := make(map[string]string)
|
||||
testRepositories[repositoryName] = tags
|
||||
tags = make(map[string]string)
|
||||
testRepositories[repositoryName.String()] = tags
|
||||
}
|
||||
tagValue := ""
|
||||
readJSON(r, tagValue)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
)
|
||||
|
@ -214,13 +215,21 @@ func TestGetRemoteImageLayer(t *testing.T) {
|
|||
|
||||
func TestGetRemoteTag(t *testing.T) {
|
||||
r := spawnTestRegistrySession(t)
|
||||
tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, REPO, "test")
|
||||
repoRef, err := reference.ParseNamed(REPO)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, repoRef, "test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
|
||||
|
||||
_, err = r.GetRemoteTag([]string{makeURL("/v1/")}, "foo42/baz", "foo")
|
||||
bazRef, err := reference.ParseNamed("foo42/baz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = r.GetRemoteTag([]string{makeURL("/v1/")}, bazRef, "foo")
|
||||
if err != ErrRepoNotFound {
|
||||
t.Fatal("Expected ErrRepoNotFound error when fetching tag for bogus repo")
|
||||
}
|
||||
|
@ -228,7 +237,11 @@ func TestGetRemoteTag(t *testing.T) {
|
|||
|
||||
func TestGetRemoteTags(t *testing.T) {
|
||||
r := spawnTestRegistrySession(t)
|
||||
tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO)
|
||||
repoRef, err := reference.ParseNamed(REPO)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, repoRef)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -236,7 +249,11 @@ func TestGetRemoteTags(t *testing.T) {
|
|||
assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
|
||||
assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
|
||||
|
||||
_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz")
|
||||
bazRef, err := reference.ParseNamed("foo42/baz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, bazRef)
|
||||
if err != ErrRepoNotFound {
|
||||
t.Fatal("Expected ErrRepoNotFound error when fetching tags for bogus repo")
|
||||
}
|
||||
|
@ -249,7 +266,11 @@ func TestGetRepositoryData(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
host := "http://" + parsedURL.Host + "/v1/"
|
||||
data, err := r.GetRepositoryData("foo42/bar")
|
||||
repoRef, err := reference.ParseNamed(REPO)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := r.GetRepositoryData(repoRef)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -315,29 +336,41 @@ func TestValidateRepositoryName(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, name := range invalidRepoNames {
|
||||
err := ValidateRepositoryName(name)
|
||||
assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
|
||||
named, err := reference.WithName(name)
|
||||
if err == nil {
|
||||
err := ValidateRepositoryName(named)
|
||||
assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range validRepoNames {
|
||||
err := ValidateRepositoryName(name)
|
||||
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)
|
||||
}
|
||||
|
||||
err := ValidateRepositoryName(invalidRepoNames[0])
|
||||
assertEqual(t, err, ErrInvalidRepositoryName, "Expected ErrInvalidRepositoryName: "+invalidRepoNames[0])
|
||||
}
|
||||
|
||||
func TestParseRepositoryInfo(t *testing.T) {
|
||||
withName := func(name string) reference.Named {
|
||||
named, err := reference.WithName(name)
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse reference %s", name)
|
||||
}
|
||||
return named
|
||||
}
|
||||
|
||||
expectedRepoInfos := map[string]RepositoryInfo{
|
||||
"fooo/bar": {
|
||||
Index: &IndexInfo{
|
||||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "fooo/bar",
|
||||
LocalName: "fooo/bar",
|
||||
CanonicalName: "docker.io/fooo/bar",
|
||||
RemoteName: withName("fooo/bar"),
|
||||
LocalName: withName("fooo/bar"),
|
||||
CanonicalName: withName("docker.io/fooo/bar"),
|
||||
Official: false,
|
||||
},
|
||||
"library/ubuntu": {
|
||||
|
@ -345,9 +378,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "library/ubuntu",
|
||||
LocalName: "ubuntu",
|
||||
CanonicalName: "docker.io/library/ubuntu",
|
||||
RemoteName: withName("library/ubuntu"),
|
||||
LocalName: withName("ubuntu"),
|
||||
CanonicalName: withName("docker.io/library/ubuntu"),
|
||||
Official: true,
|
||||
},
|
||||
"nonlibrary/ubuntu": {
|
||||
|
@ -355,9 +388,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "nonlibrary/ubuntu",
|
||||
LocalName: "nonlibrary/ubuntu",
|
||||
CanonicalName: "docker.io/nonlibrary/ubuntu",
|
||||
RemoteName: withName("nonlibrary/ubuntu"),
|
||||
LocalName: withName("nonlibrary/ubuntu"),
|
||||
CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
|
||||
Official: false,
|
||||
},
|
||||
"ubuntu": {
|
||||
|
@ -365,9 +398,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "library/ubuntu",
|
||||
LocalName: "ubuntu",
|
||||
CanonicalName: "docker.io/library/ubuntu",
|
||||
RemoteName: withName("library/ubuntu"),
|
||||
LocalName: withName("ubuntu"),
|
||||
CanonicalName: withName("docker.io/library/ubuntu"),
|
||||
Official: true,
|
||||
},
|
||||
"other/library": {
|
||||
|
@ -375,9 +408,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "other/library",
|
||||
LocalName: "other/library",
|
||||
CanonicalName: "docker.io/other/library",
|
||||
RemoteName: withName("other/library"),
|
||||
LocalName: withName("other/library"),
|
||||
CanonicalName: withName("docker.io/other/library"),
|
||||
Official: false,
|
||||
},
|
||||
"127.0.0.1:8000/private/moonbase": {
|
||||
|
@ -385,9 +418,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "127.0.0.1:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "private/moonbase",
|
||||
LocalName: "127.0.0.1:8000/private/moonbase",
|
||||
CanonicalName: "127.0.0.1:8000/private/moonbase",
|
||||
RemoteName: withName("private/moonbase"),
|
||||
LocalName: withName("127.0.0.1:8000/private/moonbase"),
|
||||
CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"127.0.0.1:8000/privatebase": {
|
||||
|
@ -395,9 +428,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "127.0.0.1:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "privatebase",
|
||||
LocalName: "127.0.0.1:8000/privatebase",
|
||||
CanonicalName: "127.0.0.1:8000/privatebase",
|
||||
RemoteName: withName("privatebase"),
|
||||
LocalName: withName("127.0.0.1:8000/privatebase"),
|
||||
CanonicalName: withName("127.0.0.1:8000/privatebase"),
|
||||
Official: false,
|
||||
},
|
||||
"localhost:8000/private/moonbase": {
|
||||
|
@ -405,9 +438,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "localhost:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "private/moonbase",
|
||||
LocalName: "localhost:8000/private/moonbase",
|
||||
CanonicalName: "localhost:8000/private/moonbase",
|
||||
RemoteName: withName("private/moonbase"),
|
||||
LocalName: withName("localhost:8000/private/moonbase"),
|
||||
CanonicalName: withName("localhost:8000/private/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"localhost:8000/privatebase": {
|
||||
|
@ -415,9 +448,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "localhost:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "privatebase",
|
||||
LocalName: "localhost:8000/privatebase",
|
||||
CanonicalName: "localhost:8000/privatebase",
|
||||
RemoteName: withName("privatebase"),
|
||||
LocalName: withName("localhost:8000/privatebase"),
|
||||
CanonicalName: withName("localhost:8000/privatebase"),
|
||||
Official: false,
|
||||
},
|
||||
"example.com/private/moonbase": {
|
||||
|
@ -425,9 +458,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "example.com",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "private/moonbase",
|
||||
LocalName: "example.com/private/moonbase",
|
||||
CanonicalName: "example.com/private/moonbase",
|
||||
RemoteName: withName("private/moonbase"),
|
||||
LocalName: withName("example.com/private/moonbase"),
|
||||
CanonicalName: withName("example.com/private/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"example.com/privatebase": {
|
||||
|
@ -435,9 +468,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "example.com",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "privatebase",
|
||||
LocalName: "example.com/privatebase",
|
||||
CanonicalName: "example.com/privatebase",
|
||||
RemoteName: withName("privatebase"),
|
||||
LocalName: withName("example.com/privatebase"),
|
||||
CanonicalName: withName("example.com/privatebase"),
|
||||
Official: false,
|
||||
},
|
||||
"example.com:8000/private/moonbase": {
|
||||
|
@ -445,9 +478,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "example.com:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "private/moonbase",
|
||||
LocalName: "example.com:8000/private/moonbase",
|
||||
CanonicalName: "example.com:8000/private/moonbase",
|
||||
RemoteName: withName("private/moonbase"),
|
||||
LocalName: withName("example.com:8000/private/moonbase"),
|
||||
CanonicalName: withName("example.com:8000/private/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"example.com:8000/privatebase": {
|
||||
|
@ -455,9 +488,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "example.com:8000",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "privatebase",
|
||||
LocalName: "example.com:8000/privatebase",
|
||||
CanonicalName: "example.com:8000/privatebase",
|
||||
RemoteName: withName("privatebase"),
|
||||
LocalName: withName("example.com:8000/privatebase"),
|
||||
CanonicalName: withName("example.com:8000/privatebase"),
|
||||
Official: false,
|
||||
},
|
||||
"localhost/private/moonbase": {
|
||||
|
@ -465,9 +498,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "localhost",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "private/moonbase",
|
||||
LocalName: "localhost/private/moonbase",
|
||||
CanonicalName: "localhost/private/moonbase",
|
||||
RemoteName: withName("private/moonbase"),
|
||||
LocalName: withName("localhost/private/moonbase"),
|
||||
CanonicalName: withName("localhost/private/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"localhost/privatebase": {
|
||||
|
@ -475,9 +508,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: "localhost",
|
||||
Official: false,
|
||||
},
|
||||
RemoteName: "privatebase",
|
||||
LocalName: "localhost/privatebase",
|
||||
CanonicalName: "localhost/privatebase",
|
||||
RemoteName: withName("privatebase"),
|
||||
LocalName: withName("localhost/privatebase"),
|
||||
CanonicalName: withName("localhost/privatebase"),
|
||||
Official: false,
|
||||
},
|
||||
IndexName + "/public/moonbase": {
|
||||
|
@ -485,9 +518,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "public/moonbase",
|
||||
LocalName: "public/moonbase",
|
||||
CanonicalName: "docker.io/public/moonbase",
|
||||
RemoteName: withName("public/moonbase"),
|
||||
LocalName: withName("public/moonbase"),
|
||||
CanonicalName: withName("docker.io/public/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"index." + IndexName + "/public/moonbase": {
|
||||
|
@ -495,9 +528,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "public/moonbase",
|
||||
LocalName: "public/moonbase",
|
||||
CanonicalName: "docker.io/public/moonbase",
|
||||
RemoteName: withName("public/moonbase"),
|
||||
LocalName: withName("public/moonbase"),
|
||||
CanonicalName: withName("docker.io/public/moonbase"),
|
||||
Official: false,
|
||||
},
|
||||
"ubuntu-12.04-base": {
|
||||
|
@ -505,9 +538,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "library/ubuntu-12.04-base",
|
||||
LocalName: "ubuntu-12.04-base",
|
||||
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
||||
LocalName: withName("ubuntu-12.04-base"),
|
||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
||||
Official: true,
|
||||
},
|
||||
IndexName + "/ubuntu-12.04-base": {
|
||||
|
@ -515,9 +548,9 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "library/ubuntu-12.04-base",
|
||||
LocalName: "ubuntu-12.04-base",
|
||||
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
||||
LocalName: withName("ubuntu-12.04-base"),
|
||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
||||
Official: true,
|
||||
},
|
||||
"index." + IndexName + "/ubuntu-12.04-base": {
|
||||
|
@ -525,22 +558,27 @@ func TestParseRepositoryInfo(t *testing.T) {
|
|||
Name: IndexName,
|
||||
Official: true,
|
||||
},
|
||||
RemoteName: "library/ubuntu-12.04-base",
|
||||
LocalName: "ubuntu-12.04-base",
|
||||
CanonicalName: "docker.io/library/ubuntu-12.04-base",
|
||||
RemoteName: withName("library/ubuntu-12.04-base"),
|
||||
LocalName: withName("ubuntu-12.04-base"),
|
||||
CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
|
||||
Official: true,
|
||||
},
|
||||
}
|
||||
|
||||
for reposName, expectedRepoInfo := range expectedRepoInfos {
|
||||
repoInfo, err := ParseRepositoryInfo(reposName)
|
||||
named, err := reference.WithName(reposName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
repoInfo, err := ParseRepositoryInfo(named)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
|
||||
checkEqual(t, repoInfo.RemoteName, expectedRepoInfo.RemoteName, reposName)
|
||||
checkEqual(t, repoInfo.LocalName, expectedRepoInfo.LocalName, reposName)
|
||||
checkEqual(t, repoInfo.CanonicalName, expectedRepoInfo.CanonicalName, reposName)
|
||||
checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName)
|
||||
checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName)
|
||||
checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName)
|
||||
checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
|
||||
checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
|
||||
}
|
||||
|
@ -687,8 +725,11 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
|||
return false
|
||||
}
|
||||
s := Service{Config: makeServiceConfig([]string{"my.mirror"}, nil)}
|
||||
imageName := IndexName + "/test/image"
|
||||
|
||||
imageName, err := reference.WithName(IndexName + "/test/image")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
pushAPIEndpoints, err := s.LookupPushEndpoints(imageName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -708,7 +749,11 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
|||
|
||||
func TestPushRegistryTag(t *testing.T) {
|
||||
r := spawnTestRegistrySession(t)
|
||||
err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"))
|
||||
repoRef, err := reference.ParseNamed(REPO)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.PushRegistryTag(repoRef, imageID, "stable", makeURL("/v1/"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -726,14 +771,18 @@ func TestPushImageJSONIndex(t *testing.T) {
|
|||
Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
|
||||
},
|
||||
}
|
||||
repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
|
||||
repoRef, err := reference.ParseNamed(REPO)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repoData, err := r.PushImageJSONIndex(repoRef, imgData, false, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if repoData == nil {
|
||||
t.Fatal("Expected RepositoryData object")
|
||||
}
|
||||
repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()})
|
||||
repoData, err = r.PushImageJSONIndex(repoRef, imgData, true, []string{r.indexEndpoint.String()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -781,7 +830,11 @@ func TestValidRemoteName(t *testing.T) {
|
|||
"dock__er/docker",
|
||||
}
|
||||
for _, repositoryName := range validRepositoryNames {
|
||||
if err := validateRemoteName(repositoryName); err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -818,7 +871,11 @@ func TestValidRemoteName(t *testing.T) {
|
|||
"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 {
|
||||
if err := validateRemoteName(repositoryName); err == nil {
|
||||
repositoryRef, err := reference.ParseNamed(repositoryName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := validateRemoteName(repositoryRef); err == nil {
|
||||
t.Errorf("Repository name should be invalid: %v", repositoryName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
)
|
||||
|
@ -51,17 +53,39 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
|
|||
return Login(authConfig, endpoint)
|
||||
}
|
||||
|
||||
// splitReposSearchTerm breaks a search term into an index name and remote name
|
||||
func splitReposSearchTerm(reposName string) (string, string) {
|
||||
nameParts := strings.SplitN(reposName, "/", 2)
|
||||
var indexName, remoteName string
|
||||
if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") &&
|
||||
!strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
|
||||
// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
|
||||
// 'docker.io'
|
||||
indexName = IndexName
|
||||
remoteName = reposName
|
||||
} else {
|
||||
indexName = nameParts[0]
|
||||
remoteName = nameParts[1]
|
||||
}
|
||||
return indexName, remoteName
|
||||
}
|
||||
|
||||
// Search queries the public registry for images matching the specified
|
||||
// search terms, and returns the results.
|
||||
func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
|
||||
if err := validateNoSchema(term); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoInfo, err := s.ResolveRepositoryBySearch(term)
|
||||
indexName, remoteName := splitReposSearchTerm(term)
|
||||
|
||||
index, err := s.Config.NewIndexInfo(indexName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// *TODO: Search multiple indexes.
|
||||
endpoint, err := NewEndpoint(repoInfo.Index, http.Header(headers), APIVersionUnknown)
|
||||
endpoint, err := NewEndpoint(index, http.Header(headers), APIVersionUnknown)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -70,19 +94,23 @@ func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.SearchRepositories(repoInfo.GetSearchTerm())
|
||||
|
||||
if index.Official {
|
||||
localName := remoteName
|
||||
if strings.HasPrefix(localName, "library/") {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
localName = strings.SplitN(localName, "/", 2)[1]
|
||||
}
|
||||
|
||||
return r.SearchRepositories(localName)
|
||||
}
|
||||
return r.SearchRepositories(remoteName)
|
||||
}
|
||||
|
||||
// ResolveRepository splits a repository name into its components
|
||||
// and configuration of the associated registry.
|
||||
func (s *Service) ResolveRepository(name string) (*RepositoryInfo, error) {
|
||||
return s.Config.NewRepositoryInfo(name, false)
|
||||
}
|
||||
|
||||
// ResolveRepositoryBySearch splits a repository name into its components
|
||||
// and configuration of the associated registry.
|
||||
func (s *Service) ResolveRepositoryBySearch(name string) (*RepositoryInfo, error) {
|
||||
return s.Config.NewRepositoryInfo(name, true)
|
||||
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
||||
return s.Config.NewRepositoryInfo(name)
|
||||
}
|
||||
|
||||
// ResolveIndex takes indexName and returns index info
|
||||
|
@ -123,14 +151,14 @@ func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) {
|
|||
// LookupPullEndpoints creates an list of endpoints to try to pull from, in order of preference.
|
||||
// It gives preference to v2 endpoints over v1, mirrors over the actual
|
||||
// registry, and HTTPS over plain HTTP.
|
||||
func (s *Service) LookupPullEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
||||
func (s *Service) LookupPullEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||
return s.lookupEndpoints(repoName)
|
||||
}
|
||||
|
||||
// LookupPushEndpoints creates an list of endpoints to try to push to, in order of preference.
|
||||
// It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
|
||||
// Mirrors are not included.
|
||||
func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
||||
func (s *Service) LookupPushEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||
allEndpoints, err := s.lookupEndpoints(repoName)
|
||||
if err == nil {
|
||||
for _, endpoint := range allEndpoints {
|
||||
|
@ -142,7 +170,7 @@ func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint,
|
|||
return endpoints, err
|
||||
}
|
||||
|
||||
func (s *Service) lookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
||||
func (s *Service) lookupEndpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||
endpoints, err = s.lookupV2Endpoints(repoName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -4,13 +4,15 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/pkg/tlsconfig"
|
||||
)
|
||||
|
||||
func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
||||
func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||
var cfg = tlsconfig.ServerDefault
|
||||
tlsConfig := &cfg
|
||||
if strings.HasPrefix(repoName, DefaultNamespace+"/") {
|
||||
nameString := repoName.Name()
|
||||
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
||||
endpoints = append(endpoints, APIEndpoint{
|
||||
URL: DefaultV1Registry,
|
||||
Version: APIVersion1,
|
||||
|
@ -21,11 +23,11 @@ func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, e
|
|||
return endpoints, nil
|
||||
}
|
||||
|
||||
slashIndex := strings.IndexRune(repoName, '/')
|
||||
slashIndex := strings.IndexRune(nameString, '/')
|
||||
if slashIndex <= 0 {
|
||||
return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
|
||||
return nil, fmt.Errorf("invalid repo name: missing '/': %s", nameString)
|
||||
}
|
||||
hostname := repoName[:slashIndex]
|
||||
hostname := nameString[:slashIndex]
|
||||
|
||||
tlsConfig, err = s.TLSConfig(hostname)
|
||||
if err != nil {
|
||||
|
|
|
@ -4,14 +4,16 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/docker/pkg/tlsconfig"
|
||||
)
|
||||
|
||||
func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
||||
func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
|
||||
var cfg = tlsconfig.ServerDefault
|
||||
tlsConfig := &cfg
|
||||
if strings.HasPrefix(repoName, DefaultNamespace+"/") {
|
||||
nameString := repoName.Name()
|
||||
if strings.HasPrefix(nameString, DefaultNamespace+"/") {
|
||||
// v2 mirrors
|
||||
for _, mirror := range s.Config.Mirrors {
|
||||
mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
|
||||
|
@ -39,11 +41,11 @@ func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, e
|
|||
return endpoints, nil
|
||||
}
|
||||
|
||||
slashIndex := strings.IndexRune(repoName, '/')
|
||||
slashIndex := strings.IndexRune(nameString, '/')
|
||||
if slashIndex <= 0 {
|
||||
return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
|
||||
return nil, fmt.Errorf("invalid repo name: missing '/': %s", nameString)
|
||||
}
|
||||
hostname := repoName[:slashIndex]
|
||||
hostname := nameString[:slashIndex]
|
||||
|
||||
tlsConfig, err = s.TLSConfig(hostname)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/pkg/httputils"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
|
@ -320,7 +321,9 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
|
|||
// repository. It queries each of the registries supplied in the registries
|
||||
// argument, and returns data from the first one that answers the query
|
||||
// successfully.
|
||||
func (r *Session) GetRemoteTag(registries []string, repository string, askedTag string) (string, error) {
|
||||
func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
|
||||
repository := repositoryRef.Name()
|
||||
|
||||
if strings.Count(repository, "/") == 0 {
|
||||
// This will be removed once the registry supports auto-resolution on
|
||||
// the "library" namespace
|
||||
|
@ -356,7 +359,9 @@ func (r *Session) GetRemoteTag(registries []string, repository string, askedTag
|
|||
// of the registries supplied in the registries argument, and returns data from
|
||||
// the first one that answers the query successfully. It returns a map with
|
||||
// tag names as the keys and image IDs as the values.
|
||||
func (r *Session) GetRemoteTags(registries []string, repository string) (map[string]string, error) {
|
||||
func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
|
||||
repository := repositoryRef.Name()
|
||||
|
||||
if strings.Count(repository, "/") == 0 {
|
||||
// This will be removed once the registry supports auto-resolution on
|
||||
// the "library" namespace
|
||||
|
@ -408,8 +413,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
|
|||
}
|
||||
|
||||
// GetRepositoryData returns lists of images and endpoints for the repository
|
||||
func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
|
||||
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote)
|
||||
func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
|
||||
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
|
||||
|
||||
logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
|
||||
|
||||
|
@ -443,7 +448,7 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) {
|
|||
if err != nil {
|
||||
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, errBody), res)
|
||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
||||
}
|
||||
|
||||
var endpoints []string
|
||||
|
@ -595,10 +600,10 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
|
|||
|
||||
// PushRegistryTag pushes a tag on the registry.
|
||||
// Remote has the format '<user>/<repo>
|
||||
func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error {
|
||||
func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
|
||||
// "jsonify" the string
|
||||
revision = "\"" + revision + "\""
|
||||
path := fmt.Sprintf("repositories/%s/tags/%s", remote, tag)
|
||||
path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
|
||||
|
||||
req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
|
||||
if err != nil {
|
||||
|
@ -612,13 +617,13 @@ func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error
|
|||
}
|
||||
res.Body.Close()
|
||||
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), res)
|
||||
return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushImageJSONIndex uploads an image list to the repository
|
||||
func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
|
||||
func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
|
||||
cleanImgList := []*ImgData{}
|
||||
if validate {
|
||||
for _, elem := range imgList {
|
||||
|
@ -638,7 +643,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
|
|||
if validate {
|
||||
suffix = "images"
|
||||
}
|
||||
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote, suffix)
|
||||
u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
|
||||
logrus.Debugf("[registry] PUT %s", u)
|
||||
logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
|
||||
headers := map[string][]string{
|
||||
|
@ -676,7 +681,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
|
|||
if err != nil {
|
||||
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, errBody), res)
|
||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
||||
}
|
||||
tokens = res.Header["X-Docker-Token"]
|
||||
logrus.Debugf("Auth token: %v", tokens)
|
||||
|
@ -694,7 +699,7 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
|
|||
if err != nil {
|
||||
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, errBody), res)
|
||||
return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"github.com/docker/distribution/reference"
|
||||
)
|
||||
|
||||
// SearchResult describes a search result returned from a registry
|
||||
type SearchResult struct {
|
||||
// StarCount indicates the number of stars this repository has
|
||||
|
@ -126,13 +130,13 @@ type RepositoryInfo struct {
|
|||
Index *IndexInfo
|
||||
// RemoteName is the remote name of the repository, such as
|
||||
// "library/ubuntu-12.04-base"
|
||||
RemoteName string
|
||||
RemoteName reference.Named
|
||||
// LocalName is the local name of the repository, such as
|
||||
// "ubuntu-12.04-base"
|
||||
LocalName string
|
||||
LocalName reference.Named
|
||||
// CanonicalName is the canonical name of the repository, such as
|
||||
// "docker.io/library/ubuntu-12.04-base"
|
||||
CanonicalName string
|
||||
CanonicalName reference.Named
|
||||
// Official indicates whether the repository is considered official.
|
||||
// If the registry is official, and the normalized name does not
|
||||
// contain a '/' (e.g. "foo"), then it is considered an official repo.
|
||||
|
|
Loading…
Reference in a new issue