Merge pull request #1115 from stevvooe/consolidate-errors

errdefs: centralize error handling
This commit is contained in:
Derek McGowan 2017-06-29 15:18:24 -07:00 committed by GitHub
commit eedcbc64cc
42 changed files with 331 additions and 555 deletions

View File

@ -8,8 +8,8 @@ import (
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/metadata"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -183,7 +183,7 @@ var namespacesRemoveCommand = cli.Command{
for _, target := range clicontext.Args() { for _, target := range clicontext.Args() {
if err := namespaces.Delete(ctx, target); err != nil { if err := namespaces.Delete(ctx, target); err != nil {
if !metadata.IsNotFound(err) { if !errdefs.IsNotFound(err) {
if exitErr == nil { if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to delete %v", target) exitErr = errors.Wrapf(err, "unable to delete %v", target)
} }

4
cmd/dist/delete.go vendored
View File

@ -3,7 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -43,7 +43,7 @@ var deleteCommand = cli.Command{
} }
if err := cs.Delete(ctx, dgst); err != nil { if err := cs.Delete(ctx, dgst); err != nil {
if !content.IsNotFound(err) { if !errdefs.IsNotFound(err) {
if exitError == nil { if exitError == nil {
exitError = err exitError = err
} }

3
cmd/dist/fetch.go vendored
View File

@ -11,6 +11,7 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/progress" "github.com/containerd/containerd/progress"
@ -153,7 +154,7 @@ outer:
if !done && (!ok || status.Status == "downloading") { if !done && (!ok || status.Status == "downloading") {
info, err := cs.Info(ctx, j.Digest) info, err := cs.Info(ctx, j.Digest)
if err != nil { if err != nil {
if !content.IsNotFound(err) { if !errdefs.IsNotFound(err) {
log.G(ctx).WithError(err).Errorf("failed to get content info") log.G(ctx).WithError(err).Errorf("failed to get content info")
continue outer continue outer
} else { } else {

4
cmd/dist/images.go vendored
View File

@ -5,8 +5,8 @@ import (
"os" "os"
"text/tabwriter" "text/tabwriter"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/progress" "github.com/containerd/containerd/progress"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -83,7 +83,7 @@ var imageRemoveCommand = cli.Command{
for _, target := range clicontext.Args() { for _, target := range clicontext.Args() {
if err := imageStore.Delete(ctx, target); err != nil { if err := imageStore.Delete(ctx, target); err != nil {
if !metadata.IsNotFound(err) { if !errdefs.IsNotFound(err) {
if exitErr == nil { if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to delete %v", target) exitErr = errors.Wrapf(err, "unable to delete %v", target)
} }

View File

@ -12,8 +12,8 @@ import (
"github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/snapshot"
protobuf "github.com/gogo/protobuf/types" protobuf "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/identity"
@ -46,7 +46,7 @@ func WithCheckpoint(desc v1.Descriptor, rootfsID string) NewContainerOpts {
return err return err
} }
if _, err := client.SnapshotService().Prepare(ctx, rootfsID, identity.ChainID(diffIDs).String()); err != nil { if _, err := client.SnapshotService().Prepare(ctx, rootfsID, identity.ChainID(diffIDs).String()); err != nil {
if !snapshot.IsExist(err) { if !errdefs.IsAlreadyExists(err) {
return err return err
} }
} }

View File

@ -1,98 +0,0 @@
package content
import "github.com/pkg/errors"
type contentExistsErr struct {
desc string
}
type contentNotFoundErr struct {
desc string
}
type contentLockedErr struct {
desc string
}
// ErrExists is returned when something exists when it may not be expected.
func ErrExists(msg string) error {
if msg == "" {
msg = "content: exists"
}
return errors.WithStack(contentExistsErr{
desc: msg,
})
}
// ErrNotFound is returned when an item is not found.
func ErrNotFound(msg string) error {
if msg == "" {
msg = "content: not found"
}
return errors.WithStack(contentNotFoundErr{
desc: msg,
})
}
// ErrLocked is returned when content is actively being uploaded, this
// indicates that another process is attempting to upload the same content.
func ErrLocked(msg string) error {
if msg == "" {
msg = "content: locked"
}
return errors.WithStack(contentLockedErr{
desc: msg,
})
}
func (c contentExistsErr) Error() string {
return c.desc
}
func (c contentNotFoundErr) Error() string {
return c.desc
}
func (c contentLockedErr) Error() string {
return c.desc
}
func (c contentExistsErr) Exists() bool {
return true
}
func (c contentNotFoundErr) NotFound() bool {
return true
}
func (c contentLockedErr) Locked() bool {
return true
}
// IsNotFound returns true if the error is due to a not found content item
func IsNotFound(err error) bool {
if err, ok := errors.Cause(err).(interface {
NotFound() bool
}); ok {
return err.NotFound()
}
return false
}
// IsExists returns true if the error is due to an already existing content item
func IsExists(err error) bool {
if err, ok := errors.Cause(err).(interface {
Exists() bool
}); ok {
return err.Exists()
}
return false
}
// IsLocked returns true if the error is due to a currently locked content item
func IsLocked(err error) bool {
if err, ok := errors.Cause(err).(interface {
Locked() bool
}); ok {
return err.Locked()
}
return false
}

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/containerd/containerd/errdefs"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -33,7 +34,7 @@ func ReadBlob(ctx context.Context, provider Provider, dgst digest.Digest) ([]byt
func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size int64, expected digest.Digest) error { func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size int64, expected digest.Digest) error {
cw, err := cs.Writer(ctx, ref, size, expected) cw, err := cs.Writer(ctx, ref, size, expected)
if err != nil { if err != nil {
if !IsExists(err) { if !errdefs.IsAlreadyExists(err) {
return err return err
} }
@ -79,7 +80,7 @@ func Copy(cw Writer, r io.Reader, size int64, expected digest.Digest) error {
} }
if err := cw.Commit(size, expected); err != nil { if err := cw.Commit(size, expected); err != nil {
if !IsExists(err) { if !errdefs.IsAlreadyExists(err) {
return errors.Wrapf(err, "failed commit on ref %q", ws.Ref) return errors.Wrapf(err, "failed commit on ref %q", ws.Ref)
} }
} }

View File

@ -1,8 +1,10 @@
package content package content
import ( import (
"fmt"
"sync" "sync"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
) )
// Handles locking references // Handles locking references
@ -19,7 +21,7 @@ func tryLock(ref string) error {
defer locksMu.Unlock() defer locksMu.Unlock()
if _, ok := locks[ref]; ok { if _, ok := locks[ref]; ok {
return ErrLocked(fmt.Sprintf("key %s is locked", ref)) return errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", ref)
} }
locks[ref] = struct{}{} locks[ref] = struct{}{}

View File

@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -40,7 +41,7 @@ func (s *store) Info(ctx context.Context, dgst digest.Digest) (Info, error) {
fi, err := os.Stat(p) fi, err := os.Stat(p)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = ErrNotFound("") err = errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
} }
return Info{}, err return Info{}, err
@ -62,7 +63,7 @@ func (s *store) Reader(ctx context.Context, dgst digest.Digest) (io.ReadCloser,
fp, err := os.Open(s.blobPath(dgst)) fp, err := os.Open(s.blobPath(dgst))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = ErrNotFound("") err = errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
} }
return nil, err return nil, err
} }
@ -85,7 +86,7 @@ func (cs *store) Delete(ctx context.Context, dgst digest.Digest) error {
return err return err
} }
return ErrNotFound("") return errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
} }
return nil return nil
@ -232,7 +233,7 @@ func (s *store) Writer(ctx context.Context, ref string, total int64, expected di
if expected != "" { if expected != "" {
p := s.blobPath(expected) p := s.blobPath(expected)
if _, err := os.Stat(p); err == nil { if _, err := os.Stat(p); err == nil {
return nil, ErrExists("") return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected)
} }
} }
@ -329,7 +330,7 @@ func (s *store) Abort(ctx context.Context, ref string) error {
root := s.ingestRoot(ref) root := s.ingestRoot(ref)
if err := os.RemoveAll(root); err != nil { if err := os.RemoveAll(root); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return ErrNotFound("") return errors.Wrapf(errdefs.ErrNotFound, "ingest ref %q", ref)
} }
return err return err

View File

@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/containerd/containerd/errdefs"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -99,7 +100,7 @@ func (w *writer) Commit(size int64, expected digest.Digest) error {
if err := os.Rename(ingest, target); err != nil { if err := os.Rename(ingest, target); err != nil {
if os.IsExist(err) { if os.IsExist(err) {
// collision with the target file! // collision with the target file!
return ErrExists("") return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst)
} }
return err return err
} }

54
errdefs/errors.go Normal file
View File

@ -0,0 +1,54 @@
// Package errdefs defines the common errors used throughout containerd
// packages.
//
// Use with errors.Wrap and error.Wrapf to add context to an error.
//
// To detect an error class, use the IsXXX functions to tell whether an error
// is of a certain type.
//
// The functions ToGRPC and FromGRPC can be used to map server-side and
// client-side errors to the correct types.
package errdefs
import "github.com/pkg/errors"
// Definitions of common error types used throughout containerd. All containerd
// errors returned by most packages will map into one of these errors classes.
// Packages should return errors of these types when they want to instruct a
// client to take a particular action.
//
// For the most part, we just try to provide local grpc errors. Most conditions
// map very well to those defined by grpc.
var (
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
ErrInvalidArgument = errors.New("invalid")
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition")
ErrUnavailable = errors.New("unavailable")
)
func IsInvalidArgument(err error) bool {
return errors.Cause(err) == ErrInvalidArgument
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errors.Cause(err) == ErrNotFound
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errors.Cause(err) == ErrAlreadyExists
}
// IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition.
func IsFailedPrecondition(err error) bool {
return errors.Cause(err) == ErrFailedPrecondition
}
func IsUnavailable(err error) bool {
return errors.Cause(err) == ErrUnavailable
}

106
errdefs/grpc.go Normal file
View File

@ -0,0 +1,106 @@
package errdefs
import (
"strings"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ToGRPC will attempt to map the backend containerd error into a grpc error,
// using the original error message as a description.
//
// Further information may be extracted from certain errors depending on their
// type.
//
// If the error is unmapped, the original error will be returned to be handled
// by the regular grpc error handling stack.
func ToGRPC(err error) error {
if err == nil {
return nil
}
if isGRPCError(err) {
// error has already been mapped to grpc
return err
}
switch {
case IsInvalidArgument(err):
return grpc.Errorf(codes.InvalidArgument, err.Error())
case IsNotFound(err):
return grpc.Errorf(codes.NotFound, err.Error())
case IsAlreadyExists(err):
return grpc.Errorf(codes.AlreadyExists, err.Error())
case IsFailedPrecondition(err):
return grpc.Errorf(codes.FailedPrecondition, err.Error())
case IsUnavailable(err):
return grpc.Errorf(codes.Unavailable, err.Error())
}
return err
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...))
func ToGRPCf(err error, format string, args ...interface{}) error {
return ToGRPC(errors.Wrapf(err, format, args...))
}
func FromGRPC(err error) error {
if err == nil {
return nil
}
var cls error // divide these into error classes, becomes the cause
switch grpc.Code(err) {
case codes.InvalidArgument:
cls = ErrInvalidArgument
case codes.AlreadyExists:
cls = ErrAlreadyExists
case codes.NotFound:
cls = ErrNotFound
case codes.Unavailable:
cls = ErrUnavailable
case codes.FailedPrecondition:
cls = ErrFailedPrecondition
default:
cls = ErrUnknown
}
if cls != nil {
msg := rebaseMessage(cls, err)
if msg != "" {
err = errors.Wrapf(cls, msg)
} else {
err = cls
}
}
return err
}
// rebaseMessage removes the repeats for an error at the end of an error
// string. This will happen when taking an error over grpc then remapping it.
//
// Effectively, we just remove the string of cls from the end of err if it
// appears there.
func rebaseMessage(cls error, err error) string {
desc := grpc.ErrorDesc(err)
clss := cls.Error()
if desc == clss {
return ""
}
return strings.TrimSuffix(desc, ": "+clss)
}
func isGRPCError(err error) bool {
_, ok := status.FromError(err)
return ok
}

View File

@ -17,6 +17,7 @@ package identifiers
import ( import (
"regexp" "regexp"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -32,15 +33,8 @@ var (
// Rules for domains, defined in RFC 1035, section 2.3.1, are used for // Rules for domains, defined in RFC 1035, section 2.3.1, are used for
// identifiers. // identifiers.
identifierRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*")) identifierRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*"))
errIdentifierInvalid = errors.New("invalid identifier")
) )
// IsInvalid return true if the error was due to an invalid identifer.
func IsInvalid(err error) bool {
return errors.Cause(err) == errIdentifierInvalid
}
// Validate return nil if the string s is a valid identifier. // Validate return nil if the string s is a valid identifier.
// //
// identifiers must be valid domain identifiers according to RFC 1035, section 2.3.1. To // identifiers must be valid domain identifiers according to RFC 1035, section 2.3.1. To
@ -50,11 +44,11 @@ func IsInvalid(err error) bool {
// a domain identifier or filesystem path component. // a domain identifier or filesystem path component.
func Validate(s string) error { func Validate(s string) error {
if len(s) > maxLength { if len(s) > maxLength {
return errors.Wrapf(errIdentifierInvalid, "identifier %q greater than maximum length (%d characters)", s, maxLength) return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier %q greater than maximum length (%d characters)", s, maxLength)
} }
if !identifierRe.MatchString(s) { if !identifierRe.MatchString(s) {
return errors.Wrapf(errIdentifierInvalid, "identifier %q must match %v", s, identifierRe) return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier %q must match %v", s, identifierRe)
} }
return nil return nil
} }

View File

@ -3,6 +3,8 @@ package identifiers
import ( import (
"strings" "strings"
"testing" "testing"
"github.com/containerd/containerd/errdefs"
) )
func TestValidIdentifiers(t *testing.T) { func TestValidIdentifiers(t *testing.T) {
@ -44,7 +46,7 @@ func TestInvalidIdentifiers(t *testing.T) {
t.Run(input, func(t *testing.T) { t.Run(input, func(t *testing.T) {
if err := Validate(input); err == nil { if err := Validate(input); err == nil {
t.Fatal("expected invalid error") t.Fatal("expected invalid error")
} else if !IsInvalid(err) { } else if !errdefs.IsInvalidArgument(err) {
t.Fatal("error should be an invalid identifier error") t.Fatal("error should be an invalid identifier error")
} }
}) })

View File

@ -6,6 +6,7 @@ import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -29,12 +30,12 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain
bkt := getContainerBucket(s.tx, namespace, id) bkt := getContainerBucket(s.tx, namespace, id)
if bkt == nil { if bkt == nil {
return containers.Container{}, ErrNotFound("bucket does not exist") return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "bucket name %q")
} }
container := containers.Container{ID: id} container := containers.Container{ID: id}
if err := readContainer(&container, bkt); err != nil { if err := readContainer(&container, bkt); err != nil {
return containers.Container{}, errors.Wrap(err, "failed to read container") return containers.Container{}, errors.Wrapf(err, "failed to read container %v", id)
} }
return container, nil return container, nil
@ -90,7 +91,7 @@ func (s *containerStore) Create(ctx context.Context, container containers.Contai
cbkt, err := bkt.CreateBucket([]byte(container.ID)) cbkt, err := bkt.CreateBucket([]byte(container.ID))
if err != nil { if err != nil {
if err == bolt.ErrBucketExists { if err == bolt.ErrBucketExists {
err = ErrExists("content for id already exists") err = errors.Wrapf(errdefs.ErrAlreadyExists, "content %q")
} }
return containers.Container{}, err return containers.Container{}, err
} }
@ -110,14 +111,18 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
return containers.Container{}, err return containers.Container{}, err
} }
if container.ID == "" {
return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "must specify a container id")
}
bkt := getContainersBucket(s.tx, namespace) bkt := getContainersBucket(s.tx, namespace)
if bkt == nil { if bkt == nil {
return containers.Container{}, ErrNotFound("no containers") return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID)
} }
cbkt := bkt.Bucket([]byte(container.ID)) cbkt := bkt.Bucket([]byte(container.ID))
if cbkt == nil { if cbkt == nil {
return containers.Container{}, ErrNotFound("no content for id") return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID)
} }
container.UpdatedAt = time.Now() container.UpdatedAt = time.Now()
@ -136,11 +141,11 @@ func (s *containerStore) Delete(ctx context.Context, id string) error {
bkt := getContainersBucket(s.tx, namespace) bkt := getContainersBucket(s.tx, namespace)
if bkt == nil { if bkt == nil {
return ErrNotFound("no containers") return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %v, bucket not present", id)
} }
if err := bkt.DeleteBucket([]byte(id)); err == bolt.ErrBucketNotFound { if err := bkt.DeleteBucket([]byte(id)); err == bolt.ErrBucketNotFound {
return ErrNotFound("no content for id") return errors.Wrapf(errdefs.ErrNotFound, "container %v", id)
} }
return err return err
} }

View File

@ -1,95 +0,0 @@
package metadata
import "github.com/pkg/errors"
type metadataExistsErr struct {
desc string
}
type metadataNotFoundErr struct {
desc string
}
type metadataNotEmptyErr struct {
desc string
}
// ErrExists is returned when an item already exists in metadata
func ErrExists(msg string) error {
if msg == "" {
msg = "metadata: exists"
}
return errors.WithStack(metadataExistsErr{
desc: msg,
})
}
// ErrNotFound is returned when an item cannot be found in metadata
func ErrNotFound(msg string) error {
if msg == "" {
msg = "metadata: not found"
}
return errors.WithStack(metadataNotFoundErr{
desc: msg,
})
}
// ErrNotEmpty is returned when a metadata item can't be deleted because it is not empty
func ErrNotEmpty(msg string) error {
if msg == "" {
msg = "metadata: namespace not empty"
}
return errors.WithStack(metadataNotEmptyErr{
desc: msg,
})
}
func (m metadataExistsErr) Error() string {
return m.desc
}
func (m metadataNotFoundErr) Error() string {
return m.desc
}
func (m metadataNotEmptyErr) Error() string {
return m.desc
}
func (m metadataExistsErr) Exists() bool {
return true
}
func (m metadataNotFoundErr) NotFound() bool {
return true
}
func (m metadataNotEmptyErr) NotEmpty() bool {
return true
}
// IsNotFound returns true if the error is due to a missing metadata item
func IsNotFound(err error) bool {
if err, ok := errors.Cause(err).(interface {
NotFound() bool
}); ok {
return err.NotFound()
}
return false
}
// IsExists returns true if the error is due to an already existing metadata item
func IsExists(err error) bool {
if err, ok := errors.Cause(err).(interface {
Exists() bool
}); ok {
return err.Exists()
}
return false
}
// IsNotEmpty returns true if the error is due to delete request of a non-empty metadata item
func IsNotEmpty(err error) bool {
if err, ok := errors.Cause(err).(interface {
NotEmpty() bool
}); ok {
return err.NotEmpty()
}
return false
}

View File

@ -6,10 +6,12 @@ import (
"fmt" "fmt"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
) )
type imageStore struct { type imageStore struct {
@ -30,12 +32,12 @@ func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error)
bkt := getImagesBucket(s.tx, namespace) bkt := getImagesBucket(s.tx, namespace)
if bkt == nil { if bkt == nil {
return images.Image{}, ErrNotFound("") return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
} }
ibkt := bkt.Bucket([]byte(name)) ibkt := bkt.Bucket([]byte(name))
if ibkt == nil { if ibkt == nil {
return images.Image{}, ErrNotFound("") return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
} }
image.Name = name image.Name = name
@ -124,7 +126,7 @@ func (s *imageStore) Delete(ctx context.Context, name string) error {
return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
err := bkt.DeleteBucket([]byte(name)) err := bkt.DeleteBucket([]byte(name))
if err == bolt.ErrBucketNotFound { if err == bolt.ErrBucketNotFound {
return ErrNotFound("") return errors.Wrapf(errdefs.ErrNotFound, "image %q", name)
} }
return err return err
}) })

View File

@ -4,8 +4,10 @@ import (
"context" "context"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/pkg/errors"
) )
type namespaceStore struct { type namespaceStore struct {
@ -30,7 +32,7 @@ func (s *namespaceStore) Create(ctx context.Context, namespace string, labels ma
bkt, err := topbkt.CreateBucket([]byte(namespace)) bkt, err := topbkt.CreateBucket([]byte(namespace))
if err != nil { if err != nil {
if err == bolt.ErrBucketExists { if err == bolt.ErrBucketExists {
return ErrExists("") return errors.Wrapf(errdefs.ErrAlreadyExists, "namespace %q")
} }
return err return err
@ -105,12 +107,12 @@ func (s *namespaceStore) Delete(ctx context.Context, namespace string) error {
if empty, err := s.namespaceEmpty(ctx, namespace); err != nil { if empty, err := s.namespaceEmpty(ctx, namespace); err != nil {
return err return err
} else if !empty { } else if !empty {
return ErrNotEmpty("") return errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace %q must be empty", namespace)
} }
if err := bkt.DeleteBucket([]byte(namespace)); err != nil { if err := bkt.DeleteBucket([]byte(namespace)); err != nil {
if err == bolt.ErrBucketNotFound { if err == bolt.ErrBucketNotFound {
return ErrNotFound("") return errors.Wrapf(errdefs.ErrNotFound, "namespace %q", namespace)
} }
return err return err

View File

@ -3,6 +3,7 @@ package namespaces
import ( import (
"os" "os"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/identifiers"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -13,10 +14,6 @@ const (
Default = "default" Default = "default"
) )
var (
errNamespaceRequired = errors.New("namespace is required")
)
type namespaceKey struct{} type namespaceKey struct{}
// WithNamespace sets a given namespace on the context // WithNamespace sets a given namespace on the context
@ -50,16 +47,11 @@ func Namespace(ctx context.Context) (string, bool) {
return namespace, ok return namespace, ok
} }
// IsNamespaceRequired returns whether an error is caused by a missing namespace
func IsNamespaceRequired(err error) bool {
return errors.Cause(err) == errNamespaceRequired
}
// NamespaceRequired returns the valid namepace from the context or an error. // NamespaceRequired returns the valid namepace from the context or an error.
func NamespaceRequired(ctx context.Context) (string, error) { func NamespaceRequired(ctx context.Context) (string, error) {
namespace, ok := Namespace(ctx) namespace, ok := Namespace(ctx)
if !ok || namespace == "" { if !ok || namespace == "" {
return "", errNamespaceRequired return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace is required")
} }
if err := identifiers.Validate(namespace); err != nil { if err := identifiers.Validate(namespace); err != nil {

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
@ -31,10 +32,10 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
status, err := p.tracker.GetStatus(ref) status, err := p.tracker.GetStatus(ref)
if err == nil { if err == nil {
if status.Offset == status.Total { if status.Offset == status.Total {
return nil, content.ErrExists("") return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "ref %v already exists", ref)
} }
// TODO: Handle incomplete status // TODO: Handle incomplete status
} else if !content.IsNotFound(err) { } else if !errdefs.IsNotFound(err) {
return nil, errors.Wrap(err, "failed to get status") return nil, errors.Wrap(err, "failed to get status")
} }
@ -72,7 +73,7 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
// TODO: Set updated time? // TODO: Set updated time?
}, },
}) })
return nil, content.ErrExists("") return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest)
} }
if resp.StatusCode != http.StatusNotFound { if resp.StatusCode != http.StatusNotFound {
// TODO: log error // TODO: log error

View File

@ -16,6 +16,7 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
@ -202,7 +203,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
cw, err := c.contentStore.Writer(ctx, ref, desc.Size, desc.Digest) cw, err := c.contentStore.Writer(ctx, ref, desc.Size, desc.Digest)
if err != nil { if err != nil {
if !content.IsExists(err) { if !errdefs.IsAlreadyExists(err) {
return err return err
} }

View File

@ -4,6 +4,8 @@ import (
"sync" "sync"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
) )
type Status struct { type Status struct {
@ -34,7 +36,7 @@ func (t *memoryStatusTracker) GetStatus(ref string) (Status, error) {
defer t.m.Unlock() defer t.m.Unlock()
status, ok := t.statuses[ref] status, ok := t.statuses[ref]
if !ok { if !ok {
return Status{}, content.ErrNotFound("") return Status{}, errors.Wrapf(errdefs.ErrNotFound, "status for ref %v", ref)
} }
return status, nil return status, nil
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@ -69,9 +70,9 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc
for { for {
cw, err = ingester.Writer(ctx, ref, desc.Size, desc.Digest) cw, err = ingester.Writer(ctx, ref, desc.Size, desc.Digest)
if err != nil { if err != nil {
if content.IsExists(err) { if errdefs.IsAlreadyExists(err) {
return nil return nil
} else if !content.IsLocked(err) { } else if !errdefs.IsUnavailable(err) {
return err return err
} }
@ -122,7 +123,7 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc
cw, err := pusher.Push(ctx, desc) cw, err := pusher.Push(ctx, desc)
if err != nil { if err != nil {
if !content.IsExists(err) { if !errdefs.IsAlreadyExists(err) {
return err return err
} }

View File

@ -3,6 +3,7 @@ package rootfs
import ( import (
"fmt" "fmt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshot" "github.com/containerd/containerd/snapshot"
@ -46,7 +47,7 @@ func applyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap
if err == nil { if err == nil {
log.G(ctx).Debugf("Extraction not needed, layer snapshot exists") log.G(ctx).Debugf("Extraction not needed, layer snapshot exists")
return nil return nil
} else if !snapshot.IsNotExist(err) { } else if !errdefs.IsNotFound(err) {
return errors.Wrap(err, "failed to stat snapshot") return errors.Wrap(err, "failed to stat snapshot")
} }

View File

@ -3,13 +3,8 @@ package containers
import ( import (
api "github.com/containerd/containerd/api/services/containers/v1" api "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/namespaces"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
) )
func containersToProto(containers []containers.Container) []api.Container { func containersToProto(containers []containers.Container) []api.Container {
@ -52,18 +47,3 @@ func containerFromProto(containerpb *api.Container) containers.Container {
RootFS: containerpb.RootFS, RootFS: containerpb.RootFS,
} }
} }
func mapGRPCError(err error, id string) error {
switch {
case metadata.IsNotFound(err):
return grpc.Errorf(codes.NotFound, "container %v not found", id)
case metadata.IsExists(err):
return grpc.Errorf(codes.AlreadyExists, "container %v already exists", id)
case namespaces.IsNamespaceRequired(err):
return grpc.Errorf(codes.InvalidArgument, "namespace required, please set %q header", namespaces.GRPCHeader)
case identifiers.IsInvalid(err):
return grpc.Errorf(codes.InvalidArgument, err.Error())
}
return err
}

View File

@ -5,6 +5,7 @@ import (
api "github.com/containerd/containerd/api/services/containers/v1" api "github.com/containerd/containerd/api/services/containers/v1"
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/metadata" "github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
@ -49,31 +50,30 @@ func (s *Service) Register(server *grpc.Server) error {
func (s *Service) Get(ctx context.Context, req *api.GetContainerRequest) (*api.GetContainerResponse, error) { func (s *Service) Get(ctx context.Context, req *api.GetContainerRequest) (*api.GetContainerResponse, error) {
var resp api.GetContainerResponse var resp api.GetContainerResponse
return &resp, s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error { return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
container, err := store.Get(ctx, req.ID) container, err := store.Get(ctx, req.ID)
if err != nil { if err != nil {
return mapGRPCError(err, req.ID) return err
} }
containerpb := containerToProto(&container) containerpb := containerToProto(&container)
resp.Container = containerpb resp.Container = containerpb
return nil return nil
}) }))
} }
func (s *Service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) { func (s *Service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) {
var resp api.ListContainersResponse var resp api.ListContainersResponse
return &resp, s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error { return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
containers, err := store.List(ctx, req.Filter) containers, err := store.List(ctx, req.Filter)
if err != nil { if err != nil {
return mapGRPCError(err, "") return err
} }
resp.Containers = containersToProto(containers) resp.Containers = containersToProto(containers)
return nil return nil
}) }))
} }
func (s *Service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) { func (s *Service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) {
@ -84,14 +84,14 @@ func (s *Service) Create(ctx context.Context, req *api.CreateContainerRequest) (
created, err := store.Create(ctx, container) created, err := store.Create(ctx, container)
if err != nil { if err != nil {
return mapGRPCError(err, req.Container.ID) return err
} }
resp.Container = containerToProto(&created) resp.Container = containerToProto(&created)
return nil return nil
}); err != nil { }); err != nil {
return &resp, err return &resp, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/containers/create", &eventsapi.ContainerCreate{ if err := s.emit(ctx, "/containers/create", &eventsapi.ContainerCreate{
ContainerID: resp.Container.ID, ContainerID: resp.Container.ID,
@ -115,7 +115,7 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (
current, err := store.Get(ctx, container.ID) current, err := store.Get(ctx, container.ID)
if err != nil { if err != nil {
return mapGRPCError(err, container.ID) return err
} }
if current.ID != container.ID { if current.ID != container.ID {
@ -150,14 +150,14 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (
created, err := store.Update(ctx, container) created, err := store.Update(ctx, container)
if err != nil { if err != nil {
return mapGRPCError(err, req.Container.ID) return err
} }
resp.Container = containerToProto(&created) resp.Container = containerToProto(&created)
return nil return nil
}); err != nil { }); err != nil {
return &resp, err return &resp, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/containers/update", &eventsapi.ContainerUpdate{ if err := s.emit(ctx, "/containers/update", &eventsapi.ContainerUpdate{
@ -174,9 +174,9 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateContainerRequest) (
func (s *Service) Delete(ctx context.Context, req *api.DeleteContainerRequest) (*empty.Empty, error) { func (s *Service) Delete(ctx context.Context, req *api.DeleteContainerRequest) (*empty.Empty, error) {
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
return mapGRPCError(store.Delete(ctx, req.ID), req.ID) return store.Delete(ctx, req.ID)
}); err != nil { }); err != nil {
return &empty.Empty{}, mapGRPCError(err, req.ID) return &empty.Empty{}, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/containers/delete", &eventsapi.ContainerDelete{ if err := s.emit(ctx, "/containers/delete", &eventsapi.ContainerDelete{

View File

@ -1,33 +0,0 @@
package content
import (
"github.com/containerd/containerd/content"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
func rewriteGRPCError(err error) error {
switch grpc.Code(errors.Cause(err)) {
case codes.AlreadyExists:
return content.ErrExists(grpc.ErrorDesc(err))
case codes.NotFound:
return content.ErrNotFound(grpc.ErrorDesc(err))
case codes.Unavailable:
return content.ErrLocked(grpc.ErrorDesc(err))
}
return err
}
func serverErrorToGRPC(err error, id string) error {
switch {
case content.IsNotFound(err):
return grpc.Errorf(codes.NotFound, "%v: not found", id)
case content.IsExists(err):
return grpc.Errorf(codes.AlreadyExists, "%v: exists", id)
case content.IsLocked(err):
return grpc.Errorf(codes.Unavailable, "%v: locked", id)
}
return err
}

View File

@ -8,6 +8,7 @@ import (
api "github.com/containerd/containerd/api/services/content/v1" api "github.com/containerd/containerd/api/services/content/v1"
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
@ -66,7 +67,7 @@ func (s *Service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResp
bi, err := s.store.Info(ctx, req.Digest) bi, err := s.store.Info(ctx, req.Digest)
if err != nil { if err != nil {
return nil, serverErrorToGRPC(err, req.Digest.String()) return nil, errdefs.ToGRPC(err)
} }
return &api.InfoResponse{ return &api.InfoResponse{
@ -125,7 +126,7 @@ func (s *Service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*e
} }
if err := s.store.Delete(ctx, req.Digest); err != nil { if err := s.store.Delete(ctx, req.Digest); err != nil {
return nil, serverErrorToGRPC(err, req.Digest.String()) return nil, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/content/delete", &eventsapi.ContentDelete{ if err := s.emit(ctx, "/content/delete", &eventsapi.ContentDelete{
@ -144,12 +145,12 @@ func (s *Service) Read(req *api.ReadContentRequest, session api.Content_ReadServ
oi, err := s.store.Info(session.Context(), req.Digest) oi, err := s.store.Info(session.Context(), req.Digest)
if err != nil { if err != nil {
return serverErrorToGRPC(err, req.Digest.String()) return errdefs.ToGRPC(err)
} }
rc, err := s.store.Reader(session.Context(), req.Digest) rc, err := s.store.Reader(session.Context(), req.Digest)
if err != nil { if err != nil {
return serverErrorToGRPC(err, req.Digest.String()) return errdefs.ToGRPC(err)
} }
defer rc.Close() // TODO(stevvooe): Cache these file descriptors for performance. defer rc.Close() // TODO(stevvooe): Cache these file descriptors for performance.
@ -217,7 +218,7 @@ func (rw *readResponseWriter) Write(p []byte) (n int, err error) {
func (s *Service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) { func (s *Service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) {
statuses, err := s.store.Status(ctx, req.Filter) statuses, err := s.store.Status(ctx, req.Filter)
if err != nil { if err != nil {
return nil, serverErrorToGRPC(err, req.Filter) return nil, errdefs.ToGRPCf(err, "could not get status for filter %q", req.Filter)
} }
var resp api.StatusResponse var resp api.StatusResponse
@ -295,7 +296,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) {
// this action locks the writer for the session. // this action locks the writer for the session.
wr, err := s.store.Writer(ctx, ref, total, expected) wr, err := s.store.Writer(ctx, ref, total, expected)
if err != nil { if err != nil {
return serverErrorToGRPC(err, ref) return errdefs.ToGRPC(err)
} }
defer wr.Close() defer wr.Close()
@ -303,7 +304,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) {
msg.Action = req.Action msg.Action = req.Action
ws, err := wr.Status() ws, err := wr.Status()
if err != nil { if err != nil {
return serverErrorToGRPC(err, ref) return errdefs.ToGRPC(err)
} }
msg.Offset = ws.Offset // always set the offset. msg.Offset = ws.Offset // always set the offset.
@ -414,7 +415,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) {
func (s *Service) Abort(ctx context.Context, req *api.AbortRequest) (*empty.Empty, error) { func (s *Service) Abort(ctx context.Context, req *api.AbortRequest) (*empty.Empty, error) {
if err := s.store.Abort(ctx, req.Ref); err != nil { if err := s.store.Abort(ctx, req.Ref); err != nil {
return nil, serverErrorToGRPC(err, req.Ref) return nil, errdefs.ToGRPC(err)
} }
return &empty.Empty{}, nil return &empty.Empty{}, nil

View File

@ -6,6 +6,7 @@ import (
contentapi "github.com/containerd/containerd/api/services/content/v1" contentapi "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
) )
@ -24,7 +25,7 @@ func (rs *remoteStore) Info(ctx context.Context, dgst digest.Digest) (content.In
Digest: dgst, Digest: dgst,
}) })
if err != nil { if err != nil {
return content.Info{}, rewriteGRPCError(err) return content.Info{}, errdefs.FromGRPC(err)
} }
return content.Info{ return content.Info{
@ -37,14 +38,14 @@ func (rs *remoteStore) Info(ctx context.Context, dgst digest.Digest) (content.In
func (rs *remoteStore) Walk(ctx context.Context, fn content.WalkFunc) error { func (rs *remoteStore) Walk(ctx context.Context, fn content.WalkFunc) error {
session, err := rs.client.List(ctx, &contentapi.ListContentRequest{}) session, err := rs.client.List(ctx, &contentapi.ListContentRequest{})
if err != nil { if err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
for { for {
msg, err := session.Recv() msg, err := session.Recv()
if err != nil { if err != nil {
if err != io.EOF { if err != io.EOF {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
break break
@ -68,7 +69,7 @@ func (rs *remoteStore) Delete(ctx context.Context, dgst digest.Digest) error {
if _, err := rs.client.Delete(ctx, &contentapi.DeleteContentRequest{ if _, err := rs.client.Delete(ctx, &contentapi.DeleteContentRequest{
Digest: dgst, Digest: dgst,
}); err != nil { }); err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
return nil return nil
@ -98,7 +99,7 @@ func (rs *remoteStore) Status(ctx context.Context, filter string) ([]content.Sta
Filter: filter, Filter: filter,
}) })
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
var statuses []content.Status var statuses []content.Status
@ -119,7 +120,7 @@ func (rs *remoteStore) Status(ctx context.Context, filter string) ([]content.Sta
func (rs *remoteStore) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) { func (rs *remoteStore) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
wrclient, offset, err := rs.negotiate(ctx, ref, size, expected) wrclient, offset, err := rs.negotiate(ctx, ref, size, expected)
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return &remoteWriter{ return &remoteWriter{
@ -134,7 +135,7 @@ func (rs *remoteStore) Abort(ctx context.Context, ref string) error {
if _, err := rs.client.Abort(ctx, &contentapi.AbortRequest{ if _, err := rs.client.Abort(ctx, &contentapi.AbortRequest{
Ref: ref, Ref: ref,
}); err != nil { }); err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
return nil return nil

View File

@ -5,6 +5,7 @@ import (
contentapi "github.com/containerd/containerd/api/services/content/v1" contentapi "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -86,7 +87,7 @@ func (rw *remoteWriter) Commit(size int64, expected digest.Digest) error {
Expected: expected, Expected: expected,
}) })
if err != nil { if err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
if size != 0 && resp.Offset != size { if size != 0 && resp.Offset != size {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
imagesapi "github.com/containerd/containerd/api/services/images/v1" imagesapi "github.com/containerd/containerd/api/services/images/v1"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -28,7 +29,7 @@ func (s *remoteStore) Update(ctx context.Context, name string, desc ocispec.Desc
}, },
}) })
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
func (s *remoteStore) Get(ctx context.Context, name string) (images.Image, error) { func (s *remoteStore) Get(ctx context.Context, name string) (images.Image, error) {
@ -36,7 +37,7 @@ func (s *remoteStore) Get(ctx context.Context, name string) (images.Image, error
Name: name, Name: name,
}) })
if err != nil { if err != nil {
return images.Image{}, rewriteGRPCError(err) return images.Image{}, errdefs.FromGRPC(err)
} }
return imageFromProto(resp.Image), nil return imageFromProto(resp.Image), nil
@ -45,7 +46,7 @@ func (s *remoteStore) Get(ctx context.Context, name string) (images.Image, error
func (s *remoteStore) List(ctx context.Context) ([]images.Image, error) { func (s *remoteStore) List(ctx context.Context) ([]images.Image, error) {
resp, err := s.client.List(ctx, &imagesapi.ListImagesRequest{}) resp, err := s.client.List(ctx, &imagesapi.ListImagesRequest{})
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return imagesFromProto(resp.Images), nil return imagesFromProto(resp.Images), nil
@ -56,5 +57,5 @@ func (s *remoteStore) Delete(ctx context.Context, name string) error {
Name: name, Name: name,
}) })
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }

View File

@ -3,14 +3,8 @@ package images
import ( import (
imagesapi "github.com/containerd/containerd/api/services/images/v1" imagesapi "github.com/containerd/containerd/api/services/images/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/namespaces"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
) )
func imagesToProto(images []images.Image) []imagesapi.Image { func imagesToProto(images []images.Image) []imagesapi.Image {
@ -62,33 +56,3 @@ func descToProto(desc *ocispec.Descriptor) types.Descriptor {
Digest: desc.Digest, Digest: desc.Digest,
} }
} }
func rewriteGRPCError(err error) error {
if err == nil {
return err
}
switch grpc.Code(errors.Cause(err)) {
case codes.AlreadyExists:
return metadata.ErrExists(grpc.ErrorDesc(err))
case codes.NotFound:
return metadata.ErrNotFound(grpc.ErrorDesc(err))
}
return err
}
func mapGRPCError(err error, id string) error {
switch {
case metadata.IsNotFound(err):
return grpc.Errorf(codes.NotFound, "image %v not found", id)
case metadata.IsExists(err):
return grpc.Errorf(codes.AlreadyExists, "image %v already exists", id)
case namespaces.IsNamespaceRequired(err):
return grpc.Errorf(codes.InvalidArgument, "namespace required, please set %q header", namespaces.GRPCHeader)
case identifiers.IsInvalid(err):
return grpc.Errorf(codes.InvalidArgument, err.Error())
}
return err
}

View File

@ -4,6 +4,7 @@ import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
imagesapi "github.com/containerd/containerd/api/services/images/v1" imagesapi "github.com/containerd/containerd/api/services/images/v1"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/metadata" "github.com/containerd/containerd/metadata"
@ -51,22 +52,22 @@ func (s *Service) Register(server *grpc.Server) error {
func (s *Service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) { func (s *Service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) {
var resp imagesapi.GetImageResponse var resp imagesapi.GetImageResponse
return &resp, s.withStoreView(ctx, func(ctx context.Context, store images.Store) error { return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store images.Store) error {
image, err := store.Get(ctx, req.Name) image, err := store.Get(ctx, req.Name)
if err != nil { if err != nil {
return mapGRPCError(err, req.Name) return err
} }
imagepb := imageToProto(&image) imagepb := imageToProto(&image)
resp.Image = &imagepb resp.Image = &imagepb
return nil return nil
}) }))
} }
func (s *Service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) { func (s *Service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) {
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store images.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store images.Store) error {
return mapGRPCError(store.Update(ctx, req.Image.Name, descFromProto(&req.Image.Target)), req.Image.Name) return store.Update(ctx, req.Image.Name, descFromProto(&req.Image.Target))
}); err != nil { }); err != nil {
return nil, err return nil, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/images/update", &eventsapi.ImageUpdate{ if err := s.emit(ctx, "/images/update", &eventsapi.ImageUpdate{
@ -88,7 +89,7 @@ func (s *Service) List(ctx context.Context, _ *imagesapi.ListImagesRequest) (*im
return &resp, s.withStoreView(ctx, func(ctx context.Context, store images.Store) error { return &resp, s.withStoreView(ctx, func(ctx context.Context, store images.Store) error {
images, err := store.List(ctx) images, err := store.List(ctx)
if err != nil { if err != nil {
return mapGRPCError(err, "") return errdefs.ToGRPC(err)
} }
resp.Images = imagesToProto(images) resp.Images = imagesToProto(images)
@ -98,7 +99,7 @@ func (s *Service) List(ctx context.Context, _ *imagesapi.ListImagesRequest) (*im
func (s *Service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*empty.Empty, error) { func (s *Service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*empty.Empty, error) {
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store images.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store images.Store) error {
return mapGRPCError(store.Delete(ctx, req.Name), req.Name) return errdefs.ToGRPC(store.Delete(ctx, req.Name))
}); err != nil { }); err != nil {
return nil, err return nil, err
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
api "github.com/containerd/containerd/api/services/namespaces/v1" api "github.com/containerd/containerd/api/services/namespaces/v1"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
) )
@ -27,7 +28,7 @@ func (r *remote) Create(ctx context.Context, namespace string, labels map[string
_, err := r.client.Create(ctx, &req) _, err := r.client.Create(ctx, &req)
if err != nil { if err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
return nil return nil
@ -39,7 +40,7 @@ func (r *remote) Labels(ctx context.Context, namespace string) (map[string]strin
resp, err := r.client.Get(ctx, &req) resp, err := r.client.Get(ctx, &req)
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return resp.Namespace.Labels, nil return resp.Namespace.Labels, nil
@ -59,7 +60,7 @@ func (r *remote) SetLabel(ctx context.Context, namespace, key, value string) err
_, err := r.client.Update(ctx, &req) _, err := r.client.Update(ctx, &req)
if err != nil { if err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
return nil return nil
@ -70,7 +71,7 @@ func (r *remote) List(ctx context.Context) ([]string, error) {
resp, err := r.client.List(ctx, &req) resp, err := r.client.List(ctx, &req)
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
var namespaces []string var namespaces []string
@ -88,7 +89,7 @@ func (r *remote) Delete(ctx context.Context, namespace string) error {
req.Name = namespace req.Name = namespace
_, err := r.client.Delete(ctx, &req) _, err := r.client.Delete(ctx, &req)
if err != nil { if err != nil {
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
return nil return nil

View File

@ -1,36 +0,0 @@
package namespaces
import (
"github.com/containerd/containerd/metadata"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
func mapGRPCError(err error, id string) error {
switch {
case metadata.IsNotFound(err):
return grpc.Errorf(codes.NotFound, "namespace %v not found", id)
case metadata.IsExists(err):
return grpc.Errorf(codes.AlreadyExists, "namespace %v already exists", id)
case metadata.IsNotEmpty(err):
return grpc.Errorf(codes.FailedPrecondition, "namespace %v must be empty", id)
}
return err
}
func rewriteGRPCError(err error) error {
if err == nil {
return err
}
switch grpc.Code(errors.Cause(err)) {
case codes.AlreadyExists:
return metadata.ErrExists(grpc.ErrorDesc(err))
case codes.NotFound:
return metadata.ErrNotFound(grpc.ErrorDesc(err))
}
return err
}

View File

@ -6,6 +6,7 @@ import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
api "github.com/containerd/containerd/api/services/namespaces/v1" api "github.com/containerd/containerd/api/services/namespaces/v1"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/metadata" "github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
@ -59,7 +60,7 @@ func (s *Service) Get(ctx context.Context, req *api.GetNamespaceRequest) (*api.G
return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error { return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
labels, err := store.Labels(ctx, req.Name) labels, err := store.Labels(ctx, req.Name)
if err != nil { if err != nil {
return mapGRPCError(err, req.Name) return errdefs.ToGRPC(err)
} }
resp.Namespace = api.Namespace{ resp.Namespace = api.Namespace{
@ -85,7 +86,7 @@ func (s *Service) List(ctx context.Context, req *api.ListNamespacesRequest) (*ap
if err != nil { if err != nil {
// In general, this should be unlikely, since we are holding a // In general, this should be unlikely, since we are holding a
// transaction to service this request. // transaction to service this request.
return mapGRPCError(err, namespace) return errdefs.ToGRPC(err)
} }
resp.Namespaces = append(resp.Namespaces, api.Namespace{ resp.Namespaces = append(resp.Namespaces, api.Namespace{
@ -103,7 +104,7 @@ func (s *Service) Create(ctx context.Context, req *api.CreateNamespaceRequest) (
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil { if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil {
return mapGRPCError(err, req.Namespace.Name) return errdefs.ToGRPC(err)
} }
for k, v := range req.Namespace.Labels { for k, v := range req.Namespace.Labels {
@ -149,7 +150,7 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (
// get current set of labels // get current set of labels
labels, err := store.Labels(ctx, req.Namespace.Name) labels, err := store.Labels(ctx, req.Namespace.Name)
if err != nil { if err != nil {
return mapGRPCError(err, req.Namespace.Name) return errdefs.ToGRPC(err)
} }
for k := range labels { for k := range labels {
@ -183,7 +184,7 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (
func (s *Service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*empty.Empty, error) { func (s *Service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*empty.Empty, error) {
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
return mapGRPCError(store.Delete(ctx, req.Name), req.Name) return errdefs.ToGRPC(store.Delete(ctx, req.Name))
}); err != nil { }); err != nil {
return &empty.Empty{}, err return &empty.Empty{}, err
} }

View File

@ -3,16 +3,12 @@ package snapshot
import ( import (
"context" "context"
"io" "io"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1" snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshot" "github.com/containerd/containerd/snapshot"
"github.com/pkg/errors"
) )
// NewSnapshotterFromClient returns a new Snapshotter which communicates // NewSnapshotterFromClient returns a new Snapshotter which communicates
@ -30,7 +26,7 @@ type remoteSnapshotter struct {
func (r *remoteSnapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) { func (r *remoteSnapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
resp, err := r.client.Stat(ctx, &snapshotapi.StatSnapshotRequest{Key: key}) resp, err := r.client.Stat(ctx, &snapshotapi.StatSnapshotRequest{Key: key})
if err != nil { if err != nil {
return snapshot.Info{}, rewriteGRPCError(err) return snapshot.Info{}, errdefs.FromGRPC(err)
} }
return toInfo(resp.Info), nil return toInfo(resp.Info), nil
} }
@ -38,7 +34,7 @@ func (r *remoteSnapshotter) Stat(ctx context.Context, key string) (snapshot.Info
func (r *remoteSnapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) { func (r *remoteSnapshotter) Usage(ctx context.Context, key string) (snapshot.Usage, error) {
resp, err := r.client.Usage(ctx, &snapshotapi.UsageRequest{Key: key}) resp, err := r.client.Usage(ctx, &snapshotapi.UsageRequest{Key: key})
if err != nil { if err != nil {
return snapshot.Usage{}, rewriteGRPCError(err) return snapshot.Usage{}, errdefs.FromGRPC(err)
} }
return toUsage(resp), nil return toUsage(resp), nil
} }
@ -46,7 +42,7 @@ func (r *remoteSnapshotter) Usage(ctx context.Context, key string) (snapshot.Usa
func (r *remoteSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { func (r *remoteSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
resp, err := r.client.Mounts(ctx, &snapshotapi.MountsRequest{Key: key}) resp, err := r.client.Mounts(ctx, &snapshotapi.MountsRequest{Key: key})
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return toMounts(resp.Mounts), nil return toMounts(resp.Mounts), nil
} }
@ -54,7 +50,7 @@ func (r *remoteSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mou
func (r *remoteSnapshotter) Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error) { func (r *remoteSnapshotter) Prepare(ctx context.Context, key, parent string) ([]mount.Mount, error) {
resp, err := r.client.Prepare(ctx, &snapshotapi.PrepareSnapshotRequest{Key: key, Parent: parent}) resp, err := r.client.Prepare(ctx, &snapshotapi.PrepareSnapshotRequest{Key: key, Parent: parent})
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return toMounts(resp.Mounts), nil return toMounts(resp.Mounts), nil
} }
@ -62,7 +58,7 @@ func (r *remoteSnapshotter) Prepare(ctx context.Context, key, parent string) ([]
func (r *remoteSnapshotter) View(ctx context.Context, key, parent string) ([]mount.Mount, error) { func (r *remoteSnapshotter) View(ctx context.Context, key, parent string) ([]mount.Mount, error) {
resp, err := r.client.View(ctx, &snapshotapi.ViewSnapshotRequest{Key: key, Parent: parent}) resp, err := r.client.View(ctx, &snapshotapi.ViewSnapshotRequest{Key: key, Parent: parent})
if err != nil { if err != nil {
return nil, rewriteGRPCError(err) return nil, errdefs.FromGRPC(err)
} }
return toMounts(resp.Mounts), nil return toMounts(resp.Mounts), nil
} }
@ -72,18 +68,18 @@ func (r *remoteSnapshotter) Commit(ctx context.Context, name, key string) error
Name: name, Name: name,
Key: key, Key: key,
}) })
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
func (r *remoteSnapshotter) Remove(ctx context.Context, key string) error { func (r *remoteSnapshotter) Remove(ctx context.Context, key string) error {
_, err := r.client.Remove(ctx, &snapshotapi.RemoveSnapshotRequest{Key: key}) _, err := r.client.Remove(ctx, &snapshotapi.RemoveSnapshotRequest{Key: key})
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
func (r *remoteSnapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error { func (r *remoteSnapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
sc, err := r.client.List(ctx, &snapshotapi.ListSnapshotsRequest{}) sc, err := r.client.List(ctx, &snapshotapi.ListSnapshotsRequest{})
if err != nil { if err != nil {
rewriteGRPCError(err) errdefs.FromGRPC(err)
} }
for { for {
resp, err := sc.Recv() resp, err := sc.Recv()
@ -91,7 +87,7 @@ func (r *remoteSnapshotter) Walk(ctx context.Context, fn func(context.Context, s
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
return rewriteGRPCError(err) return errdefs.FromGRPC(err)
} }
if resp == nil { if resp == nil {
return nil return nil
@ -104,25 +100,6 @@ func (r *remoteSnapshotter) Walk(ctx context.Context, fn func(context.Context, s
} }
} }
func rewriteGRPCError(err error) error {
switch grpc.Code(errors.Cause(err)) {
case codes.AlreadyExists:
return snapshot.ErrSnapshotExist
case codes.NotFound:
return snapshot.ErrSnapshotNotExist
case codes.FailedPrecondition:
desc := grpc.ErrorDesc(errors.Cause(err))
if strings.Contains(desc, snapshot.ErrSnapshotNotActive.Error()) {
return snapshot.ErrSnapshotNotActive
}
if strings.Contains(desc, snapshot.ErrSnapshotNotCommitted.Error()) {
return snapshot.ErrSnapshotNotCommitted
}
}
return err
}
func toKind(kind snapshotapi.Kind) snapshot.Kind { func toKind(kind snapshotapi.Kind) snapshot.Kind {
if kind == snapshotapi.KindActive { if kind == snapshotapi.KindActive {
return snapshot.KindActive return snapshot.KindActive

View File

@ -6,6 +6,7 @@ import (
eventsapi "github.com/containerd/containerd/api/services/events/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1"
snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1" snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
@ -14,7 +15,6 @@ import (
protoempty "github.com/golang/protobuf/ptypes/empty" protoempty "github.com/golang/protobuf/ptypes/empty"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes"
) )
func init() { func init() {
@ -60,7 +60,7 @@ func (s *service) Prepare(ctx context.Context, pr *snapshotapi.PrepareSnapshotRe
// TODO: Lookup snapshot id from metadata store // TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.Prepare(ctx, pr.Key, pr.Parent) mounts, err := s.snapshotter.Prepare(ctx, pr.Key, pr.Parent)
if err != nil { if err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/snapshot/prepare", &eventsapi.SnapshotPrepare{ if err := s.emit(ctx, "/snapshot/prepare", &eventsapi.SnapshotPrepare{
@ -80,7 +80,7 @@ func (s *service) View(ctx context.Context, pr *snapshotapi.ViewSnapshotRequest)
// TODO: Lookup snapshot id from metadata store // TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.View(ctx, pr.Key, pr.Parent) mounts, err := s.snapshotter.View(ctx, pr.Key, pr.Parent)
if err != nil { if err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
return &snapshotapi.ViewSnapshotResponse{ return &snapshotapi.ViewSnapshotResponse{
Mounts: fromMounts(mounts), Mounts: fromMounts(mounts),
@ -93,7 +93,7 @@ func (s *service) Mounts(ctx context.Context, mr *snapshotapi.MountsRequest) (*s
// TODO: Lookup snapshot id from metadata store // TODO: Lookup snapshot id from metadata store
mounts, err := s.snapshotter.Mounts(ctx, mr.Key) mounts, err := s.snapshotter.Mounts(ctx, mr.Key)
if err != nil { if err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
return &snapshotapi.MountsResponse{ return &snapshotapi.MountsResponse{
Mounts: fromMounts(mounts), Mounts: fromMounts(mounts),
@ -105,7 +105,7 @@ func (s *service) Commit(ctx context.Context, cr *snapshotapi.CommitSnapshotRequ
// TODO: Apply namespace // TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store // TODO: Lookup snapshot id from metadata store
if err := s.snapshotter.Commit(ctx, cr.Name, cr.Key); err != nil { if err := s.snapshotter.Commit(ctx, cr.Name, cr.Key); err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/snapshot/commit", &eventsapi.SnapshotCommit{ if err := s.emit(ctx, "/snapshot/commit", &eventsapi.SnapshotCommit{
@ -122,7 +122,7 @@ func (s *service) Remove(ctx context.Context, rr *snapshotapi.RemoveSnapshotRequ
// TODO: Apply namespace // TODO: Apply namespace
// TODO: Lookup snapshot id from metadata store // TODO: Lookup snapshot id from metadata store
if err := s.snapshotter.Remove(ctx, rr.Key); err != nil { if err := s.snapshotter.Remove(ctx, rr.Key); err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
if err := s.emit(ctx, "/snapshot/remove", &eventsapi.SnapshotRemove{ if err := s.emit(ctx, "/snapshot/remove", &eventsapi.SnapshotRemove{
@ -138,7 +138,7 @@ func (s *service) Stat(ctx context.Context, sr *snapshotapi.StatSnapshotRequest)
// TODO: Apply namespace // TODO: Apply namespace
info, err := s.snapshotter.Stat(ctx, sr.Key) info, err := s.snapshotter.Stat(ctx, sr.Key)
if err != nil { if err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
return &snapshotapi.StatSnapshotResponse{Info: fromInfo(info)}, nil return &snapshotapi.StatSnapshotResponse{Info: fromInfo(info)}, nil
@ -184,26 +184,12 @@ func (s *service) Usage(ctx context.Context, ur *snapshotapi.UsageRequest) (*sna
// TODO: Apply namespace // TODO: Apply namespace
usage, err := s.snapshotter.Usage(ctx, ur.Key) usage, err := s.snapshotter.Usage(ctx, ur.Key)
if err != nil { if err != nil {
return nil, grpcError(err) return nil, errdefs.ToGRPC(err)
} }
return fromUsage(usage), nil return fromUsage(usage), nil
} }
func grpcError(err error) error {
if snapshot.IsNotExist(err) {
return grpc.Errorf(codes.NotFound, err.Error())
}
if snapshot.IsExist(err) {
return grpc.Errorf(codes.AlreadyExists, err.Error())
}
if snapshot.IsNotActive(err) || snapshot.IsNotCommitted(err) {
return grpc.Errorf(codes.FailedPrecondition, err.Error())
}
return err
}
func fromKind(kind snapshot.Kind) snapshotapi.Kind { func fromKind(kind snapshot.Kind) snapshotapi.Kind {
if kind == snapshot.KindActive { if kind == snapshot.KindActive {
return snapshotapi.KindActive return snapshotapi.KindActive

View File

@ -16,6 +16,7 @@ import (
"github.com/containerd/containerd/archive" "github.com/containerd/containerd/archive"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
@ -37,6 +38,9 @@ var (
empty = &google_protobuf.Empty{} empty = &google_protobuf.Empty{}
) )
// TODO(stevvooe): Clean up error mapping to avoid double mapping certain
// errors within helper methods.
func init() { func init() {
plugin.Register(&plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
@ -115,14 +119,7 @@ func (s *Service) Create(ctx context.Context, r *api.CreateTaskRequest) (*api.Cr
container, err := s.getContainer(ctx, r.ContainerID) container, err := s.getContainer(ctx, r.ContainerID)
if err != nil { if err != nil {
switch { return nil, errdefs.ToGRPC(err)
case metadata.IsNotFound(err):
return nil, grpc.Errorf(codes.NotFound, "container %v not found", r.ContainerID)
case metadata.IsExists(err):
return nil, grpc.Errorf(codes.AlreadyExists, "container %v already exists", r.ContainerID)
}
return nil, err
} }
opts := runtime.CreateOpts{ opts := runtime.CreateOpts{
@ -484,7 +481,7 @@ func (s *Service) getContainer(ctx context.Context, id string) (containers.Conta
container, err = store.Get(ctx, id) container, err = store.Get(ctx, id)
return err return err
}); err != nil { }); err != nil {
return containers.Container{}, err return containers.Container{}, errdefs.ToGRPC(err)
} }
return container, nil return container, nil
@ -493,12 +490,12 @@ func (s *Service) getContainer(ctx context.Context, id string) (containers.Conta
func (s *Service) getTask(ctx context.Context, id string) (runtime.Task, error) { func (s *Service) getTask(ctx context.Context, id string) (runtime.Task, error) {
container, err := s.getContainer(ctx, id) container, err := s.getContainer(ctx, id)
if err != nil { if err != nil {
return nil, grpc.Errorf(codes.InvalidArgument, "task %v not found: %s", id, err.Error()) return nil, err
} }
runtime, err := s.getRuntime(container.Runtime.Name) runtime, err := s.getRuntime(container.Runtime.Name)
if err != nil { if err != nil {
return nil, grpc.Errorf(codes.NotFound, "task %v not found: %s", id, err.Error()) return nil, errdefs.ToGRPCf(err, "runtime for task %v", id)
} }
t, err := runtime.Get(ctx, id) t, err := runtime.Get(ctx, id)
@ -512,7 +509,7 @@ func (s *Service) getTask(ctx context.Context, id string) (runtime.Task, error)
func (s *Service) getRuntime(name string) (runtime.Runtime, error) { func (s *Service) getRuntime(name string) (runtime.Runtime, error) {
runtime, ok := s.runtimes[name] runtime, ok := s.runtimes[name]
if !ok { if !ok {
return nil, fmt.Errorf("unknown runtime %q", name) return nil, grpc.Errorf(codes.NotFound, "unknown runtime %q", name)
} }
return runtime, nil return runtime, nil
} }

View File

@ -1,44 +0,0 @@
package snapshot
import "github.com/pkg/errors"
var (
// ErrSnapshotNotExist is returned when a snapshot cannot be found
ErrSnapshotNotExist = errors.New("snapshot does not exist")
// ErrSnapshotExist is returned when an operation to create a snapshot
// encounters a snapshot with the same key
ErrSnapshotExist = errors.New("snapshot already exists")
// ErrSnapshotNotActive is returned when a request which requires an
// active snapshot encounters a non-active snapshot.
ErrSnapshotNotActive = errors.New("snapshot is not active")
// ErrSnapshotNotCommitted is returned when a request which requires a
// committed snapshot encounters a non-committed snapshot.
ErrSnapshotNotCommitted = errors.New("snapshot is not committed")
)
// IsNotExist returns whether the error represents that a snapshot
// was not found.
func IsNotExist(err error) bool {
return errors.Cause(err) == ErrSnapshotNotExist
}
// IsExist returns whether the error represents whether a snapshot
// already exists using a provided key.
func IsExist(err error) bool {
return errors.Cause(err) == ErrSnapshotExist
}
// IsNotActive returns whether the error represents a request
// for a non active snapshot when an active snapshot is expected.
func IsNotActive(err error) bool {
return errors.Cause(err) == ErrSnapshotNotActive
}
// IsNotCommitted returns whether the error represents a request
// for a non committed snapshot when a committed snapshot is expected.
func IsNotCommitted(err error) bool {
return errors.Cause(err) == ErrSnapshotNotCommitted
}

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/snapshot" "github.com/containerd/containerd/snapshot"
db "github.com/containerd/containerd/snapshot/storage/proto" db "github.com/containerd/containerd/snapshot/storage/proto"
@ -129,12 +130,12 @@ func CreateActive(ctx context.Context, key, parent string, readonly bool) (a Act
} }
if parentS.Kind != db.KindCommitted { if parentS.Kind != db.KindCommitted {
return errors.Wrap(snapshot.ErrSnapshotNotCommitted, "parent is not committed snapshot") return errors.Wrap(errdefs.ErrInvalidArgument, "parent is not committed snapshot")
} }
} }
b := bkt.Get([]byte(key)) b := bkt.Get([]byte(key))
if len(b) != 0 { if len(b) != 0 {
return snapshot.ErrSnapshotExist return errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %v", key)
} }
id, err := nextSequence(ctx) id, err := nextSequence(ctx)
@ -183,7 +184,7 @@ func GetActive(ctx context.Context, key string) (a Active, err error) {
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
b := bkt.Get([]byte(key)) b := bkt.Get([]byte(key))
if len(b) == 0 { if len(b) == 0 {
return snapshot.ErrSnapshotNotExist return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
} }
var ss db.Snapshot var ss db.Snapshot
@ -191,7 +192,7 @@ func GetActive(ctx context.Context, key string) (a Active, err error) {
return errors.Wrap(err, "failed to unmarshal snapshot") return errors.Wrap(err, "failed to unmarshal snapshot")
} }
if ss.Kind != db.KindActive { if ss.Kind != db.KindActive {
return snapshot.ErrSnapshotNotActive return errors.Wrapf(errdefs.ErrFailedPrecondition, "requested snapshot %v not active", key)
} }
a.ID = fmt.Sprintf("%d", ss.ID) a.ID = fmt.Sprintf("%d", ss.ID)
@ -225,7 +226,7 @@ func Remove(ctx context.Context, key string) (id string, k snapshot.Kind, err er
var ss db.Snapshot var ss db.Snapshot
b := bkt.Get([]byte(key)) b := bkt.Get([]byte(key))
if len(b) == 0 { if len(b) == 0 {
return snapshot.ErrSnapshotNotExist return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
} }
if err := proto.Unmarshal(b, &ss); err != nil { if err := proto.Unmarshal(b, &ss); err != nil {
@ -273,7 +274,7 @@ func CommitActive(ctx context.Context, key, name string, usage snapshot.Usage) (
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
b := bkt.Get([]byte(name)) b := bkt.Get([]byte(name))
if len(b) != 0 { if len(b) != 0 {
return errors.Wrap(snapshot.ErrSnapshotExist, "committed name already exists") return errors.Wrapf(errdefs.ErrAlreadyExists, "committed snapshot %v", name)
} }
var ss db.Snapshot var ss db.Snapshot
@ -281,7 +282,7 @@ func CommitActive(ctx context.Context, key, name string, usage snapshot.Usage) (
return errors.Wrap(err, "failed to get active snapshot") return errors.Wrap(err, "failed to get active snapshot")
} }
if ss.Kind != db.KindActive { if ss.Kind != db.KindActive {
return snapshot.ErrSnapshotNotActive return errors.Wrapf(errdefs.ErrFailedPrecondition, "snapshot %v is not active", name)
} }
if ss.Readonly { if ss.Readonly {
return errors.Errorf("active snapshot is readonly") return errors.Errorf("active snapshot is readonly")
@ -340,12 +341,12 @@ func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bol
} }
nbkt := t.tx.Bucket(bucketKeyStorageVersion) nbkt := t.tx.Bucket(bucketKeyStorageVersion)
if nbkt == nil { if nbkt == nil {
return errors.Wrap(snapshot.ErrSnapshotNotExist, "bucket does not exist") return errors.Wrapf(errdefs.ErrNotFound, "bucket does not exist")
} }
bkt := nbkt.Bucket([]byte(namespace)) bkt := nbkt.Bucket([]byte(namespace))
if bkt == nil { if bkt == nil {
return errors.Wrap(snapshot.ErrSnapshotNotExist, "namespace not available in snapshotter") return errors.Wrapf(errdefs.ErrNotFound, "namespace not available in snapshotter")
} }
return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents)) return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents))
@ -409,7 +410,7 @@ func parents(bkt *bolt.Bucket, parent *db.Snapshot) (parents []string, err error
func getSnapshot(bkt *bolt.Bucket, key string, ss *db.Snapshot) error { func getSnapshot(bkt *bolt.Bucket, key string, ss *db.Snapshot) error {
b := bkt.Get([]byte(key)) b := bkt.Get([]byte(key))
if len(b) == 0 { if len(b) == 0 {
return snapshot.ErrSnapshotNotExist return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
} }
if err := proto.Unmarshal(b, ss); err != nil { if err := proto.Unmarshal(b, ss); err != nil {
return errors.Wrap(err, "failed to unmarshal snapshot") return errors.Wrap(err, "failed to unmarshal snapshot")

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/snapshot" "github.com/containerd/containerd/snapshot"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -205,7 +206,7 @@ func assertNotExist(t *testing.T, err error) {
if err == nil { if err == nil {
t.Fatal("Expected not exist error") t.Fatal("Expected not exist error")
} }
if !snapshot.IsNotExist(err) { if !errdefs.IsNotFound(err) {
t.Fatalf("Expected not exist error, got %+v", err) t.Fatalf("Expected not exist error, got %+v", err)
} }
} }
@ -214,7 +215,7 @@ func assertNotActive(t *testing.T, err error) {
if err == nil { if err == nil {
t.Fatal("Expected not active error") t.Fatal("Expected not active error")
} }
if !snapshot.IsNotActive(err) { if !errdefs.IsFailedPrecondition(err) {
t.Fatalf("Expected not active error, got %+v", err) t.Fatalf("Expected not active error, got %+v", err)
} }
} }
@ -223,7 +224,7 @@ func assertNotCommitted(t *testing.T, err error) {
if err == nil { if err == nil {
t.Fatal("Expected active error") t.Fatal("Expected active error")
} }
if !snapshot.IsNotCommitted(err) { if !errdefs.IsInvalidArgument(err) {
t.Fatalf("Expected active error, got %+v", err) t.Fatalf("Expected active error, got %+v", err)
} }
} }
@ -232,7 +233,7 @@ func assertExist(t *testing.T, err error) {
if err == nil { if err == nil {
t.Fatal("Expected exist error") t.Fatal("Expected exist error")
} }
if !snapshot.IsExist(err) { if !errdefs.IsAlreadyExists(err) {
t.Fatalf("Expected exist error, got %+v", err) t.Fatalf("Expected exist error, got %+v", err)
} }
} }