Skip to content

Commit 7993a99

Browse files
committed
update error codes, api and finalize implementation
1 parent 98a7db5 commit 7993a99

File tree

6 files changed

+259
-314
lines changed

6 files changed

+259
-314
lines changed

pkg/capabilities/errors/error.go

Lines changed: 106 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,70 +2,147 @@ package errors
22

33
import "fmt"
44

5-
type ReportType int
5+
type Origin int
66

77
const (
8-
// LocalOnly The message in the error may contain sensitive node local information and should not be reported remotely.
9-
// In addition the serialised string representation is prefixed with an identify that prevents the error
10-
// from being accidentally or maliciously marked as remote reportable by manipulating the error string.
11-
LocalOnly ReportType = 0
8+
// OriginSystem The error originated from a system issue.
9+
OriginSystem = 0
1210

13-
// RemoteReportable The message in the error is safe to report remotely between nodes.
14-
RemoteReportable ReportType = 1
11+
// OriginUser The error originated from user input or action.
12+
OriginUser = 1
13+
)
14+
15+
func (o Origin) String() string {
16+
switch o {
17+
case OriginSystem:
18+
return "System"
19+
case OriginUser:
20+
return "User"
21+
default:
22+
return "Unknown"
23+
}
24+
}
1525

16-
// ReportableUser The error is due to user error and is safe to report remotely between nodes.
17-
ReportableUser ReportType = 2
26+
// FromOriginString converts a string to an Origin value.
27+
func FromOriginString(s string) Origin {
28+
switch s {
29+
case "System":
30+
return OriginSystem
31+
case "User":
32+
return OriginUser
33+
default:
34+
return Origin(-1)
35+
}
36+
}
37+
38+
type Visibility int
39+
40+
const (
41+
// VisibilityPublic The full details of the error can be shared across all nodes in the network.
42+
VisibilityPublic = 0
43+
44+
// VisibilityPrivate The error contains sensitive information that should only be visible to the local node.
45+
VisibilityPrivate = 1
1846
)
1947

48+
// String returns the string representation of the Visibility value.
49+
func (v Visibility) String() string {
50+
switch v {
51+
case VisibilityPublic:
52+
return "Public"
53+
case VisibilityPrivate:
54+
return "Private"
55+
default:
56+
return "Unknown"
57+
}
58+
}
59+
60+
// FromVisibilityString converts a string to a Visibility value.
61+
func FromVisibilityString(s string) Visibility {
62+
switch s {
63+
case "Public":
64+
return VisibilityPublic
65+
case "Private":
66+
return VisibilityPrivate
67+
default:
68+
return Visibility(-1)
69+
}
70+
}
71+
2072
type Error interface {
2173
error
2274

23-
ReportType() ReportType
75+
Visibility() Visibility
76+
Origin() Origin
2477
Code() ErrorCode
2578
SerializeToString() string
2679
SerializeToRemoteReportableString() string
80+
Equals(otherErr Error) bool
2781
}
2882

2983
type capabilityError struct {
3084
err error
31-
reportType ReportType
85+
origin Origin
86+
visibility Visibility
3287
errorCode ErrorCode
3388
}
3489

