aws-inventory-manager 0.2.0__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.
Potentially problematic release.
This version of aws-inventory-manager might be problematic. Click here for more details.
- aws_inventory_manager-0.2.0.dist-info/METADATA +508 -0
- aws_inventory_manager-0.2.0.dist-info/RECORD +65 -0
- aws_inventory_manager-0.2.0.dist-info/WHEEL +5 -0
- aws_inventory_manager-0.2.0.dist-info/entry_points.txt +2 -0
- aws_inventory_manager-0.2.0.dist-info/licenses/LICENSE +21 -0
- aws_inventory_manager-0.2.0.dist-info/top_level.txt +1 -0
- src/__init__.py +3 -0
- src/aws/__init__.py +11 -0
- src/aws/client.py +128 -0
- src/aws/credentials.py +191 -0
- src/aws/rate_limiter.py +177 -0
- src/cli/__init__.py +5 -0
- src/cli/config.py +130 -0
- src/cli/main.py +1450 -0
- src/cost/__init__.py +5 -0
- src/cost/analyzer.py +226 -0
- src/cost/explorer.py +209 -0
- src/cost/reporter.py +237 -0
- src/delta/__init__.py +5 -0
- src/delta/calculator.py +180 -0
- src/delta/reporter.py +225 -0
- src/models/__init__.py +17 -0
- src/models/cost_report.py +87 -0
- src/models/delta_report.py +111 -0
- src/models/inventory.py +124 -0
- src/models/resource.py +99 -0
- src/models/snapshot.py +108 -0
- src/snapshot/__init__.py +6 -0
- src/snapshot/capturer.py +347 -0
- src/snapshot/filter.py +245 -0
- src/snapshot/inventory_storage.py +264 -0
- src/snapshot/resource_collectors/__init__.py +5 -0
- src/snapshot/resource_collectors/apigateway.py +140 -0
- src/snapshot/resource_collectors/backup.py +136 -0
- src/snapshot/resource_collectors/base.py +81 -0
- src/snapshot/resource_collectors/cloudformation.py +55 -0
- src/snapshot/resource_collectors/cloudwatch.py +109 -0
- src/snapshot/resource_collectors/codebuild.py +69 -0
- src/snapshot/resource_collectors/codepipeline.py +82 -0
- src/snapshot/resource_collectors/dynamodb.py +65 -0
- src/snapshot/resource_collectors/ec2.py +240 -0
- src/snapshot/resource_collectors/ecs.py +215 -0
- src/snapshot/resource_collectors/eks.py +200 -0
- src/snapshot/resource_collectors/elb.py +126 -0
- src/snapshot/resource_collectors/eventbridge.py +156 -0
- src/snapshot/resource_collectors/iam.py +188 -0
- src/snapshot/resource_collectors/kms.py +111 -0
- src/snapshot/resource_collectors/lambda_func.py +112 -0
- src/snapshot/resource_collectors/rds.py +109 -0
- src/snapshot/resource_collectors/route53.py +86 -0
- src/snapshot/resource_collectors/s3.py +105 -0
- src/snapshot/resource_collectors/secretsmanager.py +70 -0
- src/snapshot/resource_collectors/sns.py +68 -0
- src/snapshot/resource_collectors/sqs.py +72 -0
- src/snapshot/resource_collectors/ssm.py +160 -0
- src/snapshot/resource_collectors/stepfunctions.py +74 -0
- src/snapshot/resource_collectors/vpcendpoints.py +79 -0
- src/snapshot/resource_collectors/waf.py +159 -0
- src/snapshot/storage.py +259 -0
- src/utils/__init__.py +12 -0
- src/utils/export.py +87 -0
- src/utils/hash.py +60 -0
- src/utils/logging.py +63 -0
- src/utils/paths.py +51 -0
- src/utils/progress.py +41 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""EC2 resource collector."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from ...models.resource import Resource
|
|
6
|
+
from ...utils.hash import compute_config_hash
|
|
7
|
+
from .base import BaseResourceCollector
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EC2Collector(BaseResourceCollector):
|
|
11
|
+
"""Collector for AWS EC2 resources (instances, volumes, VPCs, security groups, etc.)."""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def service_name(self) -> str:
|
|
15
|
+
return "ec2"
|
|
16
|
+
|
|
17
|
+
def collect(self) -> List[Resource]:
|
|
18
|
+
"""Collect EC2 resources.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
List of EC2 resources
|
|
22
|
+
"""
|
|
23
|
+
resources = []
|
|
24
|
+
account_id = self._get_account_id()
|
|
25
|
+
|
|
26
|
+
# Collect instances
|
|
27
|
+
resources.extend(self._collect_instances(account_id))
|
|
28
|
+
|
|
29
|
+
# Collect volumes
|
|
30
|
+
resources.extend(self._collect_volumes(account_id))
|
|
31
|
+
|
|
32
|
+
# Collect VPCs
|
|
33
|
+
resources.extend(self._collect_vpcs(account_id))
|
|
34
|
+
|
|
35
|
+
# Collect security groups
|
|
36
|
+
resources.extend(self._collect_security_groups(account_id))
|
|
37
|
+
|
|
38
|
+
# Collect subnets
|
|
39
|
+
resources.extend(self._collect_subnets(account_id))
|
|
40
|
+
|
|
41
|
+
self.logger.debug(f"Collected {len(resources)} EC2 resources in {self.region}")
|
|
42
|
+
return resources
|
|
43
|
+
|
|
44
|
+
def _collect_instances(self, account_id: str) -> List[Resource]:
|
|
45
|
+
"""Collect EC2 instances."""
|
|
46
|
+
resources = []
|
|
47
|
+
client = self._create_client()
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
paginator = client.get_paginator("describe_instances")
|
|
51
|
+
for page in paginator.paginate():
|
|
52
|
+
for reservation in page["Reservations"]:
|
|
53
|
+
for instance in reservation["Instances"]:
|
|
54
|
+
instance_id = instance["InstanceId"]
|
|
55
|
+
|
|
56
|
+
# Extract tags
|
|
57
|
+
tags = {}
|
|
58
|
+
for tag in instance.get("Tags", []):
|
|
59
|
+
tags[tag["Key"]] = tag["Value"]
|
|
60
|
+
|
|
61
|
+
# Get instance name from tags
|
|
62
|
+
name = tags.get("Name", instance_id)
|
|
63
|
+
|
|
64
|
+
# Build ARN
|
|
65
|
+
arn = f"arn:aws:ec2:{self.region}:{account_id}:instance/{instance_id}"
|
|
66
|
+
|
|
67
|
+
# Create resource
|
|
68
|
+
resource = Resource(
|
|
69
|
+
arn=arn,
|
|
70
|
+
resource_type="AWS::EC2::Instance",
|
|
71
|
+
name=name,
|
|
72
|
+
region=self.region,
|
|
73
|
+
tags=tags,
|
|
74
|
+
config_hash=compute_config_hash(instance),
|
|
75
|
+
created_at=instance.get("LaunchTime"),
|
|
76
|
+
raw_config=instance,
|
|
77
|
+
)
|
|
78
|
+
resources.append(resource)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
self.logger.error(f"Error collecting EC2 instances in {self.region}: {e}")
|
|
82
|
+
|
|
83
|
+
return resources
|
|
84
|
+
|
|
85
|
+
def _collect_volumes(self, account_id: str) -> List[Resource]:
|
|
86
|
+
"""Collect EBS volumes."""
|
|
87
|
+
resources = []
|
|
88
|
+
client = self._create_client()
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
paginator = client.get_paginator("describe_volumes")
|
|
92
|
+
for page in paginator.paginate():
|
|
93
|
+
for volume in page["Volumes"]:
|
|
94
|
+
volume_id = volume["VolumeId"]
|
|
95
|
+
|
|
96
|
+
# Extract tags
|
|
97
|
+
tags = {}
|
|
98
|
+
for tag in volume.get("Tags", []):
|
|
99
|
+
tags[tag["Key"]] = tag["Value"]
|
|
100
|
+
|
|
101
|
+
# Get volume name from tags
|
|
102
|
+
name = tags.get("Name", volume_id)
|
|
103
|
+
|
|
104
|
+
# Build ARN
|
|
105
|
+
arn = f"arn:aws:ec2:{self.region}:{account_id}:volume/{volume_id}"
|
|
106
|
+
|
|
107
|
+
# Create resource
|
|
108
|
+
resource = Resource(
|
|
109
|
+
arn=arn,
|
|
110
|
+
resource_type="AWS::EC2::Volume",
|
|
111
|
+
name=name,
|
|
112
|
+
region=self.region,
|
|
113
|
+
tags=tags,
|
|
114
|
+
config_hash=compute_config_hash(volume),
|
|
115
|
+
created_at=volume.get("CreateTime"),
|
|
116
|
+
raw_config=volume,
|
|
117
|
+
)
|
|
118
|
+
resources.append(resource)
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
self.logger.error(f"Error collecting EBS volumes in {self.region}: {e}")
|
|
122
|
+
|
|
123
|
+
return resources
|
|
124
|
+
|
|
125
|
+
def _collect_vpcs(self, account_id: str) -> List[Resource]:
|
|
126
|
+
"""Collect VPCs."""
|
|
127
|
+
resources = []
|
|
128
|
+
client = self._create_client()
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
response = client.describe_vpcs()
|
|
132
|
+
for vpc in response["Vpcs"]:
|
|
133
|
+
vpc_id = vpc["VpcId"]
|
|
134
|
+
|
|
135
|
+
# Extract tags
|
|
136
|
+
tags = {}
|
|
137
|
+
for tag in vpc.get("Tags", []):
|
|
138
|
+
tags[tag["Key"]] = tag["Value"]
|
|
139
|
+
|
|
140
|
+
# Get VPC name from tags
|
|
141
|
+
name = tags.get("Name", vpc_id)
|
|
142
|
+
|
|
143
|
+
# Build ARN
|
|
144
|
+
arn = f"arn:aws:ec2:{self.region}:{account_id}:vpc/{vpc_id}"
|
|
145
|
+
|
|
146
|
+
# Create resource
|
|
147
|
+
resource = Resource(
|
|
148
|
+
arn=arn,
|
|
149
|
+
resource_type="AWS::EC2::VPC",
|
|
150
|
+
name=name,
|
|
151
|
+
region=self.region,
|
|
152
|
+
tags=tags,
|
|
153
|
+
config_hash=compute_config_hash(vpc),
|
|
154
|
+
created_at=None, # VPCs don't have creation timestamp
|
|
155
|
+
raw_config=vpc,
|
|
156
|
+
)
|
|
157
|
+
resources.append(resource)
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
self.logger.error(f"Error collecting VPCs in {self.region}: {e}")
|
|
161
|
+
|
|
162
|
+
return resources
|
|
163
|
+
|
|
164
|
+
def _collect_security_groups(self, account_id: str) -> List[Resource]:
|
|
165
|
+
"""Collect security groups."""
|
|
166
|
+
resources = []
|
|
167
|
+
client = self._create_client()
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
paginator = client.get_paginator("describe_security_groups")
|
|
171
|
+
for page in paginator.paginate():
|
|
172
|
+
for sg in page["SecurityGroups"]:
|
|
173
|
+
sg_id = sg["GroupId"]
|
|
174
|
+
sg_name = sg["GroupName"]
|
|
175
|
+
|
|
176
|
+
# Extract tags
|
|
177
|
+
tags = {}
|
|
178
|
+
for tag in sg.get("Tags", []):
|
|
179
|
+
tags[tag["Key"]] = tag["Value"]
|
|
180
|
+
|
|
181
|
+
# Build ARN
|
|
182
|
+
arn = f"arn:aws:ec2:{self.region}:{account_id}:security-group/{sg_id}"
|
|
183
|
+
|
|
184
|
+
# Create resource
|
|
185
|
+
resource = Resource(
|
|
186
|
+
arn=arn,
|
|
187
|
+
resource_type="AWS::EC2::SecurityGroup",
|
|
188
|
+
name=sg_name,
|
|
189
|
+
region=self.region,
|
|
190
|
+
tags=tags,
|
|
191
|
+
config_hash=compute_config_hash(sg),
|
|
192
|
+
created_at=None, # Security groups don't have creation timestamp
|
|
193
|
+
raw_config=sg,
|
|
194
|
+
)
|
|
195
|
+
resources.append(resource)
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self.logger.error(f"Error collecting security groups in {self.region}: {e}")
|
|
199
|
+
|
|
200
|
+
return resources
|
|
201
|
+
|
|
202
|
+
def _collect_subnets(self, account_id: str) -> List[Resource]:
|
|
203
|
+
"""Collect subnets."""
|
|
204
|
+
resources = []
|
|
205
|
+
client = self._create_client()
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
paginator = client.get_paginator("describe_subnets")
|
|
209
|
+
for page in paginator.paginate():
|
|
210
|
+
for subnet in page["Subnets"]:
|
|
211
|
+
subnet_id = subnet["SubnetId"]
|
|
212
|
+
|
|
213
|
+
# Extract tags
|
|
214
|
+
tags = {}
|
|
215
|
+
for tag in subnet.get("Tags", []):
|
|
216
|
+
tags[tag["Key"]] = tag["Value"]
|
|
217
|
+
|
|
218
|
+
# Get subnet name from tags
|
|
219
|
+
name = tags.get("Name", subnet_id)
|
|
220
|
+
|
|
221
|
+
# Build ARN
|
|
222
|
+
arn = f"arn:aws:ec2:{self.region}:{account_id}:subnet/{subnet_id}"
|
|
223
|
+
|
|
224
|
+
# Create resource
|
|
225
|
+
resource = Resource(
|
|
226
|
+
arn=arn,
|
|
227
|
+
resource_type="AWS::EC2::Subnet",
|
|
228
|
+
name=name,
|
|
229
|
+
region=self.region,
|
|
230
|
+
tags=tags,
|
|
231
|
+
config_hash=compute_config_hash(subnet),
|
|
232
|
+
created_at=None, # Subnets don't have creation timestamp
|
|
233
|
+
raw_config=subnet,
|
|
234
|
+
)
|
|
235
|
+
resources.append(resource)
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
self.logger.error(f"Error collecting subnets in {self.region}: {e}")
|
|
239
|
+
|
|
240
|
+
return resources
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""ECS resource collector."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from ...models.resource import Resource
|
|
6
|
+
from ...utils.hash import compute_config_hash
|
|
7
|
+
from .base import BaseResourceCollector
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ECSCollector(BaseResourceCollector):
|
|
11
|
+
"""Collector for Amazon ECS (Elastic Container Service) resources."""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def service_name(self) -> str:
|
|
15
|
+
return "ecs"
|
|
16
|
+
|
|
17
|
+
def collect(self) -> List[Resource]:
|
|
18
|
+
"""Collect ECS resources.
|
|
19
|
+
|
|
20
|
+
Collects:
|
|
21
|
+
- ECS Clusters
|
|
22
|
+
- ECS Services (within each cluster)
|
|
23
|
+
- ECS Task Definitions (active revisions)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
List of ECS resources
|
|
27
|
+
"""
|
|
28
|
+
resources = []
|
|
29
|
+
|
|
30
|
+
# Collect clusters
|
|
31
|
+
resources.extend(self._collect_clusters())
|
|
32
|
+
|
|
33
|
+
# Collect services (across all clusters)
|
|
34
|
+
resources.extend(self._collect_services())
|
|
35
|
+
|
|
36
|
+
# Collect task definitions
|
|
37
|
+
resources.extend(self._collect_task_definitions())
|
|
38
|
+
|
|
39
|
+
self.logger.debug(f"Collected {len(resources)} ECS resources in {self.region}")
|
|
40
|
+
return resources
|
|
41
|
+
|
|
42
|
+
def _collect_clusters(self) -> List[Resource]:
|
|
43
|
+
"""Collect ECS clusters.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of ECS cluster resources
|
|
47
|
+
"""
|
|
48
|
+
resources = []
|
|
49
|
+
client = self._create_client()
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
paginator = client.get_paginator("list_clusters")
|
|
53
|
+
for page in paginator.paginate():
|
|
54
|
+
cluster_arns = page.get("clusterArns", [])
|
|
55
|
+
|
|
56
|
+
if not cluster_arns:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
# Get detailed info for clusters (in batches of 100)
|
|
60
|
+
for i in range(0, len(cluster_arns), 100):
|
|
61
|
+
batch = cluster_arns[i : i + 100]
|
|
62
|
+
try:
|
|
63
|
+
response = client.describe_clusters(clusters=batch, include=["TAGS"])
|
|
64
|
+
for cluster in response.get("clusters", []):
|
|
65
|
+
cluster_name = cluster["clusterName"]
|
|
66
|
+
cluster_arn = cluster["clusterArn"]
|
|
67
|
+
|
|
68
|
+
# Extract tags
|
|
69
|
+
tags = {}
|
|
70
|
+
for tag in cluster.get("tags", []):
|
|
71
|
+
tags[tag["key"]] = tag["value"]
|
|
72
|
+
|
|
73
|
+
# Extract creation timestamp (not always available)
|
|
74
|
+
created_at = None
|
|
75
|
+
|
|
76
|
+
# Create resource
|
|
77
|
+
resource = Resource(
|
|
78
|
+
arn=cluster_arn,
|
|
79
|
+
resource_type="AWS::ECS::Cluster",
|
|
80
|
+
name=cluster_name,
|
|
81
|
+
region=self.region,
|
|
82
|
+
tags=tags,
|
|
83
|
+
config_hash=compute_config_hash(cluster),
|
|
84
|
+
created_at=created_at,
|
|
85
|
+
raw_config=cluster,
|
|
86
|
+
)
|
|
87
|
+
resources.append(resource)
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
self.logger.debug(f"Error describing cluster batch: {e}")
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
self.logger.error(f"Error collecting ECS clusters in {self.region}: {e}")
|
|
94
|
+
|
|
95
|
+
return resources
|
|
96
|
+
|
|
97
|
+
def _collect_services(self) -> List[Resource]:
|
|
98
|
+
"""Collect ECS services across all clusters.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of ECS service resources
|
|
102
|
+
"""
|
|
103
|
+
resources = []
|
|
104
|
+
client = self._create_client()
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
# First, get all clusters
|
|
108
|
+
cluster_arns = []
|
|
109
|
+
paginator = client.get_paginator("list_clusters")
|
|
110
|
+
for page in paginator.paginate():
|
|
111
|
+
cluster_arns.extend(page.get("clusterArns", []))
|
|
112
|
+
|
|
113
|
+
# Collect services from each cluster
|
|
114
|
+
for cluster_arn in cluster_arns:
|
|
115
|
+
try:
|
|
116
|
+
service_paginator = client.get_paginator("list_services")
|
|
117
|
+
service_arns = []
|
|
118
|
+
for page in service_paginator.paginate(cluster=cluster_arn):
|
|
119
|
+
service_arns.extend(page.get("serviceArns", []))
|
|
120
|
+
|
|
121
|
+
if not service_arns:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
# Get detailed info for services (in batches of 10)
|
|
125
|
+
for i in range(0, len(service_arns), 10):
|
|
126
|
+
batch = service_arns[i : i + 10]
|
|
127
|
+
try:
|
|
128
|
+
response = client.describe_services(cluster=cluster_arn, services=batch, include=["TAGS"])
|
|
129
|
+
for service in response.get("services", []):
|
|
130
|
+
service_name = service["serviceName"]
|
|
131
|
+
service_arn = service["serviceArn"]
|
|
132
|
+
|
|
133
|
+
# Extract tags
|
|
134
|
+
tags = {}
|
|
135
|
+
for tag in service.get("tags", []):
|
|
136
|
+
tags[tag["key"]] = tag["value"]
|
|
137
|
+
|
|
138
|
+
# Extract creation date
|
|
139
|
+
created_at = service.get("createdAt")
|
|
140
|
+
|
|
141
|
+
# Create resource
|
|
142
|
+
resource = Resource(
|
|
143
|
+
arn=service_arn,
|
|
144
|
+
resource_type="AWS::ECS::Service",
|
|
145
|
+
name=service_name,
|
|
146
|
+
region=self.region,
|
|
147
|
+
tags=tags,
|
|
148
|
+
config_hash=compute_config_hash(service),
|
|
149
|
+
created_at=created_at,
|
|
150
|
+
raw_config=service,
|
|
151
|
+
)
|
|
152
|
+
resources.append(resource)
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
self.logger.debug(f"Error describing service batch: {e}")
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
self.logger.debug(f"Error collecting services from cluster {cluster_arn}: {e}")
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
self.logger.error(f"Error collecting ECS services in {self.region}: {e}")
|
|
162
|
+
|
|
163
|
+
return resources
|
|
164
|
+
|
|
165
|
+
def _collect_task_definitions(self) -> List[Resource]:
|
|
166
|
+
"""Collect ECS task definitions (active revisions only).
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
List of ECS task definition resources
|
|
170
|
+
"""
|
|
171
|
+
resources = []
|
|
172
|
+
client = self._create_client()
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
paginator = client.get_paginator("list_task_definitions")
|
|
176
|
+
for page in paginator.paginate(status="ACTIVE"):
|
|
177
|
+
for task_def_arn in page.get("taskDefinitionArns", []):
|
|
178
|
+
try:
|
|
179
|
+
# Get task definition details
|
|
180
|
+
response = client.describe_task_definition(taskDefinition=task_def_arn, include=["TAGS"])
|
|
181
|
+
task_def = response.get("taskDefinition", {})
|
|
182
|
+
|
|
183
|
+
# Extract family and revision
|
|
184
|
+
family = task_def.get("family", "unknown")
|
|
185
|
+
revision = task_def.get("revision", 0)
|
|
186
|
+
name = f"{family}:{revision}"
|
|
187
|
+
|
|
188
|
+
# Extract tags
|
|
189
|
+
tags = {}
|
|
190
|
+
for tag in response.get("tags", []):
|
|
191
|
+
tags[tag["key"]] = tag["value"]
|
|
192
|
+
|
|
193
|
+
# Task definitions don't have creation timestamp
|
|
194
|
+
created_at = None
|
|
195
|
+
|
|
196
|
+
# Create resource
|
|
197
|
+
resource = Resource(
|
|
198
|
+
arn=task_def_arn,
|
|
199
|
+
resource_type="AWS::ECS::TaskDefinition",
|
|
200
|
+
name=name,
|
|
201
|
+
region=self.region,
|
|
202
|
+
tags=tags,
|
|
203
|
+
config_hash=compute_config_hash(task_def),
|
|
204
|
+
created_at=created_at,
|
|
205
|
+
raw_config=task_def,
|
|
206
|
+
)
|
|
207
|
+
resources.append(resource)
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
self.logger.debug(f"Error describing task definition {task_def_arn}: {e}")
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
self.logger.error(f"Error collecting ECS task definitions in {self.region}: {e}")
|
|
214
|
+
|
|
215
|
+
return resources
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""EKS resource collector."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from ...models.resource import Resource
|
|
6
|
+
from ...utils.hash import compute_config_hash
|
|
7
|
+
from .base import BaseResourceCollector
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EKSCollector(BaseResourceCollector):
|
|
11
|
+
"""Collector for Amazon EKS (Elastic Kubernetes Service) resources."""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def service_name(self) -> str:
|
|
15
|
+
return "eks"
|
|
16
|
+
|
|
17
|
+
def collect(self) -> List[Resource]:
|
|
18
|
+
"""Collect EKS resources.
|
|
19
|
+
|
|
20
|
+
Collects:
|
|
21
|
+
- EKS Clusters
|
|
22
|
+
- Node Groups (within each cluster)
|
|
23
|
+
- Fargate Profiles (within each cluster)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
List of EKS resources
|
|
27
|
+
"""
|
|
28
|
+
resources = []
|
|
29
|
+
|
|
30
|
+
# Collect clusters
|
|
31
|
+
cluster_resources, cluster_names = self._collect_clusters()
|
|
32
|
+
resources.extend(cluster_resources)
|
|
33
|
+
|
|
34
|
+
# Collect node groups for each cluster
|
|
35
|
+
resources.extend(self._collect_node_groups(cluster_names))
|
|
36
|
+
|
|
37
|
+
# Collect Fargate profiles for each cluster
|
|
38
|
+
resources.extend(self._collect_fargate_profiles(cluster_names))
|
|
39
|
+
|
|
40
|
+
self.logger.debug(f"Collected {len(resources)} EKS resources in {self.region}")
|
|
41
|
+
return resources
|
|
42
|
+
|
|
43
|
+
def _collect_clusters(self) -> tuple[List[Resource], List[str]]: # type: ignore
|
|
44
|
+
"""Collect EKS clusters.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Tuple of (list of cluster resources, list of cluster names)
|
|
48
|
+
"""
|
|
49
|
+
resources = []
|
|
50
|
+
cluster_names = []
|
|
51
|
+
client = self._create_client()
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
paginator = client.get_paginator("list_clusters")
|
|
55
|
+
for page in paginator.paginate():
|
|
56
|
+
for cluster_name in page.get("clusters", []):
|
|
57
|
+
cluster_names.append(cluster_name)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Get detailed cluster info
|
|
61
|
+
cluster_response = client.describe_cluster(name=cluster_name)
|
|
62
|
+
cluster = cluster_response.get("cluster", {})
|
|
63
|
+
|
|
64
|
+
cluster_arn = cluster.get("arn", "")
|
|
65
|
+
|
|
66
|
+
# Extract tags
|
|
67
|
+
tags = cluster.get("tags", {})
|
|
68
|
+
|
|
69
|
+
# Extract creation date
|
|
70
|
+
created_at = cluster.get("createdAt")
|
|
71
|
+
|
|
72
|
+
# Create resource
|
|
73
|
+
resource = Resource(
|
|
74
|
+
arn=cluster_arn,
|
|
75
|
+
resource_type="AWS::EKS::Cluster",
|
|
76
|
+
name=cluster_name,
|
|
77
|
+
region=self.region,
|
|
78
|
+
tags=tags,
|
|
79
|
+
config_hash=compute_config_hash(cluster),
|
|
80
|
+
created_at=created_at,
|
|
81
|
+
raw_config=cluster,
|
|
82
|
+
)
|
|
83
|
+
resources.append(resource)
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
self.logger.debug(f"Error processing cluster {cluster_name}: {e}")
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
self.logger.error(f"Error collecting EKS clusters in {self.region}: {e}")
|
|
91
|
+
|
|
92
|
+
return resources, cluster_names
|
|
93
|
+
|
|
94
|
+
def _collect_node_groups(self, cluster_names: List[str]) -> List[Resource]:
|
|
95
|
+
"""Collect EKS node groups for given clusters.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
cluster_names: List of cluster names to collect node groups from
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of node group resources
|
|
102
|
+
"""
|
|
103
|
+
resources = []
|
|
104
|
+
client = self._create_client()
|
|
105
|
+
|
|
106
|
+
for cluster_name in cluster_names:
|
|
107
|
+
try:
|
|
108
|
+
paginator = client.get_paginator("list_nodegroups")
|
|
109
|
+
for page in paginator.paginate(clusterName=cluster_name):
|
|
110
|
+
for nodegroup_name in page.get("nodegroups", []):
|
|
111
|
+
try:
|
|
112
|
+
# Get detailed node group info
|
|
113
|
+
ng_response = client.describe_nodegroup(
|
|
114
|
+
clusterName=cluster_name, nodegroupName=nodegroup_name
|
|
115
|
+
)
|
|
116
|
+
nodegroup = ng_response.get("nodegroup", {})
|
|
117
|
+
|
|
118
|
+
ng_arn = nodegroup.get("nodegroupArn", "")
|
|
119
|
+
|
|
120
|
+
# Extract tags
|
|
121
|
+
tags = nodegroup.get("tags", {})
|
|
122
|
+
|
|
123
|
+
# Extract creation date
|
|
124
|
+
created_at = nodegroup.get("createdAt")
|
|
125
|
+
|
|
126
|
+
# Create resource
|
|
127
|
+
resource = Resource(
|
|
128
|
+
arn=ng_arn,
|
|
129
|
+
resource_type="AWS::EKS::Nodegroup",
|
|
130
|
+
name=f"{cluster_name}/{nodegroup_name}",
|
|
131
|
+
region=self.region,
|
|
132
|
+
tags=tags,
|
|
133
|
+
config_hash=compute_config_hash(nodegroup),
|
|
134
|
+
created_at=created_at,
|
|
135
|
+
raw_config=nodegroup,
|
|
136
|
+
)
|
|
137
|
+
resources.append(resource)
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
self.logger.debug(f"Error processing node group {nodegroup_name}: {e}")
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
except Exception as e:
|
|
144
|
+
self.logger.debug(f"Error collecting node groups for cluster {cluster_name}: {e}")
|
|
145
|
+
|
|
146
|
+
return resources
|
|
147
|
+
|
|
148
|
+
def _collect_fargate_profiles(self, cluster_names: List[str]) -> List[Resource]:
|
|
149
|
+
"""Collect EKS Fargate profiles for given clusters.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
cluster_names: List of cluster names to collect Fargate profiles from
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
List of Fargate profile resources
|
|
156
|
+
"""
|
|
157
|
+
resources = []
|
|
158
|
+
client = self._create_client()
|
|
159
|
+
|
|
160
|
+
for cluster_name in cluster_names:
|
|
161
|
+
try:
|
|
162
|
+
paginator = client.get_paginator("list_fargate_profiles")
|
|
163
|
+
for page in paginator.paginate(clusterName=cluster_name):
|
|
164
|
+
for profile_name in page.get("fargateProfileNames", []):
|
|
165
|
+
try:
|
|
166
|
+
# Get detailed Fargate profile info
|
|
167
|
+
profile_response = client.describe_fargate_profile(
|
|
168
|
+
clusterName=cluster_name, fargateProfileName=profile_name
|
|
169
|
+
)
|
|
170
|
+
profile = profile_response.get("fargateProfile", {})
|
|
171
|
+
|
|
172
|
+
profile_arn = profile.get("fargateProfileArn", "")
|
|
173
|
+
|
|
174
|
+
# Extract tags
|
|
175
|
+
tags = profile.get("tags", {})
|
|
176
|
+
|
|
177
|
+
# Extract creation date
|
|
178
|
+
created_at = profile.get("createdAt")
|
|
179
|
+
|
|
180
|
+
# Create resource
|
|
181
|
+
resource = Resource(
|
|
182
|
+
arn=profile_arn,
|
|
183
|
+
resource_type="AWS::EKS::FargateProfile",
|
|
184
|
+
name=f"{cluster_name}/{profile_name}",
|
|
185
|
+
region=self.region,
|
|
186
|
+
tags=tags,
|
|
187
|
+
config_hash=compute_config_hash(profile),
|
|
188
|
+
created_at=created_at,
|
|
189
|
+
raw_config=profile,
|
|
190
|
+
)
|
|
191
|
+
resources.append(resource)
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
self.logger.debug(f"Error processing Fargate profile {profile_name}: {e}")
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self.logger.debug(f"Error collecting Fargate profiles for cluster {cluster_name}: {e}")
|
|
199
|
+
|
|
200
|
+
return resources
|