From c183286f6ee8cad0dd70191222e02376c153581a Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Wed, 23 Oct 2024 10:58:02 +0400 Subject: [PATCH] *: Add timeout for SDK waiter operations Closes #246. Signed-off-by: Evgenii Baidakov --- cmd/neofs-rest-gw/config.go | 1 + handlers/api.go | 3 +++ handlers/containers.go | 21 +++++++++++----- handlers/conv.go | 50 +++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 handlers/conv.go diff --git a/cmd/neofs-rest-gw/config.go b/cmd/neofs-rest-gw/config.go index aba4b6e..734f6e8 100644 --- a/cmd/neofs-rest-gw/config.go +++ b/cmd/neofs-rest-gw/config.go @@ -596,6 +596,7 @@ func newNeofsAPI(ctx context.Context, logger *zap.Logger, v *viper.Viper) (*hand } apiPrm.DefaultTimestamp = v.GetBool(cfgPoolDefaultTimestamp) + apiPrm.WaiterOperationTimeout = time.Duration(uint64(ni.MsPerBlock())*4) * time.Millisecond return handlers.NewAPI(&apiPrm) } diff --git a/handlers/api.go b/handlers/api.go index 64c5771..12c534d 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -32,6 +32,7 @@ type PrmAPI struct { PrometheusService *metrics.Service PprofService *metrics.Service ServiceShutdownTimeout time.Duration + WaiterOperationTimeout time.Duration } type BearerToken struct { @@ -75,6 +76,7 @@ func NewAPI(prm *PrmAPI) (*RestAPI, error) { pprofService: prm.PprofService, gateMetric: prm.GateMetric, serviceShutdownTimeout: prm.ServiceShutdownTimeout, + waiterOperationTimeout: prm.WaiterOperationTimeout, }, nil } @@ -141,6 +143,7 @@ type RestAPI struct { prometheusService *metrics.Service pprofService *metrics.Service serviceShutdownTimeout time.Duration + waiterOperationTimeout time.Duration } func (a *RestAPI) StartCallback() { diff --git a/handlers/containers.go b/handlers/containers.go index 53c552d..e74dcd8 100644 --- a/handlers/containers.go +++ b/handlers/containers.go @@ -64,10 +64,13 @@ func (a *RestAPI) PutContainer(ctx echo.Context, params apiserver.PutContainerPa return ctx.JSON(http.StatusBadRequest, resp) } - cnrID, err := createContainer(ctx.Request().Context(), a.pool, stoken, body, params, a.signer) + wCtx, cancel := context.WithTimeout(ctx.Request().Context(), a.waiterOperationTimeout) + defer cancel() + + cnrID, err := createContainer(wCtx, a.pool, stoken, body, params, a.signer) if err != nil { resp := a.logAndGetErrorResponse("create container", err) - return ctx.JSON(http.StatusBadRequest, resp) + return ctx.JSON(getResponseCodeFromStatus(err), resp) } resp := apiserver.PutContainerOK{ @@ -136,9 +139,12 @@ func (a *RestAPI) PutContainerEACL(ctx echo.Context, containerID apiserver.Conta return ctx.JSON(http.StatusBadRequest, resp) } - if err = setContainerEACL(ctx.Request().Context(), a.pool, cnrID, stoken, body, a.signer); err != nil { + wCtx, cancel := context.WithTimeout(ctx.Request().Context(), a.waiterOperationTimeout) + defer cancel() + + if err = setContainerEACL(wCtx, a.pool, cnrID, stoken, body, a.signer); err != nil { resp := a.logAndGetErrorResponse("failed set container eacl", err) - return ctx.JSON(http.StatusBadRequest, resp) + return ctx.JSON(getResponseCodeFromStatus(err), resp) } ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") @@ -255,9 +261,12 @@ func (a *RestAPI) DeleteContainer(ctx echo.Context, containerID apiserver.Contai prm.WithinSession(stoken) wait := waiter.NewContainerDeleteWaiter(a.pool, waiter.DefaultPollInterval) - if err = wait.ContainerDelete(ctx.Request().Context(), cnrID, a.signer, prm); err != nil { + wCtx, cancel := context.WithTimeout(ctx.Request().Context(), a.waiterOperationTimeout) + defer cancel() + + if err = wait.ContainerDelete(wCtx, cnrID, a.signer, prm); err != nil { resp := a.logAndGetErrorResponse("delete container", err, zap.String("container", containerID)) - return ctx.JSON(http.StatusBadRequest, resp) + return ctx.JSON(getResponseCodeFromStatus(err), resp) } ctx.Response().Header().Set(accessControlAllowOriginHeader, "*") diff --git a/handlers/conv.go b/handlers/conv.go new file mode 100644 index 0000000..4590f04 --- /dev/null +++ b/handlers/conv.go @@ -0,0 +1,50 @@ +package handlers + +import ( + "errors" + "net/http" + + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" +) + +func getResponseCodeFromStatus(err error) int { + if err == nil { + return http.StatusOK + } + + switch { + case errors.Is(err, apistatus.ErrEACLNotFound): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrContainerNotFound): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrServerInternal): + return http.StatusInternalServerError + case errors.Is(err, apistatus.ErrSessionTokenNotFound): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrSessionTokenExpired): + return http.StatusInternalServerError + case errors.Is(err, apistatus.ErrObjectLocked): + return http.StatusForbidden + case errors.Is(err, apistatus.ErrObjectAlreadyRemoved): + return http.StatusInternalServerError + case errors.Is(err, apistatus.ErrLockNonRegularObject): + return http.StatusInternalServerError + case errors.Is(err, apistatus.ErrObjectAccessDenied): + return http.StatusForbidden + case errors.Is(err, apistatus.ErrObjectNotFound): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrObjectOutOfRange): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrServerInternal): + return http.StatusInternalServerError + case errors.Is(err, apistatus.ErrWrongMagicNumber): + return http.StatusNotFound + case errors.Is(err, apistatus.ErrSignatureVerification): + return http.StatusForbidden + case errors.Is(err, apistatus.ErrNodeUnderMaintenance): + return http.StatusInternalServerError + + default: + return http.StatusInternalServerError + } +}