Skip to content

Commit

Permalink
modules and makefile
Browse files Browse the repository at this point in the history
  • Loading branch information
morga471 committed Sep 30, 2025
1 parent 6479a1c commit b351816
Show file tree
Hide file tree
Showing 17 changed files with 913 additions and 822 deletions.
92 changes: 92 additions & 0 deletions local-app/python-tools/gfl-resource-actions/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# AWS Resource Management
# Makefile for development, testing, and execution

.PHONY: help setup install-dev code-check run-dry-run run-stop run-start \
run-stop-ec2 run-start-ec2 clean all

# Variables
SCRIPT_DIR = $(shell pwd)
MODULE_PATH = $(SCRIPT_DIR)/aws_resource_management.py
LOG_FILE = aws_resource_management.log

# Default target
help:
@echo "AWS Resource Management Tool Makefile"
@echo ""
@echo "Available targets:"
@echo " help - Show this help message"
@echo " setup - Set up the development environment"
@echo " install-dev - Install development dependencies"
@echo " code-check - Basic code checks (uses 'python -m py_compile')"
@echo " run-dry-run - Run the tool in dry-run mode (no changes)"
@echo " run-stop - Run the tool to stop all resources"
@echo " run-start - Run the tool to start all resources"
@echo " run-stop-ec2 - Run the tool to stop EC2 instances only"
@echo " run-start-ec2 - Run the tool to start EC2 instances only"
@echo " clean - Remove temporary and generated files"
@echo " all - Run code-check"

# Setup the development environment
setup: install-dev

# Install development dependencies
install-dev:
pip install boto3

# Basic code check - no external tools needed
code-check:
@echo "Checking Python files for syntax errors..."
@if [ -f $(MODULE_PATH) ]; then \
python -m py_compile $(MODULE_PATH) && echo "No syntax errors found in main script."; \
else \
echo "Warning: Main script $(MODULE_PATH) not found."; \
fi
@if [ -d aws_resource_management ]; then \
find aws_resource_management -name "*.py" -type f -exec python -m py_compile {} \; && \
echo "No syntax errors found in module files."; \
else \
echo "Note: aws_resource_management directory not found. Skipping module checks."; \
fi

# Run basic test verification
test:
@echo "Basic verification:"
@if [ -f $(MODULE_PATH) ]; then \
echo "Main script exists."; \
else \
echo "Main script $(MODULE_PATH) not found."; \
fi
@echo "For full testing, install pytest: pip install pytest pytest-mock"

# Run the tool in dry-run mode
run-dry-run:
python $(MODULE_PATH) --dry-run

# Run the tool to stop all resources
run-stop:
python $(MODULE_PATH) --action stop

# Run the tool to start all resources
run-start:
python $(MODULE_PATH) --action start

# Run the tool to stop EC2 instances only
run-stop-ec2:
python $(MODULE_PATH) --action stop --exclude-resources rds eks emr

# Run the tool to start EC2 instances only
run-start-ec2:
python $(MODULE_PATH) --action start --exclude-resources rds eks emr

# Clean up temporary files
clean:
rm -f $(LOG_FILE)
find . -name "__pycache__" -type d -exec rm -rf {} +
find . -name "*.pyc" -delete
find . -name "*.pyo" -delete
find . -name "*.pyd" -delete
find . -name ".pytest_cache" -type d -exec rm -rf {} +
@echo "Cleaned up temporary files."

# Run all development tasks
all: clean setup code-check test run-dry-run
6 changes: 6 additions & 0 deletions local-app/python-tools/gfl-resource-actions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
AWS Resource Management package.
Provides tools for managing AWS resources across multiple accounts.
"""

__version__ = "1.0.0"
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""
Runner script for AWS Resource Management.
Acts as a wrapper for the main.py script.
"""

import sys
import os
import tempfile
import subprocess

if __name__ == "__main__":
# Get the current script directory
script_dir = os.path.dirname(os.path.abspath(__file__))

# Path to main.py
main_path = os.path.join(script_dir, "main.py")

# Check if the main script exists
if not os.path.exists(main_path):
print(f"Error: Main script not found at {main_path}")
sys.exit(1)

# Create a temporary version of main.py with absolute imports
with open(main_path, "r") as f:
content = f.read()

# Replace relative imports with absolute imports
content = content.replace("from . import config", "import config")
content = content.replace("from .logging_utils", "from logging_utils")
content = content.replace("from .aws_utils", "from aws_utils")
content = content.replace("from .discovery", "from discovery")
content = content.replace("from .managers", "from managers")

# Write to temporary file
temp_file = tempfile.NamedTemporaryFile(suffix=".py", delete=False)
temp_file.write(content.encode('utf-8'))
temp_file.close()

try:
# Execute the temporary file with the current script directory in PYTHONPATH
cmd = [sys.executable, temp_file.name] + sys.argv[1:]
env = os.environ.copy()
env['PYTHONPATH'] = script_dir + os.pathsep + env.get('PYTHONPATH', '')

