forked from TrueCloudLab/frostfs-node
[#872] object: Introduce APE middlewar for object service
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
parent
e43609c616
commit
c8baf76fae
13 changed files with 1456 additions and 175 deletions
324
pkg/services/object/ape/request_test.go
Normal file
324
pkg/services/object/ape/request_test.go
Normal file
|
@ -0,0 +1,324 @@
|
|||
package ape
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestObjectProperties(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
container string
|
||||
object *string
|
||||
header *headerObjectSDKParams
|
||||
}{
|
||||
{
|
||||
name: "fully filled header",
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
header: &headerObjectSDKParams{
|
||||
majorVersion: 1,
|
||||
minorVersion: 1,
|
||||
owner: usertest.ID(),
|
||||
epoch: 3,
|
||||
payloadSize: 1000,
|
||||
typ: objectSDK.TypeRegular,
|
||||
payloadChecksum: checksumtest.Checksum(),
|
||||
payloadHomomorphicHash: checksumtest.Checksum(),
|
||||
attributes: []struct {
|
||||
key string
|
||||
val string
|
||||
}{
|
||||
{
|
||||
key: "attr1",
|
||||
val: "val1",
|
||||
},
|
||||
{
|
||||
key: "attr2",
|
||||
val: "val2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "partially filled header",
|
||||
container: containerID,
|
||||
header: &headerObjectSDKParams{
|
||||
majorVersion: 1,
|
||||
minorVersion: 1,
|
||||
owner: usertest.ID(),
|
||||
epoch: 3,
|
||||
attributes: []struct {
|
||||
key string
|
||||
val string
|
||||
}{
|
||||
{
|
||||
key: "attr1",
|
||||
val: "val1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "only address paramaters set in header",
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
},
|
||||
{
|
||||
name: "only container set in header",
|
||||
container: containerID,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cnr := newContainerIDSDK(t, test.container)
|
||||
obj := newObjectIDSDK(t, test.object)
|
||||
header := newHeaderObjectSDK(t, cnr, obj, test.header)
|
||||
|
||||
props := objectProperties(cnr, obj, header.ToV2().GetHeader())
|
||||
require.Equal(t, test.container, props[nativeschema.PropertyKeyObjectContainerID])
|
||||
|
||||
if obj != nil {
|
||||
require.Equal(t, *test.object, props[nativeschema.PropertyKeyObjectID])
|
||||
}
|
||||
|
||||
if test.header != nil {
|
||||
require.Equal(t,
|
||||
fmt.Sprintf("v%d.%d", test.header.majorVersion, test.header.minorVersion),
|
||||
props[nativeschema.PropertyKeyObjectVersion],
|
||||
)
|
||||
require.Equal(t, test.header.owner.EncodeToString(), props[nativeschema.PropertyKeyObjectOwnerID])
|
||||
require.Equal(t, fmt.Sprintf("%d", test.header.epoch), props[nativeschema.PropertyKeyObjectCreationEpoch])
|
||||
require.Equal(t, fmt.Sprintf("%d", test.header.payloadSize), props[nativeschema.PropertyKeyObjectPayloadLength])
|
||||
require.Equal(t, test.header.typ.String(), props[nativeschema.PropertyKeyObjectType])
|
||||
require.Equal(t, test.header.payloadChecksum.String(), props[nativeschema.PropertyKeyObjectPayloadHash])
|
||||
require.Equal(t, test.header.payloadHomomorphicHash.String(), props[nativeschema.PropertyKeyObjectHomomorphicHash])
|
||||
|
||||
for _, attr := range test.header.attributes {
|
||||
require.Equal(t, attr.val, props[attr.key])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAPERequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
methods []string
|
||||
namespace string
|
||||
container string
|
||||
object *string
|
||||
header testHeader
|
||||
expectErr error
|
||||
}{
|
||||
{
|
||||
name: "oid required requests",
|
||||
methods: methodsRequiredOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
header: testHeader{
|
||||
headerObjSDK: &headerObjectSDKParams{
|
||||
majorVersion: 1,
|
||||
minorVersion: 1,
|
||||
owner: usertest.ID(),
|
||||
epoch: 3,
|
||||
payloadSize: 1000,
|
||||
typ: objectSDK.TypeRegular,
|
||||
payloadChecksum: checksumtest.Checksum(),
|
||||
payloadHomomorphicHash: checksumtest.Checksum(),
|
||||
},
|
||||
fromHeaderProvider: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "oid required requests but header cannot be found locally",
|
||||
methods: methodsRequiredOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
header: testHeader{},
|
||||
},
|
||||
{
|
||||
name: "oid required requests missed oid",
|
||||
methods: methodsRequiredOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: nil,
|
||||
header: testHeader{},
|
||||
expectErr: errMissingOID,
|
||||
},
|
||||
{
|
||||
name: "response for oid required requests",
|
||||
methods: methodsRequiredOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
header: testHeader{
|
||||
headerObjSDK: &headerObjectSDKParams{
|
||||
majorVersion: 1,
|
||||
minorVersion: 1,
|
||||
owner: usertest.ID(),
|
||||
epoch: 3,
|
||||
payloadSize: 1000,
|
||||
typ: objectSDK.TypeRegular,
|
||||
payloadChecksum: checksumtest.Checksum(),
|
||||
payloadHomomorphicHash: checksumtest.Checksum(),
|
||||
},
|
||||
fromRequestResponseHeader: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "oid not required methods request",
|
||||
methods: methodsOptionalOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: nil,
|
||||
header: testHeader{
|
||||
headerObjSDK: &headerObjectSDKParams{
|
||||
majorVersion: 6,
|
||||
minorVersion: 66,
|
||||
owner: usertest.ID(),
|
||||
epoch: 3,
|
||||
typ: objectSDK.TypeLock,
|
||||
},
|
||||
fromRequestResponseHeader: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "oid not required methods request but no header",
|
||||
methods: methodsOptionalOID,
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: nil,
|
||||
header: testHeader{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, method := range test.methods {
|
||||
t.Run(method, func(t *testing.T) {
|
||||
cnr := newContainerIDSDK(t, test.container)
|
||||
obj := newObjectIDSDK(t, test.object)
|
||||
|
||||
prm := Prm{
|
||||
Namespace: test.namespace,
|
||||
Method: method,
|
||||
Container: cnr,
|
||||
Object: obj,
|
||||
Role: role,
|
||||
SenderKey: senderKey,
|
||||
}
|
||||
|
||||
headerSource := newHeaderProviderMock()
|
||||
|
||||
var headerObjSDK *objectSDK.Object
|
||||
if test.header.headerObjSDK != nil {
|
||||
headerObjSDK = newHeaderObjectSDK(t, cnr, obj, test.header.headerObjSDK)
|
||||
if test.header.fromHeaderProvider {
|
||||
require.NotNil(t, obj, "oid is required if a header is expected to be found in header provider")
|
||||
headerSource.addHeader(cnr, *obj, headerObjSDK)
|
||||
} else if test.header.fromRequestResponseHeader {
|
||||
prm.Header = headerObjSDK.ToV2().GetHeader()
|
||||
}
|
||||
}
|
||||
|
||||
c := checkerImpl{
|
||||
headerProvider: headerSource,
|
||||
}
|
||||
|
||||
r, err := c.newAPERequest(context.TODO(), prm)
|
||||
if test.expectErr != nil {
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, test.expectErr)
|
||||
return
|
||||
}
|
||||
|
||||
expectedRequest := request{
|
||||
operation: method,
|
||||
resource: &resource{
|
||||
name: resourceName(cnr, obj, prm.Namespace),
|
||||
properties: objectProperties(cnr, obj, func() *objectV2.Header {
|
||||
if headerObjSDK != nil {
|
||||
return headerObjSDK.ToV2().GetHeader()
|
||||
}
|
||||
return prm.Header
|
||||
}()),
|
||||
},
|
||||
properties: map[string]string{
|
||||
nativeschema.PropertyKeyActorPublicKey: prm.SenderKey,
|
||||
nativeschema.PropertyKeyActorRole: prm.Role,
|
||||
},
|
||||
}
|
||||
|
||||
require.Equal(t, expectedRequest, *r)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceName(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
namespace string
|
||||
container string
|
||||
object *string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "non-root namespace, CID",
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
expected: fmt.Sprintf("native:object/%s/%s/*", namespace, containerID),
|
||||
},
|
||||
{
|
||||
name: "non-root namespace, CID, OID",
|
||||
namespace: namespace,
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
expected: fmt.Sprintf("native:object/%s/%s/%s", namespace, containerID, objectID),
|
||||
},
|
||||
{
|
||||
name: "empty namespace, CID",
|
||||
namespace: "",
|
||||
container: containerID,
|
||||
expected: fmt.Sprintf("native:object//%s/*", containerID),
|
||||
},
|
||||
{
|
||||
name: "empty namespace, CID, OID",
|
||||
namespace: "",
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
expected: fmt.Sprintf("native:object//%s/%s", containerID, objectID),
|
||||
},
|
||||
{
|
||||
name: "root namespace, CID",
|
||||
namespace: "root",
|
||||
container: containerID,
|
||||
expected: fmt.Sprintf("native:object//%s/*", containerID),
|
||||
},
|
||||
{
|
||||
name: "root namespace, CID, OID",
|
||||
namespace: "root",
|
||||
container: containerID,
|
||||
object: stringPtr(objectID),
|
||||
expected: fmt.Sprintf("native:object//%s/%s", containerID, objectID),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cnr := newContainerIDSDK(t, test.container)
|
||||
obj := newObjectIDSDK(t, test.object)
|
||||
require.Equal(t, test.expected, resourceName(cnr, obj, test.namespace))
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue