forked from TrueCloudLab/distribution
Remove decorator package
After implementing notifications end to end, it was found that decorating repositories was more straightforward that previously thought. It's unfortunate to can this package, but it led to the techniques employed in storage/notifications/listeners.go. The ultimate result turned out much better.
This commit is contained in:
parent
9f0c8d6616
commit
e5de2594ad
2 changed files with 0 additions and 323 deletions
|
@ -1,185 +0,0 @@
|
|||
package decorator
|
||||
|
||||
import (
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/storage"
|
||||
)
|
||||
|
||||
// Decorator provides an interface for intercepting object creation within a
|
||||
// registry. The single method accepts an registry storage object, such as a
|
||||
// Layer, optionally replacing it upon with an alternative object or a
|
||||
// wrapper.
|
||||
//
|
||||
// For example, if one wants to intercept the instantiation of a layer, an
|
||||
// implementation might be as follows:
|
||||
//
|
||||
// func (md *DecoratorImplementation) Decorate(v interface{}) interface{} {
|
||||
// switch v := v.(type) {
|
||||
// case Layer:
|
||||
// return wrapLayer(v)
|
||||
// }
|
||||
//
|
||||
// // Make sure to return the object or nil if the decorator doesn't require
|
||||
// // replacement.
|
||||
// return v
|
||||
// }
|
||||
//
|
||||
// Such a decorator can be used to intercept calls to support implementing
|
||||
// complex features outside of the storage package.
|
||||
type Decorator interface {
|
||||
Decorate(v interface{}) interface{}
|
||||
}
|
||||
|
||||
// Func provides a shortcut handler for decorators that only need a
|
||||
// function. Use is similar to http.HandlerFunc.
|
||||
type Func func(v interface{}) interface{}
|
||||
|
||||
// Decorate allows DecoratorFunc to implement the Decorator interface.
|
||||
func (df Func) Decorate(v interface{}) interface{} {
|
||||
return df(v)
|
||||
}
|
||||
|
||||
// DecorateRegistry the provided registry with decorator. Registries may be
|
||||
// decorated multiple times.
|
||||
func DecorateRegistry(registry storage.Registry, decorator Decorator) storage.Registry {
|
||||
return ®istryDecorator{
|
||||
Registry: registry,
|
||||
decorator: decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// registryDecorator intercepts registry object creation with a decorator.
|
||||
type registryDecorator struct {
|
||||
storage.Registry
|
||||
decorator Decorator
|
||||
}
|
||||
|
||||
// Repository overrides the method of the same name on the Registry, replacing
|
||||
// the returned instance with a decorator.
|
||||
func (rd *registryDecorator) Repository(name string) storage.Repository {
|
||||
delegate := rd.Registry.Repository(name)
|
||||
decorated := rd.decorator.Decorate(delegate)
|
||||
if decorated != nil {
|
||||
repository, ok := decorated.(storage.Repository)
|
||||
|
||||
if ok {
|
||||
delegate = repository
|
||||
}
|
||||
}
|
||||
|
||||
return &repositoryDecorator{
|
||||
Repository: delegate,
|
||||
decorator: rd.decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// repositoryDecorator decorates a repository, intercepting calls to Layers
|
||||
// and Manifests with injected variants.
|
||||
type repositoryDecorator struct {
|
||||
storage.Repository
|
||||
decorator Decorator
|
||||
}
|
||||
|
||||
// Layers overrides the Layers method of Repository.
|
||||
func (rd *repositoryDecorator) Layers() storage.LayerService {
|
||||
delegate := rd.Repository.Layers()
|
||||
decorated := rd.decorator.Decorate(delegate)
|
||||
|
||||
if decorated != nil {
|
||||
layers, ok := decorated.(storage.LayerService)
|
||||
|
||||
if ok {
|
||||
delegate = layers
|
||||
}
|
||||
}
|
||||
|
||||
return &layerServiceDecorator{
|
||||
LayerService: delegate,
|
||||
decorator: rd.decorator,
|
||||
}
|
||||
}
|
||||
|
||||
// Manifests overrides the Manifests method of Repository.
|
||||
func (rd *repositoryDecorator) Manifests() storage.ManifestService {
|
||||
delegate := rd.Repository.Manifests()
|
||||
decorated := rd.decorator.Decorate(delegate)
|
||||
|
||||
if decorated != nil {
|
||||
manifests, ok := decorated.(storage.ManifestService)
|
||||
|
||||
if ok {
|
||||
delegate = manifests
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(stevvooe): We do not have to intercept delegate calls to the
|
||||
// manifest service since it doesn't produce any interfaces for which
|
||||
// interception is supported.
|
||||
return delegate
|
||||
}
|
||||
|
||||
// layerServiceDecorator intercepts calls that generate Layer and LayerUpload
|
||||
// instances, replacing them with instances from the decorator.
|
||||
type layerServiceDecorator struct {
|
||||
storage.LayerService
|
||||
decorator Decorator
|
||||
}
|
||||
|
||||
// Fetch overrides the Fetch method of LayerService.
|
||||
func (lsd *layerServiceDecorator) Fetch(digest digest.Digest) (storage.Layer, error) {
|
||||
delegate, err := lsd.LayerService.Fetch(digest)
|
||||
return decorateLayer(lsd.decorator, delegate), err
|
||||
}
|
||||
|
||||
// Upload overrides the Upload method of LayerService.
|
||||
func (lsd *layerServiceDecorator) Upload() (storage.LayerUpload, error) {
|
||||
delegate, err := lsd.LayerService.Upload()
|
||||
return decorateLayerUpload(lsd.decorator, delegate), err
|
||||
}
|
||||
|
||||
// Resume overrides the Resume method of LayerService.
|
||||
func (lsd *layerServiceDecorator) Resume(uuid string) (storage.LayerUpload, error) {
|
||||
delegate, err := lsd.LayerService.Resume(uuid)
|
||||
return decorateLayerUpload(lsd.decorator, delegate), err
|
||||
}
|
||||
|
||||
// layerUploadDecorator intercepts calls that generate Layer instances,
|
||||
// replacing them with instances from the decorator.
|
||||
type layerUploadDecorator struct {
|
||||
storage.LayerUpload
|
||||
decorator Decorator
|
||||
}
|
||||
|
||||
func (lud *layerUploadDecorator) Finish(dgst digest.Digest) (storage.Layer, error) {
|
||||
delegate, err := lud.LayerUpload.Finish(dgst)
|
||||
return decorateLayer(lud.decorator, delegate), err
|
||||
}
|
||||
|
||||
// decorateLayer guarantees that a layer gets correctly decorated.
|
||||
func decorateLayer(decorator Decorator, delegate storage.Layer) storage.Layer {
|
||||
decorated := decorator.Decorate(delegate)
|
||||
if decorated != nil {
|
||||
layer, ok := decorated.(storage.Layer)
|
||||
if ok {
|
||||
delegate = layer
|
||||
}
|
||||
}
|
||||
|
||||
return delegate
|
||||
}
|
||||
|
||||
// decorateLayerUpload guarantees that an upload gets correctly decorated.
|
||||
func decorateLayerUpload(decorator Decorator, delegate storage.LayerUpload) storage.LayerUpload {
|
||||
decorated := decorator.Decorate(delegate)
|
||||
if decorated != nil {
|
||||
layerUpload, ok := decorated.(storage.LayerUpload)
|
||||
if ok {
|
||||
delegate = layerUpload
|
||||
}
|
||||
}
|
||||
|
||||
return &layerUploadDecorator{
|
||||
LayerUpload: delegate,
|
||||
decorator: decorator,
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package decorator
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/storage"
|
||||
"github.com/docker/distribution/storagedriver/inmemory"
|
||||
"github.com/docker/distribution/testutil"
|
||||
)
|
||||
|
||||
func TestRegistryDecorator(t *testing.T) {
|
||||
// Initialize the expected decorations. Call counting is a horrible way to
|
||||
// test this but should keep this code from being atrocious.
|
||||
expected := map[string]int{
|
||||
"repository": 1,
|
||||
"manifestservice": 1,
|
||||
"layerservice": 1,
|
||||
"layer": 4,
|
||||
"layerupload": 4,
|
||||
}
|
||||
decorated := map[string]int{}
|
||||
|
||||
decorator := Func(func(v interface{}) interface{} {
|
||||
switch v := v.(type) {
|
||||
case storage.Repository:
|
||||
t.Logf("decorate repository: %T", v)
|
||||
decorated["repository"]++
|
||||
case storage.ManifestService:
|
||||
t.Logf("decorate manifestservice: %T", v)
|
||||
decorated["manifestservice"]++
|
||||
case storage.LayerService:
|
||||
t.Logf("decorate layerservice: %T", v)
|
||||
decorated["layerservice"]++
|
||||
case storage.Layer:
|
||||
t.Logf("decorate layer: %T", v)
|
||||
decorated["layer"]++
|
||||
case storage.LayerUpload:
|
||||
t.Logf("decorate layerupload: %T", v)
|
||||
decorated["layerupload"]++
|
||||
default:
|
||||
t.Fatalf("unexpected object decorated: %v", v)
|
||||
}
|
||||
|
||||
return v
|
||||
})
|
||||
|
||||
registry := storage.NewRegistryWithDriver(inmemory.New())
|
||||
registry = DecorateRegistry(registry, decorator)
|
||||
|
||||
// Now take the registry through a number of operations
|
||||
checkExerciseRegistry(t, registry)
|
||||
|
||||
for component, calls := range expected {
|
||||
if decorated[component] != calls {
|
||||
t.Fatalf("%v was not decorated expected number of times: %d != %d", component, decorated[component], calls)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// checkExerciseRegistry takes the registry through all of its operations,
|
||||
// carrying out generic checks.
|
||||
func checkExerciseRegistry(t *testing.T, registry storage.Registry) {
|
||||
name := "foo/bar"
|
||||
tag := "thetag"
|
||||
repository := registry.Repository(name)
|
||||
m := manifest.Manifest{
|
||||
Versioned: manifest.Versioned{
|
||||
SchemaVersion: 1,
|
||||
},
|
||||
Name: name,
|
||||
Tag: tag,
|
||||
}
|
||||
|
||||
layers := repository.Layers()
|
||||
for i := 0; i < 2; i++ {
|
||||
rs, ds, err := testutil.CreateRandomTarFile()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating test layer: %v", err)
|
||||
}
|
||||
dgst := digest.Digest(ds)
|
||||
upload, err := layers.Upload()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating layer upload: %v", err)
|
||||
}
|
||||
|
||||
// Use the resumes, as well!
|
||||
upload, err = layers.Resume(upload.UUID())
|
||||
if err != nil {
|
||||
t.Fatalf("error resuming layer upload: %v", err)
|
||||
}
|
||||
|
||||
io.Copy(upload, rs)
|
||||
|
||||
if _, err := upload.Finish(dgst); err != nil {
|
||||
t.Fatalf("unexpected error finishing upload: %v", err)
|
||||
}
|
||||
|
||||
m.FSLayers = append(m.FSLayers, manifest.FSLayer{
|
||||
BlobSum: dgst,
|
||||
})
|
||||
|
||||
// Then fetch the layers
|
||||
if _, err := layers.Fetch(dgst); err != nil {
|
||||
t.Fatalf("error fetching layer: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
pk, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error generating key: %v", err)
|
||||
}
|
||||
|
||||
sm, err := manifest.Sign(&m, pk)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error signing manifest: %v", err)
|
||||
}
|
||||
|
||||
manifests := repository.Manifests()
|
||||
|
||||
if err := manifests.Put(tag, sm); err != nil {
|
||||
t.Fatalf("unexpected error putting the manifest: %v", err)
|
||||
}
|
||||
|
||||
fetched, err := manifests.Get(tag)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error fetching manifest: %v", err)
|
||||
}
|
||||
|
||||
if fetched.Tag != fetched.Tag {
|
||||
t.Fatalf("retrieved unexpected manifest: %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue