Skip to content

Commit

Permalink
registry: Info collection
Browse files Browse the repository at this point in the history
roll version and standalone information into the _ping. And to support
Headers they are checked after the JSON is loaded (if there is anything
to load). To stay backwards compatible, if the _ping contents are not
able to unmarshal to RegistryInfo, do not stop, but continue with the
same behavior.

Docker-DCO-1.1-Signed-off-by: Vincent Batts <[email protected]> (github: vbatts)
  • Loading branch information
vbatts committed Mar 12, 2014
1 parent 2a2c694 commit 2b855af
Showing 1 changed file with 40 additions and 38 deletions.
78 changes: 40 additions & 38 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ var (
errLoginRequired = errors.New("Authentication is required.")
)

// reuse this chunk of code
func newClient() *http.Client {
func pingRegistryEndpoint(endpoint string) (RegistryInfo, error) {
if endpoint == IndexServerAddress() {
// Skip the check, we now this one is valid
// (and we never want to fallback to http in case of error)
return RegistryInfo{Standalone: 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)
Expand All @@ -38,51 +42,44 @@ func newClient() *http.Client {
return conn, nil
}
httpTransport := &http.Transport{Dial: httpDial}
return &http.Client{Transport: httpTransport}
}

// Have an API to access the version of the registry
func getRegistryVersion(endpoint string) (string, error) {

client := newClient()
resp, err := client.Get(endpoint + "_version")
client := &http.Client{Transport: httpTransport}
resp, err := client.Get(endpoint + "_ping")
if err != nil {
return "", err
return RegistryInfo{Standalone: false}, err
}
defer resp.Body.Close()

if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
return hdr, nil
jsonString, err := ioutil.ReadAll(resp.Body)
if err != nil {
return RegistryInfo{Standalone: false}, fmt.Errorf("Error while reading the http response: %s", err)
}
versionBody, err := ioutil.ReadAll(resp.Body)
return string(versionBody), err
}

func pingRegistryEndpoint(endpoint string) (bool, error) {
if endpoint == 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
// If the header is absent, we assume true for compatibility with earlier
// versions of the registry. default to true
info := RegistryInfo{
Standalone: true,
}
client := newClient()
resp, err := client.Get(endpoint + "_ping")
if err != nil {
return false, err
if err := json.Unmarshal(jsonString, &info); err != nil {
utils.Debugf("Error unmarshalling the _ping RegistryInfo: %s", err)
// don't stop here. Just assume sane defaults
}
defer resp.Body.Close()
if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
utils.Debugf("Registry version header: '%s'", hdr)
info.Version = hdr
}
utils.Debugf("RegistryInfo.Version: %q", info.Version)

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
// Accepted values are "true" (case-insensitive) and "1".
if strings.EqualFold(standalone, "true") || standalone == "1" {
info.Standalone = true
} else if len(standalone) > 0 {
// there is a header set, and it is not "true" or "1", so assume fails
info.Standalone = false
}
utils.Debugf("RegistryInfo.Standalone: %q", info.Standalone)
return info, nil
}

func validateRepositoryName(repositoryName string) error {
Expand Down Expand Up @@ -688,6 +685,11 @@ type ImgData struct {
Tag string `json:",omitempty"`
}

type RegistryInfo struct {
Version string `json:"version"`
Standalone bool `json:"standalone"`
}

type Registry struct {
client *http.Client
authConfig *AuthConfig
Expand Down Expand Up @@ -716,11 +718,11 @@ func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, inde
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
// alongside our requests.
if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
standalone, err := pingRegistryEndpoint(indexEndpoint)
info, err := pingRegistryEndpoint(indexEndpoint)
if err != nil {
return nil, err
}
if standalone {
if info.Standalone {
utils.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
factory.AddDecorator(dec)
Expand Down

0 comments on commit 2b855af

Please sign in to comment.