35-
func newError(err error, reportType ReportType, errorCode ErrorCode) Error {
90+
func NewError(err error, visibility Visibility, origin Origin, errorCode ErrorCode) Error {
3691
return &capabilityError{
3792
err: err,
38-
reportType: reportType,
93+
origin: origin,
94+
visibility: visibility,
3995
errorCode: errorCode,
4096
}
4197
}
4298

43-
// NewRemoteReportableError indicates that the wrapped error does not contain any node local confidential information
44-
// and is safe to report to other nodes in the network.
45-
func NewRemoteReportableError(err error, errorCode ErrorCode) Error {
46-
return newError(err, RemoteReportable, errorCode)
99+
// NewPublicSystemError indicates that the wrapped error is due to a system-level issue and does not contain any
100+
// sensitive information that should only be visible to the node on which it occurred, making it safe to share the full error details
101+
// with other nodes in the network.
102+
func NewPublicSystemError(err error, errorCode ErrorCode) Error {
103+
return NewError(err, VisibilityPublic, OriginSystem, errorCode)
47104
}
48105

49-
// NewReportableUserError indicates that the wrapped error is due to user error and does not contain any node local confidential information
50-
// and is safe to report to other nodes in the network.
51-
func NewReportableUserError(err error, errorCode ErrorCode) Error {
52-
return newError(err, ReportableUser, errorCode)
106+
// NewPublicUserError indicates that the wrapped error is due to a user-level issue and does not contain any
107+
// information that should only be visible to the node on which it occurred, making it safe to share the full error details
108+
// with other nodes in the network.
109+
func NewPublicUserError(err error, errorCode ErrorCode) Error {
110+
return NewError(err, VisibilityPublic, OriginUser, errorCode)
53111
}
54112

55-
// NewLocalReportableError indicates that the wrapped error may contain node local confidential information
56-
// that should not be reported to other nodes in the network. Only the error code and generic message will be reported remotely.
57-
func NewLocalReportableError(err error, errorCode ErrorCode) Error {
58-
return newError(err, LocalOnly, errorCode)
113+
// NewPrivateSystemError indicates that the wrapped error is due to a system-level issue and may contain
114+
// sensitive information that should only be visible to the node on which it occurred. The error code will still be
115+
// visible to other nodes in the network.
116+
func NewPrivateSystemError(err error, errorCode ErrorCode) Error {
117+
return NewError(err, VisibilityPrivate, OriginSystem, errorCode)
59118
}
60119

61-
func (e *capabilityError) Error() string {
120+
// NewPrivateUserError indicates that the wrapped error is due to a user-level issue and may contain
121+
// sensitive information that should only be visible to the node on which it occurred. The error code will still be
122+
// visible to other nodes in the network.
123+
func NewPrivateUserError(err error, errorCode ErrorCode) Error {
124+
return NewError(err, VisibilityPrivate, OriginSystem, errorCode)
125+
}
126+
127+
func (e capabilityError) Error() string {
62128
return fmt.Sprintf("[%d]%s:", e.errorCode, e.errorCode.String()) + " " + e.err.Error()
63129
}
64130

65-
func (e *capabilityError) ReportType() ReportType {
66-
return e.reportType
131+
func (e capabilityError) Origin() Origin {
132+
return e.origin
133+
}
134+
135+
func (e capabilityError) Visibility() Visibility {
136+
return e.visibility
67137
}
68138

69-
func (e *capabilityError) Code() ErrorCode {
139+
func (e capabilityError) Code() ErrorCode {
70140
return e.errorCode
71141
}
142+
143+
func (e capabilityError) Equals(otherErr Error) bool {
144+
return e.errorCode == otherErr.Code() &&
145+
e.origin == otherErr.Origin() &&
146+
e.visibility == otherErr.Visibility() &&
147+
e.Error() == otherErr.Error()
148+
}

pkg/capabilities/errors/error_codes.go

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,54 @@ package errors
22

33
type ErrorCode uint32
44

5-
// *** TODO ***
6-
// Just use GRPC error codes? or use GRPC error codes as inspiration (as below) but customize
7-
// to make them relevant to capabilities?, IMO latter is better as it gives the flexibility to add
8-
// more capability specific error codes (e.g. ConsensusFailed) whilst removing those
9-
// that are not relevant (e.g OK, Unknown,
10-
5+
// Capability error codes. These are largely based on gRPC error codes:
6+
// https://grpc.github.io/grpc/core/md_doc_statuscodes.html. Additional
7+
// custom error codes can be added starting from 100 to avoid collision with
8+
// future gRPC defined codes. 0 (OK) is excluded is it is never a valid code for
9+
// a capability error as capability errors must always represent some failure condition.
1110
const (
12-
// Uncategorized indicates no appropriate error code available - the capability author should consider adding a
13-
// code that fits better. If the error code does not exist on the receiving side, it will be treated as Uncategorized
14-
// to ensure backwards compatibility.
15-
Uncategorized ErrorCode = 0
11+
// Canceled indicates the operation was canceled (typically by the caller).
12+
Canceled ErrorCode = 1
1613

17-
// Cancelled indicates the operation was canceled (typically by the caller).
18-
Cancelled ErrorCode = 1
14+
// Unknown error. An example of where this error may be returned is
15+
// if a Status value received from another address space belongs to
16+
// an error-space that is not known in this address space. Also
17+
// errors raised by APIs that do not return enough error information
18+
// may be converted to this error.
19+
Unknown ErrorCode = 2
1920

2021
// InvalidArgument indicates client specified an invalid argument.
2122
// Note that this differs from FailedPrecondition. It indicates arguments
2223
// that are problematic regardless of the state of the system
2324
// (e.g., a malformed file name).
24-
InvalidArgument ErrorCode = 2
25+
InvalidArgument ErrorCode = 3
2526

2627
// DeadlineExceeded means operation expired before completion.
27-
DeadlineExceeded ErrorCode = 3
28+
// For operations that change the state of the system, this error may be
29+
// returned even if the operation has completed successfully. For
30+
// example, a successful response from a server could have been delayed
31+
// long enough for the deadline to expire.
32+
DeadlineExceeded ErrorCode = 4
2833

2934
// NotFound means some requested entity (e.g., file or directory) was
3035
// not found.
31-
NotFound ErrorCode = 4
36+
NotFound ErrorCode = 5
3237

3338
// AlreadyExists means an attempt to create an entity failed because one
3439
// already exists.
35-
AlreadyExists ErrorCode = 5
40+
AlreadyExists ErrorCode = 6
3641

3742
// PermissionDenied indicates the caller does not have permission to
3843
// execute the specified operation. It must not be used for rejections
3944
// caused by exhausting some resource (use ResourceExhausted
4045
// instead for those errors). It must not be
4146
// used if the caller cannot be identified (use Unauthenticated
4247
// instead for those errors).
43-
PermissionDenied ErrorCode = 6
48+
PermissionDenied ErrorCode = 7
4449

4550
// ResourceExhausted indicates some resource has been exhausted, perhaps
4651
// a per-user quota, or perhaps the entire file system is out of space.
47-
ResourceExhausted ErrorCode = 7
52+
ResourceExhausted ErrorCode = 8
4853

4954
// FailedPrecondition indicates operation was rejected because the
5055
// system is not in a state required for the operation's execution.
@@ -65,15 +70,15 @@ const (
6570
// REST Get/Update/Delete on a resource and the resource on the
6671
// server does not match the condition. E.g., conflicting
6772
// read-modify-write on the same resource.
68-
FailedPrecondition ErrorCode = 8
73+
FailedPrecondition ErrorCode = 9
6974

7075
// Aborted indicates the operation was aborted, typically due to a
7176
// concurrency issue like sequencer check failures, transaction aborts,
7277
// etc.
7378
//
7479
// See litmus test above for deciding between FailedPrecondition,
7580
// Aborted, and Unavailable.
76-
Aborted ErrorCode = 9
81+
Aborted ErrorCode = 10
7782

7883
// OutOfRange means operation was attempted past the valid range.
7984
// E.g., seeking or reading past end of file.
@@ -90,24 +95,16 @@ const (
9095
// error) when it applies so that callers who are iterating through
9196
// a space can easily look for an OutOfRange error to detect when
9297
// they are done.
93-
//
94-
// This error code will not be generated by the gRPC framework.
95-
OutOfRange ErrorCode = 10
98+
OutOfRange ErrorCode = 11
9699

97100
// Unimplemented indicates operation is not implemented or not
98101
// supported/enabled in this service.
99-
//
100-
// This error code will be generated by the gRPC framework. Most
101-
// commonly, you will see this error code when a method implementation
102-
// is missing on the server. It can also be generated for unknown
103-
// compression algorithms or a disagreement as to whether an RPC should
104-
// be streaming.
105-
Unimplemented ErrorCode = 11
102+
Unimplemented ErrorCode = 12
106103

107104
// Internal errors. Means some invariants expected by underlying
108105
// system has been broken. If you see one of these errors,
109106
// something is very broken.
110-
Internal ErrorCode = 12
107+
Internal ErrorCode = 13
111108

112109
// Unavailable indicates the service is currently unavailable.
113110
// This is a most likely a transient condition and may be corrected
@@ -116,17 +113,20 @@ const (
116113
//
117114
// See litmus test above for deciding between FailedPrecondition,
118115
// Aborted, and Unavailable.
119-
Unavailable ErrorCode = 13
116+
Unavailable ErrorCode = 14
120117

121118
// DataLoss indicates unrecoverable data loss or corruption.
122-
DataLoss ErrorCode = 14
119+
DataLoss ErrorCode = 15
123120

124121
// Unauthenticated indicates the request does not have valid
125122
// authentication credentials for the operation.
126-
Unauthenticated ErrorCode = 15
123+
Unauthenticated ErrorCode = 16
124+
125+
// Custom error codes not defined in the gRPC error space below here,
126+
// starting at 100 to avoid collision with future gRPC defined codes.
127127

128128
// ConsensusFailed indicates failure to reach consensus
129-
ConsensusFailed ErrorCode = 16
129+
ConsensusFailed ErrorCode = 100
130130
)
131131

132132
// String returns the string representation of the ErrorCode.
@@ -138,8 +138,8 @@ func (e ErrorCode) String() string {
138138
}
139139

140140
var errorCodeToString = map[ErrorCode]string{
141-
Uncategorized: "Uncategorized",
142-
Cancelled: "Cancelled",
141+
Canceled: "Canceled",
142+
Unknown: "Unknown",
143143
InvalidArgument: "InvalidArgument",
144144
DeadlineExceeded: "DeadlineExceeded",
145145
NotFound: "NotFound",
@@ -157,11 +157,29 @@ var errorCodeToString = map[ErrorCode]string{
157157
ConsensusFailed: "ConsensusFailed",
158158
}
159159

160-
// ErrorCodeFromInt returns the ErrorCode for a given int, or Uncategorized if not found.
161-
func ErrorCodeFromInt(i uint32) ErrorCode {
162-
code := ErrorCode(i)
163-
if _, ok := errorCodeToString[code]; ok {
160+
var stringToErrorCode = map[string]ErrorCode{
161+
"Canceled": Canceled,
162+
"Unknown": Unknown,
163+
"InvalidArgument": InvalidArgument,
164+
"DeadlineExceeded": DeadlineExceeded,
165+
"NotFound": NotFound,
166+
"AlreadyExists": AlreadyExists,
167+
"PermissionDenied": PermissionDenied,
168+
"ResourceExhausted": ResourceExhausted,
169+
"FailedPrecondition": FailedPrecondition,
170+
"Aborted": Aborted,
171+
"OutOfRange": OutOfRange,
172+
"Unimplemented": Unimplemented,
173+
"Internal": Internal,
174+
"Unavailable": Unavailable,
175+
"DataLoss": DataLoss,
176+
"Unauthenticated": Unauthenticated,
177+
"ConsensusFailed": ConsensusFailed,
178+
}
179+
180+
func FromErrorCodeString(str string) ErrorCode {
181+
if code, ok := stringToErrorCode[str]; ok {
164182
return code
165183
}
166-
return Uncategorized
184+
return Unknown
167185
}

0 commit comments

Comments
 (0)