Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions graph/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out

endpoint, err := r.V2RegistryEndpoint(repoInfo.Index)
if err != nil {
if err == registry.ErrEndpointNotFound {
return err
}
return fmt.Errorf("error getting registry endpoint: %s", err)
}
auth, err := r.GetV2Authorization(endpoint, repoInfo.RemoteName, false)
Expand Down Expand Up @@ -378,6 +381,12 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
job.GetenvJson("authConfig", authConfig)
job.GetenvJson("metaHeaders", &metaHeaders)

// Set a header so remotes can identify the command being carried out. If
// present, the remote may act on the field but this is mostly advisory.
metaHeaders["Docker-Command"] = []string{"push"}

repoInfo.Index.Headers = metaHeaders
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtle.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dirty. I really don't like it but doing it right will require (imo) the refactor we discussed, having the endpoint be the top level value from which sessions and factories are derived.


if _, err := s.poolAdd("push", repoInfo.LocalName); err != nil {
return job.Error(err)
}
Expand All @@ -403,9 +412,12 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
if err == nil {
return engine.StatusOK
}

// error out, no fallback to V1
return job.Error(err)
if err != registry.ErrEndpointNotFound {
// error out, no fallback to V1
return job.Error(err)
} else {
log.Debugf("Skipping V2 registry: endpoint not available")
}
}

if err != nil {
Expand Down
24 changes: 20 additions & 4 deletions registry/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package registry

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
Expand All @@ -11,11 +12,14 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/docker/registry/v2"
"github.com/docker/docker/utils"
)

// for mocking in unit tests
var lookupIP = net.LookupIP

var ErrEndpointNotFound = errors.New("endpoint not found")

// scans string for api version in the URL path. returns the trimmed address, if version found, string and API version.
func scanForAPIVersion(address string) (string, APIVersion) {
var (
Expand Down Expand Up @@ -43,7 +47,7 @@ func scanForAPIVersion(address string) (string, APIVersion) {
// NewEndpoint parses the given address to return a registry endpoint.
func NewEndpoint(index *IndexInfo) (*Endpoint, error) {
// *TODO: Allow per-registry configuration of endpoints.
endpoint, err := newEndpoint(index.GetAuthConfigKey(), index.Secure)
endpoint, err := newEndpoint(index.GetAuthConfigKey(), index.Secure, index.Headers)
if err != nil {
return nil, err
}
Expand All @@ -60,6 +64,9 @@ func validateEndpoint(endpoint *Endpoint) error {
// Try HTTPS ping to registry
endpoint.URL.Scheme = "https"
if _, err := endpoint.Ping(); err != nil {
if err == ErrEndpointNotFound {
return err
}
if endpoint.IsSecure {
// If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry`
// in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP.
Expand All @@ -74,14 +81,17 @@ func validateEndpoint(endpoint *Endpoint) error {
if _, err2 = endpoint.Ping(); err2 == nil {
return nil
}
if err2 == ErrEndpointNotFound {
return err2
}

return fmt.Errorf("invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2)
}

return nil
}

func newEndpoint(address string, secure bool) (*Endpoint, error) {
func newEndpoint(address string, secure bool, headers map[string][]string) (*Endpoint, error) {
var (
endpoint = new(Endpoint)
trimmedAddress string
Expand All @@ -98,6 +108,7 @@ func newEndpoint(address string, secure bool) (*Endpoint, error) {
return nil, err
}
endpoint.IsSecure = secure
endpoint.ReqFactory = HTTPRequestFactory(headers)
return endpoint, nil
}

Expand All @@ -112,6 +123,7 @@ type Endpoint struct {
IsSecure bool
AuthChallenges []*AuthorizationChallenge
URLBuilder *v2.URLBuilder
ReqFactory *utils.HTTPRequestFactory
}

// Get the formated URL for the root of this registry Endpoint
Expand Down Expand Up @@ -168,7 +180,7 @@ func (e *Endpoint) pingV1() (RegistryInfo, error) {
return RegistryInfo{Standalone: false}, nil
}

req, err := http.NewRequest("GET", e.Path("_ping"), nil)
req, err := e.ReqFactory.NewRequest("GET", e.Path("_ping"), nil)
if err != nil {
return RegistryInfo{Standalone: false}, err
}
Expand Down Expand Up @@ -216,7 +228,7 @@ func (e *Endpoint) pingV1() (RegistryInfo, error) {
func (e *Endpoint) pingV2() (RegistryInfo, error) {
log.Debugf("attempting v2 ping for registry endpoint %s", e)

req, err := http.NewRequest("GET", e.Path(""), nil)
req, err := e.ReqFactory.NewRequest("GET", e.Path(""), nil)
if err != nil {
return RegistryInfo{}, err
}
Expand All @@ -240,5 +252,9 @@ func (e *Endpoint) pingV2() (RegistryInfo, error) {
return RegistryInfo{}, nil
}

if resp.StatusCode == http.StatusNotFound {
return RegistryInfo{}, ErrEndpointNotFound
}

return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode))
}
8 changes: 7 additions & 1 deletion registry/session_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func getV2Builder(e *Endpoint) *v2.URLBuilder {
func (r *Session) V2RegistryEndpoint(index *IndexInfo) (ep *Endpoint, err error) {
// TODO check if should use Mirror
if index.Official {
ep, err = newEndpoint(REGISTRYSERVER, true)
ep, err = newEndpoint(REGISTRYSERVER, index.Secure, index.Headers)
if err != nil {
return
}
Expand Down Expand Up @@ -222,6 +222,12 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName, sumType, sumStr string
if err != nil {
return err
}
if res.StatusCode != 202 {
if res.StatusCode == 401 {
return errLoginRequired
}
return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s blob", res.StatusCode, imageName), res)
}
location := res.Header.Get("Location")

method := "PUT"
Expand Down
1 change: 1 addition & 0 deletions registry/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ type IndexInfo struct {
Mirrors []string
Secure bool
Official bool
Headers map[string][]string
}

type RepositoryInfo struct {
Expand Down