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>master
							parent
							
								
									c9bb330b71
								
							
						
					
					
						commit
						a601f92336
					
				|  | @ -118,7 +118,8 @@ information about each option that appears later in this page. | |||
|           age: 168h | ||||
|           interval: 24h | ||||
|           dryrun: false | ||||
|         readonly: false | ||||
|         readonly: | ||||
|           enabled: false | ||||
|     auth: | ||||
|       silly: | ||||
|         realm: silly-realm | ||||
|  | @ -667,12 +668,13 @@ Note: `age` and `interval` are strings containing a number with optional fractio | |||
| 
 | ||||
| ### Read-only mode | ||||
| 
 | ||||
| If the `readonly` parameter in the `maintenance` section is set to true, clients | ||||
| will not be allowed to write to the registry. This mode is useful to temporarily | ||||
| prevent writes to the backend storage so a garbage collection pass can be run. | ||||
| Before running garbage collection, the registry should be restarted with | ||||
| `readonly` set to true. After the garbage collection pass finishes, the registry | ||||
| may be restarted again, this time with `readonly` removed from the configuration. | ||||
| If the `readonly` section under `maintenance` has `enabled` set to `true`, | ||||
| clients will not be allowed to write to the registry. This mode is useful to | ||||
| temporarily prevent writes to the backend storage so a garbage collection pass | ||||
| can be run.  Before running garbage collection, the registry should be | ||||
| restarted with readonly's `enabled` set to true. After the garbage collection | ||||
| pass finishes, the registry may be restarted again, this time with `readonly` | ||||
| removed from the configuration (or set to false). | ||||
| 
 | ||||
| ### Openstack Swift | ||||
| 
 | ||||
|  |  | |||
|  | @ -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, | ||||
| 	}) | ||||
| ) | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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") | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue