mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	object: handle Error responses and handle errDiskFull. (#1331)
This commit is contained in:
		
							parent
							
								
									6bc17a3aea
								
							
						
					
					
						commit
						e0f8fed011
					
				| @ -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: { | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
							
								
								
									
										2
									
								
								fs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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. | ||||
|  | ||||
							
								
								
									
										10
									
								
								httprange.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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 | ||||
|  | ||||
| @ -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(): | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
							
								
								
									
										203
									
								
								object-errors.go
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								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" | ||||
| } | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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.") | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user