diff --git a/channel.go b/channel.go index 9810695..aa8c954 100644 --- a/channel.go +++ b/channel.go @@ -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 ( diff --git a/channel_test.go b/channel_test.go index c7b4dcb..2242336 100644 --- a/channel_test.go +++ b/channel_test.go @@ -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) { diff --git a/client.go b/client.go index c195d11..e816941 100644 --- a/client.go +++ b/client.go @@ -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 diff --git a/codes/code_string.go b/codes/code_string.go deleted file mode 100644 index 27b12a6..0000000 --- a/codes/code_string.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - 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) + ")" - } -} diff --git a/codes/codes.go b/codes/codes.go deleted file mode 100644 index 91300c9..0000000 --- a/codes/codes.go +++ /dev/null @@ -1,214 +0,0 @@ -/* - 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)) -} diff --git a/codes/codes_test.go b/codes/codes_test.go deleted file mode 100644 index 0f1a416..0000000 --- a/codes/codes_test.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - 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 . 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 . 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 . 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 . 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) - } - } -} diff --git a/codes/doc.go b/codes/doc.go deleted file mode 100644 index 01b0266..0000000 --- a/codes/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - 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 diff --git a/go.mod b/go.mod index 036247c..2b662a7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ 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 diff --git a/go.sum b/go.sum index ebccedd..66aa0b1 100644 --- a/go.sum +++ b/go.sum @@ -5,7 +5,6 @@ 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= @@ -35,7 +34,6 @@ 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= @@ -46,7 +44,6 @@ 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= diff --git a/server.go b/server.go index 3f72493..1d4f1df 100644 --- a/server.go +++ b/server.go @@ -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 ( diff --git a/server_test.go b/server_test.go index 7164b98..1358b08 100644 --- a/server_test.go +++ b/server_test.go @@ -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" diff --git a/services.go b/services.go index c1e7776..2a83ba8 100644 --- a/services.go +++ b/services.go @@ -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) diff --git a/status/doc.go b/status/doc.go deleted file mode 100644 index 4b091ec..0000000 --- a/status/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - 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 diff --git a/status/status.go b/status/status.go deleted file mode 100644 index a78674c..0000000 --- a/status/status.go +++ /dev/null @@ -1,237 +0,0 @@ -/* - 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()) - } -} diff --git a/status/status_ext_test.go b/status/status_ext_test.go deleted file mode 100644 index b183d62..0000000 --- a/status/status_ext_test.go +++ /dev/null @@ -1,88 +0,0 @@ -/* - 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 _, ", 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) - } - } -} diff --git a/status/status_test.go b/status/status_test.go deleted file mode 100644 index 4f3d3d1..0000000 --- a/status/status_test.go +++ /dev/null @@ -1,378 +0,0 @@ -/* - 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 , 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 , 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 , 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 ", 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 , 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 ", 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 ", 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 "" - } - return fmt.Sprintf("", 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) - } - } -}