add support for repo deleted event also

by having another interface RepositoryRemover that is implemented by
registry instance and is injected in app context for event tracking

Signed-off-by: Manish Tomar <manish.tomar@docker.com>
This commit is contained in:
Manish Tomar 2018-08-02 22:58:52 -07:00
parent 0d8f4ac7b8
commit 328069bb4d
7 changed files with 76 additions and 7 deletions

View file

@ -116,6 +116,13 @@ func (b *bridge) TagDeleted(repo reference.Named, tag string) error {
return b.sink.Write(*event) return b.sink.Write(*event)
} }
func (b *bridge) RepoDeleted(repo reference.Named) error {
event := b.createEvent(EventActionDelete)
event.Target.Repository = repo.Name()
return b.sink.Write(*event)
}
func (b *bridge) createManifestEventAndWrite(action string, repo reference.Named, sm distribution.Manifest) error { func (b *bridge) createManifestEventAndWrite(action string, repo reference.Named, sm distribution.Manifest) error {
manifestEvent, err := b.createManifestEvent(action, repo, sm) manifestEvent, err := b.createManifestEvent(action, repo, sm)
if err != nil { if err != nil {

View file

@ -124,6 +124,18 @@ func TestEventBridgeTagDeleted(t *testing.T) {
} }
} }
func TestEventBridgeRepoDeleted(t *testing.T) {
l := createTestEnv(t, testSinkFn(func(events ...Event) error {
checkDeleted(t, EventActionDelete, events...)
return nil
}))
repoRef, _ := reference.WithName(repo)
if err := l.RepoDeleted(repoRef); err != nil {
t.Fatalf("unexpected error notifying repo deletion: %v", err)
}
}
func createTestEnv(t *testing.T, fn testSinkFn) Listener { func createTestEnv(t *testing.T, fn testSinkFn) Listener {
pk, err := libtrust.GenerateECP256PrivateKey() pk, err := libtrust.GenerateECP256PrivateKey()
if err != nil { if err != nil {

View file

@ -26,9 +26,9 @@ type BlobListener interface {
BlobDeleted(repo reference.Named, desc digest.Digest) error BlobDeleted(repo reference.Named, desc digest.Digest) error
} }
// RepoListener describes a listener that can respond to repository related events.
type RepoListener interface { type RepoListener interface {
TagDeleted(repo reference.Named, tag string) error TagDeleted(repo reference.Named, tag string) error
RepoDeleted(repo reference.Named) error
} }
// Listener combines all repository events into a single interface. // Listener combines all repository events into a single interface.
@ -43,12 +43,28 @@ type repositoryListener struct {
listener Listener listener Listener
} }
type removerListener struct {
distribution.RepositoryRemover
listener Listener
}
// Listen dispatches events on the repository to the listener. // Listen dispatches events on the repository to the listener.
func Listen(repo distribution.Repository, listener Listener) distribution.Repository { func Listen(repo distribution.Repository, remover distribution.RepositoryRemover, listener Listener) (distribution.Repository, distribution.RepositoryRemover) {
return &repositoryListener{ return &repositoryListener{
Repository: repo, Repository: repo,
listener: listener, listener: listener,
}, &removerListener{
RepositoryRemover: remover,
listener: listener,
}
}
func (nl *removerListener) Remove(ctx context.Context, name reference.Named) error {
err := nl.RepositoryRemover.Remove(ctx, name)
if err != nil {
return err
} }
return nl.listener.RepoDeleted(name)
} }
func (rl *repositoryListener) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { func (rl *repositoryListener) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {

View file

@ -38,10 +38,15 @@ func TestListener(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }
repository = Listen(repository, tl)
remover, ok := registry.(distribution.RepositoryRemover)
if !ok {
t.Fatal("registry does not implement RepositoryRemover")
}
repository, remover = Listen(repository, remover, tl)
// Now take the registry through a number of operations // Now take the registry through a number of operations
checkExerciseRepository(t, repository) checkExerciseRepository(t, repository, remover)
expectedOps := map[string]int{ expectedOps := map[string]int{
"manifest:push": 1, "manifest:push": 1,
@ -51,6 +56,7 @@ func TestListener(t *testing.T) {
"layer:pull": 2, "layer:pull": 2,
"layer:delete": 2, "layer:delete": 2,
"tag:delete": 1, "tag:delete": 1,
"repo:delete": 1,
} }
if !reflect.DeepEqual(tl.ops, expectedOps) { if !reflect.DeepEqual(tl.ops, expectedOps) {
@ -102,9 +108,14 @@ func (tl *testListener) TagDeleted(repo reference.Named, tag string) error {
return nil return nil
} }
func (tl *testListener) RepoDeleted(repo reference.Named) error {
tl.ops["repo:delete"]++
return nil
}
// checkExerciseRegistry takes the registry through all of its operations, // checkExerciseRegistry takes the registry through all of its operations,
// carrying out generic checks. // carrying out generic checks.
func checkExerciseRepository(t *testing.T, repository distribution.Repository) { func checkExerciseRepository(t *testing.T, repository distribution.Repository, remover distribution.RepositoryRemover) {
// TODO(stevvooe): This would be a nice testutil function. Basically, it // TODO(stevvooe): This would be a nice testutil function. Basically, it
// takes the registry through a common set of operations. This could be // takes the registry through a common set of operations. This could be
// used to make cross-cutting updates by changing internals that affect // used to make cross-cutting updates by changing internals that affect
@ -210,4 +221,9 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository) {
if err != nil { if err != nil {
t.Fatalf("unexpected error deleting tag: %v", err) t.Fatalf("unexpected error deleting tag: %v", err)
} }
err = remover.Remove(ctx, repository.Named())
if err != nil {
t.Fatalf("unexpected error deleting repo: %v", err)
}
} }

View file

@ -54,6 +54,11 @@ type RepositoryEnumerator interface {
Enumerate(ctx context.Context, ingester func(string) error) error Enumerate(ctx context.Context, ingester func(string) error) error
} }
// RepositoryRemover removes given repository
type RepositoryRemover interface {
Remove(ctx context.Context, name reference.Named) error
}
// ManifestServiceOption is a function argument for Manifest Service methods // ManifestServiceOption is a function argument for Manifest Service methods
type ManifestServiceOption interface { type ManifestServiceOption interface {
Apply(ManifestService) error Apply(ManifestService) error

View file

@ -7,6 +7,7 @@ import (
"path" "path"
"strings" "strings"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/storage/driver" "github.com/docker/distribution/registry/storage/driver"
) )
@ -70,6 +71,16 @@ func (reg *registry) Enumerate(ctx context.Context, ingester func(string) error)
return err return err
} }
// Remove removes a repository from storage
func (r *registry) Remove(ctx context.Context, name reference.Named) error {
root, err := pathFor(repositoriesRootPathSpec{})
if err != nil {
return err
}
repoDir := path.Join(root, name.Name())
return r.driver.Delete(ctx, repoDir)
}
// lessPath returns true if one path a is less than path b. // lessPath returns true if one path a is less than path b.
// //
// A component-wise comparison is done, rather than the lexical comparison of // A component-wise comparison is done, rather than the lexical comparison of

View file

@ -23,6 +23,7 @@ type registry struct {
schema1SigningKey libtrust.PrivateKey schema1SigningKey libtrust.PrivateKey
blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
manifestURLs manifestURLs manifestURLs manifestURLs
driver storagedriver.StorageDriver
} }
// manifestURLs holds regular expressions for controlling manifest URL whitelisting // manifestURLs holds regular expressions for controlling manifest URL whitelisting
@ -133,6 +134,7 @@ func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, option
}, },
statter: statter, statter: statter,
resumableDigestEnabled: true, resumableDigestEnabled: true,
driver: driver,
} }
for _, option := range options { for _, option := range options {