From e0f8fed011ea7ec626ab02cf0ec9c9865520f855 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 19 Apr 2016 02:42:10 -0700 Subject: [PATCH] object: handle Error responses and handle errDiskFull. (#1331) --- api-errors.go | 8 +- bucket-handlers.go | 4 +- fs.go | 2 +- httprange.go | 10 ++ network-fs.go | 2 + object-api-multipart.go | 17 +++- object-api.go | 9 +- object-errors.go | 203 +++++----------------------------------- object-handlers.go | 16 ++-- storage-errors.go | 4 +- web-handlers.go | 4 +- 11 files changed, 77 insertions(+), 202 deletions(-) diff --git a/api-errors.go b/api-errors.go index 0ef40712d..6dc73bf23 100644 --- a/api-errors.go +++ b/api-errors.go @@ -83,7 +83,7 @@ const ( ErrMalformedPOSTRequest ErrSignatureVersionNotSupported ErrBucketNotEmpty - ErrRootPathFull + ErrStorageFull ErrObjectExistsAsPrefix ErrAllAccessDisabled ErrMalformedPolicy @@ -295,9 +295,9 @@ var errorCodeResponse = map[APIErrorCode]APIError{ Description: "The bucket you tried to delete is not empty.", HTTPStatusCode: http.StatusConflict, }, - ErrRootPathFull: { - Code: "RootPathFull", - Description: "Root path has reached its minimum free disk threshold. Please delete few objects to proceed.", + ErrStorageFull: { + Code: "StorageFull", + Description: "Storage backend has reached its minimum free disk threshold. Please delete few objects to proceed.", HTTPStatusCode: http.StatusInternalServerError, }, ErrObjectExistsAsPrefix: { diff --git a/bucket-handlers.go b/bucket-handlers.go index 22401f1ed..af6aeec2f 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -562,8 +562,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h if err != nil { errorIf(err.Trace(), "PutObject failed.", nil) switch err.ToGoError().(type) { - case RootPathFull: - writeErrorResponse(w, r, ErrRootPathFull, r.URL.Path) + case StorageFull: + writeErrorResponse(w, r, ErrStorageFull, r.URL.Path) case BucketNotFound: writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path) case BucketNameInvalid: diff --git a/fs.go b/fs.go index 12857c306..e21fed2c5 100644 --- a/fs.go +++ b/fs.go @@ -111,7 +111,7 @@ func checkDiskFree(diskPath string, minFreeDisk int64) (err error) { // space used for journalling, inodes etc. availableDiskSpace := (float64(di.Free) / (float64(di.Total) - (0.05 * float64(di.Total)))) * 100 if int64(availableDiskSpace) <= minFreeDisk { - return errDiskPathFull + return errDiskFull } // Success. diff --git a/httprange.go b/httprange.go index 09389db45..cc32a946f 100644 --- a/httprange.go +++ b/httprange.go @@ -29,6 +29,16 @@ const ( b = "bytes=" ) +// InvalidRange - invalid range +type InvalidRange struct { + Start int64 + Length int64 +} + +func (e InvalidRange) Error() string { + return fmt.Sprintf("Invalid range start:%d length:%d", e.Start, e.Length) +} + // HttpRange specifies the byte range to be sent to the client. type httpRange struct { start, length, size int64 diff --git a/network-fs.go b/network-fs.go index 3872deaf0..8cce493c5 100644 --- a/network-fs.go +++ b/network-fs.go @@ -54,6 +54,8 @@ func splitNetPath(networkPath string) (netAddr, netPath string) { // disks as well. func toStorageErr(err error) error { switch err.Error() { + case errDiskFull.Error(): + return errDiskFull case errVolumeNotFound.Error(): return errVolumeNotFound case errVolumeExists.Error(): diff --git a/object-api-multipart.go b/object-api-multipart.go index 6c6980b0f..9a7c02716 100644 --- a/object-api-multipart.go +++ b/object-api-multipart.go @@ -250,6 +250,9 @@ func (o objectAPI) NewMultipartUpload(bucket, object string) (string, *probe.Err if e == errVolumeNotFound { e = o.storage.MakeVol(minioMetaVolume) if e != nil { + if e == errDiskFull { + return "", probe.NewError(StorageFull{}) + } return "", probe.NewError(e) } } @@ -272,6 +275,9 @@ func (o objectAPI) NewMultipartUpload(bucket, object string) (string, *probe.Err return "", probe.NewError(e) } } else { + if e == errDiskFull { + return "", probe.NewError(StorageFull{}) + } return "", probe.NewError(e) } return uploadID, nil @@ -320,8 +326,10 @@ func (o objectAPI) PutObjectPart(bucket, object, uploadID string, partID int, si } else if e == errIsNotRegular { return "", probe.NewError(ObjectExistsAsPrefix{ Bucket: bucket, - Prefix: object, + Object: object, }) + } else if e == errDiskFull { + return "", probe.NewError(StorageFull{}) } return "", probe.NewError(e) } @@ -336,6 +344,9 @@ 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 { + return "", probe.NewError(IncompleteBody{}) + } return "", probe.NewError(e) } } else { @@ -468,8 +479,10 @@ func (o objectAPI) CompleteMultipartUpload(bucket string, object string, uploadI } else if e == errIsNotRegular { return "", probe.NewError(ObjectExistsAsPrefix{ Bucket: bucket, - Prefix: object, + Object: object, }) + } else if e == errDiskFull { + return "", probe.NewError(StorageFull{}) } return "", probe.NewError(e) } diff --git a/object-api.go b/object-api.go index bd2ad1ff2..bb5d1c0b8 100644 --- a/object-api.go +++ b/object-api.go @@ -48,6 +48,8 @@ func (o objectAPI) MakeBucket(bucket string) *probe.Error { 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(e) } @@ -209,8 +211,10 @@ func (o objectAPI) PutObject(bucket string, object string, size int64, data io.R } else if e == errIsNotRegular { return "", probe.NewError(ObjectExistsAsPrefix{ Bucket: bucket, - Prefix: object, + Object: object, }) + } else if e == errDiskFull { + return "", probe.NewError(StorageFull{}) } return "", probe.NewError(e) } @@ -225,6 +229,9 @@ func (o objectAPI) PutObject(bucket string, object string, size int64, data io.R if size > 0 { if _, e = io.CopyN(multiWriter, data, size); e != nil { safeCloseAndRemove(fileWriter) + if e == io.ErrUnexpectedEOF { + return "", probe.NewError(IncompleteBody{}) + } return "", probe.NewError(e) } } else { diff --git a/object-errors.go b/object-errors.go index 5c4a8d015..04e973799 100644 --- a/object-errors.go +++ b/object-errors.go @@ -18,112 +18,54 @@ package main import "fmt" -// InvalidArgument invalid argument -type InvalidArgument struct{} +// StorageFull storage ran out of space +type StorageFull struct{} -func (e InvalidArgument) Error() string { - return "Invalid argument" +func (e StorageFull) Error() string { + return "Storage reached its minimum free disk threshold." } -// UnsupportedFilesystem unsupported filesystem type -type UnsupportedFilesystem struct { - Type string -} - -func (e UnsupportedFilesystem) Error() string { - return "Unsupported filesystem: " + e.Type -} - -// RootPathFull root path out of space -type RootPathFull struct { - Path string -} - -func (e RootPathFull) Error() string { - return "Root path " + e.Path + " reached its minimum free disk threshold." +// GenericError - generic object layer error. +type GenericError struct { + Bucket string + Object string } // BucketNotFound bucket does not exist -type BucketNotFound struct { - Bucket string -} +type BucketNotFound GenericError func (e BucketNotFound) Error() string { return "Bucket not found: " + e.Bucket } // BucketNotEmpty bucket is not empty -type BucketNotEmpty struct { - Bucket string -} +type BucketNotEmpty GenericError func (e BucketNotEmpty) Error() string { return "Bucket not empty: " + e.Bucket } // ObjectNotFound object does not exist -type ObjectNotFound struct { - Bucket string - Object string -} +type ObjectNotFound GenericError func (e ObjectNotFound) Error() string { return "Object not found: " + e.Bucket + "#" + e.Object } // ObjectExistsAsPrefix object already exists with a requested prefix. -type ObjectExistsAsPrefix struct { - Bucket string - Prefix string -} +type ObjectExistsAsPrefix GenericError func (e ObjectExistsAsPrefix) Error() string { - return "Object exists on : " + e.Bucket + " as prefix " + e.Prefix -} - -// ObjectCorrupted object found to be corrupted -type ObjectCorrupted struct { - Object string -} - -func (e ObjectCorrupted) Error() string { - return "Object found corrupted: " + e.Object + return "Object exists on : " + e.Bucket + " as prefix " + e.Object } // BucketExists bucket exists -type BucketExists struct { - Bucket string -} +type BucketExists GenericError func (e BucketExists) Error() string { return "Bucket exists: " + e.Bucket } -// CorruptedBackend backend found to be corrupted -type CorruptedBackend struct { - Backend string -} - -func (e CorruptedBackend) Error() string { - return "Corrupted backend: " + e.Backend -} - -// NotImplemented function not implemented -type NotImplemented struct { - Function string -} - -func (e NotImplemented) Error() string { - return "Not implemented: " + e.Function -} - -// InvalidDisksArgument invalid number of disks per node -type InvalidDisksArgument struct{} - -func (e InvalidDisksArgument) Error() string { - return "Invalid number of disks per node" -} - // BadDigest - Content-MD5 you specified did not match what we received. type BadDigest struct { ExpectedMD5 string @@ -143,8 +85,7 @@ func (e UnsupportedDelimiter) Error() string { return fmt.Sprintf("delimiter '%s' is not supported. Only '/' is supported", e.Delimiter) } -// InvalidUploadIDKeyCombination - invalid upload id and key marker -// combination. +// InvalidUploadIDKeyCombination - invalid upload id and key marker combination. type InvalidUploadIDKeyCombination struct { UploadIDMarker, KeyMarker string } @@ -162,132 +103,41 @@ func (e InvalidMarkerPrefixCombination) Error() string { return fmt.Sprintf("Invalid combination of marker '%s' and prefix '%s'", e.Marker, e.Prefix) } -// InternalError - generic internal error -type InternalError struct{} - -// BackendError - generic disk backend error -type BackendError struct { - Path string -} - -// BackendCorrupted - path has corrupted data -type BackendCorrupted BackendError - -// APINotImplemented - generic API not implemented error -type APINotImplemented struct { - API string -} - -// GenericBucketError - generic bucket error -type GenericBucketError struct { - Bucket string -} - // BucketPolicyNotFound - no bucket policy found. -type BucketPolicyNotFound GenericBucketError +type BucketPolicyNotFound GenericError func (e BucketPolicyNotFound) Error() string { return "No bucket policy found for bucket: " + e.Bucket } -// GenericObjectError - generic object error -type GenericObjectError struct { - Bucket string - Object string -} - -// ImplementationError - generic implementation error -type ImplementationError struct { - Bucket string - Object string - Err error -} - /// Bucket related errors // BucketNameInvalid - bucketname provided is invalid -type BucketNameInvalid GenericBucketError - -/// Object related errors - -// ObjectNameInvalid - object name provided is invalid -type ObjectNameInvalid GenericObjectError - -// Return string an error formatted as the given text -func (e ImplementationError) Error() string { - error := "" - if e.Bucket != "" { - error = error + "Bucket: " + e.Bucket + " " - } - if e.Object != "" { - error = error + "Object: " + e.Object + " " - } - error = error + "Error: " + e.Err.Error() - return error -} - -// EmbedError - wrapper function for error object -func EmbedError(bucket, object string, err error) ImplementationError { - return ImplementationError{ - Bucket: bucket, - Object: object, - Err: err, - } -} - -// Return string an error formatted as the given text -func (e InternalError) Error() string { - return "Internal error occured" -} - -// Return string an error formatted as the given text -func (e APINotImplemented) Error() string { - return "Api not implemented: " + e.API -} +type BucketNameInvalid GenericError // Return string an error formatted as the given text func (e BucketNameInvalid) Error() string { return "Bucket name invalid: " + e.Bucket } +/// Object related errors + +// ObjectNameInvalid - object name provided is invalid +type ObjectNameInvalid GenericError + // Return string an error formatted as the given text func (e ObjectNameInvalid) Error() string { return "Object name invalid: " + e.Bucket + "#" + e.Object } // IncompleteBody You did not provide the number of bytes specified by the Content-Length HTTP header -type IncompleteBody GenericObjectError +type IncompleteBody GenericError // Return string an error formatted as the given text func (e IncompleteBody) Error() string { return e.Bucket + "#" + e.Object + "has incomplete body" } -// Return string an error formatted as the given text -func (e BackendCorrupted) Error() string { - return "Backend corrupted: " + e.Path -} - -// OperationNotPermitted - operation not permitted -type OperationNotPermitted struct { - Op string - Reason string -} - -func (e OperationNotPermitted) Error() string { - return "Operation " + e.Op + " not permitted for reason: " + e.Reason -} - -// InvalidRange - invalid range -type InvalidRange struct { - Start int64 - Length int64 -} - -func (e InvalidRange) Error() string { - return fmt.Sprintf("Invalid range start:%d length:%d", e.Start, e.Length) -} - /// Multipart related errors // MalformedUploadID malformed upload id. @@ -323,10 +173,3 @@ type InvalidPartOrder struct { func (e InvalidPartOrder) Error() string { return "Invalid part order sent for " + e.UploadID } - -// MalformedXML invalid xml format -type MalformedXML struct{} - -func (e MalformedXML) Error() string { - return "Malformed XML" -} diff --git a/object-handlers.go b/object-handlers.go index 3424db5b0..be0bd7d03 100644 --- a/object-handlers.go +++ b/object-handlers.go @@ -460,8 +460,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re md5Sum, err := api.ObjectAPI.PutObject(bucket, object, size, readCloser, metadata) if err != nil { switch err.ToGoError().(type) { - case RootPathFull: - writeErrorResponse(w, r, ErrRootPathFull, r.URL.Path) + case StorageFull: + writeErrorResponse(w, r, ErrStorageFull, r.URL.Path) case BucketNotFound: writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path) case BucketNameInvalid: @@ -684,8 +684,8 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req return } switch e.(type) { - case RootPathFull: - writeErrorResponse(w, r, ErrRootPathFull, r.URL.Path) + case StorageFull: + writeErrorResponse(w, r, ErrStorageFull, r.URL.Path) case BucketNotFound: writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path) case BucketNameInvalid: @@ -738,8 +738,8 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r if err != nil { errorIf(err.Trace(), "NewMultipartUpload failed.", nil) switch err.ToGoError().(type) { - case RootPathFull: - writeErrorResponse(w, r, ErrRootPathFull, r.URL.Path) + case StorageFull: + writeErrorResponse(w, r, ErrStorageFull, r.URL.Path) case BucketNameInvalid: writeErrorResponse(w, r, ErrInvalidBucketName, r.URL.Path) case BucketNotFound: @@ -856,8 +856,8 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http return } switch e.(type) { - case RootPathFull: - writeErrorResponse(w, r, ErrRootPathFull, r.URL.Path) + case StorageFull: + writeErrorResponse(w, r, ErrStorageFull, r.URL.Path) case InvalidUploadID: writeErrorResponse(w, r, ErrNoSuchUpload, r.URL.Path) case BadDigest: diff --git a/storage-errors.go b/storage-errors.go index 0e78ffdab..a4de8d357 100644 --- a/storage-errors.go +++ b/storage-errors.go @@ -18,8 +18,8 @@ package main import "errors" -// errDiskPathFull - cannot create volume or files when disk is full. -var errDiskPathFull = errors.New("Disk path full.") +// errDiskFull - cannot create volume or files when disk is full. +var errDiskFull = errors.New("Disk path full.") // errFileNotFound - cannot find the file. var errFileNotFound = errors.New("File not found.") diff --git a/web-handlers.go b/web-handlers.go index d8d87ae6d..c99b8355c 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -413,8 +413,8 @@ func writeWebErrorResponse(w http.ResponseWriter, err error) { // Convert error type to api error code. var apiErrCode APIErrorCode switch err.(type) { - case RootPathFull: - apiErrCode = ErrRootPathFull + case StorageFull: + apiErrCode = ErrStorageFull case BucketNotFound: apiErrCode = ErrNoSuchBucket case BucketNameInvalid: