mirror of
https://github.com/beshu-tech/deltaglider.git
synced 2026-07-20 03:31:30 +02:00
feat: Add bucket management APIs and improve SDK filtering
This commit adds core bucket management functionality and enhances the SDK's internal file filtering to provide a cleaner abstraction layer. **Bucket Management**: - Add create_bucket(), delete_bucket(), list_buckets() to DeltaGliderClient - Idempotent operations (creating existing bucket or deleting non-existent returns success) - Complete boto3-compatible API for basic bucket operations - Eliminates need for boto3 in most use cases **Enhanced SDK Filtering**: - SDK now filters .delta suffix and reference.bin from all list_objects() responses - Simplified CLI to rely on SDK filtering (removed duplicate logic) - Single source of truth for internal file hiding **Delete Cleanup Logic**: - Automatically removes orphaned reference.bin when last delta in DeltaSpace is deleted - Prevents storage waste from abandoned reference files - Works for both single delete() and recursive delete_recursive() **Documentation & Testing**: - Added BOTO3_COMPATIBILITY.md documenting actual 20% method coverage (21/100+ methods) - Updated README to reflect accurate boto3 compatibility claims - New comprehensive test suite for filtering and cleanup features (test_filtering_and_cleanup.py) - New bucket management test suite (test_bucket_management.py) - Example code for bucket lifecycle management (examples/bucket_management.py) - Fixed mypy configuration to eliminate source file found twice errors - All CI checks passing (lint, format, type check, 18 unit tests, 61 integration tests) **Cleanup**: - Removed PYPI_RELEASE.md (redundant with existing docs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+148
-1
@@ -107,7 +107,16 @@ class BucketStats:
|
||||
|
||||
|
||||
class DeltaGliderClient:
|
||||
"""DeltaGlider client with boto3-compatible APIs and advanced features."""
|
||||
"""DeltaGlider client with boto3-compatible APIs and advanced features.
|
||||
|
||||
Implements core boto3 S3 client methods (~21 methods covering 80% of use cases):
|
||||
- Object operations: put_object, get_object, delete_object, list_objects, head_object
|
||||
- Bucket operations: create_bucket, delete_bucket, list_buckets
|
||||
- Presigned URLs: generate_presigned_url, generate_presigned_post
|
||||
- Plus DeltaGlider extensions for compression stats and batch operations
|
||||
|
||||
See BOTO3_COMPATIBILITY.md for complete compatibility matrix.
|
||||
"""
|
||||
|
||||
def __init__(self, service: DeltaService, endpoint_url: str | None = None):
|
||||
"""Initialize client with service."""
|
||||
@@ -1234,6 +1243,144 @@ class DeltaGliderClient:
|
||||
},
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Bucket Management APIs (boto3-compatible)
|
||||
# ============================================================================
|
||||
|
||||
def create_bucket(
|
||||
self,
|
||||
Bucket: str,
|
||||
CreateBucketConfiguration: dict[str, str] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
"""Create an S3 bucket (boto3-compatible).
|
||||
|
||||
Args:
|
||||
Bucket: Bucket name to create
|
||||
CreateBucketConfiguration: Optional bucket configuration (e.g., LocationConstraint)
|
||||
**kwargs: Additional S3 parameters (for compatibility)
|
||||
|
||||
Returns:
|
||||
Response dict with bucket location
|
||||
|
||||
Example:
|
||||
>>> client = create_client()
|
||||
>>> client.create_bucket(Bucket='my-bucket')
|
||||
>>> # With region
|
||||
>>> client.create_bucket(
|
||||
... Bucket='my-bucket',
|
||||
... CreateBucketConfiguration={'LocationConstraint': 'us-west-2'}
|
||||
... )
|
||||
"""
|
||||
storage_adapter = self.service.storage
|
||||
|
||||
# Check if storage adapter has boto3 client
|
||||
if hasattr(storage_adapter, "client"):
|
||||
try:
|
||||
params: dict[str, Any] = {"Bucket": Bucket}
|
||||
if CreateBucketConfiguration:
|
||||
params["CreateBucketConfiguration"] = CreateBucketConfiguration
|
||||
|
||||
response = storage_adapter.client.create_bucket(**params)
|
||||
return {
|
||||
"Location": response.get("Location", f"/{Bucket}"),
|
||||
"ResponseMetadata": {
|
||||
"HTTPStatusCode": 200,
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "BucketAlreadyExists" in error_msg or "BucketAlreadyOwnedByYou" in error_msg:
|
||||
# Bucket already exists - return success
|
||||
self.service.logger.debug(f"Bucket {Bucket} already exists")
|
||||
return {
|
||||
"Location": f"/{Bucket}",
|
||||
"ResponseMetadata": {
|
||||
"HTTPStatusCode": 200,
|
||||
},
|
||||
}
|
||||
raise RuntimeError(f"Failed to create bucket: {e}") from e
|
||||
else:
|
||||
raise NotImplementedError("Storage adapter does not support bucket creation")
|
||||
|
||||
def delete_bucket(
|
||||
self,
|
||||
Bucket: str,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
"""Delete an S3 bucket (boto3-compatible).
|
||||
|
||||
Note: Bucket must be empty before deletion.
|
||||
|
||||
Args:
|
||||
Bucket: Bucket name to delete
|
||||
**kwargs: Additional S3 parameters (for compatibility)
|
||||
|
||||
Returns:
|
||||
Response dict with deletion status
|
||||
|
||||
Example:
|
||||
>>> client = create_client()
|
||||
>>> client.delete_bucket(Bucket='my-bucket')
|
||||
"""
|
||||
storage_adapter = self.service.storage
|
||||
|
||||
# Check if storage adapter has boto3 client
|
||||
if hasattr(storage_adapter, "client"):
|
||||
try:
|
||||
storage_adapter.client.delete_bucket(Bucket=Bucket)
|
||||
return {
|
||||
"ResponseMetadata": {
|
||||
"HTTPStatusCode": 204,
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "NoSuchBucket" in error_msg:
|
||||
# Bucket doesn't exist - return success
|
||||
self.service.logger.debug(f"Bucket {Bucket} does not exist")
|
||||
return {
|
||||
"ResponseMetadata": {
|
||||
"HTTPStatusCode": 204,
|
||||
},
|
||||
}
|
||||
raise RuntimeError(f"Failed to delete bucket: {e}") from e
|
||||
else:
|
||||
raise NotImplementedError("Storage adapter does not support bucket deletion")
|
||||
|
||||
def list_buckets(self, **kwargs: Any) -> dict[str, Any]:
|
||||
"""List all S3 buckets (boto3-compatible).
|
||||
|
||||
Args:
|
||||
**kwargs: Additional S3 parameters (for compatibility)
|
||||
|
||||
Returns:
|
||||
Response dict with bucket list
|
||||
|
||||
Example:
|
||||
>>> client = create_client()
|
||||
>>> response = client.list_buckets()
|
||||
>>> for bucket in response['Buckets']:
|
||||
... print(bucket['Name'])
|
||||
"""
|
||||
storage_adapter = self.service.storage
|
||||
|
||||
# Check if storage adapter has boto3 client
|
||||
if hasattr(storage_adapter, "client"):
|
||||
try:
|
||||
response = storage_adapter.client.list_buckets()
|
||||
return {
|
||||
"Buckets": response.get("Buckets", []),
|
||||
"Owner": response.get("Owner", {}),
|
||||
"ResponseMetadata": {
|
||||
"HTTPStatusCode": 200,
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Failed to list buckets: {e}") from e
|
||||
else:
|
||||
raise NotImplementedError("Storage adapter does not support bucket listing")
|
||||
|
||||
def _parse_tagging(self, tagging: str) -> dict[str, str]:
|
||||
"""Parse URL-encoded tagging string to dict."""
|
||||
tags = {}
|
||||
|
||||
Reference in New Issue
Block a user