func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) { utils.Debugf("Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repositoryTarget := auth.IndexServerAddress() + "/repositories/" + remote + "/images" req, err := http.NewRequest("GET", repositoryTarget, nil) if err != nil { return nil, err } if r.authConfig != nil && len(r.authConfig.Username) > 0 { req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) } req.Header.Set("X-Docker-Token", "true") res, err := r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode == 401 { return nil, fmt.Errorf("Please login first (HTTP code %d)", res.StatusCode) } // TODO: Right now we're ignoring checksums in the response body. // In the future, we need to use them to check image validity. if res.StatusCode != 200 { return nil, fmt.Errorf("HTTP code: %d", res.StatusCode) } var tokens []string if res.Header.Get("X-Docker-Token") != "" { tokens = res.Header["X-Docker-Token"] } var endpoints []string if res.Header.Get("X-Docker-Endpoints") != "" { endpoints = res.Header["X-Docker-Endpoints"] } else { return nil, fmt.Errorf("Index response didn't contain any endpoints") } checksumsJson, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } remoteChecksums := []*ImgData{} if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil { return nil, err } // Forge a better object from the retrieved data imgsData := make(map[string]*ImgData) for _, elem := range remoteChecksums { imgsData[elem.Id] = elem } return &RepositoryData{ ImgList: imgsData, Endpoints: endpoints, Tokens: tokens, }, nil }
func (cli *DockerCli) checkIfLogged(action string) error { // If condition AND the login failed if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { if err := cli.CmdLogin(""); err != nil { return err } if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { return fmt.Errorf("Please login prior to %s. ('docker login')", action) } } return nil }
func (cli *DockerCli) CmdPush(args ...string) error { cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry") if err := cmd.Parse(args); err != nil { return nil } name := cmd.Arg(0) if name == "" { cmd.Usage() return nil } cli.LoadConfigFile() // If we're not using a custom registry, we know the restrictions // applied to repository names and can warn the user in advance. // Custom repositories can have different rules, and we must also // allow pushing by image ID. if len(strings.SplitN(name, "/", 2)) == 1 { username := cli.configFile.Configs[auth.IndexServerAddress()].Username if username == "" { username = "******" } return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name) } v := url.Values{} push := func() error { buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) if err != nil { return err } return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out) } if err := push(); err != nil { if err.Error() == "Authentication is required." { fmt.Fprintln(cli.out, "\nPlease login prior to push:") if err := cli.CmdLogin(""); err != nil { return err } return push() } return err } return nil }
func (r *Registry) SearchRepositories(term string) (*SearchResults, error) { utils.Debugf("Index server: %s", r.indexEndpoint) u := auth.IndexServerAddress() + "search?q=" + url.QueryEscape(term) req, err := r.reqFactory.NewRequest("GET", u, nil) if err != nil { return nil, err } if r.authConfig != nil && len(r.authConfig.Username) > 0 { req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) } req.Header.Set("X-Docker-Token", "true") res, err := r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, utils.NewHTTPRequestError(fmt.Sprintf("Unexepected status code %d", res.StatusCode), res) } rawData, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } result := new(SearchResults) err = json.Unmarshal(rawData, result) return result, err }
func UrlScheme() string { u, err := url.Parse(auth.IndexServerAddress()) if err != nil { return "https" } return u.Scheme }
func (srv *Server) DockerInfo() *APIInfo { images, _ := srv.runtime.graph.Map() var imgcount int if images == nil { imgcount = 0 } else { imgcount = len(images) } lxcVersion := "" if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil { outputStr := string(output) if len(strings.SplitN(outputStr, ":", 2)) == 2 { lxcVersion = strings.TrimSpace(strings.SplitN(string(output), ":", 2)[1]) } } kernelVersion := "<unknown>" if kv, err := utils.GetKernelVersion(); err == nil { kernelVersion = kv.String() } return &APIInfo{ Containers: len(srv.runtime.List()), Images: imgcount, MemoryLimit: srv.runtime.capabilities.MemoryLimit, SwapLimit: srv.runtime.capabilities.SwapLimit, IPv4Forwarding: !srv.runtime.capabilities.IPv4ForwardingDisabled, Debug: os.Getenv("DEBUG") != "", NFd: utils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), LXCVersion: lxcVersion, NEventsListener: len(srv.events), KernelVersion: kernelVersion, IndexServerAddress: auth.IndexServerAddress(), } }
// Resolves a repository name to a endpoint + name func ResolveRepositoryName(reposName string) (string, string, error) { if strings.Contains(reposName, "://") { // It cannot contain a scheme! return "", "", ErrInvalidRepositoryName } nameParts := strings.SplitN(reposName, "/", 2) if !strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost" { // This is a Docker Index repos (ex: samalba/hipache or ubuntu) err := validateRepositoryName(reposName) return auth.IndexServerAddress(), reposName, err } if len(nameParts) < 2 { // There is a dot in repos name (and no registry address) // Is it a Registry address without repos name? return "", "", ErrInvalidRepositoryName } hostname := nameParts[0] 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, err := ExpandAndVerifyRegistryUrl(hostname) if err != nil { return "", "", err } return endpoint, reposName, err }
func (cli *DockerCli) CmdPush(args ...string) error { cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry") if err := cmd.Parse(args); err != nil { return nil } name := cmd.Arg(0) if name == "" { cmd.Usage() return nil } if err := cli.checkIfLogged("push"); err != nil { return err } // If we're not using a custom registry, we know the restrictions // applied to repository names and can warn the user in advance. // Custom repositories can have different rules, and we must also // allow pushing by image ID. if len(strings.SplitN(name, "/", 2)) == 1 { return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.configFile.Configs[auth.IndexServerAddress()].Username, name) } buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) if err != nil { return err } v := url.Values{} if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out); err != nil { return err } return nil }
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 } httpDial := func(proto string, addr string) (net.Conn, error) { // Set the connect timeout to 5 seconds conn, err := net.DialTimeout(proto, addr, time.Duration(5)*time.Second) if err != nil { return nil, err } // Set the recv timeout to 10 seconds conn.SetDeadline(time.Now().Add(time.Duration(10) * time.Second)) return conn, nil } httpTransport := &http.Transport{Dial: httpDial} client := &http.Client{Transport: httpTransport} resp, err := client.Get(endpoint + "_ping") if err != nil { return err } defer resp.Body.Close() if resp.Header.Get("X-Docker-Registry-Version") == "" { return errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)") } return nil }
func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) { u := auth.IndexServerAddress() + "/repositories/" + repository + "/images" req, err := http.NewRequest("GET", u, nil) if err != nil { return nil, err } if authConfig != nil && len(authConfig.Username) > 0 { req.SetBasicAuth(authConfig.Username, authConfig.Password) } res, err := r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() // Repository doesn't exist yet if res.StatusCode == 404 { return nil, nil } jsonData, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } imageList := []map[string]string{} if err := json.Unmarshal(jsonData, &imageList); err != nil { utils.Debugf("Body: %s (%s)\n", res.Body, u) return nil, err } return imageList, nil }
func NewRegistry(authConfig *auth.AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) { httpTransport := &http.Transport{ DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, } r = &Registry{ authConfig: authConfig, client: &http.Client{ Transport: httpTransport, }, indexEndpoint: indexEndpoint, } r.client.Jar, err = cookiejar.New(nil) if err != nil { return nil, err } // If we're working with a private registry over HTTPS, send Basic Auth headers // alongside our requests. if indexEndpoint != auth.IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") { utils.Debugf("Endpoint %s is eligible for private registry auth. Enabling decorator.", indexEndpoint) dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password) factory.AddDecorator(dec) } r.reqFactory = factory return r, nil }
func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error { r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.HTTPRequestFactory(metaHeaders)) if err != nil { return err } if err := srv.poolAdd("pull", localName+":"+tag); err != nil { return err } defer srv.poolRemove("pull", localName+":"+tag) // Resolve the Repository name from fqn to endpoint + name endpoint, remoteName, err := registry.ResolveRepositoryName(localName) if err != nil { 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) err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel) if err == registry.ErrLoginRequired { return err } if err != nil { if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil { return err } return nil } return nil }
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") if err != nil { return err } if resp.Header.Get("X-Docker-Registry-Version") == "" { return errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)") } return nil }
func TestResolveRepositoryName(t *testing.T) { _, _, err := ResolveRepositoryName("https://github.com/dotcloud/docker") assertEqual(t, err, ErrInvalidRepositoryName, "Expected error invalid repo name") ep, repo, err := ResolveRepositoryName("fooo/bar") if err != nil { t.Fatal(err) } assertEqual(t, ep, auth.IndexServerAddress(), "Expected endpoint to be index server address") assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar") u := makeURL("")[7:] ep, repo, err = ResolveRepositoryName(u + "/private/moonbase") if err != nil { t.Fatal(err) } assertEqual(t, ep, "http://"+u+"/v1/", "Expected endpoint to be "+u) assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase") }
func pingRegistryEndpoint(endpoint string) (bool, 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 false, nil } httpDial := func(proto string, addr string) (net.Conn, error) { // Set the connect timeout to 5 seconds conn, err := net.DialTimeout(proto, addr, time.Duration(5)*time.Second) if err != nil { return nil, err } // Set the recv timeout to 10 seconds conn.SetDeadline(time.Now().Add(time.Duration(10) * time.Second)) return conn, nil } httpTransport := &http.Transport{Dial: httpDial} client := &http.Client{Transport: httpTransport} resp, err := client.Get(endpoint + "_ping") if err != nil { return false, err } defer resp.Body.Close() if resp.Header.Get("X-Docker-Registry-Version") == "" { return false, errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)") } standalone := resp.Header.Get("X-Docker-Registry-Standalone") utils.Debugf("Registry standalone header: '%s'", standalone) // If the header is absent, we assume true for compatibility with earlier // versions of the registry if standalone == "" { return true, nil // Accepted values are "true" (case-insensitive) and "1". } else if strings.EqualFold(standalone, "true") || standalone == "1" { return true, nil } // Otherwise, not standalone return false, nil }
func (r *Registry) SearchRepositories(term string) (*SearchResults, error) { u := auth.IndexServerAddress() + "search?q=" + url.QueryEscape(term) req, err := r.reqFactory.NewRequest("GET", u, nil) if err != nil { return nil, err } res, err := r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, utils.NewHTTPRequestError(fmt.Sprintf("Unexepected status code %d", res.StatusCode), res) } rawData, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } result := new(SearchResults) err = json.Unmarshal(rawData, result) return result, err }
// Resolves a repository name to a endpoint + name func ResolveRepositoryName(reposName string) (string, string, error) { if strings.Contains(reposName, "://") { // It cannot contain a scheme! return "", "", ErrInvalidRepositoryName } nameParts := strings.SplitN(reposName, "/", 2) if !strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost" { // This is a Docker Index repos (ex: samalba/hipache or ubuntu) err := validateRepositoryName(reposName) return auth.IndexServerAddress(), reposName, err } if len(nameParts) < 2 { // There is a dot in repos name (and no registry address) // Is it a Registry address without repos name? return "", "", ErrInvalidRepositoryName } hostname := nameParts[0] 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) if err := pingRegistryEndpoint(endpoint); err != nil { utils.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err) endpoint = fmt.Sprintf("http://%s/v1/", hostname) if err = pingRegistryEndpoint(endpoint); err != nil { //TODO: triggering highland build can be done there without "failing" return "", "", errors.New("Invalid Registry endpoint: " + err.Error()) } } err := validateRepositoryName(reposName) return endpoint, reposName, err }
func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) { imgListJson, err := json.Marshal(imgList) if err != nil { return nil, err } var suffix string if validate { suffix = "images" } req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson)) if err != nil { return nil, err } req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJson)) req.Header.Set("X-Docker-Token", "true") res, err := r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() // Redirect if necessary for res.StatusCode >= 300 && res.StatusCode < 400 { utils.Debugf("Redirected to %s\n", res.Header.Get("Location")) req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson)) if err != nil { return nil, err } req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJson)) req.Header.Set("X-Docker-Token", "true") res, err = r.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() } var tokens, endpoints []string if !validate { if res.StatusCode != 200 && res.StatusCode != 201 { errBody, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } return nil, fmt.Errorf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody) } if res.Header.Get("X-Docker-Token") != "" { tokens = res.Header["X-Docker-Token"] utils.Debugf("Auth token: %v", tokens) } else { return nil, fmt.Errorf("Index response didn't contain an access token") } if res.Header.Get("X-Docker-Endpoints") != "" { endpoints = res.Header["X-Docker-Endpoints"] } else { return nil, fmt.Errorf("Index response didn't contain any endpoints") } } if validate { if res.StatusCode != 204 { if errBody, err := ioutil.ReadAll(res.Body); err != nil { return nil, err } else { return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody) } } } return &RepositoryData{ Tokens: tokens, Endpoints: endpoints, }, nil }
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error { out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress())) repoData, err := r.GetRepositoryData(remote) if err != nil { return err } utils.Debugf("Updating checksums") // Reload the json file to make sure not to overwrite faster sums if err := srv.runtime.graph.UpdateChecksums(repoData.ImgList); err != nil { return err } utils.Debugf("Retrieving the tag list") tagsList, err := r.GetRemoteTags(repoData.Endpoints, remote, repoData.Tokens) if err != nil { return err } utils.Debugf("Registering tags") // If not specific tag have been asked, take all if askedTag == "" { for tag, id := range tagsList { repoData.ImgList[id].Tag = tag } } else { // Otherwise, check that the tag exists and use only that one id, exists := tagsList[askedTag] if !exists { return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote) } repoData.ImgList[id].Tag = askedTag } for _, img := range repoData.ImgList { if askedTag != "" && img.Tag != askedTag { utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID) continue } out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, remote)) success := false for _, ep := range repoData.Endpoints { if err := srv.pullImage(r, out, img.ID, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil { out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err)) continue } success = true break } if !success { return fmt.Errorf("Could not find repository on any of the indexed registries.") } } for tag, id := range tagsList { if askedTag != "" && tag != askedTag { continue } if err := srv.runtime.repositories.Set(remote, tag, id, true); err != nil { return err } } if err := srv.runtime.repositories.Save(); err != nil { return err } return nil }
func (cli *DockerCli) CmdPush(args ...string) error { cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry") if err := cmd.Parse(args); err != nil { return nil } name := cmd.Arg(0) if name == "" { cmd.Usage() return nil } cli.LoadConfigFile() // Resolve the Repository name from fqn to endpoint + name endpoint, _, err := registry.ResolveRepositoryName(name) if err != nil { return err } // Resolve the Auth config relevant for this server authConfig := cli.configFile.ResolveAuthConfig(endpoint) // If we're not using a custom registry, we know the restrictions // applied to repository names and can warn the user in advance. // Custom repositories can have different rules, and we must also // allow pushing by image ID. if len(strings.SplitN(name, "/", 2)) == 1 { username := cli.configFile.Configs[auth.IndexServerAddress()].Username if username == "" { username = "******" } return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name) } v := url.Values{} push := func(authConfig auth.AuthConfig) error { buf, err := json.Marshal(authConfig) if err != nil { return err } registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{ "X-Registry-Auth": registryAuthHeader, }) } if err := push(authConfig); err != nil { if err.Error() == registry.ErrLoginRequired.Error() { fmt.Fprintln(cli.out, "\nPlease login prior to push:") if err := cli.CmdLogin(endpoint); err != nil { return err } authConfig := cli.configFile.ResolveAuthConfig(endpoint) return push(authConfig) } return err } return nil }
// 'docker login': login / register a user to registry service. func (cli *DockerCli) CmdLogin(args ...string) error { cmd := Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.") var username, password, email string cmd.StringVar(&username, "u", "", "username") cmd.StringVar(&password, "p", "", "password") cmd.StringVar(&email, "e", "", "email") err := cmd.Parse(args) if err != nil { return nil } serverAddress := auth.IndexServerAddress() if len(cmd.Args()) > 0 { serverAddress, err = registry.ExpandAndVerifyRegistryUrl(cmd.Arg(0)) if err != nil { return err } fmt.Fprintf(cli.out, "Login against server at %s\n", serverAddress) } promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } readInput := func(in io.Reader, out io.Writer) string { reader := bufio.NewReader(in) line, _, err := reader.ReadLine() if err != nil { fmt.Fprintln(out, err.Error()) os.Exit(1) } return string(line) } cli.LoadConfigFile() authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()] if !ok { authconfig = auth.AuthConfig{} } if username == "" { promptDefault("Username", authconfig.Username) username = readInput(cli.in, cli.out) if username == "" { username = authconfig.Username } } if username != authconfig.Username { if password == "" { oldState, _ := term.SaveState(cli.terminalFd) fmt.Fprintf(cli.out, "Password: "******"\n") term.RestoreTerminal(cli.terminalFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } } if email == "" { promptDefault("Email", authconfig.Email) email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } } } else { password = authconfig.Password email = authconfig.Email } authconfig.Username = username authconfig.Password = password authconfig.Email = email authconfig.ServerAddress = serverAddress cli.configFile.Configs[serverAddress] = authconfig body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress]) if statusCode == 401 { delete(cli.configFile.Configs, serverAddress) auth.SaveConfig(cli.configFile) return err } if err != nil { return err } var out2 APIAuth err = json.Unmarshal(body, &out2) if err != nil { cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME")) return err } auth.SaveConfig(cli.configFile) if out2.Status != "" { fmt.Fprintf(cli.out, "%s\n", out2.Status) } return nil }
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, remote, askedTag, registryEp string, sf *utils.StreamFormatter) error { out.Write(sf.FormatStatus("Pulling repository %s from %s", local, auth.IndexServerAddress())) var repoData *registry.RepositoryData var err error if registryEp == "" { repoData, err = r.GetRepositoryData(remote) if err != nil { return err } utils.Debugf("Updating checksums") // Reload the json file to make sure not to overwrite faster sums if err := srv.runtime.graph.UpdateChecksums(repoData.ImgList); err != nil { return err } } else { repoData = ®istry.RepositoryData{ Tokens: []string{}, ImgList: make(map[string]*registry.ImgData), Endpoints: []string{registryEp}, } } utils.Debugf("Retrieving the tag list") tagsList, err := r.GetRemoteTags(repoData.Endpoints, remote, repoData.Tokens) if err != nil { utils.Debugf("%v", err) return err } if registryEp != "" { for tag, id := range tagsList { repoData.ImgList[id] = ®istry.ImgData{ ID: id, Tag: tag, Checksum: "", } } } utils.Debugf("Registering tags") // If no tag has been specified, pull them all if askedTag == "" { for tag, id := range tagsList { repoData.ImgList[id].Tag = tag } } else { // Otherwise, check that the tag exists and use only that one id, exists := tagsList[askedTag] if !exists { return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local) } repoData.ImgList[id].Tag = askedTag } for _, img := range repoData.ImgList { if askedTag != "" && img.Tag != askedTag { utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID) continue } out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.ID, img.Tag, remote)) success := false for _, ep := range repoData.Endpoints { if !(strings.HasPrefix(ep, "http://") || strings.HasPrefix(ep, "https://")) { ep = fmt.Sprintf("%s://%s", registry.UrlScheme(), ep) } if err := srv.pullImage(r, out, img.ID, ep+"/v1", repoData.Tokens, sf); err != nil { out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err)) continue } success = true break } if !success { return fmt.Errorf("Could not find repository on any of the indexed registries.") } } for tag, id := range tagsList { if askedTag != "" && tag != askedTag { continue } if err := srv.runtime.repositories.Set(local, tag, id, true); err != nil { return err } } if err := srv.runtime.repositories.Save(); err != nil { return err } return nil }
// 'docker login': login / register a user to registry service. func (cli *DockerCli) CmdLogin(args ...string) error { var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string { char := make([]byte, 1) buffer := make([]byte, 64) var i = 0 for i < len(buffer) { n, err := stdin.Read(char) if n > 0 { if char[0] == '\r' || char[0] == '\n' { stdout.Write([]byte{'\r', '\n'}) break } else if char[0] == 127 || char[0] == '\b' { if i > 0 { if echo { stdout.Write([]byte{'\b', ' ', '\b'}) } i-- } } else if !unicode.IsSpace(rune(char[0])) && !unicode.IsControl(rune(char[0])) { if echo { stdout.Write(char) } buffer[i] = char[0] i++ } } if err != nil { if err != io.EOF { fmt.Fprintf(stdout, "Read error: %v\r\n", err) } break } } return string(buffer[:i]) } var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string { return readStringOnRawTerminal(stdin, stdout, true) } var readString = func(stdin io.Reader, stdout io.Writer) string { return readStringOnRawTerminal(stdin, stdout, false) } cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server") flUsername := cmd.String("u", "", "username") flPassword := cmd.String("p", "", "password") flEmail := cmd.String("e", "", "email") err := cmd.Parse(args) if err != nil { return nil } var oldState *term.State if *flUsername == "" || *flPassword == "" || *flEmail == "" { oldState, err = term.SetRawTerminal(cli.terminalFd) if err != nil { return err } defer term.RestoreTerminal(cli.terminalFd, oldState) } var ( username string password string email string ) var promptDefault = func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()] if !ok { authconfig = auth.AuthConfig{} } if *flUsername == "" { promptDefault("Username", authconfig.Username) username = readAndEchoString(cli.in, cli.out) if username == "" { username = authconfig.Username } } else { username = *flUsername } if username != authconfig.Username { if *flPassword == "" { fmt.Fprintf(cli.out, "Password: "******"" { return fmt.Errorf("Error : Password Required") } } else { password = *flPassword } if *flEmail == "" { promptDefault("Email", authconfig.Email) email = readAndEchoString(cli.in, cli.out) if email == "" { email = authconfig.Email } } else { email = *flEmail } } else { password = authconfig.Password email = authconfig.Email } if oldState != nil { term.RestoreTerminal(cli.terminalFd, oldState) } authconfig.Username = username authconfig.Password = password authconfig.Email = email cli.configFile.Configs[auth.IndexServerAddress()] = authconfig body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[auth.IndexServerAddress()]) if statusCode == 401 { delete(cli.configFile.Configs, auth.IndexServerAddress()) auth.SaveConfig(cli.configFile) return err } if err != nil { return err } var out2 APIAuth err = json.Unmarshal(body, &out2) if err != nil { cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME")) return err } auth.SaveConfig(cli.configFile) if out2.Status != "" { fmt.Fprintf(cli.out, "%s\n", out2.Status) } return nil }