cloudburn 0.9.8 → 0.9.10
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 +4 -4
- package/dist/cli.js +92 -75
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -60,21 +60,21 @@ Use `discover` to run the same rules against live AWS resources.
|
|
|
60
60
|
|
|
61
61
|
Run `cloudburn discover init` first. It automatically configures AWS Resource Explorer indexes, which CloudBurn uses as its live service catalog before it evaluates rules.
|
|
62
62
|
|
|
63
|
-
By default, `cloudburn discover` runs against your active AWS region. You can pass `--region <region>` to target
|
|
63
|
+
By default, `cloudburn discover` runs against your active AWS region. You can pass `--region <region>` to target one explicit region.
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
cloudburn discover init
|
|
67
67
|
cloudburn discover
|
|
68
68
|
cloudburn discover --region eu-central-1
|
|
69
|
-
cloudburn discover --region all
|
|
70
69
|
cloudburn discover --config .cloudburn.yml --enabled-rules CLDBRN-AWS-EBS-1
|
|
71
70
|
cloudburn discover --service ec2,s3
|
|
72
|
-
cloudburn discover
|
|
71
|
+
cloudburn --debug discover --region eu-central-1
|
|
73
72
|
cloudburn rules list
|
|
74
73
|
cloudburn rules list --service ec2 --source discovery
|
|
75
74
|
```
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
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.
|
|
77
|
+
Use `--debug` to print SDK and provider execution tracing to `stderr` without changing the normal `stdout` format.
|
|
78
78
|
|
|
79
79
|
## Shell Completion
|
|
80
80
|
|
package/dist/cli.js
CHANGED
|
@@ -557,6 +557,15 @@ var scanColumns = [
|
|
|
557
557
|
{ key: "column", header: "Column" },
|
|
558
558
|
{ key: "message", header: "Message" }
|
|
559
559
|
];
|
|
560
|
+
var diagnosticColumns = [
|
|
561
|
+
{ key: "provider", header: "Provider" },
|
|
562
|
+
{ key: "status", header: "Status" },
|
|
563
|
+
{ key: "ruleId", header: "RuleId" },
|
|
564
|
+
{ key: "source", header: "Source" },
|
|
565
|
+
{ key: "service", header: "Service" },
|
|
566
|
+
{ key: "region", header: "Region" },
|
|
567
|
+
{ key: "message", header: "Message" }
|
|
568
|
+
];
|
|
560
569
|
var ruleListColumns = [
|
|
561
570
|
{ key: "ruleId", header: "RuleId" },
|
|
562
571
|
{ key: "provider", header: "Provider" },
|
|
@@ -637,8 +646,22 @@ ${regionsTable}`;
|
|
|
637
646
|
case "rule-list":
|
|
638
647
|
return renderRuleTable(response.rules, response.emptyMessage);
|
|
639
648
|
case "scan-result": {
|
|
640
|
-
const
|
|
641
|
-
|
|
649
|
+
const findingRows = projectFindingRows(response.result);
|
|
650
|
+
const diagnosticRows = projectDiagnosticRows(response.result);
|
|
651
|
+
if (findingRows.length === 0 && diagnosticRows.length === 0) {
|
|
652
|
+
return "No findings.";
|
|
653
|
+
}
|
|
654
|
+
if (findingRows.length === 0) {
|
|
655
|
+
return `Diagnostics
|
|
656
|
+
${renderAsciiTable(diagnosticRows, diagnosticColumns)}`;
|
|
657
|
+
}
|
|
658
|
+
if (diagnosticRows.length === 0) {
|
|
659
|
+
return renderAsciiTable(findingRows, scanColumns);
|
|
660
|
+
}
|
|
661
|
+
return `${renderAsciiTable(findingRows, scanColumns)}
|
|
662
|
+
|
|
663
|
+
Diagnostics
|
|
664
|
+
${renderAsciiTable(diagnosticRows, diagnosticColumns)}`;
|
|
642
665
|
}
|
|
643
666
|
case "status":
|
|
644
667
|
return renderAsciiTable(
|
|
@@ -655,34 +678,28 @@ ${regionsTable}`;
|
|
|
655
678
|
);
|
|
656
679
|
}
|
|
657
680
|
};
|
|
658
|
-
var
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
resourceId: "",
|
|
681
|
-
ruleId: "",
|
|
682
|
-
service: diagnostic.service,
|
|
683
|
-
source: diagnostic.source
|
|
684
|
-
}))
|
|
685
|
-
];
|
|
681
|
+
var projectFindingRows = (result) => flattenScanResult(result).map(({ finding, message, provider, ruleId, service, source }) => ({
|
|
682
|
+
accountId: finding.accountId ?? "",
|
|
683
|
+
message,
|
|
684
|
+
path: finding.location?.path ?? "",
|
|
685
|
+
provider,
|
|
686
|
+
region: finding.region ?? "",
|
|
687
|
+
resourceId: finding.resourceId,
|
|
688
|
+
ruleId,
|
|
689
|
+
service,
|
|
690
|
+
source,
|
|
691
|
+
column: finding.location?.column ?? "",
|
|
692
|
+
line: finding.location?.line ?? ""
|
|
693
|
+
}));
|
|
694
|
+
var projectDiagnosticRows = (result) => getScanDiagnostics(result).map((diagnostic) => ({
|
|
695
|
+
message: diagnostic.message,
|
|
696
|
+
provider: diagnostic.provider,
|
|
697
|
+
region: diagnostic.region ?? "",
|
|
698
|
+
ruleId: diagnostic.ruleId ?? "",
|
|
699
|
+
service: diagnostic.service,
|
|
700
|
+
source: diagnostic.source,
|
|
701
|
+
status: diagnostic.status
|
|
702
|
+
}));
|
|
686
703
|
var inferColumns = (rows) => {
|
|
687
704
|
const keys = Array.from(new Set(rows.flatMap((row) => Object.keys(row)))).sort(
|
|
688
705
|
(left, right) => left.localeCompare(right)
|
|
@@ -1023,9 +1040,24 @@ var registerConfigCommand = (program) => {
|
|
|
1023
1040
|
};
|
|
1024
1041
|
|
|
1025
1042
|
// src/commands/discover.ts
|
|
1026
|
-
import {
|
|
1043
|
+
import { assertSupportedAwsRegion, CloudBurnClient } from "@cloudburn/sdk";
|
|
1027
1044
|
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
1028
1045
|
|
|
1046
|
+
// src/debug.ts
|
|
1047
|
+
var isDebugEnabled = (command) => {
|
|
1048
|
+
const options = command.optsWithGlobals();
|
|
1049
|
+
return options.debug === true;
|
|
1050
|
+
};
|
|
1051
|
+
var resolveCliDebugLogger = (command) => {
|
|
1052
|
+
if (!isDebugEnabled(command)) {
|
|
1053
|
+
return void 0;
|
|
1054
|
+
}
|
|
1055
|
+
return (message) => {
|
|
1056
|
+
process.stderr.write(`[debug] ${message}
|
|
1057
|
+
`);
|
|
1058
|
+
};
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1029
1061
|
// src/commands/config-options.ts
|
|
1030
1062
|
import { builtInRuleMetadata } from "@cloudburn/sdk";
|
|
1031
1063
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
@@ -1119,18 +1151,25 @@ var buildInitializationStatusData = (result, message, format) => {
|
|
|
1119
1151
|
};
|
|
1120
1152
|
var parseAwsRegion = (value) => {
|
|
1121
1153
|
try {
|
|
1122
|
-
return
|
|
1154
|
+
return assertSupportedAwsRegion(value);
|
|
1123
1155
|
} catch (err) {
|
|
1124
1156
|
throw new InvalidArgumentError3(err instanceof Error ? err.message : "Invalid AWS region.");
|
|
1125
1157
|
}
|
|
1126
1158
|
};
|
|
1127
|
-
var
|
|
1128
|
-
|
|
1129
|
-
|
|
1159
|
+
var resolveDiscoveryTarget = (region) => region === void 0 ? { mode: "current" } : { mode: "regions", regions: [region] };
|
|
1160
|
+
var resolveNestedRegionOption = (command, subcommandName) => {
|
|
1161
|
+
const rootCommand = command.parent?.parent;
|
|
1162
|
+
const rawArgs = rootCommand?.rawArgs ?? [];
|
|
1163
|
+
const subcommandIndex = rawArgs.lastIndexOf(subcommandName);
|
|
1164
|
+
if (subcommandIndex === -1) {
|
|
1165
|
+
return void 0;
|
|
1130
1166
|
}
|
|
1131
|
-
|
|
1167
|
+
const optionIndex = rawArgs.indexOf("--region", subcommandIndex + 1);
|
|
1168
|
+
if (optionIndex === -1 || optionIndex + 1 >= rawArgs.length) {
|
|
1169
|
+
return void 0;
|
|
1170
|
+
}
|
|
1171
|
+
return rawArgs[optionIndex + 1];
|
|
1132
1172
|
};
|
|
1133
|
-
var resolveDiscoveryTarget = (region) => region === void 0 ? { mode: "current" } : region === "all" ? { mode: "all" } : { mode: "region", region };
|
|
1134
1173
|
var toDiscoveryConfigOverride = (options) => {
|
|
1135
1174
|
if (options.enabledRules === void 0 && options.disabledRules === void 0 && options.service === void 0) {
|
|
1136
1175
|
return void 0;
|
|
@@ -1154,10 +1193,10 @@ var runCommand = async (action) => {
|
|
|
1154
1193
|
};
|
|
1155
1194
|
var registerDiscoverCommand = (program) => {
|
|
1156
1195
|
const discoverCommand = setCommandExamples(
|
|
1157
|
-
program.command("discover").description("Run a live AWS discovery").
|
|
1196
|
+
program.command("discover").description("Run a live AWS discovery").option(
|
|
1158
1197
|
"--region <region>",
|
|
1159
|
-
|
|
1160
|
-
|
|
1198
|
+
"AWS region to discover. Defaults to the current AWS region from AWS_REGION when omitted.",
|
|
1199
|
+
parseAwsRegion
|
|
1161
1200
|
).option("--config <path>", "Explicit CloudBurn config file to load").option(
|
|
1162
1201
|
"--enabled-rules <ruleIds>",
|
|
1163
1202
|
"Comma-separated rule IDs to enable. When set, CloudBurn checks only these rules. By default, all rules are enabled.",
|
|
@@ -1172,7 +1211,8 @@ var registerDiscoverCommand = (program) => {
|
|
|
1172
1211
|
parseDiscoveryServiceList
|
|
1173
1212
|
).option("--exit-code", "Exit with code 1 when findings exist").action(async (options, command) => {
|
|
1174
1213
|
await runCommand(async () => {
|
|
1175
|
-
const
|
|
1214
|
+
const debugLogger = resolveCliDebugLogger(command);
|
|
1215
|
+
const scanner = new CloudBurnClient({ debugLogger });
|
|
1176
1216
|
const configOverride = toDiscoveryConfigOverride(options);
|
|
1177
1217
|
const loadedConfig = await scanner.loadConfig(options.config);
|
|
1178
1218
|
const discoveryOptions = {
|
|
@@ -1198,18 +1238,15 @@ var registerDiscoverCommand = (program) => {
|
|
|
1198
1238
|
[
|
|
1199
1239
|
"cloudburn discover",
|
|
1200
1240
|
"cloudburn discover --region eu-central-1",
|
|
1201
|
-
"cloudburn discover --region all",
|
|
1202
1241
|
"cloudburn discover status",
|
|
1203
|
-
"cloudburn discover list-enabled-regions",
|
|
1204
1242
|
"cloudburn discover init"
|
|
1205
1243
|
]
|
|
1206
1244
|
);
|
|
1207
1245
|
discoverCommand.command("status").description("Show Resource Explorer status across all enabled AWS regions").action(async (_options, command) => {
|
|
1208
1246
|
await runCommand(async () => {
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1212
|
-
const status = await scanner.getDiscoveryStatus({ region });
|
|
1247
|
+
const debugLogger = resolveCliDebugLogger(command);
|
|
1248
|
+
const scanner = new CloudBurnClient({ debugLogger });
|
|
1249
|
+
const status = await scanner.getDiscoveryStatus({ region: void 0 });
|
|
1213
1250
|
const format = resolveOutputFormat(command);
|
|
1214
1251
|
const rows = format === "json" ? status.regions.map((regionStatus) => ({
|
|
1215
1252
|
...regionStatus,
|
|
@@ -1252,28 +1289,6 @@ var registerDiscoverCommand = (program) => {
|
|
|
1252
1289
|
format
|
|
1253
1290
|
);
|
|
1254
1291
|
process.stdout.write(`${output}
|
|
1255
|
-
`);
|
|
1256
|
-
return EXIT_CODE_OK;
|
|
1257
|
-
});
|
|
1258
|
-
});
|
|
1259
|
-
discoverCommand.command("list-enabled-regions").description("List AWS regions with a local or aggregator Resource Explorer index").action(async (_options, command) => {
|
|
1260
|
-
await runCommand(async () => {
|
|
1261
|
-
const scanner = new CloudBurnClient();
|
|
1262
|
-
const regions = await scanner.listEnabledDiscoveryRegions();
|
|
1263
|
-
const format = resolveOutputFormat(command);
|
|
1264
|
-
const output = renderResponse(
|
|
1265
|
-
{
|
|
1266
|
-
kind: "record-list",
|
|
1267
|
-
columns: [
|
|
1268
|
-
{ key: "region", header: "Region" },
|
|
1269
|
-
{ key: "type", header: "Type" }
|
|
1270
|
-
],
|
|
1271
|
-
emptyMessage: "No Resource Explorer indexes are enabled.",
|
|
1272
|
-
rows: regions
|
|
1273
|
-
},
|
|
1274
|
-
format
|
|
1275
|
-
);
|
|
1276
|
-
process.stdout.write(`${output}
|
|
1277
1292
|
`);
|
|
1278
1293
|
return EXIT_CODE_OK;
|
|
1279
1294
|
});
|
|
@@ -1284,9 +1299,9 @@ var registerDiscoverCommand = (program) => {
|
|
|
1284
1299
|
parseAwsRegion
|
|
1285
1300
|
).action(async (options, command) => {
|
|
1286
1301
|
await runCommand(async () => {
|
|
1287
|
-
const
|
|
1288
|
-
const
|
|
1289
|
-
const region = options.region ?? (
|
|
1302
|
+
const debugLogger = resolveCliDebugLogger(command);
|
|
1303
|
+
const scanner = new CloudBurnClient({ debugLogger });
|
|
1304
|
+
const region = options.region ?? resolveNestedRegionOption(command, "init");
|
|
1290
1305
|
const result = await scanner.initializeDiscovery({ region });
|
|
1291
1306
|
const message = describeInitializationMessage(result);
|
|
1292
1307
|
const format = resolveOutputFormat(command);
|
|
@@ -1304,7 +1319,8 @@ var registerDiscoverCommand = (program) => {
|
|
|
1304
1319
|
});
|
|
1305
1320
|
discoverCommand.command("supported-resource-types").description("List Resource Explorer supported AWS resource types").action(async (_options, command) => {
|
|
1306
1321
|
await runCommand(async () => {
|
|
1307
|
-
const
|
|
1322
|
+
const debugLogger = resolveCliDebugLogger(command);
|
|
1323
|
+
const scanner = new CloudBurnClient({ debugLogger });
|
|
1308
1324
|
const resourceTypes = await scanner.listSupportedDiscoveryResourceTypes();
|
|
1309
1325
|
const format = resolveOutputFormat(command);
|
|
1310
1326
|
const output = renderResponse(
|
|
@@ -1434,7 +1450,8 @@ var registerScanCommand = (program) => {
|
|
|
1434
1450
|
parseRuleIdList
|
|
1435
1451
|
).option("--service <services>", "Comma-separated services to include in the scan rule set.", parseIaCServiceList).option("--exit-code", "Exit with code 1 when findings exist").action(async (path, options, command) => {
|
|
1436
1452
|
try {
|
|
1437
|
-
const
|
|
1453
|
+
const debugLogger = resolveCliDebugLogger(command);
|
|
1454
|
+
const scanner = new CloudBurnClient2({ debugLogger });
|
|
1438
1455
|
const configOverride = toScanConfigOverride(options);
|
|
1439
1456
|
const loadedConfig = await scanner.loadConfig(options.config);
|
|
1440
1457
|
const scanPath = path ?? process.cwd();
|
|
@@ -1474,7 +1491,7 @@ var isCliEntrypoint = (moduleUrl, argvEntry = process.argv[1]) => {
|
|
|
1474
1491
|
};
|
|
1475
1492
|
var createProgram = () => {
|
|
1476
1493
|
const program = createCliCommand();
|
|
1477
|
-
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.9.
|
|
1494
|
+
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.9.10").option("--debug", "Write execution trace messages to stderr").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
|
|
1478
1495
|
configureCliHelp(program);
|
|
1479
1496
|
registerCompletionCommand(program);
|
|
1480
1497
|
registerConfigCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudburn",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.10",
|
|
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.21.
|
|
23
|
+
"@cloudburn/sdk": "0.21.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@biomejs/biome": "^2.4.6",
|