Преглед изворни кода

Separate init blob upload

Pushing a v2 image layer has two steps:

- POST to get a new upload URL
- PUT to that upload URL

We were previously not checking the response code of
the POST request and the PUT would fail in weird ways.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
Josh Hawn пре 10 година
родитељ
комит
73b33db04b
1 измењених фајлова са 49 додато и 18 уклоњено
  1. 49 18
      registry/session_v2.go

+ 49 - 18
registry/session_v2.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net/http"
 	"strconv"
 
 	log "github.com/Sirupsen/logrus"
@@ -209,29 +210,14 @@ func (r *Session) GetV2ImageBlobReader(ep *Endpoint, imageName, sumType, sum str
 // 'layer' is an uncompressed reader of the blob to be pushed.
 // The server will generate it's own checksum calculation.
 func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName, sumType, sumStr string, blobRdr io.Reader, auth *RequestAuthorization) error {
-	routeURL, err := getV2Builder(ep).BuildBlobUploadURL(imageName)
-	if err != nil {
-		return err
-	}
-
-	log.Debugf("[registry] Calling %q %s", "POST", routeURL)
-	req, err := r.reqFactory.NewRequest("POST", routeURL, nil)
-	if err != nil {
-		return err
-	}
-
-	if err := auth.Authorize(req); err != nil {
-		return err
-	}
-	res, _, err := r.doRequest(req)
+	location, err := r.initiateBlobUpload(ep, imageName, auth)
 	if err != nil {
 		return err
 	}
-	location := res.Header.Get("Location")
 
 	method := "PUT"
 	log.Debugf("[registry] Calling %q %s", method, location)
-	req, err = r.reqFactory.NewRequest(method, location, ioutil.NopCloser(blobRdr))
+	req, err := r.reqFactory.NewRequest(method, location, ioutil.NopCloser(blobRdr))
 	if err != nil {
 		return err
 	}
@@ -241,7 +227,7 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName, sumType, sumStr string
 	if err := auth.Authorize(req); err != nil {
 		return err
 	}
-	res, _, err = r.doRequest(req)
+	res, _, err := r.doRequest(req)
 	if err != nil {
 		return err
 	}
@@ -262,6 +248,51 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName, sumType, sumStr string
 	return nil
 }
 
+// initiateBlobUpload gets the blob upload location for the given image name.
+func (r *Session) initiateBlobUpload(ep *Endpoint, imageName string, auth *RequestAuthorization) (location string, err error) {
+	routeURL, err := getV2Builder(ep).BuildBlobUploadURL(imageName)
+	if err != nil {
+		return "", err
+	}
+
+	log.Debugf("[registry] Calling %q %s", "POST", routeURL)
+	req, err := r.reqFactory.NewRequest("POST", routeURL, nil)
+	if err != nil {
+		return "", err
+	}
+
+	if err := auth.Authorize(req); err != nil {
+		return "", err
+	}
+	res, _, err := r.doRequest(req)
+	if err != nil {
+		return "", err
+	}
+
+	if res.StatusCode != http.StatusAccepted {
+		if res.StatusCode == http.StatusUnauthorized {
+			return "", errLoginRequired
+		}
+		if res.StatusCode == http.StatusNotFound {
+			return "", ErrDoesNotExist
+		}
+
+		errBody, err := ioutil.ReadAll(res.Body)
+		if err != nil {
+			return "", err
+		}
+
+		log.Debugf("Unexpected response from server: %q %#v", errBody, res.Header)
+		return "", utils.NewHTTPRequestError(fmt.Sprintf("Server error: unexpected %d response status trying to initiate upload of %s", res.StatusCode, imageName), res)
+	}
+
+	if location = res.Header.Get("Location"); location == "" {
+		return "", fmt.Errorf("registry did not return a Location header for resumable blob upload for image %s", imageName)
+	}
+
+	return
+}
+
 // Finally Push the (signed) manifest of the blobs we've just pushed
 func (r *Session) PutV2ImageManifest(ep *Endpoint, imageName, tagName string, manifestRdr io.Reader, auth *RequestAuthorization) (string, error) {
 	routeURL, err := getV2Builder(ep).BuildManifestURL(imageName, tagName)