| """
|
| AWS Operations
|
| Part of SOVEREIGN PYTHON LLM ENGINE
|
|
|
| AWS cloud operations using boto3.
|
| """
|
|
|
| from typing import Any
|
| from pathlib import Path
|
| from dataclasses import dataclass
|
|
|
| from ...core.evidence import WORMLedger
|
|
|
|
|
| @dataclass
|
| class S3Object:
|
| """S3 object metadata"""
|
| key: str
|
| size: int
|
| last_modified: str
|
| etag: str
|
|
|
|
|
| class AWSOperations:
|
| """
|
| AWS cloud operations.
|
|
|
| Supports:
|
| - S3 (storage)
|
| - Lambda (functions)
|
| - Secrets Manager
|
| - SSM Parameter Store
|
| """
|
|
|
| def __init__(
|
| self,
|
| region: str = "us-east-1",
|
| worm_ledger: WORMLedger | None = None
|
| ):
|
| """
|
| Initialize AWS operations.
|
|
|
| Args:
|
| region: AWS region
|
| worm_ledger: Optional WORM ledger
|
| """
|
| self.region = region
|
| self.worm_ledger = worm_ledger
|
|
|
| try:
|
| import boto3
|
| except ImportError:
|
| raise ImportError("boto3 is required. Install: pip install boto3")
|
|
|
| self.boto3 = boto3
|
|
|
|
|
|
|
| async def s3_upload(
|
| self,
|
| bucket: str,
|
| key: str,
|
| file_path: Path
|
| ) -> str:
|
| """
|
| Upload file to S3.
|
|
|
| Args:
|
| bucket: S3 bucket name
|
| key: Object key
|
| file_path: Local file path
|
|
|
| Returns:
|
| S3 URL
|
| """
|
| import asyncio
|
|
|
| def _sync_upload():
|
| s3 = self.boto3.client("s3", region_name=self.region)
|
| s3.upload_file(str(file_path), bucket, key)
|
| return f"s3://{bucket}/{key}"
|
|
|
| url = await asyncio.to_thread(_sync_upload)
|
|
|
|
|
| if self.worm_ledger:
|
| await self.worm_ledger.append({
|
| "event": "s3_upload",
|
| "bucket": bucket,
|
| "key": key,
|
| "size": file_path.stat().st_size
|
| })
|
|
|
| return url
|
|
|
| async def s3_download(
|
| self,
|
| bucket: str,
|
| key: str,
|
| file_path: Path
|
| ) -> None:
|
| """
|
| Download file from S3.
|
|
|
| Args:
|
| bucket: S3 bucket name
|
| key: Object key
|
| file_path: Local file path
|
| """
|
| import asyncio
|
|
|
| def _sync_download():
|
| s3 = self.boto3.client("s3", region_name=self.region)
|
| s3.download_file(bucket, key, str(file_path))
|
|
|
| await asyncio.to_thread(_sync_download)
|
|
|
| async def s3_list(
|
| self,
|
| bucket: str,
|
| prefix: str = ""
|
| ) -> list[S3Object]:
|
| """
|
| List objects in S3 bucket.
|
|
|
| Args:
|
| bucket: S3 bucket name
|
| prefix: Optional prefix filter
|
|
|
| Returns:
|
| List of S3Object
|
| """
|
| import asyncio
|
|
|
| def _sync_list():
|
| s3 = self.boto3.client("s3", region_name=self.region)
|
| response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix)
|
|
|
| objects = []
|
| for obj in response.get("Contents", []):
|
| objects.append(S3Object(
|
| key=obj["Key"],
|
| size=obj["Size"],
|
| last_modified=obj["LastModified"].isoformat(),
|
| etag=obj["ETag"]
|
| ))
|
|
|
| return objects
|
|
|
| return await asyncio.to_thread(_sync_list)
|
|
|
| async def s3_delete(
|
| self,
|
| bucket: str,
|
| key: str
|
| ) -> None:
|
| """
|
| Delete object from S3.
|
|
|
| Args:
|
| bucket: S3 bucket name
|
| key: Object key
|
| """
|
| import asyncio
|
|
|
| def _sync_delete():
|
| s3 = self.boto3.client("s3", region_name=self.region)
|
| s3.delete_object(Bucket=bucket, Key=key)
|
|
|
| await asyncio.to_thread(_sync_delete)
|
|
|
|
|
| if self.worm_ledger:
|
| await self.worm_ledger.append({
|
| "event": "s3_delete",
|
| "bucket": bucket,
|
| "key": key
|
| })
|
|
|
|
|
|
|
| async def get_secret(
|
| self,
|
| secret_id: str
|
| ) -> dict[str, Any]:
|
| """
|
| Get secret from Secrets Manager.
|
|
|
| Args:
|
| secret_id: Secret ID or ARN
|
|
|
| Returns:
|
| Secret value (as dict if JSON)
|
| """
|
| import asyncio
|
| import json
|
|
|
| def _sync_get_secret():
|
| sm = self.boto3.client("secretsmanager", region_name=self.region)
|
| response = sm.get_secret_value(SecretId=secret_id)
|
|
|
| secret_string = response["SecretString"]
|
|
|
|
|
| try:
|
| return json.loads(secret_string)
|
| except json.JSONDecodeError:
|
| return {"value": secret_string}
|
|
|
| return await asyncio.to_thread(_sync_get_secret)
|
|
|
| async def put_secret(
|
| self,
|
| secret_id: str,
|
| secret_value: dict[str, Any] | str
|
| ) -> None:
|
| """
|
| Put secret to Secrets Manager.
|
|
|
| Args:
|
| secret_id: Secret ID
|
| secret_value: Secret value (dict or string)
|
| """
|
| import asyncio
|
| import json
|
|
|
| def _sync_put_secret():
|
| sm = self.boto3.client("secretsmanager", region_name=self.region)
|
|
|
|
|
| if isinstance(secret_value, dict):
|
| secret_string = json.dumps(secret_value)
|
| else:
|
| secret_string = secret_value
|
|
|
| try:
|
| sm.update_secret(SecretId=secret_id, SecretString=secret_string)
|
| except sm.exceptions.ResourceNotFoundException:
|
| sm.create_secret(Name=secret_id, SecretString=secret_string)
|
|
|
| await asyncio.to_thread(_sync_put_secret)
|
|
|
|
|
| if self.worm_ledger:
|
| await self.worm_ledger.append({
|
| "event": "secrets_manager_put",
|
| "secret_id": secret_id
|
| })
|
|
|
|
|
|
|
| async def invoke_lambda(
|
| self,
|
| function_name: str,
|
| payload: dict[str, Any]
|
| ) -> dict[str, Any]:
|
| """
|
| Invoke Lambda function.
|
|
|
| Args:
|
| function_name: Function name or ARN
|
| payload: Function payload
|
|
|
| Returns:
|
| Function response
|
| """
|
| import asyncio
|
| import json
|
|
|
| def _sync_invoke():
|
| lambda_client = self.boto3.client("lambda", region_name=self.region)
|
|
|
| response = lambda_client.invoke(
|
| FunctionName=function_name,
|
| InvocationType="RequestResponse",
|
| Payload=json.dumps(payload)
|
| )
|
|
|
| result_payload = json.loads(response["Payload"].read())
|
| return result_payload
|
|
|
| result = await asyncio.to_thread(_sync_invoke)
|
|
|
|
|
| if self.worm_ledger:
|
| await self.worm_ledger.append({
|
| "event": "lambda_invoke",
|
| "function": function_name
|
| })
|
|
|
| return result
|
|
|
|
|
|
|
| async def s3_upload_tool(
|
| bucket: str,
|
| key: str,
|
| file_path: str,
|
| region: str = "us-east-1"
|
| ) -> dict:
|
| """Upload to S3 tool"""
|
| aws = AWSOperations(region=region)
|
| url = await aws.s3_upload(bucket, key, Path(file_path))
|
|
|
| return {
|
| "url": url,
|
| "bucket": bucket,
|
| "key": key
|
| }
|
|
|
|
|
| async def s3_list_tool(
|
| bucket: str,
|
| prefix: str = "",
|
| region: str = "us-east-1"
|
| ) -> dict:
|
| """List S3 objects tool"""
|
| aws = AWSOperations(region=region)
|
| objects = await aws.s3_list(bucket, prefix)
|
|
|
| return {
|
| "objects": [
|
| {
|
| "key": obj.key,
|
| "size": obj.size,
|
| "last_modified": obj.last_modified
|
| }
|
| for obj in objects
|
| ],
|
| "count": len(objects)
|
| }
|
|
|
|
|
| async def get_secret_tool(
|
| secret_id: str,
|
| region: str = "us-east-1"
|
| ) -> dict:
|
| """Get secret tool"""
|
| aws = AWSOperations(region=region)
|
| secret = await aws.get_secret(secret_id)
|
|
|
| return {
|
| "secret_id": secret_id,
|
| "value": secret
|
| }
|
|
|
|
|
| async def invoke_lambda_tool(
|
| function_name: str,
|
| payload: dict,
|
| region: str = "us-east-1"
|
| ) -> dict:
|
| """Invoke Lambda tool"""
|
| aws = AWSOperations(region=region)
|
| result = await aws.invoke_lambda(function_name, payload)
|
|
|
| return {
|
| "function": function_name,
|
| "result": result
|
| }
|
|
|