Merge pull request #174 from stevvooe/registry-interface-improvements
Add error return to Repository method on Registry
This commit is contained in:
commit
61fb9ae16a
14 changed files with 236 additions and 174 deletions
109
errors.go
Normal file
109
errors.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package distribution
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
)
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
// ErrRepositoryUnknown is returned if the named repository is not known by
|
||||
// the registry.
|
||||
type ErrRepositoryUnknown struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (err ErrRepositoryUnknown) Error() string {
|
||||
return fmt.Sprintf("unknown respository name=%s", err.Name)
|
||||
}
|
||||
|
||||
// ErrRepositoryNameInvalid should be used to denote an invalid repository
|
||||
// name. Reason may set, indicating the cause of invalidity.
|
||||
type ErrRepositoryNameInvalid struct {
|
||||
Name string
|
||||
Reason error
|
||||
}
|
||||
|
||||
func (err ErrRepositoryNameInvalid) Error() string {
|
||||
return fmt.Sprintf("repository name %q invalid: %v", err.Name, err.Reason)
|
||||
}
|
||||
|
||||
// ErrManifestUnknown is returned if the manifest is not known by the
|
||||
// registry.
|
||||
type ErrManifestUnknown struct {
|
||||
Name string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (err ErrManifestUnknown) Error() string {
|
||||
return fmt.Sprintf("unknown manifest name=%s tag=%s", err.Name, err.Tag)
|
||||
}
|
||||
|
||||
// ErrUnknownManifestRevision is returned when a manifest cannot be found by
|
||||
// revision within a repository.
|
||||
type ErrUnknownManifestRevision struct {
|
||||
Name string
|
||||
Revision digest.Digest
|
||||
}
|
||||
|
||||
func (err ErrUnknownManifestRevision) Error() string {
|
||||
return fmt.Sprintf("unknown manifest name=%s revision=%s", err.Name, err.Revision)
|
||||
}
|
||||
|
||||
// ErrManifestUnverified is returned when the registry is unable to verify
|
||||
// the manifest.
|
||||
type ErrManifestUnverified struct{}
|
||||
|
||||
func (ErrManifestUnverified) Error() string {
|
||||
return fmt.Sprintf("unverified manifest")
|
||||
}
|
||||
|
||||
// ErrManifestVerification provides a type to collect errors encountered
|
||||
// during manifest verification. Currently, it accepts errors of all types,
|
||||
// but it may be narrowed to those involving manifest verification.
|
||||
type ErrManifestVerification []error
|
||||
|
||||
func (errs ErrManifestVerification) Error() string {
|
||||
var parts []string
|
||||
for _, err := range errs {
|
||||
parts = append(parts, err.Error())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("errors verifying manifest: %v", strings.Join(parts, ","))
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
84
layer.go
84
layer.go
|
@ -1,84 +0,0 @@
|
|||
package distribution
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
}
|
|
@ -21,7 +21,11 @@ func TestListener(t *testing.T) {
|
|||
ops: make(map[string]int),
|
||||
}
|
||||
ctx := context.Background()
|
||||
repository := Listen(registry.Repository(ctx, "foo/bar"), tl)
|
||||
repository, err := registry.Repository(ctx, "foo/bar")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
}
|
||||
repository = Listen(repository, tl)
|
||||
|
||||
// Now take the registry through a number of operations
|
||||
checkExerciseRepository(t, repository)
|
||||
|
|
46
registry.go
46
registry.go
|
@ -1,19 +1,20 @@
|
|||
package distribution
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"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(ctx context.Context, name string) (Repository, error)
|
||||
}
|
||||
|
||||
// Repository is a named collection of manifests and layers.
|
||||
|
@ -82,3 +83,42 @@ type LayerService interface {
|
|||
// before proceeding.
|
||||
Resume(uuid string) (LayerUpload, error)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// TODO(stevvooe): Move these definitions back to an exported package. While
|
||||
// they are used with v2 definitions, their relevance expands beyond.
|
||||
// "distribution/names" is a candidate package.
|
||||
|
||||
const (
|
||||
// RepositoryNameComponentMinLength is the minimum number of characters in a
|
||||
// single repository name slash-delimited component
|
||||
|
@ -37,10 +41,6 @@ var RepositoryNameComponentRegexp = regexp.MustCompile(`[a-z0-9]+(?:[._-][a-z0-9
|
|||
// RepositoryNameComponentRegexp which must completely match the content
|
||||
var RepositoryNameComponentAnchoredRegexp = regexp.MustCompile(`^` + RepositoryNameComponentRegexp.String() + `$`)
|
||||
|
||||
// TODO(stevvooe): RepositoryName needs to be limited to some fixed length.
|
||||
// Looking path prefixes and s3 limitation of 1024, this should likely be
|
||||
// around 512 bytes. 256 bytes might be more manageable.
|
||||
|
||||
// RepositoryNameRegexp builds on RepositoryNameComponentRegexp to allow 1 to
|
||||
// 5 path components, separated by a forward slash.
|
||||
var RepositoryNameRegexp = regexp.MustCompile(`(?:` + RepositoryNameComponentRegexp.String() + `/){0,4}` + RepositoryNameComponentRegexp.String())
|
||||
|
|
|
@ -227,10 +227,30 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
// decorate the authorized repository with an event bridge.
|
||||
context.Repository = notifications.Listen(
|
||||
app.registry.Repository(context, getName(context)),
|
||||
app.eventBridge(context, r))
|
||||
if app.nameRequired(r) {
|
||||
repository, err := app.registry.Repository(context, getName(context))
|
||||
|
||||
if err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error resolving repository: %v", err)
|
||||
|
||||
switch err := err.(type) {
|
||||
case distribution.ErrRepositoryUnknown:
|
||||
context.Errors.Push(v2.ErrorCodeNameUnknown, err)
|
||||
case distribution.ErrRepositoryNameInvalid:
|
||||
context.Errors.Push(v2.ErrorCodeNameInvalid, err)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
serveJSON(w, context.Errors)
|
||||
return
|
||||
}
|
||||
|
||||
// assign and decorate the authorized repository with an event bridge.
|
||||
context.Repository = notifications.Listen(
|
||||
repository,
|
||||
app.eventBridge(context, r))
|
||||
}
|
||||
|
||||
handler := dispatch(context, r)
|
||||
|
||||
ssrw := &singleStatusResponseWriter{ResponseWriter: w}
|
||||
|
@ -318,9 +338,7 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont
|
|||
}
|
||||
} else {
|
||||
// Only allow the name not to be set on the base route.
|
||||
route := mux.CurrentRoute(r)
|
||||
|
||||
if route == nil || route.GetName() != v2.RouteNameBase {
|
||||
if app.nameRequired(r) {
|
||||
// For this to be properly secured, repo must always be set for a
|
||||
// resource that may make a modification. The only condition under
|
||||
// which name is not set and we still allow access is when the
|
||||
|
@ -378,6 +396,12 @@ func (app *App) eventBridge(ctx *Context, r *http.Request) notifications.Listene
|
|||
return notifications.NewBridge(ctx.urlBuilder, app.events.source, actor, request, app.events.sink)
|
||||
}
|
||||
|
||||
// nameRequired returns true if the route requires a name.
|
||||
func (app *App) nameRequired(r *http.Request) bool {
|
||||
route := mux.CurrentRoute(r)
|
||||
return route == nil || route.GetName() != v2.RouteNameBase
|
||||
}
|
||||
|
||||
// apiBase implements a simple yes-man for doing overall checks against the
|
||||
// api. This can support auth roundtrips to support docker login.
|
||||
func apiBase(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/storage"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
|
@ -70,12 +69,12 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
|||
// TODO(stevvooe): These error handling switches really need to be
|
||||
// handled by an app global mapper.
|
||||
switch err := err.(type) {
|
||||
case storage.ErrManifestVerification:
|
||||
case distribution.ErrManifestVerification:
|
||||
for _, verificationError := range err {
|
||||
switch verificationError := verificationError.(type) {
|
||||
case distribution.ErrUnknownLayer:
|
||||
imh.Errors.Push(v2.ErrorCodeBlobUnknown, verificationError.FSLayer)
|
||||
case storage.ErrManifestUnverified:
|
||||
case distribution.ErrManifestUnverified:
|
||||
imh.Errors.Push(v2.ErrorCodeManifestUnverified)
|
||||
default:
|
||||
if verificationError == digest.ErrDigestInvalidFormat {
|
||||
|
@ -104,7 +103,7 @@ func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *h
|
|||
manifests := imh.Repository.Manifests()
|
||||
if err := manifests.Delete(imh.Tag); err != nil {
|
||||
switch err := err.(type) {
|
||||
case storage.ErrUnknownManifest:
|
||||
case distribution.ErrManifestUnknown:
|
||||
imh.Errors.Push(v2.ErrorCodeManifestUnknown, err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
default:
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/docker/distribution/registry/storage"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
|
@ -38,7 +38,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) {
|
|||
tags, err := manifests.Tags()
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case storage.ErrUnknownRepository:
|
||||
case distribution.ErrRepositoryUnknown:
|
||||
w.WriteHeader(404)
|
||||
th.Errors.Push(v2.ErrorCodeNameUnknown, map[string]string{"name": th.Repository.Name()})
|
||||
default:
|
||||
|
|
|
@ -36,7 +36,11 @@ func TestSimpleLayerUpload(t *testing.T) {
|
|||
imageName := "foo/bar"
|
||||
driver := inmemory.New()
|
||||
registry := NewRegistryWithDriver(driver)
|
||||
ls := registry.Repository(ctx, imageName).Layers()
|
||||
repository, err := registry.Repository(ctx, imageName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
}
|
||||
ls := repository.Layers()
|
||||
|
||||
h := sha256.New()
|
||||
rd := io.TeeReader(randomDataReader, h)
|
||||
|
@ -140,7 +144,11 @@ func TestSimpleLayerRead(t *testing.T) {
|
|||
imageName := "foo/bar"
|
||||
driver := inmemory.New()
|
||||
registry := NewRegistryWithDriver(driver)
|
||||
ls := registry.Repository(ctx, imageName).Layers()
|
||||
repository, err := registry.Repository(ctx, imageName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
}
|
||||
ls := repository.Layers()
|
||||
|
||||
randomLayerReader, tarSumStr, err := testutil.CreateRandomTarFile()
|
||||
if err != nil {
|
||||
|
@ -245,7 +253,11 @@ func TestLayerUploadZeroLength(t *testing.T) {
|
|||
imageName := "foo/bar"
|
||||
driver := inmemory.New()
|
||||
registry := NewRegistryWithDriver(driver)
|
||||
ls := registry.Repository(ctx, imageName).Layers()
|
||||
repository, err := registry.Repository(ctx, imageName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
}
|
||||
ls := repository.Layers()
|
||||
|
||||
upload, err := ls.Upload()
|
||||
if err != nil {
|
||||
|
|
|
@ -2,69 +2,13 @@ package storage
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
// ErrUnknownRepository is returned if the named repository is not known by
|
||||
// the registry.
|
||||
type ErrUnknownRepository struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (err ErrUnknownRepository) Error() string {
|
||||
return fmt.Sprintf("unknown respository name=%s", err.Name)
|
||||
}
|
||||
|
||||
// ErrUnknownManifest is returned if the manifest is not known by the
|
||||
// registry.
|
||||
type ErrUnknownManifest struct {
|
||||
Name string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (err ErrUnknownManifest) Error() string {
|
||||
return fmt.Sprintf("unknown manifest name=%s tag=%s", err.Name, err.Tag)
|
||||
}
|
||||
|
||||
// ErrUnknownManifestRevision is returned when a manifest cannot be found by
|
||||
// revision within a repository.
|
||||
type ErrUnknownManifestRevision struct {
|
||||
Name string
|
||||
Revision digest.Digest
|
||||
}
|
||||
|
||||
func (err ErrUnknownManifestRevision) Error() string {
|
||||
return fmt.Sprintf("unknown manifest name=%s revision=%s", err.Name, err.Revision)
|
||||
}
|
||||
|
||||
// ErrManifestUnverified is returned when the registry is unable to verify
|
||||
// the manifest.
|
||||
type ErrManifestUnverified struct{}
|
||||
|
||||
func (ErrManifestUnverified) Error() string {
|
||||
return fmt.Sprintf("unverified manifest")
|
||||
}
|
||||
|
||||
// ErrManifestVerification provides a type to collect errors encountered
|
||||
// during manifest verification. Currently, it accepts errors of all types,
|
||||
// but it may be narrowed to those involving manifest verification.
|
||||
type ErrManifestVerification []error
|
||||
|
||||
func (errs ErrManifestVerification) Error() string {
|
||||
var parts []string
|
||||
for _, err := range errs {
|
||||
parts = append(parts, err.Error())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("errors verifying manifest: %v", strings.Join(parts, ","))
|
||||
}
|
||||
|
||||
type manifestStore struct {
|
||||
repository *repository
|
||||
|
||||
|
@ -147,7 +91,7 @@ func (ms *manifestStore) Delete(tag string) error {
|
|||
// registry only tries to store valid content, leaving trust policies of that
|
||||
// content up to consumers.
|
||||
func (ms *manifestStore) verifyManifest(tag string, mnfst *manifest.SignedManifest) error {
|
||||
var errs ErrManifestVerification
|
||||
var errs distribution.ErrManifestVerification
|
||||
if mnfst.Name != ms.repository.Name() {
|
||||
// TODO(stevvooe): This needs to be an exported error
|
||||
errs = append(errs, fmt.Errorf("repository name does not match manifest name"))
|
||||
|
@ -161,10 +105,10 @@ func (ms *manifestStore) verifyManifest(tag string, mnfst *manifest.SignedManife
|
|||
if _, err := manifest.Verify(mnfst); err != nil {
|
||||
switch err {
|
||||
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
||||
errs = append(errs, ErrManifestUnverified{})
|
||||
errs = append(errs, distribution.ErrManifestUnverified{})
|
||||
default:
|
||||
if err.Error() == "invalid signature" { // TODO(stevvooe): This should be exported by libtrust
|
||||
errs = append(errs, ErrManifestUnverified{})
|
||||
errs = append(errs, distribution.ErrManifestUnverified{})
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
|
@ -20,7 +21,10 @@ func TestManifestStorage(t *testing.T) {
|
|||
tag := "thetag"
|
||||
driver := inmemory.New()
|
||||
registry := NewRegistryWithDriver(driver)
|
||||
repo := registry.Repository(ctx, name)
|
||||
repo, err := registry.Repository(ctx, name)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting repo: %v", err)
|
||||
}
|
||||
ms := repo.Manifests()
|
||||
|
||||
exists, err := ms.Exists(tag)
|
||||
|
@ -34,7 +38,7 @@ func TestManifestStorage(t *testing.T) {
|
|||
|
||||
if _, err := ms.Get(tag); true {
|
||||
switch err.(type) {
|
||||
case ErrUnknownManifest:
|
||||
case distribution.ErrManifestUnknown:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("expected manifest unknown error: %#v", err)
|
||||
|
|
|
@ -2,6 +2,7 @@ package storage
|
|||
|
||||
import (
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
@ -36,12 +37,19 @@ func NewRegistryWithDriver(driver storagedriver.StorageDriver) distribution.Regi
|
|||
// 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) distribution.Repository {
|
||||
func (reg *registry) Repository(ctx context.Context, name string) (distribution.Repository, error) {
|
||||
if err := v2.ValidateRespositoryName(name); err != nil {
|
||||
return nil, distribution.ErrRepositoryNameInvalid{
|
||||
Name: name,
|
||||
Reason: err,
|
||||
}
|
||||
}
|
||||
|
||||
return &repository{
|
||||
ctx: ctx,
|
||||
registry: reg,
|
||||
name: name,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// repository provides name-scoped access to various services.
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"path"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest"
|
||||
"github.com/docker/libtrust"
|
||||
|
@ -40,7 +41,7 @@ func (rs *revisionStore) get(revision digest.Digest) (*manifest.SignedManifest,
|
|||
if exists, err := rs.exists(revision); err != nil {
|
||||
return nil, err
|
||||
} else if !exists {
|
||||
return nil, ErrUnknownManifestRevision{
|
||||
return nil, distribution.ErrUnknownManifestRevision{
|
||||
Name: rs.Name(),
|
||||
Revision: revision,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package storage
|
|||
import (
|
||||
"path"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/digest"
|
||||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||
)
|
||||
|
@ -26,7 +27,7 @@ func (ts *tagStore) tags() ([]string, error) {
|
|||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case storagedriver.PathNotFoundError:
|
||||
return nil, ErrUnknownRepository{Name: ts.name}
|
||||
return nil, distribution.ErrRepositoryUnknown{Name: ts.name}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ func (ts *tagStore) resolve(tag string) (digest.Digest, error) {
|
|||
if exists, err := exists(ts.driver, currentPath); err != nil {
|
||||
return "", err
|
||||
} else if !exists {
|
||||
return "", ErrUnknownManifest{Name: ts.Name(), Tag: tag}
|
||||
return "", distribution.ErrManifestUnknown{Name: ts.Name(), Tag: tag}
|
||||
}
|
||||
|
||||
revision, err := ts.blobStore.readlink(currentPath)
|
||||
|
|
Loading…
Reference in a new issue