Spaces:
Runtime error
Runtime error
File size: 7,933 Bytes
105b369 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
from typing import Optional, Any, Dict, List
from typing_extensions import Literal
from phi.aws.api_client import AwsApiClient
from phi.aws.resource.base import AwsResource
from phi.aws.resource.s3.object import S3Object
from phi.cli.console import print_info
from phi.utils.log import logger
class S3Bucket(AwsResource):
"""
Reference:
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#service-resource
"""
resource_type: str = "s3"
service_name: str = "s3"
# Name of the bucket
name: str
# The canned ACL to apply to the bucket.
acl: Optional[Literal["private", "public-read", "public-read-write", "authenticated-read"]] = None
grant_full_control: Optional[str] = None
grant_read: Optional[str] = None
grant_read_ACP: Optional[str] = None
grant_write: Optional[str] = None
grant_write_ACP: Optional[str] = None
object_lock_enabled_for_bucket: Optional[bool] = None
object_ownership: Optional[Literal["BucketOwnerPreferred", "ObjectWriter", "BucketOwnerEnforced"]] = None
@property
def uri(self) -> str:
"""Returns the URI of the s3.Bucket
Returns:
str: The URI of the s3.Bucket
"""
return f"s3://{self.name}"
def get_resource(self, aws_client: Optional[AwsApiClient] = None) -> Optional[Any]:
"""Returns the s3.Bucket
Args:
aws_client: The AwsApiClient for the current cluster
"""
client: AwsApiClient = aws_client or self.get_aws_client()
service_resource = self.get_service_resource(client)
return service_resource.Bucket(name=self.name)
def _create(self, aws_client: AwsApiClient) -> bool:
"""Creates the s3.Bucket
Args:
aws_client: The AwsApiClient for the current cluster
"""
print_info(f"Creating {self.get_resource_type()}: {self.get_resource_name()}")
# Step 1: Build bucket configuration
# Bucket names are GLOBALLY unique!
# AWS will give you the IllegalLocationConstraintException if you collide
# with an already existing bucket if you've specified a region different than
# the region of the already existing bucket. If you happen to guess the correct region of the
# existing bucket it will give you the BucketAlreadyExists exception.
bucket_configuration = None
if aws_client.aws_region is not None and aws_client.aws_region != "us-east-1":
bucket_configuration = {"LocationConstraint": aws_client.aws_region}
# create a dict of args which are not null, otherwise aws type validation fails
not_null_args: Dict[str, Any] = {}
if bucket_configuration:
not_null_args["CreateBucketConfiguration"] = bucket_configuration
if self.acl:
not_null_args["ACL"] = self.acl
if self.grant_full_control:
not_null_args["GrantFullControl"] = self.grant_full_control
if self.grant_read:
not_null_args["GrantRead"] = self.grant_read
if self.grant_read_ACP:
not_null_args["GrantReadACP"] = self.grant_read_ACP
if self.grant_write:
not_null_args["GrantWrite"] = self.grant_write
if self.grant_write_ACP:
not_null_args["GrantWriteACP"] = self.grant_write_ACP
if self.object_lock_enabled_for_bucket:
not_null_args["ObjectLockEnabledForBucket"] = self.object_lock_enabled_for_bucket
if self.object_ownership:
not_null_args["ObjectOwnership"] = self.object_ownership
# Step 2: Create Bucket
service_client = self.get_service_client(aws_client)
try:
response = service_client.create_bucket(
Bucket=self.name,
**not_null_args,
)
logger.debug(f"Response: {response}")
bucket_location = response.get("Location")
if bucket_location is not None:
logger.debug(f"Bucket created: {bucket_location}")
self.active_resource = response
return True
except Exception as e:
logger.error(f"{self.get_resource_type()} could not be created.")
logger.error(e)
return False
def post_create(self, aws_client: AwsApiClient) -> bool:
# Wait for Bucket to be created
if self.wait_for_create:
try:
print_info(f"Waiting for {self.get_resource_type()} to be created.")
waiter = self.get_service_client(aws_client).get_waiter("bucket_exists")
waiter.wait(
Bucket=self.name,
WaiterConfig={
"Delay": self.waiter_delay,
"MaxAttempts": self.waiter_max_attempts,
},
)
except Exception as e:
logger.error("Waiter failed.")
logger.error(e)
return True
def _read(self, aws_client: AwsApiClient) -> Optional[Any]:
"""Returns the s3.Bucket
Args:
aws_client: The AwsApiClient for the current cluster
"""
logger.debug(f"Reading {self.get_resource_type()}: {self.get_resource_name()}")
from botocore.exceptions import ClientError
try:
service_resource = self.get_service_resource(aws_client)
bucket = service_resource.Bucket(name=self.name)
bucket.load()
creation_date = bucket.creation_date
logger.debug(f"Bucket creation_date: {creation_date}")
if creation_date is not None:
logger.debug(f"Bucket found: {bucket.name}")
self.active_resource = {
"name": bucket.name,
"creation_date": creation_date,
}
except ClientError as ce:
logger.debug(f"ClientError: {ce}")
except Exception as e:
logger.error(f"Error reading {self.get_resource_type()}.")
logger.error(e)
return self.active_resource
def _delete(self, aws_client: AwsApiClient) -> bool:
"""Deletes the s3.Bucket
Args:
aws_client: The AwsApiClient for the current cluster
"""
print_info(f"Deleting {self.get_resource_type()}: {self.get_resource_name()}")
service_client = self.get_service_client(aws_client)
self.active_resource = None
try:
response = service_client.delete_bucket(Bucket=self.name)
logger.debug(f"Response: {response}")
return True
except Exception as e:
logger.error(f"{self.get_resource_type()} could not be deleted.")
logger.error("Please try again or delete resources manually.")
logger.error(e)
return False
def get_objects(self, aws_client: Optional[AwsApiClient] = None, prefix: Optional[str] = None) -> List[Any]:
"""Returns a list of s3.Object objects for the s3.Bucket
Args:
aws_client: The AwsApiClient for the current cluster
prefix: Prefix to filter objects by
"""
bucket = self.get_resource(aws_client)
if bucket is None:
logger.warning(f"Could not get bucket: {self.name}")
return []
logger.debug(f"Getting objects for bucket: {bucket.name}")
# Get all objects in bucket
object_summaries = bucket.objects.all()
all_objects: List[S3Object] = []
for object_summary in object_summaries:
if prefix is not None and not object_summary.key.startswith(prefix):
continue
all_objects.append(
S3Object(
bucket_name=bucket.name,
name=object_summary.key,
)
)
return all_objects
|