runbooks 0.7.6__py3-none-any.whl → 0.7.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/base.py +5 -1
  3. runbooks/cfat/__init__.py +8 -4
  4. runbooks/cfat/assessment/collectors.py +171 -14
  5. runbooks/cfat/assessment/compliance.py +871 -0
  6. runbooks/cfat/assessment/runner.py +122 -11
  7. runbooks/cfat/models.py +6 -2
  8. runbooks/common/logger.py +14 -0
  9. runbooks/common/rich_utils.py +451 -0
  10. runbooks/enterprise/__init__.py +68 -0
  11. runbooks/enterprise/error_handling.py +411 -0
  12. runbooks/enterprise/logging.py +439 -0
  13. runbooks/enterprise/multi_tenant.py +583 -0
  14. runbooks/finops/README.md +468 -241
  15. runbooks/finops/__init__.py +39 -3
  16. runbooks/finops/cli.py +83 -18
  17. runbooks/finops/cross_validation.py +375 -0
  18. runbooks/finops/dashboard_runner.py +812 -164
  19. runbooks/finops/enhanced_dashboard_runner.py +525 -0
  20. runbooks/finops/finops_dashboard.py +1892 -0
  21. runbooks/finops/helpers.py +485 -51
  22. runbooks/finops/optimizer.py +823 -0
  23. runbooks/finops/tests/__init__.py +19 -0
  24. runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
  25. runbooks/finops/tests/run_comprehensive_tests.py +421 -0
  26. runbooks/finops/tests/run_tests.py +305 -0
  27. runbooks/finops/tests/test_finops_dashboard.py +705 -0
  28. runbooks/finops/tests/test_integration.py +477 -0
  29. runbooks/finops/tests/test_performance.py +380 -0
  30. runbooks/finops/tests/test_performance_benchmarks.py +500 -0
  31. runbooks/finops/tests/test_reference_images_validation.py +867 -0
  32. runbooks/finops/tests/test_single_account_features.py +715 -0
  33. runbooks/finops/tests/validate_test_suite.py +220 -0
  34. runbooks/finops/types.py +1 -1
  35. runbooks/hitl/enhanced_workflow_engine.py +725 -0
  36. runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
  37. runbooks/inventory/collectors/aws_comprehensive.py +442 -0
  38. runbooks/inventory/collectors/enterprise_scale.py +281 -0
  39. runbooks/inventory/core/collector.py +172 -13
  40. runbooks/inventory/discovery.md +1 -1
  41. runbooks/inventory/list_ec2_instances.py +18 -20
  42. runbooks/inventory/list_ssm_parameters.py +31 -3
  43. runbooks/inventory/organizations_discovery.py +1269 -0
  44. runbooks/inventory/rich_inventory_display.py +393 -0
  45. runbooks/inventory/run_on_multi_accounts.py +35 -19
  46. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  47. runbooks/inventory/runbooks.security.run_script.log +0 -0
  48. runbooks/inventory/vpc_flow_analyzer.py +1030 -0
  49. runbooks/main.py +2215 -119
  50. runbooks/metrics/dora_metrics_engine.py +599 -0
  51. runbooks/operate/__init__.py +2 -2
  52. runbooks/operate/base.py +122 -10
  53. runbooks/operate/deployment_framework.py +1032 -0
  54. runbooks/operate/deployment_validator.py +853 -0
  55. runbooks/operate/dynamodb_operations.py +10 -6
  56. runbooks/operate/ec2_operations.py +319 -11
  57. runbooks/operate/executive_dashboard.py +779 -0
  58. runbooks/operate/mcp_integration.py +750 -0
  59. runbooks/operate/nat_gateway_operations.py +1120 -0
  60. runbooks/operate/networking_cost_heatmap.py +685 -0
  61. runbooks/operate/privatelink_operations.py +940 -0
  62. runbooks/operate/s3_operations.py +10 -6
  63. runbooks/operate/vpc_endpoints.py +644 -0
  64. runbooks/operate/vpc_operations.py +1038 -0
  65. runbooks/remediation/__init__.py +2 -2
  66. runbooks/remediation/acm_remediation.py +1 -1
  67. runbooks/remediation/base.py +1 -1
  68. runbooks/remediation/cloudtrail_remediation.py +1 -1
  69. runbooks/remediation/cognito_remediation.py +1 -1
  70. runbooks/remediation/dynamodb_remediation.py +1 -1
  71. runbooks/remediation/ec2_remediation.py +1 -1
  72. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
  73. runbooks/remediation/kms_enable_key_rotation.py +1 -1
  74. runbooks/remediation/kms_remediation.py +1 -1
  75. runbooks/remediation/lambda_remediation.py +1 -1
  76. runbooks/remediation/multi_account.py +1 -1
  77. runbooks/remediation/rds_remediation.py +1 -1
  78. runbooks/remediation/s3_block_public_access.py +1 -1
  79. runbooks/remediation/s3_enable_access_logging.py +1 -1
  80. runbooks/remediation/s3_encryption.py +1 -1
  81. runbooks/remediation/s3_remediation.py +1 -1
  82. runbooks/remediation/vpc_remediation.py +475 -0
  83. runbooks/security/__init__.py +3 -1
  84. runbooks/security/compliance_automation.py +632 -0
  85. runbooks/security/report_generator.py +10 -0
  86. runbooks/security/run_script.py +31 -5
  87. runbooks/security/security_baseline_tester.py +169 -30
  88. runbooks/security/security_export.py +477 -0
  89. runbooks/validation/__init__.py +10 -0
  90. runbooks/validation/benchmark.py +484 -0
  91. runbooks/validation/cli.py +356 -0
  92. runbooks/validation/mcp_validator.py +768 -0
  93. runbooks/vpc/__init__.py +38 -0
  94. runbooks/vpc/config.py +212 -0
  95. runbooks/vpc/cost_engine.py +347 -0
  96. runbooks/vpc/heatmap_engine.py +605 -0
  97. runbooks/vpc/manager_interface.py +634 -0
  98. runbooks/vpc/networking_wrapper.py +1260 -0
  99. runbooks/vpc/rich_formatters.py +679 -0
  100. runbooks/vpc/tests/__init__.py +5 -0
  101. runbooks/vpc/tests/conftest.py +356 -0
  102. runbooks/vpc/tests/test_cli_integration.py +530 -0
  103. runbooks/vpc/tests/test_config.py +458 -0
  104. runbooks/vpc/tests/test_cost_engine.py +479 -0
  105. runbooks/vpc/tests/test_networking_wrapper.py +512 -0
  106. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
  107. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
  108. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
  109. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
  110. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
  111. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,356 @@
