-
Notifications
You must be signed in to change notification settings - Fork 117
[Integration][AWS-V2] Add Support For S3 Resource As Standalone Exporter #1916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks beautiful I really like the OOP approach, but I missing the tests since we already implemented a DI to all the classes
async def inspect_bucket(bucket_name: str) -> dict[str, Any]: | ||
s3_bucket: S3Bucket = await inspector.inspect(bucket_name) | ||
return s3_bucket.dict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this an internal function?
class S3BucketBuilder: | ||
def __init__(self, name: str) -> None: | ||
self._bucket = S3Bucket(Identifier=name, Properties=S3BucketProperties()) | ||
|
||
def with_data(self, data: Dict[str, Any]) -> Self: | ||
for k, v in data.items(): | ||
setattr(self._bucket.Properties, k, v) | ||
return self | ||
|
||
def build(self) -> S3Bucket: | ||
return self._bucket |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels kinda general, might be a good idea to abstract it so we won't have to repeat it on every resource
from typing import Optional, Dict, Any, List | ||
from pydantic import BaseModel, Field | ||
|
||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-s3-bucket.html#aws-resource-s3-bucket-syntax |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we strictly following the CloudControl properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are trying following CloudFormation's recommendation to packages implementing API composition (e.g Cloud Control) should structure the models
def is_access_denied_exception(e: Exception) -> bool: | ||
access_denied_error_codes = [ | ||
"AccessDenied", | ||
"AccessDeniedException", | ||
"UnauthorizedOperation", | ||
] | ||
response = getattr(e, "response", None) | ||
if isinstance(response, dict): | ||
error_code = response.get("Error", {}).get("Code") | ||
return error_code in access_denied_error_codes | ||
return False | ||
|
||
|
||
def is_resource_not_found_exception(e: Exception) -> bool: | ||
resource_not_found_error_codes = [ | ||
"ResourceNotFoundException", | ||
"ResourceNotFound", | ||
"ResourceNotFoundFault", | ||
] | ||
response = getattr(e, "response", None) | ||
if isinstance(response, dict): | ||
error_code = response.get("Error", {}).get("Code") | ||
return error_code in resource_not_found_error_codes | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like
|
||
|
||
class ExporterOptions(BaseModel): | ||
region: str = Field(..., description="The AWS region to export resources from") | ||
include: List[str] = Field( | ||
default_factory=list, | ||
description="The resources to include in the export", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be a global thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right!
defaults: List[Type[IAction]] | ||
optional: List[Type[IAction]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we separate them if we merge them when used?
class S3BucketInspector: | ||
"""A Facade for inspecting S3 buckets.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it should be a global thing / inherit an abstract or something
except Exception as e: | ||
logger.warning(f"{action.__class__.__name__} failed: {e}") | ||
return {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 - maybe it'll be worthwhile to save the errors for later as well, that'll give us more control on what to do with them later (showing them to clients in the logs, reporting back to port, remediations, etc...)
integrations/aws-v3/main.py
Outdated
if is_access_denied_exception(e): | ||
logger.warning( | ||
f"Access denied in region '{region}' for kind '{kind}', skipping." | ||
) | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm just confused from all the code, but I don't think errors will end up here since they're being caught internally in the inspector
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right
description = "AWS" | ||
authors = ["Shariff Mohammed <mohammed.s@getport.io>"] | ||
authors = ["Shariff Mohammed <mohammed.s@getport.io>", "Michael Armah <mikeyarmah@gmail.com>"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
- Replace `IAction` with `Action` interface - Update from S3-specific classes to generic `ResourceInspector` and models - Fix imports and class names in all test files - Delete redundant test files (`test_bucket_builder.py`, `test_bucket_inspector.py`) - Restore comprehensive model testing in `test_options_and_models.py` All tests now passing with new architecture.
User description
Description
What - Added Support For AWS S3 Exporter
Why -
How -
Type of change
Please leave one option from the following and delete the rest:
All tests should be run against the port production environment(using a testing org).
Core testing checklist
Integration testing checklist
examples
folder in the integration directory.Preflight checklist
Screenshots
Include screenshots from your environment showing how the resources of the integration will look.
API Documentation
Provide links to the API documentation used for this integration.
PR Type
Enhancement
Description
Add S3 resource exporter with standalone architecture
Replace CloudControl sync with declarative exporters
Implement S3 bucket inspection with detailed attributes
Refactor core utilities and interfaces
Diagram Walkthrough
File Walkthrough