a4edbdd005
This changes metabase behavior such that the latest object will be the committed object which may result in a new version assigned to the objects. Change-Id: I7a731d7db4696caba75fff65107a248569b6111f
1340 lines
47 KiB
Go
1340 lines
47 KiB
Go
// Copyright (C) 2022 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package metabase_test
|
|
|
|
import (
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"storj.io/common/testcontext"
|
|
"storj.io/common/testrand"
|
|
"storj.io/storj/satellite/metabase"
|
|
"storj.io/storj/satellite/metabase/metabasetest"
|
|
)
|
|
|
|
func TestBeginCopyObject(t *testing.T) {
|
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
obj := metabasetest.RandObjectStream()
|
|
|
|
for _, test := range metabasetest.InvalidObjectLocations(obj.Location()) {
|
|
test := test
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
metabasetest.BeginCopyObject{
|
|
Opts: metabase.BeginCopyObject{
|
|
ObjectLocation: test.ObjectLocation,
|
|
},
|
|
ErrClass: test.ErrClass,
|
|
ErrText: test.ErrText,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
}
|
|
|
|
t.Run("begin copy object", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
expectedRawObjects := []metabase.RawObject{}
|
|
expectedRawSegments := []metabase.RawSegment{}
|
|
|
|
for _, expectedVersion := range []metabase.Version{1, 2, 3, 11} {
|
|
obj.ObjectKey = metabasetest.RandObjectKey()
|
|
obj.StreamID = testrand.UUID()
|
|
obj.Version = expectedVersion
|
|
expectedObject, expectedSegments := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
},
|
|
}.Run(ctx, t, db, obj, 10)
|
|
|
|
expectedRawObjects = append(expectedRawObjects, metabase.RawObject(expectedObject))
|
|
|
|
var encKeyAndNonces []metabase.EncryptedKeyAndNonce
|
|
for _, expectedSegment := range expectedSegments {
|
|
encKeyAndNonces = append(encKeyAndNonces, metabase.EncryptedKeyAndNonce{
|
|
EncryptedKeyNonce: expectedSegment.EncryptedKeyNonce,
|
|
EncryptedKey: expectedSegment.EncryptedKey,
|
|
Position: expectedSegment.Position,
|
|
})
|
|
|
|
expectedRawSegments = append(expectedRawSegments, metabase.RawSegment(expectedSegment))
|
|
}
|
|
|
|
metabasetest.BeginCopyObject{
|
|
Opts: metabase.BeginCopyObject{
|
|
ObjectLocation: obj.Location(),
|
|
},
|
|
Result: metabase.BeginCopyObjectResult{
|
|
StreamID: expectedObject.StreamID,
|
|
Version: expectedVersion,
|
|
EncryptedKeysNonces: encKeyAndNonces,
|
|
EncryptionParameters: expectedObject.Encryption,
|
|
},
|
|
}.Check(ctx, t, db)
|
|
}
|
|
|
|
metabasetest.Verify{
|
|
Objects: expectedRawObjects,
|
|
Segments: expectedRawSegments,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestFinishCopyObject(t *testing.T) {
|
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
obj := metabasetest.RandObjectStream()
|
|
newBucketName := "New bucket name"
|
|
|
|
newStreamID := testrand.UUID()
|
|
for _, test := range metabasetest.InvalidObjectStreams(obj) {
|
|
test := test
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: test.ObjectStream,
|
|
NewStreamID: newStreamID,
|
|
},
|
|
ErrClass: test.ErrClass,
|
|
ErrText: test.ErrText,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
}
|
|
|
|
t.Run("invalid NewBucket", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKey: []byte{1, 2, 3},
|
|
NewEncryptedMetadataKeyNonce: testrand.Nonce(),
|
|
NewStreamID: newStreamID,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "NewBucket is missing",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("invalid NewStreamID", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: obj,
|
|
NewBucket: newBucketName,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKey: []byte{1, 2, 3},
|
|
NewEncryptedMetadataKeyNonce: testrand.Nonce(),
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "NewStreamID is missing",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
t.Run("copy to the same StreamID", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: obj,
|
|
NewBucket: newBucketName,
|
|
NewStreamID: obj.StreamID,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "StreamIDs are identical",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("invalid NewEncryptedObjectKey", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewStreamID: newStreamID,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "NewEncryptedObjectKey is missing",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("invalid EncryptedMetadataKeyNonce", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewStreamID: newStreamID,
|
|
NewEncryptedMetadataKey: []byte{0},
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "EncryptedMetadataKeyNonce is missing",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("invalid EncryptedMetadataKey", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKeyNonce: testrand.Nonce(),
|
|
NewStreamID: newStreamID,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "EncryptedMetadataKey is missing",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("empty EncryptedMetadataKey and EncryptedMetadataKeyNonce", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewStreamID: newStreamID,
|
|
},
|
|
// validation pass without EncryptedMetadataKey and EncryptedMetadataKeyNonce
|
|
ErrClass: &metabase.ErrObjectNotFound,
|
|
ErrText: "source object not found",
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("empty EncryptedMetadata with OverrideMetadata=true", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
|
|
OverrideMetadata: true,
|
|
NewEncryptedMetadataKey: []byte{1},
|
|
NewEncryptedMetadataKeyNonce: testrand.Nonce(),
|
|
NewStreamID: newStreamID,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "EncryptedMetadataNonce and EncryptedMetadataEncryptedKey must be not set if EncryptedMetadata is not set",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("empty NewEncryptedMetadataKey and NewEncryptedMetadataKeyNonce with OverrideMetadata=true", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewStreamID: newStreamID,
|
|
|
|
OverrideMetadata: true,
|
|
NewEncryptedMetadata: testrand.BytesInt(256),
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "EncryptedMetadataNonce and EncryptedMetadataEncryptedKey must be set if EncryptedMetadata is set",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("object does not exist", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
newObj := metabasetest.RandObjectStream()
|
|
|
|
newEncryptedMetadataKeyNonce := testrand.Nonce()
|
|
newEncryptedMetadataKey := testrand.Bytes(32)
|
|
newEncryptedKeysNonces := make([]metabase.EncryptedKeyAndNonce, 10)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
NewStreamID: newStreamID,
|
|
ObjectStream: newObj,
|
|
NewSegmentKeys: newEncryptedKeysNonces,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKeyNonce: newEncryptedMetadataKeyNonce,
|
|
NewEncryptedMetadataKey: newEncryptedMetadataKey,
|
|
},
|
|
ErrClass: &metabase.ErrObjectNotFound,
|
|
ErrText: "source object not found",
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
})
|
|
|
|
// Assert that an error occurs when a new object has been put at the source key
|
|
// between BeginCopyObject and FinishCopyObject. (stream_id of source key changed)
|
|
t.Run("source object changed", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
newObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
OverrideEncryptedMetadata: true,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, 2)
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewStreamID: testrand.UUID(),
|
|
NewBucket: newBucketName,
|
|
ObjectStream: metabase.ObjectStream{
|
|
ProjectID: newObj.ProjectID,
|
|
BucketName: newObj.BucketName,
|
|
ObjectKey: newObj.ObjectKey,
|
|
Version: newObj.Version,
|
|
StreamID: testrand.UUID(),
|
|
},
|
|
NewSegmentKeys: []metabase.EncryptedKeyAndNonce{
|
|
metabasetest.RandEncryptedKeyAndNonce(0),
|
|
metabasetest.RandEncryptedKeyAndNonce(1),
|
|
},
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKeyNonce: testrand.Nonce(),
|
|
NewEncryptedMetadataKey: testrand.Bytes(32),
|
|
},
|
|
ErrClass: &metabase.ErrObjectNotFound,
|
|
ErrText: "object was changed during copy",
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("not enough segments", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
numberOfSegments := 10
|
|
|
|
newObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, byte(numberOfSegments))
|
|
|
|
newEncryptedMetadataKeyNonce := testrand.Nonce()
|
|
newEncryptedMetadataKey := testrand.Bytes(32)
|
|
newEncryptedKeysNonces := make([]metabase.EncryptedKeyAndNonce, newObj.SegmentCount-1)
|
|
expectedSegments := make([]metabase.RawSegment, newObj.SegmentCount)
|
|
|
|
for i := 0; i < int(newObj.SegmentCount-1); i++ {
|
|
newEncryptedKeysNonces[i] = metabase.EncryptedKeyAndNonce{
|
|
Position: metabase.SegmentPosition{Index: uint32(i)},
|
|
EncryptedKeyNonce: testrand.Nonce().Bytes(),
|
|
EncryptedKey: testrand.Bytes(32),
|
|
}
|
|
|
|
expectedSegments[i] = metabasetest.DefaultRawSegment(newObj.ObjectStream, metabase.SegmentPosition{Index: uint32(i)})
|
|
expectedSegments[i].EncryptedKeyNonce = newEncryptedKeysNonces[i].EncryptedKeyNonce
|
|
expectedSegments[i].EncryptedKey = newEncryptedKeysNonces[i].EncryptedKey
|
|
expectedSegments[i].PlainOffset = int64(int32(i) * expectedSegments[i].PlainSize)
|
|
expectedSegments[i].EncryptedSize = int32(0)
|
|
}
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewStreamID: newStreamID,
|
|
NewSegmentKeys: newEncryptedKeysNonces,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKeyNonce: newEncryptedMetadataKeyNonce,
|
|
NewEncryptedMetadataKey: newEncryptedMetadataKey,
|
|
},
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
ErrText: "wrong number of segments keys received (received 9, need 10)",
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("wrong segment indexes", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
numberOfSegments := 10
|
|
|
|
newObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, byte(numberOfSegments))
|
|
|
|
newEncryptedMetadataKeyNonce := testrand.Nonce()
|
|
newEncryptedMetadataKey := testrand.Bytes(32)
|
|
newEncryptedKeysNonces := make([]metabase.EncryptedKeyAndNonce, newObj.SegmentCount)
|
|
expectedEncryptedSize := 1060
|
|
expectedSegments := make([]metabase.RawSegment, newObj.SegmentCount)
|
|
|
|
for i := 0; i < int(newObj.SegmentCount); i++ {
|
|
newEncryptedKeysNonces[i] = metabase.EncryptedKeyAndNonce{
|
|
Position: metabase.SegmentPosition{Index: uint32(i + 5)},
|
|
EncryptedKeyNonce: testrand.Nonce().Bytes(),
|
|
EncryptedKey: testrand.Bytes(32),
|
|
}
|
|
|
|
expectedSegments[i] = metabasetest.DefaultRawSegment(newObj.ObjectStream, metabase.SegmentPosition{Index: uint32(i)})
|
|
expectedSegments[i].EncryptedKeyNonce = newEncryptedKeysNonces[i].EncryptedKeyNonce
|
|
expectedSegments[i].EncryptedKey = newEncryptedKeysNonces[i].EncryptedKey
|
|
expectedSegments[i].PlainOffset = int64(int32(i) * expectedSegments[i].PlainSize)
|
|
expectedSegments[i].EncryptedSize = int32(expectedEncryptedSize)
|
|
}
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewStreamID: newStreamID,
|
|
NewBucket: newBucketName,
|
|
ObjectStream: obj,
|
|
NewSegmentKeys: newEncryptedKeysNonces,
|
|
NewEncryptedObjectKey: metabasetest.RandObjectKey(),
|
|
NewEncryptedMetadataKeyNonce: newEncryptedMetadataKeyNonce,
|
|
NewEncryptedMetadataKey: newEncryptedMetadataKey,
|
|
},
|
|
ErrClass: &metabase.Error,
|
|
ErrText: "missing new segment keys for segment 0",
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("returned object", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
expectedRawObjects := []metabase.RawObject{}
|
|
|
|
for _, expectedVersion := range []metabase.Version{1} {
|
|
objStream := metabasetest.RandObjectStream()
|
|
objStream.Version = expectedVersion
|
|
|
|
copyStream := metabasetest.RandObjectStream()
|
|
copyStream.ProjectID = objStream.ProjectID
|
|
copyStream.BucketName = objStream.BucketName
|
|
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: objStream,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, objStream, 0)
|
|
|
|
metadataNonce := testrand.Nonce()
|
|
expectedCopyObject := originalObj
|
|
expectedCopyObject.ObjectKey = copyStream.ObjectKey
|
|
expectedCopyObject.StreamID = copyStream.StreamID
|
|
expectedCopyObject.Version = metabase.DefaultVersion // it will always copy into first available version
|
|
expectedCopyObject.EncryptedMetadataEncryptedKey = testrand.Bytes(32)
|
|
expectedCopyObject.EncryptedMetadataNonce = metadataNonce.Bytes()
|
|
|
|
objectCopy := metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: objStream,
|
|
NewBucket: copyStream.BucketName,
|
|
NewStreamID: copyStream.StreamID,
|
|
NewEncryptedObjectKey: copyStream.ObjectKey,
|
|
NewEncryptedMetadataKey: expectedCopyObject.EncryptedMetadataEncryptedKey,
|
|
NewEncryptedMetadataKeyNonce: metadataNonce,
|
|
},
|
|
Result: expectedCopyObject,
|
|
}.Check(ctx, t, db)
|
|
|
|
require.NotEqual(t, originalObj.CreatedAt, objectCopy.CreatedAt)
|
|
|
|
expectedRawObjects = append(expectedRawObjects, metabase.RawObject(originalObj))
|
|
expectedRawObjects = append(expectedRawObjects, metabase.RawObject(expectedCopyObject))
|
|
}
|
|
|
|
metabasetest.Verify{
|
|
Objects: expectedRawObjects,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object with existing metadata", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
numberOfSegments := 10
|
|
copyStream := metabasetest.RandObjectStream()
|
|
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, byte(numberOfSegments))
|
|
|
|
copyObj, expectedOriginalSegments, expectedCopySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©Stream,
|
|
}.Run(ctx, t, db)
|
|
|
|
var expectedRawSegments []metabase.RawSegment
|
|
expectedRawSegments = append(expectedRawSegments, expectedOriginalSegments...)
|
|
expectedRawSegments = append(expectedRawSegments, expectedCopySegments...)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(originalObj),
|
|
metabase.RawObject(copyObj),
|
|
},
|
|
Segments: expectedRawSegments,
|
|
}.Check(ctx, t, db)
|
|
|
|
// TODO find better names
|
|
copyOfCopyStream := metabasetest.RandObjectStream()
|
|
copyOfCopyObj, _, expectedCopyOfCopySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: copyObj,
|
|
CopyObjectStream: ©OfCopyStream,
|
|
}.Run(ctx, t, db)
|
|
|
|
expectedRawSegments = append(expectedRawSegments, expectedCopyOfCopySegments...)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(originalObj),
|
|
metabase.RawObject(copyObj),
|
|
metabase.RawObject(copyOfCopyObj),
|
|
},
|
|
Segments: expectedRawSegments,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object with new metadata", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
copyStream := metabasetest.RandObjectStream()
|
|
copyStreamNoOverride := metabasetest.RandObjectStream()
|
|
|
|
originalMetadata := testrand.Bytes(64)
|
|
originalMetadataNonce := testrand.Nonce().Bytes()
|
|
originalMetadataEncryptedKey := testrand.Bytes(265)
|
|
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
OverrideEncryptedMetadata: true,
|
|
EncryptedMetadata: originalMetadata,
|
|
EncryptedMetadataNonce: originalMetadataNonce,
|
|
EncryptedMetadataEncryptedKey: originalMetadataEncryptedKey,
|
|
},
|
|
}.Run(ctx, t, db, obj, 0)
|
|
|
|
newMetadata := testrand.Bytes(256)
|
|
newMetadataKey := testrand.Bytes(32)
|
|
newMetadataKeyNonce := testrand.Nonce()
|
|
|
|
// do a copy without OverrideMetadata field set to true,
|
|
// metadata shouldn't be updated even if NewEncryptedMetadata
|
|
// field is set
|
|
copyObjNoOverride, _, _ := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©StreamNoOverride,
|
|
FinishObject: &metabase.FinishCopyObject{
|
|
ObjectStream: originalObj.ObjectStream,
|
|
|
|
NewBucket: copyStreamNoOverride.BucketName,
|
|
NewStreamID: copyStreamNoOverride.StreamID,
|
|
|
|
NewEncryptedObjectKey: copyStreamNoOverride.ObjectKey,
|
|
|
|
OverrideMetadata: false,
|
|
NewEncryptedMetadata: newMetadata,
|
|
NewEncryptedMetadataKeyNonce: newMetadataKeyNonce,
|
|
NewEncryptedMetadataKey: newMetadataKey,
|
|
},
|
|
}.Run(ctx, t, db)
|
|
|
|
require.Equal(t, originalMetadata, copyObjNoOverride.EncryptedMetadata)
|
|
require.Equal(t, newMetadataKey, copyObjNoOverride.EncryptedMetadataEncryptedKey)
|
|
require.Equal(t, newMetadataKeyNonce.Bytes(), copyObjNoOverride.EncryptedMetadataNonce)
|
|
|
|
// do a copy WITH OverrideMetadata field set to true,
|
|
// metadata should be updated to NewEncryptedMetadata
|
|
copyObj, _, _ := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©Stream,
|
|
FinishObject: &metabase.FinishCopyObject{
|
|
ObjectStream: originalObj.ObjectStream,
|
|
|
|
NewBucket: copyStream.BucketName,
|
|
NewStreamID: copyStream.StreamID,
|
|
|
|
NewEncryptedObjectKey: copyStream.ObjectKey,
|
|
|
|
OverrideMetadata: true,
|
|
NewEncryptedMetadata: newMetadata,
|
|
NewEncryptedMetadataKeyNonce: newMetadataKeyNonce,
|
|
NewEncryptedMetadataKey: newMetadataKey,
|
|
},
|
|
}.Run(ctx, t, db)
|
|
|
|
require.Equal(t, newMetadata, copyObj.EncryptedMetadata)
|
|
require.Equal(t, newMetadataKey, copyObj.EncryptedMetadataEncryptedKey)
|
|
require.Equal(t, newMetadataKeyNonce.Bytes(), copyObj.EncryptedMetadataNonce)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(originalObj),
|
|
metabase.RawObject(copyObj),
|
|
metabase.RawObject(copyObjNoOverride),
|
|
},
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object to already existing destination", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
// Test:
|
|
// - 3 objects: objA, objB, objC
|
|
// - copy objB to objA - creating objBprime
|
|
// - check that segments of original objA have been deleted
|
|
// - check that we now have three objects: objBprime, objB, objC
|
|
// - copy objC to objB creating objCprime
|
|
// - check that we now have three objects: objBprime, objCprime, objC
|
|
// - check that objBprime has become an original object, now that its ancestor
|
|
// objB has been overwritten
|
|
|
|
// object that already exists
|
|
objStreamA := metabasetest.RandObjectStream()
|
|
objStreamB := metabasetest.RandObjectStream()
|
|
objStreamC := metabasetest.RandObjectStream()
|
|
|
|
// set same projectID for all
|
|
objStreamB.ProjectID = objStreamA.ProjectID
|
|
objStreamC.ProjectID = objStreamA.ProjectID
|
|
|
|
objA, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: objStreamA,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, objStreamA, 4)
|
|
|
|
objB, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: objStreamB,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, objStreamB, 3)
|
|
|
|
objC, segmentsOfC := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: objStreamC,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, objStreamC, 1)
|
|
|
|
// B' is a copy of B to A
|
|
objStreamBprime := objStreamA
|
|
objStreamBprime.StreamID = testrand.UUID()
|
|
objBprime, expectedSegmentsOfB, expectedSegmentsOfBprime := metabasetest.CreateObjectCopy{
|
|
OriginalObject: objB,
|
|
CopyObjectStream: &objStreamBprime,
|
|
}.Run(ctx, t, db)
|
|
|
|
// check that we indeed overwrote object A
|
|
require.Equal(t, objA.BucketName, objBprime.BucketName)
|
|
require.Equal(t, objA.ProjectID, objBprime.ProjectID)
|
|
require.Equal(t, objA.ObjectKey, objBprime.ObjectKey)
|
|
|
|
require.NotEqual(t, objA.StreamID, objBprime.StreamID)
|
|
|
|
var expectedRawSegments []metabase.RawSegment
|
|
expectedRawSegments = append(expectedRawSegments, expectedSegmentsOfBprime...)
|
|
expectedRawSegments = append(expectedRawSegments, expectedSegmentsOfB...)
|
|
expectedRawSegments = append(expectedRawSegments, metabasetest.SegmentsToRaw(segmentsOfC)...)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(objBprime),
|
|
metabase.RawObject(objB),
|
|
metabase.RawObject(objC),
|
|
},
|
|
Segments: expectedRawSegments,
|
|
}.Check(ctx, t, db)
|
|
|
|
// C' is a copy of C to B
|
|
objStreamCprime := objStreamB
|
|
objStreamCprime.StreamID = testrand.UUID()
|
|
objCprime, _, expectedSegmentsOfCprime := metabasetest.CreateObjectCopy{
|
|
OriginalObject: objC,
|
|
CopyObjectStream: &objStreamCprime,
|
|
}.Run(ctx, t, db)
|
|
|
|
require.Equal(t, objStreamB.BucketName, objCprime.BucketName)
|
|
require.Equal(t, objStreamB.ProjectID, objCprime.ProjectID)
|
|
require.Equal(t, objStreamB.ObjectKey, objCprime.ObjectKey)
|
|
require.NotEqual(t, objB.StreamID, objCprime)
|
|
|
|
// B' should become the original of B and now hold pieces.
|
|
for i := range expectedSegmentsOfBprime {
|
|
expectedSegmentsOfBprime[i].EncryptedETag = nil
|
|
expectedSegmentsOfBprime[i].Pieces = expectedSegmentsOfB[i].Pieces
|
|
}
|
|
|
|
var expectedSegments []metabase.RawSegment
|
|
expectedSegments = append(expectedSegments, expectedSegmentsOfBprime...)
|
|
expectedSegments = append(expectedSegments, expectedSegmentsOfCprime...)
|
|
expectedSegments = append(expectedSegments, metabasetest.SegmentsToRaw(segmentsOfC)...)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(objBprime),
|
|
metabase.RawObject(objCprime),
|
|
metabase.RawObject(objC),
|
|
},
|
|
Segments: expectedSegments,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
// checks that a copy can be copied to it's ancestor location
|
|
t.Run("Copy child to ancestor", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
originalObjStream := metabasetest.RandObjectStream()
|
|
copyObjStream := metabasetest.RandObjectStream()
|
|
// Copy back to original object key.
|
|
// StreamID is independent of key.
|
|
copyBackObjStream := originalObjStream
|
|
copyBackObjStream.StreamID = testrand.UUID()
|
|
|
|
originalObj, originalSegments := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: originalObjStream,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, originalObjStream, 4)
|
|
|
|
copyObj, _, copySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©ObjStream,
|
|
}.Run(ctx, t, db)
|
|
|
|
// Copy the copy back to the source location
|
|
opts := metabase.FinishCopyObject{
|
|
// source
|
|
ObjectStream: copyObj.ObjectStream,
|
|
// destination
|
|
NewBucket: originalObj.BucketName,
|
|
NewEncryptedObjectKey: originalObj.ObjectKey,
|
|
NewStreamID: copyBackObjStream.StreamID,
|
|
OverrideMetadata: false,
|
|
NewSegmentKeys: []metabase.EncryptedKeyAndNonce{
|
|
metabasetest.RandEncryptedKeyAndNonce(0),
|
|
metabasetest.RandEncryptedKeyAndNonce(1),
|
|
metabasetest.RandEncryptedKeyAndNonce(2),
|
|
metabasetest.RandEncryptedKeyAndNonce(3),
|
|
},
|
|
}
|
|
metabasetest.CreateObjectCopy{
|
|
OriginalObject: copyObj,
|
|
CopyObjectStream: ©BackObjStream,
|
|
FinishObject: &opts,
|
|
}.Run(ctx, t, db)
|
|
|
|
// expected object at the location which was previously the original object
|
|
copyBackObj := originalObj
|
|
copyBackObj.Version = originalObj.Version + 1 // copy is placed into next version
|
|
copyBackObj.StreamID = opts.NewStreamID
|
|
|
|
for i := 0; i < 4; i++ {
|
|
copySegments[i].Pieces = originalSegments[i].Pieces
|
|
copySegments[i].InlineData = originalSegments[i].InlineData
|
|
copySegments[i].EncryptedETag = nil // TODO: ETag seems lost after copy
|
|
|
|
originalSegments[i].StreamID = opts.NewStreamID
|
|
originalSegments[i].InlineData = nil
|
|
originalSegments[i].EncryptedKey = opts.NewSegmentKeys[i].EncryptedKey
|
|
originalSegments[i].EncryptedKeyNonce = opts.NewSegmentKeys[i].EncryptedKeyNonce
|
|
originalSegments[i].EncryptedETag = nil // TODO: ETag seems lost after copy
|
|
}
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(copyObj),
|
|
metabase.RawObject(copyBackObj),
|
|
},
|
|
Segments: append(metabasetest.SegmentsToRaw(originalSegments), copySegments...),
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
// checks that a copy ancestor can be copied to itself
|
|
t.Run("Copy ancestor to itself", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
originalObjStream := metabasetest.RandObjectStream()
|
|
copyObjStream := metabasetest.RandObjectStream()
|
|
// Copy back to same object key.
|
|
// StreamID is independent of key.
|
|
copyBackObjStream := originalObjStream
|
|
copyBackObjStream.StreamID = testrand.UUID()
|
|
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: originalObjStream,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, originalObjStream, 4)
|
|
|
|
copyObj, originalSegments, copySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©ObjStream,
|
|
}.Run(ctx, t, db)
|
|
|
|
opts := metabase.FinishCopyObject{
|
|
// source
|
|
ObjectStream: copyObj.ObjectStream,
|
|
// destination
|
|
NewBucket: originalObj.BucketName,
|
|
NewEncryptedObjectKey: originalObj.ObjectKey,
|
|
NewStreamID: copyBackObjStream.StreamID,
|
|
OverrideMetadata: false,
|
|
NewSegmentKeys: []metabase.EncryptedKeyAndNonce{
|
|
metabasetest.RandEncryptedKeyAndNonce(0),
|
|
metabasetest.RandEncryptedKeyAndNonce(1),
|
|
metabasetest.RandEncryptedKeyAndNonce(2),
|
|
metabasetest.RandEncryptedKeyAndNonce(3),
|
|
},
|
|
}
|
|
// Copy the copy back to the source location
|
|
metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: ©BackObjStream,
|
|
FinishObject: &opts,
|
|
}.Run(ctx, t, db)
|
|
|
|
copyBackObj := originalObj
|
|
copyBackObj.Version = originalObj.Version + 1 // copy is placed into next version
|
|
copyBackObj.StreamID = copyBackObjStream.StreamID
|
|
|
|
for i := 0; i < 4; i++ {
|
|
copySegments[i].Pieces = originalSegments[i].Pieces
|
|
copySegments[i].InlineData = originalSegments[i].InlineData
|
|
copySegments[i].EncryptedETag = nil // TODO: ETag seems lost after copy
|
|
|
|
originalSegments[i].StreamID = opts.NewStreamID
|
|
originalSegments[i].InlineData = nil
|
|
originalSegments[i].EncryptedKey = opts.NewSegmentKeys[i].EncryptedKey
|
|
originalSegments[i].EncryptedKeyNonce = opts.NewSegmentKeys[i].EncryptedKeyNonce
|
|
originalSegments[i].EncryptedETag = nil // TODO: ETag seems lost after copy
|
|
}
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(copyObj),
|
|
metabase.RawObject(copyBackObj),
|
|
},
|
|
Segments: append(originalSegments, copySegments...),
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("copied segments has same expires_at as original", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
expiresAt := time.Now().Add(2 * time.Hour)
|
|
|
|
objStream := metabasetest.RandObjectStream()
|
|
copyStream := metabasetest.RandObjectStream()
|
|
copyStream.ProjectID = objStream.ProjectID
|
|
copyStream.BucketName = objStream.BucketName
|
|
|
|
originalObj := metabasetest.CreateExpiredObject(ctx, t, db, objStream, 10, expiresAt)
|
|
|
|
metadataNonce := testrand.Nonce()
|
|
expectedCopyObject := originalObj
|
|
expectedCopyObject.Version = 1 // it'll assign the next available version
|
|
expectedCopyObject.ObjectKey = copyStream.ObjectKey
|
|
expectedCopyObject.StreamID = copyStream.StreamID
|
|
expectedCopyObject.EncryptedMetadataEncryptedKey = testrand.Bytes(32)
|
|
expectedCopyObject.EncryptedMetadataNonce = metadataNonce.Bytes()
|
|
|
|
newEncryptedKeysNonces := make([]metabase.EncryptedKeyAndNonce, originalObj.SegmentCount)
|
|
expectedSegments := make([]metabase.RawSegment, originalObj.SegmentCount)
|
|
|
|
for i := 0; i < int(originalObj.SegmentCount); i++ {
|
|
newEncryptedKeysNonces[i] = metabase.EncryptedKeyAndNonce{
|
|
Position: metabase.SegmentPosition{Index: uint32(i)},
|
|
EncryptedKeyNonce: testrand.Nonce().Bytes(),
|
|
EncryptedKey: testrand.Bytes(32),
|
|
}
|
|
|
|
expectedSegments[i] = metabasetest.DefaultRawSegment(originalObj.ObjectStream, metabase.SegmentPosition{Index: uint32(i)})
|
|
expectedSegments[i].EncryptedKeyNonce = newEncryptedKeysNonces[i].EncryptedKeyNonce
|
|
expectedSegments[i].EncryptedKey = newEncryptedKeysNonces[i].EncryptedKey
|
|
expectedSegments[i].PlainOffset = int64(int32(i) * expectedSegments[i].PlainSize)
|
|
expectedSegments[i].EncryptedSize = int32(0)
|
|
}
|
|
|
|
copyObj := metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: objStream,
|
|
NewBucket: copyStream.BucketName,
|
|
NewStreamID: copyStream.StreamID,
|
|
NewEncryptedObjectKey: copyStream.ObjectKey,
|
|
NewEncryptedMetadataKey: expectedCopyObject.EncryptedMetadataEncryptedKey,
|
|
NewEncryptedMetadataKeyNonce: metadataNonce,
|
|
NewSegmentKeys: newEncryptedKeysNonces,
|
|
},
|
|
Result: expectedCopyObject,
|
|
}.Check(ctx, t, db)
|
|
|
|
var listSegments []metabase.Segment
|
|
|
|
copiedSegments, err := db.ListSegments(ctx, metabase.ListSegments{
|
|
StreamID: copyObj.StreamID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
originalSegments, err := db.ListSegments(ctx, metabase.ListSegments{
|
|
StreamID: originalObj.StreamID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
listSegments = append(listSegments, originalSegments.Segments...)
|
|
listSegments = append(listSegments, copiedSegments.Segments...)
|
|
|
|
for _, v := range listSegments {
|
|
require.Equal(t, expiresAt.Unix(), v.ExpiresAt.Unix())
|
|
}
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(originalObj),
|
|
metabase.RawObject(copyObj),
|
|
},
|
|
Segments: metabasetest.SegmentsToRaw(listSegments),
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object to same destination", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
numberOfSegments := 10
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, byte(numberOfSegments))
|
|
|
|
obj.StreamID = testrand.UUID()
|
|
expectedCopy, _, expectedCopySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: &obj,
|
|
}.Run(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(expectedCopy),
|
|
},
|
|
Segments: expectedCopySegments,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object versioned to same destination", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
// both should be preserved
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
numberOfSegments := 10
|
|
originalObj, _ := metabasetest.CreateTestObject{
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: obj,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
}.Run(ctx, t, db, obj, byte(numberOfSegments))
|
|
|
|
obj.StreamID = testrand.UUID()
|
|
expectedCopy, expectedOriginalSegments, expectedCopySegments := metabasetest.CreateObjectCopy{
|
|
OriginalObject: originalObj,
|
|
CopyObjectStream: &obj,
|
|
|
|
NewVersioned: true,
|
|
}.Run(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(originalObj),
|
|
metabase.RawObject(expectedCopy),
|
|
},
|
|
Segments: append(expectedCopySegments, expectedOriginalSegments...),
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("finish copy object to existing pending destination", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
now := time.Now()
|
|
zombieDeadline := now.Add(24 * time.Hour)
|
|
|
|
sourceObjStream := metabasetest.RandObjectStream()
|
|
destinationObjStream := metabasetest.RandObjectStream()
|
|
destinationObjStream.ProjectID = sourceObjStream.ProjectID
|
|
// testcases:
|
|
// - versions of pending objects
|
|
// - version of committed object
|
|
// - expected copy version
|
|
|
|
testCases := []struct {
|
|
Bucket string
|
|
Key metabase.ObjectKey
|
|
NewBucket string
|
|
NewKey metabase.ObjectKey
|
|
sourcePendingVersions []metabase.Version
|
|
sourceCommitVersion metabase.Version
|
|
sourceCommittedVersion metabase.Version
|
|
destinationPendingVersions []metabase.Version
|
|
destinationCommitVersion metabase.Version
|
|
destionationCommittedVersion metabase.Version
|
|
expectedCopyVersion metabase.Version
|
|
}{
|
|
// the same bucket
|
|
0: {"testbucket", "object", "testbucket", "new-object",
|
|
[]metabase.Version{}, 2, 2,
|
|
[]metabase.Version{}, 1, 1,
|
|
2},
|
|
1: {"testbucket", "object", "testbucket", "new-object",
|
|
[]metabase.Version{}, 1, 1,
|
|
[]metabase.Version{1}, 2, 2,
|
|
3},
|
|
2: {"testbucket", "object", "testbucket", "new-object",
|
|
[]metabase.Version{}, 1, 1,
|
|
[]metabase.Version{1, 3}, 2, 4,
|
|
5},
|
|
3: {"testbucket", "object", "testbucket", "new-object",
|
|
[]metabase.Version{1, 5}, 2, 6,
|
|
[]metabase.Version{1, 3}, 2, 4,
|
|
5},
|
|
4: {"testbucket", "object", "newbucket", "object",
|
|
[]metabase.Version{2, 3}, 1, 4,
|
|
[]metabase.Version{1, 5}, 2, 6,
|
|
7},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
sourceObjStream.BucketName = tc.Bucket
|
|
sourceObjStream.ObjectKey = tc.Key
|
|
destinationObjStream.BucketName = tc.NewBucket
|
|
destinationObjStream.ObjectKey = tc.NewKey
|
|
|
|
var rawObjects []metabase.RawObject
|
|
for _, version := range tc.sourcePendingVersions {
|
|
sourceObjStream.Version = version
|
|
sourceObjStream.StreamID = testrand.UUID()
|
|
metabasetest.CreatePendingObject(ctx, t, db, sourceObjStream, 0)
|
|
|
|
rawObjects = append(rawObjects, metabase.RawObject{
|
|
ObjectStream: sourceObjStream,
|
|
CreatedAt: now,
|
|
Status: metabase.Pending,
|
|
|
|
Encryption: metabasetest.DefaultEncryption,
|
|
ZombieDeletionDeadline: &zombieDeadline,
|
|
})
|
|
}
|
|
sourceObjStream.Version = tc.sourceCommitVersion
|
|
sourceObjStream.StreamID = testrand.UUID()
|
|
sourceObj, _ := metabasetest.CreateTestObject{
|
|
BeginObjectExactVersion: &metabase.BeginObjectExactVersion{
|
|
ObjectStream: sourceObjStream,
|
|
Encryption: metabasetest.DefaultEncryption,
|
|
},
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: sourceObjStream,
|
|
OverrideEncryptedMetadata: true,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
ExpectVersion: tc.sourceCommittedVersion,
|
|
}.Run(ctx, t, db, sourceObjStream, 0)
|
|
|
|
rawObjects = append(rawObjects, metabase.RawObject(sourceObj))
|
|
|
|
for _, version := range tc.destinationPendingVersions {
|
|
destinationObjStream.Version = version
|
|
destinationObjStream.StreamID = testrand.UUID()
|
|
metabasetest.CreatePendingObject(ctx, t, db, destinationObjStream, 0)
|
|
|
|
rawObjects = append(rawObjects, metabase.RawObject{
|
|
ObjectStream: destinationObjStream,
|
|
CreatedAt: now,
|
|
Status: metabase.Pending,
|
|
|
|
Encryption: metabasetest.DefaultEncryption,
|
|
ZombieDeletionDeadline: &zombieDeadline,
|
|
})
|
|
}
|
|
|
|
if tc.destinationCommitVersion != 0 {
|
|
destinationObjStream.StreamID = testrand.UUID()
|
|
destinationObjStream.Version = tc.destinationCommitVersion
|
|
_, _ = metabasetest.CreateTestObject{
|
|
BeginObjectExactVersion: &metabase.BeginObjectExactVersion{
|
|
ObjectStream: destinationObjStream,
|
|
Encryption: metabasetest.DefaultEncryption,
|
|
},
|
|
CommitObject: &metabase.CommitObject{
|
|
ObjectStream: destinationObjStream,
|
|
OverrideEncryptedMetadata: true,
|
|
EncryptedMetadata: testrand.Bytes(64),
|
|
EncryptedMetadataNonce: testrand.Nonce().Bytes(),
|
|
EncryptedMetadataEncryptedKey: testrand.Bytes(265),
|
|
},
|
|
ExpectVersion: tc.destionationCommittedVersion,
|
|
}.Run(ctx, t, db, destinationObjStream, 0)
|
|
}
|
|
|
|
copyObj, expectedOriginalSegments, _ := metabasetest.CreateObjectCopy{
|
|
OriginalObject: sourceObj,
|
|
CopyObjectStream: &destinationObjStream,
|
|
}.Run(ctx, t, db)
|
|
|
|
require.Equal(t, tc.expectedCopyVersion, copyObj.Version)
|
|
|
|
rawObjects = append(rawObjects, metabase.RawObject(copyObj))
|
|
|
|
metabasetest.Verify{
|
|
Objects: rawObjects,
|
|
Segments: expectedOriginalSegments,
|
|
}.Check(ctx, t, db)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("existing object is overwritten", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
initialStream := metabasetest.RandObjectStream()
|
|
initialObject := metabasetest.CreateObject(ctx, t, db, initialStream, 0)
|
|
|
|
conflictObjStream := metabasetest.RandObjectStream()
|
|
conflictObjStream.ProjectID = initialStream.ProjectID
|
|
metabasetest.CreateObject(ctx, t, db, conflictObjStream, 0)
|
|
|
|
newNonce := testrand.Nonce()
|
|
newMetadataKey := testrand.Bytes(265)
|
|
newUUID := testrand.UUID()
|
|
|
|
now := time.Now()
|
|
|
|
copiedObject := metabase.Object{
|
|
ObjectStream: metabase.ObjectStream{
|
|
ProjectID: conflictObjStream.ProjectID,
|
|
BucketName: conflictObjStream.BucketName,
|
|
ObjectKey: conflictObjStream.ObjectKey,
|
|
StreamID: newUUID,
|
|
Version: conflictObjStream.Version + 1,
|
|
},
|
|
CreatedAt: now,
|
|
Status: metabase.CommittedUnversioned,
|
|
Encryption: initialObject.Encryption,
|
|
EncryptedMetadataNonce: newNonce[:],
|
|
EncryptedMetadataEncryptedKey: newMetadataKey,
|
|
}
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: conflictObjStream.BucketName,
|
|
NewStreamID: newUUID,
|
|
ObjectStream: initialStream,
|
|
NewEncryptedObjectKey: conflictObjStream.ObjectKey,
|
|
NewEncryptedMetadataKeyNonce: newNonce,
|
|
NewEncryptedMetadataKey: newMetadataKey,
|
|
},
|
|
Result: copiedObject,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(initialObject),
|
|
metabase.RawObject(copiedObject),
|
|
},
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("existing object is not overwritten, permission denied", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
initialStream := metabasetest.RandObjectStream()
|
|
initialObject := metabasetest.CreateObject(ctx, t, db, initialStream, 0)
|
|
|
|
conflictObjStream := metabasetest.RandObjectStream()
|
|
conflictObjStream.ProjectID = initialStream.ProjectID
|
|
conflictObject := metabasetest.CreateObject(ctx, t, db, conflictObjStream, 0)
|
|
|
|
newNonce := testrand.Nonce()
|
|
newMetadataKey := testrand.Bytes(265)
|
|
newUUID := testrand.UUID()
|
|
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
NewBucket: conflictObjStream.BucketName,
|
|
ObjectStream: initialStream,
|
|
NewStreamID: newUUID,
|
|
|
|
NewEncryptedObjectKey: conflictObjStream.ObjectKey,
|
|
NewEncryptedMetadataKeyNonce: newNonce,
|
|
NewEncryptedMetadataKey: newMetadataKey,
|
|
|
|
NewDisallowDelete: true,
|
|
},
|
|
ErrClass: &metabase.ErrPermissionDenied,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(conflictObject),
|
|
metabase.RawObject(initialObject),
|
|
},
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("versioned targets unversioned and versioned", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
obj.Version = 12000
|
|
unversionedObject := metabasetest.CreateObject(ctx, t, db, obj, 0)
|
|
obj.Version = 13000
|
|
versionedObject := metabasetest.CreateObjectVersioned(ctx, t, db, obj, 0)
|
|
|
|
sourceStream := metabasetest.RandObjectStream()
|
|
sourceStream.ProjectID = obj.ProjectID
|
|
sourceObject := metabasetest.CreateObject(ctx, t, db, sourceStream, 0)
|
|
|
|
newStreamID := testrand.UUID()
|
|
|
|
copiedObject := sourceObject
|
|
copiedObject.ObjectStream.ProjectID = obj.ProjectID
|
|
copiedObject.ObjectStream.BucketName = obj.BucketName
|
|
copiedObject.ObjectStream.ObjectKey = obj.ObjectKey
|
|
copiedObject.ObjectStream.Version = 13001
|
|
copiedObject.ObjectStream.StreamID = newStreamID
|
|
copiedObject.Status = metabase.CommittedVersioned
|
|
|
|
// versioned copy should leave everything else as is
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: sourceStream,
|
|
NewBucket: obj.BucketName,
|
|
NewStreamID: newStreamID,
|
|
NewEncryptedObjectKey: obj.ObjectKey,
|
|
|
|
NewVersioned: true,
|
|
},
|
|
Result: copiedObject,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(unversionedObject),
|
|
metabase.RawObject(versionedObject),
|
|
metabase.RawObject(sourceObject),
|
|
metabase.RawObject(copiedObject),
|
|
},
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
t.Run("unversioned targets unversioned and versioned", func(t *testing.T) {
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
obj.Version = 12000
|
|
metabasetest.CreateObject(ctx, t, db, obj, 0)
|
|
obj.Version = 13000
|
|
versionedObject := metabasetest.CreateObjectVersioned(ctx, t, db, obj, 0)
|
|
|
|
sourceStream := metabasetest.RandObjectStream()
|
|
sourceStream.ProjectID = obj.ProjectID
|
|
sourceObject := metabasetest.CreateObject(ctx, t, db, sourceStream, 0)
|
|
|
|
newStreamID := testrand.UUID()
|
|
|
|
copiedObject := sourceObject
|
|
copiedObject.ObjectStream.ProjectID = obj.ProjectID
|
|
copiedObject.ObjectStream.BucketName = obj.BucketName
|
|
copiedObject.ObjectStream.ObjectKey = obj.ObjectKey
|
|
copiedObject.ObjectStream.Version = 13001
|
|
copiedObject.ObjectStream.StreamID = newStreamID
|
|
copiedObject.Status = metabase.CommittedUnversioned
|
|
|
|
// unversioned copy should only delete the unversioned object
|
|
metabasetest.FinishCopyObject{
|
|
Opts: metabase.FinishCopyObject{
|
|
ObjectStream: sourceStream,
|
|
NewBucket: obj.BucketName,
|
|
NewStreamID: newStreamID,
|
|
NewEncryptedObjectKey: obj.ObjectKey,
|
|
NewVersioned: false,
|
|
},
|
|
Result: copiedObject,
|
|
}.Check(ctx, t, db)
|
|
|
|
metabasetest.Verify{
|
|
Objects: []metabase.RawObject{
|
|
metabase.RawObject(versionedObject),
|
|
metabase.RawObject(sourceObject),
|
|
metabase.RawObject(copiedObject),
|
|
},
|
|
}.Check(ctx, t, db)
|
|
})
|
|
|
|
})
|
|
}
|