Move layer interface definitions to distribution package

After consideration, it has been decided that the interfaces defined in the
storage package provide a good base for interacting with various registry
instances. Whether interacting with a remote API or a local, on-disk registry,
these types have proved flexible. By moving them here, they can become the
central components of interacting with distribution components.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2015-02-11 16:49:49 -08:00
parent 5a0f1ceeef
commit 553d48d618
20 changed files with 113 additions and 265 deletions

View file

@ -7,8 +7,10 @@ import (
"os"
"code.google.com/p/go-uuid/uuid"
"github.com/docker/distribution"
"github.com/docker/distribution/configuration"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/notifications"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/auth"
"github.com/docker/distribution/registry/storage"
@ -32,7 +34,7 @@ type App struct {
router *mux.Router // main application router, configured with dispatchers
driver storagedriver.StorageDriver // driver maintains the app global storage driver instance.
registry storage.Registry // registry is the primary registry backend for the app instance.
registry distribution.Registry // registry is the primary registry backend for the app instance.
accessController auth.AccessController // main access controller for application
// events contains notification related configuration.

View file

@ -4,10 +4,10 @@ import (
"fmt"
"net/http"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/storage"
"golang.org/x/net/context"
)
@ -21,7 +21,7 @@ type Context struct {
// Repository is the repository for the current request. All requests
// should be scoped to a single repository. This field may be nil.
Repository storage.Repository
Repository distribution.Repository
// Errors is a collection of errors encountered during the request to be
// returned to the client API. If errors are added to the collection, the

View file

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
@ -72,7 +73,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
case storage.ErrManifestVerification:
for _, verificationError := range err {
switch verificationError := verificationError.(type) {
case storage.ErrUnknownLayer:
case distribution.ErrUnknownLayer:
imh.Errors.Push(v2.ErrorCodeBlobUnknown, verificationError.FSLayer)
case storage.ErrManifestUnverified:
imh.Errors.Push(v2.ErrorCodeManifestUnverified)

View file

@ -3,10 +3,10 @@ package handlers
import (
"net/http"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/storage"
"github.com/gorilla/handlers"
)
@ -54,7 +54,7 @@ func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) {
if err != nil {
switch err := err.(type) {
case storage.ErrUnknownLayer:
case distribution.ErrUnknownLayer:
w.WriteHeader(http.StatusNotFound)
lh.Errors.Push(v2.ErrorCodeBlobUnknown, err.FSLayer)
default:

View file

@ -7,10 +7,10 @@ import (
"net/url"
"os"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/storage"
"github.com/gorilla/handlers"
)
@ -63,7 +63,7 @@ func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
upload, err := layers.Resume(luh.UUID)
if err != nil {
ctxu.GetLogger(ctx).Errorf("error resolving upload: %v", err)
if err == storage.ErrLayerUploadUnknown {
if err == distribution.ErrLayerUploadUnknown {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown, err)
@ -114,7 +114,7 @@ type layerUploadHandler struct {
// UUID identifies the upload instance for the current request.
UUID string
Upload storage.LayerUpload
Upload distribution.LayerUpload
State layerUploadState
}
@ -196,7 +196,7 @@ func (luh *layerUploadHandler) PutLayerUploadComplete(w http.ResponseWriter, r *
layer, err := luh.Upload.Finish(dgst)
if err != nil {
switch err := err.(type) {
case storage.ErrLayerInvalidDigest:
case distribution.ErrLayerInvalidDigest:
w.WriteHeader(http.StatusBadRequest)
luh.Errors.Push(v2.ErrorCodeDigestInvalid, err)
default:

View file

@ -10,6 +10,7 @@ import (
"time"
"github.com/AdRoll/goamz/cloudfront"
"github.com/docker/distribution"
storagedriver "github.com/docker/distribution/registry/storage/driver"
)
@ -95,7 +96,7 @@ func newCloudFrontLayerHandler(storageDriver storagedriver.StorageDriver, option
// Resolve returns an http.Handler which can serve the contents of the given
// Layer, or an error if not supported by the storagedriver.
func (lh *cloudFrontLayerHandler) Resolve(layer Layer) (http.Handler, error) {
func (lh *cloudFrontLayerHandler) Resolve(layer distribution.Layer) (http.Handler, error) {
layerURLStr, err := lh.delegateLayerHandler.urlFor(layer, nil)
if err != nil {
return nil, err

View file

@ -5,6 +5,7 @@ import (
"net/http"
"time"
"github.com/docker/distribution"
storagedriver "github.com/docker/distribution/registry/storage/driver"
)
@ -40,7 +41,7 @@ func newDelegateLayerHandler(storageDriver storagedriver.StorageDriver, options
// Resolve returns an http.Handler which can serve the contents of the given
// Layer, or an error if not supported by the storagedriver.
func (lh *delegateLayerHandler) Resolve(layer Layer) (http.Handler, error) {
func (lh *delegateLayerHandler) Resolve(layer distribution.Layer) (http.Handler, error) {
// TODO(bbland): This is just a sanity check to ensure that the
// storagedriver supports url generation. It would be nice if we didn't have
// to do this twice for non-GET requests.
@ -64,7 +65,7 @@ func (lh *delegateLayerHandler) Resolve(layer Layer) (http.Handler, error) {
// urlFor returns a download URL for the given layer, or the empty string if
// unsupported.
func (lh *delegateLayerHandler) urlFor(layer Layer, options map[string]interface{}) (string, error) {
func (lh *delegateLayerHandler) urlFor(layer distribution.Layer, options map[string]interface{}) (string, error) {
// Crack open the layer to get at the layerStore
layerRd, ok := layer.(*layerReader)
if !ok {

View file

@ -125,23 +125,8 @@ func (fr *fileReader) Seek(offset int64, whence int) (int64, error) {
return fr.offset, err
}
// Close the layer. Should be called when the resource is no longer needed.
func (fr *fileReader) Close() error {
if fr.err != nil {
return fr.err
}
fr.err = ErrLayerClosed
// close and release reader chain
if fr.rc != nil {
fr.rc.Close()
}
fr.rc = nil
fr.brd = nil
return fr.err
return fr.closeWithErr(fmt.Errorf("fileReader: closed"))
}
// reader prepares the current reader at the lrs offset, ensuring its buffered
@ -199,3 +184,21 @@ func (fr *fileReader) reset() {
fr.rc = nil
}
}
func (fr *fileReader) closeWithErr(err error) error {
if fr.err != nil {
return fr.err
}
fr.err = err
// close and release reader chain
if fr.rc != nil {
fr.rc.Close()
}
fr.rc = nil
fr.brd = nil
return fr.err
}

View file

@ -1,90 +0,0 @@
package storage
import (
"fmt"
"io"
"time"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
)
// Layer provides a readable and seekable layer object. Typically,
// implementations are *not* goroutine safe.
type Layer interface {
// http.ServeContent requires an efficient implementation of
// ReadSeeker.Seek(0, os.SEEK_END).
io.ReadSeeker
io.Closer
// Name returns the repository under which this layer is linked.
Name() string // TODO(stevvooe): struggling with nomenclature: should this be "repo" or "name"?
// Digest returns the unique digest of the blob, which is the tarsum for
// layers.
Digest() digest.Digest
// CreatedAt returns the time this layer was created.
CreatedAt() time.Time
}
// LayerUpload provides a handle for working with in-progress uploads.
// Instances can be obtained from the LayerService.Upload and
// LayerService.Resume.
type LayerUpload interface {
io.WriteSeeker
io.ReaderFrom
io.Closer
// Name of the repository under which the layer will be linked.
Name() string
// UUID returns the identifier for this upload.
UUID() string
// StartedAt returns the time this layer upload was started.
StartedAt() time.Time
// Finish marks the upload as completed, returning a valid handle to the
// uploaded layer. The digest is validated against the contents of the
// uploaded layer.
Finish(digest digest.Digest) (Layer, error)
// Cancel the layer upload process.
Cancel() error
}
var (
// ErrLayerExists returned when layer already exists
ErrLayerExists = fmt.Errorf("layer exists")
// ErrLayerTarSumVersionUnsupported when tarsum is unsupported version.
ErrLayerTarSumVersionUnsupported = fmt.Errorf("unsupported tarsum version")
// ErrLayerUploadUnknown returned when upload is not found.
ErrLayerUploadUnknown = fmt.Errorf("layer upload unknown")
// ErrLayerClosed returned when an operation is attempted on a closed
// Layer or LayerUpload.
ErrLayerClosed = fmt.Errorf("layer closed")
)
// ErrUnknownLayer returned when layer cannot be found.
type ErrUnknownLayer struct {
FSLayer manifest.FSLayer
}
func (err ErrUnknownLayer) Error() string {
return fmt.Sprintf("unknown layer %v", err.FSLayer.BlobSum)
}
// ErrLayerInvalidDigest returned when tarsum check fails.
type ErrLayerInvalidDigest struct {
Digest digest.Digest
Reason error
}
func (err ErrLayerInvalidDigest) Error() string {
return fmt.Sprintf("invalid digest for referenced layer: %v, %v",
err.Digest, err.Reason)
}

View file

@ -9,6 +9,7 @@ import (
"os"
"testing"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
storagedriver "github.com/docker/distribution/registry/storage/driver"
"github.com/docker/distribution/registry/storage/driver/inmemory"
@ -53,7 +54,7 @@ func TestSimpleLayerUpload(t *testing.T) {
// Do a resume, get unknown upload
layerUpload, err = ls.Resume(layerUpload.UUID())
if err != ErrLayerUploadUnknown {
if err != distribution.ErrLayerUploadUnknown {
t.Fatalf("unexpected error resuming upload, should be unkown: %v", err)
}
@ -102,7 +103,7 @@ func TestSimpleLayerUpload(t *testing.T) {
}
// After finishing an upload, it should no longer exist.
if _, err := ls.Resume(layerUpload.UUID()); err != ErrLayerUploadUnknown {
if _, err := ls.Resume(layerUpload.UUID()); err != distribution.ErrLayerUploadUnknown {
t.Fatalf("expected layer upload to be unknown, got %v", err)
}
@ -165,7 +166,7 @@ func TestSimpleLayerRead(t *testing.T) {
}
switch err.(type) {
case ErrUnknownLayer:
case distribution.ErrUnknownLayer:
err = nil
default:
t.Fatalf("unexpected error fetching non-existent layer: %v", err)

View file

@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"github.com/docker/distribution"
storagedriver "github.com/docker/distribution/registry/storage/driver"
)
@ -13,7 +14,7 @@ type LayerHandler interface {
// Layer if possible, or nil and an error when unsupported. This may
// directly serve the contents of the layer or issue a redirect to another
// URL hosting the content.
Resolve(layer Layer) (http.Handler, error)
Resolve(layer distribution.Layer) (http.Handler, error)
}
// LayerHandlerInitFunc is the type of a LayerHandler factory function and is

View file

@ -3,6 +3,7 @@ package storage
import (
"time"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
)
@ -15,7 +16,7 @@ type layerReader struct {
digest digest.Digest
}
var _ Layer = &layerReader{}
var _ distribution.Layer = &layerReader{}
func (lrs *layerReader) Name() string {
return lrs.name
@ -28,3 +29,8 @@ func (lrs *layerReader) Digest() digest.Digest {
func (lrs *layerReader) CreatedAt() time.Time {
return lrs.modtime
}
// Close the layer. Should be called when the resource is no longer needed.
func (lrs *layerReader) Close() error {
return lrs.closeWithErr(distribution.ErrLayerClosed)
}

View file

@ -4,6 +4,7 @@ import (
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
@ -23,7 +24,7 @@ func (ls *layerStore) Exists(digest digest.Digest) (bool, error) {
if err != nil {
switch err.(type) {
case ErrUnknownLayer:
case distribution.ErrUnknownLayer:
return false, nil
}
@ -33,7 +34,7 @@ func (ls *layerStore) Exists(digest digest.Digest) (bool, error) {
return true, nil
}
func (ls *layerStore) Fetch(dgst digest.Digest) (Layer, error) {
func (ls *layerStore) Fetch(dgst digest.Digest) (distribution.Layer, error) {
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Fetch")
bp, err := ls.path(dgst)
if err != nil {
@ -55,7 +56,7 @@ func (ls *layerStore) Fetch(dgst digest.Digest) (Layer, error) {
// Upload begins a layer upload, returning a handle. If the layer upload
// is already in progress or the layer has already been uploaded, this
// will return an error.
func (ls *layerStore) Upload() (LayerUpload, error) {
func (ls *layerStore) Upload() (distribution.LayerUpload, error) {
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Upload")
// NOTE(stevvooe): Consider the issues with allowing concurrent upload of
@ -93,7 +94,7 @@ func (ls *layerStore) Upload() (LayerUpload, error) {
// Resume continues an in progress layer upload, returning the current
// state of the upload.
func (ls *layerStore) Resume(uuid string) (LayerUpload, error) {
func (ls *layerStore) Resume(uuid string) (distribution.LayerUpload, error) {
ctxu.GetLogger(ls.repository.ctx).Debug("(*layerStore).Resume")
startedAtPath, err := ls.repository.registry.pm.path(uploadStartedAtPathSpec{
name: ls.repository.Name(),
@ -108,7 +109,7 @@ func (ls *layerStore) Resume(uuid string) (LayerUpload, error) {
if err != nil {
switch err := err.(type) {
case storagedriver.PathNotFoundError:
return nil, ErrLayerUploadUnknown
return nil, distribution.ErrLayerUploadUnknown
default:
return nil, err
}
@ -132,7 +133,7 @@ func (ls *layerStore) Resume(uuid string) (LayerUpload, error) {
}
// newLayerUpload allocates a new upload controller with the given state.
func (ls *layerStore) newLayerUpload(uuid, path string, startedAt time.Time) (LayerUpload, error) {
func (ls *layerStore) newLayerUpload(uuid, path string, startedAt time.Time) (distribution.LayerUpload, error) {
fw, err := newFileWriter(ls.repository.driver, path)
if err != nil {
return nil, err
@ -158,7 +159,9 @@ func (ls *layerStore) path(dgst digest.Digest) (string, error) {
if err != nil {
switch err := err.(type) {
case storagedriver.PathNotFoundError:
return "", ErrUnknownLayer{manifest.FSLayer{BlobSum: dgst}}
return "", distribution.ErrUnknownLayer{
FSLayer: manifest.FSLayer{BlobSum: dgst},
}
default:
return "", err
}

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
storagedriver "github.com/docker/distribution/registry/storage/driver"
@ -24,7 +25,7 @@ type layerUploadController struct {
fileWriter
}
var _ LayerUpload = &layerUploadController{}
var _ distribution.LayerUpload = &layerUploadController{}
// Name of the repository under which the layer will be linked.
func (luc *layerUploadController) Name() string {
@ -44,7 +45,7 @@ func (luc *layerUploadController) StartedAt() time.Time {
// uploaded layer. The final size and checksum are validated against the
// contents of the uploaded layer. The checksum should be provided in the
// format <algorithm>:<hex digest>.
func (luc *layerUploadController) Finish(digest digest.Digest) (Layer, error) {
func (luc *layerUploadController) Finish(digest digest.Digest) (distribution.Layer, error) {
ctxu.GetLogger(luc.layerStore.repository.ctx).Debug("(*layerUploadController).Finish")
canonical, err := luc.validateLayer(digest)
if err != nil {
@ -93,9 +94,9 @@ func (luc *layerUploadController) validateLayer(dgst digest.Digest) (digest.Dige
case tarsum.Version1:
default:
// version 0 and dev, for now.
return "", ErrLayerInvalidDigest{
return "", distribution.ErrLayerInvalidDigest{
Digest: dgst,
Reason: ErrLayerTarSumVersionUnsupported,
Reason: distribution.ErrLayerTarSumVersionUnsupported,
}
}
@ -124,7 +125,7 @@ func (luc *layerUploadController) validateLayer(dgst digest.Digest) (digest.Dige
}
if !digestVerifier.Verified() {
return "", ErrLayerInvalidDigest{
return "", distribution.ErrLayerInvalidDigest{
Digest: dgst,
Reason: fmt.Errorf("content does not match digest"),
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/docker/distribution"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
@ -71,7 +72,7 @@ type manifestStore struct {
tagStore *tagStore
}
var _ ManifestService = &manifestStore{}
var _ distribution.ManifestService = &manifestStore{}
// func (ms *manifestStore) Repository() Repository {
// return ms.repository
@ -177,7 +178,7 @@ func (ms *manifestStore) verifyManifest(tag string, mnfst *manifest.SignedManife
}
if !exists {
errs = append(errs, ErrUnknownLayer{FSLayer: fsLayer})
errs = append(errs, distribution.ErrUnknownLayer{FSLayer: fsLayer})
}
}

View file

@ -4,11 +4,10 @@ import (
"net/http"
"time"
"github.com/docker/distribution/manifest"
"code.google.com/p/go-uuid/uuid"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/registry/storage"
"github.com/docker/distribution/manifest"
)
type bridge struct {
@ -53,31 +52,31 @@ func NewRequestRecord(id string, r *http.Request) RequestRecord {
}
}
func (b *bridge) ManifestPushed(repo storage.Repository, sm *manifest.SignedManifest) error {
func (b *bridge) ManifestPushed(repo distribution.Repository, sm *manifest.SignedManifest) error {
return b.createManifestEventAndWrite(EventActionPush, repo, sm)
}
func (b *bridge) ManifestPulled(repo storage.Repository, sm *manifest.SignedManifest) error {
func (b *bridge) ManifestPulled(repo distribution.Repository, sm *manifest.SignedManifest) error {
return b.createManifestEventAndWrite(EventActionPull, repo, sm)
}
func (b *bridge) ManifestDeleted(repo storage.Repository, sm *manifest.SignedManifest) error {
func (b *bridge) ManifestDeleted(repo distribution.Repository, sm *manifest.SignedManifest) error {
return b.createManifestEventAndWrite(EventActionDelete, repo, sm)
}
func (b *bridge) LayerPushed(repo storage.Repository, layer storage.Layer) error {
func (b *bridge) LayerPushed(repo distribution.Repository, layer distribution.Layer) error {
return b.createLayerEventAndWrite(EventActionPush, repo, layer.Digest())
}
func (b *bridge) LayerPulled(repo storage.Repository, layer storage.Layer) error {
func (b *bridge) LayerPulled(repo distribution.Repository, layer distribution.Layer) error {
return b.createLayerEventAndWrite(EventActionPull, repo, layer.Digest())
}
func (b *bridge) LayerDeleted(repo storage.Repository, layer storage.Layer) error {
func (b *bridge) LayerDeleted(repo distribution.Repository, layer distribution.Layer) error {
return b.createLayerEventAndWrite(EventActionDelete, repo, layer.Digest())
}
func (b *bridge) createManifestEventAndWrite(action string, repo storage.Repository, sm *manifest.SignedManifest) error {
func (b *bridge) createManifestEventAndWrite(action string, repo distribution.Repository, sm *manifest.SignedManifest) error {
event, err := b.createManifestEvent(action, repo, sm)
if err != nil {
return err
@ -86,7 +85,7 @@ func (b *bridge) createManifestEventAndWrite(action string, repo storage.Reposit
return b.sink.Write(*event)
}
func (b *bridge) createManifestEvent(action string, repo storage.Repository, sm *manifest.SignedManifest) (*Event, error) {
func (b *bridge) createManifestEvent(action string, repo distribution.Repository, sm *manifest.SignedManifest) (*Event, error) {
event := b.createEvent(action)
event.Target.Type = EventTargetTypeManifest
event.Target.Name = repo.Name()
@ -112,7 +111,7 @@ func (b *bridge) createManifestEvent(action string, repo storage.Repository, sm
return event, nil
}
func (b *bridge) createLayerEventAndWrite(action string, repo storage.Repository, dgst digest.Digest) error {
func (b *bridge) createLayerEventAndWrite(action string, repo distribution.Repository, dgst digest.Digest) error {
event, err := b.createLayerEvent(action, repo, dgst)
if err != nil {
return err
@ -121,7 +120,7 @@ func (b *bridge) createLayerEventAndWrite(action string, repo storage.Repository
return b.sink.Write(*event)
}
func (b *bridge) createLayerEvent(action string, repo storage.Repository, dgst digest.Digest) (*Event, error) {
func (b *bridge) createLayerEvent(action string, repo distribution.Repository, dgst digest.Digest) (*Event, error) {
event := b.createEvent(action)
event.Target.Type = EventTargetTypeBlob
event.Target.Name = repo.Name()

View file

@ -2,31 +2,31 @@ package notifications
import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/registry/storage"
)
// ManifestListener describes a set of methods for listening to events related to manifests.
type ManifestListener interface {
ManifestPushed(repo storage.Repository, sm *manifest.SignedManifest) error
ManifestPulled(repo storage.Repository, sm *manifest.SignedManifest) error
ManifestPushed(repo distribution.Repository, sm *manifest.SignedManifest) error
ManifestPulled(repo distribution.Repository, sm *manifest.SignedManifest) error
// TODO(stevvooe): Please note that delete support is still a little shaky
// and we'll need to propagate these in the future.
ManifestDeleted(repo storage.Repository, sm *manifest.SignedManifest) error
ManifestDeleted(repo distribution.Repository, sm *manifest.SignedManifest) error
}
// LayerListener describes a listener that can respond to layer related events.
type LayerListener interface {
LayerPushed(repo storage.Repository, layer storage.Layer) error
LayerPulled(repo storage.Repository, layer storage.Layer) error
LayerPushed(repo distribution.Repository, layer distribution.Layer) error
LayerPulled(repo distribution.Repository, layer distribution.Layer) error
// TODO(stevvooe): Please note that delete support is still a little shaky
// and we'll need to propagate these in the future.
LayerDeleted(repo storage.Repository, layer storage.Layer) error
LayerDeleted(repo distribution.Repository, layer distribution.Layer) error
}
// Listener combines all repository events into a single interface.
@ -36,26 +36,26 @@ type Listener interface {
}
type repositoryListener struct {
storage.Repository
distribution.Repository
listener Listener
}
// Listen dispatches events on the repository to the listener.
func Listen(repo storage.Repository, listener Listener) storage.Repository {
func Listen(repo distribution.Repository, listener Listener) distribution.Repository {
return &repositoryListener{
Repository: repo,
listener: listener,
}
}
func (rl *repositoryListener) Manifests() storage.ManifestService {
func (rl *repositoryListener) Manifests() distribution.ManifestService {
return &manifestServiceListener{
ManifestService: rl.Repository.Manifests(),
parent: rl,
}
}
func (rl *repositoryListener) Layers() storage.LayerService {
func (rl *repositoryListener) Layers() distribution.LayerService {
return &layerServiceListener{
LayerService: rl.Repository.Layers(),
parent: rl,
@ -63,7 +63,7 @@ func (rl *repositoryListener) Layers() storage.LayerService {
}
type manifestServiceListener struct {
storage.ManifestService
distribution.ManifestService
parent *repositoryListener
}
@ -91,11 +91,11 @@ func (msl *manifestServiceListener) Put(tag string, sm *manifest.SignedManifest)
}
type layerServiceListener struct {
storage.LayerService
distribution.LayerService
parent *repositoryListener
}
func (lsl *layerServiceListener) Fetch(dgst digest.Digest) (storage.Layer, error) {
func (lsl *layerServiceListener) Fetch(dgst digest.Digest) (distribution.Layer, error) {
layer, err := lsl.LayerService.Fetch(dgst)
if err == nil {
if err := lsl.parent.listener.LayerPulled(lsl.parent.Repository, layer); err != nil {
@ -106,17 +106,17 @@ func (lsl *layerServiceListener) Fetch(dgst digest.Digest) (storage.Layer, error
return layer, err
}
func (lsl *layerServiceListener) Upload() (storage.LayerUpload, error) {
func (lsl *layerServiceListener) Upload() (distribution.LayerUpload, error) {
lu, err := lsl.LayerService.Upload()
return lsl.decorateUpload(lu), err
}
func (lsl *layerServiceListener) Resume(uuid string) (storage.LayerUpload, error) {
func (lsl *layerServiceListener) Resume(uuid string) (distribution.LayerUpload, error) {
lu, err := lsl.LayerService.Resume(uuid)
return lsl.decorateUpload(lu), err
}
func (lsl *layerServiceListener) decorateUpload(lu storage.LayerUpload) storage.LayerUpload {
func (lsl *layerServiceListener) decorateUpload(lu distribution.LayerUpload) distribution.LayerUpload {
return &layerUploadListener{
LayerUpload: lu,
parent: lsl,
@ -124,11 +124,11 @@ func (lsl *layerServiceListener) decorateUpload(lu storage.LayerUpload) storage.
}
type layerUploadListener struct {
storage.LayerUpload
distribution.LayerUpload
parent *layerServiceListener
}
func (lul *layerUploadListener) Finish(dgst digest.Digest) (storage.Layer, error) {
func (lul *layerUploadListener) Finish(dgst digest.Digest) (distribution.Layer, error) {
layer, err := lul.LayerUpload.Finish(dgst)
if err == nil {
if err := lul.parent.parent.listener.LayerPushed(lul.parent.parent.Repository, layer); err != nil {

View file

@ -5,6 +5,7 @@ import (
"reflect"
"testing"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/registry/storage"
@ -44,40 +45,40 @@ type testListener struct {
ops map[string]int
}
func (tl *testListener) ManifestPushed(repo storage.Repository, sm *manifest.SignedManifest) error {
func (tl *testListener) ManifestPushed(repo distribution.Repository, sm *manifest.SignedManifest) error {
tl.ops["manifest:push"]++
return nil
}
func (tl *testListener) ManifestPulled(repo storage.Repository, sm *manifest.SignedManifest) error {
func (tl *testListener) ManifestPulled(repo distribution.Repository, sm *manifest.SignedManifest) error {
tl.ops["manifest:pull"]++
return nil
}
func (tl *testListener) ManifestDeleted(repo storage.Repository, sm *manifest.SignedManifest) error {
func (tl *testListener) ManifestDeleted(repo distribution.Repository, sm *manifest.SignedManifest) error {
tl.ops["manifest:delete"]++
return nil
}
func (tl *testListener) LayerPushed(repo storage.Repository, layer storage.Layer) error {
func (tl *testListener) LayerPushed(repo distribution.Repository, layer distribution.Layer) error {
tl.ops["layer:push"]++
return nil
}
func (tl *testListener) LayerPulled(repo storage.Repository, layer storage.Layer) error {
func (tl *testListener) LayerPulled(repo distribution.Repository, layer distribution.Layer) error {
tl.ops["layer:pull"]++
return nil
}
func (tl *testListener) LayerDeleted(repo storage.Repository, layer storage.Layer) error {
func (tl *testListener) LayerDeleted(repo distribution.Repository, layer distribution.Layer) error {
tl.ops["layer:delete"]++
return nil
}
// checkExerciseRegistry takes the registry through all of its operations,
// carrying out generic checks.
func checkExerciseRepository(t *testing.T, repository storage.Repository) {
func checkExerciseRepository(t *testing.T, repository distribution.Repository) {
// 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

View file

@ -1,6 +1,7 @@
package storage
import (
"github.com/docker/distribution"
storagedriver "github.com/docker/distribution/registry/storage/driver"
"golang.org/x/net/context"
)
@ -16,7 +17,7 @@ type registry struct {
// NewRegistryWithDriver creates a new registry instance from the provided
// driver. The resulting registry may be shared by multiple goroutines but is
// cheap to allocate.
func NewRegistryWithDriver(driver storagedriver.StorageDriver) Registry {
func NewRegistryWithDriver(driver storagedriver.StorageDriver) distribution.Registry {
bs := &blobStore{}
reg := &registry{
@ -35,7 +36,7 @@ func NewRegistryWithDriver(driver storagedriver.StorageDriver) Registry {
// Repository returns an instance of the repository tied to the registry.
// Instances should not be shared between goroutines but are cheap to
// allocate. In general, they should be request scoped.
func (reg *registry) Repository(ctx context.Context, name string) Repository {
func (reg *registry) Repository(ctx context.Context, name string) distribution.Repository {
return &repository{
ctx: ctx,
registry: reg,
@ -58,7 +59,7 @@ func (repo *repository) Name() string {
// Manifests returns an instance of ManifestService. Instantiation is cheap and
// may be context sensitive in the future. The instance should be used similar
// to a request local.
func (repo *repository) Manifests() ManifestService {
func (repo *repository) Manifests() distribution.ManifestService {
return &manifestStore{
repository: repo,
revisionStore: &revisionStore{
@ -73,7 +74,7 @@ func (repo *repository) Manifests() ManifestService {
// Layers returns an instance of the LayerService. Instantiation is cheap and
// may be context sensitive in the future. The instance should be used similar
// to a request local.
func (repo *repository) Layers() LayerService {
func (repo *repository) Layers() distribution.LayerService {
return &layerStore{
repository: repo,
}

View file

@ -1,84 +0,0 @@
package storage
import (
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
"golang.org/x/net/context"
)
// TODO(stevvooe): These types need to be moved out of the storage package.
// Registry represents a collection of repositories, addressable by name.
type Registry interface {
// Repository should return a reference to the named repository. The
// registry may or may not have the repository but should always return a
// reference.
Repository(ctx context.Context, name string) Repository
}
// Repository is a named collection of manifests and layers.
type Repository interface {
// Name returns the name of the repository.
Name() string
// Manifests returns a reference to this repository's manifest service.
Manifests() ManifestService
// Layers returns a reference to this repository's layers service.
Layers() LayerService
}
// ManifestService provides operations on image manifests.
type ManifestService interface {
// Tags lists the tags under the named repository.
Tags() ([]string, error)
// Exists returns true if the manifest exists.
Exists(tag string) (bool, error)
// Get retrieves the named manifest, if it exists.
Get(tag string) (*manifest.SignedManifest, error)
// Put creates or updates the named manifest.
// Put(tag string, manifest *manifest.SignedManifest) (digest.Digest, error)
Put(tag string, manifest *manifest.SignedManifest) error
// Delete removes the named manifest, if it exists.
Delete(tag string) error
// TODO(stevvooe): There are several changes that need to be done to this
// interface:
//
// 1. Get(tag string) should be GetByTag(tag string)
// 2. Put(tag string, manifest *manifest.SignedManifest) should be
// Put(manifest *manifest.SignedManifest). The method can read the
// tag on manifest to automatically tag it in the repository.
// 3. Need a GetByDigest(dgst digest.Digest) method.
// 4. Allow explicit tagging with Tag(digest digest.Digest, tag string)
// 5. Support reading tags with a re-entrant reader to avoid large
// allocations in the registry.
// 6. Long-term: Provide All() method that lets one scroll through all of
// the manifest entries.
// 7. Long-term: break out concept of signing from manifests. This is
// really a part of the distribution sprint.
// 8. Long-term: Manifest should be an interface. This code shouldn't
// really be concerned with the storage format.
}
// LayerService provides operations on layer files in a backend storage.
type LayerService interface {
// Exists returns true if the layer exists.
Exists(digest digest.Digest) (bool, error)
// Fetch the layer identifed by TarSum.
Fetch(digest digest.Digest) (Layer, error)
// Upload begins a layer upload to repository identified by name,
// returning a handle.
Upload() (LayerUpload, error)
// Resume continues an in progress layer upload, returning a handle to the
// upload. The caller should seek to the latest desired upload location
// before proceeding.
Resume(uuid string) (LayerUpload, error)
}