// 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 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 standalone private registry over HTTPS, send Basic Auth headers // alongside our requests. if indexEndpoint != auth.IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") { standalone, err := pingRegistryEndpoint(indexEndpoint) if err != nil { return nil, err } if standalone { 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 (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 TestResolveRepositoryName(t *testing.T) { _, _, err := ResolveRepositoryName("https://github.com/Nerdness/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 }