1
+ """
2
+ VPC Testing Configuration and Fixtures
3
+
4
+ Provides specialized fixtures for VPC networking component testing
5
+ with comprehensive AWS service mocking and test data.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import sys
11
+ import tempfile
12
+ from datetime import datetime, timedelta
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional
15
+ from unittest.mock import MagicMock, Mock, patch
16
+
17
+ import boto3
18
+ import pytest
19
+ from moto import mock_aws
20
+ from rich.console import Console
21
+
22
+ # Add src to Python path
23
+ src_path = Path(__file__).parent.parent.parent.parent.parent / "src"
24
+ if str(src_path) not in sys.path:
25
+ sys.path.insert(0, str(src_path))
26
+
27
+ from runbooks.vpc.config import AWSCostModel, OptimizationThresholds, VPCNetworkingConfig
28
+ from runbooks.vpc.cost_engine import NetworkingCostEngine
29
+ from runbooks.vpc.networking_wrapper import VPCNetworkingWrapper
30
+
31
+
32
+ @pytest.fixture(scope="session")
33
+ def aws_credentials():
34
+ """Mock AWS credentials for VPC testing."""
35
+ os.environ["AWS_ACCESS_KEY_ID"] = "testing"
36
+ os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
37
+ os.environ["AWS_SECURITY_TOKEN"] = "testing"
38
+ os.environ["AWS_SESSION_TOKEN"] = "testing"
39
+ os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
40
+
41
+
42
+ @pytest.fixture
43
+ def vpc_test_profiles():
44
+ """Test profile configurations for VPC testing."""
45
+ return {
46
+ "billing_profile": "test-billing-profile",
47
+ "management_profile": "test-management-profile",
48
+ "centralised_ops_profile": "test-ops-profile",
49
+ "single_account_profile": "test-single-account-profile",
50
+ }
51
+
52
+
53
+ @pytest.fixture
54
+ def vpc_test_config():
55
+ """Standard VPC test configuration."""
56
+ return VPCNetworkingConfig(
57
+ default_region="us-east-1",
58
+ billing_profile="test-billing-profile",
59
+ default_analysis_days=30,
60
+ default_output_format="json",
61
+ enable_cost_approval_workflow=True,
62
+ enable_mcp_validation=False,
63
+ )
64
+
65
+
66
+ @pytest.fixture
67
+ def mock_console():
68
+ """Mock Rich Console for testing output."""
69
+ console = Mock(spec=Console)
70
+ console.print = Mock()
71
+
72
+ # Mock status context manager
73
+ mock_status = Mock()
74
+ mock_status.__enter__ = Mock(return_value=mock_status)
75
+ mock_status.__exit__ = Mock(return_value=None)
76
+ console.status = Mock(return_value=mock_status)
77
+
78
+ return console
79
+
80
+
81
+ @pytest.fixture
82
+ def sample_nat_gateways():
83
+ """Sample NAT Gateway data for testing."""
84
+ return [
85
+ {
86
+ "NatGatewayId": "nat-0123456789abcdef0",
87
+ "State": "available",
88
+ "VpcId": "vpc-0123456789abcdef0",
89
+ "SubnetId": "subnet-0123456789abcdef0",
90
+ "CreationTime": datetime.now(),
91
+ "NatGatewayAddresses": [
92
+ {
93
+ "AllocationId": "eipalloc-0123456789abcdef0",
94
+ "NetworkInterfaceId": "eni-0123456789abcdef0",
95
+ "PrivateIp": "10.0.1.5",
96
+ "PublicIp": "203.0.113.5",
97
+ }
98
+ ],
99
+ },
100
+ {
101
+ "NatGatewayId": "nat-0123456789abcdef1",
102
+ "State": "available",
103
+ "VpcId": "vpc-0123456789abcdef1",
104
+ "SubnetId": "subnet-0123456789abcdef1",
105
+ "CreationTime": datetime.now(),
106
+ "NatGatewayAddresses": [
107
+ {
108
+ "AllocationId": "eipalloc-0123456789abcdef1",
109
+ "NetworkInterfaceId": "eni-0123456789abcdef1",
110
+ "PrivateIp": "10.0.2.5",
111
+ "PublicIp": "203.0.113.6",
112
+ }
113
+ ],
114
+ },
115
+ ]
116
+
117
+
118
+ @pytest.fixture
119
+ def sample_vpc_endpoints():
120
+ """Sample VPC Endpoint data for testing."""
121
+ return [
122
+ {
123
+ "VpcEndpointId": "vpce-0123456789abcdef0",
124
+ "VpcEndpointType": "Interface",
125
+ "VpcId": "vpc-0123456789abcdef0",
126
+ "ServiceName": "com.amazonaws.us-east-1.s3",
127
+ "State": "available",
128
+ "CreationTimestamp": datetime.now(),
129
+ "SubnetIds": ["subnet-0123456789abcdef0", "subnet-0123456789abcdef1"],
130
+ },
131
+ {
132
+ "VpcEndpointId": "vpce-0123456789abcdef1",
133
+ "VpcEndpointType": "Gateway",
134
+ "VpcId": "vpc-0123456789abcdef1",
135
+ "ServiceName": "com.amazonaws.us-east-1.dynamodb",
136
+ "State": "available",
137
+ "CreationTimestamp": datetime.now(),
138
+ "SubnetIds": [],
139
+ },
140
+ ]
141
+
142
+
143
+ @pytest.fixture
144
+ def sample_cloudwatch_metrics():
145
+ """Sample CloudWatch metrics data for NAT Gateway testing."""
146
+ return {
147
+ "ActiveConnectionCount": [
148
+ {"Timestamp": datetime.now() - timedelta(days=1), "Average": 150.0, "Maximum": 200.0, "Unit": "Count"},
149
+ {"Timestamp": datetime.now() - timedelta(days=2), "Average": 120.0, "Maximum": 180.0, "Unit": "Count"},
150
+ ],
151
+ "BytesOutToDestination": [
152
+ {
153
+ "Timestamp": datetime.now() - timedelta(days=1),
154
+ "Sum": 5368709120.0, # 5 GB
155
+ "Unit": "Bytes",
156
+ },
157
+ {
158
+ "Timestamp": datetime.now() - timedelta(days=2),
159
+ "Sum": 3221225472.0, # 3 GB
160
+ "Unit": "Bytes",
161
+ },
162
+ ],
163
+ }
164
+
165
+
166
+ @pytest.fixture
167
+ def mock_aws_vpc_comprehensive(aws_credentials, sample_nat_gateways, sample_vpc_endpoints):
168
+ """Comprehensive AWS VPC mock with all networking components."""
169
+ with mock_aws():
170
+ # Create clients
171
+ ec2_client = boto3.client("ec2", region_name="us-east-1")
172
+ cloudwatch_client = boto3.client("cloudwatch", region_name="us-east-1")
173
+
174
+ # Create VPC infrastructure
175
+ vpc_response = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
176
+ vpc_id = vpc_response["Vpc"]["VpcId"]
177
+
178
+ # Create subnets
179
+ subnet1 = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.1.0/24", AvailabilityZone="us-east-1a")
180
+ subnet2 = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.2.0/24", AvailabilityZone="us-east-1b")
181
+
182
+ # Create Internet Gateway
183
+ igw_response = ec2_client.create_internet_gateway()
184
+ igw_id = igw_response["InternetGateway"]["InternetGatewayId"]
185
+ ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id)
186
+
187
+ # Create Elastic IPs for NAT Gateways
188
+ eip1 = ec2_client.allocate_address(Domain="vpc")
189
+ eip2 = ec2_client.allocate_address(Domain="vpc")
190
+
191
+ # Create NAT Gateways
192
+ nat_gw1 = ec2_client.create_nat_gateway(
193
+ SubnetId=subnet1["Subnet"]["SubnetId"], AllocationId=eip1["AllocationId"]
194
+ )
195
+ nat_gw2 = ec2_client.create_nat_gateway(
196
+ SubnetId=subnet2["Subnet"]["SubnetId"], AllocationId=eip2["AllocationId"]
197
+ )
198
+
199
+ # Create VPC Endpoints
200
+ vpc_endpoint_s3 = ec2_client.create_vpc_endpoint(
201
+ VpcId=vpc_id,
202
+ ServiceName="com.amazonaws.us-east-1.s3",
203
+ VpcEndpointType="Interface",
204
+ SubnetIds=[subnet1["Subnet"]["SubnetId"], subnet2["Subnet"]["SubnetId"]],
205
+ )
206
+
207
+ vpc_endpoint_dynamodb = ec2_client.create_vpc_endpoint(
208
+ VpcId=vpc_id, ServiceName="com.amazonaws.us-east-1.dynamodb", VpcEndpointType="Gateway"
209
+ )
210
+
211
+ test_infrastructure = {
212
+ "vpc_id": vpc_id,
213
+ "subnet_ids": [subnet1["Subnet"]["SubnetId"], subnet2["Subnet"]["SubnetId"]],
214
+ "igw_id": igw_id,
215
+ "nat_gateway_ids": [nat_gw1["NatGateway"]["NatGatewayId"], nat_gw2["NatGateway"]["NatGatewayId"]],
216
+ "vpc_endpoint_ids": [
217
+ vpc_endpoint_s3["VpcEndpoint"]["VpcEndpointId"],
218
+ vpc_endpoint_dynamodb["VpcEndpoint"]["VpcEndpointId"],
219
+ ],
220
+ "allocation_ids": [eip1["AllocationId"], eip2["AllocationId"]],
221
+ }
222
+
223
+ yield {"ec2_client": ec2_client, "cloudwatch_client": cloudwatch_client, "infrastructure": test_infrastructure}
224
+
225
+
226
+ @pytest.fixture
227
+ def vpc_networking_wrapper(mock_console, vpc_test_config):
228
+ """VPC Networking Wrapper instance for testing."""
229
+ with patch("runbooks.vpc.networking_wrapper.boto3.Session") as mock_session:
230
+ # Configure mock session
231
+ mock_session_instance = Mock()
232
+ mock_session.return_value = mock_session_instance
233
+
234
+ wrapper = VPCNetworkingWrapper(
235
+ profile="test-profile",
236
+ region="us-east-1",
237
+ billing_profile="test-billing-profile",
238
+ output_format="json",
239
+ console=mock_console,
240
+ )
241
+
242
+ # Set mock session
243
+ wrapper.session = mock_session_instance
244
+
245
+ yield wrapper
246
+
247
+
248
+ @pytest.fixture
249
+ def networking_cost_engine(vpc_test_config):
250
+ """Networking Cost Engine instance for testing."""
251
+ with patch("runbooks.vpc.cost_engine.boto3.Session") as mock_session:
252
+ mock_session_instance = Mock()
253
+ mock_session.return_value = mock_session_instance
254
+
255
+ engine = NetworkingCostEngine(session=mock_session_instance, config=vpc_test_config)
256
+
257
+ yield engine
258
+
259
+
260
+ @pytest.fixture
261
+ def performance_benchmarks():
262
+ """Performance benchmark thresholds for testing."""
263
+ return {
264
+ "nat_gateway_analysis_max_time": 5.0, # seconds
265
+ "vpc_endpoint_analysis_max_time": 3.0, # seconds
266
+ "cost_calculation_max_time": 1.0, # seconds
267
+ "cli_response_max_time": 2.0, # seconds
268
+ "heatmap_generation_max_time": 10.0, # seconds
269
+ }
270
+
271
+
272
+ @pytest.fixture
273
+ def mock_cost_explorer_responses():
274
+ """Mock Cost Explorer API responses for testing."""
275
+ return {
276
+ "vpc_costs": {
277
+ "ResultsByTime": [
278
+ {
279
+ "TimePeriod": {"Start": "2024-01-01", "End": "2024-01-31"},
280
+ "Total": {"BlendedCost": {"Amount": "145.67", "Unit": "USD"}},
281
+ }
282
+ ]
283
+ },
284
+ "nat_gateway_costs": {
285
+ "ResultsByTime": [
286
+ {
287
+ "TimePeriod": {"Start": "2024-01-01", "End": "2024-01-31"},
288
+ "Total": {"BlendedCost": {"Amount": "89.32", "Unit": "USD"}},
289
+ }
290
+ ]
291
+ },
292
+ }
293
+
294
+
295
+ @pytest.fixture
296
+ def temp_output_directory():
297
+ """Temporary directory for test output files."""
298
+ with tempfile.TemporaryDirectory() as temp_dir:
299
+ yield Path(temp_dir)
300
+
301
+
302
+ # Utility functions for tests
303
+
304
+
305
+ @pytest.fixture
306
+ def assert_performance_benchmark():
307
+ """Utility function to assert performance benchmarks."""
308
+
309
+ def _assert_performance(execution_time: float, benchmark_name: str, benchmarks: dict):
310
+ """Assert that execution time meets performance benchmark."""
311
+ if benchmark_name in benchmarks:
312
+ max_time = benchmarks[benchmark_name]
313
+ assert execution_time < max_time, (
314
+ f"Performance benchmark failed: {execution_time:.2f}s > {max_time}s for {benchmark_name}"
315
+ )
316
+ return True
317
+
318
+ return _assert_performance
319
+
320
+
321
+ @pytest.fixture
322
+ def validate_vpc_structure():
323
+ """Utility function to validate VPC analysis result structure."""
324
+
325
+ def _validate_structure(result: Dict[str, Any], expected_keys: List[str]):
326
+ """Validate that result contains all expected keys."""
327
+ for key in expected_keys:
328
+ assert key in result, f"Missing required key: {key}"
329
+
330
+ # Validate common structure elements
331
+ assert "timestamp" in result
332
+ assert "profile" in result
333
+ assert "region" in result
334
+
335
+ return True
336
+
337
+ return _validate_structure
338
+
339
+
340
+ @pytest.fixture
341
+ def security_test_validator():
342
+ """Utility for security validation testing."""
343
+
344
+ def _validate_security(func_call_result: Any, sensitive_patterns: List[str] = None):
345
+ """Validate that no sensitive information is exposed."""
346
+ if sensitive_patterns is None:
347
+ sensitive_patterns = ["AKIA", "SECRET", "TOKEN", "PASSWORD"]
348
+
349
+ result_str = str(func_call_result)
350
+
351
+ for pattern in sensitive_patterns:
352
+ assert pattern not in result_str.upper(), f"Sensitive pattern '{pattern}' found in result"
353
+
354
+ return True
355
+
356
+ return _validate_security