2 changed files with 175 additions and 0 deletions
@ -0,0 +1,169 @@ |
|||
#!/usr/bin/env python3 |
|||
""" |
|||
Fix S3 tests to handle bucket creation conflicts properly. |
|||
|
|||
This script patches the s3-tests repository to handle BucketAlreadyExists errors |
|||
by retrying with new bucket names instead of failing. |
|||
|
|||
This is needed because SeaweedFS now properly returns BucketAlreadyExists errors |
|||
per S3 specification, but the original s3-tests code doesn't handle this case. |
|||
""" |
|||
|
|||
import os |
|||
import sys |
|||
import re |
|||
|
|||
def patch_s3_tests_init_file(file_path): |
|||
"""Patch the s3tests_boto3/functional/__init__.py file to handle bucket conflicts.""" |
|||
|
|||
if not os.path.exists(file_path): |
|||
print(f"Error: File {file_path} not found") |
|||
return False |
|||
|
|||
print(f"Patching {file_path}...") |
|||
|
|||
with open(file_path, 'r') as f: |
|||
content = f.read() |
|||
|
|||
# Check if already patched |
|||
if 'max_retries = 10' in content: |
|||
print("File already patched, skipping...") |
|||
return True |
|||
|
|||
# Patch get_new_bucket_resource function |
|||
old_resource_func = '''def get_new_bucket_resource(name=None): |
|||
""" |
|||
Get a bucket that exists and is empty. |
|||
|
|||
Always recreates a bucket from scratch. This is useful to also |
|||
reset ACLs and such. |
|||
""" |
|||
s3 = boto3.resource('s3', |
|||
aws_access_key_id=config.main_access_key, |
|||
aws_secret_access_key=config.main_secret_key, |
|||
endpoint_url=config.default_endpoint, |
|||
use_ssl=config.default_is_secure, |
|||
verify=config.default_ssl_verify) |
|||
if name is None: |
|||
name = get_new_bucket_name() |
|||
bucket = s3.Bucket(name) |
|||
bucket_location = bucket.create() |
|||
return bucket''' |
|||
|
|||
new_resource_func = '''def get_new_bucket_resource(name=None): |
|||
""" |
|||
Get a bucket that exists and is empty. |
|||
|
|||
Always recreates a bucket from scratch. This is useful to also |
|||
reset ACLs and such. |
|||
""" |
|||
s3 = boto3.resource('s3', |
|||
aws_access_key_id=config.main_access_key, |
|||
aws_secret_access_key=config.main_secret_key, |
|||
endpoint_url=config.default_endpoint, |
|||
use_ssl=config.default_is_secure, |
|||
verify=config.default_ssl_verify) |
|||
|
|||
# Retry bucket creation if name conflicts occur |
|||
max_retries = 10 |
|||
for attempt in range(max_retries): |
|||
if name is None or attempt > 0: |
|||
name = get_new_bucket_name() |
|||
|
|||
bucket = s3.Bucket(name) |
|||
try: |
|||
bucket_location = bucket.create() |
|||
return bucket |
|||
except ClientError as e: |
|||
error_code = e.response['Error']['Code'] |
|||
if error_code in ['BucketAlreadyExists', 'BucketAlreadyOwnedByYou']: |
|||
# Bucket name conflict, try again with a new name |
|||
if attempt == max_retries - 1: |
|||
raise Exception(f"Failed to create unique bucket after {max_retries} attempts") |
|||
continue |
|||
else: |
|||
# Other error, re-raise |
|||
raise''' |
|||
|
|||
# Patch get_new_bucket function |
|||
old_client_func = '''def get_new_bucket(client=None, name=None): |
|||
""" |
|||
Get a bucket that exists and is empty. |
|||
|
|||
Always recreates a bucket from scratch. This is useful to also |
|||
reset ACLs and such. |
|||
""" |
|||
if client is None: |
|||
client = get_client() |
|||
if name is None: |
|||
name = get_new_bucket_name() |
|||
|
|||
client.create_bucket(Bucket=name) |
|||
return name''' |
|||
|
|||
new_client_func = '''def get_new_bucket(client=None, name=None): |
|||
""" |
|||
Get a bucket that exists and is empty. |
|||
|
|||
Always recreates a bucket from scratch. This is useful to also |
|||
reset ACLs and such. |
|||
""" |
|||
if client is None: |
|||
client = get_client() |
|||
|
|||
# Retry bucket creation if name conflicts occur |
|||
max_retries = 10 |
|||
for attempt in range(max_retries): |
|||
if name is None or attempt > 0: |
|||
name = get_new_bucket_name() |
|||
|
|||
try: |
|||
client.create_bucket(Bucket=name) |
|||
return name |
|||
except ClientError as e: |
|||
error_code = e.response['Error']['Code'] |
|||
if error_code in ['BucketAlreadyExists', 'BucketAlreadyOwnedByYou']: |
|||
# Bucket name conflict, try again with a new name |
|||
if attempt == max_retries - 1: |
|||
raise Exception(f"Failed to create unique bucket after {max_retries} attempts") |
|||
continue |
|||
else: |
|||
# Other error, re-raise |
|||
raise''' |
|||
|
|||
# Apply patches |
|||
content = content.replace(old_resource_func, new_resource_func) |
|||
content = content.replace(old_client_func, new_client_func) |
|||
|
|||
# Write back the patched content |
|||
with open(file_path, 'w') as f: |
|||
f.write(content) |
|||
|
|||
print("Successfully patched s3-tests to handle bucket creation conflicts!") |
|||
return True |
|||
|
|||
def main(): |
|||
"""Main function to apply the patch.""" |
|||
|
|||
# Default path for s3-tests in GitHub Actions |
|||
s3_tests_path = os.environ.get('S3_TESTS_PATH', 's3-tests') |
|||
init_file_path = os.path.join(s3_tests_path, 's3tests_boto3', 'functional', '__init__.py') |
|||
|
|||
print("🔧 Fixing S3 tests bucket creation conflicts...") |
|||
print(f"Looking for s3-tests at: {s3_tests_path}") |
|||
|
|||
if not os.path.exists(s3_tests_path): |
|||
print(f"Error: s3-tests directory not found at {s3_tests_path}") |
|||
print("Make sure to check out the s3-tests repository first") |
|||
return 1 |
|||
|
|||
if patch_s3_tests_init_file(init_file_path): |
|||
print("✅ S3 tests successfully patched!") |
|||
print("The tests will now handle BucketAlreadyExists errors properly") |
|||
return 0 |
|||
else: |
|||
print("❌ Failed to patch s3-tests") |
|||
return 1 |
|||
|
|||
if __name__ == '__main__': |
|||
sys.exit(main()) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue