From 213d20e3fb14cefb4c5db240eced7957489b539f Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 2 Dec 2021 15:21:54 +0300 Subject: [PATCH] [#92] client: Accept structured parameters in non-object operations Define `XPrm` type for each `X` client operation which structures parameters. Export setters of each parameterized value. Emphasize that some parameters are required. Make the client panic when the parameters are incorrectly set. Get rid of vadiadic call options and `CallOption` type. Improve documentation of client behavior. Signed-off-by: Leonard Lyubich --- client/accounting.go | 101 +++--- client/common.go | 176 ++++++++++ client/container.go | 776 ++++++++++++++++++++++++------------------ client/netmap.go | 199 ++++++----- client/object.go | 6 - client/object_test.go | 94 ----- client/opts.go | 2 - client/reputation.go | 205 ++++++----- client/session.go | 102 +++--- pool/mock_test.go | 127 +++---- pool/pool.go | 171 +++++++--- pool/pool_test.go | 22 +- pool/sampler_test.go | 4 +- 13 files changed, 1100 insertions(+), 885 deletions(-) delete mode 100644 client/object_test.go diff --git a/client/accounting.go b/client/accounting.go index 9b85bc7..e9ea673 100644 --- a/client/accounting.go +++ b/client/accounting.go @@ -2,82 +2,99 @@ package client import ( "context" - "fmt" v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" - v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/accounting" "github.com/nspcc-dev/neofs-sdk-go/owner" ) -type BalanceOfRes struct { +// GetBalancePrm groups parameters of GetBalance operation. +type GetBalancePrm struct { + ownerSet bool + ownerID owner.ID +} + +// SetAccount sets identifier of the NeoFS account for which the balance is requested. +// Required parameter. Must be a valid ID according to NeoFS API protocol. +func (x *GetBalancePrm) SetAccount(id owner.ID) { + x.ownerID = id + x.ownerSet = true +} + +// GetBalanceRes groups resulting values of GetBalance operation. +type GetBalanceRes struct { statusRes amount *accounting.Decimal } -func (x *BalanceOfRes) setAmount(v *accounting.Decimal) { +func (x *GetBalanceRes) setAmount(v *accounting.Decimal) { x.amount = v } -func (x BalanceOfRes) Amount() *accounting.Decimal { +// Amount returns current amount of funds on the NeoFS account as decimal number. +// +// Client doesn't retain value so modification is safe. +func (x GetBalanceRes) Amount() *accounting.Decimal { return x.amount } -// GetBalance receives owner balance through NeoFS API call. +// GetBalance requests current balance of the NeoFS account. // // Any client's internal or transport errors are returned as `error`, // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) GetBalance(ctx context.Context, owner *owner.ID, opts ...CallOption) (*BalanceOfRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see GetBalancePrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in GetBalanceRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) GetBalance(ctx context.Context, prm GetBalancePrm) (*GetBalanceRes, error) { + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.ownerSet: + panic("account not set") + case !prm.ownerID.Valid(): + panic("invalid account ID") } - reqBody := new(v2accounting.BalanceRequestBody) - reqBody.SetOwnerID(owner.ToV2()) + // form request body + var body v2accounting.BalanceRequestBody - req := new(v2accounting.BalanceRequest) - req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) + body.SetOwnerID(prm.ownerID.ToV2()) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } + // form request + var req v2accounting.BalanceRequest - resp, err := rpcapi.Balance(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + req.SetBody(&body) + + // init call context var ( - res = new(BalanceOfRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res GetBalanceRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.Balance(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2accounting.BalanceResponse) + res.setAmount(accounting.NewDecimalFromV2(resp.GetBody().GetBalance())) } - res.setAmount(accounting.NewDecimalFromV2(resp.GetBody().GetBalance())) + // process call + if !cc.processCall() { + return nil, cc.err + } - return res, nil + return &res, nil } diff --git a/client/common.go b/client/common.go index aa1d042..7afab98 100644 --- a/client/common.go +++ b/client/common.go @@ -1,10 +1,14 @@ package client import ( + "crypto/ecdsa" "fmt" + v2session "github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-api-go/v2/signature" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + "github.com/nspcc-dev/neofs-sdk-go/session" + "github.com/nspcc-dev/neofs-sdk-go/version" ) // common interface of resulting structures with API status. @@ -87,3 +91,175 @@ func (c *Client) processResponseV2(res *processResponseV2Res, prm processRespons return unsuccessfulStatus } + +type prmSession struct { + tokenSessionSet bool + tokenSession session.Token +} + +// SetSessionToken sets token of the session within which request should be sent. +func (x *prmSession) SetSessionToken(tok session.Token) { + x.tokenSession = tok + x.tokenSessionSet = true +} + +func (x prmSession) writeToMetaHeader(meta *v2session.RequestMetaHeader) { + if x.tokenSessionSet { + meta.SetSessionToken(x.tokenSession.ToV2()) + } +} + +// panic messages. +const ( + panicMsgMissingContext = "missing context" + panicMsgMissingContainer = "missing container" +) + +// groups all the details required to send a single request and process a response to it. +type contextCall struct { + // ================================================== + // state vars that do not require explicit initialization + + // final error to be returned from client method + err error + + // received response + resp responseV2 + + // ================================================== + // shared parameters which are set uniformly on all calls + + // request signing key + key ecdsa.PrivateKey + + // callback prior to processing the response by the client + callbackResp func(ResponseMetaInfo) error + + // if set, protocol errors will be expanded into a final error + resolveAPIFailures bool + + // NeoFS network magic + netMagic uint64 + + // ================================================== + // custom call parameters + + // structure of the call result + statusRes resCommon + + // request to be signed with a key and sent + req interface { + GetMetaHeader() *v2session.RequestMetaHeader + SetMetaHeader(*v2session.RequestMetaHeader) + } + + // function to send a request (unary) and receive a response + call func() (responseV2, error) + + // function of writing response fields to the resulting structure (optional) + result func(v2 responseV2) +} + +func (x contextCall) prepareRequest() { + meta := x.req.GetMetaHeader() + if meta == nil { + meta = new(v2session.RequestMetaHeader) + x.req.SetMetaHeader(meta) + } + + if meta.GetTTL() == 0 { + meta.SetTTL(2) + } + + if meta.GetVersion() == nil { + meta.SetVersion(version.Current().ToV2()) + } + + meta.SetNetworkMagic(x.netMagic) +} + +// performs common actions of response processing and writes any problem as a result status or client error +// (in both cases returns false). +// +// Actions: +// * verify signature (internal); +// * call response callback (internal); +// * unwrap status error (optional). +func (x *contextCall) processResponse() bool { + // call response callback if set + if x.callbackResp != nil { + x.err = x.callbackResp(ResponseMetaInfo{ + key: x.resp.GetVerificationHeader().GetBodySignature().GetKey(), + }) + if x.err != nil { + x.err = fmt.Errorf("response callback error: %w", x.err) + return false + } + } + + // note that we call response callback before signature check since it is expected more lightweight + // while verification needs marshaling + + // verify response signature + x.err = signature.VerifyServiceMessage(x.resp) + if x.err != nil { + x.err = fmt.Errorf("invalid response signature: %w", x.err) + return false + } + + // get result status + st := apistatus.FromStatusV2(x.resp.GetMetaHeader().GetStatus()) + + // unwrap unsuccessful status and return it + // as error if client has been configured so + successfulStatus := apistatus.IsSuccessful(st) + if !successfulStatus && x.resolveAPIFailures { + x.err = apistatus.ErrFromStatus(st) + return false + } + + x.statusRes.setStatus(st) + + return successfulStatus +} + +// goes through all stages of sending a request and processing a response. Returns true if successful. +func (x *contextCall) processCall() bool { + // prepare the request + x.prepareRequest() + + // sign the request + x.err = signature.SignServiceMessage(&x.key, x.req) + if x.err != nil { + x.err = fmt.Errorf("sign request: %w", x.err) + return false + } + + // perform RPC + x.resp, x.err = x.call() + if x.err != nil { + x.err = fmt.Errorf("transport error: %w", x.err) + return false + } + + // process the response + ok := x.processResponse() + if !ok { + return false + } + + // write response to resulting structure + if x.result != nil { + x.result(x.resp) + } + + return true +} + +// initializes static cross-call parameters inherited from client. +func (c *Client) initCallContext(ctx *contextCall) { + ctx.key = *c.opts.key + ctx.resolveAPIFailures = c.opts.parseNeoFSErrors + ctx.callbackResp = c.opts.cbRespInfo + ctx.netMagic = c.opts.netMagic +} diff --git a/client/container.go b/client/container.go index 7ccb4f8..d0909e8 100644 --- a/client/container.go +++ b/client/container.go @@ -2,14 +2,13 @@ package client import ( "context" - "fmt" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/refs" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" + v2session "github.com/nspcc-dev/neofs-api-go/v2/session" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" - apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" @@ -17,44 +16,35 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/signature" sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature" - "github.com/nspcc-dev/neofs-sdk-go/version" ) -type delContainerSignWrapper struct { - body *v2container.DeleteRequestBody +// ContainerPutPrm groups parameters of PutContainer operation. +type ContainerPutPrm struct { + prmSession + + cnrSet bool + cnr container.Container } -// EACLWithSignature represents eACL table/signature pair. -type EACLWithSignature struct { - table *eacl.Table -} - -func (c delContainerSignWrapper) ReadSignedData(bytes []byte) ([]byte, error) { - return c.body.GetContainerID().GetValue(), nil -} - -func (c delContainerSignWrapper) SignedDataSize() int { - return len(c.body.GetContainerID().GetValue()) -} - -// EACL returns eACL table. -func (e EACLWithSignature) EACL() *eacl.Table { - return e.table -} - -// Signature returns table signature. -// -// Deprecated: use EACL().Signature() instead. -func (e EACLWithSignature) Signature() *signature.Signature { - return e.table.Signature() +// SetContainer sets structured information about new NeoFS container. +// Required parameter. +func (x *ContainerPutPrm) SetContainer(cnr container.Container) { + x.cnr = cnr + x.cnrSet = true } +// ContainerPutRes groups resulting values of PutContainer operation. type ContainerPutRes struct { statusRes id *cid.ID } +// ID returns identifier of the container declared to be stored in the system. +// Used as a link to information about the container (in particular, you can +// asynchronously check if the save was successful). +// +// Client doesn't retain value so modification is safe. func (x ContainerPutRes) ID() *cid.ID { return x.id } @@ -63,37 +53,42 @@ func (x *ContainerPutRes) setID(id *cid.ID) { x.id = id } -// PutContainer puts container through NeoFS API call. +// PutContainer sends request to save container in NeoFS. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) PutContainer(ctx context.Context, cnr *container.Container, opts ...CallOption) (*ContainerPutRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Operation is asynchronous and no guaranteed even in the absence of errors. +// The required time is also not predictable. +// +// Success can be verified by reading by identifier (see ContainerPutRes.ID). +// +// Immediately panics if parameters are set incorrectly (see ContainerPutPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in ContainerPutRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) PutContainer(ctx context.Context, prm ContainerPutPrm) (*ContainerPutRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.cnrSet: + panic(panicMsgMissingContainer) } - // set transport version - cnr.SetVersion(version.Current()) - - // if container owner is not set, then use client key as owner - if cnr.OwnerID() == nil { - ownerID := owner.NewIDFromPublicKey(&callOptions.key.PublicKey) - - cnr.SetOwnerID(ownerID) - } + // TODO: check private key is set before forming the request + // form request body reqBody := new(v2container.PutRequestBody) - reqBody.SetContainer(cnr.ToV2()) + reqBody.SetContainer(prm.cnr.ToV2()) // sign container signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetContainer()} - err := sigutil.SignDataWithHandler(callOptions.key, signWrapper, func(key []byte, sig []byte) { + err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(key []byte, sig []byte) { containerSignature := new(refs.Signature) containerSignature.SetKey(key) containerSignature.SetSign(sig) @@ -103,62 +98,66 @@ func (c *Client) PutContainer(ctx context.Context, cnr *container.Container, opt return nil, err } - req := new(v2container.PutRequest) + // form meta header + var meta v2session.RequestMetaHeader + + prm.prmSession.writeToMetaHeader(&meta) + + // form request + var req v2container.PutRequest + req.SetBody(reqBody) + req.SetMetaHeader(&meta) - meta := v2MetaHeaderFromOpts(callOptions) - meta.SetSessionToken(cnr.SessionToken().ToV2()) - - req.SetMetaHeader(meta) - - err = v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.PutContainer(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, err - } + // init call context var ( - res = new(ContainerPutRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res ContainerPutRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.PutContainer(c.Raw(), &req, client.WithContext(ctx)) } - - // sets result status - st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus()) - - res.setStatus(st) - - if apistatus.IsSuccessful(st) { + cc.result = func(r responseV2) { + resp := r.(*v2container.PutResponse) res.setID(cid.NewFromV2(resp.GetBody().GetContainerID())) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } +// ContainerGetPrm groups parameters of GetContainer operation. +type ContainerGetPrm struct { + idSet bool + id cid.ID +} + +// SetContainer sets identifier of the container to be read. +// Required parameter. +func (x *ContainerGetPrm) SetContainer(id cid.ID) { + x.id = id + x.idSet = true +} + +// ContainerGetRes groups resulting values of GetContainer operation. type ContainerGetRes struct { statusRes cnr *container.Container } +// Container returns structured information about the requested container. +// +// Client doesn't retain value so modification is safe. func (x ContainerGetRes) Container() *container.Container { return x.cnr } @@ -167,177 +166,235 @@ func (x *ContainerGetRes) setContainer(cnr *container.Container) { x.cnr = cnr } -// GetContainer receives container structure through NeoFS API call. +// GetContainer reads NeoFS container from by ID. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) GetContainer(ctx context.Context, id *cid.ID, opts ...CallOption) (*ContainerGetRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see ContainerGetPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in ContainerGetRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) GetContainer(ctx context.Context, prm ContainerGetPrm) (*ContainerGetRes, error) { + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.idSet: + panic(panicMsgMissingContainer) } + // form request body reqBody := new(v2container.GetRequestBody) - reqBody.SetContainerID(id.ToV2()) + reqBody.SetContainerID(prm.id.ToV2()) + + // form request + var req v2container.GetRequest - req := new(v2container.GetRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.GetContainer(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(ContainerGetRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res ContainerGetRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.GetContainer(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2container.GetResponse) - procRes.statusRes = res + body := resp.GetBody() - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } + cnr := container.NewContainerFromV2(body.GetContainer()) - return res, nil + cnr.SetSessionToken( + session.NewTokenFromV2(body.GetSessionToken()), + ) + + cnr.SetSignature( + signature.NewFromV2(body.GetSignature()), + ) + + res.setContainer(cnr) } - body := resp.GetBody() + // process call + if !cc.processCall() { + return nil, cc.err + } - cnr := container.NewContainerFromV2(body.GetContainer()) - - cnr.SetSessionToken( - session.NewTokenFromV2(body.GetSessionToken()), - ) - - cnr.SetSignature( - signature.NewFromV2(body.GetSignature()), - ) - - res.setContainer(cnr) - - return res, nil + return &res, nil } +// ContainerListPrm groups parameters of ListContainers operation. +type ContainerListPrm struct { + ownerSet bool + ownerID owner.ID +} + +// SetAccount sets identifier of the NeoFS account to list the containers. +// Required parameter. Must be a valid ID according to NeoFS API protocol. +func (x *ContainerListPrm) SetAccount(id owner.ID) { + x.ownerID = id + x.ownerSet = true +} + +// ContainerListRes groups resulting values of ListContainers operation. type ContainerListRes struct { statusRes ids []*cid.ID } -func (x ContainerListRes) IDList() []*cid.ID { +// Containers returns list of identifiers of the account-owned containers. +// +// Client doesn't retain value so modification is safe. +func (x ContainerListRes) Containers() []*cid.ID { return x.ids } -func (x *ContainerListRes) setIDList(ids []*cid.ID) { +func (x *ContainerListRes) setContainers(ids []*cid.ID) { x.ids = ids } -// ListContainers receives all owner's containers through NeoFS API call. +// ListContainers requests identifiers of the account-owned containers. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) ListContainers(ctx context.Context, ownerID *owner.ID, opts ...CallOption) (*ContainerListRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) - } - - if ownerID == nil { - ownerID = owner.NewIDFromPublicKey(&callOptions.key.PublicKey) +// +// Immediately panics if parameters are set incorrectly (see ContainerListPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in ContainerListRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) ListContainers(ctx context.Context, prm ContainerListPrm) (*ContainerListRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.ownerSet: + panic("account not set") + case !prm.ownerID.Valid(): + panic("invalid account") } + // form request body reqBody := new(v2container.ListRequestBody) - reqBody.SetOwnerID(ownerID.ToV2()) + reqBody.SetOwnerID(prm.ownerID.ToV2()) + + // form request + var req v2container.ListRequest - req := new(v2container.ListRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.ListContainers(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(ContainerListRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res ContainerListRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.ListContainers(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2container.ListResponse) - procRes.statusRes = res + ids := make([]*cid.ID, 0, len(resp.GetBody().GetContainerIDs())) - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr + for _, cidV2 := range resp.GetBody().GetContainerIDs() { + ids = append(ids, cid.NewFromV2(cidV2)) } - return res, nil + res.setContainers(ids) } - ids := make([]*cid.ID, 0, len(resp.GetBody().GetContainerIDs())) - - for _, cidV2 := range resp.GetBody().GetContainerIDs() { - ids = append(ids, cid.NewFromV2(cidV2)) + // process call + if !cc.processCall() { + return nil, cc.err } - res.setIDList(ids) - - return res, nil + return &res, nil } +// ContainerDeletePrm groups parameters of DeleteContainer operation. +type ContainerDeletePrm struct { + prmSession + + idSet bool + id cid.ID +} + +// SetContainer sets identifier of the NeoFS container to be removed. +// Required parameter. +func (x *ContainerDeletePrm) SetContainer(id cid.ID) { + x.id = id + x.idSet = true +} + +// ContainerDeleteRes groups resulting values of DeleteContainer operation. type ContainerDeleteRes struct { statusRes } -// DeleteContainer deletes specified container through NeoFS API call. +// implements github.com/nspcc-dev/neofs-sdk-go/util/signature.DataSource. +type delContainerSignWrapper struct { + body *v2container.DeleteRequestBody +} + +func (c delContainerSignWrapper) ReadSignedData([]byte) ([]byte, error) { + return c.body.GetContainerID().GetValue(), nil +} + +func (c delContainerSignWrapper) SignedDataSize() int { + return len(c.body.GetContainerID().GetValue()) +} + +// DeleteContainer sends request to remove the NeoFS container. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) DeleteContainer(ctx context.Context, id *cid.ID, opts ...CallOption) (*ContainerDeleteRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Operation is asynchronous and no guaranteed even in the absence of errors. +// The required time is also not predictable. +// +// Success can be verified by reading by identifier (see GetContainer). +// +// Immediately panics if parameters are set incorrectly (see ContainerDeletePrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in ContainerDeleteRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) DeleteContainer(ctx context.Context, prm ContainerDeletePrm) (*ContainerDeleteRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.idSet: + panic(panicMsgMissingContainer) } + // form request body reqBody := new(v2container.DeleteRequestBody) - reqBody.SetContainerID(id.ToV2()) + reqBody.SetContainerID(prm.id.ToV2()) // sign container - err := sigutil.SignDataWithHandler(callOptions.key, + err := sigutil.SignDataWithHandler(c.opts.key, delContainerSignWrapper{ body: reqBody, }, @@ -352,149 +409,193 @@ func (c *Client) DeleteContainer(ctx context.Context, id *cid.ID, opts ...CallOp return nil, err } - req := new(v2container.DeleteRequest) + // form meta header + var meta v2session.RequestMetaHeader + + prm.prmSession.writeToMetaHeader(&meta) + + // form request + var req v2container.DeleteRequest + req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) + req.SetMetaHeader(&meta) - err = v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.DeleteContainer(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(ContainerDeleteRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res ContainerDeleteRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.DeleteContainer(c.Raw(), &req, client.WithContext(ctx)) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } +// EACLPrm groups parameters of EACL operation. +type EACLPrm struct { + idSet bool + id cid.ID +} + +// SetContainer sets identifier of the NeoFS container to read the eACL table. +// Required parameter. +func (x *EACLPrm) SetContainer(id cid.ID) { + x.id = id + x.idSet = true +} + +// EACLRes groups resulting values of EACL operation. type EACLRes struct { statusRes table *eacl.Table } +// Table returns eACL table of the requested container. +// +// Client doesn't retain value so modification is safe. func (x EACLRes) Table() *eacl.Table { return x.table } -func (x *EACLRes) SetTable(table *eacl.Table) { +func (x *EACLRes) setTable(table *eacl.Table) { x.table = table } -// EACL receives eACL of the specified container through NeoFS API call. +// EACL reads eACL table of the NeoFS container. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) EACL(ctx context.Context, id *cid.ID, opts ...CallOption) (*EACLRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see EACLPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in EACLRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) EACL(ctx context.Context, prm EACLPrm) (*EACLRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.idSet: + panic(panicMsgMissingContainer) } + // form request body reqBody := new(v2container.GetExtendedACLRequestBody) - reqBody.SetContainerID(id.ToV2()) + reqBody.SetContainerID(prm.id.ToV2()) + + // form request + var req v2container.GetExtendedACLRequest - req := new(v2container.GetExtendedACLRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.GetEACL(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(EACLRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res EACLRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.GetEACL(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2container.GetExtendedACLResponse) - procRes.statusRes = res + body := resp.GetBody() - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } + table := eacl.NewTableFromV2(body.GetEACL()) - return res, nil + table.SetSessionToken( + session.NewTokenFromV2(body.GetSessionToken()), + ) + + table.SetSignature( + signature.NewFromV2(body.GetSignature()), + ) + + res.setTable(table) } - body := resp.GetBody() + // process call + if !cc.processCall() { + return nil, cc.err + } - table := eacl.NewTableFromV2(body.GetEACL()) - - table.SetSessionToken( - session.NewTokenFromV2(body.GetSessionToken()), - ) - - table.SetSignature( - signature.NewFromV2(body.GetSignature()), - ) - - res.SetTable(table) - - return res, nil + return &res, nil } +// SetEACLPrm groups parameters of SetEACL operation. +type SetEACLPrm struct { + prmSession + + tableSet bool + table eacl.Table +} + +// SetTable sets eACL table structure to be set for the container. +// Required parameter. +func (x *SetEACLPrm) SetTable(table eacl.Table) { + x.table = table + x.tableSet = true +} + +// SetEACLRes groups resulting values of SetEACL operation. type SetEACLRes struct { statusRes } -// SetEACL sets eACL through NeoFS API call. +// SetEACL sends request to update eACL table of the NeoFS container. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) SetEACL(ctx context.Context, eacl *eacl.Table, opts ...CallOption) (*SetEACLRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Operation is asynchronous and no guaranteed even in the absence of errors. +// The required time is also not predictable. +// +// Success can be verified by reading by identifier (see EACL). +// +// Immediately panics if parameters are set incorrectly (see SetEACLPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in ContainerDeleteRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) SetEACL(ctx context.Context, prm SetEACLPrm) (*SetEACLRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case !prm.tableSet: + panic("eACL table not set") } + // form request body reqBody := new(v2container.SetExtendedACLRequestBody) - reqBody.SetEACL(eacl.ToV2()) + reqBody.SetEACL(prm.table.ToV2()) + // sign the eACL table signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetEACL()} - err := sigutil.SignDataWithHandler(callOptions.key, signWrapper, func(key []byte, sig []byte) { + err := sigutil.SignDataWithHandler(c.opts.key, signWrapper, func(key []byte, sig []byte) { eaclSignature := new(refs.Signature) eaclSignature.SetKey(key) eaclSignature.SetSign(sig) @@ -504,113 +605,116 @@ func (c *Client) SetEACL(ctx context.Context, eacl *eacl.Table, opts ...CallOpti return nil, err } - req := new(v2container.SetExtendedACLRequest) + // form meta header + var meta v2session.RequestMetaHeader + + prm.prmSession.writeToMetaHeader(&meta) + + // form request + var req v2container.SetExtendedACLRequest + req.SetBody(reqBody) + req.SetMetaHeader(&meta) - meta := v2MetaHeaderFromOpts(callOptions) - meta.SetSessionToken(eacl.SessionToken().ToV2()) - - req.SetMetaHeader(meta) - - err = v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.SetEACL(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(SetEACLRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res SetEACLRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.SetEACL(c.Raw(), &req, client.WithContext(ctx)) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } +// AnnounceSpacePrm groups parameters of AnnounceContainerUsedSpace operation. +type AnnounceSpacePrm struct { + announcements []container.UsedSpaceAnnouncement +} + +// SetValues sets values describing volume of space that is used for the container objects. +// Required parameter. Must not be empty. +// +// Must not be mutated before the end of the operation. +func (x *AnnounceSpacePrm) SetValues(announcements []container.UsedSpaceAnnouncement) { + x.announcements = announcements +} + +// AnnounceSpaceRes groups resulting values of AnnounceContainerUsedSpace operation. type AnnounceSpaceRes struct { statusRes } -// AnnounceContainerUsedSpace used by storage nodes to estimate their container -// sizes during lifetime. Use it only in storage node applications. +// AnnounceContainerUsedSpace sends request to announce volume of the space used for the container objects. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) AnnounceContainerUsedSpace( - ctx context.Context, - announce []container.UsedSpaceAnnouncement, - opts ...CallOption, -) (*AnnounceSpaceRes, error) { - callOptions := c.defaultCallOptions() // apply all available options - - for i := range opts { - opts[i](callOptions) +// +// Operation is asynchronous and no guaranteed even in the absence of errors. +// The required time is also not predictable. +// +// At this moment success can not be checked. +// +// Immediately panics if parameters are set incorrectly (see AnnounceSpacePrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in AnnounceSpaceRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) AnnounceContainerUsedSpace(ctx context.Context, prm AnnounceSpacePrm) (*AnnounceSpaceRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case len(prm.announcements) == 0: + panic("missing announcements") } // convert list of SDK announcement structures into NeoFS-API v2 list - v2announce := make([]*v2container.UsedSpaceAnnouncement, 0, len(announce)) - for i := range announce { - v2announce = append(v2announce, announce[i].ToV2()) + v2announce := make([]*v2container.UsedSpaceAnnouncement, 0, len(prm.announcements)) + for i := range prm.announcements { + v2announce = append(v2announce, prm.announcements[i].ToV2()) } // prepare body of the NeoFS-API v2 request and request itself reqBody := new(v2container.AnnounceUsedSpaceRequestBody) reqBody.SetAnnouncements(v2announce) - req := new(v2container.AnnounceUsedSpaceRequest) + // form request + var req v2container.AnnounceUsedSpaceRequest + req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - // sign the request - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.AnnounceUsedSpace(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(AnnounceSpaceRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res AnnounceSpaceRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.AnnounceUsedSpace(c.Raw(), &req, client.WithContext(ctx)) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } diff --git a/client/netmap.go b/client/netmap.go index 7b7f908..0edfe88 100644 --- a/client/netmap.go +++ b/client/netmap.go @@ -2,116 +2,120 @@ package client import ( "context" - "fmt" v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" - v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/version" ) -// EndpointInfo represents versioned information about the node -// specified in the client. -type EndpointInfo struct { +// EndpointInfoPrm groups parameters of EndpointInfo operation. +// +// At the moment the operation is not parameterized, however, +// the structure is still declared for backward compatibility. +type EndpointInfoPrm struct{} + +// EndpointInfoRes group resulting values of EndpointInfo operation. +type EndpointInfoRes struct { + statusRes + version *version.Version ni *netmap.NodeInfo } -// LatestVersion returns latest NeoFS API version in use. -func (e *EndpointInfo) LatestVersion() *version.Version { - return e.version +// LatestVersion returns latest NeoFS API protocol's version in use. +// +// Client doesn't retain value so modification is safe. +func (x EndpointInfoRes) LatestVersion() *version.Version { + return x.version } -// NodeInfo returns information about the NeoFS node. -func (e *EndpointInfo) NodeInfo() *netmap.NodeInfo { - return e.ni +func (x *EndpointInfoRes) setLatestVersion(ver *version.Version) { + x.version = ver } -type EndpointInfoRes struct { - statusRes - - info *EndpointInfo +// NodeInfo returns information about the NeoFS node served on the remote endpoint. +// +// Client doesn't retain value so modification is safe. +func (x EndpointInfoRes) NodeInfo() *netmap.NodeInfo { + return x.ni } -func (x EndpointInfoRes) Info() *EndpointInfo { - return x.info +func (x *EndpointInfoRes) setNodeInfo(info *netmap.NodeInfo) { + x.ni = info } -func (x *EndpointInfoRes) setInfo(info *EndpointInfo) { - x.info = info -} - -// EndpointInfo returns attributes, address and public key of the node, specified -// in client constructor via address or open connection. This can be used as a -// health check to see if node is alive and responses to requests. +// EndpointInfo requests information about the storage node served on the remote endpoint. +// +// Method can be used as a health check to see if node is alive and responds to requests. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) EndpointInfo(ctx context.Context, opts ...CallOption) (*EndpointInfoRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see EndpointInfoPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in EndpointInfoRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) EndpointInfo(ctx context.Context, _ EndpointInfoPrm) (*EndpointInfoRes, error) { + // check context + if ctx == nil { + panic(panicMsgMissingContext) } - reqBody := new(v2netmap.LocalNodeInfoRequestBody) + // form request + var req v2netmap.LocalNodeInfoRequest - req := new(v2netmap.LocalNodeInfoRequest) - req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.LocalNodeInfo(c.Raw(), req) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(EndpointInfoRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res EndpointInfoRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.LocalNodeInfo(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2netmap.LocalNodeInfoResponse) - procRes.statusRes = res + body := resp.GetBody() - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + res.setLatestVersion(version.NewFromV2(body.GetVersion())) + res.setNodeInfo(netmap.NewNodeInfoFromV2(body.GetNodeInfo())) } - body := resp.GetBody() + // process call + if !cc.processCall() { + return nil, cc.err + } - res.setInfo(&EndpointInfo{ - version: version.NewFromV2(body.GetVersion()), - ni: netmap.NewNodeInfoFromV2(body.GetNodeInfo()), - }) - - return res, nil + return &res, nil } +// NetworkInfoPrm groups parameters of NetworkInfo operation. +// +// At the moment the operation is not parameterized, however, +// the structure is still declared for backward compatibility. +type NetworkInfoPrm struct{} + +// NetworkInfoRes groups resulting values of NetworkInfo operation. type NetworkInfoRes struct { statusRes info *netmap.NetworkInfo } +// Info returns structured information about the NeoFS network. +// +// Client doesn't retain value so modification is safe. func (x NetworkInfoRes) Info() *netmap.NetworkInfo { return x.info } @@ -120,57 +124,50 @@ func (x *NetworkInfoRes) setInfo(info *netmap.NetworkInfo) { x.info = info } -// NetworkInfo returns information about the NeoFS network of which the remote server is a part. +// NetworkInfo requests information about the NeoFS network of which the remote server is a part. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) NetworkInfo(ctx context.Context, opts ...CallOption) (*NetworkInfoRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see NetworkInfoPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in NetworkInfoRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) NetworkInfo(ctx context.Context, _ NetworkInfoPrm) (*NetworkInfoRes, error) { + // check context + if ctx == nil { + panic(panicMsgMissingContext) } - reqBody := new(v2netmap.NetworkInfoRequestBody) + // form request + var req v2netmap.NetworkInfoRequest - req := new(v2netmap.NetworkInfoRequest) - req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.NetworkInfo(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("v2 NetworkInfo RPC failure: %w", err) - } + // init call context var ( - res = new(NetworkInfoRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res NetworkInfoRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.NetworkInfo(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2netmap.NetworkInfoResponse) - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + res.setInfo(netmap.NewNetworkInfoFromV2(resp.GetBody().GetNetworkInfo())) } - res.setInfo(netmap.NewNetworkInfoFromV2(resp.GetBody().GetNetworkInfo())) + // process call + if !cc.processCall() { + return nil, cc.err + } - return res, nil + return &res, nil } diff --git a/client/object.go b/client/object.go index cc4ae22..54ed6fa 100644 --- a/client/object.go +++ b/client/object.go @@ -1461,17 +1461,11 @@ func (c *Client) attachV2SessionToken(opts *callOptions, hdr *v2session.RequestM opCtx.SetAddress(info.addr) opCtx.SetVerb(info.verb) - lt := new(v2session.TokenLifetime) - lt.SetIat(info.iat) - lt.SetNbf(info.nbf) - lt.SetExp(info.exp) - body := new(v2session.SessionTokenBody) body.SetID(opts.session.ID()) body.SetOwnerID(opts.session.OwnerID().ToV2()) body.SetSessionKey(opts.session.SessionKey()) body.SetContext(opCtx) - body.SetLifetime(lt) token := new(v2session.SessionToken) token.SetBody(body) diff --git a/client/object_test.go b/client/object_test.go deleted file mode 100644 index 5666ea9..0000000 --- a/client/object_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package client - -import ( - "io" - "testing" - - "github.com/nspcc-dev/neofs-api-go/v2/object" - "github.com/nspcc-dev/neofs-api-go/v2/signature" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -type singleResponseStream struct { - called bool - resp object.GetResponse -} - -func (x *singleResponseStream) Read(r *object.GetResponse) error { - if x.called { - return io.EOF - } - - x.called = true - - *r = x.resp - - return nil -} - -var key = test.DecodeKey(0) - -func chunkResponse(c []byte) (r object.GetResponse) { - chunkPart := new(object.GetObjectPartChunk) - chunkPart.SetChunk(c) - - body := new(object.GetResponseBody) - body.SetObjectPart(chunkPart) - - r.SetBody(body) - - if err := signature.SignServiceMessage(key, &r); err != nil { - panic(err) - } - - return -} - -func data(sz int) []byte { - data := make([]byte, sz) - - for i := range data { - data[i] = byte(i) % ^byte(0) - } - - return data -} - -func checkFullRead(t *testing.T, r io.Reader, buf, payload []byte) { - var ( - restored []byte - read int - ) - - for { - n, err := r.Read(buf) - - read += n - restored = append(restored, buf[:n]...) - - if err != nil { - require.Equal(t, err, io.EOF) - break - } - } - - require.Equal(t, payload, restored) - require.EqualValues(t, len(payload), read) -} - -func TestObjectPayloadReader_Read(t *testing.T) { - t.Run("read with tail", func(t *testing.T) { - payload := data(10) - - buf := make([]byte, len(payload)-1) - - var r io.Reader = &objectPayloadReader{ - stream: &singleResponseStream{ - resp: chunkResponse(payload), - }, - } - - checkFullRead(t, r, buf, payload) - }) -} diff --git a/client/opts.go b/client/opts.go index 2afd17f..a6bc25d 100644 --- a/client/opts.go +++ b/client/opts.go @@ -51,8 +51,6 @@ type ( v2SessionReqInfo struct { addr *refs.Address verb v2session.ObjectSessionVerb - - exp, nbf, iat uint64 } ) diff --git a/client/reputation.go b/client/reputation.go index d6b8f9d..1c53a0a 100644 --- a/client/reputation.go +++ b/client/reputation.go @@ -6,7 +6,6 @@ import ( v2reputation "github.com/nspcc-dev/neofs-api-go/v2/reputation" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" - v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/reputation" ) @@ -14,26 +13,20 @@ import ( type AnnounceLocalTrustPrm struct { epoch uint64 - trusts []*reputation.Trust + trusts []reputation.Trust } -// Epoch returns epoch in which the trust was assessed. -func (x AnnounceLocalTrustPrm) Epoch() uint64 { - return x.epoch -} - -// SetEpoch sets epoch in which the trust was assessed. +// SetEpoch sets number of NeoFS epoch in which the trust was assessed. +// Required parameter, must not be zero. func (x *AnnounceLocalTrustPrm) SetEpoch(epoch uint64) { x.epoch = epoch } -// Trusts returns list of local trust values. -func (x AnnounceLocalTrustPrm) Trusts() []*reputation.Trust { - return x.trusts -} - -// SetTrusts sets list of local trust values. -func (x *AnnounceLocalTrustPrm) SetTrusts(trusts []*reputation.Trust) { +// SetValues sets values describing trust of the client to the NeoFS network participants. +// Required parameter. Must not be empty. +// +// Must not be mutated before the end of the operation. +func (x *AnnounceLocalTrustPrm) SetValues(trusts []reputation.Trust) { x.trusts = trusts } @@ -42,59 +35,66 @@ type AnnounceLocalTrustRes struct { statusRes } -// AnnounceLocalTrust announces node's local trusts through NeoFS API call. +// AnnounceLocalTrust sends client's trust values to the NeoFS network participants. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) AnnounceLocalTrust(ctx context.Context, prm AnnounceLocalTrustPrm, opts ...CallOption) (*AnnounceLocalTrustRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see AnnounceLocalTrustPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in AnnounceLocalTrustRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) AnnounceLocalTrust(ctx context.Context, prm AnnounceLocalTrustPrm) (*AnnounceLocalTrustRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case prm.epoch == 0: + panic("zero epoch") + case len(prm.trusts) == 0: + panic("missing trusts") } + // form request body reqBody := new(v2reputation.AnnounceLocalTrustRequestBody) - reqBody.SetEpoch(prm.Epoch()) - reqBody.SetTrusts(reputation.TrustsToV2(prm.Trusts())) + reqBody.SetEpoch(prm.epoch) + + trusts := make([]*reputation.Trust, 0, len(prm.trusts)) + + for i := range prm.trusts { + trusts = append(trusts, &prm.trusts[i]) + } + + reqBody.SetTrusts(reputation.TrustsToV2(trusts)) + + // form request + var req v2reputation.AnnounceLocalTrustRequest - req := new(v2reputation.AnnounceLocalTrustRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.AnnounceLocalTrust(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, err - } + // init call context var ( - res = new(AnnounceLocalTrustRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res AnnounceLocalTrustRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.AnnounceLocalTrust(c.Raw(), &req, client.WithContext(ctx)) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } // AnnounceIntermediateTrustPrm groups parameters of AnnounceIntermediateTrust operation. @@ -103,35 +103,27 @@ type AnnounceIntermediateTrustPrm struct { iter uint32 - trust *reputation.PeerToPeerTrust -} - -func (x *AnnounceIntermediateTrustPrm) Epoch() uint64 { - return x.epoch + trustSet bool + trust reputation.PeerToPeerTrust } +// SetEpoch sets number of NeoFS epoch with which client's calculation algorithm is initialized. +// Required parameter, must not be zero. func (x *AnnounceIntermediateTrustPrm) SetEpoch(epoch uint64) { x.epoch = epoch } -// Iteration returns sequence number of the iteration. -func (x AnnounceIntermediateTrustPrm) Iteration() uint32 { - return x.iter -} - -// SetIteration sets sequence number of the iteration. +// SetIteration sets current sequence number of the client's calculation algorithm. +// By default, corresponds to initial (zero) iteration. func (x *AnnounceIntermediateTrustPrm) SetIteration(iter uint32) { x.iter = iter } -// Trust returns current global trust value computed at the specified iteration. -func (x AnnounceIntermediateTrustPrm) Trust() *reputation.PeerToPeerTrust { - return x.trust -} - -// SetTrust sets current global trust value computed at the specified iteration. -func (x *AnnounceIntermediateTrustPrm) SetTrust(trust *reputation.PeerToPeerTrust) { +// SetCurrentValue sets current global trust value computed at the specified iteration +//of the client's calculation algorithm. Required parameter. +func (x *AnnounceIntermediateTrustPrm) SetCurrentValue(trust reputation.PeerToPeerTrust) { x.trust = trust + x.trustSet = true } // AnnounceIntermediateTrustRes groups results of AnnounceIntermediateTrust operation. @@ -139,58 +131,59 @@ type AnnounceIntermediateTrustRes struct { statusRes } -// AnnounceIntermediateTrust announces node's intermediate trusts through NeoFS API call. +// AnnounceIntermediateTrust sends global trust values calculated for the specified NeoFS network participants +// at some stage of client's calculation algorithm. // // Any client's internal or transport errors are returned as `error`. // If WithNeoFSErrorParsing option has been provided, unsuccessful // NeoFS status codes are returned as `error`, otherwise, are included // in the returned result structure. -func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm AnnounceIntermediateTrustPrm, opts ...CallOption) (*AnnounceIntermediateTrustRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// +// Immediately panics if parameters are set incorrectly (see AnnounceIntermediateTrustPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in AnnounceIntermediateTrustRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm AnnounceIntermediateTrustPrm) (*AnnounceIntermediateTrustRes, error) { + // check parameters + switch { + case ctx == nil: + panic(panicMsgMissingContext) + case prm.epoch == 0: + panic("zero epoch") + case !prm.trustSet: + panic("current trust value not set") } + // form request body reqBody := new(v2reputation.AnnounceIntermediateResultRequestBody) - reqBody.SetEpoch(prm.Epoch()) - reqBody.SetIteration(prm.Iteration()) - reqBody.SetTrust(prm.Trust().ToV2()) + reqBody.SetEpoch(prm.epoch) + reqBody.SetIteration(prm.iter) + reqBody.SetTrust(prm.trust.ToV2()) + + // form request + var req v2reputation.AnnounceIntermediateResultRequest - req := new(v2reputation.AnnounceIntermediateResultRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.AnnounceIntermediateResult(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, err - } + // init call context var ( - res = new(AnnounceIntermediateTrustRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res AnnounceIntermediateTrustRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp - - procRes.statusRes = res - - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.AnnounceIntermediateResult(c.Raw(), &req, client.WithContext(ctx)) } - return res, nil + // process call + if !cc.processCall() { + return nil, cc.err + } + + return &res, nil } diff --git a/client/session.go b/client/session.go index c7d7131..65d0051 100644 --- a/client/session.go +++ b/client/session.go @@ -2,18 +2,24 @@ package client import ( "context" - "errors" - "fmt" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" - v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/owner" ) -var errMalformedResponseBody = errors.New("malformed response body") +// CreateSessionPrm groups parameters of CreateSession operation. +type CreateSessionPrm struct { + exp uint64 +} +// SetExp sets number of the last NepFS epoch in the lifetime of the session after which it will be expired. +func (x *CreateSessionPrm) SetExp(exp uint64) { + x.exp = exp +} + +// CreateSessionRes groups resulting values of CreateSession operation. type CreateSessionRes struct { statusRes @@ -26,6 +32,9 @@ func (x *CreateSessionRes) setID(id []byte) { x.id = id } +// ID returns identifier of the opened session in a binary NeoFS API protocol format. +// +// Client doesn't retain value so modification is safe. func (x CreateSessionRes) ID() []byte { return x.id } @@ -34,66 +43,69 @@ func (x *CreateSessionRes) setSessionKey(key []byte) { x.sessionKey = key } -func (x CreateSessionRes) SessionKey() []byte { +// PublicKey returns public key of the opened session in a binary NeoFS API protocol format. +func (x CreateSessionRes) PublicKey() []byte { return x.sessionKey } -// CreateSession creates session through NeoFS API call. +// CreateSession opens a session with the node server on the remote endpoint. +// The session lifetime coincides with the server lifetime. Results can be written +// to session token which can be later attached to the requests. // -// Any client's internal or transport errors are returned as error, -// NeoFS status codes are included in the returned results. -func (c *Client) CreateSession(ctx context.Context, expiration uint64, opts ...CallOption) (*CreateSessionRes, error) { - // apply all available options - callOptions := c.defaultCallOptions() - - for i := range opts { - opts[i](callOptions) +// Any client's internal or transport errors are returned as `error`. +// If WithNeoFSErrorParsing option has been provided, unsuccessful +// NeoFS status codes are returned as `error`, otherwise, are included +// in the returned result structure. +// +// Immediately panics if parameters are set incorrectly (see CreateSessionPrm docs). +// Context is required and must not be nil. It is used for network communication. +// +// Exactly one return value is non-nil. Server status return is returned in CreateSessionRes. +// Reflects all internal errors in second return value (transport problems, response processing, etc.). +func (c *Client) CreateSession(ctx context.Context, prm CreateSessionPrm) (*CreateSessionRes, error) { + // check context + if ctx == nil { + panic(panicMsgMissingContext) } - ownerID := owner.NewIDFromPublicKey(&callOptions.key.PublicKey) + ownerID := owner.NewIDFromPublicKey(&c.opts.key.PublicKey) + // form request body reqBody := new(v2session.CreateRequestBody) reqBody.SetOwnerID(ownerID.ToV2()) - reqBody.SetExpiration(expiration) + reqBody.SetExpiration(prm.exp) + + // for request + var req v2session.CreateRequest - req := new(v2session.CreateRequest) req.SetBody(reqBody) - req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err := v2signature.SignServiceMessage(callOptions.key, req) - if err != nil { - return nil, err - } - - resp, err := rpcapi.CreateSession(c.Raw(), req, client.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("transport error: %w", err) - } + // init call context var ( - res = new(CreateSessionRes) - procPrm processResponseV2Prm - procRes processResponseV2Res + cc contextCall + res CreateSessionRes ) - procPrm.callOpts = callOptions - procPrm.resp = resp + c.initCallContext(&cc) + cc.req = &req + cc.statusRes = &res + cc.call = func() (responseV2, error) { + return rpcapi.CreateSession(c.Raw(), &req, client.WithContext(ctx)) + } + cc.result = func(r responseV2) { + resp := r.(*v2session.CreateResponse) - procRes.statusRes = res + body := resp.GetBody() - // process response in general - if c.processResponseV2(&procRes, procPrm) { - if procRes.cliErr != nil { - return nil, procRes.cliErr - } - - return res, nil + res.setID(body.GetID()) + res.setSessionKey(body.GetSessionKey()) } - body := resp.GetBody() + // process call + if !cc.processCall() { + return nil, cc.err + } - res.setID(body.GetID()) - res.setSessionKey(body.GetSessionKey()) - - return res, nil + return &res, nil } diff --git a/pool/mock_test.go b/pool/mock_test.go index ae42ece..7918531 100644 --- a/pool/mock_test.go +++ b/pool/mock_test.go @@ -12,10 +12,6 @@ import ( gomock "github.com/golang/mock/gomock" client "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" client0 "github.com/nspcc-dev/neofs-sdk-go/client" - container "github.com/nspcc-dev/neofs-sdk-go/container" - cid "github.com/nspcc-dev/neofs-sdk-go/container/id" - eacl "github.com/nspcc-dev/neofs-sdk-go/eacl" - owner "github.com/nspcc-dev/neofs-sdk-go/owner" ) // MockClient is a mock of Client interface. @@ -42,12 +38,9 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { } // AnnounceContainerUsedSpace mocks base method. -func (m *MockClient) AnnounceContainerUsedSpace(arg0 context.Context, arg1 []container.UsedSpaceAnnouncement, arg2 ...client0.CallOption) (*client0.AnnounceSpaceRes, error) { +func (m *MockClient) AnnounceContainerUsedSpace(arg0 context.Context, arg1 client0.AnnounceSpacePrm) (*client0.AnnounceSpaceRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "AnnounceContainerUsedSpace", varargs...) ret0, _ := ret[0].(*client0.AnnounceSpaceRes) ret1, _ := ret[1].(error) @@ -55,19 +48,16 @@ func (m *MockClient) AnnounceContainerUsedSpace(arg0 context.Context, arg1 []con } // AnnounceContainerUsedSpace indicates an expected call of AnnounceContainerUsedSpace. -func (mr *MockClientMockRecorder) AnnounceContainerUsedSpace(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) AnnounceContainerUsedSpace(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceContainerUsedSpace", reflect.TypeOf((*MockClient)(nil).AnnounceContainerUsedSpace), varargs...) } // AnnounceIntermediateTrust mocks base method. -func (m *MockClient) AnnounceIntermediateTrust(arg0 context.Context, arg1 client0.AnnounceIntermediateTrustPrm, arg2 ...client0.CallOption) (*client0.AnnounceIntermediateTrustRes, error) { +func (m *MockClient) AnnounceIntermediateTrust(arg0 context.Context, arg1 client0.AnnounceIntermediateTrustPrm) (*client0.AnnounceIntermediateTrustRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "AnnounceIntermediateTrust", varargs...) ret0, _ := ret[0].(*client0.AnnounceIntermediateTrustRes) ret1, _ := ret[1].(error) @@ -75,19 +65,16 @@ func (m *MockClient) AnnounceIntermediateTrust(arg0 context.Context, arg1 client } // AnnounceIntermediateTrust indicates an expected call of AnnounceIntermediateTrust. -func (mr *MockClientMockRecorder) AnnounceIntermediateTrust(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) AnnounceIntermediateTrust(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceIntermediateTrust", reflect.TypeOf((*MockClient)(nil).AnnounceIntermediateTrust), varargs...) } // AnnounceLocalTrust mocks base method. -func (m *MockClient) AnnounceLocalTrust(arg0 context.Context, arg1 client0.AnnounceLocalTrustPrm, arg2 ...client0.CallOption) (*client0.AnnounceLocalTrustRes, error) { +func (m *MockClient) AnnounceLocalTrust(arg0 context.Context, arg1 client0.AnnounceLocalTrustPrm) (*client0.AnnounceLocalTrustRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "AnnounceLocalTrust", varargs...) ret0, _ := ret[0].(*client0.AnnounceLocalTrustRes) ret1, _ := ret[1].(error) @@ -95,9 +82,9 @@ func (m *MockClient) AnnounceLocalTrust(arg0 context.Context, arg1 client0.Annou } // AnnounceLocalTrust indicates an expected call of AnnounceLocalTrust. -func (mr *MockClientMockRecorder) AnnounceLocalTrust(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) AnnounceLocalTrust(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnnounceLocalTrust", reflect.TypeOf((*MockClient)(nil).AnnounceLocalTrust), varargs...) } @@ -116,12 +103,9 @@ func (mr *MockClientMockRecorder) Conn() *gomock.Call { } // CreateSession mocks base method. -func (m *MockClient) CreateSession(arg0 context.Context, arg1 uint64, arg2 ...client0.CallOption) (*client0.CreateSessionRes, error) { +func (m *MockClient) CreateSession(arg0 context.Context, arg1 client0.CreateSessionPrm) (*client0.CreateSessionRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "CreateSession", varargs...) ret0, _ := ret[0].(*client0.CreateSessionRes) ret1, _ := ret[1].(error) @@ -129,19 +113,16 @@ func (m *MockClient) CreateSession(arg0 context.Context, arg1 uint64, arg2 ...cl } // CreateSession indicates an expected call of CreateSession. -func (mr *MockClientMockRecorder) CreateSession(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) CreateSession(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockClient)(nil).CreateSession), varargs...) } // DeleteContainer mocks base method. -func (m *MockClient) DeleteContainer(arg0 context.Context, arg1 *cid.ID, arg2 ...client0.CallOption) (*client0.ContainerDeleteRes, error) { +func (m *MockClient) DeleteContainer(arg0 context.Context, arg1 client0.ContainerDeletePrm) (*client0.ContainerDeleteRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "DeleteContainer", varargs...) ret0, _ := ret[0].(*client0.ContainerDeleteRes) ret1, _ := ret[1].(error) @@ -149,9 +130,9 @@ func (m *MockClient) DeleteContainer(arg0 context.Context, arg1 *cid.ID, arg2 .. } // DeleteContainer indicates an expected call of DeleteContainer. -func (mr *MockClientMockRecorder) DeleteContainer(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) DeleteContainer(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteContainer", reflect.TypeOf((*MockClient)(nil).DeleteContainer), varargs...) } @@ -176,12 +157,9 @@ func (mr *MockClientMockRecorder) DeleteObject(arg0, arg1 interface{}, arg2 ...i } // EACL mocks base method. -func (m *MockClient) EACL(arg0 context.Context, arg1 *cid.ID, arg2 ...client0.CallOption) (*client0.EACLRes, error) { +func (m *MockClient) EACL(arg0 context.Context, arg1 client0.EACLPrm) (*client0.EACLRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "EACL", varargs...) ret0, _ := ret[0].(*client0.EACLRes) ret1, _ := ret[1].(error) @@ -189,19 +167,16 @@ func (m *MockClient) EACL(arg0 context.Context, arg1 *cid.ID, arg2 ...client0.Ca } // EACL indicates an expected call of EACL. -func (mr *MockClientMockRecorder) EACL(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) EACL(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EACL", reflect.TypeOf((*MockClient)(nil).EACL), varargs...) } // EndpointInfo mocks base method. -func (m *MockClient) EndpointInfo(arg0 context.Context, arg1 ...client0.CallOption) (*client0.EndpointInfoRes, error) { +func (m *MockClient) EndpointInfo(arg0 context.Context, arg1 client0.EndpointInfoPrm) (*client0.EndpointInfoRes, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } + varargs := []interface{}{arg0, arg1} ret := m.ctrl.Call(m, "EndpointInfo", varargs...) ret0, _ := ret[0].(*client0.EndpointInfoRes) ret1, _ := ret[1].(error) @@ -209,39 +184,33 @@ func (m *MockClient) EndpointInfo(arg0 context.Context, arg1 ...client0.CallOpti } // EndpointInfo indicates an expected call of EndpointInfo. -func (mr *MockClientMockRecorder) EndpointInfo(arg0 interface{}, arg1 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) EndpointInfo(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EndpointInfo", reflect.TypeOf((*MockClient)(nil).EndpointInfo), varargs...) } // GetBalance mocks base method. -func (m *MockClient) GetBalance(arg0 context.Context, arg1 *owner.ID, arg2 ...client0.CallOption) (*client0.BalanceOfRes, error) { +func (m *MockClient) GetBalance(arg0 context.Context, arg1 client0.GetBalancePrm) (*client0.GetBalanceRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "GetBalance", varargs...) - ret0, _ := ret[0].(*client0.BalanceOfRes) + ret0, _ := ret[0].(*client0.GetBalanceRes) ret1, _ := ret[1].(error) return ret0, ret1 } // GetBalance indicates an expected call of GetBalance. -func (mr *MockClientMockRecorder) GetBalance(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) GetBalance(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockClient)(nil).GetBalance), varargs...) } // GetContainer mocks base method. -func (m *MockClient) GetContainer(arg0 context.Context, arg1 *cid.ID, arg2 ...client0.CallOption) (*client0.ContainerGetRes, error) { +func (m *MockClient) GetContainer(arg0 context.Context, arg1 client0.ContainerGetPrm) (*client0.ContainerGetRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "GetContainer", varargs...) ret0, _ := ret[0].(*client0.ContainerGetRes) ret1, _ := ret[1].(error) @@ -249,9 +218,9 @@ func (m *MockClient) GetContainer(arg0 context.Context, arg1 *cid.ID, arg2 ...cl } // GetContainer indicates an expected call of GetContainer. -func (mr *MockClientMockRecorder) GetContainer(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) GetContainer(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContainer", reflect.TypeOf((*MockClient)(nil).GetContainer), varargs...) } @@ -316,12 +285,9 @@ func (mr *MockClientMockRecorder) HeadObject(arg0, arg1 interface{}, arg2 ...int } // ListContainers mocks base method. -func (m *MockClient) ListContainers(arg0 context.Context, arg1 *owner.ID, arg2 ...client0.CallOption) (*client0.ContainerListRes, error) { +func (m *MockClient) ListContainers(arg0 context.Context, arg1 client0.ContainerListPrm) (*client0.ContainerListRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "ListContainers", varargs...) ret0, _ := ret[0].(*client0.ContainerListRes) ret1, _ := ret[1].(error) @@ -329,19 +295,16 @@ func (m *MockClient) ListContainers(arg0 context.Context, arg1 *owner.ID, arg2 . } // ListContainers indicates an expected call of ListContainers. -func (mr *MockClientMockRecorder) ListContainers(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) ListContainers(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListContainers", reflect.TypeOf((*MockClient)(nil).ListContainers), varargs...) } // NetworkInfo mocks base method. -func (m *MockClient) NetworkInfo(arg0 context.Context, arg1 ...client0.CallOption) (*client0.NetworkInfoRes, error) { +func (m *MockClient) NetworkInfo(arg0 context.Context, arg1 client0.NetworkInfoPrm) (*client0.NetworkInfoRes, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } + varargs := []interface{}{arg0, arg1} ret := m.ctrl.Call(m, "NetworkInfo", varargs...) ret0, _ := ret[0].(*client0.NetworkInfoRes) ret1, _ := ret[1].(error) @@ -349,9 +312,9 @@ func (m *MockClient) NetworkInfo(arg0 context.Context, arg1 ...client0.CallOptio } // NetworkInfo indicates an expected call of NetworkInfo. -func (mr *MockClientMockRecorder) NetworkInfo(arg0 interface{}, arg1 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) NetworkInfo(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkInfo", reflect.TypeOf((*MockClient)(nil).NetworkInfo), varargs...) } @@ -376,12 +339,9 @@ func (mr *MockClientMockRecorder) ObjectPayloadRangeData(arg0, arg1 interface{}, } // PutContainer mocks base method. -func (m *MockClient) PutContainer(arg0 context.Context, arg1 *container.Container, arg2 ...client0.CallOption) (*client0.ContainerPutRes, error) { +func (m *MockClient) PutContainer(arg0 context.Context, arg1 client0.ContainerPutPrm) (*client0.ContainerPutRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "PutContainer", varargs...) ret0, _ := ret[0].(*client0.ContainerPutRes) ret1, _ := ret[1].(error) @@ -389,9 +349,9 @@ func (m *MockClient) PutContainer(arg0 context.Context, arg1 *container.Containe } // PutContainer indicates an expected call of PutContainer. -func (mr *MockClientMockRecorder) PutContainer(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) PutContainer(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutContainer", reflect.TypeOf((*MockClient)(nil).PutContainer), varargs...) } @@ -450,12 +410,9 @@ func (mr *MockClientMockRecorder) SearchObjects(arg0, arg1 interface{}, arg2 ... } // SetEACL mocks base method. -func (m *MockClient) SetEACL(arg0 context.Context, arg1 *eacl.Table, arg2 ...client0.CallOption) (*client0.SetEACLRes, error) { +func (m *MockClient) SetEACL(arg0 context.Context, arg1 client0.SetEACLPrm) (*client0.SetEACLRes, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } ret := m.ctrl.Call(m, "SetEACL", varargs...) ret0, _ := ret[0].(*client0.SetEACLRes) ret1, _ := ret[1].(error) @@ -463,8 +420,8 @@ func (m *MockClient) SetEACL(arg0 context.Context, arg1 *eacl.Table, arg2 ...cli } // SetEACL indicates an expected call of SetEACL. -func (mr *MockClientMockRecorder) SetEACL(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) SetEACL(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := []interface{}{arg0, arg1} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEACL", reflect.TypeOf((*MockClient)(nil).SetEACL), varargs...) } diff --git a/pool/pool.go b/pool/pool.go index 52de9b3..b0af1d4 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -30,16 +30,16 @@ import ( // Client is a wrapper for client.Client to generate mock. type Client interface { - GetBalance(context.Context, *owner.ID, ...client.CallOption) (*client.BalanceOfRes, error) - PutContainer(context.Context, *container.Container, ...client.CallOption) (*client.ContainerPutRes, error) - GetContainer(context.Context, *cid.ID, ...client.CallOption) (*client.ContainerGetRes, error) - ListContainers(context.Context, *owner.ID, ...client.CallOption) (*client.ContainerListRes, error) - DeleteContainer(context.Context, *cid.ID, ...client.CallOption) (*client.ContainerDeleteRes, error) - EACL(context.Context, *cid.ID, ...client.CallOption) (*client.EACLRes, error) - SetEACL(context.Context, *eacl.Table, ...client.CallOption) (*client.SetEACLRes, error) - AnnounceContainerUsedSpace(context.Context, []container.UsedSpaceAnnouncement, ...client.CallOption) (*client.AnnounceSpaceRes, error) - EndpointInfo(context.Context, ...client.CallOption) (*client.EndpointInfoRes, error) - NetworkInfo(context.Context, ...client.CallOption) (*client.NetworkInfoRes, error) + GetBalance(context.Context, client.GetBalancePrm) (*client.GetBalanceRes, error) + PutContainer(context.Context, client.ContainerPutPrm) (*client.ContainerPutRes, error) + GetContainer(context.Context, client.ContainerGetPrm) (*client.ContainerGetRes, error) + ListContainers(context.Context, client.ContainerListPrm) (*client.ContainerListRes, error) + DeleteContainer(context.Context, client.ContainerDeletePrm) (*client.ContainerDeleteRes, error) + EACL(context.Context, client.EACLPrm) (*client.EACLRes, error) + SetEACL(context.Context, client.SetEACLPrm) (*client.SetEACLRes, error) + AnnounceContainerUsedSpace(context.Context, client.AnnounceSpacePrm) (*client.AnnounceSpaceRes, error) + EndpointInfo(context.Context, client.EndpointInfoPrm) (*client.EndpointInfoRes, error) + NetworkInfo(context.Context, client.NetworkInfoPrm) (*client.NetworkInfoRes, error) PutObject(context.Context, *client.PutObjectParams, ...client.CallOption) (*client.ObjectPutRes, error) DeleteObject(context.Context, *client.DeleteObjectParams, ...client.CallOption) (*client.ObjectDeleteRes, error) GetObject(context.Context, *client.GetObjectParams, ...client.CallOption) (*client.ObjectGetRes, error) @@ -47,9 +47,9 @@ type Client interface { ObjectPayloadRangeData(context.Context, *client.RangeDataParams, ...client.CallOption) (*client.ObjectRangeRes, error) HashObjectPayloadRanges(context.Context, *client.RangeChecksumParams, ...client.CallOption) (*client.ObjectRangeHashRes, error) SearchObjects(context.Context, *client.SearchObjectParams, ...client.CallOption) (*client.ObjectSearchRes, error) - AnnounceLocalTrust(context.Context, client.AnnounceLocalTrustPrm, ...client.CallOption) (*client.AnnounceLocalTrustRes, error) - AnnounceIntermediateTrust(context.Context, client.AnnounceIntermediateTrustPrm, ...client.CallOption) (*client.AnnounceIntermediateTrustRes, error) - CreateSession(context.Context, uint64, ...client.CallOption) (*client.CreateSessionRes, error) + AnnounceLocalTrust(context.Context, client.AnnounceLocalTrustPrm) (*client.AnnounceLocalTrustRes, error) + AnnounceIntermediateTrust(context.Context, client.AnnounceIntermediateTrustPrm) (*client.AnnounceIntermediateTrustRes, error) + CreateSession(context.Context, client.CreateSessionPrm) (*client.CreateSessionRes, error) Raw() *apiclient.Client @@ -174,7 +174,6 @@ type Container interface { DeleteContainer(ctx context.Context, cid *cid.ID, opts ...CallOption) error GetEACL(ctx context.Context, cid *cid.ID, opts ...CallOption) (*eacl.Table, error) SetEACL(ctx context.Context, table *eacl.Table, opts ...CallOption) error - AnnounceContainerUsedSpace(ctx context.Context, announce []container.UsedSpaceAnnouncement, opts ...CallOption) error } type Accounting interface { @@ -263,6 +262,11 @@ func newPool(ctx context.Context, options *BuilderOptions) (Pool, error) { inner := make([]*innerPool, len(options.nodesParams)) var atLeastOneHealthy bool + + var cliPrm client.CreateSessionPrm + + cliPrm.SetExp(options.SessionExpirationEpoch) + for i, params := range options.nodesParams { clientPacks := make([]*clientPack, len(params.weights)) for j, address := range params.addresses { @@ -274,7 +278,7 @@ func newPool(ctx context.Context, options *BuilderOptions) (Pool, error) { return nil, err } var healthy bool - cliRes, err := c.CreateSession(ctx, options.SessionExpirationEpoch) + cliRes, err := c.CreateSession(ctx, cliPrm) if err != nil && options.Logger != nil { options.Logger.Warn("failed to create neofs session token for client", zap.String("address", address), @@ -353,14 +357,23 @@ func updateInnerNodesHealth(ctx context.Context, pool *pool, i int, options *Bui healthyChanged := false wg := sync.WaitGroup{} + + var ( + prmEndpoint client.EndpointInfoPrm + prmSession client.CreateSessionPrm + ) + + prmSession.SetExp(options.SessionExpirationEpoch) + for j, cPack := range p.clientPacks { wg.Add(1) - go func(j int, client Client) { + go func(j int, cli Client) { defer wg.Done() ok := true tctx, c := context.WithTimeout(ctx, options.NodeRequestTimeout) defer c() - if _, err := client.EndpointInfo(tctx); err != nil { + + if _, err := cli.EndpointInfo(tctx, prmEndpoint); err != nil { ok = false bufferWeights[j] = 0 } @@ -371,7 +384,7 @@ func updateInnerNodesHealth(ctx context.Context, pool *pool, i int, options *Bui if ok { bufferWeights[j] = options.nodesParams[i].weights[j] if !cp.healthy { - if cliRes, err := client.CreateSession(ctx, options.SessionExpirationEpoch); err != nil { + if cliRes, err := cli.CreateSession(ctx, prmSession); err != nil { ok = false bufferWeights[j] = 0 } else { @@ -488,7 +501,11 @@ func (p *pool) conn(ctx context.Context, cfg *callConfig) (*clientPack, []client cacheKey := formCacheKey(cp.address, key) sessionToken = p.cache.Get(cacheKey) if sessionToken == nil { - cliRes, err := cp.client.CreateSession(ctx, math.MaxUint32, clientCallOptions...) + var cliPrm client.CreateSessionPrm + + cliPrm.SetExp(math.MaxUint32) + + cliRes, err := cp.client.CreateSession(ctx, cliPrm) if err != nil { return nil, nil, err } @@ -496,6 +513,8 @@ func (p *pool) conn(ctx context.Context, cfg *callConfig) (*clientPack, []client ownerID := owner.NewIDFromPublicKey(&key.PublicKey) sessionToken = sessionTokenForOwner(ownerID, cliRes) + cfg.stoken = sessionToken + _ = p.cache.Put(cacheKey, sessionToken) } } @@ -733,12 +752,22 @@ func (p *pool) SearchObject(ctx context.Context, params *client.SearchObjectPara func (p *pool) PutContainer(ctx context.Context, cnr *container.Container, opts ...CallOption) (*cid.ID, error) { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return nil, err } - res, err := cp.client.PutContainer(ctx, cnr, options...) + var cliPrm client.ContainerPutPrm + + if cnr != nil { + cliPrm.SetContainer(*cnr) + } + + if cfg.stoken != nil { + cliPrm.SetSessionToken(*cfg.stoken) + } + + res, err := cp.client.PutContainer(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -754,12 +783,18 @@ func (p *pool) PutContainer(ctx context.Context, cnr *container.Container, opts func (p *pool) GetContainer(ctx context.Context, cid *cid.ID, opts ...CallOption) (*container.Container, error) { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return nil, err } - res, err := cp.client.GetContainer(ctx, cid, options...) + var cliPrm client.ContainerGetPrm + + if cid != nil { + cliPrm.SetContainer(*cid) + } + + res, err := cp.client.GetContainer(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -775,12 +810,18 @@ func (p *pool) GetContainer(ctx context.Context, cid *cid.ID, opts ...CallOption func (p *pool) ListContainers(ctx context.Context, ownerID *owner.ID, opts ...CallOption) ([]*cid.ID, error) { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return nil, err } - res, err := cp.client.ListContainers(ctx, ownerID, options...) + var cliPrm client.ContainerListPrm + + if ownerID != nil { + cliPrm.SetAccount(*ownerID) + } + + res, err := cp.client.ListContainers(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -791,17 +832,27 @@ func (p *pool) ListContainers(ctx context.Context, ownerID *owner.ID, opts ...Ca return nil, err } - return res.IDList(), nil + return res.Containers(), nil } func (p *pool) DeleteContainer(ctx context.Context, cid *cid.ID, opts ...CallOption) error { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return err } - _, err = cp.client.DeleteContainer(ctx, cid, options...) + var cliPrm client.ContainerDeletePrm + + if cid != nil { + cliPrm.SetContainer(*cid) + } + + if cfg.stoken != nil { + cliPrm.SetSessionToken(*cfg.stoken) + } + + _, err = cp.client.DeleteContainer(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -815,12 +866,18 @@ func (p *pool) DeleteContainer(ctx context.Context, cid *cid.ID, opts ...CallOpt func (p *pool) GetEACL(ctx context.Context, cid *cid.ID, opts ...CallOption) (*eacl.Table, error) { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return nil, err } - res, err := cp.client.EACL(ctx, cid, options...) + var cliPrm client.EACLPrm + + if cid != nil { + cliPrm.SetContainer(*cid) + } + + res, err := cp.client.EACL(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -836,12 +893,22 @@ func (p *pool) GetEACL(ctx context.Context, cid *cid.ID, opts ...CallOption) (*e func (p *pool) SetEACL(ctx context.Context, table *eacl.Table, opts ...CallOption) error { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return err } - _, err = cp.client.SetEACL(ctx, table, options...) + var cliPrm client.SetEACLPrm + + if table != nil { + cliPrm.SetTable(*table) + } + + if cfg.stoken != nil { + cliPrm.SetSessionToken(*cfg.stoken) + } + + _, err = cp.client.SetEACL(ctx, cliPrm) if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { opts = append(opts, retry()) @@ -853,33 +920,20 @@ func (p *pool) SetEACL(ctx context.Context, table *eacl.Table, opts ...CallOptio return err } -func (p *pool) AnnounceContainerUsedSpace(ctx context.Context, announce []container.UsedSpaceAnnouncement, opts ...CallOption) error { - cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) - if err != nil { - return err - } - - _, err = cp.client.AnnounceContainerUsedSpace(ctx, announce, options...) - - if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry { - opts = append(opts, retry()) - return p.AnnounceContainerUsedSpace(ctx, announce, opts...) - } - - // here err already carries both status and client errors - - return err -} - func (p *pool) Balance(ctx context.Context, o *owner.ID, opts ...CallOption) (*accounting.Decimal, error) { cfg := cfgFromOpts(opts...) - cp, options, err := p.conn(ctx, cfg) + cp, _, err := p.conn(ctx, cfg) if err != nil { return nil, err } - res, err := cp.client.GetBalance(ctx, o, options...) + var cliPrm client.GetBalancePrm + + if o != nil { + cliPrm.SetAccount(*o) + } + + res, err := cp.client.GetBalance(ctx, cliPrm) if err != nil { // here err already carries both status and client errors return nil, err } @@ -898,6 +952,13 @@ func (p *pool) WaitForContainerPresence(ctx context.Context, cid *cid.ID, pollPa defer ticker.Stop() wdone := wctx.Done() done := ctx.Done() + + var cliPrm client.ContainerGetPrm + + if cid != nil { + cliPrm.SetContainer(*cid) + } + for { select { case <-done: @@ -905,7 +966,7 @@ func (p *pool) WaitForContainerPresence(ctx context.Context, cid *cid.ID, pollPa case <-wdone: return wctx.Err() case <-ticker.C: - _, err = conn.GetContainer(ctx, cid) + _, err = conn.GetContainer(ctx, cliPrm) if err == nil { return nil } @@ -930,7 +991,7 @@ func sessionTokenForOwner(id *owner.ID, cliRes *client.CreateSessionRes) *sessio st := session.NewToken() st.SetOwnerID(id) st.SetID(cliRes.ID()) - st.SetSessionKey(cliRes.SessionKey()) + st.SetSessionKey(cliRes.PublicKey()) return st } diff --git a/pool/pool_test.go b/pool/pool_test.go index e6202e9..6f3a5a6 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -152,7 +152,7 @@ func TestOneNode(t *testing.T) { clientBuilder := func(opts ...client.Option) (Client, error) { mockClient := NewMockClient(ctrl) mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(tok, nil) - mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfo{}, nil).AnyTimes() + mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfoRes{}, nil).AnyTimes() return mockClient, nil } @@ -189,7 +189,7 @@ func TestTwoNodes(t *testing.T) { tokens = append(tokens, tok) return tok, err }) - mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfo{}, nil).AnyTimes() + mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfoRes{}, nil).AnyTimes() return mockClient, nil } @@ -222,7 +222,7 @@ func TestOneOfTwoFailed(t *testing.T) { clientBuilder := func(opts ...client.Option) (Client, error) { clientCount++ mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { tok := newToken(t) tokens = append(tokens, tok) return tok, nil @@ -235,7 +235,7 @@ func TestOneOfTwoFailed(t *testing.T) { tokens = append(tokens, tok) return tok, nil }).AnyTimes() - mockClient2.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).DoAndReturn(func(_ interface{}, _ ...interface{}) (*client.EndpointInfo, error) { + mockClient2.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).DoAndReturn(func(_ interface{}, _ ...interface{}) (*client.EndpointInfoRes, error) { return nil, fmt.Errorf("error") }).AnyTimes() @@ -275,7 +275,7 @@ func TestTwoFailed(t *testing.T) { clientBuilder := func(opts ...client.Option) (Client, error) { mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")).AnyTimes() return mockClient, nil } @@ -309,7 +309,7 @@ func TestSessionCache(t *testing.T) { var tokens []*session.Token clientBuilder := func(opts ...client.Option) (Client, error) { mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { tok := session.NewToken() uid, err := uuid.New().MarshalBinary() require.NoError(t, err) @@ -373,7 +373,7 @@ func TestPriority(t *testing.T) { clientBuilder := func(opts ...client.Option) (Client, error) { clientCount++ mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { tok := newToken(t) tokens = append(tokens, tok) return tok, nil @@ -435,7 +435,7 @@ func TestSessionCacheWithKey(t *testing.T) { var tokens []*session.Token clientBuilder := func(opts ...client.Option) (Client, error) { mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ interface{}, _ ...interface{}) (*session.Token, error) { tok := session.NewToken() uid, err := uuid.New().MarshalBinary() require.NoError(t, err) @@ -487,7 +487,7 @@ func TestSessionTokenOwner(t *testing.T) { ctrl := gomock.NewController(t) clientBuilder := func(opts ...client.Option) (Client, error) { mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(&client.CreateSessionRes{}, nil).AnyTimes() + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&client.CreateSessionRes{}, nil).AnyTimes() mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(&client.EndpointInfoRes{}, nil).AnyTimes() return mockClient, nil } @@ -525,9 +525,9 @@ func TestWaitPresence(t *testing.T) { ctrl := gomock.NewController(t) mockClient := NewMockClient(ctrl) - mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockClient.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() mockClient.EXPECT().EndpointInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() - mockClient.EXPECT().GetContainer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockClient.EXPECT().GetContainer(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() cache, err := NewCache() require.NoError(t, err) diff --git a/pool/sampler_test.go b/pool/sampler_test.go index a286739..e14a544 100644 --- a/pool/sampler_test.go +++ b/pool/sampler_test.go @@ -48,11 +48,11 @@ type clientMock struct { err error } -func (c *clientMock) EndpointInfo(context.Context, ...client.CallOption) (*client.EndpointInfoRes, error) { +func (c *clientMock) EndpointInfo(context.Context, client.EndpointInfoPrm) (*client.EndpointInfoRes, error) { return nil, nil } -func (c *clientMock) NetworkInfo(context.Context, ...client.CallOption) (*client.NetworkInfoRes, error) { +func (c *clientMock) NetworkInfo(context.Context, client.NetworkInfoPrm) (*client.NetworkInfoRes, error) { return nil, nil }