github.com/npaton/distribution@v2.3.1-rc.0+incompatible/notifications/listener.go (about)

     1  package notifications
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"github.com/Sirupsen/logrus"
     7  	"github.com/docker/distribution"
     8  	"github.com/docker/distribution/context"
     9  	"github.com/docker/distribution/digest"
    10  	"github.com/docker/distribution/reference"
    11  )
    12  
    13  // ManifestListener describes a set of methods for listening to events related to manifests.
    14  type ManifestListener interface {
    15  	ManifestPushed(repo reference.Named, sm distribution.Manifest) error
    16  	ManifestPulled(repo reference.Named, sm distribution.Manifest) error
    17  
    18  	// TODO(stevvooe): Please note that delete support is still a little shaky
    19  	// and we'll need to propagate these in the future.
    20  
    21  	ManifestDeleted(repo reference.Named, sm distribution.Manifest) error
    22  }
    23  
    24  // BlobListener describes a listener that can respond to layer related events.
    25  type BlobListener interface {
    26  	BlobPushed(repo reference.Named, desc distribution.Descriptor) error
    27  	BlobPulled(repo reference.Named, desc distribution.Descriptor) error
    28  	BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error
    29  
    30  	// TODO(stevvooe): Please note that delete support is still a little shaky
    31  	// and we'll need to propagate these in the future.
    32  
    33  	BlobDeleted(repo reference.Named, desc distribution.Descriptor) error
    34  }
    35  
    36  // Listener combines all repository events into a single interface.
    37  type Listener interface {
    38  	ManifestListener
    39  	BlobListener
    40  }
    41  
    42  type repositoryListener struct {
    43  	distribution.Repository
    44  	listener Listener
    45  }
    46  
    47  // Listen dispatches events on the repository to the listener.
    48  func Listen(repo distribution.Repository, listener Listener) distribution.Repository {
    49  	return &repositoryListener{
    50  		Repository: repo,
    51  		listener:   listener,
    52  	}
    53  }
    54  
    55  func (rl *repositoryListener) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
    56  	manifests, err := rl.Repository.Manifests(ctx, options...)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return &manifestServiceListener{
    61  		ManifestService: manifests,
    62  		parent:          rl,
    63  	}, nil
    64  }
    65  
    66  func (rl *repositoryListener) Blobs(ctx context.Context) distribution.BlobStore {
    67  	return &blobServiceListener{
    68  		BlobStore: rl.Repository.Blobs(ctx),
    69  		parent:    rl,
    70  	}
    71  }
    72  
    73  type manifestServiceListener struct {
    74  	distribution.ManifestService
    75  	parent *repositoryListener
    76  }
    77  
    78  func (msl *manifestServiceListener) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
    79  	sm, err := msl.ManifestService.Get(ctx, dgst)
    80  	if err == nil {
    81  		if err := msl.parent.listener.ManifestPulled(msl.parent.Repository.Name(), sm); err != nil {
    82  			logrus.Errorf("error dispatching manifest pull to listener: %v", err)
    83  		}
    84  	}
    85  
    86  	return sm, err
    87  }
    88  
    89  func (msl *manifestServiceListener) Put(ctx context.Context, sm distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
    90  	dgst, err := msl.ManifestService.Put(ctx, sm, options...)
    91  
    92  	if err == nil {
    93  		if err := msl.parent.listener.ManifestPushed(msl.parent.Repository.Name(), sm); err != nil {
    94  			logrus.Errorf("error dispatching manifest push to listener: %v", err)
    95  		}
    96  	}
    97  
    98  	return dgst, err
    99  }
   100  
   101  type blobServiceListener struct {
   102  	distribution.BlobStore
   103  	parent *repositoryListener
   104  }
   105  
   106  var _ distribution.BlobStore = &blobServiceListener{}
   107  
   108  func (bsl *blobServiceListener) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
   109  	p, err := bsl.BlobStore.Get(ctx, dgst)
   110  	if err == nil {
   111  		if desc, err := bsl.Stat(ctx, dgst); err != nil {
   112  			context.GetLogger(ctx).Errorf("error resolving descriptor in ServeBlob listener: %v", err)
   113  		} else {
   114  			if err := bsl.parent.listener.BlobPulled(bsl.parent.Repository.Name(), desc); err != nil {
   115  				context.GetLogger(ctx).Errorf("error dispatching layer pull to listener: %v", err)
   116  			}
   117  		}
   118  	}
   119  
   120  	return p, err
   121  }
   122  
   123  func (bsl *blobServiceListener) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
   124  	rc, err := bsl.BlobStore.Open(ctx, dgst)
   125  	if err == nil {
   126  		if desc, err := bsl.Stat(ctx, dgst); err != nil {
   127  			context.GetLogger(ctx).Errorf("error resolving descriptor in ServeBlob listener: %v", err)
   128  		} else {
   129  			if err := bsl.parent.listener.BlobPulled(bsl.parent.Repository.Name(), desc); err != nil {
   130  				context.GetLogger(ctx).Errorf("error dispatching layer pull to listener: %v", err)
   131  			}
   132  		}
   133  	}
   134  
   135  	return rc, err
   136  }
   137  
   138  func (bsl *blobServiceListener) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error {
   139  	err := bsl.BlobStore.ServeBlob(ctx, w, r, dgst)
   140  	if err == nil {
   141  		if desc, err := bsl.Stat(ctx, dgst); err != nil {
   142  			context.GetLogger(ctx).Errorf("error resolving descriptor in ServeBlob listener: %v", err)
   143  		} else {
   144  			if err := bsl.parent.listener.BlobPulled(bsl.parent.Repository.Name(), desc); err != nil {
   145  				context.GetLogger(ctx).Errorf("error dispatching layer pull to listener: %v", err)
   146  			}
   147  		}
   148  	}
   149  
   150  	return err
   151  }
   152  
   153  func (bsl *blobServiceListener) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
   154  	desc, err := bsl.BlobStore.Put(ctx, mediaType, p)
   155  	if err == nil {
   156  		if err := bsl.parent.listener.BlobPushed(bsl.parent.Repository.Name(), desc); err != nil {
   157  			context.GetLogger(ctx).Errorf("error dispatching layer pull to listener: %v", err)
   158  		}
   159  	}
   160  
   161  	return desc, err
   162  }
   163  
   164  func (bsl *blobServiceListener) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
   165  	wr, err := bsl.BlobStore.Create(ctx, options...)
   166  	switch err := err.(type) {
   167  	case distribution.ErrBlobMounted:
   168  		if err := bsl.parent.listener.BlobMounted(bsl.parent.Repository.Name(), err.Descriptor, err.From); err != nil {
   169  			context.GetLogger(ctx).Errorf("error dispatching blob mount to listener: %v", err)
   170  		}
   171  		return nil, err
   172  	}
   173  	return bsl.decorateWriter(wr), err
   174  }
   175  
   176  func (bsl *blobServiceListener) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
   177  	wr, err := bsl.BlobStore.Resume(ctx, id)
   178  	return bsl.decorateWriter(wr), err
   179  }
   180  
   181  func (bsl *blobServiceListener) decorateWriter(wr distribution.BlobWriter) distribution.BlobWriter {
   182  	return &blobWriterListener{
   183  		BlobWriter: wr,
   184  		parent:     bsl,
   185  	}
   186  }
   187  
   188  type blobWriterListener struct {
   189  	distribution.BlobWriter
   190  	parent *blobServiceListener
   191  }
   192  
   193  func (bwl *blobWriterListener) Commit(ctx context.Context, desc distribution.Descriptor) (distribution.Descriptor, error) {
   194  	committed, err := bwl.BlobWriter.Commit(ctx, desc)
   195  	if err == nil {
   196  		if err := bwl.parent.parent.listener.BlobPushed(bwl.parent.parent.Repository.Name(), committed); err != nil {
   197  			context.GetLogger(ctx).Errorf("error dispatching blob push to listener: %v", err)
   198  		}
   199  	}
   200  
   201  	return committed, err
   202  }