obj: Object api handle all errors in common location. (#1343)

master
Harshavardhana 9 years ago committed by Harshavardhana
parent 5c33b68318
commit 91588209fa
  1. 14
      api-errors.go
  2. 75
      object-api-multipart.go
  3. 86
      object-api.go
  4. 58
      object-errors.go
  5. 5
      storage-errors.go
  6. 4
      web-handlers.go
  7. 4
      xl-v1-createfile.go

@ -105,6 +105,10 @@ const (
ErrInvalidQuerySignatureAlgo ErrInvalidQuerySignatureAlgo
ErrInvalidQueryParams ErrInvalidQueryParams
// Add new error codes here. // Add new error codes here.
// Extended errors.
ErrInsufficientReadResources
ErrInsufficientWriteResources
) )
// error code to APIError structure, these fields carry respective // 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.", Description: "We encountered an internal error, please try again.",
HTTPStatusCode: http.StatusInternalServerError, 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: { ErrInvalidAccessKeyID: {
Code: "InvalidAccessKeyID", Code: "InvalidAccessKeyID",
Description: "The access key ID you provided does not exist in our records.", 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) { if !IsValidObjectPrefix(prefix) {
return ListMultipartsInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: 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. // Verify if delimiter is anything other than '/', which we do not support.
if delimiter != "" && delimiter != slashSeparator { if delimiter != "" && delimiter != slashSeparator {
return ListMultipartsInfo{}, probe.NewError(UnsupportedDelimiter{ 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) uploadIDPath := path.Join(bucket, object, uploadID)
if _, e = o.storage.StatFile(minioMetaVolume, uploadIDPath); e != nil { if _, e = o.storage.StatFile(minioMetaVolume, uploadIDPath); e != nil {
if e != errFileNotFound { 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 // uploadIDPath doesn't exist, so create empty file to reserve the name
var w io.WriteCloser var w io.WriteCloser
if w, e = o.storage.CreateFile(minioMetaVolume, uploadIDPath); e == nil { if w, e = o.storage.CreateFile(minioMetaVolume, uploadIDPath); e == nil {
// Just write some data for erasure code, rather than zero bytes. // 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. // Close the writer.
if e = w.Close(); e != nil { if e = w.Close(); e != nil {
return "", probe.NewError(e) return "", probe.NewError(e)
} }
} else { } else {
if e == errDiskFull { return "", probe.NewError(toObjectErr(e, minioMetaVolume, uploadIDPath))
return "", probe.NewError(StorageFull{})
}
return "", probe.NewError(e)
} }
return uploadID, nil 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) partSuffix := fmt.Sprintf("%s.%d.%s", uploadID, partID, md5Hex)
fileWriter, e := o.storage.CreateFile(minioMetaVolume, path.Join(bucket, object, partSuffix)) fileWriter, e := o.storage.CreateFile(minioMetaVolume, path.Join(bucket, object, partSuffix))
if e != nil { if e != nil {
if e == errVolumeNotFound { return "", probe.NewError(toObjectErr(e, bucket, object))
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)
} }
// Initialize md5 writer. // Initialize md5 writer.
@ -383,10 +362,7 @@ func (o objectAPI) PutObjectPart(bucket, object, uploadID string, partID int, si
if size > 0 { if size > 0 {
if _, e = io.CopyN(multiWriter, data, size); e != nil { if _, e = io.CopyN(multiWriter, data, size); e != nil {
safeCloseAndRemove(fileWriter) safeCloseAndRemove(fileWriter)
if e == io.ErrUnexpectedEOF || e == io.ErrShortWrite { return "", probe.NewError(toObjectErr(e))
return "", probe.NewError(IncompleteBody{})
}
return "", probe.NewError(e)
} }
// Reader shouldn't have more data what mentioned in size argument. // Reader shouldn't have more data what mentioned in size argument.
// reading one more byte from the reader to validate it. // 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 { } else {
if _, e = io.Copy(multiWriter, data); e != nil { if _, e = io.Copy(multiWriter, data); e != nil {
safeCloseAndRemove(fileWriter) 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) { if !IsValidObjectName(object) {
return ListPartsInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: 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 { if status, e := o.isUploadIDExists(bucket, object, uploadID); e != nil {
return ListPartsInfo{}, probe.NewError(e) return ListPartsInfo{}, probe.NewError(e)
} else if !status { } 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 // Figure out the marker for the next subsequent calls, if the
// partNumberMarker is already set. // partNumberMarker is already set.
if partNumberMarker > 0 { if partNumberMarker > 0 {
uploadIDPartPrefix := uploadIDPath + "." + strconv.Itoa(partNumberMarker) + "." partNumberMarkerPath := uploadIDPath + "." + strconv.Itoa(partNumberMarker) + "."
fileInfos, _, e := o.storage.ListFiles(minioMetaVolume, uploadIDPartPrefix, "", false, 1) fileInfos, _, e := o.storage.ListFiles(minioMetaVolume, partNumberMarkerPath, "", false, 1)
if e != nil { if e != nil {
return result, probe.NewError(e) return result, probe.NewError(toObjectErr(e, minioMetaVolume, partNumberMarkerPath))
} }
if len(fileInfos) == 0 { if len(fileInfos) == 0 {
return result, probe.NewError(InvalidPart{}) 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) fileWriter, e := o.storage.CreateFile(bucket, object)
if e != nil { if e != nil {
if e == errVolumeNotFound { return "", probe.NewError(toObjectErr(e, bucket, object))
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)
} }
var md5Sums []string var md5Sums []string
@ -585,16 +540,14 @@ func (o objectAPI) removeMultipartUpload(bucket, object, uploadID string) *probe
if !IsValidObjectName(object) { if !IsValidObjectName(object) {
return probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object}) return probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
} }
if _, e := o.storage.StatVol(minioMetaVolume); e != nil {
return nil
}
marker := "" marker := ""
for { for {
uploadIDPath := path.Join(bucket, object, uploadID) uploadIDPath := path.Join(bucket, object, uploadID)
fileInfos, eof, e := o.storage.ListFiles(minioMetaVolume, uploadIDPath, marker, false, 1000) fileInfos, eof, e := o.storage.ListFiles(minioMetaVolume, uploadIDPath, marker, false, 1000)
if e != nil { if e != nil {
return probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
return probe.NewError(InvalidUploadID{UploadID: uploadID})
} }
for _, fileInfo := range fileInfos { for _, fileInfo := range fileInfos {
o.storage.DeleteFile(minioMetaVolume, fileInfo.Name) o.storage.DeleteFile(minioMetaVolume, fileInfo.Name)

@ -46,12 +46,16 @@ func (o objectAPI) MakeBucket(bucket string) *probe.Error {
return probe.NewError(BucketNameInvalid{Bucket: bucket}) return probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
if e := o.storage.MakeVol(bucket); e != nil { if e := o.storage.MakeVol(bucket); e != nil {
if e == errVolumeExists { return probe.NewError(toObjectErr(e, bucket))
return probe.NewError(BucketExists{Bucket: bucket}) }
} else if e == errDiskFull { // This happens for the first time, but keep this here since this
return probe.NewError(StorageFull{}) // 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 return nil
} }
@ -64,10 +68,7 @@ func (o objectAPI) GetBucketInfo(bucket string) (BucketInfo, *probe.Error) {
} }
vi, e := o.storage.StatVol(bucket) vi, e := o.storage.StatVol(bucket)
if e != nil { if e != nil {
if e == errVolumeNotFound { return BucketInfo{}, probe.NewError(toObjectErr(e, bucket))
return BucketInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
}
return BucketInfo{}, probe.NewError(e)
} }
return BucketInfo{ return BucketInfo{
Name: bucket, Name: bucket,
@ -82,7 +83,7 @@ func (o objectAPI) ListBuckets() ([]BucketInfo, *probe.Error) {
var bucketInfos []BucketInfo var bucketInfos []BucketInfo
vols, e := o.storage.ListVols() vols, e := o.storage.ListVols()
if e != nil { if e != nil {
return nil, probe.NewError(e) return nil, probe.NewError(toObjectErr(e))
} }
for _, vol := range vols { for _, vol := range vols {
// StorageAPI can send volume names which are incompatible // 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}) return probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
if e := o.storage.DeleteVol(bucket); e != nil { if e := o.storage.DeleteVol(bucket); e != nil {
if e == errVolumeNotFound { return probe.NewError(toObjectErr(e))
return probe.NewError(BucketNotFound{Bucket: bucket})
} else if e == errVolumeNotEmpty {
return probe.NewError(BucketNotEmpty{Bucket: bucket})
}
return probe.NewError(e)
} }
return nil 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) r, e := o.storage.ReadFile(bucket, object, startOffset)
if e != nil { if e != nil {
if e == errVolumeNotFound { return nil, probe.NewError(toObjectErr(e, bucket, object))
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 r, nil return r, nil
} }
@ -158,14 +144,7 @@ func (o objectAPI) GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Erro
} }
fi, e := o.storage.StatFile(bucket, object) fi, e := o.storage.StatFile(bucket, object)
if e != nil { if e != nil {
if e == errVolumeNotFound { return ObjectInfo{}, probe.NewError(toObjectErr(e, bucket, object))
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)
}
} }
contentType := "application/octet-stream" contentType := "application/octet-stream"
if objectExt := filepath.Ext(object); objectExt != "" { 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) fileWriter, e := o.storage.CreateFile(bucket, object)
if e != nil { if e != nil {
if e == errVolumeNotFound { return "", probe.NewError(toObjectErr(e, bucket, object))
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)
} }
// Initialize md5 writer. // 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 { if clErr := safeCloseAndRemove(fileWriter); clErr != nil {
return "", probe.NewError(clErr) return "", probe.NewError(clErr)
} }
if e == io.ErrUnexpectedEOF { return "", probe.NewError(toObjectErr(e))
return "", probe.NewError(IncompleteBody{})
}
return "", probe.NewError(e)
} }
} else { } else {
if _, e = io.Copy(multiWriter, data); e != nil { 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}) return probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
} }
if e := o.storage.DeleteFile(bucket, object); e != nil { if e := o.storage.DeleteFile(bucket, object); e != nil {
if e == errVolumeNotFound { return probe.NewError(toObjectErr(e, bucket, object))
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 nil 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) fileInfos, eof, e := o.storage.ListFiles(bucket, prefix, marker, recursive, maxKeys)
if e != nil { if e != nil {
if e == errVolumeNotFound { return ListObjectsInfo{}, probe.NewError(toObjectErr(e, bucket))
return ListObjectsInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
}
return ListObjectsInfo{}, probe.NewError(e)
} }
if maxKeys == 0 { if maxKeys == 0 {
return ListObjectsInfo{}, nil return ListObjectsInfo{}, nil

@ -16,7 +16,49 @@
package main 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 // StorageFull storage ran out of space
type StorageFull struct{} type StorageFull struct{}
@ -25,6 +67,20 @@ func (e StorageFull) Error() string {
return "Storage reached its minimum free disk threshold." 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. // GenericError - generic object layer error.
type GenericError struct { type GenericError struct {
Bucket string Bucket string

@ -44,4 +44,7 @@ var errVolumeAccessDenied = errors.New("volume access denied")
var errFileAccessDenied = errors.New("file access denied") var errFileAccessDenied = errors.New("file access denied")
// errReadQuorum - did not meet read quorum. // 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 apiErrCode = ErrNoSuchKey
case ObjectNameInvalid: case ObjectNameInvalid:
apiErrCode = ErrNoSuchKey apiErrCode = ErrNoSuchKey
case StorageInsufficientWriteResources:
apiErrCode = ErrInsufficientWriteResources
case StorageInsufficientReadResources:
apiErrCode = ErrInsufficientReadResources
default: default:
apiErrCode = ErrInternalError apiErrCode = ErrInternalError
} }

@ -148,7 +148,7 @@ func (xl XL) writeErasure(volume, path string, reader *io.PipeReader) {
// Remove previous temp writers for any failure. // Remove previous temp writers for any failure.
xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...)
reader.CloseWithError(err) reader.CloseWithError(errWriteQuorum)
return return
} }
@ -166,7 +166,7 @@ func (xl XL) writeErasure(volume, path string, reader *io.PipeReader) {
// Remove previous temp writers for any failure. // Remove previous temp writers for any failure.
xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...) xl.cleanupCreateFileOps(volume, path, append(writers, metadataWriters...)...)
reader.CloseWithError(err) reader.CloseWithError(errWriteQuorum)
return return
} }

Loading…
Cancel
Save