From 6e0efb6faa4cb5f0171eea7a87de8758ca1074ae Mon Sep 17 00:00:00 2001 From: robertojrojas Date: Sun, 6 Aug 2023 18:49:41 -0400 Subject: [PATCH] starts error codes Etag Mismatch Signed-off-by: robertojrojas --- internal/errorcodes/errorcodes.go | 89 +++++++++++++++++++++++++++++++ state/redis/redis.go | 17 ++++++ 2 files changed, 106 insertions(+) create mode 100644 internal/errorcodes/errorcodes.go diff --git a/internal/errorcodes/errorcodes.go b/internal/errorcodes/errorcodes.go new file mode 100644 index 0000000000..63ae664fd4 --- /dev/null +++ b/internal/errorcodes/errorcodes.go @@ -0,0 +1,89 @@ +/* +Copyright 2023 The Dapr 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 implieout. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errorcodes + +import ( + "google.golang.org/genproto/googleapis/rpc/errdetails" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/runtime/protoiface" +) + +type Reason string + +const ( + ErrorCodesFeatureMetadataKey = "error_codes_feature" + Owner = "components-contrib" + Domain = "dapr.io" +) + +var ( + StateETagMismatchReason = Reason("DAPR_STATE_ETAG_MISMATCH") +) + +type ResourceInfoData struct { + ResourceType, ResourceName string +} + +func FeatureEnabled(md map[string]string) bool { + if _, ok := md[ErrorCodesFeatureMetadataKey]; ok { + return true + } + return false +} + +// NewStatusError returns a Status representing Code, error Reason, Message, and optional ResourceInfo and Metadata. +// When successful, it returns a StatusError, otherwise returns the original error +func NewStatusError(code codes.Code, err error, errDescription string, reason Reason, rid *ResourceInfoData, metadata map[string]string) error { + md := metadata + if md == nil { + md = map[string]string{} + } + + messages := []protoiface.MessageV1{ + NewErrorInfo(reason, md), + } + + if rid != nil { + messages = append(messages, NewResourceInfo(rid, errDescription)) + } + + ste, stErr := status.New(code, errDescription).WithDetails(messages...) + if stErr != nil { + return err + } + + return ste.Err() +} + +func NewErrorInfo(reason Reason, md map[string]string) *errdetails.ErrorInfo { + ei := errdetails.ErrorInfo{ + Domain: Domain, + Reason: string(reason), + Metadata: md, + } + + return &ei +} + +func NewResourceInfo(rid *ResourceInfoData, description string) *errdetails.ResourceInfo { + return &errdetails.ResourceInfo{ + ResourceType: rid.ResourceType, + ResourceName: rid.ResourceName, + Owner: Owner, + Description: description, + } +} diff --git a/state/redis/redis.go b/state/redis/redis.go index a3f65af26b..90bceb10b1 100644 --- a/state/redis/redis.go +++ b/state/redis/redis.go @@ -23,9 +23,11 @@ import ( "sync/atomic" jsoniter "github.com/json-iterator/go" + "google.golang.org/grpc/codes" "github.com/dapr/components-contrib/contenttype" rediscomponent "github.com/dapr/components-contrib/internal/component/redis" + "github.com/dapr/components-contrib/internal/errorcodes" daprmetadata "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/state" "github.com/dapr/components-contrib/state/query" @@ -101,6 +103,7 @@ type StateStore struct { replicas int querySchemas querySchemas suppressActorStateStoreWarning atomic.Bool + resourceInfoData errorcodes.ResourceInfoData logger logger.Logger } @@ -117,6 +120,10 @@ func newStateStore(log logger.Logger) *StateStore { json: jsoniter.ConfigFastest, logger: log, suppressActorStateStoreWarning: atomic.Bool{}, + resourceInfoData: errorcodes.ResourceInfoData{ + ResourceType: "state.redis/v1", + ResourceName: "REDIS", + }, } } @@ -213,6 +220,11 @@ func (r *StateStore) Delete(ctx context.Context, req *state.DeleteRequest) error err = r.client.DoWrite(ctx, "EVAL", delDefaultQuery, 1, req.Key, *req.ETag) } if err != nil { + if errorcodes.FeatureEnabled(req.Metadata) { + errMsg := fmt.Sprintf("state store Delete - possible etag(%s) %s. original error: %v", *req.ETag, string(state.ETagMismatch), err) + return errorcodes.NewStatusError(codes.InvalidArgument, err, errMsg, errorcodes.StateETagMismatchReason, &r.resourceInfoData, nil) + } + return state.NewETagError(state.ETagMismatch, err) } @@ -354,6 +366,11 @@ func (r *StateStore) Set(ctx context.Context, req *state.SetRequest) error { if err != nil { if req.HasETag() { + if errorcodes.FeatureEnabled(req.Metadata) { + errMsg := fmt.Sprintf("state store Set - possible etag(%s) %s. original error: %v", *req.ETag, string(state.ETagMismatch), err) + return errorcodes.NewStatusError(codes.InvalidArgument, fmt.Errorf(errMsg), errMsg, errorcodes.StateETagMismatchReason, &r.resourceInfoData, nil) + } + return state.NewETagError(state.ETagMismatch, err) }