Merge pull request #301 from runcom/perf-gain

vendor c/image@c1893ff40c
This commit is contained in:
Antonio Murdaca 2017-02-02 17:41:13 +01:00 committed by GitHub
commit c011e81b38
2 changed files with 54 additions and 38 deletions

View File

@ -147,6 +147,10 @@ func (d *daemonImageDestination) AcceptsForeignLayerURLs() bool {
// to any other readers for download using the supplied digest. // to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far. // If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
func (d *daemonImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) { func (d *daemonImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
if inputInfo.Digest.String() == "" {
return types.BlobInfo{}, errors.Errorf(`Can not stream a blob with unknown digest to "docker-daemon:"`)
}
if ok, size, err := d.HasBlob(inputInfo); err == nil && ok { if ok, size, err := d.HasBlob(inputInfo); err == nil && ok {
return types.BlobInfo{Digest: inputInfo.Digest, Size: size}, nil return types.BlobInfo{Digest: inputInfo.Digest, Size: size}, nil
} }

View File

@ -37,12 +37,20 @@ const (
manifestURL = "%s/manifests/%s" manifestURL = "%s/manifests/%s"
blobsURL = "%s/blobs/%s" blobsURL = "%s/blobs/%s"
blobUploadURL = "%s/blobs/uploads/" blobUploadURL = "%s/blobs/uploads/"
minimumTokenLifetimeSeconds = 60
) )
// ErrV1NotSupported is returned when we're trying to talk to a // ErrV1NotSupported is returned when we're trying to talk to a
// docker V1 registry. // docker V1 registry.
var ErrV1NotSupported = errors.New("can't talk to a V1 docker registry") var ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
type bearerToken struct {
Token string `json:"token"`
ExpiresIn int `json:"expires_in"`
IssuedAt time.Time `json:"issued_at"`
}
// dockerClient is configuration for dealing with a single Docker registry. // dockerClient is configuration for dealing with a single Docker registry.
type dockerClient struct { type dockerClient struct {
ctx *types.SystemContext ctx *types.SystemContext
@ -54,6 +62,8 @@ type dockerClient struct {
signatureBase signatureStorageBase signatureBase signatureStorageBase
challenges []challenge challenges []challenge
scope authScope scope authScope
token *bearerToken
tokenExpiration time.Time
} }
type authScope struct { type authScope struct {
@ -262,6 +272,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
req.SetBasicAuth(c.username, c.password) req.SetBasicAuth(c.username, c.password)
return nil return nil
case "bearer": case "bearer":
if c.token == nil || time.Now().After(c.tokenExpiration) {
realm, ok := challenge.Parameters["realm"] realm, ok := challenge.Parameters["realm"]
if !ok { if !ok {
return errors.Errorf("missing realm in bearer auth challenge") return errors.Errorf("missing realm in bearer auth challenge")
@ -272,16 +283,19 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
if err != nil { if err != nil {
return err return err
} }
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) c.token = token
c.tokenExpiration = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token.Token))
return nil return nil
} }
return errors.Errorf("no handler for %s authentication", challenge.Scheme) return errors.Errorf("no handler for %s authentication", challenge.Scheme)
} }
func (c *dockerClient) getBearerToken(realm, service, scope string) (string, error) { func (c *dockerClient) getBearerToken(realm, service, scope string) (*bearerToken, error) {
authReq, err := http.NewRequest("GET", realm, nil) authReq, err := http.NewRequest("GET", realm, nil)
if err != nil { if err != nil {
return "", err return nil, err
} }
getParams := authReq.URL.Query() getParams := authReq.URL.Query()
if service != "" { if service != "" {
@ -300,35 +314,33 @@ func (c *dockerClient) getBearerToken(realm, service, scope string) (string, err
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
res, err := client.Do(authReq) res, err := client.Do(authReq)
if err != nil { if err != nil {
return "", err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
switch res.StatusCode { switch res.StatusCode {
case http.StatusUnauthorized: case http.StatusUnauthorized:
return "", errors.Errorf("unable to retrieve auth token: 401 unauthorized") return nil, errors.Errorf("unable to retrieve auth token: 401 unauthorized")
case http.StatusOK: case http.StatusOK:
break break
default: default:
return "", errors.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL) return nil, errors.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL)
} }
tokenBlob, err := ioutil.ReadAll(res.Body) tokenBlob, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return "", err return nil, err
} }
tokenStruct := struct { var token bearerToken
Token string `json:"token"` if err := json.Unmarshal(tokenBlob, &token); err != nil {
}{} return nil, err
if err := json.Unmarshal(tokenBlob, &tokenStruct); err != nil {
return "", err
} }
// TODO(runcom): reuse tokens? if token.ExpiresIn < minimumTokenLifetimeSeconds {
//hostAuthTokens, ok = rb.hostsV2AuthTokens[req.URL.Host] token.ExpiresIn = minimumTokenLifetimeSeconds
//if !ok { logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
//hostAuthTokens = make(map[string]string) }
//rb.hostsV2AuthTokens[req.URL.Host] = hostAuthTokens if token.IssuedAt.IsZero() {
//} token.IssuedAt = time.Now().UTC()
//hostAuthTokens[repo] = tokenStruct.Token }
return tokenStruct.Token, nil return &token, nil
} }
func getAuth(ctx *types.SystemContext, registry string) (string, string, error) { func getAuth(ctx *types.SystemContext, registry string) (string, string, error) {