diff --git a/Gopkg.lock b/Gopkg.lock index caa3a743830..31cbe9a70d0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -313,6 +313,22 @@ pruneopts = "NUT" revision = "1c287c953996ab3a0bf535dba9d53d809d3dc0b6" +[[projects]] + digest = "1:cffc7d72991e2d84e164a07a0fabbc67a3fce70745d425e41245358a0c107d66" + name = "github.com/pingcap/errcode" + packages = ["."] + pruneopts = "NUT" + revision = "a1a7271709d9bea2d0a0d2aab8a97d7a2ce0ea51" + version = "v0.1" + +[[projects]] + digest = "1:677ff2ee188099669fbecf583de8c23111020adee48d71a6f60c733f6b2fea7a" + name = "github.com/pingcap/errors" + packages = ["."] + pruneopts = "NUT" + revision = "31ffda8a65b0f910c6d65f1fef40e761fd606384" + version = "v0.10.1" + [[projects]] branch = "master" digest = "1:3be99496a5d1fc018eb176c53a81df986408af94bece871d829eaceddf7c5325" @@ -640,6 +656,7 @@ "github.com/montanaflynn/stats", "github.com/opentracing/opentracing-go", "github.com/pingcap/check", + "github.com/pingcap/errcode", "github.com/pingcap/kvproto/pkg/eraftpb", "github.com/pingcap/kvproto/pkg/metapb", "github.com/pingcap/kvproto/pkg/pdpb", diff --git a/Gopkg.toml b/Gopkg.toml index 101eeb18673..3bec080f2e3 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -40,6 +40,10 @@ name = "github.com/pkg/errors" version = ">= 0.8.0" +[[constraint]] + name = "github.com/pingcap/errcode" + version = ">= 0.1.0" + [[override]] name = "github.com/BurntSushi/toml" revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" diff --git a/docs/development.md b/docs/development.md index eda2905af1f..607946e057a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -42,5 +42,5 @@ Consider that raml2html depends on various npm packages and can only be run unde ## Error responses -Error responses from the server are switching to using [error codes](../pkg/error_code/error_code.go). +Error responses from the server are switching to using [errcode codes](https://github.com/pingcap/errcode). The should use the `errorResp` function. Please look at other places in the codebase that use `errorResp`. diff --git a/pkg/error_code/error_code_test.go b/pkg/error_code/error_code_test.go deleted file mode 100644 index 7ea62dc03ed..00000000000 --- a/pkg/error_code/error_code_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2018 PingCAP, 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package errcode_test - -import ( - "errors" - "fmt" - "reflect" - "testing" - - "github.com/pingcap/pd/pkg/error_code" -) - -// Test setting the HTTP code -type HTTPError struct{} - -func (e HTTPError) Error() string { return "error" } - -const httpCodeStr = "input.http" - -var codeHttp900 = errcode.InvalidInputCode.Child(httpCodeStr).SetHTTP(900) - -func (e HTTPError) Code() errcode.Code { - return codeHttp900 -} - -func TestHttpErrorCode(t *testing.T) { - http := HTTPError{} - AssertHTTPCode(t, http, 900) - ErrorEquals(t, http, "error") - ClientDataEquals(t, http, http, httpCodeStr) -} - -// Test a very simple error -type MinimalError struct{} - -func (e MinimalError) Error() string { return "error" } - -var _ errcode.ErrorCode = (*MinimalError)(nil) // assert implements interface - -const codeString errcode.CodeStr = "input.testcode" - -var registeredCode errcode.Code = errcode.InvalidInputCode.Child(codeString) - -func (e MinimalError) Code() errcode.Code { return registeredCode } - -func TestMinimalErrorCode(t *testing.T) { - minimal := MinimalError{} - AssertCodes(t, minimal) - ErrorEquals(t, minimal, "error") - ClientDataEquals(t, minimal, minimal) - OpEquals(t, minimal, "") -} - -// We don't prevent duplicate codes -var childPathOnlyCode errcode.Code = errcode.InvalidInputCode.Child("testcode") - -type ChildOnlyError struct{} - -func (e ChildOnlyError) Error() string { return "error" } - -var _ errcode.ErrorCode = (*ChildOnlyError)(nil) // assert implements interface - -func (e ChildOnlyError) Code() errcode.Code { return childPathOnlyCode } - -func TestChildOnlyErrorCode(t *testing.T) { - coe := ChildOnlyError{} - AssertCodes(t, coe) - ErrorEquals(t, coe, "error") - ClientDataEquals(t, coe, coe) -} - -// Test a top-level error -type TopError struct{} - -func (e TopError) Error() string { return "error" } - -var _ errcode.ErrorCode = (*TopError)(nil) // assert implements interface - -const topCodeStr errcode.CodeStr = "top" - -var topCode errcode.Code = errcode.NewCode(topCodeStr) - -func (e TopError) Code() errcode.Code { return topCode } - -func TestTopErrorCode(t *testing.T) { - top := TopError{} - AssertCodes(t, top, topCodeStr) - ErrorEquals(t, top, "error") - ClientDataEquals(t, top, top, topCodeStr) -} - -// Test a deep hierarchy -type DeepError struct{} - -func (e DeepError) Error() string { return "error" } - -var _ errcode.ErrorCode = (*DeepError)(nil) // assert implements interface - -const deepCodeStr errcode.CodeStr = "input.testcode.very.very.deep" - -var intermediateCode = registeredCode.Child("input.testcode.very").SetHTTP(800) -var deepCode errcode.Code = intermediateCode.Child("input.testcode.very.very").Child(deepCodeStr) - -func (e DeepError) Code() errcode.Code { return deepCode } - -func TestDeepErrorCode(t *testing.T) { - deep := DeepError{} - AssertHTTPCode(t, deep, 800) - AssertCode(t, deep, deepCodeStr) - ErrorEquals(t, deep, "error") - ClientDataEquals(t, deep, deep, deepCodeStr) -} - -// Test an ErrorWrapper that has different error types placed into it -type ErrorWrapper struct{ Err error } - -var _ errcode.ErrorCode = (*ErrorWrapper)(nil) // assert implements interface -var _ errcode.HasClientData = (*ErrorWrapper)(nil) // assert implements interface - -func (e ErrorWrapper) Code() errcode.Code { - return registeredCode -} -func (e ErrorWrapper) Error() string { - return e.Err.Error() -} -func (e ErrorWrapper) GetClientData() interface{} { - return e.Err -} - -type Struct1 struct{ A string } -type StructConstError1 struct{ A string } - -func (e Struct1) Error() string { - return e.A -} - -func (e StructConstError1) Error() string { - return "error" -} - -type Struct2 struct { - A string - B string -} - -func (e Struct2) Error() string { - return fmt.Sprintf("error A & B %s & %s", e.A, e.B) -} - -func TestErrorWrapperCode(t *testing.T) { - wrapped := ErrorWrapper{Err: errors.New("error")} - AssertCodes(t, wrapped) - ErrorEquals(t, wrapped, "error") - ClientDataEquals(t, wrapped, errors.New("error")) - s2 := Struct2{A: "A", B: "B"} - wrappedS2 := ErrorWrapper{Err: s2} - AssertCodes(t, wrappedS2) - ErrorEquals(t, wrappedS2, "error A & B A & B") - ClientDataEquals(t, wrappedS2, s2) - s1 := Struct1{A: "A"} - ClientDataEquals(t, ErrorWrapper{Err: s1}, s1) - sconst := StructConstError1{A: "A"} - ClientDataEquals(t, ErrorWrapper{Err: sconst}, sconst) -} - -var internalChildCodeStr errcode.CodeStr = "internal.child.granchild" -var internalChild = errcode.InternalCode.Child("internal.child").SetHTTP(503).Child(internalChildCodeStr) - -type InternalChild struct{} - -func (ic InternalChild) Error() string { return "internal child error" } -func (ic InternalChild) Code() errcode.Code { return internalChild } - -func TestNewInvalidInputErr(t *testing.T) { - err := errcode.NewInvalidInputErr(errors.New("new error")) - AssertCodes(t, err, "input") - ErrorEquals(t, err, "new error") - ClientDataEquals(t, err, errors.New("new error"), "input") - - err = errcode.NewInvalidInputErr(MinimalError{}) - AssertCodes(t, err, "input.testcode") - ErrorEquals(t, err, "error") - ClientDataEquals(t, err, MinimalError{}, errcode.CodeStr("input.testcode")) - - internalErr := errcode.NewInternalErr(MinimalError{}) - err = errcode.NewInvalidInputErr(internalErr) - internalCodeStr := errcode.CodeStr("internal") - AssertCode(t, err, internalCodeStr) - AssertHTTPCode(t, err, 500) - ErrorEquals(t, err, "error") - ClientDataEquals(t, err, MinimalError{}, internalCodeStr) - - err = errcode.NewInvalidInputErr(InternalChild{}) - AssertCode(t, err, internalChildCodeStr) - AssertHTTPCode(t, err, 503) - ErrorEquals(t, err, "internal child error") - ClientDataEquals(t, err, InternalChild{}, internalChildCodeStr) -} - -func TestNewInternalErr(t *testing.T) { - internalCodeStr := errcode.CodeStr("internal") - err := errcode.NewInternalErr(errors.New("new error")) - AssertCode(t, err, internalCodeStr) - AssertHTTPCode(t, err, 500) - ErrorEquals(t, err, "new error") - ClientDataEquals(t, err, errors.New("new error"), "internal") - - err = errcode.NewInternalErr(MinimalError{}) - AssertCode(t, err, internalCodeStr) - AssertHTTPCode(t, err, 500) - ErrorEquals(t, err, "error") - ClientDataEquals(t, err, MinimalError{}, internalCodeStr) - - invalidErr := errcode.NewInvalidInputErr(MinimalError{}) - err = errcode.NewInternalErr(invalidErr) - AssertCode(t, err, internalCodeStr) - AssertHTTPCode(t, err, 500) - ErrorEquals(t, err, "error") - ClientDataEquals(t, err, MinimalError{}, internalCodeStr) -} - -// Test Operation -type OpErrorHas struct{ MinimalError } - -func (e OpErrorHas) GetOperation() string { return "has" } - -type OpErrorEmbed struct { - errcode.EmbedOp - MinimalError -} - -var _ errcode.ErrorCode = (*OpErrorHas)(nil) // assert implements interface -var _ errcode.HasOperation = (*OpErrorHas)(nil) // assert implements interface -var _ errcode.ErrorCode = (*OpErrorEmbed)(nil) // assert implements interface -var _ errcode.HasOperation = (*OpErrorEmbed)(nil) // assert implements interface - -func TestOpErrorCode(t *testing.T) { - AssertOperation(t, "foo", "") - has := OpErrorHas{} - AssertOperation(t, has, "has") - AssertCodes(t, has) - ErrorEquals(t, has, "error") - ClientDataEquals(t, has, has) - OpEquals(t, has, "has") - - OpEquals(t, OpErrorEmbed{}, "") - OpEquals(t, OpErrorEmbed{EmbedOp: errcode.EmbedOp{Op: "field"}}, "field") - - opEmpty := errcode.Op("") - op := errcode.Op("modify") - OpEquals(t, opEmpty.AddTo(MinimalError{}), "") - OpEquals(t, op.AddTo(MinimalError{}), "modify") - - OpEquals(t, ErrorWrapper{Err: has}, "has") - OpEquals(t, ErrorWrapper{Err: OpErrorEmbed{EmbedOp: errcode.EmbedOp{Op: "field"}}}, "field") - - opErrCode := errcode.OpErrCode{Operation: "opcode", Err: MinimalError{}} - AssertOperation(t, opErrCode, "opcode") - OpEquals(t, opErrCode, "opcode") - - OpEquals(t, ErrorWrapper{Err: opErrCode}, "opcode") - wrappedHas := ErrorWrapper{Err: errcode.OpErrCode{Operation: "opcode", Err: has}} - AssertOperation(t, wrappedHas, "") - OpEquals(t, wrappedHas, "opcode") - OpEquals(t, errcode.OpErrCode{Operation: "opcode", Err: has}, "opcode") -} - -func AssertCodes(t *testing.T, code errcode.ErrorCode, codeStrs ...errcode.CodeStr) { - t.Helper() - AssertCode(t, code, codeStrs...) - AssertHTTPCode(t, code, 400) -} - -func AssertCode(t *testing.T, code errcode.ErrorCode, codeStrs ...errcode.CodeStr) { - t.Helper() - codeStr := codeString - if len(codeStrs) > 0 { - codeStr = codeStrs[0] - } - if code.Code().CodeStr() != codeStr { - t.Errorf("code expected %v\ncode but got %v", codeStr, code.Code().CodeStr()) - } -} - -func AssertHTTPCode(t *testing.T, code errcode.ErrorCode, httpCode int) { - t.Helper() - expected := code.Code().HTTPCode() - if expected != httpCode { - t.Errorf("excpected HTTP Code %v but got %v", httpCode, expected) - } -} - -func ErrorEquals(t *testing.T, err error, msg string) { - if err.Error() != msg { - t.Errorf("Expected error %v. Got error %v", msg, err.Error()) - } -} - -func ClientDataEquals(t *testing.T, code errcode.ErrorCode, data interface{}, codeStrs ...errcode.CodeStr) { - codeStr := codeString - if len(codeStrs) > 0 { - codeStr = codeStrs[0] - } - t.Helper() - if !reflect.DeepEqual(errcode.ClientData(code), data) { - t.Errorf("\nClientData expected: %#v\n ClientData but got: %#v", data, errcode.ClientData(code)) - } - jsonExpected := errcode.JSONFormat{ - Data: data, - Msg: code.Error(), - Code: codeStr, - Operation: errcode.Operation(data), - } - if !reflect.DeepEqual(errcode.NewJSONFormat(code), jsonExpected) { - t.Errorf("\nJSON expected: %+v\n JSON but got: %+v", jsonExpected, errcode.NewJSONFormat(code)) - } -} - -func OpEquals(t *testing.T, code errcode.ErrorCode, op string) { - t.Helper() - opGot, _ := errcode.OperationClientData(code) - if opGot != op { - t.Errorf("\nOp expected: %#v\n Op but got: %#v", op, opGot) - } -} - -func AssertOperation(t *testing.T, v interface{}, op string) { - t.Helper() - opGot := errcode.Operation(v) - if opGot != op { - t.Errorf("\nOp expected: %#v\n Op but got: %#v", op, opGot) - } -} diff --git a/server/api/config.go b/server/api/config.go index d05b07042c1..31a686ef515 100644 --- a/server/api/config.go +++ b/server/api/config.go @@ -20,7 +20,7 @@ import ( "net/http" "github.com/gorilla/mux" - "github.com/pingcap/pd/pkg/error_code" + "github.com/pingcap/errcode" "github.com/pingcap/pd/server" "github.com/pkg/errors" "github.com/unrolled/render" diff --git a/server/api/store.go b/server/api/store.go index 891cc8f7801..267088c3bb8 100644 --- a/server/api/store.go +++ b/server/api/store.go @@ -20,9 +20,9 @@ import ( "time" "github.com/gorilla/mux" + "github.com/pingcap/errcode" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/pd/pkg/apiutil" - "github.com/pingcap/pd/pkg/error_code" "github.com/pingcap/pd/pkg/typeutil" "github.com/pingcap/pd/server" "github.com/pingcap/pd/server/core" diff --git a/server/api/util.go b/server/api/util.go index c94ab46cdac..d8b5ae5fb6a 100644 --- a/server/api/util.go +++ b/server/api/util.go @@ -20,8 +20,8 @@ import ( "io/ioutil" "net/http" + "github.com/pingcap/errcode" "github.com/pingcap/pd/pkg/apiutil" - "github.com/pingcap/pd/pkg/error_code" "github.com/pingcap/pd/server" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -40,7 +40,7 @@ func errorResp(rd *render.Render, w http.ResponseWriter, err error) { rd.JSON(w, http.StatusInternalServerError, "nil error") return } - if errCode, ok := errors.Cause(err).(errcode.ErrorCode); ok { + if errCode := errcode.CodeChain(err); errCode != nil { w.Header().Set("TiDB-Error-Code", errCode.Code().CodeStr().String()) rd.JSON(w, errCode.Code().HTTPCode(), errcode.NewJSONFormat(errCode)) } else { diff --git a/server/cluster.go b/server/cluster.go index 3f50961376c..c3a0f527606 100644 --- a/server/cluster.go +++ b/server/cluster.go @@ -19,9 +19,9 @@ import ( "sync" "time" + "github.com/pingcap/errcode" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" - "github.com/pingcap/pd/pkg/error_code" "github.com/pingcap/pd/pkg/logutil" "github.com/pingcap/pd/server/core" "github.com/pingcap/pd/server/namespace" diff --git a/server/core/errors.go b/server/core/errors.go index 30de0dba901..a58b9adcc53 100644 --- a/server/core/errors.go +++ b/server/core/errors.go @@ -20,7 +20,7 @@ import ( "fmt" "net/http" - "github.com/pingcap/pd/pkg/error_code" + "github.com/pingcap/errcode" ) var ( diff --git a/server/core/store.go b/server/core/store.go index e619201e881..767f6e8c521 100644 --- a/server/core/store.go +++ b/server/core/store.go @@ -21,9 +21,9 @@ import ( "time" "github.com/gogo/protobuf/proto" + "github.com/pingcap/errcode" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" - "github.com/pingcap/pd/pkg/error_code" log "github.com/sirupsen/logrus" ) diff --git a/pkg/error_code/codes.go b/vendor/github.com/pingcap/errcode/codes.go similarity index 87% rename from pkg/error_code/codes.go rename to vendor/github.com/pingcap/errcode/codes.go index a9ce721365c..a5bbcf9c64c 100644 --- a/pkg/error_code/codes.go +++ b/vendor/github.com/pingcap/errcode/codes.go @@ -39,14 +39,16 @@ func NewInvalidInputErr(err error) ErrorCode { var _ ErrorCode = (*invalidInputErr)(nil) // assert implements interface var _ HasClientData = (*invalidInputErr)(nil) // assert implements interface +var _ Causer = (*invalidInputErr)(nil) // assert implements interface -// internalError gives the code InvalidInputCode -type internalErr struct{ CodedError } +// internalError gives the code InternalCode +type internalErr struct{ StackCode } // NewInternalErr creates an internalError from an err // If the given err is an ErrorCode that is a descendant of InternalCode, // its code will be used. // This ensures the intention of sending an HTTP 50x. +// This function also records a stack trace. func NewInternalErr(err error) ErrorCode { code := InternalCode if errcode, ok := err.(ErrorCode); ok { @@ -55,11 +57,12 @@ func NewInternalErr(err error) ErrorCode { code = errCode } } - return internalErr{CodedError{GetCode: code, Err: err}} + return internalErr{NewStackCode(CodedError{GetCode: code, Err: err}, 2)} } var _ ErrorCode = (*internalErr)(nil) // assert implements interface var _ HasClientData = (*internalErr)(nil) // assert implements interface +var _ Causer = (*internalErr)(nil) // assert implements interface // notFound gives the code NotFoundCode type notFoundErr struct{ CodedError } @@ -73,6 +76,7 @@ func NewNotFoundErr(err error) ErrorCode { var _ ErrorCode = (*notFoundErr)(nil) // assert implements interface var _ HasClientData = (*notFoundErr)(nil) // assert implements interface +var _ Causer = (*notFoundErr)(nil) // assert implements interface // CodedError is a convenience to attach a code to an error and already satisfy the ErrorCode interface. // If the error is a struct, that struct will get preseneted as data to the client. @@ -101,11 +105,17 @@ func NewCodedError(err error, code Code) CodedError { var _ ErrorCode = (*CodedError)(nil) // assert implements interface var _ HasClientData = (*CodedError)(nil) // assert implements interface +var _ Causer = (*CodedError)(nil) // assert implements interface func (e CodedError) Error() string { return e.Err.Error() } +// Cause satisfies the Causer interface. +func (e CodedError) Cause() error { + return e.Err +} + // Code returns the GetCode field func (e CodedError) Code() Code { return e.GetCode diff --git a/pkg/error_code/error_code.go b/vendor/github.com/pingcap/errcode/error_code.go similarity index 71% rename from pkg/error_code/error_code.go rename to vendor/github.com/pingcap/errcode/error_code.go index 76df51d73f7..86056e7b7ab 100644 --- a/pkg/error_code/error_code.go +++ b/vendor/github.com/pingcap/errcode/error_code.go @@ -13,47 +13,57 @@ // Package errcode facilitates standardized API error codes. // The goal is that clients can reliably understand errors by checking against immutable error codes -// A Code should never be modified once committed (and released for use by clients). -// Instead a new Code should be created. +// +// This godoc documents usage. For broader context, see https://github.com/pingcap/errcode/tree/master/README.md // // Error codes are represented as strings by CodeStr (see CodeStr documentation). // // This package is designed to have few opinions and be a starting point for how you want to do errors in your project. // The main requirement is to satisfy the ErrorCode interface by attaching a Code to an Error. // See the documentation of ErrorCode. -// Additional optional interfaces HasClientData and HasOperation are provided for extensibility +// Additional optional interfaces HasClientData, HasOperation, Causer, and StackTracer are provided for extensibility // in creating structured error data representations. // // Hierarchies are supported: a Code can point to a parent. // This is used in the HTTPCode implementation to inherit HTTP codes found with MetaDataFromAncestors. // The hierarchy is present in the Code's string representation with a dot separation. // -// A few generic top-level error codes are provided here. -// You are encouraged to create your own application customized error codes rather than just using generic errors. +// A few generic top-level error codes are provided (see the variables section of the doc). +// You are encouraged to create your own error codes customized to your application rather than solely using generic errors. // -// See JSONFormat for an opinion on how to send back meta data about errors with the error data to a client. +// See NewJSONFormat for an opinion on how to send back meta data about errors with the error data to a client. // JSONFormat includes a body of response data (the "data field") that is by default the data from the Error // serialized to JSON. -// This package provides no help on versioning error data. +// +// Stack traces are automatically added by NewInternalErr and show up as the Stack field in JSONFormat. +// Errors can be grouped with Combine() and ungrouped via Errors() which show up as the Others field in JSONFormat. +// +// To extract any ErrorCodes from an error, use CodeChain(). +// This extracts error codes without information loss (using ChainContext). package errcode import ( "fmt" "net/http" "strings" + + "github.com/pingcap/errors" ) -// CodeStr is a representation of the type of a particular error. +// CodeStr is the name of the error code. +// It is a representation of the type of a particular error. // The underlying type is string rather than int. // This enhances both extensibility (avoids merge conflicts) and user-friendliness. // A CodeStr can have dot separators indicating a hierarchy. +// +// Generally a CodeStr should never be modified once used by clients. +// Instead a new CodeStr should be created. type CodeStr string func (str CodeStr) String() string { return string(str) } // A Code has a CodeStr representation. // It is attached to a Parent to find metadata from it. -// The Meta field is provided for extensibility: e.g. attaching HTTP codes. type Code struct { // codeStr does not include parent paths // The full code (with parent paths) is accessed with CodeStr @@ -111,9 +121,9 @@ func (code Code) IsAncestor(ancestorCode Code) bool { return nil != code.findAncestor(func(an Code) bool { return an == ancestorCode }) } -// MetaData is a pattern for attaching meta data to codes and inheriting it from a parent. +// MetaData is used in a pattern for attaching meta data to codes and inheriting it from a parent. // See MetaDataFromAncestors. -// This is used to attach an HTTP code to a Code. +// This is used to attach an HTTP code to a Code as meta data. type MetaData map[CodeStr]interface{} // MetaDataFromAncestors looks for meta data starting at the current code. @@ -163,7 +173,7 @@ func (code Code) HTTPCode() int { // For an application specific error with a 1:1 mapping between a go error structure and a RegisteredCode, // You probably want to use this interface directly. Example: // -// // First define a normal error type +// // First define a normal error type // type PathBlocked struct { // start uint64 `json:"start"` // end uint64 `json:"end"` @@ -177,7 +187,7 @@ func (code Code) HTTPCode() int { // // Now define the code // var PathBlockedCode = errcode.StateCode.Child("state.blocked") // -// // Now attach the code to the error type +// // Now attach the code to the error type // func (e PathBlocked) Code() Code { // return PathBlockedCode // } @@ -186,6 +196,16 @@ type ErrorCode interface { Code() Code } +// Causer allows the abstract retrieval of the underlying error. +// This is the interface that pkg/errors does not export but is considered part of the stable public API. +// TODO: export this from pkg/errors +// +// Types that wrap errors should implement this to allow viewing of the underlying error. +// Generally you would use this via pkg/errors.Cause or pkg/errors.Unwrap. +type Causer interface { + Cause() error +} + // HasClientData is used to defined how to retrieve the data portion of an ErrorCode to be returned to the client. // Otherwise the struct itself will be assumed to be all the data by the ClientData method. // This is provided for exensibility, but may be unnecessary for you. @@ -206,13 +226,22 @@ func ClientData(errCode ErrorCode) interface{} { } // JSONFormat is an opinion on how to serialize an ErrorCode to JSON. -// Msg is the string from Error(). -// The Data field is filled in by GetClientData +// * Code is the error code string (CodeStr) +// * Msg is the string from Error() and should be friendly to end users. +// * Data is the ad-hoc data filled in by GetClientData and should be consumable by clients. +// * Operation is the high-level operation that was happening at the time of the error. +// The Operation field may be missing, and the Data field may be empty. +// +// The rest of the fields may be populated sparsely depending on the application: +// * Stack is a stack trace. This is only given for internal errors. +// * Others gives other errors that occurred (perhaps due to parallel requests). type JSONFormat struct { - Data interface{} `json:"data"` - Msg string `json:"msg"` - Code CodeStr `json:"code"` - Operation string `json:"operation,omitempty"` + Code CodeStr `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` + Operation string `json:"operation,omitempty"` + Stack errors.StackTrace `json:"stack,omitempty"` + Others []JSONFormat `json:"others,omitempty"` } // OperationClientData gives the results of both the ClientData and Operation functions. @@ -228,14 +257,31 @@ func OperationClientData(errCode ErrorCode) (string, interface{}) { return op, data } -// NewJSONFormat turns an ErrorCode into a JSONFormat +// NewJSONFormat turns an ErrorCode into a JSONFormat. +// If you use ErrorCodeChain first, you will ensure proper population of the Others field. func NewJSONFormat(errCode ErrorCode) JSONFormat { + // Gather up multiple errors. + // We discard any that are not ErrorCode. + errorCodes := ErrorCodes(errCode) + others := make([]JSONFormat, len(errorCodes)-1) + for i, err := range errorCodes[1:] { + others[i] = NewJSONFormat(err) + } + op, data := OperationClientData(errCode) + + var stack errors.StackTrace + if errCode.Code().IsAncestor(InternalCode) { + stack = StackTrace(errCode) + } + return JSONFormat{ Data: data, Msg: errCode.Error(), Code: errCode.Code().CodeStr(), Operation: op, + Stack: stack, + Others: others, } } diff --git a/vendor/github.com/pingcap/errcode/group.go b/vendor/github.com/pingcap/errcode/group.go new file mode 100644 index 00000000000..45b4c4dca80 --- /dev/null +++ b/vendor/github.com/pingcap/errcode/group.go @@ -0,0 +1,234 @@ +// Copyright 2018 PingCAP, 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package errcode + +import ( + "fmt" + + "github.com/pingcap/errors" +) + +// ErrorCodes return all errors (from an ErrorGroup) that are of interface ErrorCode. +// It first calls the Errors function. +func ErrorCodes(err error) []ErrorCode { + errors := errors.Errors(err) + errorCodes := make([]ErrorCode, len(errors)) + for i, errItem := range errors { + if errcode, ok := errItem.(ErrorCode); ok { + errorCodes[i] = errcode + } + } + return errorCodes +} + +// A MultiErrCode contains at least one ErrorCode and uses that to satisfy the ErrorCode and related interfaces +// The Error method will produce a string of all the errors with a semi-colon separation. +// Later code (such as a JSON response) needs to look for the ErrorGroup interface. +type MultiErrCode struct { + ErrCode ErrorCode + rest []error +} + +// Combine constructs a MultiErrCode. +// It will combine any other MultiErrCode into just one MultiErrCode. +// This is "horizontal" composition. +// If you want normal "vertical" composition use BuildChain. +func Combine(initial ErrorCode, others ...ErrorCode) MultiErrCode { + var rest []error + if group, ok := initial.(errors.ErrorGroup); ok { + rest = group.Errors() + } + for _, other := range others { + rest = append(rest, errors.Errors(other)...) + } + return MultiErrCode{ + ErrCode: initial, + rest: rest, + } +} + +var _ ErrorCode = (*MultiErrCode)(nil) // assert implements interface +var _ HasClientData = (*MultiErrCode)(nil) // assert implements interface +var _ Causer = (*MultiErrCode)(nil) // assert implements interface +var _ errors.ErrorGroup = (*MultiErrCode)(nil) // assert implements interface +var _ fmt.Formatter = (*MultiErrCode)(nil) // assert implements interface + +func (e MultiErrCode) Error() string { + output := e.ErrCode.Error() + for _, item := range e.rest { + output += "; " + item.Error() + } + return output +} + +// Errors fullfills the ErrorGroup inteface +func (e MultiErrCode) Errors() []error { + return append([]error{e.ErrCode.(error)}, e.rest...) +} + +// Code fullfills the ErrorCode inteface +func (e MultiErrCode) Code() Code { + return e.ErrCode.Code() +} + +// Cause fullfills the Causer inteface +func (e MultiErrCode) Cause() error { + return e.ErrCode +} + +// GetClientData fullfills the HasClientData inteface +func (e MultiErrCode) GetClientData() interface{} { + return ClientData(e.ErrCode) +} + +// CodeChain resolves an error chain down to a chain of just error codes +// Any ErrorGroups found are converted to a MultiErrCode. +// Passed over error inforation is retained using ChainContext. +// If a code was overidden in the chain, it will show up as a MultiErrCode. +func CodeChain(err error) ErrorCode { + var code ErrorCode + currentErr := err + chainErrCode := func(errcode ErrorCode) { + if errcode.(error) != currentErr { + if chained, ok := errcode.(ChainContext); ok { + // Perhaps this is a hack because we should be passing the context to recursive CodeChain calls + chained.Top = currentErr + errcode = chained + } else { + errcode = ChainContext{currentErr, errcode} + } + } + if code == nil { + code = errcode + } else { + code = MultiErrCode{code, []error{code.(error), errcode.(error)}} + } + currentErr = errcode.(error) + } + + for err != nil { + if errcode, ok := err.(ErrorCode); ok { + if code == nil || code.Code() != errcode.Code() { + chainErrCode(errcode) + } + } else if eg, ok := err.(errors.ErrorGroup); ok { + group := []ErrorCode{} + for _, errItem := range eg.Errors() { + if itemCode := CodeChain(errItem); itemCode != nil { + group = append(group, itemCode) + } + } + if len(group) > 0 { + var codeGroup ErrorCode + if len(group) == 1 { + codeGroup = group[0] + } else { + codeGroup = Combine(group[0], group[1:]...) + } + chainErrCode(codeGroup) + } + } + err = errors.Unwrap(err) + } + + return code +} + +// ChainContext is returned by ErrorCodeChain +// to retain the full wrapped error message of the error chain. +// If you annotated an ErrorCode with additional information, it is retained in the Top field. +// The Top field is used for the Error() and Cause() methods. +type ChainContext struct { + Top error + ErrCode ErrorCode +} + +// Code satisfies the ErrorCode interface +func (err ChainContext) Code() Code { + return err.ErrCode.Code() +} + +// Error satisfies the Error interface +func (err ChainContext) Error() string { + return err.Top.Error() +} + +// Cause satisfies the Causer interface +func (err ChainContext) Cause() error { + if wrapped := errors.Unwrap(err.Top); wrapped != nil { + return wrapped + } + return err.ErrCode +} + +// GetClientData satisfies the HasClientData interface +func (err ChainContext) GetClientData() interface{} { + return ClientData(err.ErrCode) +} + +var _ ErrorCode = (*ChainContext)(nil) +var _ HasClientData = (*ChainContext)(nil) +var _ Causer = (*ChainContext)(nil) + +// Format implements the Formatter interface +func (err ChainContext) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", err.ErrCode) + if errors.HasStack(err.ErrCode) { + fmt.Fprintf(s, "%v", err.Top) + } else { + fmt.Fprintf(s, "%+v", err.Top) + } + return + } + if s.Flag('#') { + fmt.Fprintf(s, "ChainContext{Code: %#v, Top: %#v}", err.ErrCode, err.Top) + return + } + fallthrough + case 's': + fmt.Fprintf(s, "Code: %s. Top Error: %s", err.ErrCode.Code().CodeStr(), err.Top) + case 'q': + fmt.Fprintf(s, "Code: %q. Top Error: %q", err.ErrCode.Code().CodeStr(), err.Top) + } +} + +// Format implements the Formatter interface +func (e MultiErrCode) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", e.ErrCode) + if errors.HasStack(e.ErrCode) { + for _, nextErr := range e.rest { + fmt.Fprintf(s, "%v", nextErr) + } + } else { + for _, nextErr := range e.rest { + fmt.Fprintf(s, "%+v", nextErr) + } + } + return + } + fallthrough + case 's': + fmt.Fprintf(s, "%s\n", e.ErrCode) + fmt.Fprintf(s, "%s", e.rest) + case 'q': + fmt.Fprintf(s, "%q\n", e.ErrCode) + fmt.Fprintf(s, "%q\n", e.rest) + } +} diff --git a/pkg/error_code/operation.go b/vendor/github.com/pingcap/errcode/operation.go similarity index 90% rename from pkg/error_code/operation.go rename to vendor/github.com/pingcap/errcode/operation.go index 7e796d2ef25..30200272f72 100644 --- a/pkg/error_code/operation.go +++ b/vendor/github.com/pingcap/errcode/operation.go @@ -48,14 +48,19 @@ func (e EmbedOp) GetOperation() string { return e.Op } -// OpErrCode is an ErrorCode with an "Operation" field attached. -// This may be used as a convenience to record the operation information for the error. +// OpErrCode is an ErrorCode with an Operation field attached. +// This can be conveniently constructed with Op() and AddTo() to record the operation information for the error. // However, it isn't required to be used, see the HasOperation documentation for alternatives. type OpErrCode struct { Operation string Err ErrorCode } +// Cause satisfies the Causer interface +func (e OpErrCode) Cause() error { + return e.Err +} + // Error prefixes the operation to the underlying Err Error. func (e OpErrCode) Error() string { return e.Operation + ": " + e.Err.Error() @@ -66,7 +71,7 @@ func (e OpErrCode) GetOperation() string { return e.Operation } -// Code returns the unerlying Code of Err. +// Code returns the underlying Code of Err. func (e OpErrCode) Code() Code { return e.Err.Code() } @@ -79,6 +84,7 @@ func (e OpErrCode) GetClientData() interface{} { var _ ErrorCode = (*OpErrCode)(nil) // assert implements interface var _ HasClientData = (*OpErrCode)(nil) // assert implements interface var _ HasOperation = (*OpErrCode)(nil) // assert implements interface +var _ Causer = (*OpErrCode)(nil) // assert implements interface // AddOp is constructed by Op. It allows method chaining with AddTo. type AddOp func(ErrorCode) OpErrCode @@ -94,7 +100,7 @@ func (addOp AddOp) AddTo(err ErrorCode) OpErrCode { // op := errcode.Op("path.move.x") // if start < obstable && obstacle < end { // return op.AddTo(PathBlocked{start, end, obstacle}) -// } +// } // func Op(operation string) AddOp { return func(err ErrorCode) OpErrCode { diff --git a/vendor/github.com/pingcap/errcode/stack.go b/vendor/github.com/pingcap/errcode/stack.go new file mode 100644 index 00000000000..13343162d60 --- /dev/null +++ b/vendor/github.com/pingcap/errcode/stack.go @@ -0,0 +1,88 @@ +// Copyright 2018 PingCAP, 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package errcode + +import ( + "github.com/pingcap/errors" +) + +// StackTrace retrieves the errors.StackTrace from the error if it is present. +// If there is not StackTrace it will return nil +// +// StackTrace looks to see if the error is a StackTracer or if a Causer of the error is a StackTracer. +// It will return the stack trace from the deepest error it can find. +func StackTrace(err error) errors.StackTrace { + if tracer := errors.GetStackTracer(err); tracer != nil { + return tracer.StackTrace() + } + return nil +} + +// StackCode is an ErrorCode with stack trace information attached. +// This may be used as a convenience to record the strack trace information for the error. +// Generally stack traces aren't needed for user errors, but they are provided by NewInternalErr. +// Its also possible to define your own structures that satisfy the StackTracer interface. +type StackCode struct { + Err ErrorCode + GetStack errors.StackTracer +} + +// StackTrace fulfills the StackTracer interface +func (e StackCode) StackTrace() errors.StackTrace { + return e.GetStack.StackTrace() +} + +// NewStackCode constructs a StackCode, which is an ErrorCode with stack trace information +// The second variable is an optional stack position gets rid of information about function calls to construct the stack trace. +// It is defaulted to 1 to remove this function call. +// +// NewStackCode first looks at the underlying error chain to see if it already has a StackTrace. +// If so, that StackTrace is used. +func NewStackCode(err ErrorCode, position ...int) StackCode { + stackPosition := 1 + if len(position) > 0 { + stackPosition = position[0] + } + + // if there is an existing trace, take that: it should be deeper + if tracer := errors.GetStackTracer(err); tracer != nil { + return StackCode{Err: err, GetStack: tracer} + } + + return StackCode{Err: err, GetStack: errors.NewStack(stackPosition)} +} + +// Cause satisfies the Causer interface +func (e StackCode) Cause() error { + return e.Err +} + +// Error ignores the stack and gives the underlying Err Error. +func (e StackCode) Error() string { + return e.Err.Error() +} + +// Code returns the underlying Code of Err. +func (e StackCode) Code() Code { + return e.Err.Code() +} + +// GetClientData returns the ClientData of the underlying Err. +func (e StackCode) GetClientData() interface{} { + return ClientData(e.Err) +} + +var _ ErrorCode = (*StackCode)(nil) // assert implements interface +var _ HasClientData = (*StackCode)(nil) // assert implements interface +var _ Causer = (*StackCode)(nil) // assert implements interface diff --git a/vendor/github.com/pingcap/errors/LICENSE b/vendor/github.com/pingcap/errors/LICENSE new file mode 100644 index 00000000000..835ba3e755c --- /dev/null +++ b/vendor/github.com/pingcap/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pingcap/errors/errors.go b/vendor/github.com/pingcap/errors/errors.go new file mode 100644 index 00000000000..2e1d3f62896 --- /dev/null +++ b/vendor/github.com/pingcap/errors/errors.go @@ -0,0 +1,324 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Annotate function returns a new error that adds context to the +// original error by recording a stack trace at the point Annotate is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Annotate(err, "read failed") +// } +// +// If additional control is required the errors.AddStack and errors.WithMessage +// functions destructure errors.Annotate into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Annotate constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Annotate to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// errors.Unwrap is also available: this will retrieve the next error in the chain. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Annotate, and Annotatef record a stack trace at the point they are invoked. +// This information can be retrieved with the StackTracer interface that returns +// a StackTrace. Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if stacked := errors.GetStackTracer(err); stacked != nil { +// for _, f := range stacked.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// See the documentation for Frame.Format for more details. +// +// errors.Find can be used to search for an error in the error chain. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// StackTraceAware is an optimization to avoid repetitive traversals of an error chain. +// HasStack checks for this marker first. +// Annotate/Wrap and Annotatef/Wrapf will produce this marker. +type StackTraceAware interface { + HasStack() bool +} + +// HasStack tells whether a StackTracer exists in the error chain +func HasStack(err error) bool { + if errWithStack, ok := err.(StackTraceAware); ok { + return errWithStack.HasStack() + } + return GetStackTracer(err) != nil +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +// +// For most use cases this is deprecated and AddStack should be used (which will ensure just one stack trace). +// However, one may want to use this in some situations, for example to create a 2nd trace across a goroutine. +func WithStack(err error) error { + if err == nil { + return nil + } + + return &withStack{ + err, + callers(), + } +} + +// AddStack is similar to WithStack. +// However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one. +func AddStack(err error) error { + if HasStack(err) { + return err + } + return WithStack(err) +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +// +// For most use cases this is deprecated in favor of Annotate. +// Annotate avoids creating duplicate stack traces. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: message, + causeHasStack: hasStack, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +// +// For most use cases this is deprecated in favor of Annotatef. +// Annotatef avoids creating duplicate stack traces. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + causeHasStack: hasStack, + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + causeHasStack: HasStack(err), + } +} + +type withMessage struct { + cause error + msg string + causeHasStack bool +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } +func (w *withMessage) HasStack() bool { return w.causeHasStack } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + cause := Unwrap(err) + if cause == nil { + return err + } + return Cause(cause) +} + +// Unwrap uses causer to return the next error in the chain or nil. +// This goes one-level deeper, whereas Cause goes as far as possible +func Unwrap(err error) error { + type causer interface { + Cause() error + } + if unErr, ok := err.(causer); ok { + return unErr.Cause() + } + return nil +} + +// Find an error in the chain that matches a test function. +// returns nil if no error is found. +func Find(origErr error, test func(error) bool) error { + var foundErr error + WalkDeep(origErr, func(err error) bool { + if test(err) { + foundErr = err + return true + } + return false + }) + return foundErr +} diff --git a/vendor/github.com/pingcap/errors/group.go b/vendor/github.com/pingcap/errors/group.go new file mode 100644 index 00000000000..e5a969ab76f --- /dev/null +++ b/vendor/github.com/pingcap/errors/group.go @@ -0,0 +1,42 @@ +package errors + +// ErrorGroup is an interface for multiple errors that are not a chain. +// This happens for example when executing multiple operations in parallel. +type ErrorGroup interface { + Errors() []error +} + +// Errors uses the ErrorGroup interface to return a slice of errors. +// If the ErrorGroup interface is not implemented it returns an array containing just the given error. +func Errors(err error) []error { + if eg, ok := err.(ErrorGroup); ok { + return eg.Errors() + } + return []error{err} +} + +// WalkDeep does a depth-first traversal of all errors. +// Any ErrorGroup is traversed (after going deep). +// The visitor function can return true to end the traversal early +// In that case, WalkDeep will return true, otherwise false. +func WalkDeep(err error, visitor func(err error) bool) bool { + // Go deep + unErr := err + for unErr != nil { + if done := visitor(unErr); done { + return true + } + unErr = Unwrap(unErr) + } + + // Go wide + if group, ok := err.(ErrorGroup); ok { + for _, err := range group.Errors() { + if early := WalkDeep(err, visitor); early { + return true + } + } + } + + return false +} diff --git a/vendor/github.com/pingcap/errors/juju_adaptor.go b/vendor/github.com/pingcap/errors/juju_adaptor.go new file mode 100644 index 00000000000..0b20f57370c --- /dev/null +++ b/vendor/github.com/pingcap/errors/juju_adaptor.go @@ -0,0 +1,79 @@ +package errors + +import ( + "fmt" +) + +// ==================== juju adaptor start ======================== + +// Trace just calls AddStack. +func Trace(err error) error { + return AddStack(err) +} + +// Annotate adds a message and ensures there is a stack trace. +func Annotate(err error, message string) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: message, + causeHasStack: hasStack, + } + if hasStack { + return err + } + return &withStack{ + err, + callers(), + } +} + +// Annotatef adds a message and ensures there is a stack trace. +func Annotatef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + causeHasStack: hasStack, + } + if hasStack { + return err + } + return &withStack{ + err, + callers(), + } +} + +// ErrorStack will format a stack trace if it is available, otherwise it will be Error() +// If the error is nil, the empty string is returned +// Note that this just calls fmt.Sprintf("%+v", err) +func ErrorStack(err error) string { + if err == nil { + return "" + } + return fmt.Sprintf("%+v", err) +} + +// NotFoundf represents an error with not found message. +func NotFoundf(format string, args ...interface{}) error { + return Errorf(format+" not found", args...) +} + +// BadRequestf represents an error with bad request message. +func BadRequestf(format string, args ...interface{}) error { + return Errorf(format+" bad request", args...) +} + +// NotSupportedf represents an error with not supported message. +func NotSupportedf(format string, args ...interface{}) error { + return Errorf(format+" not supported", args...) +} + +// ==================== juju adaptor end ======================== diff --git a/vendor/github.com/pingcap/errors/stack.go b/vendor/github.com/pingcap/errors/stack.go new file mode 100644 index 00000000000..bb1e6a84f33 --- /dev/null +++ b/vendor/github.com/pingcap/errors/stack.go @@ -0,0 +1,226 @@ +package errors + +import ( + "bytes" + "fmt" + "io" + "path" + "runtime" + "strconv" + "strings" +) + +// StackTracer retrieves the StackTrace +// Generally you would want to use the GetStackTracer function to do that. +type StackTracer interface { + StackTrace() StackTrace +} + +// GetStackTracer will return the first StackTracer in the causer chain. +// This function is used by AddStack to avoid creating redundant stack traces. +// +// You can also use the StackTracer interface on the returned error to get the stack trace. +func GetStackTracer(origErr error) StackTracer { + var stacked StackTracer + WalkDeep(origErr, func(err error) bool { + if stackTracer, ok := err.(StackTracer); ok { + stacked = stackTracer + return true + } + return false + }) + return stacked +} + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + f.format(s, s, verb) +} + +// format allows stack trace printing calls to be made with a bytes.Buffer. +func (f Frame) format(w io.Writer, s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(w, "unknown") + } else { + file, _ := fn.FileLine(pc) + io.WriteString(w, fn.Name()) + io.WriteString(w, "\n\t") + io.WriteString(w, file) + } + default: + io.WriteString(w, path.Base(f.file())) + } + case 'd': + io.WriteString(w, strconv.Itoa(f.line())) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(w, funcname(name)) + case 'v': + f.format(w, s, 's') + io.WriteString(w, ":") + f.format(w, s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + var b bytes.Buffer + switch verb { + case 'v': + switch { + case s.Flag('+'): + b.Grow(len(st) * stackMinLen) + for _, fr := range st { + b.WriteByte('\n') + fr.format(&b, s, verb) + } + case s.Flag('#'): + fmt.Fprintf(&b, "%#v", []Frame(st)) + default: + st.formatSlice(&b, s, verb) + } + case 's': + st.formatSlice(&b, s, verb) + } + io.Copy(s, &b) +} + +// formatSlice will format this StackTrace into the given buffer as a slice of +// Frame, only valid when called with '%s' or '%v'. +func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) { + b.WriteByte('[') + if len(st) == 0 { + b.WriteByte(']') + return + } + + b.Grow(len(st) * (stackMinLen / 4)) + st[0].format(b, s, verb) + for _, fr := range st[1:] { + b.WriteByte(' ') + fr.format(b, s, verb) + } + b.WriteByte(']') +} + +// stackMinLen is a best-guess at the minimum length of a stack trace. It +// doesn't need to be exact, just give a good enough head start for the buffer +// to avoid the expensive early growth. +const stackMinLen = 96 + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + var b bytes.Buffer + b.Grow(len(*s) * stackMinLen) + for _, pc := range *s { + f := Frame(pc) + b.WriteByte('\n') + f.format(&b, st, 'v') + } + io.Copy(st, &b) + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + return callersSkip(4) +} + +func callersSkip(skip int) *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(skip, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} + +// NewStack is for library implementers that want to generate a stack trace. +// Normally you should insted use AddStack to get an error with a stack trace. +// +// The result of this function can be turned into a stack trace by calling .StackTrace() +// +// This function takes an argument for the number of stack frames to skip. +// This avoids putting stack generation function calls like this one in the stack trace. +// A value of 0 will give you the line that called NewStack(0) +// A library author wrapping this in their own function will want to use a value of at least 1. +func NewStack(skip int) StackTracer { + return callersSkip(skip + 3) +}