mirror of
https://github.com/versity/versitygw.git
synced 2026-01-05 03:24:04 +00:00
3439 lines
95 KiB
Go
3439 lines
95 KiB
Go
// Copyright 2023 Versity Software
|
|
// This file is licensed under the Apache License, Version 2.0
|
|
// (the "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/versity/versitygw/s3err"
|
|
)
|
|
|
|
func Versioning_DeleteBucket_not_empty(s *S3Conf) error {
|
|
testName := "Versioning_DeleteBucket_not_empty"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteBucket(ctx, &s3.DeleteBucketInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrVersionedBucketNotEmpty)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_PutObject_suspended_null_versionId_obj(s *S3Conf) error {
|
|
testName := "Versioning_PutObject_suspended_null_versionId_obj"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
out, err := putObjectWithData(1222, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(out.res.VersionId) != nullVersionId {
|
|
return fmt.Errorf("expected the uploaded object versionId to be %v, instead got %v",
|
|
nullVersionId, getString(out.res.VersionId))
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusSuspended))
|
|
}
|
|
|
|
func Versioning_PutObject_null_versionId_obj(s *S3Conf) error {
|
|
testName := "Versioning_PutObject_null_versionId_obj"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj, lgth := "my-obj", int64(1234)
|
|
out, err := putObjectWithData(lgth, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Enable bucket versioning
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
versions, err := createObjVersions(s3client, bucket, obj, 4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
versions = append(versions, types.ObjectVersion{
|
|
ETag: out.res.ETag,
|
|
IsLatest: getBoolPtr(false),
|
|
Key: &obj,
|
|
Size: &lgth,
|
|
VersionId: &nullVersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
})
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(versions, res.Versions) {
|
|
return fmt.Errorf("expected the listed versions to be %v, instead got %v",
|
|
versions, res.Versions)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_PutObject_overwrite_null_versionId_obj(s *S3Conf) error {
|
|
testName := "Versioning_PutObject_overwrite_null_versionId_obj"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(int64(1233), &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Enable bucket versioning
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
versions, err := createObjVersions(s3client, bucket, obj, 4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set bucket versioning status to Suspended
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusSuspended)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lgth := int64(3200)
|
|
out, err := putObjectWithData(lgth, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(out.res.VersionId) != nullVersionId {
|
|
return fmt.Errorf("expected the uploaded object versionId to be %v, insted got %v",
|
|
nullVersionId, getString(out.res.VersionId))
|
|
}
|
|
|
|
versions[0].IsLatest = getBoolPtr(false)
|
|
|
|
versions = append([]types.ObjectVersion{
|
|
{
|
|
ETag: out.res.ETag,
|
|
IsLatest: getBoolPtr(true),
|
|
Key: &obj,
|
|
Size: &lgth,
|
|
VersionId: &nullVersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
ChecksumType: out.res.ChecksumType,
|
|
},
|
|
}, versions...)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(versions, res.Versions) {
|
|
return fmt.Errorf("expected the listed versions to be %v, instead got %v",
|
|
versions, res.Versions)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_PutObject_success(s *S3Conf) error {
|
|
testName := "Versioning_PutObject_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.PutObject(ctx, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: getPtr("my-obj"),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.VersionId == nil || *res.VersionId == "" {
|
|
return fmt.Errorf("expected the versionId to be returned")
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_CopyObject_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_CopyObject_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dstObj, srcObj := "dst-obj", "src-obj"
|
|
|
|
srcObjLen := int64(2345)
|
|
_, err := putObjectWithData(srcObjLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &srcObj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId", bucket, srcObj)),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_CopyObject_success(s *S3Conf) error {
|
|
testName := "Versioning_CopyObject_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dstObj := "dst-obj"
|
|
srcBucket, srcObj := getBucketName(), "src-obj"
|
|
|
|
if err := setup(s, srcBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
dstObjVersions, err := createObjVersions(s3client, bucket, dstObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObjLen := int64(2345)
|
|
_, err = putObjectWithData(srcObjLen, &s3.PutObjectInput{
|
|
Bucket: &srcBucket,
|
|
Key: &srcObj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v", srcBucket, srcObj)),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := teardown(s, srcBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil || *out.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId in the result")
|
|
}
|
|
|
|
dstObjVersions[0].IsLatest = getBoolPtr(false)
|
|
versions := append([]types.ObjectVersion{
|
|
{
|
|
ETag: out.CopyObjectResult.ETag,
|
|
IsLatest: getBoolPtr(true),
|
|
Key: &dstObj,
|
|
Size: &srcObjLen,
|
|
VersionId: out.VersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
ChecksumType: out.CopyObjectResult.ChecksumType,
|
|
},
|
|
}, dstObjVersions...)
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(versions, res.Versions) {
|
|
return fmt.Errorf("expected the resulting versions to be %v, instead got %v",
|
|
versions, res.Versions)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_CopyObject_non_existing_version_id(s *S3Conf) error {
|
|
testName := "Versioning_CopyObject_non_existing_version_id"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dstBucket, dstObj := getBucketName(), "my-obj"
|
|
srcObj := "my-obj"
|
|
|
|
if err := setup(s, dstBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := createObjVersions(s3client, bucket, srcObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &dstBucket,
|
|
Key: &dstObj,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=01BX5ZZKBKACTAV9WEVGEMMVRZ",
|
|
bucket, srcObj)),
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := teardown(s, dstBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_CopyObject_from_an_object_version(s *S3Conf) error {
|
|
testName := "Versioning_CopyObject_from_an_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
srcBucket, srcObj, dstObj := getBucketName(), "my-obj", "my-dst-obj"
|
|
if err := setup(s, srcBucket, withVersioning(types.BucketVersioningStatusEnabled)); err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObjVersions, err := createObjVersions(s3client, srcBucket, srcObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srcObjVersion := srcObjVersions[0]
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=%v", srcBucket, srcObj, *srcObjVersion.VersionId)),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := teardown(s, srcBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil || *out.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
if out.CopySourceVersionId == nil {
|
|
return fmt.Errorf("expected non nil CopySourceVersionId")
|
|
}
|
|
if *out.CopySourceVersionId != *srcObjVersion.VersionId {
|
|
return fmt.Errorf("expected the SourceVersionId to be %v, instead got %v",
|
|
*srcObjVersion.VersionId, *out.CopySourceVersionId)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
VersionId: out.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.ContentLength == nil {
|
|
return fmt.Errorf("expected non nil ContentLength")
|
|
}
|
|
if res.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if *res.ContentLength != *srcObjVersion.Size {
|
|
return fmt.Errorf("expected the copied object size to be %v, instead got %v",
|
|
*srcObjVersion.Size, *res.ContentLength)
|
|
}
|
|
if *res.VersionId != *out.VersionId {
|
|
return fmt.Errorf("expected the copied object versionId to be %v, instead got %v",
|
|
*out.VersionId, *res.VersionId)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_CopyObject_special_chars(s *S3Conf) error {
|
|
testName := "Versioning_CopyObject_special_chars"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
srcObj, dstBucket, dstObj := "foo?bar", getBucketName(), "bar&foo"
|
|
err := setup(s, dstBucket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObjVersions, err := createObjVersions(s3client, bucket, srcObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObjVersionId := *srcObjVersions[0].VersionId
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=%v", bucket, srcObj, srcObjVersionId)),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.VersionId == nil || *res.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
if res.CopySourceVersionId == nil {
|
|
return fmt.Errorf("expected non nil CopySourceVersionId")
|
|
}
|
|
if *res.CopySourceVersionId != srcObjVersionId {
|
|
return fmt.Errorf("expected the SourceVersionId to be %v, instead got %v",
|
|
srcObjVersionId, *res.CopySourceVersionId)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
VersionId: res.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if *out.VersionId != *res.VersionId {
|
|
return fmt.Errorf("expected the copied object versionId to be %v, instead got %v",
|
|
*res.VersionId, *out.VersionId)
|
|
}
|
|
|
|
err = teardown(s, dstBucket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_HeadObject_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "BadRequest"); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_HeadObject_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "NotFound"); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_HeadObject_invalid_parent(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_invalid_parent"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "not-a-dir"
|
|
r, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
obj = "not-a-dir/bad-obj"
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: r.res.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "NotFound"); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_HeadObject_success(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
r, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: r.res.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.ContentLength == nil {
|
|
return fmt.Errorf("expected non nil ContentLength")
|
|
}
|
|
if out.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if *out.ContentLength != dLen {
|
|
return fmt.Errorf("expected the object content-length to be %v, instead got %v",
|
|
dLen, *out.ContentLength)
|
|
}
|
|
if *out.VersionId != *r.res.VersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
*r.res.VersionId, *out.VersionId)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_HeadObject_without_versionId(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_without_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lastVersion := versions[0]
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(res.VersionId) != *lastVersion.VersionId {
|
|
return fmt.Errorf("expected versionId to be %v, instead got %v",
|
|
*lastVersion.VersionId, getString(res.VersionId))
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_HeadObject_delete_marker(s *S3Conf) error {
|
|
testName := "Versioning_HeadObject_delete_marker"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil || *out.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: out.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "MethodNotAllowed"); err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "NotFound"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObject_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
|
|
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_version_id"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObject_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObject_success(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
r, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get the object by versionId
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: r.res.VersionId,
|
|
})
|
|
defer cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.ContentLength == nil {
|
|
return fmt.Errorf("expected non nil ContentLength")
|
|
}
|
|
if out.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if *out.ContentLength != dLen {
|
|
return fmt.Errorf("expected the object content-length to be %v, instead got %v",
|
|
dLen, *out.ContentLength)
|
|
}
|
|
if *out.VersionId != *r.res.VersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
*r.res.VersionId, *out.VersionId)
|
|
}
|
|
|
|
bdy, err := io.ReadAll(out.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out.Body.Close()
|
|
|
|
outCsum := sha256.Sum256(bdy)
|
|
if outCsum != r.csum {
|
|
return fmt.Errorf("incorrect output content")
|
|
}
|
|
|
|
// Get the object without versionId
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
defer cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.ContentLength == nil {
|
|
return fmt.Errorf("expected non nil ContentLength")
|
|
}
|
|
if out.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if *out.ContentLength != dLen {
|
|
return fmt.Errorf("expected the object content-length to be %v, instead got %v",
|
|
dLen, *out.ContentLength)
|
|
}
|
|
if *out.VersionId != *r.res.VersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
*r.res.VersionId, *out.VersionId)
|
|
}
|
|
|
|
bdy, err = io.ReadAll(out.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out.Body.Close()
|
|
|
|
outCsum = sha256.Sum256(bdy)
|
|
if outCsum != r.csum {
|
|
return fmt.Errorf("incorrect output content")
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObject_delete_marker_without_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_delete_marker_without_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(1234, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "NoSuchKey"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_GetObject_delete_marker(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_delete_marker"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dLen := int64(2000)
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(dLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil || *out.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: out.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObject_null_versionId_obj(s *S3Conf) error {
|
|
testName := "Versioning_GetObject_null_versionId_obj"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj, lgth := "my-obj", int64(234)
|
|
out, err := putObjectWithData(lgth, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: &nullVersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.ContentLength == nil {
|
|
return fmt.Errorf("expected non nil ContentLength")
|
|
}
|
|
if res.VersionId == nil {
|
|
return fmt.Errorf("expected non nil VersionId")
|
|
}
|
|
if res.ETag == nil {
|
|
return fmt.Errorf("expected non nil ETag")
|
|
}
|
|
if *res.ContentLength != lgth {
|
|
return fmt.Errorf("expected the Content-Length to be %v, instead got %v",
|
|
lgth, *res.ContentLength)
|
|
}
|
|
if *res.VersionId != nullVersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, insted got %v",
|
|
nullVersionId, *res.VersionId)
|
|
}
|
|
if *res.ETag != *out.res.ETag {
|
|
return fmt.Errorf("expecte the ETag to be %v, instead got %v",
|
|
*out.res.ETag, *res.ETag)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_GetObjectAttributes_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectAttributes_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
ObjectAttributes: []types.ObjectAttributes{
|
|
types.ObjectAttributesEtag,
|
|
},
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_GetObjectAttributes_object_version(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectAttributes_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version := versions[0]
|
|
|
|
getObjAttrs := func(versionId *string) (*s3.GetObjectAttributesOutput, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
ObjectAttributes: []types.ObjectAttributes{
|
|
types.ObjectAttributesEtag,
|
|
},
|
|
})
|
|
cancel()
|
|
return res, err
|
|
}
|
|
|
|
// By specifying the versionId
|
|
res, err := getObjAttrs(version.VersionId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(res.ETag) != strings.Trim(*version.ETag, "\"") {
|
|
return fmt.Errorf("expected the uploaded object ETag to be %v, instead got %v",
|
|
strings.Trim(*version.ETag, "\""), getString(res.ETag))
|
|
}
|
|
if getString(res.VersionId) != *version.VersionId {
|
|
return fmt.Errorf("expected the uploaded versionId to be %v, instead got %v",
|
|
*version.VersionId, getString(res.VersionId))
|
|
}
|
|
|
|
// Without versionId
|
|
res, err = getObjAttrs(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(res.ETag) != strings.Trim(*version.ETag, "\"") {
|
|
return fmt.Errorf("expected the uploaded object ETag to be %v, instead got %v",
|
|
strings.Trim(*version.ETag, "\""), getString(res.ETag))
|
|
}
|
|
if getString(res.VersionId) != *version.VersionId {
|
|
return fmt.Errorf("expected the uploaded object versionId to be %v, instead got %v",
|
|
*version.VersionId, getString(res.VersionId))
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObjectAttributes_delete_marker(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectAttributes_delete_marker"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: res.VersionId,
|
|
ObjectAttributes: []types.ObjectAttributes{
|
|
types.ObjectAttributesEtag,
|
|
},
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, "NoSuchKey"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObject_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(3, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_DeleteObject_delete_object_version(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_delete_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
oLen := int64(1000)
|
|
obj := "my-obj"
|
|
r, err := putObjectWithData(oLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
versionId := r.res.VersionId
|
|
if versionId == nil || *versionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
|
|
_, err = putObjects(s3client, []string{obj}, bucket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.VersionId == nil {
|
|
return fmt.Errorf("expected non nil versionId")
|
|
}
|
|
if *out.VersionId != *versionId {
|
|
return fmt.Errorf("expected deleted object versionId to be %v, instead got %v",
|
|
*versionId, *out.VersionId)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObject_non_existing_object(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_non_existing_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
|
|
ctx, canel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
canel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, canel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("non_existing_version_id"),
|
|
})
|
|
canel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObject_delete_a_delete_marker(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_delete_a_delete_marker"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
oLen := int64(1000)
|
|
obj := "my-obj"
|
|
_, err := putObjectWithData(oLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.DeleteMarker == nil || !*out.DeleteMarker {
|
|
return fmt.Errorf("expected the response DeleteMarker to be true")
|
|
}
|
|
if out.VersionId == nil || *out.VersionId == "" {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: out.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.DeleteMarker == nil || !*res.DeleteMarker {
|
|
return fmt.Errorf("expected the response DeleteMarker to be true")
|
|
}
|
|
if res.VersionId == nil {
|
|
return fmt.Errorf("expected non empty versionId")
|
|
}
|
|
if *res.VersionId != *out.VersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
*out.VersionId, *res.VersionId)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Delete_null_versionId_object(s *S3Conf) error {
|
|
testName := "Versioning_Delete_null_versionId_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj, nObjLgth := "my-obj", int64(3211)
|
|
_, err := putObjectWithData(nObjLgth, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr(nullVersionId),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if getString(res.VersionId) != nullVersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
nullVersionId, getString(res.VersionId))
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_DeleteObject_nested_dir_object(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_nested_dir_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "foo/bar/baz"
|
|
out, err := putObjectWithData(1000, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: out.res.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(res.VersionId) != getString(out.res.VersionId) {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
getString(out.res.VersionId), getString(res.VersionId))
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteBucket(ctx, &s3.DeleteBucketInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Then create the bucket back to not get error on teardown
|
|
if err := setup(s, bucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_DeleteObject_suspended(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObject_suspended"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
versions[0].IsLatest = getBoolPtr(false)
|
|
|
|
err = putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusSuspended)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for range 5 {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.DeleteMarker == nil {
|
|
return fmt.Errorf("expected the delete marker to be true")
|
|
}
|
|
if !*res.DeleteMarker {
|
|
return fmt.Errorf("expected the delete marker to be true, instead got %v",
|
|
*res.DeleteMarker)
|
|
}
|
|
if res.VersionId == nil {
|
|
return fmt.Errorf("expected non nil versionId")
|
|
}
|
|
if *res.VersionId != nullVersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
nullVersionId, *res.VersionId)
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delMarkers := []types.DeleteMarkerEntry{
|
|
{
|
|
IsLatest: getBoolPtr(true),
|
|
Key: &obj,
|
|
VersionId: &nullVersionId,
|
|
},
|
|
}
|
|
|
|
if !compareVersions(versions, res.Versions) {
|
|
return fmt.Errorf("expected the versions to be %v, instead got %v",
|
|
versions, res.Versions)
|
|
}
|
|
if !compareDelMarkers(res.DeleteMarkers, delMarkers) {
|
|
return fmt.Errorf("expected the delete markers to be %v, instead got %v",
|
|
delMarkers, res.DeleteMarkers)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObjects_success(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObjects_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj1, obj2, obj3 := "foo", "bar", "baz"
|
|
|
|
obj1Version, err := createObjVersions(s3client, bucket, obj1, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
obj2Version, err := createObjVersions(s3client, bucket, obj2, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
obj3Version, err := createObjVersions(s3client, bucket, obj3, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
|
Bucket: &bucket,
|
|
Delete: &types.Delete{
|
|
Objects: []types.ObjectIdentifier{
|
|
{
|
|
Key: obj1Version[0].Key,
|
|
VersionId: obj1Version[0].VersionId,
|
|
},
|
|
{
|
|
Key: obj2Version[0].Key,
|
|
},
|
|
{
|
|
Key: obj3Version[0].Key,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delResult := []types.DeletedObject{
|
|
{
|
|
Key: obj1Version[0].Key,
|
|
VersionId: obj1Version[0].VersionId,
|
|
DeleteMarker: getBoolPtr(false),
|
|
},
|
|
{
|
|
Key: obj2Version[0].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
},
|
|
{
|
|
Key: obj3Version[0].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
},
|
|
}
|
|
|
|
if len(out.Errors) != 0 {
|
|
return fmt.Errorf("errors occurred during the deletion: %v",
|
|
out.Errors)
|
|
}
|
|
if !compareDelObjects(delResult, out.Deleted) {
|
|
return fmt.Errorf("expected the deleted objects to be %v, instead got %v",
|
|
delResult, out.Deleted)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
obj2Version[0].IsLatest = getBoolPtr(false)
|
|
obj3Version[0].IsLatest = getBoolPtr(false)
|
|
versions := append(obj2Version, obj3Version...)
|
|
|
|
delMarkers := []types.DeleteMarkerEntry{
|
|
{
|
|
IsLatest: getBoolPtr(true),
|
|
Key: out.Deleted[1].Key,
|
|
VersionId: out.Deleted[1].DeleteMarkerVersionId,
|
|
},
|
|
{
|
|
IsLatest: getBoolPtr(true),
|
|
Key: out.Deleted[2].Key,
|
|
VersionId: out.Deleted[2].DeleteMarkerVersionId,
|
|
},
|
|
}
|
|
if !compareVersions(versions, res.Versions) {
|
|
return fmt.Errorf("expected the resulting versions to be %v, instead got %v",
|
|
versions, res.Versions)
|
|
}
|
|
if !compareDelMarkers(delMarkers, res.DeleteMarkers) {
|
|
return fmt.Errorf("expected the resulting delete markers to be %v, instead got %v",
|
|
delMarkers, res.DeleteMarkers)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObjects_delete_deleteMarkers(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObjects_delete_deleteMarkers"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj1, obj2 := "foo", "bar"
|
|
|
|
obj1Version, err := createObjVersions(s3client, bucket, obj1, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
obj2Version, err := createObjVersions(s3client, bucket, obj2, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
|
Bucket: &bucket,
|
|
Delete: &types.Delete{
|
|
Objects: []types.ObjectIdentifier{
|
|
{
|
|
Key: obj1Version[0].Key,
|
|
},
|
|
{
|
|
Key: obj2Version[0].Key,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delResult := []types.DeletedObject{
|
|
{
|
|
Key: obj1Version[0].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
},
|
|
{
|
|
Key: obj2Version[0].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
},
|
|
}
|
|
|
|
if len(out.Errors) != 0 {
|
|
return fmt.Errorf("errors occurred during the deletion: %v",
|
|
out.Errors)
|
|
}
|
|
if !compareDelObjects(delResult, out.Deleted) {
|
|
return fmt.Errorf("expected the deleted objects to be %v, instead got %v",
|
|
delResult, out.Deleted)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
|
Bucket: &bucket,
|
|
Delete: &types.Delete{
|
|
Objects: []types.ObjectIdentifier{
|
|
{
|
|
Key: out.Deleted[0].Key,
|
|
VersionId: out.Deleted[0].VersionId,
|
|
},
|
|
{
|
|
Key: out.Deleted[1].Key,
|
|
VersionId: out.Deleted[1].VersionId,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(out.Errors) != 0 {
|
|
return fmt.Errorf("errors occurred during the deletion: %v",
|
|
out.Errors)
|
|
}
|
|
|
|
delResult = []types.DeletedObject{
|
|
{
|
|
Key: out.Deleted[0].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
DeleteMarkerVersionId: out.Deleted[0].VersionId,
|
|
VersionId: out.Deleted[0].VersionId,
|
|
},
|
|
{
|
|
Key: out.Deleted[1].Key,
|
|
DeleteMarker: getBoolPtr(true),
|
|
DeleteMarkerVersionId: out.Deleted[1].VersionId,
|
|
VersionId: out.Deleted[1].VersionId,
|
|
},
|
|
}
|
|
|
|
if !compareDelObjects(delResult, res.Deleted) {
|
|
return fmt.Errorf("expected the deleted objects to be %v, instead got %v",
|
|
delResult, res.Deleted)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Multipart_Upload_success(s *S3Conf) error {
|
|
testName := "Versioning_Multipart_Upload_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
out, err := createMp(s3client, bucket, obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
objSize := int64(25 * 1024 * 1024)
|
|
parts, _, err := uploadParts(s3client, objSize, 5, bucket, obj, *out.UploadId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
compParts := []types.CompletedPart{}
|
|
for _, el := range parts {
|
|
compParts = append(compParts, types.CompletedPart{
|
|
ETag: el.ETag,
|
|
PartNumber: el.PartNumber,
|
|
})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
UploadId: out.UploadId,
|
|
MultipartUpload: &types.CompletedMultipartUpload{
|
|
Parts: compParts,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.Key == nil {
|
|
return fmt.Errorf("expected the object key to be %v, instead got nil",
|
|
obj)
|
|
}
|
|
if *res.Key != obj {
|
|
return fmt.Errorf("expected object key to be %v, instead got %v",
|
|
obj, *res.Key)
|
|
}
|
|
if res.Bucket == nil {
|
|
return fmt.Errorf("expected the bucket name to be %v, instead got nil",
|
|
bucket)
|
|
}
|
|
if *res.Bucket != bucket {
|
|
return fmt.Errorf("expected the bucket name to be %v, instead got %v",
|
|
bucket, *res.Bucket)
|
|
}
|
|
if res.ETag == nil || *res.ETag == "" {
|
|
return fmt.Errorf("expected non-empty ETag")
|
|
}
|
|
if res.VersionId == nil || *res.VersionId == "" {
|
|
return fmt.Errorf("expected non-empty versionId")
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
resp, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: res.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.ETag == nil || *resp.ETag == "" {
|
|
return fmt.Errorf("expected (head object) non-empty ETag")
|
|
}
|
|
if *resp.ETag != *res.ETag {
|
|
return fmt.Errorf("expected the uploaded object etag to be %v, instead got %v",
|
|
*res.ETag, *resp.ETag)
|
|
}
|
|
if resp.ContentLength == nil {
|
|
return fmt.Errorf("expected (head object) non nil content length")
|
|
}
|
|
if *resp.ContentLength != int64(objSize) {
|
|
return fmt.Errorf("expected the uploaded object size to be %v, instead got %v",
|
|
objSize, resp.ContentLength)
|
|
}
|
|
if resp.VersionId == nil {
|
|
return fmt.Errorf("expected (head object) non nil versionId")
|
|
}
|
|
if *resp.VersionId != *res.VersionId {
|
|
return fmt.Errorf("expected the versionId to be %v, instead got %v",
|
|
*res.VersionId, *resp.VersionId)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Multipart_Upload_overwrite_an_object(s *S3Conf) error {
|
|
testName := "Versioning_Multipart_Upload_overwrite_an_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, err := createMp(s3client, bucket, obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
objSize := int64(25 * 1024 * 1024)
|
|
parts, _, err := uploadParts(s3client, objSize, 5, bucket, obj, *out.UploadId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
compParts := []types.CompletedPart{}
|
|
for _, el := range parts {
|
|
compParts = append(compParts, types.CompletedPart{
|
|
ETag: el.ETag,
|
|
PartNumber: el.PartNumber,
|
|
})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
UploadId: out.UploadId,
|
|
MultipartUpload: &types.CompletedMultipartUpload{
|
|
Parts: compParts,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.Key == nil {
|
|
return fmt.Errorf("expected the object key to be %v, instead got nil",
|
|
obj)
|
|
}
|
|
if *res.Key != obj {
|
|
return fmt.Errorf("expected object key to be %v, instead got %v",
|
|
obj, *res.Key)
|
|
}
|
|
if res.Bucket == nil {
|
|
return fmt.Errorf("expected the bucket name to be %v, instead got nil",
|
|
bucket)
|
|
}
|
|
if *res.Bucket != bucket {
|
|
return fmt.Errorf("expected the bucket name to be %v, instead got %v",
|
|
bucket, *res.Bucket)
|
|
}
|
|
if res.ETag == nil || *res.ETag == "" {
|
|
return fmt.Errorf("expected non-empty ETag")
|
|
}
|
|
if res.VersionId == nil || *res.VersionId == "" {
|
|
return fmt.Errorf("expected non-empty versionId")
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
resp, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
size := int64(objSize)
|
|
|
|
objVersions[0].IsLatest = getBoolPtr(false)
|
|
versions := append([]types.ObjectVersion{
|
|
{
|
|
Key: &obj,
|
|
VersionId: res.VersionId,
|
|
ETag: res.ETag,
|
|
IsLatest: getBoolPtr(true),
|
|
Size: &size,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
},
|
|
}, objVersions...)
|
|
|
|
if !compareVersions(versions, resp.Versions) {
|
|
return fmt.Errorf("expected the resulting versions to be %v, instead got %v",
|
|
versions, resp.Versions)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_UploadPartCopy_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_UploadPartCopy_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dstObj, srcObj := "dst-obj", "src-obj"
|
|
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &srcObj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mp, err := createMp(s3client, bucket, dstObj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.UploadPartCopy(ctx, &s3.UploadPartCopyInput{
|
|
Bucket: &bucket,
|
|
Key: &dstObj,
|
|
UploadId: mp.UploadId,
|
|
PartNumber: getPtr(int32(1)),
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId",
|
|
bucket, srcObj)),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_UploadPartCopy_non_existing_versionId(s *S3Conf) error {
|
|
testName := "Versioning_UploadPartCopy_non_existing_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
dstBucket, dstObj, srcObj := getBucketName(), "dst-obj", "src-obj"
|
|
|
|
lgth := int64(100)
|
|
_, err := putObjectWithData(lgth, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &srcObj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := setup(s, dstBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
mp, err := createMp(s3client, dstBucket, dstObj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pNumber := int32(1)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.UploadPartCopy(ctx, &s3.UploadPartCopyInput{
|
|
Bucket: &dstBucket,
|
|
Key: &dstObj,
|
|
UploadId: mp.UploadId,
|
|
PartNumber: &pNumber,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=01BX5ZZKBKACTAV9WEVGEMMVS0",
|
|
bucket, srcObj)),
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := teardown(s, dstBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_UploadPartCopy_from_an_object_version(s *S3Conf) error {
|
|
testName := "Versioning_UploadPartCopy_from_an_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
srcObj, dstBucket, obj := "my-obj", getBucketName(), "dst-obj"
|
|
err := setup(s, dstBucket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObjVersions, err := createObjVersions(s3client, bucket, srcObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srcObjVersion := srcObjVersions[0]
|
|
|
|
out, err := createMp(s3client, dstBucket, obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
partNumber := int32(1)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
copyOut, err := s3client.UploadPartCopy(ctx, &s3.UploadPartCopyInput{
|
|
Bucket: &dstBucket,
|
|
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=%v", bucket, srcObj, *srcObjVersion.VersionId)),
|
|
UploadId: out.UploadId,
|
|
Key: &obj,
|
|
PartNumber: &partNumber,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if getString(copyOut.CopySourceVersionId) != getString(srcObjVersion.VersionId) {
|
|
return fmt.Errorf("expected the copy-source-version-id to be %v, instead got %v",
|
|
getString(srcObjVersion.VersionId), getString(copyOut.CopySourceVersionId))
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListParts(ctx, &s3.ListPartsInput{
|
|
Bucket: &dstBucket,
|
|
Key: &obj,
|
|
UploadId: out.UploadId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(res.Parts) != 1 {
|
|
return fmt.Errorf("expected parts to be 1, instead got %v",
|
|
len(res.Parts))
|
|
}
|
|
if res.Parts[0].PartNumber == nil {
|
|
return fmt.Errorf("expected part-number to be non nil")
|
|
}
|
|
if *res.Parts[0].PartNumber != partNumber {
|
|
return fmt.Errorf("expected part-number to be %v, instead got %v",
|
|
partNumber, res.Parts[0].PartNumber)
|
|
}
|
|
if res.Parts[0].Size == nil {
|
|
return fmt.Errorf("expected part size to be non nil")
|
|
}
|
|
if *res.Parts[0].Size != *srcObjVersion.Size {
|
|
return fmt.Errorf("expected part size to be %v, instead got %v",
|
|
*srcObjVersion.Size, res.Parts[0].Size)
|
|
}
|
|
if getString(res.Parts[0].ETag) != getString(copyOut.CopyPartResult.ETag) {
|
|
return fmt.Errorf("expected part etag to be %v, instead got %v",
|
|
getString(copyOut.CopyPartResult.ETag), getString(res.Parts[0].ETag))
|
|
}
|
|
|
|
if err := teardown(s, dstBucket); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Enable_object_lock(s *S3Conf) error {
|
|
testName := "Versioning_Enable_object_lock"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.Status != types.BucketVersioningStatusEnabled {
|
|
return fmt.Errorf("expected the bucket versioning status to be %v, instead got %v",
|
|
types.BucketVersioningStatusEnabled, res.Status)
|
|
}
|
|
|
|
return nil
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_object_lock_not_enabled_on_bucket_creation(s *S3Conf) error {
|
|
testName := "Versioning_not_enabled_on_bucket_creation"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{
|
|
Bucket: &bucket,
|
|
ObjectLockConfiguration: &types.ObjectLockConfiguration{
|
|
ObjectLockEnabled: types.ObjectLockEnabledEnabled,
|
|
Rule: &types.ObjectLockRule{
|
|
DefaultRetention: &types.DefaultRetention{
|
|
Mode: types.ObjectLockRetentionModeCompliance,
|
|
Days: getPtr(int32(10)),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockConfigurationNotAllowed))
|
|
})
|
|
}
|
|
|
|
func Versioning_status_switch_to_suspended_with_object_lock(s *S3Conf) error {
|
|
testName := "Versioning_status_switch_to_suspended_with_object_lock"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
err := putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusSuspended)
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrSuspendedVersioningNotAllowed)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_PutObjectRetention_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectRetention_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rDate := time.Now().Add(time.Hour * 48)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_version_id"),
|
|
Retention: &types.ObjectLockRetention{
|
|
Mode: types.ObjectLockRetentionModeGovernance,
|
|
RetainUntilDate: &rDate,
|
|
},
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_PutObjectRetention_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectRetention_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rDate := time.Now().Add(time.Hour * 48)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
Retention: &types.ObjectLockRetention{
|
|
Mode: types.ObjectLockRetentionModeGovernance,
|
|
RetainUntilDate: &rDate,
|
|
},
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObjectRetention_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectRetention_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectRetention(ctx, &s3.GetObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_GetObjectRetention_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectRetention_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectRetention(ctx, &s3.GetObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Put_GetObjectRetention_success(s *S3Conf) error {
|
|
testName := "Versioning_Put_GetObjectRetention_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
objVersion := objVersions[1]
|
|
|
|
rDate := time.Now().Add(time.Hour * 48)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objVersion.VersionId,
|
|
Retention: &types.ObjectLockRetention{
|
|
Mode: types.ObjectLockRetentionModeGovernance,
|
|
RetainUntilDate: &rDate,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetObjectRetention(ctx, &s3.GetObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objVersion.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.Retention.Mode != types.ObjectLockRetentionModeGovernance {
|
|
return fmt.Errorf("expected the object retention mode to be %v, instead got %v",
|
|
types.ObjectLockRetentionModeGovernance, res.Retention.Mode)
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{{key: getString(objVersion.Key), versionId: getString(objVersion.VersionId)}})
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_PutObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectLegalHold_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_version_id"),
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_PutObjectLegalHold_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectLegalHold_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectLegalHold_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectLegalHold(ctx, &s3.GetObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_version_id"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_GetObjectLegalHold_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectLegalHold_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
_, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectLegalHold(ctx, &s3.GetObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01G65Z755AFWAKHE12NY0CQ9FH"),
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_Put_GetObjectLegalHold_success(s *S3Conf) error {
|
|
testName := "Versioning_Put_GetObjectLegalHold_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
objVersion := objVersions[1]
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objVersion.VersionId,
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetObjectLegalHold(ctx, &s3.GetObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objVersion.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.LegalHold.Status != types.ObjectLockLegalHoldStatusOn {
|
|
return fmt.Errorf("expected the object version legal hold status to be %v, instead got %v",
|
|
types.ObjectLockLegalHoldStatusOn, res.LegalHold.Status)
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: getString(objVersion.Key),
|
|
versionId: getString(objVersion.VersionId),
|
|
removeOnlyLeglHold: true,
|
|
},
|
|
})
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_WORM_obj_version_locked_with_legal_hold(s *S3Conf) error {
|
|
testName := "Versioning_WORM_obj_version_locked_with_legal_hold"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version := objVersions[1]
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(version.VersionId),
|
|
removeOnlyLeglHold: true,
|
|
},
|
|
})
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_WORM_obj_version_locked_with_governance_retention(s *S3Conf) error {
|
|
testName := "Versioning_WORM_obj_version_locked_with_governance_retention"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version := objVersions[0]
|
|
|
|
rDate := time.Now().Add(time.Hour * 48)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
Retention: &types.ObjectLockRetention{
|
|
Mode: types.ObjectLockRetentionModeGovernance,
|
|
RetainUntilDate: &rDate,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(version.VersionId),
|
|
},
|
|
})
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_WORM_obj_version_locked_with_compliance_retention(s *S3Conf) error {
|
|
testName := "Versioning_WORM_obj_version_locked_with_compliance_retention"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objVersions, err := createObjVersions(s3client, bucket, obj, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version := objVersions[0]
|
|
|
|
rDate := time.Now().Add(time.Hour * 48)
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
Retention: &types.ObjectLockRetention{
|
|
Mode: types.ObjectLockRetentionModeCompliance,
|
|
RetainUntilDate: &rDate,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: version.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(version.VersionId),
|
|
isCompliance: true,
|
|
},
|
|
})
|
|
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_WORM_PutObject_overwrite_locked_object(s *S3Conf) error {
|
|
testName := "Versioning_WORM_PutObject_overwrite_locked_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v := versions[0]
|
|
v.IsLatest = getPtr(false)
|
|
|
|
// lock the object with legal hold
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dataLen := int64(10)
|
|
|
|
// overwrite the locked object with a new version
|
|
r, err := putObjectWithData(dataLen, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
version := types.ObjectVersion{
|
|
ETag: r.res.ETag,
|
|
IsLatest: getPtr(true),
|
|
Key: &obj,
|
|
Size: &dataLen,
|
|
VersionId: r.res.VersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
ChecksumType: r.res.ChecksumType,
|
|
}
|
|
|
|
result := []types.ObjectVersion{version, v}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(result, out.Versions) {
|
|
return fmt.Errorf("expected the object versions to be %v, instead got %v", result, out.Versions)
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(v.VersionId),
|
|
removeOnlyLeglHold: true,
|
|
},
|
|
})
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_WORM_CopyObject_overwrite_locked_object(s *S3Conf) error {
|
|
testName := "Versioning_WORM_CopyObject_overwrite_locked_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v := versions[0]
|
|
v.IsLatest = getPtr(false)
|
|
|
|
// lock the object with legal hold
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// create a source object version
|
|
srcObj := "source-object"
|
|
srcVersions, err := createObjVersions(s3client, bucket, srcObj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcVersion := srcVersions[0]
|
|
|
|
// overwrite the locked object with a new version with CopyObject
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
copyResult, err := s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
CopySource: getPtr(fmt.Sprintf("%s/%s", bucket, srcObj)),
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
version := types.ObjectVersion{
|
|
ETag: copyResult.CopyObjectResult.ETag,
|
|
IsLatest: getPtr(true),
|
|
Key: &obj,
|
|
Size: srcVersion.Size,
|
|
VersionId: copyResult.VersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
ChecksumType: copyResult.CopyObjectResult.ChecksumType,
|
|
}
|
|
|
|
result := []types.ObjectVersion{version, v, srcVersion}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(result, out.Versions) {
|
|
return fmt.Errorf("expected the object versions to be %v, instead got %v", result, out.Versions)
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(v.VersionId),
|
|
removeOnlyLeglHold: true,
|
|
},
|
|
})
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_WORM_CompleteMultipartUpload_overwrite_locked_object(s *S3Conf) error {
|
|
testName := "Versioning_WORM_CompleteMultipartUpload_overwrite_locked_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v := versions[0]
|
|
v.IsLatest = getPtr(false)
|
|
|
|
// lock the object with legal hold
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
LegalHold: &types.ObjectLockLegalHold{
|
|
Status: types.ObjectLockLegalHoldStatusOn,
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dataLen := int64(5 * 1024 * 1024)
|
|
|
|
// overwrite the locked object with a new version
|
|
mp, err := createMp(s3client, bucket, obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
parts, _, err := uploadParts(s3client, dataLen, 1, bucket, obj, *mp.UploadId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
part := parts[0]
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
MultipartUpload: &types.CompletedMultipartUpload{
|
|
Parts: []types.CompletedPart{
|
|
{
|
|
ETag: part.ETag,
|
|
PartNumber: part.PartNumber,
|
|
},
|
|
},
|
|
},
|
|
UploadId: mp.UploadId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
version := types.ObjectVersion{
|
|
ETag: res.ETag,
|
|
IsLatest: getPtr(true),
|
|
Key: &obj,
|
|
Size: &dataLen,
|
|
VersionId: res.VersionId,
|
|
StorageClass: types.ObjectVersionStorageClassStandard,
|
|
}
|
|
|
|
result := []types.ObjectVersion{version, v}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !compareVersions(result, out.Versions) {
|
|
return fmt.Errorf("expected the object versions to be %v, instead got %v", result, out.Versions)
|
|
}
|
|
|
|
return cleanupLockedObjects(s3client, bucket, []objToDelete{
|
|
{
|
|
key: obj,
|
|
versionId: getString(v.VersionId),
|
|
removeOnlyLeglHold: true,
|
|
},
|
|
})
|
|
}, withLock())
|
|
}
|
|
|
|
func Versioning_AccessControl_GetObjectVersion(s *S3Conf) error {
|
|
testName := "Versioning_AccessControl_GetObjectVersion"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objData, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
testuser := getUser("user")
|
|
err = createUsers(s, []user{testuser})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
doc := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObject"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
|
Bucket: &bucket,
|
|
Policy: &doc,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userClient := s.getUserClient(testuser)
|
|
|
|
// querying with versionId should return access denied
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objData.res.VersionId,
|
|
})
|
|
defer cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// grant the user s3:GetObjectVersion
|
|
doc = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObjectVersion"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
|
Bucket: &bucket,
|
|
Policy: &doc,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objData.res.VersionId,
|
|
})
|
|
defer cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_AccessControl_HeadObjectVersion(s *S3Conf) error {
|
|
testName := "Versioning_AccessControl_HeadObjectVersion"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
objData, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
testuser := getUser("user")
|
|
err = createUsers(s, []user{testuser})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
doc := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObject"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
|
Bucket: &bucket,
|
|
Policy: &doc,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userClient := s.getUserClient(testuser)
|
|
|
|
// querying with versionId should return access denied
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objData.res.VersionId,
|
|
})
|
|
cancel()
|
|
if err := checkSdkApiErr(err, http.StatusText(http.StatusForbidden)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// grant the user s3:GetObjectVersion
|
|
doc = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObjectVersion"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
|
Bucket: &bucket,
|
|
Policy: &doc,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.HeadObject(ctx, &s3.HeadObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: objData.res.VersionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_AccessControl_object_tagging_policy(s *S3Conf) error {
|
|
testName := "Versioning_AccessControl_PutObjectTagging_policy"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
object := "my-object"
|
|
res, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &object,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
testuser := getUser("user")
|
|
err = createUsers(s, []user{testuser})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userClient := s.getUserClient(testuser)
|
|
|
|
putGetDeleteObjectTagging := func(versionId *string, denyAccess bool) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := userClient.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &object,
|
|
VersionId: versionId,
|
|
Tagging: &types.Tagging{
|
|
TagSet: []types.Tag{
|
|
{Key: getPtr("key"), Value: getPtr("value")},
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if denyAccess {
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &object,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if denyAccess {
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = userClient.DeleteObjectTagging(ctx, &s3.DeleteObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &object,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if denyAccess {
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObjectVersionTagging", "s3:GetObjectVersionTagging", "s3:DeleteObjectVersionTagging"]`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny without versionId
|
|
err = putGetDeleteObjectTagging(nil, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow with versionId
|
|
err = putGetDeleteObjectTagging(res.res.VersionId, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `["s3:PutObjectTagging", "s3:GetObjectTagging", "s3:DeleteObjectTagging"]`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow without versionId
|
|
err = putGetDeleteObjectTagging(nil, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny with versionId
|
|
err = putGetDeleteObjectTagging(res.res.VersionId, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_AccessControl_DeleteObject_policy(s *S3Conf) error {
|
|
testName := "Versioning_AccessControl_DeleteObject_policy"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
testuser := getUser("user")
|
|
err := createUsers(s, []user{testuser})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userClient := s.getUserClient(testuser)
|
|
|
|
delObject := func(versionId *string, denyAccess bool) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := userClient.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if denyAccess {
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
res, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:DeleteObject"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny with versionId
|
|
err = delObject(res.res.VersionId, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow without versionId
|
|
err = delObject(nil, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:DeleteObjectVersion"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// recreate the object
|
|
res, err = putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny without versionId
|
|
err = delObject(nil, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow with versionId
|
|
err = delObject(res.res.VersionId, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_AccessControl_GetObjectAttributes_policy(s *S3Conf) error {
|
|
testName := "Versioning_AccessControl_GetObjectAttributes_policy"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
res, err := putObjectWithData(10, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
testuser := getUser("user")
|
|
err = createUsers(s, []user{testuser})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
userClient := s.getUserClient(testuser)
|
|
|
|
getObjectAttr := func(versionId *string, denyAccess bool) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := userClient.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
ObjectAttributes: types.ObjectAttributesChecksum.Values(),
|
|
})
|
|
cancel()
|
|
if denyAccess {
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
policy := genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObjectAttributes"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny with versionId
|
|
err = getObjectAttr(res.res.VersionId, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow without versionId
|
|
err = getObjectAttr(nil, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
policy = genPolicyDoc("Allow", fmt.Sprintf(`"%s"`, testuser.access), `"s3:GetObjectVersionAttributes"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
|
err = putBucketPolicy(s3client, bucket, policy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// deny without versionId
|
|
err = getObjectAttr(nil, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// allow with versionId
|
|
err = getObjectAttr(res.res.VersionId, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func VersioningDisabled_GetBucketVersioning_not_configured(s *S3Conf) error {
|
|
testName := "VersioningDisabled_GetBucketVersioning_not_configured"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
err := putBucketVersioningStatus(s3client, bucket, types.BucketVersioningStatusEnabled)
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func VersioningDisabled_PutBucketVersioning_not_configured(s *S3Conf) error {
|
|
testName := "VersioningDisabled_PutBucketVersioning_not_configured"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrVersioningNotConfigured)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func Versioning_concurrent_upload_object(s *S3Conf) error {
|
|
testName := "Versioninig_concurrent_upload_object"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-obj"
|
|
versionCount := 5
|
|
// Channel to collect errors
|
|
errCh := make(chan error, versionCount)
|
|
|
|
uploadVersion := func(wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.PutObject(ctx, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
// Send error to the channel
|
|
errCh <- err
|
|
return
|
|
}
|
|
|
|
fmt.Printf("uploaded object successfully: versionId: %v\n", *res.VersionId)
|
|
}
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(versionCount)
|
|
|
|
for range versionCount {
|
|
go uploadVersion(wg)
|
|
}
|
|
|
|
wg.Wait()
|
|
close(errCh)
|
|
|
|
// Check if there were any errors
|
|
for err := range errCh {
|
|
if err != nil {
|
|
fmt.Printf("error uploading an object: %v\n", err.Error())
|
|
return err
|
|
}
|
|
}
|
|
|
|
// List object versions after all uploads
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(res.Versions) != versionCount {
|
|
return fmt.Errorf("expected %v object versions, instead got %v",
|
|
versionCount, len(res.Versions))
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObjectTagging_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectTagging_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_PutObjectTagging_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectTagging_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
Tagging: &types.Tagging{
|
|
TagSet: []types.Tag{{Key: getPtr("key"), Value: getPtr("value")}},
|
|
},
|
|
VersionId: getPtr("01K97XE6PJQ1A4X5TJFDHK4EMC"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_PutObjectTagging_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_PutObjectTagging_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
Tagging: &types.Tagging{
|
|
TagSet: []types.Tag{{Key: getPtr("key"), Value: getPtr("value")}},
|
|
},
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_GetObjectTagging_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_GetObjectTagging_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01K97XE6PJQ1A4X5TJFDHK4EMC"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_DeleteObjectTagging_invalid_versionId(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObjectTagging_invalid_versionId"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObjectTagging(ctx, &s3.DeleteObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("invalid_versionId"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
|
})
|
|
}
|
|
|
|
func Versioning_DeleteObjectTagging_non_existing_object_version(s *S3Conf) error {
|
|
testName := "Versioning_DeleteObjectTagging_non_existing_object_version"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
}, s3client)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err = s3client.DeleteObjectTagging(ctx, &s3.DeleteObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: getPtr("01K97XE6PJQ1A4X5TJFDHK4EMC"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchVersion))
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|
|
|
|
func Versioning_PutGetDeleteObjectTagging_success(s *S3Conf) error {
|
|
testName := "Versioning_PutGetDeleteObjectTagging_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
obj := "my-object"
|
|
versions, err := createObjVersions(s3client, bucket, obj, 5)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
versionId := versions[2].VersionId
|
|
|
|
tagging := types.Tagging{
|
|
TagSet: []types.Tag{
|
|
{Key: getPtr("key"), Value: getPtr("value")},
|
|
},
|
|
}
|
|
|
|
compareVersionId := func(expected, input *string) error {
|
|
if getString(expected) != getString(input) {
|
|
return fmt.Errorf("expected the response versionId to be %s, instead got %s", getString(expected), getString(input))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
Tagging: &tagging,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := compareVersionId(versionId, res.VersionId); err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := s3client.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !areTagsSame(tagging.TagSet, out.TagSet) {
|
|
return fmt.Errorf("expected the object version tags to be %v, instead got %v", tagging.TagSet, out.TagSet)
|
|
}
|
|
if err := compareVersionId(versionId, out.VersionId); err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
resp, err := s3client.DeleteObjectTagging(ctx, &s3.DeleteObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := compareVersionId(versionId, resp.VersionId); err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
r, err := s3client.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
|
Bucket: &bucket,
|
|
Key: &obj,
|
|
VersionId: versionId,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(r.TagSet) != 0 {
|
|
return fmt.Errorf("expected empty tag set, instead got %v", r.TagSet)
|
|
}
|
|
|
|
return nil
|
|
}, withVersioning(types.BucketVersioningStatusEnabled))
|
|
}
|