forked from TrueCloudLab/distribution
Merge pull request #213 from stevvooe/docker-upload-uuid
doc/spec, registry/handlers: specify and implement Docker-Upload-UUID
This commit is contained in:
commit
16d8b2c34d
5 changed files with 72 additions and 11 deletions
|
@ -347,6 +347,7 @@ with the upload URL in the `Location` header:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The rest of the upload process can be carried out with the returned url,
|
The rest of the upload process can be carried out with the returned url,
|
||||||
|
@ -358,6 +359,10 @@ try to assemble the it. While the `uuid` parameter may be an actual UUID, this
|
||||||
proposal imposes no constraints on the format and clients should never impose
|
proposal imposes no constraints on the format and clients should never impose
|
||||||
any.
|
any.
|
||||||
|
|
||||||
|
If clients need to correlate local upload state with remote upload state, the
|
||||||
|
contents of the `Docker-Upload-UUID` header should be used. Such an id can be
|
||||||
|
used to key the last used location header when implementing resumable uploads.
|
||||||
|
|
||||||
##### Upload Progress
|
##### Upload Progress
|
||||||
|
|
||||||
The progress and chunk coordination of the upload process will be coordinated
|
The progress and chunk coordination of the upload process will be coordinated
|
||||||
|
@ -383,6 +388,7 @@ The response will be similar to the above, except will return 204 status:
|
||||||
204 No Content
|
204 No Content
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the HTTP `Range` header byte ranges are inclusive and that will be
|
Note that the HTTP `Range` header byte ranges are inclusive and that will be
|
||||||
|
@ -452,6 +458,7 @@ current status:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: 0-<last valid range>
|
Range: 0-<last valid range>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
If this response is received, the client should resume from the "last valid
|
If this response is received, the client should resume from the "last valid
|
||||||
|
@ -470,6 +477,7 @@ be returned, including a `Range` header with the current upload status:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Completed Upload
|
##### Completed Upload
|
||||||
|
@ -1786,6 +1794,7 @@ The following parameters should be specified on the request:
|
||||||
201 Created
|
201 Created
|
||||||
Location: <blob location>
|
Location: <blob location>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The blob has been created in the registry and is available at the provided location.
|
The blob has been created in the registry and is available at the provided location.
|
||||||
|
@ -1796,6 +1805,7 @@ The following headers will be returned with the response:
|
||||||
|----|-----------|
|
|----|-----------|
|
||||||
|`Location`||
|
|`Location`||
|
||||||
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
||||||
|
|`Docker-Upload-UUID`|Identifies the docker upload uuid for the current request.|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1889,6 +1899,7 @@ The following parameters should be specified on the request:
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: 0-0
|
Range: 0-0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The upload has been created. The `Location` header must be used to complete the upload. The response should be identical to a `GET` request on the contents of the returned `Location` header.
|
The upload has been created. The `Location` header must be used to complete the upload. The response should be identical to a `GET` request on the contents of the returned `Location` header.
|
||||||
|
@ -1900,6 +1911,7 @@ The following headers will be returned with the response:
|
||||||
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
||||||
|`Location`|The location of the created upload. Clients should use the contents verbatim to complete the upload, adding parameters where required.|
|
|`Location`|The location of the created upload. Clients should use the contents verbatim to complete the upload, adding parameters where required.|
|
||||||
|`Range`|Range header indicating the progress of the upload. When starting an upload, it will return an empty range, since no content has been received.|
|
|`Range`|Range header indicating the progress of the upload. When starting an upload, it will return an empty range, since no content has been received.|
|
||||||
|
|`Docker-Upload-UUID`|Identifies the docker upload uuid for the current request.|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2003,6 +2015,7 @@ The following parameters should be specified on the request:
|
||||||
204 No Content
|
204 No Content
|
||||||
Range: 0-<offset>
|
Range: 0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The upload is known and in progress. The last received offset is available in the `Range` header.
|
The upload is known and in progress. The last received offset is available in the `Range` header.
|
||||||
|
@ -2013,6 +2026,7 @@ The following headers will be returned with the response:
|
||||||
|----|-----------|
|
|----|-----------|
|
||||||
|`Range`|Range indicating the current progress of the upload.|
|
|`Range`|Range indicating the current progress of the upload.|
|
||||||
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
||||||
|
|`Docker-Upload-UUID`|Identifies the docker upload uuid for the current request.|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2160,6 +2174,7 @@ The following parameters should be specified on the request:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: 0-<offset>
|
Range: 0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The chunk of data has been accepted and the current progress is available in the range header. The updated upload location is available in the `Location` header.
|
The chunk of data has been accepted and the current progress is available in the range header. The updated upload location is available in the `Location` header.
|
||||||
|
@ -2171,6 +2186,7 @@ The following headers will be returned with the response:
|
||||||
|`Location`|The location of the upload. Clients should assume this changes after each request. Clients should use the contents verbatim to complete the upload, adding parameters where required.|
|
|`Location`|The location of the upload. Clients should assume this changes after each request. Clients should use the contents verbatim to complete the upload, adding parameters where required.|
|
||||||
|`Range`|Range indicating the current progress of the upload.|
|
|`Range`|Range indicating the current progress of the upload.|
|
||||||
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
|`Content-Length`|The `Content-Length` header must be zero and the body must be empty.|
|
||||||
|
|`Docker-Upload-UUID`|Identifies the docker upload uuid for the current request.|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -347,6 +347,7 @@ with the upload URL in the `Location` header:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
The rest of the upload process can be carried out with the returned url,
|
The rest of the upload process can be carried out with the returned url,
|
||||||
|
@ -358,6 +359,10 @@ try to assemble the it. While the `uuid` parameter may be an actual UUID, this
|
||||||
proposal imposes no constraints on the format and clients should never impose
|
proposal imposes no constraints on the format and clients should never impose
|
||||||
any.
|
any.
|
||||||
|
|
||||||
|
If clients need to correlate local upload state with remote upload state, the
|
||||||
|
contents of the `Docker-Upload-UUID` header should be used. Such an id can be
|
||||||
|
used to key the last used location header when implementing resumable uploads.
|
||||||
|
|
||||||
##### Upload Progress
|
##### Upload Progress
|
||||||
|
|
||||||
The progress and chunk coordination of the upload process will be coordinated
|
The progress and chunk coordination of the upload process will be coordinated
|
||||||
|
@ -384,6 +389,7 @@ The response will be similar to the above, except will return 204 status:
|
||||||
204 No Content
|
204 No Content
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the HTTP `Range` header byte ranges are inclusive and that will be
|
Note that the HTTP `Range` header byte ranges are inclusive and that will be
|
||||||
|
@ -453,6 +459,7 @@ current status:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: 0-<last valid range>
|
Range: 0-<last valid range>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
If this response is received, the client should resume from the "last valid
|
If this response is received, the client should resume from the "last valid
|
||||||
|
@ -471,6 +478,7 @@ be returned, including a `Range` header with the current upload status:
|
||||||
Location: /v2/<name>/blobs/uploads/<uuid>
|
Location: /v2/<name>/blobs/uploads/<uuid>
|
||||||
Range: bytes=0-<offset>
|
Range: bytes=0-<offset>
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
Docker-Upload-UUID: <uuid>
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Completed Upload
|
##### Completed Upload
|
||||||
|
|
|
@ -72,6 +72,13 @@ var (
|
||||||
Format: "0",
|
Format: "0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dockerUploadUUIDHeader = ParameterDescriptor{
|
||||||
|
Name: "Docker-Upload-UUID",
|
||||||
|
Description: "Identifies the docker upload uuid for the current request.",
|
||||||
|
Type: "uuid",
|
||||||
|
Format: "<uuid>",
|
||||||
|
}
|
||||||
|
|
||||||
unauthorizedResponse = ResponseDescriptor{
|
unauthorizedResponse = ResponseDescriptor{
|
||||||
Description: "The client does not have access to the repository.",
|
Description: "The client does not have access to the repository.",
|
||||||
StatusCode: http.StatusUnauthorized,
|
StatusCode: http.StatusUnauthorized,
|
||||||
|
@ -898,6 +905,7 @@ var routeDescriptors = []RouteDescriptor{
|
||||||
Format: "<blob location>",
|
Format: "<blob location>",
|
||||||
},
|
},
|
||||||
contentLengthZeroHeader,
|
contentLengthZeroHeader,
|
||||||
|
dockerUploadUUIDHeader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -941,6 +949,7 @@ var routeDescriptors = []RouteDescriptor{
|
||||||
Format: "0-0",
|
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.",
|
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.",
|
Description: "Range indicating the current progress of the upload.",
|
||||||
},
|
},
|
||||||
contentLengthZeroHeader,
|
contentLengthZeroHeader,
|
||||||
|
dockerUploadUUIDHeader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1077,6 +1087,7 @@ var routeDescriptors = []RouteDescriptor{
|
||||||
Description: "Range indicating the current progress of the upload.",
|
Description: "Range indicating the current progress of the upload.",
|
||||||
},
|
},
|
||||||
contentLengthZeroHeader,
|
contentLengthZeroHeader,
|
||||||
|
dockerUploadUUIDHeader,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -132,8 +133,20 @@ func TestLayerAPI(t *testing.T) {
|
||||||
checkResponse(t, "checking head on non-existent layer", resp, http.StatusNotFound)
|
checkResponse(t, "checking head on non-existent layer", resp, http.StatusNotFound)
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// Start an upload and cancel
|
// Start an upload, check the status then cancel
|
||||||
uploadURLBase := startPushLayer(t, env.builder, imageName)
|
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)
|
req, err := http.NewRequest("DELETE", uploadURLBase, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -156,7 +169,7 @@ func TestLayerAPI(t *testing.T) {
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Do layer push with an empty body and different digest
|
// 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{}))
|
resp, err = doPushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, bytes.NewReader([]byte{}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error doing bad layer push: %v", err)
|
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)
|
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{}))
|
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)
|
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))
|
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)
|
layerLength, _ := layerFile.Seek(0, os.SEEK_END)
|
||||||
layerFile.Seek(0, os.SEEK_SET)
|
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)
|
pushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, layerFile)
|
||||||
|
|
||||||
// ------------------------
|
// ------------------------
|
||||||
|
@ -319,7 +332,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
expectedLayers[dgst] = rs
|
expectedLayers[dgst] = rs
|
||||||
unsignedManifest.FSLayers[i].BlobSum = dgst
|
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)
|
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
|
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)
|
layerUploadURL, err := ub.BuildBlobUploadURL(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error building layer upload url: %v", err)
|
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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
checkResponse(t, fmt.Sprintf("pushing starting layer push %v", name), resp, http.StatusAccepted)
|
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{
|
checkHeaders(t, resp, http.Header{
|
||||||
"Location": []string{"*"},
|
"Location": []string{"*"},
|
||||||
"Content-Length": []string{"0"},
|
"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
|
// 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)
|
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Docker-Upload-UUID", luh.Upload.UUID())
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +157,7 @@ func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Docker-Upload-UUID", luh.UUID)
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +238,7 @@ func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Docker-Upload-UUID", luh.UUID)
|
||||||
if err := luh.Upload.Cancel(); err != nil {
|
if err := luh.Upload.Cancel(); err != nil {
|
||||||
ctxu.GetLogger(luh).Errorf("error encountered canceling upload: %v", err)
|
ctxu.GetLogger(luh).Errorf("error encountered canceling upload: %v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -277,6 +281,7 @@ func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *htt
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Docker-Upload-UUID", luh.UUID)
|
||||||
w.Header().Set("Location", uploadURL)
|
w.Header().Set("Location", uploadURL)
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
w.Header().Set("Range", fmt.Sprintf("0-%d", luh.State.Offset))
|
w.Header().Set("Range", fmt.Sprintf("0-%d", luh.State.Offset))
|
||||||
|
|
Loading…
Reference in a new issue