argus-cloud-optimizer 0.4.1__tar.gz → 0.6.0__tar.gz
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.
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/.env.example +31 -0
- {argus_cloud_optimizer-0.4.1/argus_cloud_optimizer.egg-info → argus_cloud_optimizer-0.6.0}/PKG-INFO +91 -13
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/README.md +89 -12
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/cloudwatch.py +10 -3
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/cost_management.py +1 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/monitor.py +10 -3
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/base.py +5 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/cloud_monitoring.py +10 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0/argus_cloud_optimizer.egg-info}/PKG-INFO +91 -13
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/argus_cloud_optimizer.egg-info/SOURCES.txt +21 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/argus_cloud_optimizer.egg-info/requires.txt +1 -0
- argus_cloud_optimizer-0.6.0/core/__version__.py +1 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/agent/chat.py +8 -4
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/agent/loop.py +133 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/agent/prompts.py +36 -3
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/config.py +16 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/models/finding.py +8 -0
- argus_cloud_optimizer-0.6.0/core/registry/__init__.py +52 -0
- argus_cloud_optimizer-0.6.0/core/registry/aws.py +578 -0
- argus_cloud_optimizer-0.6.0/core/registry/azure.py +699 -0
- argus_cloud_optimizer-0.6.0/core/registry/factory.py +49 -0
- argus_cloud_optimizer-0.6.0/core/registry/gcp.py +759 -0
- argus_cloud_optimizer-0.6.0/core/registry/models.py +26 -0
- argus_cloud_optimizer-0.6.0/core/registry/registry.py +62 -0
- argus_cloud_optimizer-0.6.0/core/remediation/__init__.py +376 -0
- argus_cloud_optimizer-0.6.0/core/remediation/audit.py +57 -0
- argus_cloud_optimizer-0.6.0/core/remediation/engine.py +244 -0
- argus_cloud_optimizer-0.6.0/core/remediation/loader.py +316 -0
- argus_cloud_optimizer-0.6.0/core/remediation/models.py +126 -0
- argus_cloud_optimizer-0.6.0/core/remediation/rightsizing.py +164 -0
- argus_cloud_optimizer-0.6.0/core/remediation/validator.py +196 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/generator.py +32 -3
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/html.py +31 -4
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/multi_cloud.py +4 -2
- argus_cloud_optimizer-0.6.0/docs/blog/index.md +2 -0
- argus_cloud_optimizer-0.6.0/docs/blog/posts/react-vs-rule-based.md +113 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/concepts/adapters.md +15 -12
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/concepts/ai-providers.md +3 -3
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/concepts/ai-reasoning.md +11 -10
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/concepts/how-it-works.md +28 -28
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/contributing/development.md +25 -9
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/contributing/index.md +2 -2
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/contributing/new-adapter.md +37 -19
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/contributing/new-ai-provider.md +12 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/deployment/aws.md +20 -11
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/deployment/azure.md +17 -12
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/deployment/gcp.md +15 -5
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/deployment/multi-account.md +9 -11
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/getting-started/configuration.md +15 -6
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/getting-started/first-scan.md +7 -6
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/getting-started/index.md +2 -2
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/getting-started/quickstart.md +18 -9
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/index.md +24 -27
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/reference/env-vars.md +30 -7
- argus_cloud_optimizer-0.6.0/docs/reference/iam-permissions.md +173 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/reference/report-schema.md +15 -5
- argus_cloud_optimizer-0.6.0/docs/reference/resource-types.md +139 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/reference/security.md +19 -11
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/reference/troubleshooting.md +24 -11
- argus_cloud_optimizer-0.6.0/docs/remediation/cli.md +215 -0
- argus_cloud_optimizer-0.6.0/docs/remediation/index.md +48 -0
- argus_cloud_optimizer-0.6.0/docs/remediation/jira.md +132 -0
- argus_cloud_optimizer-0.6.0/docs/remediation/policies.md +141 -0
- argus_cloud_optimizer-0.6.0/docs/roadmap.md +118 -0
- argus_cloud_optimizer-0.6.0/entrypoints/_remediation.py +159 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/entrypoints/aws_lambda.py +10 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/entrypoints/azure_function.py +10 -1
- argus_cloud_optimizer-0.6.0/entrypoints/cli.py +1154 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/entrypoints/gcp_cloudrun.py +10 -1
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/pyproject.toml +1 -0
- argus_cloud_optimizer-0.4.1/core/__version__.py +0 -1
- argus_cloud_optimizer-0.4.1/docs/reference/iam-permissions.md +0 -145
- argus_cloud_optimizer-0.4.1/docs/reference/resource-types.md +0 -103
- argus_cloud_optimizer-0.4.1/docs/roadmap.md +0 -80
- argus_cloud_optimizer-0.4.1/entrypoints/cli.py +0 -387
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/LICENSE +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/MANIFEST.in +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/accounts.yaml.example +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/adapter.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/auth.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/cloudtrail.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/config.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/cost_explorer.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/resource_explorer.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/aws/retry.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/activity_log.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/adapter.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/resource_graph.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/retry.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/adapter.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/asset_inventory.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/billing.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/cloud_logging.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/retry.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/anthropic.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/azure_openai.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/base.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/bedrock.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/ai/vertexai.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/argus_cloud_optimizer.egg-info/dependency_links.txt +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/argus_cloud_optimizer.egg-info/entry_points.txt +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/argus_cloud_optimizer.egg-info/top_level.txt +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/agent/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/log.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/models/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/py.typed +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/comparison.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/delivery.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/reports/export.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/secrets.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/token_tracker.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/core/validation.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/deploy/aws/multi-account/hub/template.yaml +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/deploy/aws/multi-account/spoke-role.yaml +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/deploy/aws/single-account/template.yaml +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/deploy/azure/function-app.bicep +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/deploy/gcp/deploy.sh +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/concepts/index.md +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/deployment/index.md +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/docs/reference/index.md +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/entrypoints/__init__.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/entrypoints/cli_chat.py +0 -0
- {argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/setup.cfg +0 -0
|
@@ -68,6 +68,37 @@ REPORT_FORMAT=json,html # comma-separated: json, html, pdf,
|
|
|
68
68
|
# Useful for local testing without a real webhook.
|
|
69
69
|
DRY_RUN=false
|
|
70
70
|
|
|
71
|
+
# -----------------------------------------------------------------------------
|
|
72
|
+
# Remediation — Policy Engine + Jira Integration
|
|
73
|
+
# Policies are YAML files in ARGUS_POLICY_DIR. Argus never executes changes;
|
|
74
|
+
# it creates Jira tickets for human review. See config/policies/ for examples
|
|
75
|
+
# and CONTRIBUTING.md for how to write policies.
|
|
76
|
+
# -----------------------------------------------------------------------------
|
|
77
|
+
# Opt-in: create Jira tickets automatically after every scan (default: false).
|
|
78
|
+
# `argus policies apply --confirm` works without this; it is already explicit.
|
|
79
|
+
REMEDIATION_ENABLED=false
|
|
80
|
+
|
|
81
|
+
# Jira credentials — required for `argus policies apply --confirm`
|
|
82
|
+
# Create an API token at: https://id.atlassian.com/manage-profile/security/api-tokens
|
|
83
|
+
JIRA_BASE_URL=https://your-org.atlassian.net
|
|
84
|
+
JIRA_USER_EMAIL=bot@your-org.com
|
|
85
|
+
JIRA_API_TOKEN= # Atlassian API token (not your password)
|
|
86
|
+
|
|
87
|
+
# Directory containing your *.yaml policy files (one policy per file).
|
|
88
|
+
# Copy from config/policies.example/ to get started, then customise.
|
|
89
|
+
# Used by both `argus policies ...` and scheduled scans. The older name
|
|
90
|
+
# ARGUS_POLICIES_DIR is still accepted; if both are set, this one wins.
|
|
91
|
+
ARGUS_POLICY_DIR=./config/policies
|
|
92
|
+
|
|
93
|
+
# Path to integrations config (project key, labels, priority map).
|
|
94
|
+
# Copy config/integrations.yaml.example → config/integrations.yaml and fill in.
|
|
95
|
+
# config/integrations.yaml is gitignored — never commit credentials.
|
|
96
|
+
ARGUS_INTEGRATIONS_CONFIG=./config/integrations.yaml
|
|
97
|
+
|
|
98
|
+
# Audit log — every `apply --confirm` appends one JSONL line mapping
|
|
99
|
+
# proposal_id → jira_key → jira_url for traceability.
|
|
100
|
+
ARGUS_AUDIT_LOG=./local_reports/audit.jsonl
|
|
101
|
+
|
|
71
102
|
# -----------------------------------------------------------------------------
|
|
72
103
|
# Logging
|
|
73
104
|
# -----------------------------------------------------------------------------
|
{argus_cloud_optimizer-0.4.1/argus_cloud_optimizer.egg-info → argus_cloud_optimizer-0.6.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: argus-cloud-optimizer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Argus — AI Cloud Detective for AWS, GCP, and Azure
|
|
5
5
|
Author-email: Vamshi Siddarth Gaddam <vamshisiddarth02@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -59,6 +59,7 @@ Requires-Dist: types-python-dateutil>=2.9.0; extra == "dev"
|
|
|
59
59
|
Requires-Dist: boto3-stubs[bedrock-runtime,ce,cloudwatch,resourceexplorer2,s3,sts]>=1.35.36; extra == "dev"
|
|
60
60
|
Requires-Dist: mkdocs-material>=9.5.44; extra == "dev"
|
|
61
61
|
Requires-Dist: mkdocs-minify-plugin>=0.8.0; extra == "dev"
|
|
62
|
+
Requires-Dist: mike>=2.1.0; extra == "dev"
|
|
62
63
|
Dynamic: license-file
|
|
63
64
|
|
|
64
65
|
<p align="center">
|
|
@@ -100,15 +101,26 @@ Top findings
|
|
|
100
101
|
|
|
101
102
|
---
|
|
102
103
|
|
|
103
|
-
## 🆕 What's new in v0.
|
|
104
|
+
## 🆕 What's new in v0.6.0
|
|
104
105
|
|
|
105
|
-
> **
|
|
106
|
+
> **Breaking:** scheduled scans now create Jira tickets only when `REMEDIATION_ENABLED=true` (default `false`). If you ran v0.5.0 with Jira configured, set it to keep tickets flowing. `argus policies apply --confirm` is unaffected.
|
|
106
107
|
|
|
107
|
-
- **
|
|
108
|
-
- **
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
- **Dry runs never touch Jira** — `DRY_RUN=true` / `--dry-run` previously still created real tickets when Jira was configured.
|
|
109
|
+
- **One summary log line per remediation run**, with `error_type` on failures.
|
|
110
|
+
- **`ARGUS_POLICY_DIR`** is read by both the CLI and scheduled scans (`ARGUS_POLICIES_DIR` still accepted).
|
|
111
|
+
|
|
112
|
+
[Full changelog →](CHANGELOG.md) · [Project status →](docs/roadmap.md)
|
|
113
|
+
|
|
114
|
+
### v0.5.0
|
|
115
|
+
|
|
116
|
+
> **Remediation is now first-class.** Argus goes beyond reporting — it generates Jira tickets, suggests exact resize targets, and tracks acceptance rates.
|
|
117
|
+
|
|
118
|
+
- **Policy engine** — YAML-driven policies match AI findings and generate prioritised `ChangeProposal` objects (weight-ordered, one proposal per resource per scan). Ships with 13 real-world policies across AWS, GCP, and Azure.
|
|
119
|
+
- **Jira integration** — idempotent ticket creation with ADF-structured descriptions (metrics table, runbook code block, snapshot fingerprint for diff-on-update). Dedup via deterministic label; diff-comment on re-scan.
|
|
120
|
+
- **Rightsizing recommendations** — `resize` and `reduce_nodes` proposals now include a specific target tier or node count derived from observed CPU% (e.g., "Recommend db.t3.small — observed CPU ~8%").
|
|
121
|
+
- **Acceptance rate monitoring** — `argus policies stats` reads the append-only audit log and prints per-policy proposal counts, Jira new/update splits, and cloud breakdown.
|
|
122
|
+
- **JSONL audit log** — every `apply --confirm` appends a line to `local_reports/audit.jsonl` mapping `proposal_id → jira_key → jira_url`.
|
|
123
|
+
- **1756 tests** — all pass offline, no cloud credentials needed.
|
|
112
124
|
|
|
113
125
|
[Full changelog →](CHANGELOG.md)
|
|
114
126
|
|
|
@@ -129,6 +141,67 @@ The **Full report** button links to a self-contained HTML file (S3 / GCS / Azure
|
|
|
129
141
|
|
|
130
142
|
---
|
|
131
143
|
|
|
144
|
+
## Remediation
|
|
145
|
+
|
|
146
|
+
Argus stays **strictly read-only** — it never mutates a resource. Instead, the policy engine turns AI findings into prioritised Jira tickets that a human reviews and acts on.
|
|
147
|
+
|
|
148
|
+
### How it works
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
argus scan → scan_report.json
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
argus policies plan --report scan_report.json
|
|
155
|
+
│
|
|
156
|
+
├── evaluates policies in config/policies/ (13 bundled — weight-ordered, first-match-wins)
|
|
157
|
+
├── applies two-tier conditions: cost/priority/idle-days + metric thresholds
|
|
158
|
+
├── computes rightsizing target (e.g. "Recommend db.t3.small — CPU ~8%")
|
|
159
|
+
└── prints proposal table with total savings estimate
|
|
160
|
+
│
|
|
161
|
+
▼ (add --confirm to create tickets)
|
|
162
|
+
argus policies apply --report scan_report.json --confirm
|
|
163
|
+
│
|
|
164
|
+
├── creates Jira ticket per proposal (idempotent — dedup via label)
|
|
165
|
+
├── diff-comments on re-scan if finding changed
|
|
166
|
+
├── appends line to local_reports/audit.jsonl
|
|
167
|
+
└── prints per-ticket status
|
|
168
|
+
|
|
169
|
+
argus policies stats # acceptance rate per policy from audit log
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
To create tickets automatically after every scheduled scan (Lambda, Cloud Run, Azure Function), set `REMEDIATION_ENABLED=true`. It is off by default; `apply --confirm` above works without it.
|
|
173
|
+
|
|
174
|
+
### Bundled policies (13)
|
|
175
|
+
|
|
176
|
+
| Policy | Cloud | Action | Cost floor | Idle days |
|
|
177
|
+
|--------|-------|--------|-----------|-----------|
|
|
178
|
+
| `aws-redshift-snapshot-delete-14d` | AWS | snapshot_delete | $100/mo | 14 |
|
|
179
|
+
| `aws-rds-resize-high-cost-idle` | AWS | resize | $100/mo | — |
|
|
180
|
+
| `aws-ebs-delete-unattached-30d` | AWS | snapshot_delete | — | 30 |
|
|
181
|
+
| `aws-elasticache-delete-idle-30d` | AWS | delete | — | 30 |
|
|
182
|
+
| `aws-elb-delete-idle` | AWS | delete | $10/mo | — |
|
|
183
|
+
| `aws-ec2-stop-idle-14d` | AWS | stop | — | 14 |
|
|
184
|
+
| `aws-lambda-delete-unused-30d` | AWS | delete | — | 30 |
|
|
185
|
+
| `gcp-gke-reduce-nodes-underutilised` | GCP | reduce_nodes | $200/mo | — |
|
|
186
|
+
| `gcp-sql-stop-idle-14d` | GCP | stop | — | 14 |
|
|
187
|
+
| `gcp-compute-stop-idle-7d` | GCP | stop | — | 7 |
|
|
188
|
+
| `azure-aks-reduce-nodes-underutilised` | Azure | reduce_nodes | $150/mo | — |
|
|
189
|
+
| `azure-sql-resize-underutilised` | Azure | resize | $50/mo | — |
|
|
190
|
+
| `azure-vm-stop-idle-14d` | Azure | stop | — | 14 |
|
|
191
|
+
|
|
192
|
+
Each policy also has AI-priority and metric conditions; see [Policies](https://vamshisiddarth.github.io/argus/remediation/policies/#bundled-policies) for the full table.
|
|
193
|
+
|
|
194
|
+
All policies skip resources tagged `environment: prod` (or `production`) and resources tagged `argus-exempt: "true"` — either tag on its own is enough. Add your own in `config/policies/` — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
195
|
+
|
|
196
|
+
### Safety guarantees
|
|
197
|
+
|
|
198
|
+
- **IAM** — read-only roles; write APIs are unavailable even with creds attached
|
|
199
|
+
- **Code** — no cloud SDK write calls anywhere in the codebase
|
|
200
|
+
- **CLI gate** — `apply` is a dry-run by default; `--confirm` is required to create tickets
|
|
201
|
+
- **Human gate** — runbook is printed in the Jira ticket; Argus never executes it
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
132
205
|
## Architecture
|
|
133
206
|
|
|
134
207
|
```
|
|
@@ -180,7 +253,7 @@ docker run --rm \
|
|
|
180
253
|
|
|
181
254
|
```bash
|
|
182
255
|
pip install argus-cloud-optimizer
|
|
183
|
-
argus --version # argus 0.
|
|
256
|
+
argus --version # argus 0.6.0
|
|
184
257
|
argus --help
|
|
185
258
|
```
|
|
186
259
|
|
|
@@ -245,7 +318,7 @@ argus chat # auto-detects cloud
|
|
|
245
318
|
```
|
|
246
319
|
|
|
247
320
|
```
|
|
248
|
-
Argus v0.
|
|
321
|
+
Argus v0.6.0 — AI Cloud Detective
|
|
249
322
|
Cloud: AWS | Accounts: prod (123456789012) | Budget: $1.00/session
|
|
250
323
|
Tip: end a line with \ to continue on the next line.
|
|
251
324
|
Type your question, or /help for commands.
|
|
@@ -545,7 +618,7 @@ Before you invest time deploying Argus, know what it **can't** do yet:
|
|
|
545
618
|
| **AI non-determinism** | By design | The AI decides what's idle — different runs may produce slightly different findings or reasoning. Set `AI_TEMPERATURE=0.0` (default) for most consistent results. |
|
|
546
619
|
| **LLM cost** | Configurable | A full scan of ~200 resources costs ~$0.05–$0.50 in LLM API fees depending on provider. Use `--llm-budget` to set a hard cap (default: $2.00/scan). Large estates (1000+ resources) will hit the budget limit — increase it or use `--max-resources`. |
|
|
547
620
|
| **AWS Resource Explorer setup** | Manual step | You must enable Resource Explorer with an **aggregator index** (typically in `us-east-1`). Without this, Argus cannot discover resources. This is a one-time setup but is easy to miss. |
|
|
548
|
-
| **Write actions** | None | Argus is read-only.
|
|
621
|
+
| **Write actions** | None (by design) | Argus is strictly read-only. The policy engine generates Jira tickets with runbooks — a human reviews and executes. Argus never calls the runbook itself. |
|
|
549
622
|
| **Multi-cloud in one scan** | Not yet | Each `argus` invocation scans one cloud. Use the merge report feature (`core/reports/multi_cloud.py`) to combine results after separate runs. |
|
|
550
623
|
| **Notifications** | Slack + Teams + webhook | No email. Slack/Teams delivery requires a webhook URL. |
|
|
551
624
|
|
|
@@ -587,6 +660,7 @@ argus/
|
|
|
587
660
|
│ ├── agent/loop.py # ReAct agent loop
|
|
588
661
|
│ ├── agent/prompts.py # System prompt + tool schemas
|
|
589
662
|
│ ├── models/finding.py # ResourceFinding dataclass
|
|
663
|
+
│ ├── remediation/ # Policy engine — models, loader, validator, engine, audit, rightsizing
|
|
590
664
|
│ └── reports/ # Report generator, multi-cloud merge, export, notifications
|
|
591
665
|
├── adapters/
|
|
592
666
|
│ ├── base.py # CloudAdapter abstract class
|
|
@@ -599,8 +673,12 @@ argus/
|
|
|
599
673
|
│ ├── bedrock.py # AWS Bedrock (Converse API)
|
|
600
674
|
│ ├── vertexai.py # Vertex AI / Gemini (GCP)
|
|
601
675
|
│ └── azure_openai.py # Azure OpenAI / GPT-4o (Azure)
|
|
676
|
+
├── integrations/
|
|
677
|
+
│ └── jira/ # Ticket lifecycle: create, dedup, diff-comment, ADF formatter, webhook
|
|
678
|
+
├── config/policies/ # 13 bundled YAML policies (AWS / GCP / Azure)
|
|
679
|
+
├── config/policies.example/ # 3 annotated starter templates — copy here to begin
|
|
602
680
|
├── entrypoints/
|
|
603
|
-
│ ├── cli.py # argus
|
|
681
|
+
│ ├── cli.py # argus scan / chat / policies validate|plan|apply|stats|docs
|
|
604
682
|
│ ├── aws_lambda.py # AWS Lambda handler
|
|
605
683
|
│ ├── gcp_cloudrun.py # GCP Cloud Run Job handler
|
|
606
684
|
│ └── azure_function.py # Azure Function timer trigger
|
|
@@ -608,7 +686,7 @@ argus/
|
|
|
608
686
|
│ ├── aws/ # CloudFormation templates
|
|
609
687
|
│ ├── gcp/ # Cloud Run + Scheduler deploy script
|
|
610
688
|
│ └── azure/ # Bicep templates
|
|
611
|
-
└── tests/ #
|
|
689
|
+
└── tests/ # 1756 tests, all pass offline
|
|
612
690
|
```
|
|
613
691
|
|
|
614
692
|
---
|
|
@@ -37,15 +37,26 @@ Top findings
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
-
## 🆕 What's new in v0.
|
|
40
|
+
## 🆕 What's new in v0.6.0
|
|
41
41
|
|
|
42
|
-
> **
|
|
42
|
+
> **Breaking:** scheduled scans now create Jira tickets only when `REMEDIATION_ENABLED=true` (default `false`). If you ran v0.5.0 with Jira configured, set it to keep tickets flowing. `argus policies apply --confirm` is unaffected.
|
|
43
43
|
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
- **Dry runs never touch Jira** — `DRY_RUN=true` / `--dry-run` previously still created real tickets when Jira was configured.
|
|
45
|
+
- **One summary log line per remediation run**, with `error_type` on failures.
|
|
46
|
+
- **`ARGUS_POLICY_DIR`** is read by both the CLI and scheduled scans (`ARGUS_POLICIES_DIR` still accepted).
|
|
47
|
+
|
|
48
|
+
[Full changelog →](CHANGELOG.md) · [Project status →](docs/roadmap.md)
|
|
49
|
+
|
|
50
|
+
### v0.5.0
|
|
51
|
+
|
|
52
|
+
> **Remediation is now first-class.** Argus goes beyond reporting — it generates Jira tickets, suggests exact resize targets, and tracks acceptance rates.
|
|
53
|
+
|
|
54
|
+
- **Policy engine** — YAML-driven policies match AI findings and generate prioritised `ChangeProposal` objects (weight-ordered, one proposal per resource per scan). Ships with 13 real-world policies across AWS, GCP, and Azure.
|
|
55
|
+
- **Jira integration** — idempotent ticket creation with ADF-structured descriptions (metrics table, runbook code block, snapshot fingerprint for diff-on-update). Dedup via deterministic label; diff-comment on re-scan.
|
|
56
|
+
- **Rightsizing recommendations** — `resize` and `reduce_nodes` proposals now include a specific target tier or node count derived from observed CPU% (e.g., "Recommend db.t3.small — observed CPU ~8%").
|
|
57
|
+
- **Acceptance rate monitoring** — `argus policies stats` reads the append-only audit log and prints per-policy proposal counts, Jira new/update splits, and cloud breakdown.
|
|
58
|
+
- **JSONL audit log** — every `apply --confirm` appends a line to `local_reports/audit.jsonl` mapping `proposal_id → jira_key → jira_url`.
|
|
59
|
+
- **1756 tests** — all pass offline, no cloud credentials needed.
|
|
49
60
|
|
|
50
61
|
[Full changelog →](CHANGELOG.md)
|
|
51
62
|
|
|
@@ -66,6 +77,67 @@ The **Full report** button links to a self-contained HTML file (S3 / GCS / Azure
|
|
|
66
77
|
|
|
67
78
|
---
|
|
68
79
|
|
|
80
|
+
## Remediation
|
|
81
|
+
|
|
82
|
+
Argus stays **strictly read-only** — it never mutates a resource. Instead, the policy engine turns AI findings into prioritised Jira tickets that a human reviews and acts on.
|
|
83
|
+
|
|
84
|
+
### How it works
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
argus scan → scan_report.json
|
|
88
|
+
│
|
|
89
|
+
▼
|
|
90
|
+
argus policies plan --report scan_report.json
|
|
91
|
+
│
|
|
92
|
+
├── evaluates policies in config/policies/ (13 bundled — weight-ordered, first-match-wins)
|
|
93
|
+
├── applies two-tier conditions: cost/priority/idle-days + metric thresholds
|
|
94
|
+
├── computes rightsizing target (e.g. "Recommend db.t3.small — CPU ~8%")
|
|
95
|
+
└── prints proposal table with total savings estimate
|
|
96
|
+
│
|
|
97
|
+
▼ (add --confirm to create tickets)
|
|
98
|
+
argus policies apply --report scan_report.json --confirm
|
|
99
|
+
│
|
|
100
|
+
├── creates Jira ticket per proposal (idempotent — dedup via label)
|
|
101
|
+
├── diff-comments on re-scan if finding changed
|
|
102
|
+
├── appends line to local_reports/audit.jsonl
|
|
103
|
+
└── prints per-ticket status
|
|
104
|
+
|
|
105
|
+
argus policies stats # acceptance rate per policy from audit log
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
To create tickets automatically after every scheduled scan (Lambda, Cloud Run, Azure Function), set `REMEDIATION_ENABLED=true`. It is off by default; `apply --confirm` above works without it.
|
|
109
|
+
|
|
110
|
+
### Bundled policies (13)
|
|
111
|
+
|
|
112
|
+
| Policy | Cloud | Action | Cost floor | Idle days |
|
|
113
|
+
|--------|-------|--------|-----------|-----------|
|
|
114
|
+
| `aws-redshift-snapshot-delete-14d` | AWS | snapshot_delete | $100/mo | 14 |
|
|
115
|
+
| `aws-rds-resize-high-cost-idle` | AWS | resize | $100/mo | — |
|
|
116
|
+
| `aws-ebs-delete-unattached-30d` | AWS | snapshot_delete | — | 30 |
|
|
117
|
+
| `aws-elasticache-delete-idle-30d` | AWS | delete | — | 30 |
|
|
118
|
+
| `aws-elb-delete-idle` | AWS | delete | $10/mo | — |
|
|
119
|
+
| `aws-ec2-stop-idle-14d` | AWS | stop | — | 14 |
|
|
120
|
+
| `aws-lambda-delete-unused-30d` | AWS | delete | — | 30 |
|
|
121
|
+
| `gcp-gke-reduce-nodes-underutilised` | GCP | reduce_nodes | $200/mo | — |
|
|
122
|
+
| `gcp-sql-stop-idle-14d` | GCP | stop | — | 14 |
|
|
123
|
+
| `gcp-compute-stop-idle-7d` | GCP | stop | — | 7 |
|
|
124
|
+
| `azure-aks-reduce-nodes-underutilised` | Azure | reduce_nodes | $150/mo | — |
|
|
125
|
+
| `azure-sql-resize-underutilised` | Azure | resize | $50/mo | — |
|
|
126
|
+
| `azure-vm-stop-idle-14d` | Azure | stop | — | 14 |
|
|
127
|
+
|
|
128
|
+
Each policy also has AI-priority and metric conditions; see [Policies](https://vamshisiddarth.github.io/argus/remediation/policies/#bundled-policies) for the full table.
|
|
129
|
+
|
|
130
|
+
All policies skip resources tagged `environment: prod` (or `production`) and resources tagged `argus-exempt: "true"` — either tag on its own is enough. Add your own in `config/policies/` — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
131
|
+
|
|
132
|
+
### Safety guarantees
|
|
133
|
+
|
|
134
|
+
- **IAM** — read-only roles; write APIs are unavailable even with creds attached
|
|
135
|
+
- **Code** — no cloud SDK write calls anywhere in the codebase
|
|
136
|
+
- **CLI gate** — `apply` is a dry-run by default; `--confirm` is required to create tickets
|
|
137
|
+
- **Human gate** — runbook is printed in the Jira ticket; Argus never executes it
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
69
141
|
## Architecture
|
|
70
142
|
|
|
71
143
|
```
|
|
@@ -117,7 +189,7 @@ docker run --rm \
|
|
|
117
189
|
|
|
118
190
|
```bash
|
|
119
191
|
pip install argus-cloud-optimizer
|
|
120
|
-
argus --version # argus 0.
|
|
192
|
+
argus --version # argus 0.6.0
|
|
121
193
|
argus --help
|
|
122
194
|
```
|
|
123
195
|
|
|
@@ -182,7 +254,7 @@ argus chat # auto-detects cloud
|
|
|
182
254
|
```
|
|
183
255
|
|
|
184
256
|
```
|
|
185
|
-
Argus v0.
|
|
257
|
+
Argus v0.6.0 — AI Cloud Detective
|
|
186
258
|
Cloud: AWS | Accounts: prod (123456789012) | Budget: $1.00/session
|
|
187
259
|
Tip: end a line with \ to continue on the next line.
|
|
188
260
|
Type your question, or /help for commands.
|
|
@@ -482,7 +554,7 @@ Before you invest time deploying Argus, know what it **can't** do yet:
|
|
|
482
554
|
| **AI non-determinism** | By design | The AI decides what's idle — different runs may produce slightly different findings or reasoning. Set `AI_TEMPERATURE=0.0` (default) for most consistent results. |
|
|
483
555
|
| **LLM cost** | Configurable | A full scan of ~200 resources costs ~$0.05–$0.50 in LLM API fees depending on provider. Use `--llm-budget` to set a hard cap (default: $2.00/scan). Large estates (1000+ resources) will hit the budget limit — increase it or use `--max-resources`. |
|
|
484
556
|
| **AWS Resource Explorer setup** | Manual step | You must enable Resource Explorer with an **aggregator index** (typically in `us-east-1`). Without this, Argus cannot discover resources. This is a one-time setup but is easy to miss. |
|
|
485
|
-
| **Write actions** | None | Argus is read-only.
|
|
557
|
+
| **Write actions** | None (by design) | Argus is strictly read-only. The policy engine generates Jira tickets with runbooks — a human reviews and executes. Argus never calls the runbook itself. |
|
|
486
558
|
| **Multi-cloud in one scan** | Not yet | Each `argus` invocation scans one cloud. Use the merge report feature (`core/reports/multi_cloud.py`) to combine results after separate runs. |
|
|
487
559
|
| **Notifications** | Slack + Teams + webhook | No email. Slack/Teams delivery requires a webhook URL. |
|
|
488
560
|
|
|
@@ -524,6 +596,7 @@ argus/
|
|
|
524
596
|
│ ├── agent/loop.py # ReAct agent loop
|
|
525
597
|
│ ├── agent/prompts.py # System prompt + tool schemas
|
|
526
598
|
│ ├── models/finding.py # ResourceFinding dataclass
|
|
599
|
+
│ ├── remediation/ # Policy engine — models, loader, validator, engine, audit, rightsizing
|
|
527
600
|
│ └── reports/ # Report generator, multi-cloud merge, export, notifications
|
|
528
601
|
├── adapters/
|
|
529
602
|
│ ├── base.py # CloudAdapter abstract class
|
|
@@ -536,8 +609,12 @@ argus/
|
|
|
536
609
|
│ ├── bedrock.py # AWS Bedrock (Converse API)
|
|
537
610
|
│ ├── vertexai.py # Vertex AI / Gemini (GCP)
|
|
538
611
|
│ └── azure_openai.py # Azure OpenAI / GPT-4o (Azure)
|
|
612
|
+
├── integrations/
|
|
613
|
+
│ └── jira/ # Ticket lifecycle: create, dedup, diff-comment, ADF formatter, webhook
|
|
614
|
+
├── config/policies/ # 13 bundled YAML policies (AWS / GCP / Azure)
|
|
615
|
+
├── config/policies.example/ # 3 annotated starter templates — copy here to begin
|
|
539
616
|
├── entrypoints/
|
|
540
|
-
│ ├── cli.py # argus
|
|
617
|
+
│ ├── cli.py # argus scan / chat / policies validate|plan|apply|stats|docs
|
|
541
618
|
│ ├── aws_lambda.py # AWS Lambda handler
|
|
542
619
|
│ ├── gcp_cloudrun.py # GCP Cloud Run Job handler
|
|
543
620
|
│ └── azure_function.py # Azure Function timer trigger
|
|
@@ -545,7 +622,7 @@ argus/
|
|
|
545
622
|
│ ├── aws/ # CloudFormation templates
|
|
546
623
|
│ ├── gcp/ # Cloud Run + Scheduler deploy script
|
|
547
624
|
│ └── azure/ # Bicep templates
|
|
548
|
-
└── tests/ #
|
|
625
|
+
└── tests/ # 1756 tests, all pass offline
|
|
549
626
|
```
|
|
550
627
|
|
|
551
628
|
---
|
|
@@ -11,11 +11,18 @@ from botocore.exceptions import ClientError
|
|
|
11
11
|
from adapters.aws.config import BOTO_TIMEOUT_CONFIG
|
|
12
12
|
from adapters.aws.retry import retry_on_transient
|
|
13
13
|
from adapters.base import MetricSummary
|
|
14
|
+
from core.registry import get_registry
|
|
14
15
|
|
|
15
16
|
logger = structlog.get_logger(__name__)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
def _metrics_for(resource_type: str) -> list[tuple[str, str, str, str]] | None:
|
|
20
|
+
spec = get_registry().get(resource_type)
|
|
21
|
+
if spec and spec.metrics:
|
|
22
|
+
return [(m.name, m.namespace, m.stat, m.dimension_key) for m in spec.metrics]
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
19
26
|
_METRICS: dict[str, list[tuple[str, str, str, str]]] = {
|
|
20
27
|
"AWS::EC2::Instance": [
|
|
21
28
|
("CPUUtilization", "AWS/EC2", "Average", "InstanceId"),
|
|
@@ -282,7 +289,7 @@ def get_metrics(
|
|
|
282
289
|
gives the AI the current instance size so it can recommend a specific
|
|
283
290
|
smaller tier rather than a generic "consider downsizing".
|
|
284
291
|
"""
|
|
285
|
-
metric_defs =
|
|
292
|
+
metric_defs = _metrics_for(resource_type)
|
|
286
293
|
if not metric_defs:
|
|
287
294
|
metric_defs = _discover_metrics(session, resource_id, resource_type)
|
|
288
295
|
if not metric_defs:
|
{argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/azure/cost_management.py
RENAMED
|
@@ -102,7 +102,7 @@ def _query_batch(
|
|
|
102
102
|
aggregation={"totalCost": {"name": "PreTaxCost", "function": "Sum"}}, # type: ignore[dict-item]
|
|
103
103
|
grouping=[QueryGrouping(type="Dimension", name="ResourceId")],
|
|
104
104
|
filter=QueryFilter(
|
|
105
|
-
dimensions=QueryComparisonExpression(
|
|
105
|
+
dimensions=QueryComparisonExpression( # type: ignore[call-overload]
|
|
106
106
|
name="ResourceId",
|
|
107
107
|
operator="In",
|
|
108
108
|
values=resource_ids,
|
|
@@ -10,9 +10,18 @@ from azure.monitor.query import MetricAggregationType, MetricsQueryClient
|
|
|
10
10
|
|
|
11
11
|
from adapters.azure.retry import retry_on_transient
|
|
12
12
|
from adapters.base import MetricSummary
|
|
13
|
+
from core.registry import get_registry
|
|
13
14
|
|
|
14
15
|
logger = structlog.get_logger(__name__)
|
|
15
16
|
|
|
17
|
+
|
|
18
|
+
def _metrics_for(resource_type: str) -> list[tuple[str, str]] | None:
|
|
19
|
+
spec = get_registry().get(resource_type.lower())
|
|
20
|
+
if spec and spec.metrics:
|
|
21
|
+
return [(m.name, m.stat) for m in spec.metrics]
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
16
25
|
# (MetricName, AggregationType)
|
|
17
26
|
_METRICS: dict[str, list[tuple[str, str]]] = {
|
|
18
27
|
# Virtual Machines
|
|
@@ -271,7 +280,7 @@ def get_metrics(
|
|
|
271
280
|
Fetch Azure Monitor metrics for a resource.
|
|
272
281
|
Falls back to querying available metric definitions for unknown resource types.
|
|
273
282
|
"""
|
|
274
|
-
metric_defs =
|
|
283
|
+
metric_defs = _metrics_for(resource_type)
|
|
275
284
|
if not metric_defs:
|
|
276
285
|
metric_defs = _discover_metrics(resource_id, resource_type, credential)
|
|
277
286
|
if not metric_defs:
|
|
@@ -363,8 +372,6 @@ def _discover_metrics(
|
|
|
363
372
|
credential: Any = None,
|
|
364
373
|
) -> list[tuple[str, str]]:
|
|
365
374
|
"""Auto-discover available metrics for unknown Azure resource types."""
|
|
366
|
-
from azure.monitor.query import MetricsQueryClient
|
|
367
|
-
|
|
368
375
|
cred = credential or DefaultAzureCredential()
|
|
369
376
|
client = MetricsQueryClient(cred, connection_timeout=10, read_timeout=60)
|
|
370
377
|
discovered: list[tuple[str, str]] = []
|
|
@@ -52,7 +52,11 @@ class CloudAdapter(ABC):
|
|
|
52
52
|
"""
|
|
53
53
|
Abstract cloud adapter. One implementation per cloud provider.
|
|
54
54
|
The agent loop only ever calls these four methods — never raw SDK clients.
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
READ-ONLY CONTRACT: All implementations must be strictly read-only.
|
|
57
|
+
No method may create, modify, delete, or mutate any cloud resource.
|
|
58
|
+
This contract is enforced by tests that scan adapter subclasses for
|
|
59
|
+
mutating method names. Violations fail CI.
|
|
56
60
|
"""
|
|
57
61
|
|
|
58
62
|
@abstractmethod
|
{argus_cloud_optimizer-0.4.1 → argus_cloud_optimizer-0.6.0}/adapters/gcp/cloud_monitoring.py
RENAMED
|
@@ -10,9 +10,18 @@ from google.protobuf.timestamp_pb2 import Timestamp
|
|
|
10
10
|
|
|
11
11
|
from adapters.base import MetricSummary
|
|
12
12
|
from adapters.gcp.retry import retry_on_transient
|
|
13
|
+
from core.registry import get_registry
|
|
13
14
|
|
|
14
15
|
logger = structlog.get_logger(__name__)
|
|
15
16
|
|
|
17
|
+
|
|
18
|
+
def _metrics_for(resource_type: str) -> list[tuple[str, str]] | None:
|
|
19
|
+
spec = get_registry().get(resource_type)
|
|
20
|
+
if spec and spec.metrics:
|
|
21
|
+
return [(m.name, m.stat) for m in spec.metrics]
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
16
25
|
# (MetricType, Stat, label_key_for_resource_filter)
|
|
17
26
|
# Stat: "mean" for utilisation, "sum" for throughput/count.
|
|
18
27
|
# label_key: the monitored-resource label used to filter to this specific resource.
|
|
@@ -254,7 +263,7 @@ def get_metrics(
|
|
|
254
263
|
Fetch Cloud Monitoring metrics for a GCP resource.
|
|
255
264
|
Falls back to listing available metrics for unknown resource types.
|
|
256
265
|
"""
|
|
257
|
-
metric_defs =
|
|
266
|
+
metric_defs = _metrics_for(resource_type)
|
|
258
267
|
if not metric_defs:
|
|
259
268
|
metric_defs = _discover_metrics(project_id, resource_id, resource_type)
|
|
260
269
|
if not metric_defs:
|