7b56d10076
This commit locks down the set of http error codes that will be part of the inital V2 specification, proposed in docker/docker#9015. The naming order has been slightly changed and there are few tweaks to ensure all conditions are captured but this will be set the docker core will be impleemnted against. To support this, the errors have been moved into an api/errors package. A new type, ErrorDescriptor, has been defined to centralize the code, message and definitions used with each type. The information therein can be used to generate documentation and response code mappings (yet to come...). In addition to the refactoring that came along with this change, several tests have been added to ensure serialization round trips are reliable. This allows better support for using these error types on the client and server side. This is coupled with some tweaks in the client code to fix issues with error reporting. Other fixes in the client include moving client-specific errors out of the base package and ensuring that we have correct parameters for finishing uploads.
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
package registry
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/docker/docker-registry/api/errors"
|
|
"github.com/docker/docker-registry/digest"
|
|
"github.com/docker/docker-registry/storage"
|
|
"github.com/gorilla/handlers"
|
|
)
|
|
|
|
// imageManifestDispatcher takes the request context and builds the
|
|
// appropriate handler for handling image manifest requests.
|
|
func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|
imageManifestHandler := &imageManifestHandler{
|
|
Context: ctx,
|
|
Tag: ctx.vars["tag"],
|
|
}
|
|
|
|
imageManifestHandler.log = imageManifestHandler.log.WithField("tag", imageManifestHandler.Tag)
|
|
|
|
return handlers.MethodHandler{
|
|
"GET": http.HandlerFunc(imageManifestHandler.GetImageManifest),
|
|
"PUT": http.HandlerFunc(imageManifestHandler.PutImageManifest),
|
|
"DELETE": http.HandlerFunc(imageManifestHandler.DeleteImageManifest),
|
|
}
|
|
}
|
|
|
|
// imageManifestHandler handles http operations on image manifests.
|
|
type imageManifestHandler struct {
|
|
*Context
|
|
|
|
Tag string
|
|
}
|
|
|
|
// GetImageManifest fetches the image manifest from the storage backend, if it exists.
|
|
func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) {
|
|
manifests := imh.services.Manifests()
|
|
manifest, err := manifests.Get(imh.Name, imh.Tag)
|
|
|
|
if err != nil {
|
|
imh.Errors.Push(errors.ErrorCodeManifestUnknown, err)
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Content-Length", fmt.Sprint(len(manifest.Raw)))
|
|
w.Write(manifest.Raw)
|
|
}
|
|
|
|
// PutImageManifest validates and stores and image in the registry.
|
|
func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) {
|
|
manifests := imh.services.Manifests()
|
|
dec := json.NewDecoder(r.Body)
|
|
|
|
var manifest storage.SignedManifest
|
|
if err := dec.Decode(&manifest); err != nil {
|
|
imh.Errors.Push(errors.ErrorCodeManifestInvalid, err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := manifests.Put(imh.Name, imh.Tag, &manifest); err != nil {
|
|
// TODO(stevvooe): These error handling switches really need to be
|
|
// handled by an app global mapper.
|
|
switch err := err.(type) {
|
|
case storage.ErrManifestVerification:
|
|
for _, verificationError := range err {
|
|
switch verificationError := verificationError.(type) {
|
|
case storage.ErrUnknownLayer:
|
|
imh.Errors.Push(errors.ErrorCodeBlobUnknown, verificationError.FSLayer)
|
|
case storage.ErrManifestUnverified:
|
|
imh.Errors.Push(errors.ErrorCodeManifestUnverified)
|
|
default:
|
|
if verificationError == digest.ErrDigestInvalidFormat {
|
|
// TODO(stevvooe): We need to really need to move all
|
|
// errors to types. Its much more straightforward.
|
|
imh.Errors.Push(errors.ErrorCodeDigestInvalid)
|
|
} else {
|
|
imh.Errors.PushErr(verificationError)
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
imh.Errors.PushErr(err)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
|
|
// DeleteImageManifest removes the image with the given tag from the registry.
|
|
func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *http.Request) {
|
|
manifests := imh.services.Manifests()
|
|
if err := manifests.Delete(imh.Name, imh.Tag); err != nil {
|
|
switch err := err.(type) {
|
|
case storage.ErrUnknownManifest:
|
|
imh.Errors.Push(errors.ErrorCodeManifestUnknown, err)
|
|
w.WriteHeader(http.StatusNotFound)
|
|
default:
|
|
imh.Errors.Push(errors.ErrorCodeUnknown, err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Length", "0")
|
|
w.WriteHeader(http.StatusAccepted)
|
|
}
|