diff --git a/client/accounting.go b/client/accounting.go index 9b85bc73..e9ea673a 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 aa1d0427..7afab982 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 7ccb4f8a..d0909e8f 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 7b7f9088..0edfe88e 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 cc4ae22d..54ed6fa9 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 5666ea9b..00000000 --- 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 2afd17f4..a6bc25d2 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 d6b8f9db..1c53a0a1 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 c7d7131b..65d0051c 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 ae42ece9..7918531f 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 52de9b3b..b0af1d48 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 e6202e9f..6f3a5a6f 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 a2867394..e14a5445 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 }