# Run the command and exit with its return code
result = subprocess.run(cmd, env=env)
sys.exit(result.returncode)
finally:
# Clean up the temporary file
os.unlink(temp_file.name)
Empty file.
27 changes: 27 additions & 0 deletions local-app/python-tools/gfl-resource-actions/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Configuration settings for AWS Resource Management.
"""

# Role name to assume in member accounts
ASSUME_ROLE_NAME = "OrganizationAccountAccessRole"

# Default node group sizes for EKS when starting
DEFAULT_NG_MIN_SIZE = 1
DEFAULT_NG_DESIRED_SIZE = 1

# Tag keys for storing node group sizes
TAG_KEY_ORIGINAL_MIN_SIZE = "managed-by:resource-management:original-min-size"
TAG_KEY_ORIGINAL_DESIRED_SIZE = "managed-by:resource-management:original-desired-size"

# Exclusion tag - resources with this tag will be excluded from management
EXCLUSION_TAG = "gfl_exclude"

# Service-specific tags that indicate managed EC2 instances
EKS_TAG = "eks:cluster-name"
EMR_TAG = "aws:elasticmapreduce:job-flow-id"

# Tag for tracking stop action
STOP_TAG = "gfl_stop"

# Log file name
LOG_FILE = "aws_resource_management.log"
133 changes: 133 additions & 0 deletions local-app/python-tools/gfl-resource-actions/discovery/discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
Resource discovery functions for AWS resources.
"""
from . import config
from .aws_utils import create_boto3_client
from .logging_utils import setup_logging

logger = setup_logging()

def get_ec2_instances(credentials, region):
"""Get EC2 instances in a region."""
try:
ec2_client = create_boto3_client('ec2', credentials, region)
instances = []

response = ec2_client.describe_instances()
for reservation in response.get('Reservations', []):
for instance in reservation.get('Instances', []):
# Capture tags for exclusion check
tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}

instances.append({
"id": instance['InstanceId'],
"state": instance['State']['Name'],
"region": region,
"tags": tags
})

# Handle pagination
while 'NextToken' in response:
response = ec2_client.describe_instances(NextToken=response['NextToken'])
for reservation in response.get('Reservations', []):
for instance in reservation.get('Instances', []):
# Capture tags for exclusion check
tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}

instances.append({
"id": instance['InstanceId'],
"state": instance['State']['Name'],
"region": region,
"tags": tags
})

return instances
except Exception as e:
logger.error(f"Error getting EC2 instances in region {region}: {e}")
return []

def get_rds_instances(credentials, region):
"""Get RDS instances in a region."""
try:
rds_client = create_boto3_client('rds', credentials, region)
instances = []

response = rds_client.describe_db_instances()
for instance in response.get('DBInstances', []):
# Capture tags for exclusion check
tags = {}
try:
tag_list = rds_client.list_tags_for_resource(
ResourceName=instance['DBInstanceArn']
).get('TagList', [])
tags = {tag['Key']: tag['Value'] for tag in tag_list}
except Exception as tag_e:
logger.warning(f"Error getting tags for RDS instance {instance['DBInstanceIdentifier']}: {tag_e}")

instances.append({
"id": instance['DBInstanceIdentifier'],
"status": instance['DBInstanceStatus'],
"region": region,
"tags": tags
})

# Handle pagination
while 'Marker' in response:
response = rds_client.describe_db_instances(Marker=response['Marker'])
for instance in response.get('DBInstances', []):
# Capture tags for exclusion check
tags = {}
try:
tag_list = rds_client.list_tags_for_resource(
ResourceName=instance['DBInstanceArn']
).get('TagList', [])
tags = {tag['Key']: tag['Value'] for tag in tag_list}
except Exception as tag_e:
logger.warning(f"Error getting tags for RDS instance {instance['DBInstanceIdentifier']}: {tag_e}")

instances.append({
"id": instance['DBInstanceIdentifier'],
"status": instance['DBInstanceStatus'],
"region": region,
"tags": tags
})

return instances
except Exception as e:
logger.error(f"Error getting RDS instances in region {region}: {e}")
return []

# Similarly implement get_eks_clusters() and get_emr_clusters()...

def get_account_resources(credentials, regions, exclude_resources=None):
"""Get all resources from an account across specified regions."""
if exclude_resources is None:
exclude_resources = []

resources = {
"ec2_instances": [],
"rds_instances": [],
"eks_clusters": [],
"emr_clusters": []
}

for region in regions:
# Get EC2 instances
if "ec2" not in exclude_resources:
resources["ec2_instances"].extend(get_ec2_instances(credentials, region))

# Get RDS instances
if "rds" not in exclude_resources:
resources["rds_instances"].extend(get_rds_instances(credentials, region))

# Get EKS clusters
if "eks" not in exclude_resources:
# Implementation would go here
pass

# Get EMR clusters
if "emr" not in exclude_resources:
# Implementation would go here
pass

return resources
Loading

0 comments on commit b351816

Please sign in to comment.