mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-20 22:27:04 +00:00
test(s3/lifecycle): integration coverage for NewerNoncurrentVersions
NewerNoncurrentVersions=N keeps the N most recent noncurrent versions and expires the rest. Distinct from per-version NoncurrentDays — depends on per-version rank, not just per-version age — and routes through routePointerTransition's "needs full expansion" path. Setup: PUT v1, v2, v3, v4 on a versioned bucket (v4 current; v1-v3 noncurrent), backdate v1+v2+v3 so all satisfy the NoncurrentDays>=1 floor, run the worker. Expect v1+v2 expired (older noncurrent), v3 (newest noncurrent within keep=1) and v4 (current) preserved. Helper added: putNewerNoncurrentLifecycle.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// NewerNoncurrentVersions integration scenario.
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// putNewerNoncurrentLifecycle keeps the N most recent noncurrent versions
|
||||
// and expires the rest. NoncurrentDays is required by the AWS XML schema
|
||||
// alongside NewerNoncurrentVersions; keeping it at 1 means the rule fires
|
||||
// as soon as a version moves past the keep-count and is older than 1 day.
|
||||
func putNewerNoncurrentLifecycle(t *testing.T, c *s3.Client, bucket, prefix string, keep int32) {
|
||||
t.Helper()
|
||||
_, err := c.PutBucketLifecycleConfiguration(context.Background(), &s3.PutBucketLifecycleConfigurationInput{
|
||||
Bucket: aws.String(bucket),
|
||||
LifecycleConfiguration: &types.BucketLifecycleConfiguration{
|
||||
Rules: []types.LifecycleRule{
|
||||
{
|
||||
ID: aws.String("keep-newest-noncurrent"),
|
||||
Status: types.ExpirationStatusEnabled,
|
||||
Filter: &types.LifecycleRuleFilter{Prefix: aws.String(prefix)},
|
||||
NoncurrentVersionExpiration: &types.NoncurrentVersionExpiration{
|
||||
NewerNoncurrentVersions: aws.Int32(keep),
|
||||
NoncurrentDays: aws.Int32(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestLifecycleNewerNoncurrentVersions: NewerNoncurrentVersions=1 keeps
|
||||
// only the most recent noncurrent version; older noncurrent versions
|
||||
// must be deleted. PUT v1, v2, v3, v4 (v4 current; v1-v3 noncurrent),
|
||||
// backdate v1, v2, v3, run the worker, expect v3 (newest noncurrent)
|
||||
// and v4 (current) to remain, v1 and v2 to be removed.
|
||||
//
|
||||
// This routes through routePointerTransition's "needs full expansion"
|
||||
// path — distinct from the per-version NoncurrentDays path because
|
||||
// the rule depends on per-version rank, not just per-version age.
|
||||
func TestLifecycleNewerNoncurrentVersions(t *testing.T) {
|
||||
c := s3Client(t)
|
||||
fc, fcClose := filerClient(t)
|
||||
defer fcClose()
|
||||
|
||||
bucket := uniqueBucket("newer-nc")
|
||||
mustCreateBucket(t, c, bucket)
|
||||
putVersioningEnabled(t, c, bucket)
|
||||
putNewerNoncurrentLifecycle(t, c, bucket, "n/", 1)
|
||||
|
||||
const key = "n/obj.txt"
|
||||
puts := make([]string, 4) // versionIds in PUT order
|
||||
for i := range puts {
|
||||
out, err := c.PutObject(context.Background(), &s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket), Key: aws.String(key),
|
||||
Body: strings.NewReader(time.Now().Format(time.RFC3339Nano) + "-" + string(rune('a'+i))),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
puts[i] = aws.ToString(out.VersionId)
|
||||
require.NotEmpty(t, puts[i])
|
||||
}
|
||||
v1, v2, v3, v4 := puts[0], puts[1], puts[2], puts[3]
|
||||
require.NotEqual(t, v1, v2)
|
||||
require.NotEqual(t, v3, v4)
|
||||
|
||||
// Backdate every noncurrent (v1, v2, v3) so all three satisfy the
|
||||
// NoncurrentDays>=1 floor; the keep-newest-1 logic then chooses
|
||||
// which to drop.
|
||||
for _, vid := range []string{v1, v2, v3} {
|
||||
backdateVersionedMtime(t, fc, bucket, key, vid, 30)
|
||||
}
|
||||
|
||||
out := runLifecycleShard(t)
|
||||
t.Logf("shell output:\n%s", out)
|
||||
|
||||
// v1 and v2 (older noncurrent) must be expired.
|
||||
for _, vid := range []string{v1, v2} {
|
||||
vid := vid
|
||||
require.Eventuallyf(t, func() bool {
|
||||
_, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
||||
Bucket: aws.String(bucket), Key: aws.String(key), VersionId: aws.String(vid),
|
||||
})
|
||||
return err != nil
|
||||
}, 30*time.Second, 500*time.Millisecond, "older noncurrent version %s must be expired", vid)
|
||||
}
|
||||
|
||||
// v3 (newest noncurrent within the keep=1 window) must remain.
|
||||
_, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
||||
Bucket: aws.String(bucket), Key: aws.String(key), VersionId: aws.String(v3),
|
||||
})
|
||||
require.NoError(t, err, "v3 (newest noncurrent within keep=1) must survive")
|
||||
|
||||
// v4 (current) must remain — current versions are immune to
|
||||
// NoncurrentVersionExpiration regardless of how the keep-count
|
||||
// arithmetic plays out.
|
||||
currentHead, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
||||
Bucket: aws.String(bucket), Key: aws.String(key),
|
||||
})
|
||||
require.NoError(t, err, "current version v4 must remain")
|
||||
require.Equal(t, v4, aws.ToString(currentHead.VersionId))
|
||||
}
|
||||
Reference in New Issue
Block a user