diff --git a/internal/plugin/loopback/storage.go b/internal/plugin/loopback/storage.go index 8ff18ef8e..984ef923c 100644 --- a/internal/plugin/loopback/storage.go +++ b/internal/plugin/loopback/storage.go @@ -7,7 +7,9 @@ import ( "bytes" "context" "crypto/sha256" + "fmt" "io" + "strings" "sync" "time" @@ -232,8 +234,12 @@ func (l *LoopbackStorage) headObject(ctx context.Context, req *plgpb.HeadObjectR return nil, status.Errorf(err.errCode, "%s: %s", op, err.errMsg) } } + var contentLength int64 + if object.contentLength != nil { + contentLength = *object.contentLength + } return &plgpb.HeadObjectResponse{ - ContentLength: *object.contentLength, + ContentLength: contentLength, LastModified: timestamppb.New(*object.lastModified), }, nil } @@ -359,6 +365,19 @@ func (l *LoopbackStorage) putObject(stream plgpb.StoragePluginService_PutObjectS } contentLength := int64(len(objectData)) lastModified := time.Now() + + // Ensure all the directories within the object key exist + parts := strings.Split(request.Request.GetKey(), "/") + tempPath := request.Request.GetBucket().GetBucketPrefix() + for _, p := range parts[:len(parts)-1] { + // Directories should have trailing `/` in the key + tempPath = fmt.Sprintf("%v%v/", tempPath, p) + bucket[ObjectName(tempPath)] = &storagePluginStorageInfo{ + lastModified: &lastModified, + } + } + + // Now insert the object objectPath := ObjectName(request.Request.GetBucket().GetBucketPrefix() + request.Request.GetKey()) bucket[objectPath] = &storagePluginStorageInfo{ DataChunks: objectChunks, diff --git a/internal/plugin/loopback/storage_test.go b/internal/plugin/loopback/storage_test.go index da046c8a9..c7b66902a 100644 --- a/internal/plugin/loopback/storage_test.go +++ b/internal/plugin/loopback/storage_test.go @@ -813,6 +813,23 @@ func TestLoopbackPutObject(t *testing.T) { }, }, }, + { + name: "valid object with dir in key", + dataChunks: []Chunk{ + []byte("THIS IS A MOCKED OBJECT"), + }, + request: &plgpb.PutObjectRequest{ + Data: &plgpb.PutObjectRequest_Request{ + Request: &plgpb.PutObjectMetadata{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: "aws_s3_mock", + Secrets: secrets, + }, + Key: "foo/bar/zoo/mocked_object", + }, + }, + }, + }, { name: "valid object w/ prefix", dataChunks: []Chunk{ @@ -948,7 +965,7 @@ func TestLoopbackStoragePlugin(t *testing.T) { Data: &plgpb.PutObjectRequest_Request{ Request: &plgpb.PutObjectMetadata{ Bucket: bucket, - Key: "mock_object", + Key: "dir1/mock_object", }, }, }) @@ -969,9 +986,19 @@ func TestLoopbackStoragePlugin(t *testing.T) { require.NoError(err) assert.EqualValues(hash.Sum(nil), putResponse.GetChecksumSha_256()) + // Check directory was created headResponse, err := plg.HeadObject(context.Background(), &plgpb.HeadObjectRequest{ Bucket: bucket, - Key: "mock_object", + Key: "dir1/", + }) + require.NoError(err) + require.NotNil(headResponse) + require.NotNil(headResponse.LastModified) + require.Equal(int64(0), headResponse.ContentLength) + + headResponse, err = plg.HeadObject(context.Background(), &plgpb.HeadObjectRequest{ + Bucket: bucket, + Key: "dir1/mock_object", }) require.NoError(err) require.NotNil(headResponse) @@ -980,7 +1007,7 @@ func TestLoopbackStoragePlugin(t *testing.T) { getObjectStream, err := client.GetObject(context.Background(), &plgpb.GetObjectRequest{ Bucket: bucket, - Key: "mock_object", + Key: "dir1/mock_object", }) assert.NoError(err)