From 67a6370178d9e527036eefa08faf49070ba06840 Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Thu, 25 Apr 2024 13:31:47 -0400 Subject: [PATCH] Fix record not found unmarshall. Fixes #1983 --- controller/handler_peer_ctrl/response.go | 8 ++- controller/raft/raft.go | 63 +++++++++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/controller/handler_peer_ctrl/response.go b/controller/handler_peer_ctrl/response.go index 030a19c69a..bc2defdd3e 100644 --- a/controller/handler_peer_ctrl/response.go +++ b/controller/handler_peer_ctrl/response.go @@ -18,12 +18,13 @@ package handler_peer_ctrl import ( "encoding/json" + "fmt" "github.com/hashicorp/raft" "github.com/openziti/channel/v2" + "github.com/openziti/foundation/v2/errorz" + "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/openziti/ziti/controller/models" "github.com/openziti/ziti/controller/peermsg" - "github.com/openziti/ziti/common/pb/cmd_pb" - "github.com/openziti/foundation/v2/errorz" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -52,6 +53,9 @@ func sendApiErrorResponse(m *channel.Message, ch channel.Channel, err *errorz.Ap encodingMap["message"] = err.Message encodingMap["status"] = err.Status encodingMap["cause"] = err.Cause + if err.Cause != nil { + encodingMap["causeType"] = fmt.Sprintf("%T", err.Cause) + } buf, encodeErr := json.Marshal(encodingMap) if encodeErr != nil { diff --git a/controller/raft/raft.go b/controller/raft/raft.go index a818755dcb..0fc7813376 100644 --- a/controller/raft/raft.go +++ b/controller/raft/raft.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "github.com/hashicorp/go-hclog" + "github.com/mitchellh/mapstructure" "github.com/openziti/foundation/v2/concurrenz" "github.com/openziti/foundation/v2/rate" "github.com/openziti/foundation/v2/versions" @@ -209,7 +210,9 @@ func NewController(env Env, migrationMgr MigrationManager) *Controller { migrationMgr: migrationMgr, clusterEvents: make(chan raft.Observation, 16), commandRateLimiter: command.NewRateLimiter(env.GetCommandRateLimiterConfig(), env.GetMetricsRegistry(), env.GetCloseNotify()), + errorMappers: map[string]func(map[string]any) error{}, } + result.initErrorMappers() return result } @@ -230,6 +233,12 @@ type Controller struct { isLeader atomic.Bool clusterEvents chan raft.Observation commandRateLimiter rate.RateLimiter + errorMappers map[string]func(map[string]any) error +} + +func (self *Controller) initErrorMappers() { + self.errorMappers[fmt.Sprintf("%T", &boltz.RecordNotFoundError{})] = self.parseBoltzNotFoundError + self.errorMappers[fmt.Sprintf("%T", &errorz.FieldError{})] = self.parseFieldError } func (self *Controller) RegisterClusterEventHandler(f func(event ClusterEvent, state ClusterState)) { @@ -408,14 +417,16 @@ func (self *Controller) decodeApiError(data []byte) error { if cause, ok := m["cause"]; ok { if strCause, ok := cause.(string); ok { apiErr.Cause = errors.New(strCause) - } else if objCause, ok := cause.(map[string]interface{}); ok { - apiErr.Cause = self.parseFieldError(objCause) + } else if objCause, ok := cause.(map[string]any); ok { + if parser := self.getErrorParser(m); parser != nil { + pfxlog.Logger().Info("parser found for cause type") + apiErr.Cause = parser(objCause) + } else { + pfxlog.Logger().Info("no parser found for cause type") + } + if apiErr.Cause == nil { - if b, err := json.Marshal(objCause); err == nil { - apiErr.Cause = errors.New(string(b)) - } else { - apiErr.Cause = errors.New(fmt.Sprintf("%+v", objCause)) - } + apiErr.Cause = self.fallbackMarshallError(objCause) } } else { pfxlog.Logger().Warnf("invalid api error encoding, no cause: %v", string(data)) @@ -426,7 +437,7 @@ func (self *Controller) decodeApiError(data []byte) error { return apiErr } -func (self *Controller) parseFieldError(m map[string]any) *errorz.FieldError { +func (self *Controller) parseFieldError(m map[string]any) error { var fieldError *errorz.FieldError field, ok := m["field"] if !ok { @@ -456,6 +467,42 @@ func (self *Controller) parseFieldError(m map[string]any) *errorz.FieldError { return fieldError } +func (self *Controller) parseBoltzNotFoundError(m map[string]any) error { + result := &boltz.RecordNotFoundError{} + err := mapstructure.Decode(m, result) + if err != nil { + multi := errorz.MultipleErrors{} + multi = append(multi, fmt.Errorf("unable to decode RecordNotFoundError (%w)", err)) + multi = append(multi, self.fallbackMarshallError(m)) + } + return result +} + +func (self *Controller) fallbackMarshallError(m map[string]any) error { + if b, err := json.Marshal(m); err == nil { + return errors.New(string(b)) + } + return errors.New(fmt.Sprintf("%+v", m)) +} + +func (self *Controller) getErrorParser(m map[string]any) func(map[string]any) error { + causeType, ok := m["causeType"] + if !ok { + pfxlog.Logger().Info("no causetype defined for error parser") + return nil + } + + causeTypeStr, ok := causeType.(string) + if !ok { + pfxlog.Logger().Info("causetype not string") + return nil + } + + pfxlog.Logger().Infof("causetype %s", causeTypeStr) + + return self.errorMappers[causeTypeStr] +} + // applyCommand encodes the command and passes it to ApplyEncodedCommand func (self *Controller) applyCommand(cmd command.Command) (uint64, error) { encoded, err := cmd.Encode()