[#215] client: Introduce apemanager rpc interface
* Introduce `APEManagerAddChain`, `APEManagerRemoveChain`, `APEManagerListChains`. * Introduce reqeuest/response types for these handlers (Prm*, Res*). * Inroduce status type for apemanager `APEManagerAccessDenied`; add unit-tests. Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
parent
32a975a20d
commit
b2ad1f3b3e
7 changed files with 310 additions and 0 deletions
79
client/apemanager_add_chain.go
Normal file
79
client/apemanager_add_chain.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
|
session_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
|
apemanager_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apemanager"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrmAPEManagerAddChain groups parameters of APEManagerAddChain operation.
|
||||||
|
type PrmAPEManagerAddChain struct {
|
||||||
|
XHeaders []string
|
||||||
|
|
||||||
|
ChainTarget apemanager_sdk.ChainTarget
|
||||||
|
|
||||||
|
Chain apemanager_sdk.Chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prm *PrmAPEManagerAddChain) buildRequest(c *Client) (*apemanager_v2.AddChainRequest, error) {
|
||||||
|
if len(prm.XHeaders)%2 != 0 {
|
||||||
|
return nil, errorInvalidXHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(apemanager_v2.AddChainRequest)
|
||||||
|
reqBody := new(apemanager_v2.AddChainRequestBody)
|
||||||
|
|
||||||
|
reqBody.SetTarget(prm.ChainTarget.ToV2())
|
||||||
|
reqBody.SetChain(prm.Chain.ToV2())
|
||||||
|
|
||||||
|
req.SetBody(reqBody)
|
||||||
|
|
||||||
|
var meta session_v2.RequestMetaHeader
|
||||||
|
writeXHeadersToMeta(prm.XHeaders, &meta)
|
||||||
|
|
||||||
|
c.prepareRequest(req, &meta)
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResAPEManagerAddChain struct {
|
||||||
|
statusRes
|
||||||
|
|
||||||
|
// ChainID of set Chain. If Chain does not contain chainID before request, then
|
||||||
|
// ChainID is generated.
|
||||||
|
ChainID apemanager_sdk.ChainID
|
||||||
|
}
|
||||||
|
|
||||||
|
// APEManagerAddChain sets Chain for ChainTarget.
|
||||||
|
func (c *Client) APEManagerAddChain(ctx context.Context, prm PrmAPEManagerAddChain) (*ResAPEManagerAddChain, error) {
|
||||||
|
req, err := prm.buildRequest(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
|
||||||
|
return nil, fmt.Errorf("sign request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := rpcapi.AddChain(&c.c, req, client.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res ResAPEManagerAddChain
|
||||||
|
res.st, err = c.processResponse(resp)
|
||||||
|
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.ChainID = resp.GetBody().GetChainID()
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
80
client/apemanager_list_chains.go
Normal file
80
client/apemanager_list_chains.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
|
session_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
|
apemanager_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apemanager"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrmAPEManagerListChains groups parameters of APEManagerListChains operation.
|
||||||
|
type PrmAPEManagerListChains struct {
|
||||||
|
XHeaders []string
|
||||||
|
|
||||||
|
ChainTarget apemanager_sdk.ChainTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prm *PrmAPEManagerListChains) buildRequest(c *Client) (*apemanager_v2.ListChainsRequest, error) {
|
||||||
|
if len(prm.XHeaders)%2 != 0 {
|
||||||
|
return nil, errorInvalidXHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(apemanager_v2.ListChainsRequest)
|
||||||
|
reqBody := new(apemanager_v2.ListChainsRequestBody)
|
||||||
|
|
||||||
|
reqBody.SetTarget(prm.ChainTarget.ToV2())
|
||||||
|
|
||||||
|
req.SetBody(reqBody)
|
||||||
|
|
||||||
|
var meta session_v2.RequestMetaHeader
|
||||||
|
writeXHeadersToMeta(prm.XHeaders, &meta)
|
||||||
|
|
||||||
|
c.prepareRequest(req, &meta)
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResAPEManagerListChains struct {
|
||||||
|
statusRes
|
||||||
|
|
||||||
|
Chains []apemanager_sdk.Chain
|
||||||
|
}
|
||||||
|
|
||||||
|
// APEManagerListChains lists Chains for ChainTarget.
|
||||||
|
func (c *Client) APEManagerListChains(ctx context.Context, prm PrmAPEManagerListChains) (*ResAPEManagerListChains, error) {
|
||||||
|
req, err := prm.buildRequest(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
|
||||||
|
return nil, fmt.Errorf("sign request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := rpcapi.ListChains(&c.c, req, client.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res ResAPEManagerListChains
|
||||||
|
res.st, err = c.processResponse(resp)
|
||||||
|
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range resp.GetBody().GetChains() {
|
||||||
|
var chSDK apemanager_sdk.Chain
|
||||||
|
if err := chSDK.ReadFromV2(ch); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Chains = append(res.Chains, chSDK)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
73
client/apemanager_remove_chain.go
Normal file
73
client/apemanager_remove_chain.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apemanager_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||||
|
session_v2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
||||||
|
apemanager_sdk "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/apemanager"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrmAPEManagerRemoveChain groups parameters of APEManagerRemoveChain operation.
|
||||||
|
type PrmAPEManagerRemoveChain struct {
|
||||||
|
XHeaders []string
|
||||||
|
|
||||||
|
ChainTarget apemanager_sdk.ChainTarget
|
||||||
|
|
||||||
|
ChainID apemanager_sdk.ChainID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prm *PrmAPEManagerRemoveChain) buildRequest(c *Client) (*apemanager_v2.RemoveChainRequest, error) {
|
||||||
|
if len(prm.XHeaders)%2 != 0 {
|
||||||
|
return nil, errorInvalidXHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(apemanager_v2.RemoveChainRequest)
|
||||||
|
reqBody := new(apemanager_v2.RemoveChainRequestBody)
|
||||||
|
|
||||||
|
reqBody.SetTarget(prm.ChainTarget.ToV2())
|
||||||
|
reqBody.SetChainID(prm.ChainID)
|
||||||
|
|
||||||
|
req.SetBody(reqBody)
|
||||||
|
|
||||||
|
var meta session_v2.RequestMetaHeader
|
||||||
|
writeXHeadersToMeta(prm.XHeaders, &meta)
|
||||||
|
|
||||||
|
c.prepareRequest(req, &meta)
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResAPEManagerRemoveChain struct {
|
||||||
|
statusRes
|
||||||
|
}
|
||||||
|
|
||||||
|
// APEManagerRemoveChain removes Chain with ChainID defined for ChainTarget.
|
||||||
|
func (c *Client) APEManagerRemoveChain(ctx context.Context, prm PrmAPEManagerRemoveChain) (*ResAPEManagerRemoveChain, error) {
|
||||||
|
req, err := prm.buildRequest(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
|
||||||
|
return nil, fmt.Errorf("sign request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := rpcapi.RemoveChain(&c.c, req, client.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res ResAPEManagerRemoveChain
|
||||||
|
res.st, err = c.processResponse(resp)
|
||||||
|
if err != nil || !apistatus.IsSuccessful(res.st) {
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
|
@ -61,6 +61,12 @@ func IsErrSessionNotFound(err error) bool {
|
||||||
return wrapsErrType[*apistatus.SessionTokenNotFound](err)
|
return wrapsErrType[*apistatus.SessionTokenNotFound](err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrAPEManagerAccessDenied checks if err corresponds to FrostFS status return
|
||||||
|
// corresponding to apemanager access deny. Supports wrapped errors.
|
||||||
|
func IsErrAPEManagerAccessDenied(err error) bool {
|
||||||
|
return wrapsErrType[*apistatus.APEManagerAccessDenied](err)
|
||||||
|
}
|
||||||
|
|
||||||
// returns error describing missing field with the given name.
|
// returns error describing missing field with the given name.
|
||||||
func newErrMissingResponseField(name string) error {
|
func newErrMissingResponseField(name string) error {
|
||||||
return fmt.Errorf("missing %s field in the response", name)
|
return fmt.Errorf("missing %s field in the response", name)
|
||||||
|
|
53
client/status/apemanager.go
Normal file
53
client/status/apemanager.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package apistatus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APEManagerAccessDenied describes status of the failure because of the access control violation.
|
||||||
|
// Instances provide Status and StatusV2 interfaces.
|
||||||
|
type APEManagerAccessDenied struct {
|
||||||
|
v2 status.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultAPEManagerAccessDeniedMsg = "apemanager access denied"
|
||||||
|
|
||||||
|
func (x *APEManagerAccessDenied) Error() string {
|
||||||
|
msg := x.v2.Message()
|
||||||
|
if msg == "" {
|
||||||
|
msg = defaultAPEManagerAccessDeniedMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return errMessageStatusV2(
|
||||||
|
globalizeCodeV2(apemanager.StatusAPEManagerAccessDenied, apemanager.GlobalizeFail),
|
||||||
|
msg,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *APEManagerAccessDenied) fromStatusV2(st *status.Status) {
|
||||||
|
x.v2 = *st
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStatusV2 converts APEManagerAccessDenied to v2's Status.
|
||||||
|
// If the value was returned by FromStatusV2, returns the source message.
|
||||||
|
// Otherwise, returns message with
|
||||||
|
// - code: APE_MANAGER_ACCESS_DENIED;
|
||||||
|
// - string message: "apemanager access denied";
|
||||||
|
// - details: empty.
|
||||||
|
func (x APEManagerAccessDenied) ToStatusV2() *status.Status {
|
||||||
|
x.v2.SetCode(globalizeCodeV2(apemanager.StatusAPEManagerAccessDenied, apemanager.GlobalizeFail))
|
||||||
|
x.v2.SetMessage(defaultAPEManagerAccessDeniedMsg)
|
||||||
|
return &x.v2
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteReason writes human-readable access rejection reason.
|
||||||
|
func (x *APEManagerAccessDenied) WriteReason(reason string) {
|
||||||
|
apemanager.WriteAccessDeniedDesc(&x.v2, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reason returns human-readable access rejection reason returned by the server.
|
||||||
|
// Returns empty value is reason is not presented.
|
||||||
|
func (x APEManagerAccessDenied) Reason() string {
|
||||||
|
return apemanager.ReadAccessDeniedDesc(x.v2)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package apistatus
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
|
@ -92,6 +93,12 @@ func FromStatusV2(st *status.Status) Status {
|
||||||
case session.StatusTokenExpired:
|
case session.StatusTokenExpired:
|
||||||
decoder = new(SessionTokenExpired)
|
decoder = new(SessionTokenExpired)
|
||||||
}
|
}
|
||||||
|
case apemanager.LocalizeFailStatus(&code):
|
||||||
|
//nolint:exhaustive
|
||||||
|
switch code {
|
||||||
|
case apemanager.StatusAPEManagerAccessDenied:
|
||||||
|
decoder = new(APEManagerAccessDenied)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if decoder == nil {
|
if decoder == nil {
|
||||||
|
|
|
@ -125,6 +125,12 @@ func TestToStatusV2(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
codeV2: 4097,
|
codeV2: 4097,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
status: (statusConstructor)(func() apistatus.Status {
|
||||||
|
return new(apistatus.APEManagerAccessDenied)
|
||||||
|
}),
|
||||||
|
codeV2: 5120,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
status: (statusConstructor)(func() apistatus.Status {
|
status: (statusConstructor)(func() apistatus.Status {
|
||||||
return new(apistatus.NodeUnderMaintenance)
|
return new(apistatus.NodeUnderMaintenance)
|
||||||
|
@ -278,6 +284,12 @@ func TestFromStatusV2(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
codeV2: 4097,
|
codeV2: 4097,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
status: (statusConstructor)(func() apistatus.Status {
|
||||||
|
return new(apistatus.APEManagerAccessDenied)
|
||||||
|
}),
|
||||||
|
codeV2: 5120,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
status: (statusConstructor)(func() apistatus.Status {
|
status: (statusConstructor)(func() apistatus.Status {
|
||||||
return new(apistatus.NodeUnderMaintenance)
|
return new(apistatus.NodeUnderMaintenance)
|
||||||
|
|
Loading…
Reference in a new issue