From 3b0883086fb1ee97ff89e45395dbe1612f15cbce Mon Sep 17 00:00:00 2001 From: Aleksey Kravchenko Date: Mon, 5 May 2025 11:54:02 +0300 Subject: [PATCH] [#222] Add support for aio v1.7.0 in integration tests Signed-off-by: Aleksey Kravchenko --- cmd/http-gw/integration_test.go | 152 ++++++++++++++++++++++++++++++-- go.mod | 3 + go.sum | 2 + 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go index 6ab8e99..96f514d 100644 --- a/cmd/http-gw/integration_test.go +++ b/cmd/http-gw/integration_test.go @@ -14,10 +14,12 @@ import ( "net/http" "os" "sort" + "strconv" "strings" "testing" "time" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" 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/client" @@ -31,6 +33,8 @@ import ( 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/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" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/wallet" @@ -51,6 +55,59 @@ const ( 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) + 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{ + 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, + Kind: chain.KindRequest, + Key: native.PropertyKeyActorRole, + Value: native.PropertyValueContainerRoleOwner, + }, + } + return rule +} + func TestIntegration(t *testing.T) { rootCtx := context.Background() aioImage := "git.frostfs.info/truecloudlab/frostfs-aio:" @@ -59,6 +116,7 @@ func TestIntegration(t *testing.T) { "1.3.0", "1.5.0", "1.6.5", + "1.7.0", } key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb") require.NoError(t, err) @@ -75,10 +133,16 @@ func TestIntegration(t *testing.T) { ctx, cancel2 := context.WithCancel(rootCtx) 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()) } + createContainer := createContainerWithACL + if versionNumber >= versionToUint(t, "1.7.0") { + createContainer = createContainerWithAPE + } + // Creating CORS container clientPool := getPool(ctx, t, key) _, 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) { basicACL := acl.Private var recs []*eacl.Record + var APERules []chain.Rule if version == "1.2.7" { basicACL = acl.PublicRWExtended rec := eacl.NewRecord() rec.SetAction(eacl.ActionDeny) rec.SetOperation(eacl.OperationGet) 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) key, err := keys.NewPrivateKey() @@ -560,11 +627,77 @@ func getPool(ctx context.Context, t *testing.T, key *keys.PrivateKey) *pool.Pool return clientPool } -func createContainer(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) +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, nil, name) } -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) +} + +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(), + }, + } + + 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): + 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) { var policy netmap.PlacementPolicy err := policy.DecodeString("REP 1") require.NoError(t, err) @@ -572,7 +705,9 @@ func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Poo var cnr container.Container cnr.Init() cnr.SetPlacementPolicy(policy) - cnr.SetBasicACL(basicACL) + if basicACL != 0 { + cnr.SetBasicACL(basicACL) + } cnr.SetOwner(ownerID) container.SetCreationTime(&cnr, time.Now()) @@ -601,6 +736,11 @@ func createContainerBase(ctx context.Context, t *testing.T, clientPool *pool.Poo } fmt.Println(CID.String()) + if len(apeRules) != 0 { + chainID := "http-aio-" + CID.String()[:8] + addAPEChainToContainer(ctx, t, clientPool, chainID, apeRules, CID) + } + return CID, err } diff --git a/go.mod b/go.mod index 6082ef6..e71aca2 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( 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/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 github.com/bluele/gcache v0.0.2 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/hcl v1.0.0 // 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/cpuid/v2 v2.2.6 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // 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/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect diff --git a/go.sum b/go.sum index 6050ad6..9f54279 100644 --- a/go.sum +++ b/go.sum @@ -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/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/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/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc= git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=