feat: implement 'rewrite' storage middleware (#4146)
This commit is contained in:
commit
4dd0ac977e
5 changed files with 209 additions and 0 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
||||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/cloudfront"
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/cloudfront"
|
||||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/redirect"
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/redirect"
|
||||||
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/middleware/rewrite"
|
||||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/s3-aws"
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/s3-aws"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
15
docs/content/storage-drivers/middleware/_index.md
Normal file
15
docs/content/storage-drivers/middleware/_index.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
description: Explains how to use storage middleware
|
||||||
|
keywords: registry, on-prem, images, tags, repository, distribution, storage drivers, advanced
|
||||||
|
title: Storage middleware
|
||||||
|
---
|
||||||
|
|
||||||
|
This document describes the registry storage middleware.
|
||||||
|
|
||||||
|
## Provided middleware
|
||||||
|
|
||||||
|
This storage driver package comes bundled with several middleware options:
|
||||||
|
|
||||||
|
- cloudfront
|
||||||
|
- redirect
|
||||||
|
- [rewrite](rewrite): Partially rewrites the URL returned by the storage driver.
|
32
docs/content/storage-drivers/middleware/rewrite.md
Normal file
32
docs/content/storage-drivers/middleware/rewrite.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
description: Explains how to use the rewrite storage middleware
|
||||||
|
keywords: registry, service, driver, images, storage, middleware, rewrite
|
||||||
|
title: Rewrite middleware
|
||||||
|
---
|
||||||
|
|
||||||
|
A storage middleware which allows to rewrite the URL returned by the storage driver.
|
||||||
|
|
||||||
|
For example, it can be used to rewrite the Blob Storage URL returned by the Azure Blob Storage driver to use Azure CDN.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
* `scheme`: (optional): Rewrite the returned URL scheme (if set).
|
||||||
|
* `host`: (optional): Rewrite the returned URL host (if set).
|
||||||
|
* `trimpathprefix` (optional): Trim the prefix from the returned URL path (if set).
|
||||||
|
|
||||||
|
## Example configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
storage:
|
||||||
|
azure:
|
||||||
|
accountname: "ACCOUNT_NAME"
|
||||||
|
accountkey: "******"
|
||||||
|
container: container-name
|
||||||
|
middleware:
|
||||||
|
storage:
|
||||||
|
- name: rewrite
|
||||||
|
options:
|
||||||
|
scheme: https
|
||||||
|
host: example-cdn-endpoint.azurefd.net
|
||||||
|
trimpathprefix: /container-name
|
||||||
|
```
|
86
registry/storage/driver/middleware/rewrite/middleware.go
Normal file
86
registry/storage/driver/middleware/rewrite/middleware.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
|
||||||
|
storagemiddleware "github.com/distribution/distribution/v3/registry/storage/driver/middleware"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := storagemiddleware.Register("rewrite", newRewriteStorageMiddleware); err != nil {
|
||||||
|
logrus.Errorf("tailed to register redirect storage middleware: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rewriteStorageMiddleware struct {
|
||||||
|
storagedriver.StorageDriver
|
||||||
|
overrideScheme string
|
||||||
|
overrideHost string
|
||||||
|
trimPathPrefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ storagedriver.StorageDriver = &rewriteStorageMiddleware{}
|
||||||
|
|
||||||
|
func getStringOption(key string, options map[string]interface{}) (string, error) {
|
||||||
|
o, ok := options[key]
|
||||||
|
if !ok {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
s, ok := o.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("%s must be a string", key)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRewriteStorageMiddleware(ctx context.Context, sd storagedriver.StorageDriver, options map[string]interface{}) (storagedriver.StorageDriver, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
r := &rewriteStorageMiddleware{StorageDriver: sd}
|
||||||
|
|
||||||
|
if r.overrideScheme, err = getStringOption("scheme", options); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.overrideHost, err = getStringOption("host", options); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.trimPathPrefix, err = getStringOption("trimpathprefix", options); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rewriteStorageMiddleware) RedirectURL(req *http.Request, path string) (string, error) {
|
||||||
|
storagePath, err := r.StorageDriver.RedirectURL(req, path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(storagePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.overrideScheme != "" {
|
||||||
|
u.Scheme = r.overrideScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.overrideHost != "" {
|
||||||
|
u.Host = r.overrideHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.trimPathPrefix != "" {
|
||||||
|
u.Path = strings.TrimPrefix(u.Path, r.trimPathPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/distribution/distribution/v3/registry/storage/driver/base"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockSD struct {
|
||||||
|
base.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*mockSD) RedirectURL(_ *http.Request, urlPath string) (string, error) {
|
||||||
|
return "http://some.host/some/path/file", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoConfig(t *testing.T) {
|
||||||
|
options := make(map[string]interface{})
|
||||||
|
middleware, err := newRewriteStorageMiddleware(context.Background(), &mockSD{}, options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, ok := middleware.(*rewriteStorageMiddleware)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
url, err := middleware.RedirectURL(nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "http://some.host/some/path/file", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrongType(t *testing.T) {
|
||||||
|
options := map[string]interface{}{
|
||||||
|
"scheme": 1,
|
||||||
|
}
|
||||||
|
_, err := newRewriteStorageMiddleware(context.TODO(), nil, options)
|
||||||
|
require.ErrorContains(t, err, "scheme must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRewriteHostsScheme(t *testing.T) {
|
||||||
|
options := map[string]interface{}{
|
||||||
|
"scheme": "https",
|
||||||
|
"host": "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware, err := newRewriteStorageMiddleware(context.TODO(), &mockSD{}, options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
m, ok := middleware.(*rewriteStorageMiddleware)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, "https", m.overrideScheme)
|
||||||
|
require.Equal(t, "example.com", m.overrideHost)
|
||||||
|
|
||||||
|
url, err := middleware.RedirectURL(nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "https://example.com/some/path/file", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrimPrefix(t *testing.T) {
|
||||||
|
options := map[string]interface{}{
|
||||||
|
"trimpathprefix": "/some/path",
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware, err := newRewriteStorageMiddleware(context.TODO(), &mockSD{}, options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
m, ok := middleware.(*rewriteStorageMiddleware)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, "/some/path", m.trimPathPrefix)
|
||||||
|
|
||||||
|
url, err := middleware.RedirectURL(nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "http://some.host/file", url)
|
||||||
|
}
|
Loading…
Reference in a new issue