forked from TrueCloudLab/distribution
Move registry package out of repo root
Since the repo is no longer just the registry, we are moving the registry web application package out of the repo root into a sub-package. We may break down the registry package further to separate webapp components and bring the client package under it. This change accomplishes the task of freeing up the repo root for a distribution-oriented package. A stub doc.go file is left in place to declare intent. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
fdea60af05
commit
ab4a9f0480
14 changed files with 8 additions and 1 deletions
245
registry/layerupload.go
Normal file
245
registry/layerupload.go
Normal file
|
@ -0,0 +1,245 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/api/v2"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/storage"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
// layerUploadDispatcher constructs and returns the layer upload handler for
|
||||
// the given request context.
|
||||
func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||
luh := &layerUploadHandler{
|
||||
Context: ctx,
|
||||
UUID: ctx.vars["uuid"],
|
||||
}
|
||||
|
||||
handler := http.Handler(handlers.MethodHandler{
|
||||
"POST": http.HandlerFunc(luh.StartLayerUpload),
|
||||
"GET": http.HandlerFunc(luh.GetUploadStatus),
|
||||
"HEAD": http.HandlerFunc(luh.GetUploadStatus),
|
||||
"PUT": http.HandlerFunc(luh.PutLayerChunk),
|
||||
"DELETE": http.HandlerFunc(luh.CancelLayerUpload),
|
||||
})
|
||||
|
||||
if luh.UUID != "" {
|
||||
luh.log = luh.log.WithField("uuid", luh.UUID)
|
||||
|
||||
state, err := ctx.tokenProvider.layerUploadStateFromToken(r.FormValue("_state"))
|
||||
if err != nil {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.Infof("error resolving upload: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
})
|
||||
}
|
||||
|
||||
layers := ctx.services.Layers()
|
||||
upload, err := layers.Resume(state)
|
||||
if err != nil && err != storage.ErrLayerUploadUnknown {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.Infof("error resolving upload: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
})
|
||||
}
|
||||
|
||||
luh.Upload = upload
|
||||
handler = closeResources(handler, luh.Upload)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// layerUploadHandler handles the http layer upload process.
|
||||
type layerUploadHandler struct {
|
||||
*Context
|
||||
|
||||
// UUID identifies the upload instance for the current request.
|
||||
UUID string
|
||||
|
||||
Upload storage.LayerUpload
|
||||
}
|
||||
|
||||
// StartLayerUpload begins the layer upload process and allocates a server-
|
||||
// side upload session.
|
||||
func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) {
|
||||
layers := luh.services.Layers()
|
||||
upload, err := layers.Upload(luh.Name)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
return
|
||||
}
|
||||
|
||||
luh.Upload = upload
|
||||
defer luh.Upload.Close()
|
||||
|
||||
if err := luh.layerUploadResponse(w, r); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
// GetUploadStatus returns the status of a given upload, identified by uuid.
|
||||
func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if luh.Upload == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
|
||||
}
|
||||
|
||||
if err := luh.layerUploadResponse(w, r); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// PutLayerChunk receives a layer chunk during the layer upload process,
|
||||
// possible completing the upload with a checksum and length.
|
||||
func (luh *layerUploadHandler) PutLayerChunk(w http.ResponseWriter, r *http.Request) {
|
||||
if luh.Upload == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
|
||||
}
|
||||
|
||||
var finished bool
|
||||
|
||||
// TODO(stevvooe): This is woefully incomplete. Missing stuff:
|
||||
//
|
||||
// 1. Extract information from range header, if present.
|
||||
// 2. Check offset of current layer.
|
||||
// 3. Emit correct error responses.
|
||||
|
||||
// Read in the chunk
|
||||
io.Copy(luh.Upload, r.Body)
|
||||
|
||||
if err := luh.maybeCompleteUpload(w, r); err != nil {
|
||||
if err != errNotReadyToComplete {
|
||||
switch err := err.(type) {
|
||||
case storage.ErrLayerInvalidSize:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
luh.Errors.Push(v2.ErrorCodeSizeInvalid, err)
|
||||
return
|
||||
case storage.ErrLayerInvalidDigest:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
luh.Errors.Push(v2.ErrorCodeDigestInvalid, err)
|
||||
return
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := luh.layerUploadResponse(w, r); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
return
|
||||
}
|
||||
|
||||
if finished {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
||||
// CancelLayerUpload cancels an in-progress upload of a layer.
|
||||
func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.Request) {
|
||||
if luh.Upload == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// layerUploadResponse provides a standard request for uploading layers and
|
||||
// chunk responses. This sets the correct headers but the response status is
|
||||
// left to the caller.
|
||||
func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *http.Request) error {
|
||||
values := make(url.Values)
|
||||
stateToken, err := luh.Context.tokenProvider.layerUploadStateToToken(storage.LayerUploadState{Name: luh.Upload.Name(), UUID: luh.Upload.UUID(), Offset: luh.Upload.Offset()})
|
||||
if err != nil {
|
||||
logrus.Infof("error building upload state token: %s", err)
|
||||
return err
|
||||
}
|
||||
values.Set("_state", stateToken)
|
||||
uploadURL, err := luh.urlBuilder.BuildBlobUploadChunkURL(luh.Upload.Name(), luh.Upload.UUID(), values)
|
||||
if err != nil {
|
||||
logrus.Infof("error building upload url: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Location", uploadURL)
|
||||
w.Header().Set("Content-Length", "0")
|
||||
w.Header().Set("Range", fmt.Sprintf("0-%d", luh.Upload.Offset()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var errNotReadyToComplete = fmt.Errorf("not ready to complete upload")
|
||||
|
||||
// maybeCompleteUpload tries to complete the upload if the correct parameters
|
||||
// are available. Returns errNotReadyToComplete if not ready to complete.
|
||||
func (luh *layerUploadHandler) maybeCompleteUpload(w http.ResponseWriter, r *http.Request) error {
|
||||
// If we get a digest and length, we can finish the upload.
|
||||
dgstStr := r.FormValue("digest") // TODO(stevvooe): Support multiple digest parameters!
|
||||
sizeStr := r.FormValue("size")
|
||||
|
||||
if dgstStr == "" {
|
||||
return errNotReadyToComplete
|
||||
}
|
||||
|
||||
dgst, err := digest.ParseDigest(dgstStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var size int64
|
||||
if sizeStr != "" {
|
||||
size, err = strconv.ParseInt(sizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
size = -1
|
||||
}
|
||||
|
||||
luh.completeUpload(w, r, size, dgst)
|
||||
return nil
|
||||
}
|
||||
|
||||
// completeUpload finishes out the upload with the correct response.
|
||||
func (luh *layerUploadHandler) completeUpload(w http.ResponseWriter, r *http.Request, size int64, dgst digest.Digest) {
|
||||
layer, err := luh.Upload.Finish(size, dgst)
|
||||
if err != nil {
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
layerURL, err := luh.urlBuilder.BuildBlobURL(layer.Name(), layer.Digest())
|
||||
if err != nil {
|
||||
luh.Errors.Push(v2.ErrorCodeUnknown, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Location", layerURL)
|
||||
w.Header().Set("Content-Length", "0")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue