From cdf93e534c872c063df930e7c6a75ceffaac57bb Mon Sep 17 00:00:00 2001 From: "Anand Babu (AB) Periasamy" Date: Tue, 18 Aug 2015 19:30:17 -0700 Subject: [PATCH] simplify probe APIs --- pkg/controller/rpc.go | 4 +- pkg/donut/bucket.go | 20 +++--- pkg/donut/donut-v1.go | 2 +- pkg/probe/probe.go | 110 +++++++----------------------- pkg/probe/probe_test.go | 6 +- pkg/probe/wrapper.go | 43 ++++++++++++ pkg/server/api/bucket-handlers.go | 14 ++-- pkg/server/api/object-handlers.go | 16 ++--- pkg/server/rpc/auth.go | 2 +- pkg/server/rpc/donut.go | 2 +- pkg/server/rpc/sysinfo.go | 4 +- 11 files changed, 104 insertions(+), 119 deletions(-) create mode 100644 pkg/probe/wrapper.go diff --git a/pkg/controller/rpc.go b/pkg/controller/rpc.go index ead241634..4b0697b32 100644 --- a/pkg/controller/rpc.go +++ b/pkg/controller/rpc.go @@ -60,8 +60,8 @@ func NewRequest(url string, op RPCOps, transport http.RoundTripper) (*RPCRequest func (r RPCRequest) Do() (*http.Response, *probe.Error) { resp, err := r.transport.RoundTrip(r.req) if err != nil { - if werr, ok := probe.ToWrappedError(err); ok { - return nil, werr.ToError().Trace() + if err, ok := probe.UnwrapError(err); ok { + return nil, err.Trace() } return nil, probe.NewError(err) } diff --git a/pkg/donut/bucket.go b/pkg/donut/bucket.go index 7646bd823..d1ac1252a 100644 --- a/pkg/donut/bucket.go +++ b/pkg/donut/bucket.go @@ -473,7 +473,7 @@ func (b bucket) writeObjectData(k, m uint8, writers []io.WriteCloser, objectData func (b bucket) readObjectData(objectName string, writer *io.PipeWriter, objMetadata ObjectMetadata) { readers, err := b.getObjectReaders(objectName, "data") if err != nil { - writer.CloseWithError(probe.NewWrappedError(err)) + writer.CloseWithError(probe.WrapError(err)) return } for _, reader := range readers { @@ -484,12 +484,12 @@ func (b bucket) readObjectData(objectName string, writer *io.PipeWriter, objMeta var err error expectedMd5sum, err = hex.DecodeString(objMetadata.MD5Sum) if err != nil { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(err))) + writer.CloseWithError(probe.WrapError(probe.NewError(err))) return } expected512Sum, err = hex.DecodeString(objMetadata.SHA512Sum) if err != nil { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(err))) + writer.CloseWithError(probe.WrapError(probe.NewError(err))) return } } @@ -499,23 +499,23 @@ func (b bucket) readObjectData(objectName string, writer *io.PipeWriter, objMeta switch len(readers) > 1 { case true: if objMetadata.ErasureTechnique == "" { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(MissingErasureTechnique{}))) + writer.CloseWithError(probe.WrapError(probe.NewError(MissingErasureTechnique{}))) return } encoder, err := newEncoder(objMetadata.DataDisks, objMetadata.ParityDisks, objMetadata.ErasureTechnique) if err != nil { - writer.CloseWithError(probe.NewWrappedError(err)) + writer.CloseWithError(probe.WrapError(err)) return } totalLeft := objMetadata.Size for i := 0; i < objMetadata.ChunkCount; i++ { decodedData, err := b.decodeEncodedData(totalLeft, int64(objMetadata.BlockSize), readers, encoder, writer) if err != nil { - writer.CloseWithError(probe.NewWrappedError(err)) + writer.CloseWithError(probe.WrapError(err)) return } if _, err := io.Copy(mwriter, bytes.NewReader(decodedData)); err != nil { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(err))) + writer.CloseWithError(probe.WrapError(probe.NewError(err))) return } totalLeft = totalLeft - int64(objMetadata.BlockSize) @@ -523,17 +523,17 @@ func (b bucket) readObjectData(objectName string, writer *io.PipeWriter, objMeta case false: _, err := io.Copy(writer, readers[0]) if err != nil { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(err))) + writer.CloseWithError(probe.WrapError(probe.NewError(err))) return } } // check if decodedData md5sum matches if !bytes.Equal(expectedMd5sum, hasher.Sum(nil)) { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(ChecksumMismatch{}))) + writer.CloseWithError(probe.WrapError(probe.NewError(ChecksumMismatch{}))) return } if !bytes.Equal(expected512Sum, sum512hasher.Sum(nil)) { - writer.CloseWithError(probe.NewWrappedError(probe.NewError(ChecksumMismatch{}))) + writer.CloseWithError(probe.WrapError(probe.NewError(ChecksumMismatch{}))) return } writer.Close() diff --git a/pkg/donut/donut-v1.go b/pkg/donut/donut-v1.go index ef0d00646..74fc43dbd 100644 --- a/pkg/donut/donut-v1.go +++ b/pkg/donut/donut-v1.go @@ -618,7 +618,7 @@ func (donut API) makeDonutBucket(bucketName, acl string) *probe.Error { } metadata, err := donut.getDonutBucketMetadata() if err != nil { - if os.IsNotExist(err.ToError()) { + if os.IsNotExist(err.ToGoError()) { metadata := new(AllBuckets) metadata.Buckets = make(map[string]BucketMetadata) metadata.Buckets[bucketName] = bucketMetadata diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go index 4436cdbcd..9aada45e4 100644 --- a/pkg/probe/probe.go +++ b/pkg/probe/probe.go @@ -18,7 +18,6 @@ package probe import ( - "encoding/json" "fmt" "os" "path/filepath" @@ -60,10 +59,10 @@ type tracePoint struct { // Error implements tracing error functionality. type Error struct { - lock sync.RWMutex - e error - sysInfo map[string]string - tracePoints []tracePoint + lock sync.RWMutex + Cause error `json:"cause"` + CallTrace []tracePoint `json:"trace"` + SysInfo map[string]string `json:"sysinfo"` } // NewError function instantiates an error probe for tracing. Original errors.error (golang's error @@ -74,7 +73,7 @@ func NewError(e error) *Error { if e == nil { return nil } - Err := Error{sync.RWMutex{}, e, GetSysInfo(), []tracePoint{}} + Err := Error{lock: sync.RWMutex{}, Cause: e, CallTrace: []tracePoint{}, SysInfo: GetSysInfo()} return Err.trace() } @@ -104,7 +103,7 @@ func (e *Error) trace(fields ...string) *Error { } else { tp = tracePoint{Line: line, Filename: file, Function: function} } - e.tracePoints = append(e.tracePoints, tp) + e.CallTrace = append(e.CallTrace, tp) return e } @@ -116,101 +115,44 @@ func (e *Error) Untrace() { e.lock.Lock() defer e.lock.Unlock() - l := len(e.tracePoints) + l := len(e.CallTrace) if l == 0 { return } - // topTP := e.tracePoints[l-1] - e.tracePoints = e.tracePoints[:l-1] + e.CallTrace = e.CallTrace[:l-1] +} + +// ToError returns original error message. +func (e *Error) ToGoError() error { + return e.Cause } // String returns error message. func (e *Error) String() string { - if e == nil || e.e == nil { + if e == nil || e.Cause == nil { return "" } e.lock.RLock() defer e.lock.RUnlock() - if e.e != nil { - trace := e.e.Error() + "\n" - for i, tp := range e.tracePoints { + if e.Cause != nil { + str := e.Cause.Error() + "\n" + for i, tp := range e.CallTrace { if len(tp.Env) > 0 { - trace += fmt.Sprintf(" (%d) %s:%d %s(..) Tags: [%s]\n", i, tp.Filename, tp.Line, tp.Function, strings.Join(tp.Env["Tags"], ", ")) + str += fmt.Sprintf(" (%d) %s:%d %s(..) Tags: [%s]\n", i, tp.Filename, tp.Line, tp.Function, strings.Join(tp.Env["Tags"], ", ")) } else { - trace += fmt.Sprintf(" (%d) %s:%d %s(..)\n", i, tp.Filename, tp.Line, tp.Function) + str += fmt.Sprintf(" (%d) %s:%d %s(..)\n", i, tp.Filename, tp.Line, tp.Function) } } - trace += " Host:" + e.sysInfo["host.name"] + " | " - trace += "OS:" + e.sysInfo["host.os"] + " | " - trace += "Arch:" + e.sysInfo["host.arch"] + " | " - trace += "Lang:" + e.sysInfo["host.lang"] + " | " - trace += "Mem:" + e.sysInfo["mem.used"] + "/" + e.sysInfo["mem.total"] + " | " - trace += "Heap:" + e.sysInfo["mem.heap.used"] + "/" + e.sysInfo["mem.heap.total"] + str += " Host:" + e.SysInfo["host.name"] + " | " + str += "OS:" + e.SysInfo["host.os"] + " | " + str += "Arch:" + e.SysInfo["host.arch"] + " | " + str += "Lang:" + e.SysInfo["host.lang"] + " | " + str += "Mem:" + e.SysInfo["mem.used"] + "/" + e.SysInfo["mem.total"] + " | " + str += "Heap:" + e.SysInfo["mem.heap.used"] + "/" + e.SysInfo["mem.heap.total"] - return trace + return str } return "" } - -// JSON returns JSON formated error trace. -func (e *Error) JSON() string { - if e == nil || e.e == nil { - return "" - } - - e.lock.RLock() - defer e.lock.RUnlock() - - anonError := struct { - SysInfo map[string]string - TracePoints []tracePoint - }{ - e.sysInfo, - e.tracePoints, - } - - // jBytes, err := json.Marshal(anonError) - jBytes, err := json.MarshalIndent(anonError, "", "\t") - if err != nil { - return "" - } - return string(jBytes) -} - -// ToError returns original embedded error. -func (e *Error) ToError() error { - // No need to lock. "e.e" is set once during New and never changed. - return e.e -} - -// WrappedError implements container for *probe.Error -type WrappedError struct { - err *Error -} - -// NewWrappedError function wraps a *probe.Error into a 'error' compatible duck type -func NewWrappedError(err *Error) error { - return &WrappedError{err: err} -} - -// Error interface method -func (w *WrappedError) Error() string { - return w.err.String() -} - -// ToError get the *probe.Error embedded internally -func (w *WrappedError) ToError() *Error { - return w.err -} - -// ToWrappedError try to convert generic 'error' into typed *WrappedError, returns true if yes, false otherwise -func ToWrappedError(err error) (*WrappedError, bool) { - switch e := err.(type) { - case *WrappedError: - return e, true - default: - return nil, false - } -} diff --git a/pkg/probe/probe_test.go b/pkg/probe/probe_test.go index 88ad6f77c..778988ac9 100644 --- a/pkg/probe/probe_test.go +++ b/pkg/probe/probe_test.go @@ -46,8 +46,8 @@ func (s *MySuite) TestProbe(c *C) { func (s *MySuite) TestWrappedError(c *C) { _, e := os.Stat("this-file-cannot-exit") - es := probe.NewError(e) // *probe.Error - e = probe.NewWrappedError(es) // *probe.WrappedError - _, ok := probe.ToWrappedError(e) + es := probe.NewError(e) // *probe.Error + e = probe.WrapError(es) // *probe.WrappedError + _, ok := probe.UnwrapError(e) c.Assert(ok, Equals, true) } diff --git a/pkg/probe/wrapper.go b/pkg/probe/wrapper.go new file mode 100644 index 000000000..817c2ad88 --- /dev/null +++ b/pkg/probe/wrapper.go @@ -0,0 +1,43 @@ +/* + * Minimalist Object Storage, (C) 2015 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses)/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package probe implements a simple mechanism to trace and return errors in large programs. +package probe + +// wrappedError implements a container for *probe.Error. +type wrappedError struct { + err *Error +} + +// WrapError function wraps a *probe.Error into a 'error' compatible duck type. +func WrapError(err *Error) error { + return &wrappedError{err: err} +} + +// UnwrapError tries to convert generic 'error' into typed *probe.Error and returns true, false otherwise. +func UnwrapError(err error) (*Error, bool) { + switch e := err.(type) { + case *wrappedError: + return e.err, true + default: + return nil, false + } +} + +// Error interface method. +func (w *wrappedError) Error() string { + return w.err.String() +} diff --git a/pkg/server/api/bucket-handlers.go b/pkg/server/api/bucket-handlers.go index 646208b82..127d24b95 100644 --- a/pkg/server/api/bucket-handlers.go +++ b/pkg/server/api/bucket-handlers.go @@ -47,7 +47,7 @@ func (api Minio) isValidOp(w http.ResponseWriter, req *http.Request, acceptsCont } return true } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.BucketNotFound: writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path) return false @@ -117,7 +117,7 @@ func (api Minio) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Re w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNotFound: @@ -188,7 +188,7 @@ func (api Minio) ListObjectsHandler(w http.ResponseWriter, req *http.Request) { w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNameInvalid: @@ -249,7 +249,7 @@ func (api Minio) ListBucketsHandler(w http.ResponseWriter, req *http.Request) { w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) default: @@ -321,7 +321,7 @@ func (api Minio) PutBucketHandler(w http.ResponseWriter, req *http.Request) { writeSuccessResponse(w, acceptsContentType) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.TooManyBuckets: @@ -377,7 +377,7 @@ func (api Minio) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) { writeSuccessResponse(w, acceptsContentType) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNameInvalid: @@ -427,7 +427,7 @@ func (api Minio) HeadBucketHandler(w http.ResponseWriter, req *http.Request) { writeSuccessResponse(w, acceptsContentType) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNotFound: diff --git a/pkg/server/api/object-handlers.go b/pkg/server/api/object-handlers.go index 4a3b2e878..973c932d5 100644 --- a/pkg/server/api/object-handlers.go +++ b/pkg/server/api/object-handlers.go @@ -80,7 +80,7 @@ func (api Minio) GetObjectHandler(w http.ResponseWriter, req *http.Request) { } return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNameInvalid: @@ -137,7 +137,7 @@ func (api Minio) HeadObjectHandler(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.BucketNameInvalid: @@ -231,7 +231,7 @@ func (api Minio) PutObjectHandler(w http.ResponseWriter, req *http.Request) { writeSuccessResponse(w, acceptsContentType) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.BucketNotFound: writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path) case donut.BucketNameInvalid: @@ -305,7 +305,7 @@ func (api Minio) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Requ w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.ObjectExists: @@ -395,7 +395,7 @@ func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) writeSuccessResponse(w, acceptsContentType) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.InvalidUploadID: writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path) case donut.ObjectExists: @@ -455,7 +455,7 @@ func (api Minio) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Re w.WriteHeader(http.StatusNoContent) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.InvalidUploadID: @@ -520,7 +520,7 @@ func (api Minio) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.SignatureDoesNotMatch: writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) case donut.InvalidUploadID: @@ -573,7 +573,7 @@ func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http w.Write(encodedSuccessResponse) return } - switch err.ToError().(type) { + switch err.ToGoError().(type) { case donut.InvalidUploadID: writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path) case donut.InvalidPart: diff --git a/pkg/server/rpc/auth.go b/pkg/server/rpc/auth.go index f6b1ff32b..83ad7d2be 100644 --- a/pkg/server/rpc/auth.go +++ b/pkg/server/rpc/auth.go @@ -49,7 +49,7 @@ func getAuth(reply *AuthReply) *probe.Error { // Get auth keys func (s *AuthService) Get(r *http.Request, args *Args, reply *AuthReply) error { if err := getAuth(reply); err != nil { - return probe.NewWrappedError(err) + return probe.WrapError(err) } return nil } diff --git a/pkg/server/rpc/donut.go b/pkg/server/rpc/donut.go index 0ed7cb176..1ace43007 100644 --- a/pkg/server/rpc/donut.go +++ b/pkg/server/rpc/donut.go @@ -57,7 +57,7 @@ func setDonut(args *DonutArgs, reply *Reply) *probe.Error { // Set method func (s *DonutService) Set(r *http.Request, args *DonutArgs, reply *Reply) error { if err := setDonut(args, reply); err != nil { - return probe.NewWrappedError(err) + return probe.WrapError(err) } return nil } diff --git a/pkg/server/rpc/sysinfo.go b/pkg/server/rpc/sysinfo.go index 8fe581865..b999ef355 100644 --- a/pkg/server/rpc/sysinfo.go +++ b/pkg/server/rpc/sysinfo.go @@ -70,7 +70,7 @@ func setMemStatsReply(sis *MemStatsReply) *probe.Error { // Get method func (s *SysInfoService) Get(r *http.Request, args *Args, reply *SysInfoReply) error { if err := setSysInfoReply(reply); err != nil { - return probe.NewWrappedError(err) + return probe.WrapError(err) } return nil } @@ -78,7 +78,7 @@ func (s *SysInfoService) Get(r *http.Request, args *Args, reply *SysInfoReply) e // Get method func (s *MemStatsService) Get(r *http.Request, args *Args, reply *MemStatsReply) error { if err := setMemStatsReply(reply); err != nil { - return probe.NewWrappedError(err) + return probe.WrapError(err) } return nil }