forked from TrueCloudLab/distribution
Merge pull request #4607 from vbatts/vbatts-static_registry
static registry support
This commit is contained in:
commit
c914523873
2 changed files with 47 additions and 22 deletions
|
@ -25,11 +25,11 @@ var (
|
||||||
errLoginRequired = errors.New("Authentication is required.")
|
errLoginRequired = errors.New("Authentication is required.")
|
||||||
)
|
)
|
||||||
|
|
||||||
func pingRegistryEndpoint(endpoint string) (bool, error) {
|
func pingRegistryEndpoint(endpoint string) (RegistryInfo, error) {
|
||||||
if endpoint == IndexServerAddress() {
|
if endpoint == IndexServerAddress() {
|
||||||
// Skip the check, we now this one is valid
|
// Skip the check, we now this one is valid
|
||||||
// (and we never want to fallback to http in case of error)
|
// (and we never want to fallback to http in case of error)
|
||||||
return false, nil
|
return RegistryInfo{Standalone: false}, nil
|
||||||
}
|
}
|
||||||
httpDial := func(proto string, addr string) (net.Conn, error) {
|
httpDial := func(proto string, addr string) (net.Conn, error) {
|
||||||
// Set the connect timeout to 5 seconds
|
// Set the connect timeout to 5 seconds
|
||||||
|
@ -48,26 +48,41 @@ func pingRegistryEndpoint(endpoint string) (bool, error) {
|
||||||
client := &http.Client{Transport: httpTransport}
|
client := &http.Client{Transport: httpTransport}
|
||||||
resp, err := client.Get(endpoint + "_ping")
|
resp, err := client.Get(endpoint + "_ping")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return RegistryInfo{Standalone: false}, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.Header.Get("X-Docker-Registry-Version") == "" {
|
jsonString, err := ioutil.ReadAll(resp.Body)
|
||||||
return false, errors.New("This does not look like a Registry server (\"X-Docker-Registry-Version\" header not found in the response)")
|
if err != nil {
|
||||||
|
return RegistryInfo{Standalone: false}, fmt.Errorf("Error while reading the http response: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the header is absent, we assume true for compatibility with earlier
|
||||||
|
// versions of the registry. default to true
|
||||||
|
info := RegistryInfo{
|
||||||
|
Standalone: true,
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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")
|
standalone := resp.Header.Get("X-Docker-Registry-Standalone")
|
||||||
utils.Debugf("Registry standalone header: '%s'", 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".
|
// Accepted values are "true" (case-insensitive) and "1".
|
||||||
} else if strings.EqualFold(standalone, "true") || standalone == "1" {
|
if strings.EqualFold(standalone, "true") || standalone == "1" {
|
||||||
return true, nil
|
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
|
||||||
}
|
}
|
||||||
// Otherwise, not standalone
|
utils.Debugf("RegistryInfo.Standalone: %q", info.Standalone)
|
||||||
return false, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateRepositoryName(repositoryName string) error {
|
func validateRepositoryName(repositoryName string) error {
|
||||||
|
@ -221,10 +236,14 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([
|
||||||
return nil, -1, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
return nil, -1, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
imageSize, err := strconv.Atoi(res.Header.Get("X-Docker-Size"))
|
// if the size header is not present, then set it to '-1'
|
||||||
|
imageSize := -1
|
||||||
|
if hdr := res.Header.Get("X-Docker-Size"); hdr != "" {
|
||||||
|
imageSize, err = strconv.Atoi(hdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jsonString, err := ioutil.ReadAll(res.Body)
|
jsonString, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -352,7 +371,8 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Index response didn't contain any endpoints")
|
// Assume the endpoint is on the same host
|
||||||
|
endpoints = append(endpoints, fmt.Sprintf("%s://%s/v1/", urlScheme, req.URL.Host))
|
||||||
}
|
}
|
||||||
|
|
||||||
checksumsJSON, err := ioutil.ReadAll(res.Body)
|
checksumsJSON, err := ioutil.ReadAll(res.Body)
|
||||||
|
@ -685,6 +705,11 @@ type ImgData struct {
|
||||||
Tag string `json:",omitempty"`
|
Tag string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegistryInfo struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Standalone bool `json:"standalone"`
|
||||||
|
}
|
||||||
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
authConfig *AuthConfig
|
authConfig *AuthConfig
|
||||||
|
@ -713,11 +738,11 @@ func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, inde
|
||||||
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
|
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
|
||||||
// alongside our requests.
|
// alongside our requests.
|
||||||
if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
|
if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
|
||||||
standalone, err := pingRegistryEndpoint(indexEndpoint)
|
info, err := pingRegistryEndpoint(indexEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if standalone {
|
if info.Standalone {
|
||||||
utils.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
|
utils.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
|
||||||
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
|
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
|
||||||
factory.AddDecorator(dec)
|
factory.AddDecorator(dec)
|
||||||
|
|
|
@ -24,11 +24,11 @@ func spawnTestRegistry(t *testing.T) *Registry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPingRegistryEndpoint(t *testing.T) {
|
func TestPingRegistryEndpoint(t *testing.T) {
|
||||||
standalone, err := pingRegistryEndpoint(makeURL("/v1/"))
|
regInfo, err := pingRegistryEndpoint(makeURL("/v1/"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assertEqual(t, standalone, true, "Expected standalone to be true (default)")
|
assertEqual(t, regInfo.Standalone, true, "Expected standalone to be true (default)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRemoteHistory(t *testing.T) {
|
func TestGetRemoteHistory(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue