mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
obj: Object api handle all errors in common location. (#1343)
This commit is contained in:
parent
5c33b68318
commit
91588209fa
@ -105,6 +105,10 @@ const (
|
||||
ErrInvalidQuerySignatureAlgo
|
||||
ErrInvalidQueryParams
|
||||
// Add new error codes here.
|
||||
|
||||
// Extended errors.
|
||||
ErrInsufficientReadResources
|
||||
ErrInsufficientWriteResources
|
||||
)
|
||||
|
||||
// error code to APIError structure, these fields carry respective
|
||||
@ -185,6 +189,16 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
||||
Description: "We encountered an internal error, please try again.",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
ErrInsufficientReadResources: {
|
||||
Code: "InternalError",
|
||||
Description: "We cannot satisfy sufficient read resources requested at this moment, please try again.",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
ErrInsufficientWriteResources: {
|
||||
Code: "InternalError",
|
||||
Description: "We cannot satisfy sufficient write resources requested at this moment, please try again.",
|
||||
HTTPStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
ErrInvalidAccessKeyID: {
|
||||
Code: "InvalidAccessKeyID",
|
||||
Description: "The access key ID you provided does not exist in our records.",
|
||||
|
@ -87,14 +87,6 @@ func (o objectAPI) ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarke
|
||||
if !IsValidObjectPrefix(prefix) {
|
||||
return ListMultipartsInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: prefix})
|
||||
}
|
||||
if _, e := o.storage.StatVol(minioMetaVolume); e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
e = o.storage.MakeVol(minioMetaVolume)
|
||||
if e != nil {
|
||||
return ListMultipartsInfo{}, probe.NewError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Verify if delimiter is anything other than '/', which we do not support.
|
||||
if delimiter != "" && delimiter != slashSeparator {
|
||||
return ListMultipartsInfo{}, probe.NewError(UnsupportedDelimiter{
|
||||
@ -292,22 +284,21 @@ func (o objectAPI) NewMultipartUpload(bucket, object string) (string, *probe.Err
|
||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
||||
if _, e = o.storage.StatFile(minioMetaVolume, uploadIDPath); e != nil {
|
||||
if e != errFileNotFound {
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e, minioMetaVolume, uploadIDPath))
|
||||
}
|
||||
// uploadIDPath doesn't exist, so create empty file to reserve the name
|
||||
var w io.WriteCloser
|
||||
if w, e = o.storage.CreateFile(minioMetaVolume, uploadIDPath); e == nil {
|
||||
// Just write some data for erasure code, rather than zero bytes.
|
||||
w.Write([]byte(uploadID))
|
||||
if _, e = w.Write([]byte(uploadID)); e != nil {
|
||||
return "", probe.NewError(toObjectErr(e, minioMetaVolume, uploadIDPath))
|
||||
}
|
||||
// Close the writer.
|
||||
if e = w.Close(); e != nil {
|
||||
return "", probe.NewError(e)
|
||||
}
|
||||
} else {
|
||||
if e == errDiskFull {
|
||||
return "", probe.NewError(StorageFull{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e, minioMetaVolume, uploadIDPath))
|
||||
}
|
||||
return uploadID, nil
|
||||
}
|
||||
@ -358,19 +349,7 @@ func (o objectAPI) PutObjectPart(bucket, object, uploadID string, partID int, si
|
||||
partSuffix := fmt.Sprintf("%s.%d.%s", uploadID, partID, md5Hex)
|
||||
fileWriter, e := o.storage.CreateFile(minioMetaVolume, path.Join(bucket, object, partSuffix))
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return "", probe.NewError(BucketNotFound{
|
||||
Bucket: bucket,
|
||||
})
|
||||
} else if e == errIsNotRegular {
|
||||
return "", probe.NewError(ObjectExistsAsPrefix{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
})
|
||||
} else if e == errDiskFull {
|
||||
return "", probe.NewError(StorageFull{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
|
||||
// Initialize md5 writer.
|
||||
@ -383,10 +362,7 @@ func (o objectAPI) PutObjectPart(bucket, object, uploadID string, partID int, si
|
||||
if size > 0 {
|
||||
if _, e = io.CopyN(multiWriter, data, size); e != nil {
|
||||
safeCloseAndRemove(fileWriter)
|
||||
if e == io.ErrUnexpectedEOF || e == io.ErrShortWrite {
|
||||
return "", probe.NewError(IncompleteBody{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e))
|
||||
}
|
||||
// Reader shouldn't have more data what mentioned in size argument.
|
||||
// reading one more byte from the reader to validate it.
|
||||
@ -398,7 +374,7 @@ func (o objectAPI) PutObjectPart(bucket, object, uploadID string, partID int, si
|
||||
} else {
|
||||
if _, e = io.Copy(multiWriter, data); e != nil {
|
||||
safeCloseAndRemove(fileWriter)
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e))
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,15 +400,6 @@ func (o objectAPI) ListObjectParts(bucket, object, uploadID string, partNumberMa
|
||||
if !IsValidObjectName(object) {
|
||||
return ListPartsInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
|
||||
}
|
||||
// Create minio meta volume, if it doesn't exist yet.
|
||||
if _, e := o.storage.StatVol(minioMetaVolume); e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
e = o.storage.MakeVol(minioMetaVolume)
|
||||
if e != nil {
|
||||
return ListPartsInfo{}, probe.NewError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
if status, e := o.isUploadIDExists(bucket, object, uploadID); e != nil {
|
||||
return ListPartsInfo{}, probe.NewError(e)
|
||||
} else if !status {
|
||||
@ -445,10 +412,10 @@ func (o objectAPI) ListObjectParts(bucket, object, uploadID string, partNumberMa
|
||||
// Figure out the marker for the next subsequent calls, if the
|
||||
// partNumberMarker is already set.
|
||||
if partNumberMarker > 0 {
|
||||
uploadIDPartPrefix := uploadIDPath + "." + strconv.Itoa(partNumberMarker) + "."
|
||||
fileInfos, _, e := o.storage.ListFiles(minioMetaVolume, uploadIDPartPrefix, "", false, 1)
|
||||
partNumberMarkerPath := uploadIDPath + "." + strconv.Itoa(partNumberMarker) + "."
|
||||
fileInfos, _, e := o.storage.ListFiles(minioMetaVolume, partNumberMarkerPath, "", false, 1)
|
||||
if e != nil {
|
||||
return result, probe.NewError(e)
|
||||
return result, probe.NewError(toObjectErr(e, minioMetaVolume, partNumberMarkerPath))
|
||||
}
|
||||
if len(fileInfos) == 0 {
|
||||
return result, probe.NewError(InvalidPart{})
|
||||
@ -521,19 +488,7 @@ func (o objectAPI) CompleteMultipartUpload(bucket string, object string, uploadI
|
||||
|
||||
fileWriter, e := o.storage.CreateFile(bucket, object)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return "", probe.NewError(BucketNotFound{
|
||||
Bucket: bucket,
|
||||
})
|
||||
} else if e == errIsNotRegular {
|
||||
return "", probe.NewError(ObjectExistsAsPrefix{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
})
|
||||
} else if e == errDiskFull {
|
||||
return "", probe.NewError(StorageFull{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
|
||||
var md5Sums []string
|
||||
@ -585,16 +540,14 @@ func (o objectAPI) removeMultipartUpload(bucket, object, uploadID string) *probe
|
||||
if !IsValidObjectName(object) {
|
||||
return probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
|
||||
}
|
||||
if _, e := o.storage.StatVol(minioMetaVolume); e != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
marker := ""
|
||||
for {
|
||||
uploadIDPath := path.Join(bucket, object, uploadID)
|
||||
fileInfos, eof, e := o.storage.ListFiles(minioMetaVolume, uploadIDPath, marker, false, 1000)
|
||||
if e != nil {
|
||||
return probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
|
||||
|
||||
return probe.NewError(InvalidUploadID{UploadID: uploadID})
|
||||
}
|
||||
for _, fileInfo := range fileInfos {
|
||||
o.storage.DeleteFile(minioMetaVolume, fileInfo.Name)
|
||||
|
@ -46,12 +46,16 @@ func (o objectAPI) MakeBucket(bucket string) *probe.Error {
|
||||
return probe.NewError(BucketNameInvalid{Bucket: bucket})
|
||||
}
|
||||
if e := o.storage.MakeVol(bucket); e != nil {
|
||||
if e == errVolumeExists {
|
||||
return probe.NewError(BucketExists{Bucket: bucket})
|
||||
} else if e == errDiskFull {
|
||||
return probe.NewError(StorageFull{})
|
||||
return probe.NewError(toObjectErr(e, bucket))
|
||||
}
|
||||
// This happens for the first time, but keep this here since this
|
||||
// is the only place where it can be made expensive optimizing all
|
||||
// other calls.
|
||||
// Create minio meta volume, if it doesn't exist yet.
|
||||
if e := o.storage.MakeVol(minioMetaVolume); e != nil {
|
||||
if e != errVolumeExists {
|
||||
return probe.NewError(toObjectErr(e, minioMetaVolume))
|
||||
}
|
||||
return probe.NewError(e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -64,10 +68,7 @@ func (o objectAPI) GetBucketInfo(bucket string) (BucketInfo, *probe.Error) {
|
||||
}
|
||||
vi, e := o.storage.StatVol(bucket)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return BucketInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
}
|
||||
return BucketInfo{}, probe.NewError(e)
|
||||
return BucketInfo{}, probe.NewError(toObjectErr(e, bucket))
|
||||
}
|
||||
return BucketInfo{
|
||||
Name: bucket,
|
||||
@ -82,7 +83,7 @@ func (o objectAPI) ListBuckets() ([]BucketInfo, *probe.Error) {
|
||||
var bucketInfos []BucketInfo
|
||||
vols, e := o.storage.ListVols()
|
||||
if e != nil {
|
||||
return nil, probe.NewError(e)
|
||||
return nil, probe.NewError(toObjectErr(e))
|
||||
}
|
||||
for _, vol := range vols {
|
||||
// StorageAPI can send volume names which are incompatible
|
||||
@ -107,12 +108,7 @@ func (o objectAPI) DeleteBucket(bucket string) *probe.Error {
|
||||
return probe.NewError(BucketNameInvalid{Bucket: bucket})
|
||||
}
|
||||
if e := o.storage.DeleteVol(bucket); e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
} else if e == errVolumeNotEmpty {
|
||||
return probe.NewError(BucketNotEmpty{Bucket: bucket})
|
||||
}
|
||||
return probe.NewError(e)
|
||||
return probe.NewError(toObjectErr(e))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -131,17 +127,7 @@ func (o objectAPI) GetObject(bucket, object string, startOffset int64) (io.ReadC
|
||||
}
|
||||
r, e := o.storage.ReadFile(bucket, object, startOffset)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return nil, probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
} else if e == errFileNotFound {
|
||||
return nil, probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
|
||||
} else if e == errIsNotRegular {
|
||||
return nil, probe.NewError(ObjectExistsAsPrefix{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
})
|
||||
}
|
||||
return nil, probe.NewError(e)
|
||||
return nil, probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@ -158,14 +144,7 @@ func (o objectAPI) GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Erro
|
||||
}
|
||||
fi, e := o.storage.StatFile(bucket, object)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return ObjectInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
} else if e == errFileNotFound || e == errIsNotRegular {
|
||||
return ObjectInfo{}, probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
|
||||
// Handle more lower level errors if needed.
|
||||
} else {
|
||||
return ObjectInfo{}, probe.NewError(e)
|
||||
}
|
||||
return ObjectInfo{}, probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
contentType := "application/octet-stream"
|
||||
if objectExt := filepath.Ext(object); objectExt != "" {
|
||||
@ -213,19 +192,7 @@ func (o objectAPI) PutObject(bucket string, object string, size int64, data io.R
|
||||
}
|
||||
fileWriter, e := o.storage.CreateFile(bucket, object)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return "", probe.NewError(BucketNotFound{
|
||||
Bucket: bucket,
|
||||
})
|
||||
} else if e == errIsNotRegular {
|
||||
return "", probe.NewError(ObjectExistsAsPrefix{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
})
|
||||
} else if e == errDiskFull {
|
||||
return "", probe.NewError(StorageFull{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
|
||||
// Initialize md5 writer.
|
||||
@ -240,10 +207,7 @@ func (o objectAPI) PutObject(bucket string, object string, size int64, data io.R
|
||||
if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
|
||||
return "", probe.NewError(clErr)
|
||||
}
|
||||
if e == io.ErrUnexpectedEOF {
|
||||
return "", probe.NewError(IncompleteBody{})
|
||||
}
|
||||
return "", probe.NewError(e)
|
||||
return "", probe.NewError(toObjectErr(e))
|
||||
}
|
||||
} else {
|
||||
if _, e = io.Copy(multiWriter, data); e != nil {
|
||||
@ -286,18 +250,7 @@ func (o objectAPI) DeleteObject(bucket, object string) *probe.Error {
|
||||
return probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
|
||||
}
|
||||
if e := o.storage.DeleteFile(bucket, object); e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
} else if e == errFileNotFound {
|
||||
return probe.NewError(ObjectNotFound{Bucket: bucket})
|
||||
}
|
||||
if e == errFileNotFound {
|
||||
return probe.NewError(ObjectNotFound{
|
||||
Bucket: bucket,
|
||||
Object: object,
|
||||
})
|
||||
}
|
||||
return probe.NewError(e)
|
||||
return probe.NewError(toObjectErr(e, bucket, object))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -331,10 +284,7 @@ func (o objectAPI) ListObjects(bucket, prefix, marker, delimiter string, maxKeys
|
||||
}
|
||||
fileInfos, eof, e := o.storage.ListFiles(bucket, prefix, marker, recursive, maxKeys)
|
||||
if e != nil {
|
||||
if e == errVolumeNotFound {
|
||||
return ListObjectsInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
|
||||
}
|
||||
return ListObjectsInfo{}, probe.NewError(e)
|
||||
return ListObjectsInfo{}, probe.NewError(toObjectErr(e, bucket))
|
||||
}
|
||||
if maxKeys == 0 {
|
||||
return ListObjectsInfo{}, nil
|
||||
|
@ -16,7 +16,49 @@
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Converts underlying storage error. Convenience function written to
|
||||
// handle all cases where we have known types of errors returned by
|
||||
// underlying storage layer.
|
||||
func toObjectErr(err error, params ...string) error {
|
||||
switch err {
|
||||
case errVolumeNotFound:
|
||||
if len(params) >= 1 {
|
||||
return BucketNotFound{Bucket: params[0]}
|
||||
}
|
||||
case errVolumeExists:
|
||||
if len(params) >= 1 {
|
||||
return BucketExists{Bucket: params[0]}
|
||||
}
|
||||
case errDiskFull:
|
||||
return StorageFull{}
|
||||
case errReadQuorum:
|
||||
return StorageInsufficientReadResources{}
|
||||
case errWriteQuorum:
|
||||
return StorageInsufficientWriteResources{}
|
||||
case errFileNotFound:
|
||||
if len(params) >= 2 {
|
||||
return ObjectNotFound{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errIsNotRegular:
|
||||
if len(params) >= 2 {
|
||||
return ObjectExistsAsPrefix{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case io.ErrUnexpectedEOF, io.ErrShortWrite:
|
||||
return IncompleteBody{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// StorageFull storage ran out of space
|
||||
type StorageFull struct{}
|
||||
@ -25,6 +67,20 @@ func (e StorageFull) Error() string {
|
||||
return "Storage reached its minimum free disk threshold."
|
||||
}
|
||||
|
||||
// StorageInsufficientReadResources storage cannot satisfy quorum for read operation.
|
||||
type StorageInsufficientReadResources struct{}
|
||||
|
||||
func (e StorageInsufficientReadResources) Error() string {
|
||||
return "Storage resources are insufficient for the read operation."
|
||||
}
|
||||
|
||||
// StorageInsufficientWriteResources storage cannot satisfy quorum for write operation.
|
||||
type StorageInsufficientWriteResources struct{}
|
||||
|
||||
func (e StorageInsufficientWriteResources) Error() string {
|
||||
return "Stroage resources are insufficient for the write operation."
|
||||
}
|
||||
|
||||
// GenericError - generic object layer error.
|
||||
type GenericError struct {
|
||||
Bucket string
|
||||
|
@ -44,4 +44,7 @@ var errVolumeAccessDenied = errors.New("volume access denied")
|
||||
var errFileAccessDenied = errors.New("file access denied")
|
||||
|
||||
// errReadQuorum - did not meet read quorum.
|
||||
var errReadQuorum = errors.New("I/O error. do not meet read quorum")
|
||||
var errReadQuorum = errors.New("I/O error. did not meet read quorum.")
|
||||
|
||||
// errWriteQuorum - did not meet write quorum.
|
||||
var errWriteQuorum = errors.New("I/O error. did not meet write quorum.")
|
||||
|
@ -413,6 +413,10 @@ func writeWebErrorResponse(w http.ResponseWriter, err error) {
|
||||
apiErrCode = ErrNoSuchKey
|
||||
case ObjectNameInvalid:
|
||||
apiErrCode = ErrNoSuchKey
|
||||
case StorageInsufficientWriteResources:
|
||||
apiErrCode = ErrInsufficientWriteResources
|
||||
case StorageInsufficientReadResources:
|
||||
apiErrCode = ErrInsufficientReadResources
|
||||
default:
|
||||
apiErrCode = ErrInternalError
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func (xl XL) writeErasure(volume, path string, reader *io.PipeReader) {
|
||||
|
||||
// Remove previous temp writers for any failure.
|
||||
xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...)
|
||||
reader.CloseWithError(err)
|
||||
reader.CloseWithError(errWriteQuorum)
|
||||
return
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ func (xl XL) writeErasure(volume, path string, reader *io.PipeReader) {
|
||||
|
||||
// Remove previous temp writers for any failure.
|
||||
xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...)
|
||||
reader.CloseWithError(err)
|
||||
reader.CloseWithError(errWriteQuorum)
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user