Merge pull request #827 from aaronlehmann/read-only-mode-2
Add a read-only mode as a configuration optionmaster
						commit
						eb484b7ddf
					
				| 
						 | 
				
			
			@ -633,6 +633,54 @@ func TestDeleteDisabled(t *testing.T) {
 | 
			
		|||
	checkResponse(t, "deleting layer with delete disabled", resp, http.StatusMethodNotAllowed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDeleteReadOnly(t *testing.T) {
 | 
			
		||||
	env := newTestEnv(t, true)
 | 
			
		||||
 | 
			
		||||
	imageName := "foo/bar"
 | 
			
		||||
	// "build" our layer file
 | 
			
		||||
	layerFile, tarSumStr, err := testutil.CreateRandomTarFile()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error creating random layer file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	layerDigest := digest.Digest(tarSumStr)
 | 
			
		||||
	layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error building blob URL")
 | 
			
		||||
	}
 | 
			
		||||
	uploadURLBase, _ := startPushLayer(t, env.builder, imageName)
 | 
			
		||||
	pushLayer(t, env.builder, imageName, layerDigest, uploadURLBase, layerFile)
 | 
			
		||||
 | 
			
		||||
	env.app.readOnly = true
 | 
			
		||||
 | 
			
		||||
	resp, err := httpDelete(layerURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error deleting layer: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkResponse(t, "deleting layer in read-only mode", resp, http.StatusMethodNotAllowed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestStartPushReadOnly(t *testing.T) {
 | 
			
		||||
	env := newTestEnv(t, true)
 | 
			
		||||
	env.app.readOnly = true
 | 
			
		||||
 | 
			
		||||
	imageName := "foo/bar"
 | 
			
		||||
 | 
			
		||||
	layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error building layer upload url: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := http.Post(layerUploadURL, "", nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error starting layer push: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	checkResponse(t, "starting push in read-only mode", resp, http.StatusMethodNotAllowed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpDelete(url string) (*http.Response, error) {
 | 
			
		||||
	req, err := http.NewRequest("DELETE", url, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,6 +69,9 @@ type App struct {
 | 
			
		|||
 | 
			
		||||
	// true if this registry is configured as a pull through cache
 | 
			
		||||
	isCache bool
 | 
			
		||||
 | 
			
		||||
	// true if the registry is in a read-only maintenance mode
 | 
			
		||||
	readOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewApp takes a configuration and returns a configured app, ready to serve
 | 
			
		||||
| 
						 | 
				
			
			@ -104,13 +107,24 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
 | 
			
		|||
 | 
			
		||||
	purgeConfig := uploadPurgeDefaultConfig()
 | 
			
		||||
	if mc, ok := configuration.Storage["maintenance"]; ok {
 | 
			
		||||
		for k, v := range mc {
 | 
			
		||||
			switch k {
 | 
			
		||||
			case "uploadpurging":
 | 
			
		||||
				purgeConfig = v.(map[interface{}]interface{})
 | 
			
		||||
		if v, ok := mc["uploadpurging"]; ok {
 | 
			
		||||
			purgeConfig, ok = v.(map[interface{}]interface{})
 | 
			
		||||
			if !ok {
 | 
			
		||||
				panic("uploadpurging config key must contain additional keys")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if v, ok := mc["readonly"]; ok {
 | 
			
		||||
			readOnly, ok := v.(map[interface{}]interface{})
 | 
			
		||||
			if !ok {
 | 
			
		||||
				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")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	startUploadPurger(app, app.driver, ctxu.GetLogger(app), purgeConfig)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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": http.HandlerFunc(blobHandler.DeleteBlob),
 | 
			
		||||
	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":   http.HandlerFunc(buh.StartBlobUpload),
 | 
			
		||||
		"GET":    http.HandlerFunc(buh.GetUploadStatus),
 | 
			
		||||
		"HEAD":   http.HandlerFunc(buh.GetUploadStatus),
 | 
			
		||||
		"PATCH":  http.HandlerFunc(buh.PatchBlobData),
 | 
			
		||||
		"PUT":    http.HandlerFunc(buh.PutBlobUploadComplete),
 | 
			
		||||
		"DELETE": http.HandlerFunc(buh.CancelBlobUpload),
 | 
			
		||||
	})
 | 
			
		||||
	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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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":    http.HandlerFunc(imageManifestHandler.PutImageManifest),
 | 
			
		||||
		"DELETE": http.HandlerFunc(imageManifestHandler.DeleteImageManifest),
 | 
			
		||||
	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