2020-12-09 12:24:37 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/uuid"
|
|
|
|
"storj.io/storj/satellite/metainfo/metabase"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestDeleteBucketObjects(t *testing.T) {
|
|
|
|
All(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
|
|
obj1 := randObjectStream()
|
|
|
|
obj2 := randObjectStream()
|
|
|
|
obj3 := randObjectStream()
|
|
|
|
objX := randObjectStream()
|
|
|
|
objY := randObjectStream()
|
|
|
|
|
|
|
|
obj2.ProjectID, obj2.BucketName = obj1.ProjectID, obj1.BucketName
|
|
|
|
obj3.ProjectID, obj3.BucketName = obj1.ProjectID, obj1.BucketName
|
|
|
|
objX.ProjectID = obj1.ProjectID
|
|
|
|
objY.BucketName = obj1.BucketName
|
|
|
|
|
|
|
|
t.Run("invalid options", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: metabase.BucketLocation{
|
|
|
|
ProjectID: uuid.UUID{},
|
|
|
|
BucketName: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
|
|
ErrText: "ProjectID missing",
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: metabase.BucketLocation{
|
|
|
|
ProjectID: uuid.UUID{1},
|
|
|
|
BucketName: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
|
|
ErrText: "BucketName missing",
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("empty bucket", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return errors.New("shouldn't be called")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 0,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("one object", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
createObject(ctx, t, db, obj1, 2)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
if len(segments) != 2 {
|
|
|
|
return errors.New("expected 2 segments")
|
|
|
|
}
|
|
|
|
for _, s := range segments {
|
|
|
|
if len(s.Pieces) != 1 {
|
|
|
|
return errors.New("expected 1 piece per segment")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 1,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("empty object", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
createObject(ctx, t, db, obj1, 0)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return errors.New("expected no segments")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// TODO: fix the count for objects without segments
|
|
|
|
// this should be 1.
|
|
|
|
Deleted: 0,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("three objects", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
createObject(ctx, t, db, obj1, 2)
|
|
|
|
createObject(ctx, t, db, obj2, 2)
|
|
|
|
createObject(ctx, t, db, obj3, 2)
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
BatchSize: 2,
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
if len(segments) != 2 && len(segments) != 4 {
|
|
|
|
return errors.New("expected 2 or 4 segments")
|
|
|
|
}
|
|
|
|
for _, s := range segments {
|
|
|
|
if len(s.Pieces) != 1 {
|
|
|
|
return errors.New("expected 1 piece per segment")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 3,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("don't delete non-exact match", func(t *testing.T) {
|
|
|
|
defer DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
createObject(ctx, t, db, obj1, 1)
|
|
|
|
createObject(ctx, t, db, objX, 1)
|
|
|
|
createObject(ctx, t, db, objY, 1)
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
},
|
|
|
|
Deleted: 1,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
Verify{
|
|
|
|
Objects: []metabase.RawObject{
|
|
|
|
{
|
|
|
|
ObjectStream: objX,
|
|
|
|
CreatedAt: now,
|
|
|
|
Status: metabase.Committed,
|
|
|
|
SegmentCount: 1,
|
|
|
|
|
|
|
|
TotalPlainSize: 512,
|
|
|
|
TotalEncryptedSize: 1024,
|
2021-01-14 11:47:29 +00:00
|
|
|
FixedSegmentSize: 512,
|
2020-12-09 12:24:37 +00:00
|
|
|
Encryption: defaultTestEncryption,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ObjectStream: objY,
|
|
|
|
CreatedAt: now,
|
|
|
|
Status: metabase.Committed,
|
|
|
|
SegmentCount: 1,
|
|
|
|
|
|
|
|
TotalPlainSize: 512,
|
|
|
|
TotalEncryptedSize: 1024,
|
2021-01-14 11:47:29 +00:00
|
|
|
FixedSegmentSize: 512,
|
2020-12-09 12:24:37 +00:00
|
|
|
Encryption: defaultTestEncryption,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Segments: []metabase.RawSegment{
|
|
|
|
{
|
|
|
|
StreamID: objX.StreamID,
|
2021-01-25 11:23:43 +00:00
|
|
|
Position: metabase.SegmentPosition{Part: 0, Index: 0},
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
RootPieceID: storj.PieceID{1},
|
|
|
|
Pieces: metabase.Pieces{{Number: 0, StorageNode: storj.NodeID{2}}},
|
|
|
|
EncryptedKey: []byte{3},
|
|
|
|
EncryptedKeyNonce: []byte{4},
|
|
|
|
|
|
|
|
EncryptedSize: 1024,
|
|
|
|
PlainSize: 512,
|
|
|
|
PlainOffset: 0,
|
|
|
|
|
|
|
|
Redundancy: defaultTestRedundancy,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
StreamID: objY.StreamID,
|
2021-01-25 11:23:43 +00:00
|
|
|
Position: metabase.SegmentPosition{Part: 0, Index: 0},
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
RootPieceID: storj.PieceID{1},
|
|
|
|
Pieces: metabase.Pieces{{Number: 0, StorageNode: storj.NodeID{2}}},
|
|
|
|
EncryptedKey: []byte{3},
|
|
|
|
EncryptedKeyNonce: []byte{4},
|
|
|
|
|
|
|
|
EncryptedSize: 1024,
|
|
|
|
PlainSize: 512,
|
|
|
|
PlainOffset: 0,
|
|
|
|
|
|
|
|
Redundancy: defaultTestRedundancy,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|