S3: Allow in place copies when bucket versioning is enabled (#7303)
This commit is contained in:
parent
7eb3c572d9
commit
a4b71877e4
@ -108,7 +108,7 @@ class FakeKey(BaseModel, ManagedState):
|
|||||||
storage: Optional[str] = "STANDARD",
|
storage: Optional[str] = "STANDARD",
|
||||||
etag: Optional[str] = None,
|
etag: Optional[str] = None,
|
||||||
is_versioned: bool = False,
|
is_versioned: bool = False,
|
||||||
version_id: str = "null",
|
version_id: Optional[str] = None,
|
||||||
max_buffer_size: Optional[int] = None,
|
max_buffer_size: Optional[int] = None,
|
||||||
multipart: Optional["FakeMultipart"] = None,
|
multipart: Optional["FakeMultipart"] = None,
|
||||||
bucket_name: Optional[str] = None,
|
bucket_name: Optional[str] = None,
|
||||||
@ -170,7 +170,7 @@ class FakeKey(BaseModel, ManagedState):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def version_id(self) -> str:
|
def version_id(self) -> str:
|
||||||
return self._version_id
|
return self._version_id or "null"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self) -> bytes:
|
def value(self) -> bytes:
|
||||||
@ -2674,6 +2674,7 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
|
|||||||
mdirective == "REPLACE",
|
mdirective == "REPLACE",
|
||||||
website_redirect_location,
|
website_redirect_location,
|
||||||
bucket.encryption, # S3 will allow copy in place if the bucket has encryption configured
|
bucket.encryption, # S3 will allow copy in place if the bucket has encryption configured
|
||||||
|
src_key._version_id and bucket.is_versioned,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise CopyObjectMustChangeSomething
|
raise CopyObjectMustChangeSomething
|
||||||
|
@ -783,6 +783,58 @@ def test_copy_object_in_place_with_bucket_encryption():
|
|||||||
assert response["ServerSideEncryption"] == "AES256"
|
assert response["ServerSideEncryption"] == "AES256"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_copy_object_in_place_with_versioning():
|
||||||
|
# If a bucket has versioning enabled, it will allow copy in place
|
||||||
|
client = boto3.client("s3", region_name=DEFAULT_REGION_NAME)
|
||||||
|
bucket_name = "testbucket"
|
||||||
|
client.create_bucket(Bucket=bucket_name)
|
||||||
|
key = "source-key"
|
||||||
|
|
||||||
|
response = client.put_object(
|
||||||
|
Body=b"",
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.put_bucket_versioning(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
VersioningConfiguration={
|
||||||
|
"MFADelete": "Disabled",
|
||||||
|
"Status": "Enabled",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
|
||||||
|
|
||||||
|
response = client.put_object(
|
||||||
|
Body=b"",
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key,
|
||||||
|
)
|
||||||
|
version_id = response["ResponseMetadata"]["HTTPHeaders"]["x-amz-version-id"]
|
||||||
|
assert version_id and version_id != "null"
|
||||||
|
|
||||||
|
response = client.copy_object(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
CopySource={"Bucket": bucket_name, "Key": key, "VersionId": version_id},
|
||||||
|
Key=key,
|
||||||
|
)
|
||||||
|
assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
|
||||||
|
|
||||||
|
response = client.copy_object(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
CopySource={"Bucket": bucket_name, "Key": key, "VersionId": "null"},
|
||||||
|
Key=key,
|
||||||
|
)
|
||||||
|
assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
|
||||||
|
|
||||||
|
response = client.list_object_versions(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Prefix=key,
|
||||||
|
)
|
||||||
|
assert len(response["Versions"]) == 4
|
||||||
|
|
||||||
|
|
||||||
@mock_aws
|
@mock_aws
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"algorithm",
|
"algorithm",
|
||||||
|
Loading…
Reference in New Issue
Block a user