Skip to content

Commit 5bc053c

Browse files
authored
Fix minor bugs (#134)
1 parent 57e11c7 commit 5bc053c

15 files changed

Lines changed: 73 additions & 39 deletions

cleancloud/providers/aws/rules/elb_idle.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,10 @@ def _get_metric_sum(
396396
return 1
397397
return 0
398398

399-
except ClientError:
400-
# If we can't get metrics, assume there might be traffic
401-
# to avoid false positives
399+
except ClientError as e:
400+
if e.response["Error"]["Code"] in ("AccessDenied", "UnauthorizedOperation"):
401+
raise PermissionError(
402+
"Missing required IAM permissions: cloudwatch:GetMetricStatistics"
403+
) from e
404+
# Other errors (throttle, transient): assume traffic to avoid false positives
402405
return 1

cleancloud/providers/aws/rules/nat_gateway_idle.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,10 @@ def _get_metric_sum(
234234
total = sum(dp.get("Sum", 0) for dp in datapoints)
235235
return int(total)
236236

237-
except ClientError:
238-
# If we can't get metrics, assume there might be traffic
239-
# to avoid false positives
240-
return 1 # Non-zero to indicate possible traffic
237+
except ClientError as e:
238+
if e.response["Error"]["Code"] in ("AccessDenied", "UnauthorizedOperation"):
239+
raise PermissionError(
240+
"Missing required IAM permissions: cloudwatch:GetMetricStatistics"
241+
) from e
242+
# Other errors (throttle, transient): assume traffic to avoid false positives
243+
return 1

cleancloud/providers/aws/rules/rds_idle.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def find_idle_rds_instances(
143143
details={
144144
"engine": f"{engine} {engine_version}",
145145
"instance_class": instance_class,
146-
"connections_14d": total_connections,
146+
f"connections_{idle_days}d": total_connections,
147147
"estimated_monthly_cost": estimated_monthly_cost,
148148
"multi_az": multi_az,
149149
"allocated_storage_gb": storage_gb,
@@ -200,10 +200,13 @@ def _get_metric_sum(
200200
return 1
201201
return 0
202202

203-
except ClientError:
204-
# If we can't get metrics, assume there might be connections
205-
# to avoid false positives
206-
return 1 # Non-zero to indicate possible connections
203+
except ClientError as e:
204+
if e.response["Error"]["Code"] in ("AccessDenied", "UnauthorizedOperation"):
205+
raise PermissionError(
206+
"Missing required IAM permissions: cloudwatch:GetMetricStatistics"
207+
) from e
208+
# Other errors (throttle, transient): assume connections to avoid false positives
209+
return 1
207210

208211

209212
def _estimate_monthly_cost(instance_class: str, multi_az: bool) -> str:

cleancloud/providers/aws/rules/untagged_resources.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import List, Optional
33

44
import boto3
5+
from botocore.exceptions import ClientError
56

67
from cleancloud.core.confidence import ConfidenceLevel
78
from cleancloud.core.evidence import Evidence
@@ -75,8 +76,11 @@ def find_untagged_resources(
7576
bucket_name = bucket["Name"]
7677
try:
7778
tag_set = s3.get_bucket_tagging(Bucket=bucket_name).get("TagSet", [])
78-
except s3.exceptions.ClientError:
79-
tag_set = []
79+
except ClientError as e:
80+
if e.response["Error"]["Code"] == "NoSuchTagSet":
81+
tag_set = []
82+
else:
83+
raise
8084

8185
if not tag_set:
8286
evidence = Evidence(

cleancloud/providers/azure/rules/app_service_plan_empty.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def find_empty_app_service_plans(
6161
for plan in web_client.app_service_plans.list():
6262
# Azure Web SDK returns display names ("West Europe") not short names ("westeurope")
6363
# Normalize to short name format for consistent output and filtering
64-
location = plan.location.lower().replace(" ", "")
64+
location = (plan.location or "").lower().replace(" ", "")
6565

6666
if region_filter and location != region_filter.lower().replace(" ", ""):
6767
continue

cleancloud/providers/azure/rules/ebs_snapshots_old.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def find_old_snapshots(
4444
)
4545

4646
for snapshot in compute_client.snapshots.list():
47-
if region_filter and snapshot.location != region_filter:
47+
if region_filter and (snapshot.location or "").lower() != region_filter.lower():
4848
continue
4949

5050
if not snapshot.time_created:
@@ -53,7 +53,7 @@ def find_old_snapshots(
5353
age_days = _age_in_days(snapshot.time_created)
5454

5555
if age_days >= max_age_days:
56-
confidence_value = ConfidenceLevel.MEDIUM # conservative
56+
confidence_value = ConfidenceLevel.HIGH
5757
elif age_days >= MIN_AGE_DAYS_MEDIUM:
5858
confidence_value = ConfidenceLevel.MEDIUM
5959
else:
@@ -67,7 +67,7 @@ def find_old_snapshots(
6767
"Disaster recovery or backup intent",
6868
"Future planned usage",
6969
],
70-
time_window=f"{MIN_AGE_DAYS_MEDIUM}-{MIN_AGE_DAYS_HIGH} days",
70+
time_window=f"{MIN_AGE_DAYS_MEDIUM}-{max_age_days} days",
7171
)
7272

7373
# ~$0.05/GB-month for managed snapshots

cleancloud/providers/azure/rules/sql_database_idle.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,17 @@ def find_idle_sql_databases(
118118
if region_filter and server_location != region_filter.lower():
119119
continue
120120

121-
resource_group = _extract_resource_group(server.id)
121+
try:
122+
resource_group = _extract_resource_group(server.id)
123+
except ValueError:
124+
continue
125+
126+
try:
127+
db_list = list(sql_client.databases.list_by_server(resource_group, server.name))
128+
except Exception:
129+
continue
122130

123-
for db in sql_client.databases.list_by_server(resource_group, server.name):
131+
for db in db_list:
124132
# Skip system databases
125133
if db.name == "master":
126134
continue
@@ -157,7 +165,7 @@ def find_idle_sql_databases(
157165

158166
signals = [
159167
f"Zero successful connections for {idle_days} days (Azure Monitor metrics)",
160-
f"Connections (14d sum): {total_connections}",
168+
f"Connections ({idle_days}d sum): {total_connections}",
161169
f"SKU: {sku_name} ({sku_tier})",
162170
f"Server: {server.name}",
163171
]
@@ -200,7 +208,7 @@ def find_idle_sql_databases(
200208
"sku_tier": sku_tier,
201209
"max_size_bytes": getattr(db, "max_size_bytes", None),
202210
"location": db.location,
203-
"connections_14d": total_connections,
211+
f"connections_{idle_days}d": total_connections,
204212
"estimated_monthly_cost": estimated_monthly_cost,
205213
"tags": db.tags,
206214
},

cleancloud/providers/azure/rules/untagged_resources.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def find_untagged_resources(
4444
# Managed Disks
4545
# ======================
4646
for disk in compute_client.disks.list():
47-
if region_filter and disk.location != region_filter:
47+
if region_filter and (disk.location or "").lower() != region_filter.lower():
4848
continue
4949

5050
if disk.tags:
@@ -92,7 +92,7 @@ def find_untagged_resources(
9292
# Snapshots
9393
# ======================
9494
for snap in compute_client.snapshots.list():
95-
if region_filter and snap.location != region_filter:
95+
if region_filter and (snap.location or "").lower() != region_filter.lower():
9696
continue
9797

9898
if snap.tags:

cleancloud/providers/azure/rules/vm_stopped_not_deallocated.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ def find_stopped_not_deallocated_vms(
5454
if region_filter and (vm.location or "").lower() != region_filter.lower():
5555
continue
5656

57-
resource_group = _extract_resource_group(vm.id)
58-
instance_view = compute_client.virtual_machines.instance_view(
59-
resource_group_name=resource_group,
60-
vm_name=vm.name,
61-
)
57+
try:
58+
resource_group = _extract_resource_group(vm.id)
59+
instance_view = compute_client.virtual_machines.instance_view(
60+
resource_group_name=resource_group,
61+
vm_name=vm.name,
62+
)
63+
except Exception:
64+
continue
6265

6366
power_state = _get_power_state(instance_view)
6467

cleancloud/providers/gcp/rules/sql_instance_idle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def find_idle_sql_instances(
280280
evidence=Evidence(
281281
signals_used=signals_used,
282282
signals_not_checked=[
283-
"Short-lived or batch connections (cron jobs, ETL) not visible in Cloud Monitoring connection metrics over the 7-day window",
283+
f"Short-lived or batch connections (cron jobs, ETL) not visible in Cloud Monitoring connection metrics over the {idle_days}-day window",
284284
"Non-TCP workloads or Unix socket connections via Cloud SQL Proxy",
285285
"Scheduled maintenance window",
286286
"Planned reactivation for upcoming sprint",

0 commit comments

Comments
 (0)