Athens: move storage.NotFound to errors.NotFound (#373)

* Athens: move storage.NotFound to errors.NotFound

* see where things went wrong

* empty means unexpected

* GCP: replace AlreadyExists err

* resolve conflicts

* Errors: change not found name

* fix build
This commit is contained in:
Marwan Sulaiman
2018-08-01 03:27:16 -04:00
committed by GitHub
parent 22c1d86411
commit 90281ccb90
21 changed files with 90 additions and 127 deletions
+4 -4
View File
@@ -49,7 +49,7 @@ func (p *protocol) List(ctx context.Context, mod string) ([]string, error) {
func (p *protocol) Info(ctx context.Context, mod, ver string) ([]byte, error) {
const op errors.Op = "protocol.Info"
v, err := p.s.Get(ctx, mod, ver)
if storage.IsNotFoundError(err) {
if errors.IsNotFoundErr(err) {
v, err = p.fillCache(ctx, mod, ver)
}
if err != nil {
@@ -87,7 +87,7 @@ func (p *protocol) Latest(ctx context.Context, mod string) (*storage.RevInfo, er
func (p *protocol) GoMod(ctx context.Context, mod, ver string) ([]byte, error) {
const op errors.Op = "protocol.GoMod"
v, err := p.s.Get(ctx, mod, ver)
if storage.IsNotFoundError(err) {
if errors.IsNotFoundErr(err) {
v, err = p.fillCache(ctx, mod, ver)
}
if err != nil {
@@ -100,7 +100,7 @@ func (p *protocol) GoMod(ctx context.Context, mod, ver string) ([]byte, error) {
func (p *protocol) Zip(ctx context.Context, mod, ver string) (io.ReadCloser, error) {
const op errors.Op = "protocol.Zip"
v, err := p.s.Get(ctx, mod, ver)
if storage.IsNotFoundError(err) {
if errors.IsNotFoundErr(err) {
v, err = p.fillCache(ctx, mod, ver)
}
if err != nil {
@@ -113,7 +113,7 @@ func (p *protocol) Zip(ctx context.Context, mod, ver string) (io.ReadCloser, err
func (p *protocol) Version(ctx context.Context, mod, ver string) (*storage.Version, error) {
const op errors.Op = "protocol.Version"
v, err := p.s.Get(ctx, mod, ver)
if errors.ErrNotFound(err) {
if errors.IsNotFoundErr(err) {
v, err = p.fillCache(ctx, mod, ver)
}
if err != nil {
+1 -1
View File
@@ -91,7 +91,7 @@ func E(op Op, args ...interface{}) error {
}
}
if e.Err == nil {
e.Err = errors.New("no error message provided")
e.Err = errors.New(KindText(e))
}
return e
}
+2 -2
View File
@@ -1,6 +1,6 @@
package errors
// ErrNotFound helper function for KindNotFound
func ErrNotFound(err error) bool {
// IsNotFoundErr helper function for KindNotFound
func IsNotFoundErr(err error) bool {
return Kind(err) == KindNotFound
}
+1 -1
View File
@@ -4,7 +4,7 @@ import "context"
// Deleter deletes module metadata and its source from underlying storage
type Deleter interface {
// Delete must return ErrVersionNotFound if the module/version are not
// Delete must return ErrNotFound if the module/version are not
// found.
Delete(ctx context.Context, module, vsn string) error
}
+3 -5
View File
@@ -3,20 +3,18 @@ package fs
import (
"context"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
opentracing "github.com/opentracing/opentracing-go"
)
// Delete removes a specific version of a module.
func (v *storageImpl) Delete(ctx context.Context, module, version string) error {
const op errors.Op = "fs.Delete"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.fs.Delete")
defer sp.Finish()
versionedPath := v.versionLocation(module, version)
if !v.Exists(ctx, module, version) {
return storage.ErrVersionNotFound{
Module: module,
Version: version,
}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
return v.filesystem.RemoveAll(versionedPath)
}
+6 -5
View File
@@ -5,29 +5,30 @@ import (
"os"
"path/filepath"
"github.com/opentracing/opentracing-go"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
"github.com/opentracing/opentracing-go"
"github.com/spf13/afero"
)
func (v *storageImpl) Get(ctx context.Context, module, version string) (*storage.Version, error) {
const op errors.Op = "fs.Get"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.fs.Get")
defer sp.Finish()
versionedPath := v.versionLocation(module, version)
mod, err := afero.ReadFile(v.filesystem, filepath.Join(versionedPath, "go.mod"))
if err != nil {
return nil, storage.ErrVersionNotFound{Module: module, Version: version}
return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
src, err := v.filesystem.OpenFile(filepath.Join(versionedPath, "source.zip"), os.O_RDONLY, 0666)
if err != nil {
return nil, storage.ErrVersionNotFound{Module: module, Version: version}
return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
info, err := afero.ReadFile(v.filesystem, filepath.Join(versionedPath, version+".info"))
if err != nil {
return nil, storage.ErrVersionNotFound{Module: module, Version: version}
return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
// TODO: store the time in the saver, and parse it here
+16 -4
View File
@@ -2,10 +2,10 @@ package gcp
import (
"context"
"fmt"
"io"
"cloud.google.com/go/storage"
"github.com/gomods/athens/pkg/errors"
"google.golang.org/api/iterator"
)
@@ -15,11 +15,22 @@ type gcpBucket struct {
}
func (b *gcpBucket) Delete(ctx context.Context, path string) error {
return b.Object(path).Delete(ctx)
const op errors.Op = "gcpBucket.Delete"
err := b.Object(path).Delete(ctx)
if err != nil {
return errors.E(op, err)
}
return nil
}
func (b *gcpBucket) Open(ctx context.Context, path string) (io.ReadCloser, error) {
return b.Object(path).NewReader(ctx)
const op errors.Op = "gcpBucket.Open"
rc, err := b.Object(path).NewReader(ctx)
if err != nil {
return rc, errors.E(op, err, errors.M(path))
}
return rc, nil
}
func (b *gcpBucket) Write(ctx context.Context, path string) io.WriteCloser {
@@ -27,6 +38,7 @@ func (b *gcpBucket) Write(ctx context.Context, path string) io.WriteCloser {
}
func (b *gcpBucket) List(ctx context.Context, prefix string) ([]string, error) {
const op errors.Op = "gcpBucket.List"
it := b.Objects(ctx, &storage.Query{Prefix: prefix})
res := []string{}
@@ -36,7 +48,7 @@ func (b *gcpBucket) List(ctx context.Context, prefix string) ([]string, error) {
break
}
if err != nil {
return nil, fmt.Errorf("could not iterate over query: %s", err)
return nil, errors.E(op, err)
}
res = append(res, attrs.Name)
}
+3 -2
View File
@@ -4,7 +4,7 @@ import (
"context"
"github.com/gomods/athens/pkg/config"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
modupl "github.com/gomods/athens/pkg/storage/module"
opentracing "github.com/opentracing/opentracing-go"
)
@@ -13,10 +13,11 @@ import (
// removes a version of a module from storage. Returning ErrNotFound
// if the version does not exist.
func (s *Storage) Delete(ctx context.Context, module, version string) error {
const op errors.Op = "gcp.Delete"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.gcp.Delete")
defer sp.Finish()
if exists := s.bucket.Exists(ctx, config.PackageVersionedName(module, version, "mod")); !exists {
return storage.ErrVersionNotFound{Module: module, Version: version}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
return modupl.Delete(ctx, module, version, s.bucket.Delete)
+3 -5
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
athensStorage "github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
)
func (g *GcpTests) TestSaveGetListExistsRoundTrip() {
@@ -67,8 +67,7 @@ func (g *GcpTests) TestNotFounds() {
g.T().Run("Get module version not found", func(t *testing.T) {
_, err := g.store.Get(context.Background(), "never", "there")
versionNotFoundErr := athensStorage.ErrVersionNotFound{Module: "never", Version: "there"}
r.EqualError(versionNotFoundErr, err.Error())
r.True(errors.IsNotFoundErr(err))
})
g.T().Run("Exists module version not found", func(t *testing.T) {
@@ -77,7 +76,6 @@ func (g *GcpTests) TestNotFounds() {
g.T().Run("List not found", func(t *testing.T) {
_, err := g.store.List(g.context, "nothing/to/see/here")
modNotFoundErr := athensStorage.ErrNotFound{Module: "nothing/to/see/here"}
r.EqualError(modNotFoundErr, err.Error())
r.True(errors.IsNotFoundErr(err))
})
}
+9 -7
View File
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"github.com/gomods/athens/pkg/config"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
opentracing "github.com/opentracing/opentracing-go"
)
@@ -14,37 +15,38 @@ import (
//
// The caller is responsible for calling close on the Zip ReadCloser
func (s *Storage) Get(ctx context.Context, module, version string) (*storage.Version, error) {
const op errors.Op = "gcp.Get"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.gcp.Get")
defer sp.Finish()
if exists := s.Exists(ctx, module, version); !exists {
return nil, storage.ErrVersionNotFound{Module: module, Version: version}
if !s.Exists(ctx, module, version) {
return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
modReader, err := s.bucket.Open(ctx, config.PackageVersionedName(module, version, "mod"))
if err != nil {
return nil, fmt.Errorf("could not get new reader for mod file: %s", err)
return nil, errors.E(op, err)
}
modBytes, err := ioutil.ReadAll(modReader)
modReader.Close()
if err != nil {
return nil, fmt.Errorf("could not read bytes of mod file: %s", err)
return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %s", err))
}
zipReader, err := s.bucket.Open(ctx, config.PackageVersionedName(module, version, "zip"))
// It is up to the caller to call Close on this reader.
// The storage.Version contains a ReadCloser for the zip.
if err != nil {
return nil, fmt.Errorf("could not get new reader for zip file: %s", err)
return nil, errors.E(op, err)
}
infoReader, err := s.bucket.Open(ctx, config.PackageVersionedName(module, version, "info"))
if err != nil {
return nil, fmt.Errorf("could not get new reader for info file: %s", err)
return nil, errors.E(op, err)
}
infoBytes, err := ioutil.ReadAll(infoReader)
infoReader.Close()
if err != nil {
return nil, fmt.Errorf("could not read bytes of info file: %s", err)
return nil, errors.E(op, fmt.Errorf("could not read bytes of info file: %s", err))
}
return &storage.Version{Mod: modBytes, Zip: zipReader, Info: infoBytes}, nil
}
+3 -2
View File
@@ -4,13 +4,14 @@ import (
"context"
"strings"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
opentracing "github.com/opentracing/opentracing-go"
)
// List implements the (./pkg/storage).Lister interface
// It returns a list of versions, if any, for a given module
func (s *Storage) List(ctx context.Context, module string) ([]string, error) {
const op errors.Op = "gcp.List"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.gcp.List")
defer sp.Finish()
paths, err := s.bucket.List(ctx, module)
@@ -19,7 +20,7 @@ func (s *Storage) List(ctx context.Context, module string) ([]string, error) {
}
versions := extractVersions(paths)
if len(versions) < 1 {
return nil, storage.ErrNotFound{Module: module}
return nil, errors.E(op, "empty versions list", errors.M(module), errors.KindNotFound)
}
return versions, nil
}
+4 -2
View File
@@ -6,7 +6,8 @@ import (
"io"
"log"
stg "github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
moduploader "github.com/gomods/athens/pkg/storage/module"
opentracing "github.com/opentracing/opentracing-go"
)
@@ -19,10 +20,11 @@ import (
// Uploaded files are publicly accessable in the storage bucket as per
// an ACL rule.
func (s *Storage) Save(ctx context.Context, module, version string, mod []byte, zip io.Reader, info []byte) error {
const op errors.Op = "gcp.Save"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.gcp.Save")
defer sp.Finish()
if exists := s.Exists(ctx, module, version); exists {
return stg.ErrVersionAlreadyExists{Module: module, Version: version}
return errors.E(op, "already exists", errors.M(module), errors.V(version), errors.KindAlreadyExists)
}
err := moduploader.Upload(ctx, module, version, bytes.NewReader(info), bytes.NewReader(mod), zip, s.upload)
+10 -8
View File
@@ -4,31 +4,33 @@ import (
"context"
"fmt"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
opentracing "github.com/opentracing/opentracing-go"
)
func (v *storageImpl) Delete(ctx context.Context, module, version string) error {
const op errors.Op = "minio.Delete"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.minio.Delete")
defer sp.Finish()
if !v.Exists(ctx, module, version) {
return storage.ErrVersionNotFound{
Module: module,
Version: version,
}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
versionedPath := v.versionLocation(module, version)
modPath := fmt.Sprintf("%s/go.mod", versionedPath)
if err := v.minioClient.RemoveObject(v.bucketName, modPath); err != nil {
return err
return errors.E(op, err)
}
zipPath := fmt.Sprintf("%s/source.zip", versionedPath)
if err := v.minioClient.RemoveObject(v.bucketName, zipPath); err != nil {
return err
return errors.E(op, err)
}
infoPath := fmt.Sprintf("%s/%s.info", versionedPath, version)
return v.minioClient.RemoveObject(v.bucketName, infoPath)
err := v.minioClient.RemoveObject(v.bucketName, infoPath)
if err != nil {
return errors.E(op, err)
}
return nil
}
+3 -1
View File
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
minio "github.com/minio/minio-go"
opentracing "github.com/opentracing/opentracing-go"
@@ -48,9 +49,10 @@ func (v *storageImpl) Get(ctx context.Context, module, version string) (*storage
}
func transformNotFoundErr(module, version string, err error) error {
const op errors.Op = "minio.transformNotFoundErr"
if eresp, ok := err.(minio.ErrorResponse); ok {
if eresp.StatusCode == http.StatusNotFound {
return storage.ErrVersionNotFound{Module: module, Version: version}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
}
return err
+1 -5
View File
@@ -5,7 +5,6 @@ import (
"github.com/globalsign/mgo/bson"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
opentracing "github.com/opentracing/opentracing-go"
)
@@ -15,10 +14,7 @@ func (s *ModuleStore) Delete(ctx context.Context, module, version string) error
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.mongo.Delete")
defer sp.Finish()
if !s.Exists(ctx, module, version) {
return storage.ErrVersionNotFound{
Module: module,
Version: version,
}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
db := s.s.DB(s.d)
c := db.C(s.c)
+1 -1
View File
@@ -20,7 +20,7 @@ func (s *ModuleStore) Get(ctx context.Context, module, vsn string) (*storage.Ver
err := c.Find(bson.M{"module": module, "version": vsn}).One(result)
if err != nil {
if strings.Contains(err.Error(), "not found") {
err = storage.ErrVersionNotFound{Module: module, Version: vsn}
err = errors.E(op, errors.M(module), errors.V(vsn), errors.KindNotFound)
}
return nil, err
}
+3 -1
View File
@@ -5,12 +5,14 @@ import (
"strings"
"github.com/globalsign/mgo/bson"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
opentracing "github.com/opentracing/opentracing-go"
)
// List lists all versions of a module
func (s *ModuleStore) List(ctx context.Context, module string) ([]string, error) {
const op errors.Op = "mongo.List"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.mongo.List")
defer sp.Finish()
c := s.s.DB(s.d).C(s.c)
@@ -18,7 +20,7 @@ func (s *ModuleStore) List(ctx context.Context, module string) ([]string, error)
err := c.Find(bson.M{"module": module}).All(&result)
if err != nil {
if strings.Contains(err.Error(), "not found") {
err = storage.ErrNotFound{Module: module}
err = errors.E(op, err, errors.M(module), errors.KindNotFound)
}
return nil, err
}
-53
View File
@@ -1,53 +0,0 @@
package storage
import (
"fmt"
)
// ErrNotFound is an error implementation that indicates a module
// doesn't exist
type ErrNotFound struct {
Module string
}
func (n ErrNotFound) Error() string {
return fmt.Sprintf("module %s not found", n.Module)
}
// ErrVersionNotFound is an error implementation that indicates a module
// at a specific version doesn't exist
type ErrVersionNotFound struct {
Module string
Version string
}
func (e ErrVersionNotFound) Error() string {
return fmt.Sprintf("module %s@%s not found", e.Module, e.Version)
}
// IsNotFoundError returns true if err is an ErrNotFound
func IsNotFoundError(err error) bool {
if _, ok := err.(ErrNotFound); ok {
return ok
}
_, ok := err.(ErrVersionNotFound)
return ok
}
// ErrVersionAlreadyExists is an error implementation that indicates that a
// module@version already exists
type ErrVersionAlreadyExists struct {
Module string
Version string
}
func (e ErrVersionAlreadyExists) Error() string {
return fmt.Sprintf("%s@%s already exists", e.Module, e.Version)
}
// IsVersionAlreadyExistsErr returns true if err is an ErrVersionAlreadyExists
func IsVersionAlreadyExistsErr(err error) bool {
_, ok := err.(ErrVersionAlreadyExists)
return ok
}
+4 -6
View File
@@ -3,25 +3,23 @@ package rdbms
import (
"context"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage/rdbms/models"
opentracing "github.com/opentracing/opentracing-go"
)
// Delete removes a specific version of a module.
func (r *ModuleStore) Delete(ctx context.Context, module, version string) error {
const op errors.Op = "rdbms.Delete"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.rdbms.Delete")
defer sp.Finish()
if !r.Exists(ctx, module, version) {
return storage.ErrVersionNotFound{
Module: module,
Version: version,
}
return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound)
}
result := &models.Module{}
query := r.conn.Where("module = ?", module).Where("version = ?", version)
if err := query.First(result); err != nil {
return err
return errors.E(op, err)
}
return r.conn.Destroy(result)
}
+6 -4
View File
@@ -6,23 +6,25 @@ import (
"database/sql"
"io/ioutil"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/storage/rdbms/models"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
pkgerrors "github.com/pkg/errors"
)
// Get a specific version of a module
func (r *ModuleStore) Get(ctx context.Context, module, vsn string) (*storage.Version, error) {
const op errors.Op = "rdbms.Get"
sp, ctx := opentracing.StartSpanFromContext(ctx, "storage.rdbms.Get")
defer sp.Finish()
result := models.Module{}
query := r.conn.Where("module = ?", module).Where("version = ?", vsn)
if err := query.First(&result); err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, storage.ErrVersionNotFound{Module: module, Version: vsn}
if pkgerrors.Cause(err) == sql.ErrNoRows { // can we just say err == sql.ErrNoRows?
return nil, errors.E(op, errors.M(module), errors.V(vsn), errors.KindNotFound)
}
return nil, err
return nil, errors.E(op, err)
}
return &storage.Version{
Mod: result.Mod,
@@ -15,6 +15,7 @@ import (
"time"
"github.com/gobuffalo/suite"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
"github.com/gomods/athens/pkg/storage/fs"
"github.com/gomods/athens/pkg/storage/mem"
@@ -88,7 +89,7 @@ func (d *TestSuites) TestStorages() {
func (d *TestSuites) testNotFound(ts storage.TestSuite) {
_, err := ts.Storage().Get(context.Background(), "some", "unknown")
d.Require().Equal(true, storage.IsNotFoundError(err), "Invalid error type for %s: %#v", ts.StorageHumanReadableName(), err)
d.Require().Equal(true, errors.IsNotFoundErr(err), "Invalid error type for %s: %#v", ts.StorageHumanReadableName(), err)
}
func (d *TestSuites) testList(ts storage.TestSuite) {
@@ -134,26 +135,24 @@ func (d *TestSuites) testDelete(ts storage.TestSuite) {
tests := []struct {
module string
version string
want error
want int
}{
{
module: "does/not/exist",
version: "v1.0.0",
want: storage.ErrVersionNotFound{
Module: "does/not/exist",
Version: "v1.0.0",
},
want: errors.KindNotFound,
},
{
module: d.module,
version: version,
want: errors.KindUnexpected,
},
}
ctx := context.Background()
for _, test := range tests {
err := ts.Storage().Delete(ctx, test.module, test.version)
r.Equal(test.want, err)
r.Equal(test.want, errors.Kind(err), ts.StorageHumanReadableName())
exists := ts.Storage().Exists(ctx, test.module, test.version)
r.Equal(false, exists)
r.Equal(false, exists, ts.StorageHumanReadableName())
}
}