Merge pull request #213 from stevvooe/docker-upload-uuid
doc/spec, registry/handlers: specify and implement Docker-Upload-UUIDmaster
						commit
						b1c8952c1a
					
				| 
						 | 
				
			
			@ -72,6 +72,13 @@ var (
 | 
			
		|||
		Format:      "0",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dockerUploadUUIDHeader = ParameterDescriptor{
 | 
			
		||||
		Name:        "Docker-Upload-UUID",
 | 
			
		||||
		Description: "Identifies the docker upload uuid for the current request.",
 | 
			
		||||
		Type:        "uuid",
 | 
			
		||||
		Format:      "<uuid>",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unauthorizedResponse = ResponseDescriptor{
 | 
			
		||||
		Description: "The client does not have access to the repository.",
 | 
			
		||||
		StatusCode:  http.StatusUnauthorized,
 | 
			
		||||
| 
						 | 
				
			
			@ -898,6 +905,7 @@ var routeDescriptors = []RouteDescriptor{
 | 
			
		|||
										Format: "<blob location>",
 | 
			
		||||
									},
 | 
			
		||||
									contentLengthZeroHeader,
 | 
			
		||||
									dockerUploadUUIDHeader,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
| 
						 | 
				
			
			@ -941,6 +949,7 @@ var routeDescriptors = []RouteDescriptor{
 | 
			
		|||
										Format:      "0-0",
 | 
			
		||||
										Description: "Range header indicating the progress of the upload. When starting an upload, it will return an empty range, since no content has been received.",
 | 
			
		||||
									},
 | 
			
		||||
									dockerUploadUUIDHeader,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
| 
						 | 
				
			
			@ -994,6 +1003,7 @@ var routeDescriptors = []RouteDescriptor{
 | 
			
		|||
										Description: "Range indicating the current progress of the upload.",
 | 
			
		||||
									},
 | 
			
		||||
									contentLengthZeroHeader,
 | 
			
		||||
									dockerUploadUUIDHeader,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
| 
						 | 
				
			
			@ -1077,6 +1087,7 @@ var routeDescriptors = []RouteDescriptor{
 | 
			
		|||
										Description: "Range indicating the current progress of the upload.",
 | 
			
		||||
									},
 | 
			
		||||
									contentLengthZeroHeader,
 | 
			
		||||
									dockerUploadUUIDHeader,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import (
 | 
			
		|||
	"net/http/httputil"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
| 
						 | 
				
			
			@ -132,8 +133,20 @@ func TestLayerAPI(t *testing.T) {
 | 
			
		|||
	checkResponse(t, "checking head on non-existent layer", resp, http.StatusNotFound)
 | 
			
		||||
 | 
			
		||||
	// ------------------------------------------
 | 
			
		||||
	// Start an upload and cancel
 | 
			
		||||
	uploadURLBase := startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	// Start an upload, check the status then cancel
 | 
			
		||||
	uploadURLBase, uploadUUID := startPushLayer(t, env.builder, imageName)
 | 
			
		||||
 | 
			
		||||
	// A status check should work
 | 
			
		||||
	resp, err = http.Get(uploadURLBase)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error getting upload status: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	checkResponse(t, "status of deleted upload", resp, http.StatusNoContent)
 | 
			
		||||
	checkHeaders(t, resp, http.Header{
 | 
			
		||||
		"Location":           []string{"*"},
 | 
			
		||||
		"Range":              []string{"0-0"},
 | 
			
		||||
		"Docker-Upload-UUID": []string{uploadUUID},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("DELETE", uploadURLBase, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +169,7 @@ func TestLayerAPI(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	// -----------------------------------------
 | 
			
		||||
	// Do layer push with an empty body and different digest
 | 
			
		||||
	uploadURLBase = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	uploadURLBase, uploadUUID = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	resp, err = doPushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, bytes.NewReader([]byte{}))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error doing bad layer push: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +185,7 @@ func TestLayerAPI(t *testing.T) {
 | 
			
		|||
		t.Fatalf("unexpected error digesting empty buffer: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uploadURLBase = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	uploadURLBase, uploadUUID = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	pushLayer(t, env.builder, imageName, zeroDigest, uploadURLBase, bytes.NewReader([]byte{}))
 | 
			
		||||
 | 
			
		||||
	// -----------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +198,7 @@ func TestLayerAPI(t *testing.T) {
 | 
			
		|||
		t.Fatalf("unexpected error digesting empty tar: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uploadURLBase = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	uploadURLBase, uploadUUID = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	pushLayer(t, env.builder, imageName, emptyDigest, uploadURLBase, bytes.NewReader(emptyTar))
 | 
			
		||||
 | 
			
		||||
	// ------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +206,7 @@ func TestLayerAPI(t *testing.T) {
 | 
			
		|||
	layerLength, _ := layerFile.Seek(0, os.SEEK_END)
 | 
			
		||||
	layerFile.Seek(0, os.SEEK_SET)
 | 
			
		||||
 | 
			
		||||
	uploadURLBase = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	uploadURLBase, uploadUUID = startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	pushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, layerFile)
 | 
			
		||||
 | 
			
		||||
	// ------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +332,7 @@ func TestManifestAPI(t *testing.T) {
 | 
			
		|||
		expectedLayers[dgst] = rs
 | 
			
		||||
		unsignedManifest.FSLayers[i].BlobSum = dgst
 | 
			
		||||
 | 
			
		||||
		uploadURLBase := startPushLayer(t, env.builder, imageName)
 | 
			
		||||
		uploadURLBase, _ := startPushLayer(t, env.builder, imageName)
 | 
			
		||||
		pushLayer(t, env.builder, imageName, dgst, uploadURLBase, rs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -451,7 +464,7 @@ func putManifest(t *testing.T, msg, url string, v interface{}) *http.Response {
 | 
			
		|||
	return resp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) string {
 | 
			
		||||
func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) (location string, uuid string) {
 | 
			
		||||
	layerUploadURL, err := ub.BuildBlobUploadURL(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error building layer upload url: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -464,12 +477,20 @@ func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) string {
 | 
			
		|||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	checkResponse(t, fmt.Sprintf("pushing starting layer push %v", name), resp, http.StatusAccepted)
 | 
			
		||||
 | 
			
		||||
	u, err := url.Parse(resp.Header.Get("Location"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error parsing location header: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uuid = path.Base(u.Path)
 | 
			
		||||
	checkHeaders(t, resp, http.Header{
 | 
			
		||||
		"Location":       []string{"*"},
 | 
			
		||||
		"Content-Length": []string{"0"},
 | 
			
		||||
		"Location":           []string{"*"},
 | 
			
		||||
		"Content-Length":     []string{"0"},
 | 
			
		||||
		"Docker-Upload-UUID": []string{uuid},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return resp.Header.Get("Location")
 | 
			
		||||
	return resp.Header.Get("Location"), uuid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// doPushLayer pushes the layer content returning the url on success returning
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,6 +138,8 @@ func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.R
 | 
			
		|||
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Docker-Upload-UUID", luh.Upload.UUID())
 | 
			
		||||
	w.WriteHeader(http.StatusAccepted)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -155,6 +157,7 @@ func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Re
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Docker-Upload-UUID", luh.UUID)
 | 
			
		||||
	w.WriteHeader(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -235,6 +238,7 @@ func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Docker-Upload-UUID", luh.UUID)
 | 
			
		||||
	if err := luh.Upload.Cancel(); err != nil {
 | 
			
		||||
		ctxu.GetLogger(luh).Errorf("error encountered canceling upload: %v", err)
 | 
			
		||||
		w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
| 
						 | 
				
			
			@ -277,6 +281,7 @@ func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *htt
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Set("Docker-Upload-UUID", luh.UUID)
 | 
			
		||||
	w.Header().Set("Location", uploadURL)
 | 
			
		||||
	w.Header().Set("Content-Length", "0")
 | 
			
		||||
	w.Header().Set("Range", fmt.Sprintf("0-%d", luh.State.Offset))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue