cloudburn 0.16.0 → 0.17.0
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.
- package/README.md +21 -0
- package/dist/cli.js +50 -9
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -115,6 +115,27 @@ cloudburn rules list --service ec2 --source discovery --severity high
|
|
|
115
115
|
|
|
116
116
|
The discovery config equivalent is `discovery.fail-on`.
|
|
117
117
|
|
|
118
|
+
Discovery has a five-minute deadline. Use `--timeout <seconds>` to change it, for example `cloudburn discover --timeout 600`. If the deadline expires, the command exits with code 2 and reports the timeout.
|
|
119
|
+
|
|
120
|
+
Discovery reuses fresh, complete evidence from a per-user cache at `$XDG_CACHE_HOME/cloudburn/evidence`, or
|
|
121
|
+
`~/.cache/cloudburn/evidence` when `XDG_CACHE_HOME` is unset. Rules and configuration are evaluated on every run.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
cloudburn discover --cache refresh
|
|
125
|
+
cloudburn discover --cache off
|
|
126
|
+
cloudburn discover --cache-dir /path/to/evidence --cache-context policy-revision-2
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`--cache refresh` collects current evidence and never silently falls back when collection fails. `--cache off` bypasses
|
|
130
|
+
persistent reads and writes. Customer evidence reuse requires a safe temporary AWS credential session scope or an
|
|
131
|
+
explicit `--cache-context` identifying the effective authorization and session-policy revision. An account ID or role
|
|
132
|
+
ARN alone is insufficient. Change the context when permissions change. Public pricing is cached independently of
|
|
133
|
+
customer evidence, and credentials are never stored.
|
|
134
|
+
|
|
135
|
+
Table output includes a freshness summary; `--format json` includes full evidence provenance and coverage. TTLs are
|
|
136
|
+
tunable starting policies rather than measured optimal defaults. See the [cache options](../../docs/reference/commands.md#discovery-evidence-cache)
|
|
137
|
+
and [SDK README](../sdk/README.md) for modes, defaults, and freshness tradeoffs.
|
|
138
|
+
|
|
118
139
|
The CLI targets one region per run. Multi-region discovery remains available through the SDK and still needs an AWS Resource Explorer aggregator plus an unfiltered default view in the aggregator region.
|
|
119
140
|
`CLDBRN-AWS-TAGGING-1` is opt-in and requires an accessible aggregator; a local-only setup cannot run account-wide tagging discovery.
|
|
120
141
|
`CLDBRN-AWS-LAMBDA-4` is opt-in and requires AWS Compute Optimizer enrollment.
|
package/dist/cli.js
CHANGED
|
@@ -654,20 +654,34 @@ ${regionsTable}`;
|
|
|
654
654
|
const findingRows = projectFindingRows(response.result);
|
|
655
655
|
const diagnosticRows = projectDiagnosticRows(response.result);
|
|
656
656
|
const suppressedCount = response.result.suppressed?.length ?? 0;
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
657
|
+
const withScanSummary = (content) => {
|
|
658
|
+
const sections = [content];
|
|
659
|
+
if (suppressedCount > 0) {
|
|
660
|
+
sections.push(`Suppressed: ${suppressedCount}`);
|
|
661
|
+
}
|
|
662
|
+
const evidence = response.result.evidence ?? [];
|
|
663
|
+
if (evidence.length > 0) {
|
|
664
|
+
const cachedCount = evidence.filter((entry) => entry.source === "cache").length;
|
|
665
|
+
const incompleteCount = evidence.filter((entry) => !entry.complete).length;
|
|
666
|
+
const oldestObservation = evidence.map((entry) => entry.observedAt).sort()[0];
|
|
667
|
+
sections.push(
|
|
668
|
+
`Evidence: ${cachedCount} cached, ${evidence.length - cachedCount} collected; ${incompleteCount} incomplete.
|
|
669
|
+
Oldest observation: ${oldestObservation}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
return sections.join("\n\n");
|
|
673
|
+
};
|
|
660
674
|
if (findingRows.length === 0 && diagnosticRows.length === 0) {
|
|
661
|
-
return
|
|
675
|
+
return withScanSummary(suppressedCount > 0 ? "No active findings." : "No findings.");
|
|
662
676
|
}
|
|
663
677
|
if (findingRows.length === 0) {
|
|
664
|
-
return
|
|
678
|
+
return withScanSummary(`Diagnostics
|
|
665
679
|
${renderAsciiTable(diagnosticRows, diagnosticColumns)}`);
|
|
666
680
|
}
|
|
667
681
|
if (diagnosticRows.length === 0) {
|
|
668
|
-
return
|
|
682
|
+
return withScanSummary(renderAsciiTable(findingRows, scanColumns));
|
|
669
683
|
}
|
|
670
|
-
return
|
|
684
|
+
return withScanSummary(
|
|
671
685
|
`${renderAsciiTable(findingRows, scanColumns)}
|
|
672
686
|
|
|
673
687
|
Diagnostics
|
|
@@ -1059,6 +1073,8 @@ var registerConfigCommand = (program) => {
|
|
|
1059
1073
|
};
|
|
1060
1074
|
|
|
1061
1075
|
// src/commands/discover.ts
|
|
1076
|
+
import { homedir } from "os";
|
|
1077
|
+
import { join as join2 } from "path";
|
|
1062
1078
|
import {
|
|
1063
1079
|
assertSupportedAwsRegion,
|
|
1064
1080
|
CloudBurnClient
|
|
@@ -1142,6 +1158,19 @@ var validateServiceList = (mode, services) => {
|
|
|
1142
1158
|
};
|
|
1143
1159
|
|
|
1144
1160
|
// src/commands/discover.ts
|
|
1161
|
+
var parseDiscoveryTimeout = (value) => {
|
|
1162
|
+
const seconds = Number(value);
|
|
1163
|
+
if (!Number.isInteger(seconds) || seconds < 1 || seconds > 2147483) {
|
|
1164
|
+
throw new InvalidArgumentError3("Timeout must be a whole number of seconds between 1 and 2147483.");
|
|
1165
|
+
}
|
|
1166
|
+
return seconds;
|
|
1167
|
+
};
|
|
1168
|
+
var parseCacheMode = (value) => {
|
|
1169
|
+
if (value === "normal" || value === "refresh" || value === "off") {
|
|
1170
|
+
return value;
|
|
1171
|
+
}
|
|
1172
|
+
throw new InvalidArgumentError3("Cache mode must be normal, refresh, or off.");
|
|
1173
|
+
};
|
|
1145
1174
|
var parseDiscoveryServiceList = (value) => validateServiceList("discovery", parseServiceList(value)) ?? [];
|
|
1146
1175
|
var describeInitializationMessage = (result) => {
|
|
1147
1176
|
const baseMessage = result.indexType === "aggregator" ? result.aggregatorAction === "promoted" ? `Promoted the existing local Resource Explorer index in ${result.aggregatorRegion} to the aggregator.` : result.aggregatorAction === "created" ? `Configured ${result.aggregatorRegion} as the Resource Explorer aggregator.` : result.coverage === "full" ? `Resource Explorer aggregator already exists in ${result.aggregatorRegion}.` : `Resource Explorer aggregator already exists in ${result.aggregatorRegion}, but only ${result.observedStatus.indexedRegionCount} of ${result.observedStatus.totalRegionCount} regions are indexed.` : result.status === "EXISTING" ? `Local Resource Explorer setup already exists in ${result.aggregatorRegion}.` : `Local Resource Explorer setup created in ${result.aggregatorRegion}.`;
|
|
@@ -1249,7 +1278,10 @@ var registerDiscoverCommand = (program) => {
|
|
|
1249
1278
|
"--region <region>",
|
|
1250
1279
|
"AWS region to discover. Defaults to the current AWS region from AWS_REGION when omitted.",
|
|
1251
1280
|
parseAwsRegion
|
|
1252
|
-
).option("--config <path>", "Explicit CloudBurn config file to load (required for config files in CI)").option(
|
|
1281
|
+
).option("--config <path>", "Explicit CloudBurn config file to load (required for config files in CI)").option("--timeout <seconds>", "Maximum discovery duration in seconds (default: 300).", parseDiscoveryTimeout).option("--cache <mode>", "Evidence cache mode: normal, refresh, or off (default: normal).", parseCacheMode).option(
|
|
1282
|
+
"--cache-dir <path>",
|
|
1283
|
+
"Evidence cache directory (default: $XDG_CACHE_HOME/cloudburn/evidence or ~/.cache/cloudburn/evidence)."
|
|
1284
|
+
).option("--cache-context <id>", "Effective authorization/session-policy revision for evidence reuse.").option(
|
|
1253
1285
|
"--enabled-rules <ruleIds>",
|
|
1254
1286
|
"Comma-separated rule IDs to enable. When set, CloudBurn checks only these rules. By default, AWS Core preset rules are enabled.",
|
|
1255
1287
|
parseRuleIdList
|
|
@@ -1269,6 +1301,11 @@ var registerDiscoverCommand = (program) => {
|
|
|
1269
1301
|
const configOverride = toDiscoveryConfigOverride(options);
|
|
1270
1302
|
const loadedConfig = await scanner.loadConfig(options.config);
|
|
1271
1303
|
const discoveryOptions = {
|
|
1304
|
+
cache: {
|
|
1305
|
+
mode: options.cache ?? "normal",
|
|
1306
|
+
directory: options.cacheDir ?? join2(process.env.XDG_CACHE_HOME ?? join2(homedir(), ".cache"), "cloudburn", "evidence"),
|
|
1307
|
+
...options.cacheContext === void 0 ? {} : { authorizationContext: options.cacheContext }
|
|
1308
|
+
},
|
|
1272
1309
|
target: resolveDiscoveryTarget(options.region)
|
|
1273
1310
|
};
|
|
1274
1311
|
if (configOverride !== void 0) {
|
|
@@ -1280,6 +1317,9 @@ var registerDiscoverCommand = (program) => {
|
|
|
1280
1317
|
if (onProgress !== void 0) {
|
|
1281
1318
|
discoveryOptions.onProgress = onProgress;
|
|
1282
1319
|
}
|
|
1320
|
+
if (options.timeout !== void 0) {
|
|
1321
|
+
discoveryOptions.timeoutMs = options.timeout * 1e3;
|
|
1322
|
+
}
|
|
1283
1323
|
const result = await scanner.discover(discoveryOptions);
|
|
1284
1324
|
const format = resolveOutputFormat(command, void 0, loadedConfig.discovery.format ?? "table");
|
|
1285
1325
|
const output = renderResponse({ kind: "scan-result", result }, format);
|
|
@@ -1297,6 +1337,7 @@ var registerDiscoverCommand = (program) => {
|
|
|
1297
1337
|
[
|
|
1298
1338
|
"cloudburn discover",
|
|
1299
1339
|
"cloudburn discover --region eu-central-1",
|
|
1340
|
+
"cloudburn discover --cache refresh",
|
|
1300
1341
|
"cloudburn discover status",
|
|
1301
1342
|
"cloudburn discover init"
|
|
1302
1343
|
]
|
|
@@ -1556,7 +1597,7 @@ var isCliEntrypoint = (moduleUrl, argvEntry = process.argv[1]) => {
|
|
|
1556
1597
|
};
|
|
1557
1598
|
var createProgram = () => {
|
|
1558
1599
|
const program = createCliCommand();
|
|
1559
|
-
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.
|
|
1600
|
+
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.17.0").option("--debug", "Write execution trace messages to stderr").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
|
|
1560
1601
|
configureCliHelp(program);
|
|
1561
1602
|
program.exitOverride();
|
|
1562
1603
|
registerCompletionCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudburn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Cloudburn CLI for cloud cost optimization",
|
|
5
5
|
"homepage": "https://cloudburn.io/docs",
|
|
6
6
|
"bugs": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"commander": "^13.1.0",
|
|
23
|
-
"@cloudburn/sdk": "0.
|
|
23
|
+
"@cloudburn/sdk": "0.34.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@biomejs/biome": "^2.5.3",
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
"build": "tsup",
|
|
52
52
|
"typecheck": "tsc --noEmit",
|
|
53
53
|
"test": "vitest run",
|
|
54
|
+
"test:e2e": "node --test test/e2e/*.test.mjs",
|
|
55
|
+
"test:package": "node --test test/package/*.test.mjs",
|
|
54
56
|
"lint": "biome check --error-on-warnings .",
|
|
55
57
|
"lint:fix": "biome check --write .",
|
|
56
58
|
"clean": "rm -rf dist",
|