Prechádzať zdrojové kódy

Merge pull request #1168 from dotcloud/standalone_registry

* Server: Allow push on standalone registry
Victor Vieux 12 rokov pred
rodič
commit
e962e9edcf
7 zmenil súbory, kde vykonal 82 pridanie a 70 odobranie
  1. 0 3
      auth/auth.go
  2. 1 11
      buildfile.go
  3. 5 7
      commands.go
  4. 19 3
      registry/registry.go
  5. 39 39
      server.go
  6. 4 7
      tags.go
  7. 14 0
      utils/utils.go

+ 0 - 3
auth/auth.go

@@ -41,9 +41,6 @@ func NewAuthConfig(username, password, email, rootPath string) *AuthConfig {
 }
 }
 
 
 func IndexServerAddress() string {
 func IndexServerAddress() string {
-	if os.Getenv("DOCKER_INDEX_URL") != "" {
-		return os.Getenv("DOCKER_INDEX_URL") + "/v1/"
-	}
 	return INDEXSERVER
 	return INDEXSERVER
 }
 }
 
 

+ 1 - 11
buildfile.go

@@ -52,20 +52,10 @@ func (b *buildFile) CmdFrom(name string) error {
 	image, err := b.runtime.repositories.LookupImage(name)
 	image, err := b.runtime.repositories.LookupImage(name)
 	if err != nil {
 	if err != nil {
 		if b.runtime.graph.IsNotExist(err) {
 		if b.runtime.graph.IsNotExist(err) {
-
-			var tag, remote string
-			if strings.Contains(name, ":") {
-				remoteParts := strings.Split(name, ":")
-				tag = remoteParts[1]
-				remote = remoteParts[0]
-			} else {
-				remote = name
-			}
-
+			remote, tag := utils.ParseRepositoryTag(name)
 			if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil); err != nil {
 			if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil); err != nil {
 				return err
 				return err
 			}
 			}
-
 			image, err = b.runtime.repositories.LookupImage(name)
 			image, err = b.runtime.repositories.LookupImage(name)
 			if err != nil {
 			if err != nil {
 				return err
 				return err

+ 5 - 7
commands.go

@@ -766,12 +766,8 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	remote := cmd.Arg(0)
-	if strings.Contains(remote, ":") {
-		remoteParts := strings.Split(remote, ":")
-		tag = &remoteParts[1]
-		remote = remoteParts[0]
-	}
+	remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
+	*tag = parsedTag
 
 
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("fromImage", remote)
 	v.Set("fromImage", remote)
@@ -1246,7 +1242,9 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	//if image not found try to pull it
 	//if image not found try to pull it
 	if statusCode == 404 {
 	if statusCode == 404 {
 		v := url.Values{}
 		v := url.Values{}
-		v.Set("fromImage", config.Image)
+		repos, tag := utils.ParseRepositoryTag(config.Image)
+		v.Set("fromImage", repos)
+		v.Set("tag", tag)
 		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err)
 		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err)
 		if err != nil {
 		if err != nil {
 			return err
 			return err

+ 19 - 3
registry/registry.go

@@ -18,8 +18,14 @@ import (
 )
 )
 
 
 var ErrAlreadyExists = errors.New("Image already exists")
 var ErrAlreadyExists = errors.New("Image already exists")
+var ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
 
 
 func pingRegistryEndpoint(endpoint string) error {
 func pingRegistryEndpoint(endpoint string) error {
+	if endpoint == auth.IndexServerAddress() {
+		// Skip the check, we now this one is valid
+		// (and we never want to fallback to http in case of error)
+		return nil
+	}
 	resp, err := http.Get(endpoint + "_ping")
 	resp, err := http.Get(endpoint + "_ping")
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -56,19 +62,29 @@ func validateRepositoryName(repositoryName string) error {
 
 
 // Resolves a repository name to a endpoint + name
 // Resolves a repository name to a endpoint + name
 func ResolveRepositoryName(reposName string) (string, string, error) {
 func ResolveRepositoryName(reposName string) (string, string, error) {
+	if strings.Contains(reposName, "://") {
+		// It cannot contain a scheme!
+		return "", "", ErrInvalidRepositoryName
+	}
 	nameParts := strings.SplitN(reposName, "/", 2)
 	nameParts := strings.SplitN(reposName, "/", 2)
-	if !strings.Contains(nameParts[0], ".") {
+	if !strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") {
 		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
 		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
 		err := validateRepositoryName(reposName)
 		err := validateRepositoryName(reposName)
-		return "https://index.docker.io/v1/", reposName, err
+		return auth.IndexServerAddress(), reposName, err
 	}
 	}
 	if len(nameParts) < 2 {
 	if len(nameParts) < 2 {
 		// There is a dot in repos name (and no registry address)
 		// There is a dot in repos name (and no registry address)
 		// Is it a Registry address without repos name?
 		// Is it a Registry address without repos name?
-		return "", "", fmt.Errorf("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
+		return "", "", ErrInvalidRepositoryName
 	}
 	}
 	hostname := nameParts[0]
 	hostname := nameParts[0]
 	reposName = nameParts[1]
 	reposName = nameParts[1]
+	if strings.Contains(hostname, "index.docker.io") {
+		return "", "", fmt.Errorf("Invalid repository name, try \"%s\" instead", reposName)
+	}
+	if err := validateRepositoryName(reposName); err != nil {
+		return "", "", err
+	}
 	endpoint := fmt.Sprintf("https://%s/v1/", hostname)
 	endpoint := fmt.Sprintf("https://%s/v1/", hostname)
 	if err := pingRegistryEndpoint(endpoint); err != nil {
 	if err := pingRegistryEndpoint(endpoint); err != nil {
 		utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
 		utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)

+ 39 - 39
server.go

@@ -351,10 +351,10 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, name, askedTag, indexEp string, sf *utils.StreamFormatter) error {
-	out.Write(sf.FormatStatus("Pulling repository %s from %s", name, indexEp))
+func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName, remoteName, askedTag, indexEp string, sf *utils.StreamFormatter) error {
+	out.Write(sf.FormatStatus("Pulling repository %s", localName))
 
 
-	repoData, err := r.GetRepositoryData(indexEp, name)
+	repoData, err := r.GetRepositoryData(indexEp, remoteName)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -366,7 +366,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, name, ask
 	}
 	}
 
 
 	utils.Debugf("Retrieving the tag list")
 	utils.Debugf("Retrieving the tag list")
-	tagsList, err := r.GetRemoteTags(repoData.Endpoints, name, repoData.Tokens)
+	tagsList, err := r.GetRemoteTags(repoData.Endpoints, remoteName, repoData.Tokens)
 	if err != nil {
 	if err != nil {
 		utils.Debugf("%v", err)
 		utils.Debugf("%v", err)
 		return err
 		return err
@@ -390,7 +390,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, name, ask
 		// Otherwise, check that the tag exists and use only that one
 		// Otherwise, check that the tag exists and use only that one
 		id, exists := tagsList[askedTag]
 		id, exists := tagsList[askedTag]
 		if !exists {
 		if !exists {
-			return fmt.Errorf("Tag %s not found in repository %s", askedTag, name)
+			return fmt.Errorf("Tag %s not found in repository %s", askedTag, localName)
 		}
 		}
 		repoData.ImgList[id].Tag = askedTag
 		repoData.ImgList[id].Tag = askedTag
 	}
 	}
@@ -405,7 +405,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, name, ask
 			utils.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID)
 			utils.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID)
 			continue
 			continue
 		}
 		}
-		out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, name))
+		out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, localName))
 		success := false
 		success := false
 		for _, ep := range repoData.Endpoints {
 		for _, ep := range repoData.Endpoints {
 			if err := srv.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
 			if err := srv.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
@@ -423,7 +423,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, name, ask
 		if askedTag != "" && tag != askedTag {
 		if askedTag != "" && tag != askedTag {
 			continue
 			continue
 		}
 		}
-		if err := srv.runtime.repositories.Set(name, tag, id, true); err != nil {
+		if err := srv.runtime.repositories.Set(localName, tag, id, true); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -469,27 +469,31 @@ func (srv *Server) poolRemove(kind, key string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (srv *Server) ImagePull(name string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
+func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
 	r, err := registry.NewRegistry(srv.runtime.root, authConfig)
 	r, err := registry.NewRegistry(srv.runtime.root, authConfig)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if err := srv.poolAdd("pull", name+":"+tag); err != nil {
+	if err := srv.poolAdd("pull", localName+":"+tag); err != nil {
 		return err
 		return err
 	}
 	}
-	defer srv.poolRemove("pull", name+":"+tag)
+	defer srv.poolRemove("pull", localName+":"+tag)
 
 
 	// Resolve the Repository name from fqn to endpoint + name
 	// Resolve the Repository name from fqn to endpoint + name
-	var endpoint string
-	endpoint, name, err = registry.ResolveRepositoryName(name)
+	endpoint, remoteName, err := registry.ResolveRepositoryName(localName)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	if endpoint == auth.IndexServerAddress() {
+		// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
+		localName = remoteName
+	}
+
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
-	err = srv.pullRepository(r, out, name, tag, endpoint, sf)
+	err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf)
 	if err != nil {
 	if err != nil {
-		if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
+		if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil {
 			return err
 			return err
 		}
 		}
 		return nil
 		return nil
@@ -564,7 +568,7 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
 	return imgList, nil
 	return imgList, nil
 }
 }
 
 
-func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string, indexEp string, sf *utils.StreamFormatter) error {
+func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, indexEp string, sf *utils.StreamFormatter) error {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	out.Write(sf.FormatStatus("Processing checksums"))
 	out.Write(sf.FormatStatus("Processing checksums"))
 	imgList, err := srv.getImageList(localRepo)
 	imgList, err := srv.getImageList(localRepo)
@@ -572,41 +576,36 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
 		return err
 		return err
 	}
 	}
 	out.Write(sf.FormatStatus("Sending image list"))
 	out.Write(sf.FormatStatus("Sending image list"))
-	srvName := name
-	parts := strings.Split(name, "/")
-	if len(parts) > 2 {
-		srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
-	}
 
 
 	var repoData *registry.RepositoryData
 	var repoData *registry.RepositoryData
-	repoData, err = r.PushImageJSONIndex(indexEp, name, imgList, false, nil)
+	repoData, err = r.PushImageJSONIndex(indexEp, remoteName, imgList, false, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	for _, ep := range repoData.Endpoints {
 	for _, ep := range repoData.Endpoints {
-		out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", name, ep, len(localRepo)))
+		out.Write(sf.FormatStatus("Pushing repository %s (%d tags)", localName, len(localRepo)))
 		// For each image within the repo, push them
 		// For each image within the repo, push them
 		for _, elem := range imgList {
 		for _, elem := range imgList {
 			if _, exists := repoData.ImgList[elem.ID]; exists {
 			if _, exists := repoData.ImgList[elem.ID]; exists {
-				out.Write(sf.FormatStatus("Image %s already on registry, skipping", name))
+				out.Write(sf.FormatStatus("Image %s already pushed, skipping", elem.ID))
 				continue
 				continue
 			} else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) {
 			} else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) {
-				fmt.Fprintf(out, "Image %s already on registry, skipping\n", name)
+				out.Write(sf.FormatStatus("Image %s already pushed, skipping", elem.ID))
 				continue
 				continue
 			}
 			}
-			if err := srv.pushImage(r, out, name, elem.ID, ep, repoData.Tokens, sf); err != nil {
+			if err := srv.pushImage(r, out, remoteName, elem.ID, ep, repoData.Tokens, sf); err != nil {
 				// FIXME: Continue on error?
 				// FIXME: Continue on error?
 				return err
 				return err
 			}
 			}
-			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"repositories/"+srvName+"/tags/"+elem.Tag))
-			if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
+			out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"repositories/"+remoteName+"/tags/"+elem.Tag))
+			if err := r.PushRegistryTag(remoteName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if _, err := r.PushImageJSONIndex(indexEp, name, imgList, true, repoData.Endpoints); err != nil {
+	if _, err := r.PushImageJSONIndex(indexEp, remoteName, imgList, true, repoData.Endpoints); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -634,7 +633,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
 	// Send the json
 	// Send the json
 	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
 	if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil {
 		if err == registry.ErrAlreadyExists {
 		if err == registry.ErrAlreadyExists {
-			out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID))
+			out.Write(sf.FormatStatus("Image %s already pushed, skipping", imgData.ID))
 			return nil
 			return nil
 		}
 		}
 		return err
 		return err
@@ -674,30 +673,31 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
 }
 }
 
 
 // FIXME: Allow to interupt current push when new push of same image is done.
 // FIXME: Allow to interupt current push when new push of same image is done.
-func (srv *Server) ImagePush(name string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
-	if err := srv.poolAdd("push", name); err != nil {
+func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
+	if err := srv.poolAdd("push", localName); err != nil {
 		return err
 		return err
 	}
 	}
-	defer srv.poolRemove("push", name)
+	defer srv.poolRemove("push", localName)
 
 
 	// Resolve the Repository name from fqn to endpoint + name
 	// Resolve the Repository name from fqn to endpoint + name
-	endpoint, name, err := registry.ResolveRepositoryName(name)
+	endpoint, remoteName, err := registry.ResolveRepositoryName(localName)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
-	img, err := srv.runtime.graph.Get(name)
+	img, err := srv.runtime.graph.Get(localName)
 	r, err2 := registry.NewRegistry(srv.runtime.root, authConfig)
 	r, err2 := registry.NewRegistry(srv.runtime.root, authConfig)
 	if err2 != nil {
 	if err2 != nil {
 		return err2
 		return err2
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {
-		out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
+		reposLen := len(srv.runtime.repositories.Repositories[localName])
+		out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", localName, reposLen))
 		// If it fails, try to get the repository
 		// If it fails, try to get the repository
-		if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
-			if err := srv.pushRepository(r, out, name, localRepo, endpoint, sf); err != nil {
+		if localRepo, exists := srv.runtime.repositories.Repositories[localName]; exists {
+			if err := srv.pushRepository(r, out, localName, remoteName, localRepo, endpoint, sf); err != nil {
 				return err
 				return err
 			}
 			}
 			return nil
 			return nil
@@ -706,8 +706,8 @@ func (srv *Server) ImagePush(name string, out io.Writer, sf *utils.StreamFormatt
 	}
 	}
 
 
 	var token []string
 	var token []string
-	out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
-	if err := srv.pushImage(r, out, name, img.ID, endpoint, token, sf); err != nil {
+	out.Write(sf.FormatStatus("The push refers to an image: [%s]", localName))
+	if err := srv.pushImage(r, out, remoteName, img.ID, endpoint, token, sf); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil

+ 4 - 7
tags.go

@@ -70,11 +70,11 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
 	if err != nil {
 	if err != nil {
 		// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 		// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
 		// (so we can pass all errors here)
 		// (so we can pass all errors here)
-		repoAndTag := strings.SplitN(name, ":", 2)
-		if len(repoAndTag) == 1 {
-			repoAndTag = append(repoAndTag, DEFAULTTAG)
+		repos, tag := utils.ParseRepositoryTag(name)
+		if tag == "" {
+			tag = DEFAULTTAG
 		}
 		}
-		if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
+		if i, err := store.GetImage(repos, tag); err != nil {
 			return nil, err
 			return nil, err
 		} else if i == nil {
 		} else if i == nil {
 			return nil, fmt.Errorf("Image does not exist: %s", name)
 			return nil, fmt.Errorf("Image does not exist: %s", name)
@@ -221,9 +221,6 @@ func validateRepoName(name string) error {
 	if name == "" {
 	if name == "" {
 		return fmt.Errorf("Repository name can't be empty")
 		return fmt.Errorf("Repository name can't be empty")
 	}
 	}
-	if strings.Contains(name, ":") {
-		return fmt.Errorf("Illegal repository name: %s", name)
-	}
 	return nil
 	return nil
 }
 }
 
 

+ 14 - 0
utils/utils.go

@@ -686,3 +686,17 @@ func ParseHost(host string, port int, addr string) string {
 	}
 	}
 	return fmt.Sprintf("tcp://%s:%d", host, port)
 	return fmt.Sprintf("tcp://%s:%d", host, port)
 }
 }
+
+// Get a repos name and returns the right reposName + tag
+// The tag can be confusing because of a port in a repository name.
+//     Ex: localhost.localdomain:5000/samalba/hipache:latest
+func ParseRepositoryTag(repos string) (string, string) {
+	n := strings.LastIndex(repos, ":")
+	if n < 0 {
+		return repos, ""
+	}
+	if tag := repos[n+1:]; !strings.Contains(tag, "/") {
+		return repos[:n], tag
+	}
+	return repos, ""
+}