Merge pull request #54 from crosbymichael/copy-grpc
Copy codes and status pkg from grpc
This commit is contained in:
commit
923a8f1f17
@ -23,9 +23,9 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -23,9 +23,9 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestReadWriteMessage(t *testing.T) {
|
||||
|
@ -26,11 +26,11 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ErrClosed is returned by client methods when the underlying connection is
|
||||
|
78
codes/code_string.go
Normal file
78
codes/code_string.go
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* 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 codes
|
||||
|
||||
import "strconv"
|
||||
|
||||
func (c Code) String() string {
|
||||
switch c {
|
||||
case OK:
|
||||
return "OK"
|
||||
case Canceled:
|
||||
return "Canceled"
|
||||
case Unknown:
|
||||
return "Unknown"
|
||||
case InvalidArgument:
|
||||
return "InvalidArgument"
|
||||
case DeadlineExceeded:
|
||||
return "DeadlineExceeded"
|
||||
case NotFound:
|
||||
return "NotFound"
|
||||
case AlreadyExists:
|
||||
return "AlreadyExists"
|
||||
case PermissionDenied:
|
||||
return "PermissionDenied"
|
||||
case ResourceExhausted:
|
||||
return "ResourceExhausted"
|
||||
case FailedPrecondition:
|
||||
return "FailedPrecondition"
|
||||
case Aborted:
|
||||
return "Aborted"
|
||||
case OutOfRange:
|
||||
return "OutOfRange"
|
||||
case Unimplemented:
|
||||
return "Unimplemented"
|
||||
case Internal:
|
||||
return "Internal"
|
||||
case Unavailable:
|
||||
return "Unavailable"
|
||||
case DataLoss:
|
||||
return "DataLoss"
|
||||
case Unauthenticated:
|
||||
return "Unauthenticated"
|
||||
default:
|
||||
return "Code(" + strconv.FormatInt(int64(c), 10) + ")"
|
||||
}
|
||||
}
|
214
codes/codes.go
Normal file
214
codes/codes.go
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* 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 codes defines the canonical error codes used by gRPC. It is
|
||||
// consistent across various languages.
|
||||
package codes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Code is an unsigned 32-bit error code as defined in the gRPC spec.
|
||||
type Code uint32
|
||||
|
||||
const (
|
||||
// OK is returned on success.
|
||||
OK Code = 0
|
||||
|
||||
// Canceled indicates the operation was canceled (typically by the caller).
|
||||
Canceled Code = 1
|
||||
|
||||
// Unknown error. An example of where this error may be returned is
|
||||
// if a Status value received from another address space belongs to
|
||||
// an error-space that is not known in this address space. Also
|
||||
// errors raised by APIs that do not return enough error information
|
||||
// may be converted to this error.
|
||||
Unknown Code = 2
|
||||
|
||||
// InvalidArgument indicates client specified an invalid argument.
|
||||
// Note that this differs from FailedPrecondition. It indicates arguments
|
||||
// that are problematic regardless of the state of the system
|
||||
// (e.g., a malformed file name).
|
||||
InvalidArgument Code = 3
|
||||
|
||||
// DeadlineExceeded means operation expired before completion.
|
||||
// For operations that change the state of the system, this error may be
|
||||
// returned even if the operation has completed successfully. For
|
||||
// example, a successful response from a server could have been delayed
|
||||
// long enough for the deadline to expire.
|
||||
DeadlineExceeded Code = 4
|
||||
|
||||
// NotFound means some requested entity (e.g., file or directory) was
|
||||
// not found.
|
||||
NotFound Code = 5
|
||||
|
||||
// AlreadyExists means an attempt to create an entity failed because one
|
||||
// already exists.
|
||||
AlreadyExists Code = 6
|
||||
|
||||
// PermissionDenied indicates the caller does not have permission to
|
||||
// execute the specified operation. It must not be used for rejections
|
||||
// caused by exhausting some resource (use ResourceExhausted
|
||||
// instead for those errors). It must not be
|
||||
// used if the caller cannot be identified (use Unauthenticated
|
||||
// instead for those errors).
|
||||
PermissionDenied Code = 7
|
||||
|
||||
// ResourceExhausted indicates some resource has been exhausted, perhaps
|
||||
// a per-user quota, or perhaps the entire file system is out of space.
|
||||
ResourceExhausted Code = 8
|
||||
|
||||
// FailedPrecondition indicates operation was rejected because the
|
||||
// system is not in a state required for the operation's execution.
|
||||
// For example, directory to be deleted may be non-empty, an rmdir
|
||||
// operation is applied to a non-directory, etc.
|
||||
//
|
||||
// A litmus test that may help a service implementor in deciding
|
||||
// between FailedPrecondition, Aborted, and Unavailable:
|
||||
// (a) Use Unavailable if the client can retry just the failing call.
|
||||
// (b) Use Aborted if the client should retry at a higher-level
|
||||
// (e.g., restarting a read-modify-write sequence).
|
||||
// (c) Use FailedPrecondition if the client should not retry until
|
||||
// the system state has been explicitly fixed. E.g., if an "rmdir"
|
||||
// fails because the directory is non-empty, FailedPrecondition
|
||||
// should be returned since the client should not retry unless
|
||||
// they have first fixed up the directory by deleting files from it.
|
||||
// (d) Use FailedPrecondition if the client performs conditional
|
||||
// REST Get/Update/Delete on a resource and the resource on the
|
||||
// server does not match the condition. E.g., conflicting
|
||||
// read-modify-write on the same resource.
|
||||
FailedPrecondition Code = 9
|
||||
|
||||
// Aborted indicates the operation was aborted, typically due to a
|
||||
// concurrency issue like sequencer check failures, transaction aborts,
|
||||
// etc.
|
||||
//
|
||||
// See litmus test above for deciding between FailedPrecondition,
|
||||
// Aborted, and Unavailable.
|
||||
Aborted Code = 10
|
||||
|
||||
// OutOfRange means operation was attempted past the valid range.
|
||||
// E.g., seeking or reading past end of file.
|
||||
//
|
||||
// Unlike InvalidArgument, this error indicates a problem that may
|
||||
// be fixed if the system state changes. For example, a 32-bit file
|
||||
// system will generate InvalidArgument if asked to read at an
|
||||
// offset that is not in the range [0,2^32-1], but it will generate
|
||||
// OutOfRange if asked to read from an offset past the current
|
||||
// file size.
|
||||
//
|
||||
// There is a fair bit of overlap between FailedPrecondition and
|
||||
// OutOfRange. We recommend using OutOfRange (the more specific
|
||||
// error) when it applies so that callers who are iterating through
|
||||
// a space can easily look for an OutOfRange error to detect when
|
||||
// they are done.
|
||||
OutOfRange Code = 11
|
||||
|
||||
// Unimplemented indicates operation is not implemented or not
|
||||
// supported/enabled in this service.
|
||||
Unimplemented Code = 12
|
||||
|
||||
// Internal errors. Means some invariants expected by underlying
|
||||
// system has been broken. If you see one of these errors,
|
||||
// something is very broken.
|
||||
Internal Code = 13
|
||||
|
||||
// Unavailable indicates the service is currently unavailable.
|
||||
// This is a most likely a transient condition and may be corrected
|
||||
// by retrying with a backoff. Note that it is not always safe to retry
|
||||
// non-idempotent operations.
|
||||
//
|
||||
// See litmus test above for deciding between FailedPrecondition,
|
||||
// Aborted, and Unavailable.
|
||||
Unavailable Code = 14
|
||||
|
||||
// DataLoss indicates unrecoverable data loss or corruption.
|
||||
DataLoss Code = 15
|
||||
|
||||
// Unauthenticated indicates the request does not have valid
|
||||
// authentication credentials for the operation.
|
||||
Unauthenticated Code = 16
|
||||
|
||||
_maxCode = 17
|
||||
)
|
||||
|
||||
var strToCode = map[string]Code{
|
||||
`"OK"`: OK,
|
||||
`"CANCELLED"`:/* [sic] */ Canceled,
|
||||
`"UNKNOWN"`: Unknown,
|
||||
`"INVALID_ARGUMENT"`: InvalidArgument,
|
||||
`"DEADLINE_EXCEEDED"`: DeadlineExceeded,
|
||||
`"NOT_FOUND"`: NotFound,
|
||||
`"ALREADY_EXISTS"`: AlreadyExists,
|
||||
`"PERMISSION_DENIED"`: PermissionDenied,
|
||||
`"RESOURCE_EXHAUSTED"`: ResourceExhausted,
|
||||
`"FAILED_PRECONDITION"`: FailedPrecondition,
|
||||
`"ABORTED"`: Aborted,
|
||||
`"OUT_OF_RANGE"`: OutOfRange,
|
||||
`"UNIMPLEMENTED"`: Unimplemented,
|
||||
`"INTERNAL"`: Internal,
|
||||
`"UNAVAILABLE"`: Unavailable,
|
||||
`"DATA_LOSS"`: DataLoss,
|
||||
`"UNAUTHENTICATED"`: Unauthenticated,
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals b into the Code.
|
||||
func (c *Code) UnmarshalJSON(b []byte) error {
|
||||
// From json.Unmarshaler: By convention, to approximate the behavior of
|
||||
// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
|
||||
// a no-op.
|
||||
if string(b) == "null" {
|
||||
return nil
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
|
||||
}
|
||||
|
||||
if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
|
||||
if ci >= _maxCode {
|
||||
return fmt.Errorf("invalid code: %q", ci)
|
||||
}
|
||||
|
||||
*c = Code(ci)
|
||||
return nil
|
||||
}
|
||||
|
||||
if jc, ok := strToCode[string(b)]; ok {
|
||||
*c = jc
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid code: %q", string(b))
|
||||
}
|
100
codes/codes_test.go
Normal file
100
codes/codes_test.go
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* 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 codes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
cpb "google.golang.org/genproto/googleapis/rpc/code"
|
||||
)
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
for s, v := range cpb.Code_value {
|
||||
want := Code(v)
|
||||
var got Code
|
||||
if err := got.UnmarshalJSON([]byte(`"` + s + `"`)); err != nil || got != want {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = %v; want <nil>. got=%v; want %v", s, err, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONUnmarshal(t *testing.T) {
|
||||
var got []Code
|
||||
want := []Code{OK, NotFound, Internal, Canceled}
|
||||
in := `["OK", "NOT_FOUND", "INTERNAL", "CANCELLED"]`
|
||||
err := json.Unmarshal([]byte(in), &got)
|
||||
if err != nil || !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("json.Unmarshal(%q, &got) = %v; want <nil>. got=%v; want %v", in, err, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON_NilReceiver(t *testing.T) {
|
||||
var got *Code
|
||||
in := OK.String()
|
||||
if err := got.UnmarshalJSON([]byte(in)); err == nil {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = nil; want <non-nil>. got=%v", in, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON_UnknownInput(t *testing.T) {
|
||||
var got Code
|
||||
for _, in := range [][]byte{[]byte(""), []byte("xxx"), []byte("Code(17)"), nil} {
|
||||
if err := got.UnmarshalJSON([]byte(in)); err == nil {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = nil; want <non-nil>. got=%v", in, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON_MarshalUnmarshal(t *testing.T) {
|
||||
for i := 0; i < _maxCode; i++ {
|
||||
var cUnMarshaled Code
|
||||
c := Code(i)
|
||||
|
||||
cJSON, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
t.Errorf("marshalling %q failed: %v", c, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(cJSON, &cUnMarshaled); err != nil {
|
||||
t.Errorf("unmarshalling code failed: %s", err)
|
||||
}
|
||||
|
||||
if c != cUnMarshaled {
|
||||
t.Errorf("code is %q after marshalling/unmarshalling, expected %q", cUnMarshaled, c)
|
||||
}
|
||||
}
|
||||
}
|
20
codes/doc.go
Normal file
20
codes/doc.go
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// This package has been copied over from the v1.23.1 release of go grpc
|
||||
// There are stability issues with point releases of grpc and ttrpc
|
||||
// only uses the codes and status package from grpc
|
||||
package codes
|
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.12
|
||||
|
||||
require (
|
||||
github.com/gogo/protobuf v1.2.1
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
|
3
go.sum
3
go.sum
@ -5,6 +5,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -34,6 +35,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -44,6 +46,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 h1:f005F/Jl5JLP036x7QIvUVhNTqxvSYwFIiyOh2q12iU=
|
||||
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -25,10 +25,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -26,11 +26,11 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/procfs"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const serviceName = "testService"
|
||||
|
@ -23,10 +23,10 @@ import (
|
||||
"path"
|
||||
"unsafe"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type Method func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error)
|
||||
|
20
status/doc.go
Normal file
20
status/doc.go
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// This package has been copied over from the v1.23.1 release of go grpc
|
||||
// There are stability issues with point releases of grpc and ttrpc
|
||||
// only uses the codes and status package from grpc
|
||||
package status
|
237
status/status.go
Normal file
237
status/status.go
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* 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 status implements errors returned by gRPC. These errors are
|
||||
// serialized and transmitted on the wire between server and client, and allow
|
||||
// for additional data to be transmitted via the Details field in the status
|
||||
// proto. gRPC service handlers should return an error created by this
|
||||
// package, and gRPC clients should expect a corresponding error to be
|
||||
// returned from the RPC call.
|
||||
//
|
||||
// This package upholds the invariants that a non-nil error may not
|
||||
// contain an OK code, and an OK code must result in a nil error.
|
||||
package status
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
)
|
||||
|
||||
// statusError is an alias of a status proto. It implements error and Status,
|
||||
// and a nil statusError should never be returned by this package.
|
||||
type statusError spb.Status
|
||||
|
||||
func (se *statusError) Error() string {
|
||||
p := (*spb.Status)(se)
|
||||
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
|
||||
}
|
||||
|
||||
func (se *statusError) GRPCStatus() *Status {
|
||||
return &Status{s: (*spb.Status)(se)}
|
||||
}
|
||||
|
||||
// Is implements future error.Is functionality.
|
||||
// A statusError is equivalent if the code and message are identical.
|
||||
func (se *statusError) Is(target error) bool {
|
||||
tse, ok := target.(*statusError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return proto.Equal((*spb.Status)(se), (*spb.Status)(tse))
|
||||
}
|
||||
|
||||
// Status represents an RPC status code, message, and details. It is immutable
|
||||
// and should be created with New, Newf, or FromProto.
|
||||
type Status struct {
|
||||
s *spb.Status
|
||||
}
|
||||
|
||||
// Code returns the status code contained in s.
|
||||
func (s *Status) Code() codes.Code {
|
||||
if s == nil || s.s == nil {
|
||||
return codes.OK
|
||||
}
|
||||
return codes.Code(s.s.Code)
|
||||
}
|
||||
|
||||
// Message returns the message contained in s.
|
||||
func (s *Status) Message() string {
|
||||
if s == nil || s.s == nil {
|
||||
return ""
|
||||
}
|
||||
return s.s.Message
|
||||
}
|
||||
|
||||
// Proto returns s's status as an spb.Status proto message.
|
||||
func (s *Status) Proto() *spb.Status {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return proto.Clone(s.s).(*spb.Status)
|
||||
}
|
||||
|
||||
// Err returns an immutable error representing s; returns nil if s.Code() is
|
||||
// OK.
|
||||
func (s *Status) Err() error {
|
||||
if s.Code() == codes.OK {
|
||||
return nil
|
||||
}
|
||||
return (*statusError)(s.s)
|
||||
}
|
||||
|
||||
// New returns a Status representing c and msg.
|
||||
func New(c codes.Code, msg string) *Status {
|
||||
return &Status{s: &spb.Status{Code: int32(c), Message: msg}}
|
||||
}
|
||||
|
||||
// Newf returns New(c, fmt.Sprintf(format, a...)).
|
||||
func Newf(c codes.Code, format string, a ...interface{}) *Status {
|
||||
return New(c, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Error returns an error representing c and msg. If c is OK, returns nil.
|
||||
func Error(c codes.Code, msg string) error {
|
||||
return New(c, msg).Err()
|
||||
}
|
||||
|
||||
// Errorf returns Error(c, fmt.Sprintf(format, a...)).
|
||||
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
||||
return Error(c, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// ErrorProto returns an error representing s. If s.Code is OK, returns nil.
|
||||
func ErrorProto(s *spb.Status) error {
|
||||
return FromProto(s).Err()
|
||||
}
|
||||
|
||||
// FromProto returns a Status representing s.
|
||||
func FromProto(s *spb.Status) *Status {
|
||||
return &Status{s: proto.Clone(s).(*spb.Status)}
|
||||
}
|
||||
|
||||
// FromError returns a Status representing err if it was produced from this
|
||||
// package or has a method `GRPCStatus() *Status`. Otherwise, ok is false and a
|
||||
// Status is returned with codes.Unknown and the original error message.
|
||||
func FromError(err error) (s *Status, ok bool) {
|
||||
if err == nil {
|
||||
return nil, true
|
||||
}
|
||||
if se, ok := err.(interface {
|
||||
GRPCStatus() *Status
|
||||
}); ok {
|
||||
return se.GRPCStatus(), true
|
||||
}
|
||||
return New(codes.Unknown, err.Error()), false
|
||||
}
|
||||
|
||||
// Convert is a convenience function which removes the need to handle the
|
||||
// boolean return value from FromError.
|
||||
func Convert(err error) *Status {
|
||||
s, _ := FromError(err)
|
||||
return s
|
||||
}
|
||||
|
||||
// WithDetails returns a new status with the provided details messages appended to the status.
|
||||
// If any errors are encountered, it returns nil and the first error encountered.
|
||||
func (s *Status) WithDetails(details ...proto.Message) (*Status, error) {
|
||||
if s.Code() == codes.OK {
|
||||
return nil, errors.New("no error details for status with code OK")
|
||||
}
|
||||
// s.Code() != OK implies that s.Proto() != nil.
|
||||
p := s.Proto()
|
||||
for _, detail := range details {
|
||||
any, err := ptypes.MarshalAny(detail)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Details = append(p.Details, any)
|
||||
}
|
||||
return &Status{s: p}, nil
|
||||
}
|
||||
|
||||
// Details returns a slice of details messages attached to the status.
|
||||
// If a detail cannot be decoded, the error is returned in place of the detail.
|
||||
func (s *Status) Details() []interface{} {
|
||||
if s == nil || s.s == nil {
|
||||
return nil
|
||||
}
|
||||
details := make([]interface{}, 0, len(s.s.Details))
|
||||
for _, any := range s.s.Details {
|
||||
detail := &ptypes.DynamicAny{}
|
||||
if err := ptypes.UnmarshalAny(any, detail); err != nil {
|
||||
details = append(details, err)
|
||||
continue
|
||||
}
|
||||
details = append(details, detail.Message)
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
// Code returns the Code of the error if it is a Status error, codes.OK if err
|
||||
// is nil, or codes.Unknown otherwise.
|
||||
func Code(err error) codes.Code {
|
||||
// Don't use FromError to avoid allocation of OK status.
|
||||
if err == nil {
|
||||
return codes.OK
|
||||
}
|
||||
if se, ok := err.(interface {
|
||||
GRPCStatus() *Status
|
||||
}); ok {
|
||||
return se.GRPCStatus().Code()
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
||||
|
||||
// FromContextError converts a context error into a Status. It returns a
|
||||
// Status with codes.OK if err is nil, or a Status with codes.Unknown if err is
|
||||
// non-nil and not a context error.
|
||||
func FromContextError(err error) *Status {
|
||||
switch err {
|
||||
case nil:
|
||||
return nil
|
||||
case context.DeadlineExceeded:
|
||||
return New(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled:
|
||||
return New(codes.Canceled, err.Error())
|
||||
default:
|
||||
return New(codes.Unknown, err.Error())
|
||||
}
|
||||
}
|
88
status/status_ext_test.go
Normal file
88
status/status_ext_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2019 gRPC authors.
|
||||
*
|
||||
* 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 status_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/containerd/ttrpc/status"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc/test/grpc_testing"
|
||||
)
|
||||
|
||||
func errWithDetails(t *testing.T, s *status.Status, details ...proto.Message) error {
|
||||
t.Helper()
|
||||
res, err := s.WithDetails(details...)
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).WithDetails(%v) = %v, %v; want _, <nil>", s, details, res, err)
|
||||
}
|
||||
return res.Err()
|
||||
}
|
||||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
// Test errors.
|
||||
testErr := status.Error(codes.Internal, "internal server error")
|
||||
testErrWithDetails := errWithDetails(t, status.New(codes.Internal, "internal server error"), &grpc_testing.Empty{})
|
||||
|
||||
// Test cases.
|
||||
testCases := []struct {
|
||||
err1, err2 error
|
||||
want bool
|
||||
}{
|
||||
{err1: testErr, err2: nil, want: false},
|
||||
{err1: testErr, err2: status.Error(codes.Internal, "internal server error"), want: true},
|
||||
{err1: testErr, err2: status.Error(codes.Internal, "internal error"), want: false},
|
||||
{err1: testErr, err2: status.Error(codes.Unknown, "internal server error"), want: false},
|
||||
{err1: testErr, err2: errors.New("non-grpc error"), want: false},
|
||||
{err1: testErrWithDetails, err2: status.Error(codes.Internal, "internal server error"), want: false},
|
||||
{err1: testErrWithDetails, err2: errWithDetails(t, status.New(codes.Internal, "internal server error"), &grpc_testing.Empty{}), want: true},
|
||||
{err1: testErrWithDetails, err2: errWithDetails(t, status.New(codes.Internal, "internal server error"), &grpc_testing.Empty{}, &grpc_testing.Empty{}), want: false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
isError, ok := tc.err1.(interface{ Is(target error) bool })
|
||||
if !ok {
|
||||
t.Errorf("(%v) does not implement is", tc.err1)
|
||||
continue
|
||||
}
|
||||
|
||||
is := isError.Is(tc.err2)
|
||||
if is != tc.want {
|
||||
t.Errorf("(%v).Is(%v) = %t; want %t", tc.err1, tc.err2, is, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
378
status/status_test.go
Normal file
378
status/status_test.go
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* 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 status
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/ttrpc/codes"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
apb "github.com/golang/protobuf/ptypes/any"
|
||||
dpb "github.com/golang/protobuf/ptypes/duration"
|
||||
cpb "google.golang.org/genproto/googleapis/rpc/code"
|
||||
epb "google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
)
|
||||
|
||||
// errEqual is essentially a copy of testutils.StatusErrEqual(), to avoid a
|
||||
// cyclic dependency.
|
||||
func errEqual(err1, err2 error) bool {
|
||||
status1, ok := FromError(err1)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
status2, ok := FromError(err2)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return proto.Equal(status1.Proto(), status2.Proto())
|
||||
}
|
||||
|
||||
func TestErrorsWithSameParameters(t *testing.T) {
|
||||
const description = "some description"
|
||||
e1 := Errorf(codes.AlreadyExists, description)
|
||||
e2 := Errorf(codes.AlreadyExists, description)
|
||||
if e1 == e2 || !errEqual(e1, e2) {
|
||||
t.Fatalf("Errors should be equivalent but unique - e1: %v, %v e2: %p, %v", e1.(*statusError), e1, e2.(*statusError), e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromToProto(t *testing.T) {
|
||||
s := &spb.Status{
|
||||
Code: int32(codes.Internal),
|
||||
Message: "test test test",
|
||||
Details: []*apb.Any{{TypeUrl: "foo", Value: []byte{3, 2, 1}}},
|
||||
}
|
||||
|
||||
err := FromProto(s)
|
||||
if got := err.Proto(); !proto.Equal(s, got) {
|
||||
t.Fatalf("Expected errors to be identical - s: %v got: %v", s, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromNilProto(t *testing.T) {
|
||||
tests := []*Status{nil, FromProto(nil)}
|
||||
for _, s := range tests {
|
||||
if c := s.Code(); c != codes.OK {
|
||||
t.Errorf("s: %v - Expected s.Code() = OK; got %v", s, c)
|
||||
}
|
||||
if m := s.Message(); m != "" {
|
||||
t.Errorf("s: %v - Expected s.Message() = \"\"; got %q", s, m)
|
||||
}
|
||||
if p := s.Proto(); p != nil {
|
||||
t.Errorf("s: %v - Expected s.Proto() = nil; got %q", s, p)
|
||||
}
|
||||
if e := s.Err(); e != nil {
|
||||
t.Errorf("s: %v - Expected s.Err() = nil; got %v", s, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
err := Error(codes.Internal, "test description")
|
||||
if got, want := err.Error(), "rpc error: code = Internal desc = test description"; got != want {
|
||||
t.Fatalf("err.Error() = %q; want %q", got, want)
|
||||
}
|
||||
s, _ := FromError(err)
|
||||
if got, want := s.Code(), codes.Internal; got != want {
|
||||
t.Fatalf("err.Code() = %s; want %s", got, want)
|
||||
}
|
||||
if got, want := s.Message(), "test description"; got != want {
|
||||
t.Fatalf("err.Message() = %s; want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorOK(t *testing.T) {
|
||||
err := Error(codes.OK, "foo")
|
||||
if err != nil {
|
||||
t.Fatalf("Error(codes.OK, _) = %p; want nil", err.(*statusError))
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorProtoOK(t *testing.T) {
|
||||
s := &spb.Status{Code: int32(codes.OK)}
|
||||
if got := ErrorProto(s); got != nil {
|
||||
t.Fatalf("ErrorProto(%v) = %v; want nil", s, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromError(t *testing.T) {
|
||||
code, message := codes.Internal, "test description"
|
||||
err := Error(code, message)
|
||||
s, ok := FromError(err)
|
||||
if !ok || s.Code() != code || s.Message() != message || s.Err() == nil {
|
||||
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromErrorOK(t *testing.T) {
|
||||
code, message := codes.OK, ""
|
||||
s, ok := FromError(nil)
|
||||
if !ok || s.Code() != code || s.Message() != message || s.Err() != nil {
|
||||
t.Fatalf("FromError(nil) = %v, %v; want <Code()=%s, Message()=%q, Err=nil>, true", s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
type customError struct {
|
||||
Code codes.Code
|
||||
Message string
|
||||
Details []*apb.Any
|
||||
}
|
||||
|
||||
func (c customError) Error() string {
|
||||
return fmt.Sprintf("rpc error: code = %s desc = %s", c.Code, c.Message)
|
||||
}
|
||||
|
||||
func (c customError) GRPCStatus() *Status {
|
||||
return &Status{
|
||||
s: &spb.Status{
|
||||
Code: int32(c.Code),
|
||||
Message: c.Message,
|
||||
Details: c.Details,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromErrorImplementsInterface(t *testing.T) {
|
||||
code, message := codes.Internal, "test description"
|
||||
details := []*apb.Any{{
|
||||
TypeUrl: "testUrl",
|
||||
Value: []byte("testValue"),
|
||||
}}
|
||||
err := customError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Details: details,
|
||||
}
|
||||
s, ok := FromError(err)
|
||||
if !ok || s.Code() != code || s.Message() != message || s.Err() == nil {
|
||||
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
|
||||
}
|
||||
pd := s.Proto().GetDetails()
|
||||
if len(pd) != 1 || !proto.Equal(pd[0], details[0]) {
|
||||
t.Fatalf("s.Proto.GetDetails() = %v; want <Details()=%s>", pd, details)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromErrorUnknownError(t *testing.T) {
|
||||
code, message := codes.Unknown, "unknown error"
|
||||
err := errors.New("unknown error")
|
||||
s, ok := FromError(err)
|
||||
if ok || s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q>, false", err, s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertKnownError(t *testing.T) {
|
||||
code, message := codes.Internal, "test description"
|
||||
err := Error(code, message)
|
||||
s := Convert(err)
|
||||
if s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertUnknownError(t *testing.T) {
|
||||
code, message := codes.Unknown, "unknown error"
|
||||
err := errors.New("unknown error")
|
||||
s := Convert(err)
|
||||
if s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_ErrorDetails(t *testing.T) {
|
||||
tests := []struct {
|
||||
code codes.Code
|
||||
details []proto.Message
|
||||
}{
|
||||
{
|
||||
code: codes.NotFound,
|
||||
details: nil,
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
details: []proto.Message{
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: codes.Internal,
|
||||
details: []proto.Message{
|
||||
&epb.DebugInfo{
|
||||
StackEntries: []string{
|
||||
"first stack",
|
||||
"second stack",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: codes.Unavailable,
|
||||
details: []proto.Message{
|
||||
&epb.RetryInfo{
|
||||
RetryDelay: &dpb.Duration{Seconds: 60},
|
||||
},
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s, err := New(tc.code, "").WithDetails(tc.details...)
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).WithDetails(%+v) failed: %v", str(s), tc.details, err)
|
||||
}
|
||||
details := s.Details()
|
||||
for i := range details {
|
||||
if !proto.Equal(details[i].(proto.Message), tc.details[i]) {
|
||||
t.Fatalf("(%v).Details()[%d] = %+v, want %+v", str(s), i, details[i], tc.details[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_WithDetails_Fail(t *testing.T) {
|
||||
tests := []*Status{
|
||||
nil,
|
||||
FromProto(nil),
|
||||
New(codes.OK, ""),
|
||||
}
|
||||
for _, s := range tests {
|
||||
if s, err := s.WithDetails(); err == nil || s != nil {
|
||||
t.Fatalf("(%v).WithDetails(%+v) = %v, %v; want nil, non-nil", str(s), []proto.Message{}, s, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_ErrorDetails_Fail(t *testing.T) {
|
||||
tests := []struct {
|
||||
s *Status
|
||||
i []interface{}
|
||||
}{
|
||||
{
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
FromProto(nil),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
New(codes.OK, ""),
|
||||
[]interface{}{},
|
||||
},
|
||||
{
|
||||
FromProto(&spb.Status{
|
||||
Code: int32(cpb.Code_CANCELLED),
|
||||
Details: []*apb.Any{
|
||||
{
|
||||
TypeUrl: "",
|
||||
Value: []byte{},
|
||||
},
|
||||
mustMarshalAny(&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
}),
|
||||
},
|
||||
}),
|
||||
[]interface{}{
|
||||
errors.New(`message type url "" is invalid`),
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.s.Details()
|
||||
if !reflect.DeepEqual(got, tc.i) {
|
||||
t.Errorf("(%v).Details() = %+v, want %+v", str(tc.s), got, tc.i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func str(s *Status) string {
|
||||
if s == nil {
|
||||
return "nil"
|
||||
}
|
||||
if s.s == nil {
|
||||
return "<Code=OK>"
|
||||
}
|
||||
return fmt.Sprintf("<Code=%v, Message=%q, Details=%+v>", codes.Code(s.s.GetCode()), s.s.GetMessage(), s.s.GetDetails())
|
||||
}
|
||||
|
||||
// mustMarshalAny converts a protobuf message to an any.
|
||||
func mustMarshalAny(msg proto.Message) *apb.Any {
|
||||
any, err := ptypes.MarshalAny(msg)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ptypes.MarshalAny(%+v) failed: %v", msg, err))
|
||||
}
|
||||
return any
|
||||
}
|
||||
|
||||
func TestFromContextError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in error
|
||||
want *Status
|
||||
}{
|
||||
{in: nil, want: New(codes.OK, "")},
|
||||
{in: context.DeadlineExceeded, want: New(codes.DeadlineExceeded, context.DeadlineExceeded.Error())},
|
||||
{in: context.Canceled, want: New(codes.Canceled, context.Canceled.Error())},
|
||||
{in: errors.New("other"), want: New(codes.Unknown, "other")},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got := FromContextError(tc.in)
|
||||
if got.Code() != tc.want.Code() || got.Message() != tc.want.Message() {
|
||||
t.Errorf("FromContextError(%v) = %v; want %v", tc.in, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user