You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
5.7 KiB
169 lines
5.7 KiB
#!/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())
|