forked from TrueCloudLab/distribution
Merge pull request #827 from aaronlehmann/read-only-mode-2
Add a read-only mode as a configuration option
This commit is contained in:
commit
eb484b7ddf
5 changed files with 97 additions and 22 deletions
|
@ -633,6 +633,54 @@ func TestDeleteDisabled(t *testing.T) {
|
||||||
checkResponse(t, "deleting layer with delete disabled", resp, http.StatusMethodNotAllowed)
|
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) {
|
func httpDelete(url string) (*http.Response, error) {
|
||||||
req, err := http.NewRequest("DELETE", url, nil)
|
req, err := http.NewRequest("DELETE", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -69,6 +69,9 @@ type App struct {
|
||||||
|
|
||||||
// true if this registry is configured as a pull through cache
|
// true if this registry is configured as a pull through cache
|
||||||
isCache bool
|
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
|
// 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()
|
purgeConfig := uploadPurgeDefaultConfig()
|
||||||
if mc, ok := configuration.Storage["maintenance"]; ok {
|
if mc, ok := configuration.Storage["maintenance"]; ok {
|
||||||
for k, v := range mc {
|
if v, ok := mc["uploadpurging"]; ok {
|
||||||
switch k {
|
purgeConfig, ok = v.(map[interface{}]interface{})
|
||||||
case "uploadpurging":
|
if !ok {
|
||||||
purgeConfig = v.(map[interface{}]interface{})
|
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)
|
startUploadPurger(app, app.driver, ctxu.GetLogger(app), purgeConfig)
|
||||||
|
|
|
@ -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": http.HandlerFunc(blobHandler.DeleteBlob),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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": http.HandlerFunc(buh.StartBlobUpload),
|
|
||||||
"GET": http.HandlerFunc(buh.GetUploadStatus),
|
"GET": http.HandlerFunc(buh.GetUploadStatus),
|
||||||
"HEAD": http.HandlerFunc(buh.GetUploadStatus),
|
"HEAD": http.HandlerFunc(buh.GetUploadStatus),
|
||||||
"PATCH": http.HandlerFunc(buh.PatchBlobData),
|
}
|
||||||
"PUT": http.HandlerFunc(buh.PutBlobUploadComplete),
|
|
||||||
"DELETE": http.HandlerFunc(buh.CancelBlobUpload),
|
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 != "" {
|
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
|
||||||
|
|
|
@ -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": http.HandlerFunc(imageManifestHandler.PutImageManifest),
|
|
||||||
"DELETE": http.HandlerFunc(imageManifestHandler.DeleteImageManifest),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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