[#222] Add support for aio v1.7.0 in integration tests #239

Open
KurlesHS wants to merge 1 commit from KurlesHS/frostfs-http-gw:feature/add-aio-1.7.0-integration-tests into master
3 changed files with 151 additions and 6 deletions

View file

@ -14,10 +14,12 @@ import (
"net/http" "net/http"
"os" "os"
"sort" "sort"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
containerv2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container" containerv2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
@ -31,6 +33,8 @@ import (
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
docker "github.com/docker/docker/api/types/container" docker "github.com/docker/docker/api/types/container"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
@ -51,6 +55,59 @@ const (
testCORSContainerName = "cors" testCORSContainerName = "cors"
) )
func versionToUint(t *testing.T, v string) uint64 {
parts := strings.Split(v, ".")
require.Len(t, parts, 3)
major, err := strconv.ParseUint(parts[0], 10, 16)

What about versions like v0.33.0-rc.6?

What about versions like `v0.33.0-rc.6`?

This function is used to convert the version numbers of aio-containers (which we test and explicitly list at the beginning of the TestIntegration function) into a numeric format. The versions follow the "MAJOR.MINOR.PATCH" format. If in the future there's a need to support versions with an additional suffix (e.g., "1.7.2-nightly1"), this function can be extended accordingly—but for now, that would be premature.

This function is used to convert the version numbers of aio-containers (which we test and explicitly list at the beginning of the TestIntegration function) into a numeric format. The versions follow the "MAJOR.MINOR.PATCH" format. If in the future there's a need to support versions with an additional suffix (e.g., "1.7.2-nightly1"), this function can be extended accordingly—but for now, that would be premature.
require.NoError(t, err)
minor, err := strconv.ParseUint(parts[1], 10, 16)
require.NoError(t, err)
patch, err := strconv.ParseUint(parts[2], 10, 32)
require.NoError(t, err)
versionNumber := major<<48 | minor<<32 | patch
return versionNumber
}
func publicReadWriteRules() []chain.Rule {
return []chain.Rule{
{
Status: chain.Allow,
Actions: chain.Actions{

Can we fix formatting (run make fmt at least)?
For example:

diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index 8c22931..d1a61f4 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -72,22 +72,24 @@ func versionToUint(t *testing.T, v string) uint64 {
 func publicReadWriteRules() []chain.Rule {
        return []chain.Rule{
                {
-                       Status: chain.Allow, Actions: chain.Actions{
-                       Inverted: false,
-                       Names: []string{
-                               native.MethodPutObject,
-                               native.MethodGetObject,
-                               native.MethodHeadObject,
-                               native.MethodDeleteObject,
-                               native.MethodSearchObject,
-                               native.MethodRangeObject,
-                               native.MethodHashObject,
-                               native.MethodPatchObject,
+                       Status: chain.Allow,
+                       Actions: chain.Actions{
+                               Names: []string{
+                                       native.MethodPutObject,
+                                       native.MethodGetObject,
+                                       native.MethodHeadObject,
+                                       native.MethodDeleteObject,
+                                       native.MethodSearchObject,
+                                       native.MethodRangeObject,
+                                       native.MethodHashObject,
+                                       native.MethodPatchObject,
+                               },
                        },
-               }, Resources: chain.Resources{
-                       Inverted: false,
-                       Names:    []string{native.ResourceFormatRootObjects},
-               }, Any: false},
+                       Resources: chain.Resources{
+                               Names: []string{native.ResourceFormatRootObjects},
+                       },
+                       Any: false,
+               },
        }
 }

Can we fix formatting (run `make fmt` at least)? For example: ```diff diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go index 8c22931..d1a61f4 100644 --- a/cmd/http-gw/integration_test.go +++ b/cmd/http-gw/integration_test.go @@ -72,22 +72,24 @@ func versionToUint(t *testing.T, v string) uint64 { func publicReadWriteRules() []chain.Rule { return []chain.Rule{ { - Status: chain.Allow, Actions: chain.Actions{ - Inverted: false, - Names: []string{ - native.MethodPutObject, - native.MethodGetObject, - native.MethodHeadObject, - native.MethodDeleteObject, - native.MethodSearchObject, - native.MethodRangeObject, - native.MethodHashObject, - native.MethodPatchObject, + Status: chain.Allow, + Actions: chain.Actions{ + Names: []string{ + native.MethodPutObject, + native.MethodGetObject, + native.MethodHeadObject, + native.MethodDeleteObject, + native.MethodSearchObject, + native.MethodRangeObject, + native.MethodHashObject, + native.MethodPatchObject, + }, }, - }, Resources: chain.Resources{ - Inverted: false, - Names: []string{native.ResourceFormatRootObjects}, - }, Any: false}, + Resources: chain.Resources{ + Names: []string{native.ResourceFormatRootObjects}, + }, + Any: false, + }, } } ```
Please align brackets https://git.frostfs.info/KurlesHS/frostfs-http-gw/src/commit/101d6370b8deaed19ba1ff1bed93ae34759a0ad5/cmd/http-gw/integration_test.go#L73 and https://git.frostfs.info/KurlesHS/frostfs-http-gw/src/commit/101d6370b8deaed19ba1ff1bed93ae34759a0ad5/cmd/http-gw/integration_test.go#L92

For some reason, gofmt is totally fine with this on my machine. =(

For some reason, gofmt is totally fine with this on my machine. =(
Inverted: false,
Names: []string{
native.MethodPutObject,
native.MethodGetObject,
native.MethodHeadObject,
native.MethodDeleteObject,
native.MethodSearchObject,
native.MethodRangeObject,
native.MethodHashObject,
native.MethodPatchObject,
},
},
Resources: chain.Resources{
Inverted: false,
Names: []string{native.ResourceFormatRootObjects},
},
Any: false,
},
}
}
func privateRules() []chain.Rule {
rule := publicReadWriteRules()
// The same as public-read-write, except that only the owner is allowed to perform the listed actions
rule[0].Condition = []chain.Condition{
{
Op: chain.CondStringEquals,

I don't know how exactly storage node determines role when bearer token is applied, but I think using public key condition is more robust
cc @alexvanin

I don't know how exactly storage node determines role when bearer token is applied, but I think using public key condition is more robust cc @alexvanin
Kind: chain.KindRequest,
Key: native.PropertyKeyActorRole,
Value: native.PropertyValueContainerRoleOwner,
},
}
return rule
}
func TestIntegration(t *testing.T) { func TestIntegration(t *testing.T) {
rootCtx := context.Background() rootCtx := context.Background()
aioImage := "git.frostfs.info/truecloudlab/frostfs-aio:" aioImage := "git.frostfs.info/truecloudlab/frostfs-aio:"
@ -59,6 +116,7 @@ func TestIntegration(t *testing.T) {
"1.3.0", "1.3.0",
"1.5.0", "1.5.0",
"1.6.5", "1.6.5",
"1.7.0",
} }
key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb") key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb")
require.NoError(t, err) require.NoError(t, err)
@ -75,10 +133,16 @@ func TestIntegration(t *testing.T) {
ctx, cancel2 := context.WithCancel(rootCtx) ctx, cancel2 := context.WithCancel(rootCtx)
aioContainer := createDockerContainer(ctx, t, aioImage+version) aioContainer := createDockerContainer(ctx, t, aioImage+version)
if strings.HasPrefix(version, "1.6") { versionNumber := versionToUint(t, version)
if versionNumber >= versionToUint(t, "1.6.0") {
registerUser(t, ctx, aioContainer, file.Name()) registerUser(t, ctx, aioContainer, file.Name())
} }
createContainer := createContainerWithACL
if versionNumber >= versionToUint(t, "1.7.0") {
createContainer = createContainerWithAPE
}
// Creating CORS container // Creating CORS container
clientPool := getPool(ctx, t, key) clientPool := getPool(ctx, t, key)
_, err = createContainer(ctx, t, clientPool, ownerID, testCORSContainerName) _, err = createContainer(ctx, t, clientPool, ownerID, testCORSContainerName)
@ -469,15 +533,18 @@ func checkStatusCodes(ctx context.Context, t *testing.T, clientPool *pool.Pool,
t.Run("access denied", func(t *testing.T) { t.Run("access denied", func(t *testing.T) {
basicACL := acl.Private basicACL := acl.Private
var recs []*eacl.Record var recs []*eacl.Record
var APERules []chain.Rule
if version == "1.2.7" { if version == "1.2.7" {
basicACL = acl.PublicRWExtended basicACL = acl.PublicRWExtended
rec := eacl.NewRecord() rec := eacl.NewRecord()
rec.SetAction(eacl.ActionDeny) rec.SetAction(eacl.ActionDeny)
rec.SetOperation(eacl.OperationGet) rec.SetOperation(eacl.OperationGet)
recs = append(recs, rec) recs = append(recs, rec)
} else if versionToUint(t, version) >= versionToUint(t, "1.7.0") {
APERules = privateRules()
} }
cnrID, err := createContainerBase(ctx, t, clientPool, ownerID, basicACL, "") cnrID, err := createContainerBase(ctx, t, clientPool, ownerID, basicACL, APERules, "")
require.NoError(t, err) require.NoError(t, err)
key, err := keys.NewPrivateKey() key, err := keys.NewPrivateKey()
@ -560,11 +627,77 @@ func getPool(ctx context.Context, t *testing.T, key *keys.PrivateKey) *pool.Pool
return clientPool return clientPool
} }
func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, name string) (cid.ID, error) { func createContainerWithACL(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, name string) (cid.ID, error) {
return createContainerBase(ctx, t, clientPool, ownerID, acl.PublicRWExtended, name) return createContainerBase(ctx, t, clientPool, ownerID, acl.PublicRWExtended, nil, name)
} }
dkirillov marked this conversation as resolved Outdated

acl here must be 0 instead of acl.PublicRWExtended

acl here must be `0` instead of `acl.PublicRWExtended`
func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, basicACL acl.Basic, name string) (cid.ID, error) { func createContainerWithAPE(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, name string) (cid.ID, error) {
return createContainerBase(ctx, t, clientPool, ownerID, 0, publicReadWriteRules(), name)
dkirillov marked this conversation as resolved Outdated

Maybe the better name is waitForAPEBeApplied or something like this?

Maybe the better name is `waitForAPEBeApplied` or something like this?
}
func waitForAPEBeApplied(ctx context.Context, clientPool *pool.Pool, expectedCh chain.Chain, cnrID cid.ID) error {
prmListAPEChains := pool.PrmListAPEChains{
Target: ape.ChainTarget{
TargetType: ape.TargetTypeContainer,
Name: cnrID.EncodeToString(),
},
dkirillov marked this conversation as resolved Outdated

By the way, why do need this function?

Can we just get error from clientPool.ListAPEChains (we pass context there) or by using ctx.Err() when case <- ctx.Done(): happens?

By the way, why do need this function? Can we just get error from `clientPool.ListAPEChains` (we pass context there) or by using `ctx.Err()` when `case <- ctx.Done():` happens?
}
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
for {
chains, err := clientPool.ListAPEChains(ctx, prmListAPEChains)
if err != nil {
return fmt.Errorf("list APE chains: %w", err)
}
for _, rawChain := range chains {
var ch chain.Chain
err = ch.UnmarshalBinary(rawChain.Raw)
if err != nil {
return fmt.Errorf("unmarshal chain: %w", err)
}
if bytes.Equal(ch.ID, expectedCh.ID) {
// At the moment, according to the core team, there is no way through the API
// to check whether the APE chain stored in the contract has been applied to the container.
// So after we make sure that the APE chain is stored, we just wait for a certain period of time
// (8 seconds by default, the time until the next block and cache invalidation)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(8 * time.Second):

Maybe we should use AIO images where this cache is disables at all?

cc @alexvanin

Maybe we should use AIO images where this cache is disables at all? cc @alexvanin
return nil
}
}
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(500 * time.Millisecond):
}
}
}
func addAPEChainToContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, chainID string, rules []chain.Rule, cnrID cid.ID) {
ch := chain.Chain{
ID: chain.ID(chainID),
Rules: rules,
}
data, err := ch.MarshalBinary()
require.NoError(t, err)
prmAddAPEChain := pool.PrmAddAPEChain{
Target: ape.ChainTarget{
TargetType: ape.TargetTypeContainer,
Name: cnrID.EncodeToString(),
},
Chain: ape.Chain{Raw: data},
}
err = clientPool.AddAPEChain(ctx, prmAddAPEChain)
require.NoError(t, err)
err = waitForAPEBeApplied(ctx, clientPool, ch, cnrID)
require.NoError(t, err)
}
func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, basicACL acl.Basic, apeRules []chain.Rule, name string) (cid.ID, error) {
dkirillov marked this conversation as resolved Outdated

Should we check err here too?

Should we check `err` here too?
var policy netmap.PlacementPolicy var policy netmap.PlacementPolicy
err := policy.DecodeString("REP 1") err := policy.DecodeString("REP 1")
require.NoError(t, err) require.NoError(t, err)
@ -572,7 +705,9 @@ func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Poo
var cnr container.Container var cnr container.Container
cnr.Init() cnr.Init()
cnr.SetPlacementPolicy(policy) cnr.SetPlacementPolicy(policy)
if basicACL != 0 {
cnr.SetBasicACL(basicACL) cnr.SetBasicACL(basicACL)
}
cnr.SetOwner(ownerID) cnr.SetOwner(ownerID)
container.SetCreationTime(&cnr, time.Now()) container.SetCreationTime(&cnr, time.Now())
@ -601,6 +736,11 @@ func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Poo
} }
fmt.Println(CID.String()) fmt.Println(CID.String())
if len(apeRules) != 0 {
chainID := "http-aio-" + CID.String()[:8]
addAPEChainToContainer(ctx, t, clientPool, chainID, apeRules, CID)
}
return CID, err return CID, err
} }

3
go.mod
View file

@ -8,6 +8,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250128150313-cfbca7fa1dfe git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250128150313-cfbca7fa1dfe
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250317082814-87bb55f992dc git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250317082814-87bb55f992dc
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250425083815-09ff3bf14991
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
github.com/docker/docker v27.1.1+incompatible github.com/docker/docker v27.1.1+incompatible
@ -68,10 +69,12 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/ipfs/go-cid v0.0.7 // indirect github.com/ipfs/go-cid v0.0.7 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect

2
go.sum
View file

@ -52,6 +52,8 @@ git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8l
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM= git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8= git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI= git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250425083815-09ff3bf14991 h1:eTefR8y2y9cg7X5kybIcXDdmABfk/3A2awdmFD3zOsA=
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250425083815-09ff3bf14991/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA= git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=