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>master
							parent
							
								
									0d8f4ac7b8
								
							
						
					
					
						commit
						328069bb4d
					
				|  | @ -116,6 +116,13 @@ func (b *bridge) TagDeleted(repo reference.Named, tag string) error { | |||
| 	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 { | ||||
| 	manifestEvent, err := b.createManifestEvent(action, repo, sm) | ||||
| 	if err != nil { | ||||
|  |  | |||
|  | @ -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 { | ||||
| 	pk, err := libtrust.GenerateECP256PrivateKey() | ||||
| 	if err != nil { | ||||
|  |  | |||
|  | @ -26,9 +26,9 @@ type BlobListener interface { | |||
| 	BlobDeleted(repo reference.Named, desc digest.Digest) error | ||||
| } | ||||
| 
 | ||||
| // RepoListener describes a listener that can respond to repository related events.
 | ||||
| type RepoListener interface { | ||||
| 	TagDeleted(repo reference.Named, tag string) error | ||||
| 	RepoDeleted(repo reference.Named) error | ||||
| } | ||||
| 
 | ||||
| // Listener combines all repository events into a single interface.
 | ||||
|  | @ -43,14 +43,30 @@ type repositoryListener struct { | |||
| 	listener Listener | ||||
| } | ||||
| 
 | ||||
| type removerListener struct { | ||||
| 	distribution.RepositoryRemover | ||||
| 	listener 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{ | ||||
| 			Repository: repo, | ||||
| 			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) { | ||||
| 	manifests, err := rl.Repository.Manifests(ctx, options...) | ||||
| 	if err != nil { | ||||
|  |  | |||
|  | @ -38,10 +38,15 @@ func TestListener(t *testing.T) { | |||
| 	if err != nil { | ||||
| 		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
 | ||||
| 	checkExerciseRepository(t, repository) | ||||
| 	checkExerciseRepository(t, repository, remover) | ||||
| 
 | ||||
| 	expectedOps := map[string]int{ | ||||
| 		"manifest:push":   1, | ||||
|  | @ -51,6 +56,7 @@ func TestListener(t *testing.T) { | |||
| 		"layer:pull":      2, | ||||
| 		"layer:delete":    2, | ||||
| 		"tag:delete":      1, | ||||
| 		"repo:delete":     1, | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(tl.ops, expectedOps) { | ||||
|  | @ -102,9 +108,14 @@ func (tl *testListener) TagDeleted(repo reference.Named, tag string) error { | |||
| 	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,
 | ||||
| // 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
 | ||||
| 	// takes the registry through a common set of operations. This could be
 | ||||
| 	// 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 { | ||||
| 		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) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -54,6 +54,11 @@ type RepositoryEnumerator interface { | |||
| 	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
 | ||||
| type ManifestServiceOption interface { | ||||
| 	Apply(ManifestService) error | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import ( | |||
| 	"path" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/docker/distribution/reference" | ||||
| 	"github.com/docker/distribution/registry/storage/driver" | ||||
| ) | ||||
| 
 | ||||
|  | @ -70,6 +71,16 @@ func (reg *registry) Enumerate(ctx context.Context, ingester func(string) error) | |||
| 	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.
 | ||||
| //
 | ||||
| // A component-wise comparison is done, rather than the lexical comparison of
 | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ type registry struct { | |||
| 	schema1SigningKey            libtrust.PrivateKey | ||||
| 	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory | ||||
| 	manifestURLs                 manifestURLs | ||||
| 	driver                       storagedriver.StorageDriver | ||||
| } | ||||
| 
 | ||||
| // manifestURLs holds regular expressions for controlling manifest URL whitelisting
 | ||||
|  | @ -133,6 +134,7 @@ func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, option | |||
| 		}, | ||||
| 		statter:                statter, | ||||
| 		resumableDigestEnabled: true, | ||||
| 		driver:                 driver, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, option := range options { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue