Skip to content

Commit

Permalink
cruft
Browse files Browse the repository at this point in the history
  • Loading branch information
morga471 committed Sep 30, 2025
1 parent 3fe6b1a commit c488d79
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 1,804 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -448,11 +448,15 @@ def _create_resource_managers(
self, credentials: Dict[str, str], account_id: str, account_name: str
) -> Dict[str, Any]:
"""Create all resource managers for an account."""
# We're using a default region here just for manager initialization
# Each method in the manager will use the appropriate region for the operation
default_region = self._get_default_regions(credentials)[0]

return {
"ec2": EC2Manager(credentials, account_id, account_name),
"rds": RDSManager(credentials, account_id, account_name),
"eks": EKSManager(credentials, account_id, account_name),
"emr": EMRManager(credentials, account_id, account_name),
"ec2": EC2Manager(account_id, default_region),
"rds": RDSManager(account_id, default_region),
"eks": EKSManager(account_id, default_region),
"emr": EMRManager(account_id, default_region),
}

def _execute_actions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,20 +510,71 @@ def _discover_eks(
return []


def get_account_resources(
account_id: str,
# Backward compatibility wrapper for get_account_resources
# This function accepts both positional and keyword arguments
def get_account_resources(*args, **kwargs) -> Dict[str, List[Dict[str, Any]]]:
"""
Get all resources for an account across multiple regions.
Backward-compatible wrapper that handles both positional and keyword arguments.
Args:
credentials: AWS credentials (dict or str)
regions: List of AWS regions to search
exclude_resources: List of resource types to exclude (keyword-only)
account_id: AWS account ID (keyword-only)
account_name: AWS account name (keyword-only)
emr_cluster_states: EMR cluster states to include (keyword-only)
Returns:
Dictionary of resource lists by type
"""
# Handle both old-style positional calling and new-style keyword calling
credentials = None
regions = []

if len(args) >= 1:
credentials = args[0]
else:
credentials = kwargs.get('credentials')

if len(args) >= 2:
regions = args[1]
else:
regions = kwargs.get('regions', [])

# Extract keyword arguments with defaults
exclude_resources = kwargs.get('exclude_resources', [])
account_id = kwargs.get('account_id', 'unknown')
account_name = kwargs.get('account_name', 'Unknown')
emr_cluster_states = kwargs.get('emr_cluster_states')

return _get_account_resources_impl(
credentials=credentials,
regions=regions,
exclude_resources=exclude_resources,
account_id=account_id,
account_name=account_name,
emr_cluster_states=emr_cluster_states
)


def _get_account_resources_impl(
credentials: Dict[str, str],
regions: List[str],
*, # Force all following parameters to be keyword-only
exclude_resources: List[str] = None,
account_id: str = 'unknown',
account_name: Optional[str] = None,
emr_cluster_states: Optional[List[str]] = None,
) -> Dict[str, List[Dict[str, Any]]]:
"""
Get all resources for an account across multiple regions using the ResourceDiscovery class.
Implementation of resource discovery for an account.
Args:
account_id: AWS account ID
credentials: AWS credentials dictionary
regions: List of AWS regions to search
exclude_resources: List of resource types to exclude
account_id: AWS account ID
account_name: AWS account name (optional)
emr_cluster_states: EMR cluster states to include (optional)
Expand All @@ -532,7 +583,7 @@ def get_account_resources(
"""
if exclude_resources is None:
exclude_resources = []

resources = {
"ec2_instances": [],
"rds_instances": [],
Expand All @@ -541,57 +592,67 @@ def get_account_resources(
"ecr_images": [],
"ecr_old_images": [],
}

try:
# Get session for the account
session = get_session_for_account(account_id)
# Create session or use provided credentials
session = None
if isinstance(credentials, dict):
# Create a session from the credentials dictionary
session = boto3.Session(
aws_access_key_id=credentials.get('aws_access_key_id'),
aws_secret_access_key=credentials.get('aws_secret_access_key'),
aws_session_token=credentials.get('aws_session_token')
)
else:
# Try to get a session for the account
session = get_session_for_account(account_id)

if not session:
logger.error(f"Could not create session for account {account_id}")
return resources

# Create resource discovery instance
from aws_resource_management.aws_utils import get_credentials

credentials = {
"aws_access_key_id": session.get_credentials().access_key,
"aws_secret_access_key": session.get_credentials().secret_key,
"aws_session_token": (
session.get_credentials().token
if session.get_credentials().token
else None
),

# Extract credentials from session for the ResourceDiscovery
creds = session.get_credentials()
discovery_credentials = {
"aws_access_key_id": creds.access_key,
"aws_secret_access_key": creds.secret_key,
"aws_session_token": creds.token if hasattr(creds, 'token') and creds.token else None,
}

discovery = ResourceDiscovery(credentials, account_id)


# Create resource discovery instance
discovery = ResourceDiscovery(discovery_credentials, account_id)

# Discover EC2 instances
if "ec2" not in exclude_resources:
logger.info(
f"Discovering EC2 instances for account {account_id} in {len(regions)} regions"
)
resources["ec2_instances"] = discovery.discover_resources("ec2", regions)

# Discover RDS instances
if "rds" not in exclude_resources:
logger.info(
f"Discovering RDS instances for account {account_id} in {len(regions)} regions"
)
resources["rds_instances"] = discovery.discover_resources("rds", regions)

# Discover EKS clusters
if "eks" not in exclude_resources and EKSManager:
logger.info(
f"Discovering EKS clusters for account {account_id} in {len(regions)} regions"
)
resources["eks_clusters"] = discovery.discover_resources("eks", regions)

# Discover EMR clusters
if "emr" not in exclude_resources and EMRManager:
logger.info(
f"Discovering EMR clusters for account {account_id} in {len(regions)} regions"
)
resources["emr_clusters"] = discovery.discover_resources("emr", regions)

# Use emr_cluster_states if provided
resources["emr_clusters"] = discovery.discover_resources(
"emr", regions, resource_ids=None
)

# Discover ECR images
if "ecr" not in exclude_resources and ECRManager:
logger.info(
Expand All @@ -603,7 +664,14 @@ def get_account_resources(
# Filter old images if needed
resources["ecr_old_images"] = []

# Add account information to all resources
for resource_type, resource_list in resources.items():
for resource in resource_list:
resource["accountId"] = account_id
if account_name:
resource["accountName"] = account_name

except Exception as e:
logger.error(f"Error discovering resources for account {account_id}: {e}")

return resources
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import logging
from datetime import datetime
from typing import Any, Dict, List, Optional, Type

import boto3
Expand All @@ -22,6 +23,15 @@ def __init__(self, account_id: str, region: str):
self.session = None
self._clients = {} # Cache for boto3 clients

def get_timestamp(self) -> str:
"""
Get current timestamp in ISO format for tagging resources.
Returns:
ISO formatted timestamp string
"""
return datetime.utcnow().isoformat()

def get_client(self, service_name: str) -> Any:
"""
Get a boto3 client for the specified service with proper partition support.
Expand Down Expand Up @@ -142,6 +152,126 @@ def discover_resources(self) -> List[Dict[str, Any]]:
List of dictionaries containing resource information
"""
raise NotImplementedError("Subclasses must implement discover_resources method")

def get_boto3_client(self, service_name: str, region: str) -> Any:
"""
Get a boto3 client for the specified service and region.
Args:
service_name: AWS service name (e.g., 'ec2', 'rds')
region: AWS region
Returns:
Boto3 client for the specified service in the specified region
"""
try:
# Get an account-specific session with proper role assumption
if not self.session:
self.session = get_session_for_account(self.account_id)

# Create config with retry settings
boto_config = Config(
region_name=region,
retries={"max_attempts": 3, "mode": "standard"},
)

# Create client with proper configuration
client = self.session.client(
service_name, region_name=region, config=boto_config
)
return client
except Exception as e:
logger.error(f"Error creating {service_name} client in {region}: {str(e)}")
return None

# Alias for backward compatibility - many manager implementations use create_client()
create_client = get_boto3_client

def paginate_boto3(self, client: Any, operation: str, result_key: str) -> List[Dict[str, Any]]:
"""
Paginate through AWS API responses.
Args:
client: Boto3 client
operation: Operation name (e.g., 'describe_instances')
result_key: Key in the response that contains the results
Returns:
Combined list of results from all pages
"""
try:
paginator = client.get_paginator(operation)
results = []
for page in paginator.paginate():
if result_key in page:
results.extend(page[result_key])
return results
except Exception as e:
logger.error(f"Error paginating {operation}: {str(e)}")
return []

def log_action(self, resource_id: str, region: str, action: str,
resource_name: str = None, details: str = None,
dry_run: bool = False, existing_schedule: str = None) -> None:
"""
Log an action taken on a resource.
Args:
resource_id: Resource ID
region: AWS region
action: Action taken (start, stop, etc.)
resource_name: Optional resource name
details: Optional action details
dry_run: Whether this was a dry run
existing_schedule: Optional existing schedule
"""
name_str = f"({resource_name})" if resource_name else ""
dry_run_prefix = "[DRY RUN] Would " if dry_run else ""
details_str = f" - {details}" if details else ""
schedule_str = f" [Schedule: {existing_schedule}]" if existing_schedule else ""

logger.info(
f"{dry_run_prefix}{action.capitalize()} {self.resource_type} {resource_id} {name_str} "
f"in {region}{details_str}{schedule_str}"
)

def should_exclude(self, resource: Dict[str, Any]) -> bool:
"""
Check if a resource should be excluded based on tags.
Args:
resource: Resource dictionary with tags
Returns:
True if the resource should be excluded, False otherwise
"""
from aws_resource_management.config_manager import get_config
config = get_config()

tags = resource.get("tags", {})
exclusion_tag = config.get("exclusion_tag")

if exclusion_tag and exclusion_tag in tags:
return True

return False

def get_partition_for_region(self, region: str) -> str:
"""
Get the AWS partition for a region.
Args:
region: AWS region
Returns:
AWS partition (aws, aws-cn, aws-us-gov)
"""
if region.startswith("us-gov-") or region.startswith("gov-"):
return "aws-us-gov"
elif region.startswith("cn-"):
return "aws-cn"
else:
return "aws"


# Create alias for backward compatibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ class EC2Manager(ResourceManager):

def __init__(
self,
credentials: Dict[str, str],
account_id: str,
account_name: Optional[str] = None,
region: str,
):
"""Initialize EC2 manager."""
super().__init__(credentials, account_id, account_name)
super().__init__(account_id, region)
self.resource_type = "ec2_instance"
self.account_name = None # This can be updated if needed
self.MAX_INSTANCES_PER_API_CALL = 50

def should_exclude(self, instance: Dict[str, Any]) -> bool:
Expand Down
Loading

0 comments on commit c488d79

Please sign in to comment.