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 <aaron.lehmann@docker.com>
This commit is contained in:
parent
c9bb330b71
commit
a601f92336
8 changed files with 49 additions and 52 deletions
|
@ -118,7 +118,8 @@ information about each option that appears later in this page.
|
||||||
age: 168h
|
age: 168h
|
||||||
interval: 24h
|
interval: 24h
|
||||||
dryrun: false
|
dryrun: false
|
||||||
readonly: false
|
readonly:
|
||||||
|
enabled: false
|
||||||
auth:
|
auth:
|
||||||
silly:
|
silly:
|
||||||
realm: silly-realm
|
realm: silly-realm
|
||||||
|
@ -667,12 +668,13 @@ Note: `age` and `interval` are strings containing a number with optional fractio
|
||||||
|
|
||||||
### Read-only mode
|
### Read-only mode
|
||||||
|
|
||||||
If the `readonly` parameter in the `maintenance` section is set to true, clients
|
If the `readonly` section under `maintenance` has `enabled` set to `true`,
|
||||||
will not be allowed to write to the registry. This mode is useful to temporarily
|
clients will not be allowed to write to the registry. This mode is useful to
|
||||||
prevent writes to the backend storage so a garbage collection pass can be run.
|
temporarily prevent writes to the backend storage so a garbage collection pass
|
||||||
Before running garbage collection, the registry should be restarted with
|
can be run. Before running garbage collection, the registry should be
|
||||||
`readonly` set to true. After the garbage collection pass finishes, the registry
|
restarted with readonly's `enabled` set to true. After the garbage collection
|
||||||
may be restarted again, this time with `readonly` removed from the configuration.
|
pass finishes, the registry may be restarted again, this time with `readonly`
|
||||||
|
removed from the configuration (or set to false).
|
||||||
|
|
||||||
### Openstack Swift
|
### Openstack Swift
|
||||||
|
|
||||||
|
|
|
@ -133,14 +133,4 @@ var (
|
||||||
longer proceed.`,
|
longer proceed.`,
|
||||||
HTTPStatusCode: http.StatusNotFound,
|
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,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -658,7 +658,7 @@ func TestDeleteReadOnly(t *testing.T) {
|
||||||
t.Fatalf("unexpected error deleting layer: %v", err)
|
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) {
|
func TestStartPushReadOnly(t *testing.T) {
|
||||||
|
@ -678,7 +678,7 @@ func TestStartPushReadOnly(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
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) {
|
func httpDelete(url string) (*http.Response, error) {
|
||||||
|
|
|
@ -109,9 +109,15 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := mc["readonly"]; ok {
|
if v, ok := mc["readonly"]; ok {
|
||||||
app.readOnly, ok = v.(bool)
|
readOnly, ok := v.(map[interface{}]interface{})
|
||||||
if !ok {
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,16 @@ func blobDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||||
Digest: dgst,
|
Digest: dgst,
|
||||||
}
|
}
|
||||||
|
|
||||||
return handlers.MethodHandler{
|
mhandler := handlers.MethodHandler{
|
||||||
"GET": http.HandlerFunc(blobHandler.GetBlob),
|
"GET": http.HandlerFunc(blobHandler.GetBlob),
|
||||||
"HEAD": http.HandlerFunc(blobHandler.GetBlob),
|
"HEAD": http.HandlerFunc(blobHandler.GetBlob),
|
||||||
"DELETE": mutableHandler(blobHandler.DeleteBlob, ctx),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ctx.readOnly {
|
||||||
|
mhandler["DELETE"] = http.HandlerFunc(blobHandler.DeleteBlob)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mhandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// blobHandler serves http blob requests.
|
// blobHandler serves http blob requests.
|
||||||
|
|
|
@ -22,14 +22,17 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||||
UUID: getUploadUUID(ctx),
|
UUID: getUploadUUID(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := http.Handler(handlers.MethodHandler{
|
handler := handlers.MethodHandler{
|
||||||
"POST": mutableHandler(buh.StartBlobUpload, ctx),
|
"GET": http.HandlerFunc(buh.GetUploadStatus),
|
||||||
"GET": http.HandlerFunc(buh.GetUploadStatus),
|
"HEAD": http.HandlerFunc(buh.GetUploadStatus),
|
||||||
"HEAD": http.HandlerFunc(buh.GetUploadStatus),
|
}
|
||||||
"PATCH": mutableHandler(buh.PatchBlobData, ctx),
|
|
||||||
"PUT": mutableHandler(buh.PutBlobUploadComplete, ctx),
|
if !ctx.readOnly {
|
||||||
"DELETE": mutableHandler(buh.CancelBlobUpload, ctx),
|
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 != "" {
|
if buh.UUID != "" {
|
||||||
state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
|
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
|
return handler
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
ctxu "github.com/docker/distribution/context"
|
ctxu "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/registry/api/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// closeResources closes all the provided resources after running the target
|
// 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
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,11 +32,16 @@ func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
||||||
imageManifestHandler.Digest = dgst
|
imageManifestHandler.Digest = dgst
|
||||||
}
|
}
|
||||||
|
|
||||||
return handlers.MethodHandler{
|
mhandler := handlers.MethodHandler{
|
||||||
"GET": http.HandlerFunc(imageManifestHandler.GetImageManifest),
|
"GET": http.HandlerFunc(imageManifestHandler.GetImageManifest),
|
||||||
"PUT": mutableHandler(imageManifestHandler.PutImageManifest, ctx),
|
|
||||||
"DELETE": mutableHandler(imageManifestHandler.DeleteImageManifest, ctx),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ctx.readOnly {
|
||||||
|
mhandler["PUT"] = http.HandlerFunc(imageManifestHandler.PutImageManifest)
|
||||||
|
mhandler["DELETE"] = http.HandlerFunc(imageManifestHandler.DeleteImageManifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mhandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageManifestHandler handles http operations on image manifests.
|
// imageManifestHandler handles http operations on image manifests.
|
||||||
|
|
Loading…
Reference in a new issue