Upstream versity/versitygw state of the touched files before 63922ed89d
This commit is contained in:
1 parent
5f0e2e55ba
commit
edbb6c2d74
4 files changed
+2030
No files matched your search
@@ -0,0 +1,497 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
"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 DeleteObjects_non_existing_bucket(s *S3Conf) error {
|
||||
testName := "DeleteObjects_non_existing_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
bckt := getBucketName()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bckt,
|
||||
Delete: &types.Delete{
|
||||
Objects: []types.ObjectIdentifier{{Key: getPtr("obj1")}, {Key: getPtr("obj2")}},
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket))
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObjects_empty_input(s *S3Conf) error {
|
||||
testName := "DeleteObjects_empty_input"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
contents, err := putObjects(s3client, []string{"foo", "bar", "baz"}, bucket)
|
||||
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{},
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out.Deleted) != 0 {
|
||||
return fmt.Errorf("expected deleted object count 0, instead got %v",
|
||||
len(out.Deleted))
|
||||
}
|
||||
if len(out.Errors) != 0 {
|
||||
return fmt.Errorf("expected 0 errors, instead got %v", len(out.Errors))
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
res, err := s3client.ListObjects(ctx, &s3.ListObjectsInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !compareObjects(contents, res.Contents) {
|
||||
return fmt.Errorf("expected the output to be %v, instead got %v",
|
||||
contents, res.Contents)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObjects_non_existing_objects(s *S3Conf) error {
|
||||
testName := "DeleteObjects_empty_input"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
delObjects := []types.ObjectIdentifier{{Key: getPtr("obj1")}, {Key: getPtr("obj2")}}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{
|
||||
Objects: delObjects,
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out.Deleted) != 2 {
|
||||
return fmt.Errorf("expected deleted object count 2, instead got %v",
|
||||
len(out.Deleted))
|
||||
}
|
||||
if len(out.Errors) != 0 {
|
||||
return fmt.Errorf("expected 0 errors, instead got %v, %v",
|
||||
len(out.Errors), out.Errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObjects_success(s *S3Conf) error {
|
||||
testName := "DeleteObjects_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
objects, objToDel := []string{"obj1", "obj2", "obj3"}, []string{"foo", "bar", "baz"}
|
||||
contents, err := putObjects(s3client, append(objToDel, objects...), bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delObjects := []types.ObjectIdentifier{}
|
||||
delResult := []types.DeletedObject{}
|
||||
for _, key := range objToDel {
|
||||
k := key
|
||||
delObjects = append(delObjects, types.ObjectIdentifier{Key: &k})
|
||||
delResult = append(delResult, types.DeletedObject{Key: &k})
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{
|
||||
Objects: delObjects,
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out.Deleted) != 3 {
|
||||
return fmt.Errorf("expected deleted object count 3, instead got %v",
|
||||
len(out.Deleted))
|
||||
}
|
||||
if len(out.Errors) != 0 {
|
||||
return fmt.Errorf("expected 2 errors, instead got %v",
|
||||
len(out.Errors))
|
||||
}
|
||||
|
||||
if !compareDelObjects(delResult, out.Deleted) {
|
||||
return fmt.Errorf("unexpected deleted output")
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
res, err := s3client.ListObjects(ctx, &s3.ListObjectsInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !compareObjects(contents[3:], res.Contents) {
|
||||
return fmt.Errorf("expected the output to be %v, instead got %v",
|
||||
contents[3:], res.Contents)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteObjects_quiet_mode checks that a quiet-mode request deletes the
|
||||
// objects but lists none of them, since quiet mode reports only the objects
|
||||
// that could not be deleted.
|
||||
func DeleteObjects_quiet_mode(s *S3Conf) error {
|
||||
testName := "DeleteObjects_quiet_mode"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
contents, err := putObjects(s3client, []string{"foo", "bar", "baz"}, bucket)
|
||||
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: getPtr("bar")}, {Key: getPtr("baz")}},
|
||||
Quiet: getBoolPtr(true),
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out.Deleted) != 0 {
|
||||
return fmt.Errorf("expected no deleted objects in quiet mode, instead got %v",
|
||||
len(out.Deleted))
|
||||
}
|
||||
if len(out.Errors) != 0 {
|
||||
return fmt.Errorf("expected no errors, instead got %v", len(out.Errors))
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
res, err := s3client.ListObjects(ctx, &s3.ListObjectsInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !compareObjects(contents[2:], res.Contents) {
|
||||
return fmt.Errorf("expected the output to be %v, instead got %v",
|
||||
contents[2:], res.Contents)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteObjects_iam_mixed_denials_and_success covers a single batch mixing
|
||||
// every DeleteObjects outcome at once: a key the identity policy denies, a
|
||||
// governance-locked key with no bypass, and keys the caller may freely
|
||||
// delete. All three outcomes land in one response — no top-level error —
|
||||
// with Deleted and Errors each preserving the order the keys were
|
||||
// requested in.
|
||||
func DeleteObjects_iam_mixed_denials_and_success(s *S3Conf) error {
|
||||
testName := "DeleteObjects_iam_mixed_denials_and_success"
|
||||
return s3IAMActionHandler(s, testName, func(root *iam.Client, bucket string) error {
|
||||
for _, key := range []string{"allowed/one", "allowed/two", "denied/one"} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s.GetClient().PutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &key})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := putGovernanceLockedObject(s, bucket, "locked/one"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, cleanup, err := newS3IAMUser(root, s, map[string]string{
|
||||
"p": policyDoc(accessStatement{
|
||||
Effect: "Allow", Action: actS3DeleteObject,
|
||||
Resource: []string{objectArn(bucket, "allowed/*"), objectArn(bucket, "locked/*")},
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
delObjects := []types.ObjectIdentifier{
|
||||
{Key: getPtr("allowed/one")},
|
||||
{Key: getPtr("denied/one")},
|
||||
{Key: getPtr("locked/one")},
|
||||
{Key: getPtr("allowed/two")},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := user.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected DeleteObjects to succeed with per-object denials, not fail outright: %w", err)
|
||||
}
|
||||
|
||||
if err := checkDeletedKeysInOrder(out.Deleted, []string{"allowed/one", "allowed/two"}); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkDeleteObjectsErrsInOrder(out.Errors, []keyDenial{
|
||||
{"denied/one", wantImplicitDeny(user.arn, actS3DeleteObject, objectArn(bucket, "denied/one"))},
|
||||
{"locked/one", s3err.GetAPIError(s3err.ErrObjectLocked)},
|
||||
})
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
// DeleteObjects_iam_all_access_denied covers a batch where the identity
|
||||
// policy grants nothing at all: the call itself still succeeds — no
|
||||
// top-level error — with every object reported denied in Errors, in
|
||||
// request order, and nothing in Deleted.
|
||||
func DeleteObjects_iam_all_access_denied(s *S3Conf) error {
|
||||
testName := "DeleteObjects_iam_all_access_denied"
|
||||
return s3IAMActionHandler(s, testName, func(root *iam.Client, bucket string) error {
|
||||
for _, key := range []string{"one", "two", "three"} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s.GetClient().PutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &key})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
user, cleanup, err := newS3IAMUser(root, s, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
delObjects := []types.ObjectIdentifier{
|
||||
{Key: getPtr("one")}, {Key: getPtr("two")}, {Key: getPtr("three")},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := user.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected DeleteObjects to succeed with per-object denials, not fail outright: %w", err)
|
||||
}
|
||||
|
||||
if len(out.Deleted) != 0 {
|
||||
return fmt.Errorf("expected nothing deleted, got %+v", out.Deleted)
|
||||
}
|
||||
return checkDeleteObjectsErrsInOrder(out.Errors, []keyDenial{
|
||||
{"one", wantImplicitDeny(user.arn, actS3DeleteObject, objectArn(bucket, "one"))},
|
||||
{"two", wantImplicitDeny(user.arn, actS3DeleteObject, objectArn(bucket, "two"))},
|
||||
{"three", wantImplicitDeny(user.arn, actS3DeleteObject, objectArn(bucket, "three"))},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteObjects_iam_all_locked covers a batch where every object is
|
||||
// governance-locked and none is deleted: the call still succeeds — no
|
||||
// top-level error — with every object reported denied in Errors, in
|
||||
// request order, and nothing in Deleted. Omitting the bypass header
|
||||
// entirely reports the generic object-lock message; sending the header
|
||||
// without s3:BypassGovernanceRetention reports the specific AccessDenied
|
||||
// naming that action instead — the same distinction the single-object
|
||||
// DELETE path makes, now confirmed for the batch path too.
|
||||
func DeleteObjects_iam_all_locked(s *S3Conf) error {
|
||||
testName := "DeleteObjects_iam_all_locked"
|
||||
return s3IAMActionHandler(s, testName, func(root *iam.Client, bucket string) error {
|
||||
for _, key := range []string{"locked/one", "locked/two"} {
|
||||
if err := putGovernanceLockedObject(s, bucket, key); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
user, cleanup, err := newS3IAMUser(root, s, map[string]string{
|
||||
"p": policyDoc(accessStatement{
|
||||
Effect: "Allow", Action: actS3DeleteObject, Resource: objectsArn(bucket),
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
delObjects := []types.ObjectIdentifier{{Key: getPtr("locked/one")}, {Key: getPtr("locked/two")}}
|
||||
|
||||
// No bypass header at all: the generic object-lock message.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := user.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected DeleteObjects to succeed with per-object denials, not fail outright: %w", err)
|
||||
}
|
||||
if len(out.Deleted) != 0 {
|
||||
return fmt.Errorf("expected nothing deleted, got %+v", out.Deleted)
|
||||
}
|
||||
if err := checkDeleteObjectsErrsInOrder(out.Errors, []keyDenial{
|
||||
{"locked/one", s3err.GetAPIError(s3err.ErrObjectLocked)},
|
||||
{"locked/two", s3err.GetAPIError(s3err.ErrObjectLocked)},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("without bypass header: %w", err)
|
||||
}
|
||||
|
||||
// Bypass header sent, but the identity policy doesn't grant
|
||||
// s3:BypassGovernanceRetention: a specific AccessDenied naming that
|
||||
// action, not the generic object-lock message.
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err = user.client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects},
|
||||
BypassGovernanceRetention: getPtr(true),
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected DeleteObjects to succeed with per-object denials, not fail outright: %w", err)
|
||||
}
|
||||
if len(out.Deleted) != 0 {
|
||||
return fmt.Errorf("expected nothing deleted, got %+v", out.Deleted)
|
||||
}
|
||||
if err := checkDeleteObjectsErrsInOrder(out.Errors, []keyDenial{
|
||||
{"locked/one", wantImplicitDeny(user.arn, actS3BypassGovernance, objectArn(bucket, "locked/one"))},
|
||||
{"locked/two", wantImplicitDeny(user.arn, actS3BypassGovernance, objectArn(bucket, "locked/two"))},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("with bypass header, no permission: %w", err)
|
||||
}
|
||||
return nil
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
// DeleteObjects_key_limit pins the S3 limit of 1000 keys per request: a
|
||||
// batch of exactly 1000 succeeds, and 1001 is rejected with MalformedXML
|
||||
// before any of the keys is deleted.
|
||||
func DeleteObjects_key_limit(s *S3Conf) error {
|
||||
testName := "DeleteObjects_key_limit"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
delObjects := make([]types.ObjectIdentifier, 1001)
|
||||
for i := range delObjects {
|
||||
delObjects[i] = types.ObjectIdentifier{Key: getPtr(fmt.Sprintf("key-%d", i))}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects[:1000]},
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("expected a 1000 key delete to succeed: %w", err)
|
||||
}
|
||||
if len(out.Deleted) != 1000 {
|
||||
return fmt.Errorf("expected 1000 deleted objects, instead got %v", len(out.Deleted))
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{Objects: delObjects},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrMalformedXML)); err != nil {
|
||||
return fmt.Errorf("expected 1001 keys to be rejected: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteObjects_invalid_object_keys(s *S3Conf) error {
|
||||
testName := "DeleteObjects_invalid_object_keys"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj, otherObj := "my-obj", "other-obj"
|
||||
_, err := putObjects(s3client, []string{obj, otherObj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Every key resolves outside the bucket or to the bucket itself;
|
||||
// the ones climbing back into it name obj. A request holding any of
|
||||
// them is rejected as a whole, so its valid key isn't deleted
|
||||
// either.
|
||||
for _, key := range []string{
|
||||
".",
|
||||
"..",
|
||||
"/",
|
||||
"../",
|
||||
"../../../etc/passwd",
|
||||
"../" + bucket + "/" + obj,
|
||||
"dir/../../" + bucket + "/" + obj,
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: &bucket,
|
||||
Delete: &types.Delete{
|
||||
Objects: []types.ObjectIdentifier{
|
||||
{Key: &otherObj},
|
||||
{Key: &key},
|
||||
},
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrBadRequest)); err != nil {
|
||||
return fmt.Errorf("key %q: %w", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range []string{obj, otherObj} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("head %v: %w", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# Copyright 2026 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.
|
||||
|
||||
load ./bats-support/load
|
||||
load ./bats-assert/load
|
||||
|
||||
source ./tests/drivers/create_bucket/create_bucket_rest.sh
|
||||
source ./tests/drivers/delete_objects/delete_objects_rest.sh
|
||||
source ./tests/setup.sh
|
||||
|
||||
# tags: curl, DeleteObjects, malformed-message
|
||||
@test "REST - DeleteObjects - missing payload" {
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run send_rest_go_command_expect_error "400" "MissingRequestBodyError" "is empty" "-bucketName" "$bucket_name" "-method" "POST" "-query" "delete" "-contentMD5"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects, malformed-message
|
||||
@test "REST - DeleteObjects - no objects added to payload" {
|
||||
if [ "$DIRECT" != "true" ]; then
|
||||
skip "https://github.com/versity/versitygw/issues/2109"
|
||||
fi
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run send_rest_go_command_expect_error "400" "MalformedXML" "was not well-formed" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" "-contentMD5"
|
||||
assert_success
|
||||
|
||||
run get_object_rest "$bucket_name" "$file_name" "$TEST_FILE_FOLDER/$file_name-copy"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects
|
||||
@test "REST - DeleteObjects - nonexistent key" {
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
local incorrect_file_name="${file_name}a"
|
||||
run send_rest_go_command_callback "200" "check_xml_element_contains" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=$incorrect_file_name" "--" "$incorrect_file_name" "DeleteResult" "Deleted" "Key"
|
||||
assert_success
|
||||
|
||||
run check_file_integrity "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name" "$TEST_FILE_FOLDER/${file_name}-copy"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects
|
||||
@test "REST - DeleteObjects - ETag mismatch" {
|
||||
if [ "$DIRECT" != "true" ]; then
|
||||
skip "https://github.com/versity/versitygw/issues/2112"
|
||||
fi
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run send_rest_go_command_callback "200" "check_delete_objects_precondition_error" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=$file_name;eTag=abc" "--" "$file_name"
|
||||
assert_success
|
||||
|
||||
run check_file_integrity "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name" "$TEST_FILE_FOLDER/${file_name}-copy"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects
|
||||
@test "REST - DeleteObject - ETag match" {
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run get_etag_rest "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
etag="$output"
|
||||
|
||||
run send_rest_go_command_callback "200" "check_xml_element_contains" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=$file_name;eTag=$etag" "--" "$file_name" "DeleteResult" "Deleted" "Key"
|
||||
assert_success
|
||||
|
||||
run verify_object_not_found "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects, versioning
|
||||
@test "REST - DeleteObjects - version ID mismatch" {
|
||||
if [ "$DIRECT" != "true" ]; then
|
||||
skip "https://github.com/versity/versitygw/issues/2119"
|
||||
fi
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
incorrect_version="abc"
|
||||
run send_rest_go_command_callback "200" "check_delete_objects_version_error" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=$file_name;versionId=$incorrect_version" "--" "$file_name" "$incorrect_version"
|
||||
assert_success
|
||||
|
||||
run check_file_integrity "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name" "$TEST_FILE_FOLDER/${file_name}-copy"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects, versioning
|
||||
@test "REST - DeleteObjects - version ID" {
|
||||
if [ "$RECREATE_BUCKETS" == "false" ]; then
|
||||
skip "cannot change versioning status for static buckets"
|
||||
fi
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_bucket_versioning_rest "$bucket_name" "Enabled"
|
||||
assert_success
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run send_rest_go_command_callback "200" "parse_version_id" "-bucketName" "$bucket_name" "-query" "versions" "--" "true"
|
||||
assert_success
|
||||
version_id="$output"
|
||||
|
||||
run send_rest_go_command_callback "200" "check_xml_element_contains" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=$file_name;versionId=$version_id" "--" "$file_name" "DeleteResult" "Deleted" "Key"
|
||||
assert_success
|
||||
|
||||
run verify_object_not_found "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# tags: curl, DeleteObjects
|
||||
@test "REST - DeleteObjects - quiet mode" {
|
||||
run setup_bucket_and_file_v3 "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
read -r bucket_name file_name <<< "$output"
|
||||
|
||||
run put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
|
||||
run send_rest_go_command_callback "200" "check_for_empty_or_nonexistent_element" "-bucketName" "$bucket_name" "-commandType" "deleteObjects" \
|
||||
"-contentMD5" "-objectsToDelete" "key=${file_name}" "-deleteObjectsQuietMode" "--" "DeleteResult"
|
||||
assert_success
|
||||
|
||||
run verify_object_not_found "$bucket_name" "$file_name"
|
||||
assert_success
|
||||
}
|
||||
Reference in new issue
Block a user