From cbf83ecd316fa16c6452fbe3601674bfca18b04a Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Thu, 6 Aug 2015 18:02:43 -0700 Subject: [PATCH] Add an "enabled" parameter under "readonly", and make it as if the mutable handlers don't exist when read-only mode is enabled Signed-off-by: Aaron Lehmann --- docs/api/v2/errors.go | 10 ---------- docs/handlers/api_test.go | 4 ++-- docs/handlers/app.go | 10 ++++++++-- docs/handlers/blob.go | 13 +++++++++---- docs/handlers/blobupload.go | 21 ++++++++++++--------- docs/handlers/helpers.go | 14 -------------- docs/handlers/images.go | 13 +++++++++---- 7 files changed, 40 insertions(+), 45 deletions(-) diff --git a/docs/api/v2/errors.go b/docs/api/v2/errors.go index 97cb03e28..ece52a2cd 100644 --- a/docs/api/v2/errors.go +++ b/docs/api/v2/errors.go @@ -133,14 +133,4 @@ var ( longer proceed.`, HTTPStatusCode: http.StatusNotFound, }) - - // ErrorCodeMaintenanceMode is returned when an upload can't be - // accepted because the registry is in maintenance mode. - ErrorCodeMaintenanceMode = errcode.Register(errGroup, errcode.ErrorDescriptor{ - Value: "MAINTENANCE_MODE", - Message: "registry in maintenance mode", - Description: `The upload cannot be accepted because the registry - is running read-only in maintenance mode.`, - HTTPStatusCode: http.StatusServiceUnavailable, - }) ) diff --git a/docs/handlers/api_test.go b/docs/handlers/api_test.go index e85ae4348..0a0b264b9 100644 --- a/docs/handlers/api_test.go +++ b/docs/handlers/api_test.go @@ -658,7 +658,7 @@ func TestDeleteReadOnly(t *testing.T) { t.Fatalf("unexpected error deleting layer: %v", err) } - checkResponse(t, "deleting layer in read-only mode", resp, http.StatusServiceUnavailable) + checkResponse(t, "deleting layer in read-only mode", resp, http.StatusMethodNotAllowed) } func TestStartPushReadOnly(t *testing.T) { @@ -678,7 +678,7 @@ func TestStartPushReadOnly(t *testing.T) { } defer resp.Body.Close() - checkResponse(t, "starting push in read-only mode", resp, http.StatusServiceUnavailable) + checkResponse(t, "starting push in read-only mode", resp, http.StatusMethodNotAllowed) } func httpDelete(url string) (*http.Response, error) { diff --git a/docs/handlers/app.go b/docs/handlers/app.go index d851714ad..b11dc5b6d 100644 --- a/docs/handlers/app.go +++ b/docs/handlers/app.go @@ -109,9 +109,15 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap } } if v, ok := mc["readonly"]; ok { - app.readOnly, ok = v.(bool) + readOnly, ok := v.(map[interface{}]interface{}) if !ok { - panic("readonly config key must have a boolean value") + panic("readonly config key must contain additional keys") + } + if readOnlyEnabled, ok := readOnly["enabled"]; ok { + app.readOnly, ok = readOnlyEnabled.(bool) + if !ok { + panic("readonly's enabled config key must have a boolean value") + } } } } diff --git a/docs/handlers/blob.go b/docs/handlers/blob.go index 69c39841b..fb250acd2 100644 --- a/docs/handlers/blob.go +++ b/docs/handlers/blob.go @@ -32,11 +32,16 @@ func blobDispatcher(ctx *Context, r *http.Request) http.Handler { Digest: dgst, } - return handlers.MethodHandler{ - "GET": http.HandlerFunc(blobHandler.GetBlob), - "HEAD": http.HandlerFunc(blobHandler.GetBlob), - "DELETE": mutableHandler(blobHandler.DeleteBlob, ctx), + mhandler := handlers.MethodHandler{ + "GET": http.HandlerFunc(blobHandler.GetBlob), + "HEAD": http.HandlerFunc(blobHandler.GetBlob), } + + if !ctx.readOnly { + mhandler["DELETE"] = http.HandlerFunc(blobHandler.DeleteBlob) + } + + return mhandler } // blobHandler serves http blob requests. diff --git a/docs/handlers/blobupload.go b/docs/handlers/blobupload.go index 198a8f67f..1bd33d337 100644 --- a/docs/handlers/blobupload.go +++ b/docs/handlers/blobupload.go @@ -22,14 +22,17 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler { UUID: getUploadUUID(ctx), } - handler := http.Handler(handlers.MethodHandler{ - "POST": mutableHandler(buh.StartBlobUpload, ctx), - "GET": http.HandlerFunc(buh.GetUploadStatus), - "HEAD": http.HandlerFunc(buh.GetUploadStatus), - "PATCH": mutableHandler(buh.PatchBlobData, ctx), - "PUT": mutableHandler(buh.PutBlobUploadComplete, ctx), - "DELETE": mutableHandler(buh.CancelBlobUpload, ctx), - }) + handler := handlers.MethodHandler{ + "GET": http.HandlerFunc(buh.GetUploadStatus), + "HEAD": http.HandlerFunc(buh.GetUploadStatus), + } + + if !ctx.readOnly { + handler["POST"] = http.HandlerFunc(buh.StartBlobUpload) + handler["PATCH"] = http.HandlerFunc(buh.PatchBlobData) + handler["PUT"] = http.HandlerFunc(buh.PutBlobUploadComplete) + handler["DELETE"] = http.HandlerFunc(buh.CancelBlobUpload) + } if buh.UUID != "" { state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state")) @@ -93,7 +96,7 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler { } } - handler = closeResources(handler, buh.Upload) + return closeResources(handler, buh.Upload) } return handler diff --git a/docs/handlers/helpers.go b/docs/handlers/helpers.go index 9b462a192..5a3c99841 100644 --- a/docs/handlers/helpers.go +++ b/docs/handlers/helpers.go @@ -7,7 +7,6 @@ import ( ctxu "github.com/docker/distribution/context" "github.com/docker/distribution/registry/api/errcode" - "github.com/docker/distribution/registry/api/v2" ) // closeResources closes all the provided resources after running the target @@ -61,16 +60,3 @@ func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWr return nil } - -// mutableHandler wraps a http.HandlerFunc with a check that the registry is -// not in read-only mode. If it is in read-only mode, the wrapper returns -// v2.ErrorCodeMaintenanceMode to the client. -func mutableHandler(handler http.HandlerFunc, ctx *Context) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if ctx.App.readOnly { - ctx.Errors = append(ctx.Errors, v2.ErrorCodeMaintenanceMode) - return - } - handler(w, r) - } -} diff --git a/docs/handlers/images.go b/docs/handlers/images.go index 78e36a13b..0aeeb6f0f 100644 --- a/docs/handlers/images.go +++ b/docs/handlers/images.go @@ -32,11 +32,16 @@ func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler { imageManifestHandler.Digest = dgst } - return handlers.MethodHandler{ - "GET": http.HandlerFunc(imageManifestHandler.GetImageManifest), - "PUT": mutableHandler(imageManifestHandler.PutImageManifest, ctx), - "DELETE": mutableHandler(imageManifestHandler.DeleteImageManifest, ctx), + mhandler := handlers.MethodHandler{ + "GET": http.HandlerFunc(imageManifestHandler.GetImageManifest), } + + if !ctx.readOnly { + mhandler["PUT"] = http.HandlerFunc(imageManifestHandler.PutImageManifest) + mhandler["DELETE"] = http.HandlerFunc(imageManifestHandler.DeleteImageManifest) + } + + return mhandler } // imageManifestHandler handles http operations on image manifests.