@pagopa/dx-savemoney 0.2.5 → 0.2.6
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/dist/azure/analyzer.d.ts +31 -16
- package/dist/azure/analyzer.d.ts.map +1 -1
- package/dist/azure/analyzer.js +110 -81
- package/dist/azure/analyzer.js.map +1 -1
- package/dist/azure/analyzers/index.d.ts +6 -0
- package/dist/azure/analyzers/index.d.ts.map +1 -0
- package/dist/azure/analyzers/index.js +5 -0
- package/dist/azure/analyzers/index.js.map +1 -0
- package/dist/azure/analyzers/registry.d.ts +21 -0
- package/dist/azure/analyzers/registry.d.ts.map +1 -0
- package/dist/azure/analyzers/registry.js +69 -0
- package/dist/azure/analyzers/registry.js.map +1 -0
- package/dist/azure/analyzers/types.d.ts +62 -0
- package/dist/azure/analyzers/types.d.ts.map +1 -0
- package/dist/azure/analyzers/types.js +15 -0
- package/dist/azure/analyzers/types.js.map +1 -0
- package/dist/azure/config.d.ts.map +1 -1
- package/dist/azure/config.js +1 -0
- package/dist/azure/config.js.map +1 -1
- package/dist/azure/resources/app-service.d.ts +2 -1
- package/dist/azure/resources/app-service.d.ts.map +1 -1
- package/dist/azure/resources/app-service.js +3 -3
- package/dist/azure/resources/app-service.js.map +1 -1
- package/dist/azure/resources/container-app.d.ts +2 -1
- package/dist/azure/resources/container-app.d.ts.map +1 -1
- package/dist/azure/resources/container-app.js +9 -9
- package/dist/azure/resources/container-app.js.map +1 -1
- package/dist/azure/resources/public-ip.d.ts +2 -1
- package/dist/azure/resources/public-ip.d.ts.map +1 -1
- package/dist/azure/resources/public-ip.js +2 -2
- package/dist/azure/resources/public-ip.js.map +1 -1
- package/dist/azure/resources/static-web-app.d.ts +2 -1
- package/dist/azure/resources/static-web-app.d.ts.map +1 -1
- package/dist/azure/resources/static-web-app.js +3 -3
- package/dist/azure/resources/static-web-app.js.map +1 -1
- package/dist/azure/resources/storage.d.ts +2 -1
- package/dist/azure/resources/storage.d.ts.map +1 -1
- package/dist/azure/resources/storage.js +2 -2
- package/dist/azure/resources/storage.js.map +1 -1
- package/dist/azure/resources/vm.d.ts +2 -1
- package/dist/azure/resources/vm.d.ts.map +1 -1
- package/dist/azure/resources/vm.js +3 -3
- package/dist/azure/resources/vm.js.map +1 -1
- package/dist/azure/types.d.ts +6 -0
- package/dist/azure/types.d.ts.map +1 -1
- package/dist/azure/utils.d.ts +35 -3
- package/dist/azure/utils.d.ts.map +1 -1
- package/dist/azure/utils.js +70 -29
- package/dist/azure/utils.js.map +1 -1
- package/dist/finding.d.ts +114 -0
- package/dist/finding.d.ts.map +1 -0
- package/dist/finding.js +51 -0
- package/dist/finding.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +5 -0
- package/dist/schema.js.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/finding.test.ts +149 -0
- package/src/azure/__tests__/utils.test.ts +164 -2
- package/src/azure/analyzer.ts +140 -165
- package/src/azure/analyzers/index.ts +6 -0
- package/src/azure/analyzers/registry.ts +184 -0
- package/src/azure/analyzers/types.ts +66 -0
- package/src/azure/config.ts +1 -0
- package/src/azure/resources/app-service.ts +4 -0
- package/src/azure/resources/container-app.ts +10 -0
- package/src/azure/resources/public-ip.ts +3 -0
- package/src/azure/resources/static-web-app.ts +4 -0
- package/src/azure/resources/storage.ts +3 -0
- package/src/azure/resources/vm.ts +4 -0
- package/src/azure/types.ts +6 -0
- package/src/azure/utils.ts +110 -39
- package/src/finding.ts +152 -0
- package/src/index.ts +18 -0
- package/src/schema.ts +5 -0
package/dist/azure/analyzer.d.ts
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Azure resource analyzer - Main orchestration logic
|
|
3
|
+
*
|
|
4
|
+
* Iterates every resource in every configured subscription, dispatches it
|
|
5
|
+
* to the registered analyzers (see `./analyzers/registry.ts`), and feeds
|
|
6
|
+
* the resulting findings into the report generator.
|
|
7
|
+
*
|
|
8
|
+
* Phase 0 refactor:
|
|
9
|
+
* - Resources are dispatched through a plugin-style `Analyzer` registry
|
|
10
|
+
* instead of a hard-coded `switch` statement.
|
|
11
|
+
* - Per-subscription resources are analyzed in parallel via a small
|
|
12
|
+
* in-process limiter (default concurrency 8, configurable via
|
|
13
|
+
* `AzureConfig.concurrency`).
|
|
14
|
+
* - Azure Monitor metric calls are memoized for the duration of a run via
|
|
15
|
+
* a per-run `MetricsCache` passed through `AnalyzerContext`, so concurrent
|
|
16
|
+
* calls to `analyzeAzureResources` stay fully isolated.
|
|
17
|
+
*
|
|
18
|
+
* The output schema (`AzureDetailedResourceReport`) is unchanged so the
|
|
19
|
+
* existing report formats keep working untouched.
|
|
3
20
|
*/
|
|
4
|
-
import { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
|
|
5
|
-
import { WebSiteManagementClient } from "@azure/arm-appservice";
|
|
6
|
-
import { ComputeManagementClient } from "@azure/arm-compute";
|
|
7
|
-
import { MonitorClient } from "@azure/arm-monitor";
|
|
8
|
-
import { NetworkManagementClient } from "@azure/arm-network";
|
|
9
21
|
import * as armResources from "@azure/arm-resources";
|
|
10
22
|
import type { AzureConfig } from "./types.js";
|
|
11
23
|
import { type AnalysisResult, type Thresholds } from "../types.js";
|
|
24
|
+
import { type Analyzer, type AzureClients } from "./analyzers/index.js";
|
|
25
|
+
import { type MetricsCache } from "./utils.js";
|
|
12
26
|
/**
|
|
13
27
|
* Analyzes resources in multiple Azure subscriptions and generates a report.
|
|
14
28
|
*
|
|
@@ -17,18 +31,19 @@ import { type AnalysisResult, type Thresholds } from "../types.js";
|
|
|
17
31
|
*/
|
|
18
32
|
export declare function analyzeAzureResources(config: AzureConfig, format: "detailed-json" | "json" | "lint" | "table"): Promise<void>;
|
|
19
33
|
/**
|
|
20
|
-
* Analyzes a single Azure resource
|
|
34
|
+
* Analyzes a single Azure resource by dispatching it to every registered
|
|
35
|
+
* analyzer that supports it. Generic checks (missing tags, location
|
|
36
|
+
* mismatch) are applied around the analyzer-specific logic.
|
|
21
37
|
*
|
|
22
|
-
* @param resource
|
|
23
|
-
* @param
|
|
24
|
-
* @param
|
|
25
|
-
* @param
|
|
26
|
-
* @param
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
29
|
-
* @param
|
|
30
|
-
* @param verbose - Whether verbose logging is enabled
|
|
38
|
+
* @param resource The Azure resource to analyze
|
|
39
|
+
* @param analyzers Registered analyzers (typically `createDefaultAnalyzers()`)
|
|
40
|
+
* @param clients Bundle of Azure SDK clients shared across analyzers
|
|
41
|
+
* @param metricsCache Run-scoped metrics cache to pass through to analyzers
|
|
42
|
+
* @param preferredLocation Preferred Azure region (resources elsewhere are flagged)
|
|
43
|
+
* @param timespanDays Look-back window for Azure Monitor metrics
|
|
44
|
+
* @param thresholds Numeric thresholds used during analysis
|
|
45
|
+
* @param verbose Whether verbose logging is enabled
|
|
31
46
|
* @returns Analysis result with cost risk and reason
|
|
32
47
|
*/
|
|
33
|
-
export declare function analyzeResource(resource: armResources.GenericResource,
|
|
48
|
+
export declare function analyzeResource(resource: armResources.GenericResource, analyzers: Analyzer[], clients: AzureClients, metricsCache: MetricsCache | undefined, preferredLocation: string, timespanDays: number, thresholds: Thresholds, verbose?: boolean): Promise<AnalysisResult>;
|
|
34
49
|
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAKrD,OAAO,KAAK,EAAE,WAAW,EAA+B,MAAM,YAAY,CAAC;AAE3E,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,YAAY,EAElB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI5D;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,iBA6GpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,YAAY,YAAY,EACtC,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,UAAQ,GACd,OAAO,CAAC,cAAc,CAAC,CA8CzB"}
|
package/dist/azure/analyzer.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Azure resource analyzer - Main orchestration logic
|
|
3
|
+
*
|
|
4
|
+
* Iterates every resource in every configured subscription, dispatches it
|
|
5
|
+
* to the registered analyzers (see `./analyzers/registry.ts`), and feeds
|
|
6
|
+
* the resulting findings into the report generator.
|
|
7
|
+
*
|
|
8
|
+
* Phase 0 refactor:
|
|
9
|
+
* - Resources are dispatched through a plugin-style `Analyzer` registry
|
|
10
|
+
* instead of a hard-coded `switch` statement.
|
|
11
|
+
* - Per-subscription resources are analyzed in parallel via a small
|
|
12
|
+
* in-process limiter (default concurrency 8, configurable via
|
|
13
|
+
* `AzureConfig.concurrency`).
|
|
14
|
+
* - Azure Monitor metric calls are memoized for the duration of a run via
|
|
15
|
+
* a per-run `MetricsCache` passed through `AnalyzerContext`, so concurrent
|
|
16
|
+
* calls to `analyzeAzureResources` stay fully isolated.
|
|
17
|
+
*
|
|
18
|
+
* The output schema (`AzureDetailedResourceReport`) is unchanged so the
|
|
19
|
+
* existing report formats keep working untouched.
|
|
3
20
|
*/
|
|
4
21
|
import { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
|
|
5
22
|
import { WebSiteManagementClient } from "@azure/arm-appservice";
|
|
@@ -9,10 +26,12 @@ import { NetworkManagementClient } from "@azure/arm-network";
|
|
|
9
26
|
import * as armResources from "@azure/arm-resources";
|
|
10
27
|
import { DefaultAzureCredential } from "@azure/identity";
|
|
11
28
|
import { getLogger } from "@logtape/logtape";
|
|
29
|
+
import pLimit from "p-limit";
|
|
12
30
|
import { DEFAULT_THRESHOLDS, mergeResults, } from "../types.js";
|
|
31
|
+
import { createDefaultAnalyzers, } from "./analyzers/index.js";
|
|
13
32
|
import { generateReport } from "./report.js";
|
|
14
|
-
import { analyzeAppServicePlan, analyzeContainerApp, analyzeDisk, analyzeNic, analyzePrivateEndpoint, analyzePublicIp, analyzeStaticSite, analyzeStorageAccount, analyzeVM, } from "./resources/index.js";
|
|
15
33
|
import { matchesTags } from "./utils.js";
|
|
34
|
+
const DEFAULT_CONCURRENCY = 8;
|
|
16
35
|
/**
|
|
17
36
|
* Analyzes resources in multiple Azure subscriptions and generates a report.
|
|
18
37
|
*
|
|
@@ -23,31 +42,71 @@ export async function analyzeAzureResources(config, format) {
|
|
|
23
42
|
const logger = getLogger(["savemoney", "azure"]);
|
|
24
43
|
const credential = new DefaultAzureCredential();
|
|
25
44
|
const allReports = [];
|
|
45
|
+
const analyzers = createDefaultAnalyzers();
|
|
46
|
+
const thresholds = config.thresholds ?? DEFAULT_THRESHOLDS;
|
|
47
|
+
// Normalise concurrency the same way p-limit does to keep maxInFlight
|
|
48
|
+
// consistent. A raw value of 0/NaN would produce maxInFlight = 0/NaN and
|
|
49
|
+
// either deadlock or silently disable backpressure.
|
|
50
|
+
const rawConcurrency = config.concurrency ?? DEFAULT_CONCURRENCY;
|
|
51
|
+
const concurrency = Number.isFinite(rawConcurrency)
|
|
52
|
+
? Math.max(1, Math.floor(rawConcurrency))
|
|
53
|
+
: 1;
|
|
54
|
+
const limit = pLimit(concurrency);
|
|
55
|
+
// Bound the in-flight Set to `2 × concurrency` so memory stays proportional
|
|
56
|
+
// to the limiter width, not the total resource count in a subscription.
|
|
57
|
+
const maxInFlight = concurrency * 2;
|
|
26
58
|
for (const subscriptionId of config.subscriptionIds) {
|
|
27
59
|
logger.info(`Analyzing subscription: ${subscriptionId}`);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
60
|
+
const sid = subscriptionId.trim();
|
|
61
|
+
// Fresh cache per subscription — bounds peak memory to one subscription's
|
|
62
|
+
// worth of metrics and keeps concurrent analyzeAzureResources calls isolated.
|
|
63
|
+
const runCache = new Map();
|
|
64
|
+
const clients = {
|
|
65
|
+
compute: new ComputeManagementClient(credential, sid),
|
|
66
|
+
containerApps: new ContainerAppsAPIClient(credential, sid),
|
|
67
|
+
monitor: new MonitorClient(credential, sid),
|
|
68
|
+
network: new NetworkManagementClient(credential, sid),
|
|
69
|
+
webSite: new WebSiteManagementClient(credential, sid),
|
|
70
|
+
};
|
|
71
|
+
const resourceClient = new armResources.ResourceManagementClient(credential, sid);
|
|
72
|
+
const inFlight = new Set();
|
|
73
|
+
// Use the async iterator to avoid loading all resources into memory at once.
|
|
36
74
|
for await (const resource of resourceClient.resources.list()) {
|
|
37
|
-
// Skip resources that don't match the requested tag filter
|
|
38
75
|
if (!matchesTags(resource, config.filterTags)) {
|
|
39
76
|
continue;
|
|
40
77
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
// Backpressure: wait for a slot before enqueuing the next task so that
|
|
79
|
+
// the inFlight Set stays bounded by maxInFlight instead of growing to the
|
|
80
|
+
// total resource count in the subscription.
|
|
81
|
+
while (inFlight.size >= maxInFlight) {
|
|
82
|
+
await Promise.race(inFlight).catch(() => undefined);
|
|
83
|
+
}
|
|
84
|
+
const task = limit(async () => {
|
|
85
|
+
const { costRisk, reason, suspectedUnused } = await analyzeResource(resource, analyzers, clients, runCache, config.preferredLocation, config.timespanDays, thresholds, config.verbose || false);
|
|
86
|
+
if (suspectedUnused) {
|
|
87
|
+
allReports.push({
|
|
88
|
+
analysis: {
|
|
89
|
+
costRisk,
|
|
90
|
+
reason: reason || "No specific findings.",
|
|
91
|
+
suspectedUnused,
|
|
92
|
+
},
|
|
93
|
+
resource,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
inFlight.add(task);
|
|
98
|
+
// Suppress the unhandled-rejection that would occur between task creation
|
|
99
|
+
// and the Promise.allSettled drain below. The .catch() handler is a no-op
|
|
100
|
+
// because the actual error is still visible to allSettled (which logs it)
|
|
101
|
+
// via the original `task` reference kept in inFlight.
|
|
102
|
+
void task.catch(() => undefined).finally(() => inFlight.delete(task));
|
|
103
|
+
}
|
|
104
|
+
// Drain remaining tasks; surface any unexpected errors so they don't
|
|
105
|
+
// disappear silently and produce an incomplete report without a signal.
|
|
106
|
+
const results = await Promise.allSettled(inFlight);
|
|
107
|
+
for (const result of results) {
|
|
108
|
+
if (result.status === "rejected") {
|
|
109
|
+
logger.error(`Resource analysis failed: ${String(result.reason)}`);
|
|
51
110
|
}
|
|
52
111
|
}
|
|
53
112
|
}
|
|
@@ -61,21 +120,21 @@ export async function analyzeAzureResources(config, format) {
|
|
|
61
120
|
await generateReport(allReports, format);
|
|
62
121
|
}
|
|
63
122
|
/**
|
|
64
|
-
* Analyzes a single Azure resource
|
|
123
|
+
* Analyzes a single Azure resource by dispatching it to every registered
|
|
124
|
+
* analyzer that supports it. Generic checks (missing tags, location
|
|
125
|
+
* mismatch) are applied around the analyzer-specific logic.
|
|
65
126
|
*
|
|
66
|
-
* @param resource
|
|
67
|
-
* @param
|
|
68
|
-
* @param
|
|
69
|
-
* @param
|
|
70
|
-
* @param
|
|
71
|
-
* @param
|
|
72
|
-
* @param
|
|
73
|
-
* @param
|
|
74
|
-
* @param verbose - Whether verbose logging is enabled
|
|
127
|
+
* @param resource The Azure resource to analyze
|
|
128
|
+
* @param analyzers Registered analyzers (typically `createDefaultAnalyzers()`)
|
|
129
|
+
* @param clients Bundle of Azure SDK clients shared across analyzers
|
|
130
|
+
* @param metricsCache Run-scoped metrics cache to pass through to analyzers
|
|
131
|
+
* @param preferredLocation Preferred Azure region (resources elsewhere are flagged)
|
|
132
|
+
* @param timespanDays Look-back window for Azure Monitor metrics
|
|
133
|
+
* @param thresholds Numeric thresholds used during analysis
|
|
134
|
+
* @param verbose Whether verbose logging is enabled
|
|
75
135
|
* @returns Analysis result with cost risk and reason
|
|
76
136
|
*/
|
|
77
|
-
export async function analyzeResource(resource,
|
|
78
|
-
const type = resource.type?.toLowerCase() || "";
|
|
137
|
+
export async function analyzeResource(resource, analyzers, clients, metricsCache = new Map(), preferredLocation, timespanDays, thresholds, verbose = false) {
|
|
79
138
|
let result = {
|
|
80
139
|
costRisk: "low",
|
|
81
140
|
reason: "",
|
|
@@ -86,56 +145,26 @@ export async function analyzeResource(resource, monitorClient, computeClient, ne
|
|
|
86
145
|
result.suspectedUnused = true;
|
|
87
146
|
result.reason += "No tags found. ";
|
|
88
147
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const vmResult = await analyzeVM(resource, monitorClient, computeClient, timespanDays, thresholds, verbose);
|
|
103
|
-
result = mergeResults(result, vmResult);
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case "microsoft.network/networkinterfaces": {
|
|
107
|
-
const nicResult = await analyzeNic(resource, networkClient, verbose);
|
|
108
|
-
result = mergeResults(result, nicResult);
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
case "microsoft.network/privateendpoints": {
|
|
112
|
-
const peResult = await analyzePrivateEndpoint(resource, networkClient, verbose);
|
|
113
|
-
result = mergeResults(result, peResult);
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case "microsoft.network/publicipaddresses": {
|
|
117
|
-
const pipResult = await analyzePublicIp(resource, networkClient, monitorClient, timespanDays, thresholds, verbose);
|
|
118
|
-
result = mergeResults(result, pipResult);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
case "microsoft.storage/storageaccounts": {
|
|
122
|
-
const storageResult = await analyzeStorageAccount(resource, monitorClient, timespanDays, thresholds, verbose);
|
|
123
|
-
result = mergeResults(result, storageResult);
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
case "microsoft.web/serverfarms": {
|
|
127
|
-
const aspResult = await analyzeAppServicePlan(resource, webSiteClient, monitorClient, timespanDays, thresholds, verbose);
|
|
128
|
-
result = mergeResults(result, aspResult);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
case "microsoft.web/staticsites": {
|
|
132
|
-
const staticSiteResult = await analyzeStaticSite(resource, monitorClient, timespanDays, thresholds, verbose);
|
|
133
|
-
result = mergeResults(result, staticSiteResult);
|
|
134
|
-
break;
|
|
148
|
+
const ctx = {
|
|
149
|
+
clients,
|
|
150
|
+
metricsCache,
|
|
151
|
+
preferredLocation,
|
|
152
|
+
resource,
|
|
153
|
+
thresholds,
|
|
154
|
+
timespanDays,
|
|
155
|
+
verbose,
|
|
156
|
+
};
|
|
157
|
+
let matched = false;
|
|
158
|
+
for (const analyzer of analyzers) {
|
|
159
|
+
if (!analyzer.supports(resource)) {
|
|
160
|
+
continue;
|
|
135
161
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
162
|
+
matched = true;
|
|
163
|
+
const specific = await analyzer.analyze(ctx);
|
|
164
|
+
result = mergeResults(result, specific);
|
|
165
|
+
}
|
|
166
|
+
if (!matched) {
|
|
167
|
+
result.reason += "No specific analysis for this resource type. ";
|
|
139
168
|
}
|
|
140
169
|
// Generic check for location
|
|
141
170
|
if (resource.location &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/azure/analyzers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/azure/analyzers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default registry of Azure analyzers.
|
|
3
|
+
*
|
|
4
|
+
* Each entry wraps an existing per-type analyzer function (kept in
|
|
5
|
+
* `../resources/`) and exposes it through the unified `Analyzer`
|
|
6
|
+
* interface. The orchestrator simply iterates the registry — no big
|
|
7
|
+
* `switch` statement, no risk of forgetting to wire a new analyzer
|
|
8
|
+
* into the orchestrator when the catalog grows.
|
|
9
|
+
*
|
|
10
|
+
* Adding a new analyzer is a single insertion here.
|
|
11
|
+
*/
|
|
12
|
+
import type { Analyzer } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Builds the default set of analyzers in the same order they were
|
|
15
|
+
* previously evaluated by the orchestrator's `switch` statement. The
|
|
16
|
+
* order is not behaviourally meaningful today (each resource is matched
|
|
17
|
+
* by exactly one analyzer) but is kept deterministic for predictable
|
|
18
|
+
* logging and to ease future debugging.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createDefaultAnalyzers(): Analyzer[];
|
|
21
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/azure/analyzers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAc3C;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,QAAQ,EAAE,CAsJnD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default registry of Azure analyzers.
|
|
3
|
+
*
|
|
4
|
+
* Each entry wraps an existing per-type analyzer function (kept in
|
|
5
|
+
* `../resources/`) and exposes it through the unified `Analyzer`
|
|
6
|
+
* interface. The orchestrator simply iterates the registry — no big
|
|
7
|
+
* `switch` statement, no risk of forgetting to wire a new analyzer
|
|
8
|
+
* into the orchestrator when the catalog grows.
|
|
9
|
+
*
|
|
10
|
+
* Adding a new analyzer is a single insertion here.
|
|
11
|
+
*/
|
|
12
|
+
import { analyzeAppServicePlan, analyzeContainerApp, analyzeDisk, analyzeNic, analyzePrivateEndpoint, analyzePublicIp, analyzeStaticSite, analyzeStorageAccount, analyzeVM, } from "../resources/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Builds the default set of analyzers in the same order they were
|
|
15
|
+
* previously evaluated by the orchestrator's `switch` statement. The
|
|
16
|
+
* order is not behaviourally meaningful today (each resource is matched
|
|
17
|
+
* by exactly one analyzer) but is kept deterministic for predictable
|
|
18
|
+
* logging and to ease future debugging.
|
|
19
|
+
*/
|
|
20
|
+
export function createDefaultAnalyzers() {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzeContainerApp(resource, clients.containerApps, clients.monitor, timespanDays, thresholds, verbose, metricsCache),
|
|
24
|
+
id: "azure.container-app",
|
|
25
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.app/containerapps",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
analyze: ({ clients, resource, verbose }) => analyzeDisk(resource, clients.compute, verbose),
|
|
29
|
+
id: "azure.disk",
|
|
30
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.compute/disks",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzeVM(resource, clients.monitor, clients.compute, timespanDays, thresholds, verbose, metricsCache),
|
|
34
|
+
id: "azure.vm",
|
|
35
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.compute/virtualmachines",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
analyze: ({ clients, resource, verbose }) => analyzeNic(resource, clients.network, verbose),
|
|
39
|
+
id: "azure.nic",
|
|
40
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.network/networkinterfaces",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
analyze: ({ clients, resource, verbose }) => analyzePrivateEndpoint(resource, clients.network, verbose),
|
|
44
|
+
id: "azure.private-endpoint",
|
|
45
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.network/privateendpoints",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzePublicIp(resource, clients.network, clients.monitor, timespanDays, thresholds, verbose, metricsCache),
|
|
49
|
+
id: "azure.public-ip",
|
|
50
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.network/publicipaddresses",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzeStorageAccount(resource, clients.monitor, timespanDays, thresholds, verbose, metricsCache),
|
|
54
|
+
id: "azure.storage-account",
|
|
55
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.storage/storageaccounts",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzeAppServicePlan(resource, clients.webSite, clients.monitor, timespanDays, thresholds, verbose, metricsCache),
|
|
59
|
+
id: "azure.app-service-plan",
|
|
60
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.web/serverfarms",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
analyze: ({ clients, metricsCache, resource, thresholds, timespanDays, verbose, }) => analyzeStaticSite(resource, clients.monitor, timespanDays, thresholds, verbose, metricsCache),
|
|
64
|
+
id: "azure.static-web-app",
|
|
65
|
+
supports: (r) => r.type?.toLowerCase() === "microsoft.web/staticsites",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/azure/analyzers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,SAAS,GACV,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,mBAAmB,CACjB,QAAQ,EACR,OAAO,CAAC,aAAa,EACrB,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,qBAAqB;YACzB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,6BAA6B;SACzE;QACD;YACE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAC1C,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC;YACjD,EAAE,EAAE,YAAY;YAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,yBAAyB;SACrE;QACD;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,SAAS,CACP,QAAQ,EACR,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,UAAU;YACd,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,mCAAmC;SAChE;QACD;YACE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAC1C,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC;YAChD,EAAE,EAAE,WAAW;YACf,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,qCAAqC;SAClE;QACD;YACE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAC1C,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC;YAC5D,EAAE,EAAE,wBAAwB;YAC5B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,oCAAoC;SACjE;QACD;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,eAAe,CACb,QAAQ,EACR,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,iBAAiB;YACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,qCAAqC;SAClE;QACD;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,qBAAqB,CACnB,QAAQ,EACR,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,uBAAuB;YAC3B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,mCAAmC;SAChE;QACD;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,qBAAqB,CACnB,QAAQ,EACR,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,wBAAwB;YAC5B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,2BAA2B;SACvE;QACD;YACE,OAAO,EAAE,CAAC,EACR,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,OAAO,GACR,EAAE,EAAE,CACH,iBAAiB,CACf,QAAQ,EACR,OAAO,CAAC,OAAO,EACf,YAAY,EACZ,UAAU,EACV,OAAO,EACP,YAAY,CACb;YACH,EAAE,EAAE,sBAAsB;YAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,2BAA2B;SACvE;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin architecture for Azure resource analyzers.
|
|
3
|
+
*
|
|
4
|
+
* Each `Analyzer` is a self-contained unit that:
|
|
5
|
+
* 1. declares the resource types it can handle (`supports`)
|
|
6
|
+
* 2. produces an `AnalysisResult` for a given resource (`analyze`)
|
|
7
|
+
*
|
|
8
|
+
* The orchestrator in `analyzer.ts` walks the registered analyzers for
|
|
9
|
+
* every resource it encounters. New sources (Azure Advisor, custom
|
|
10
|
+
* checks, pricing-enriched analyzers, …) can be added by implementing
|
|
11
|
+
* the interface and registering them in `registry.ts` without touching
|
|
12
|
+
* the orchestrator.
|
|
13
|
+
*/
|
|
14
|
+
import type { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
|
|
15
|
+
import type { WebSiteManagementClient } from "@azure/arm-appservice";
|
|
16
|
+
import type { ComputeManagementClient } from "@azure/arm-compute";
|
|
17
|
+
import type { MonitorClient } from "@azure/arm-monitor";
|
|
18
|
+
import type { NetworkManagementClient } from "@azure/arm-network";
|
|
19
|
+
import type * as armResources from "@azure/arm-resources";
|
|
20
|
+
import type { AnalysisResult, Thresholds } from "../../types.js";
|
|
21
|
+
import type { MetricsCache } from "../utils.js";
|
|
22
|
+
/**
|
|
23
|
+
* Contract every analyzer must satisfy.
|
|
24
|
+
*/
|
|
25
|
+
export type Analyzer = {
|
|
26
|
+
analyze(ctx: AnalyzerContext): Promise<AnalysisResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Stable identifier of the analyzer (e.g. `azure.vm`, `azure.advisor`).
|
|
29
|
+
* Used for logging, telemetry and future deduplication logic.
|
|
30
|
+
*/
|
|
31
|
+
readonly id: string;
|
|
32
|
+
supports(resource: armResources.GenericResource): boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Per-resource analysis context handed to every analyzer.
|
|
36
|
+
*/
|
|
37
|
+
export type AnalyzerContext = {
|
|
38
|
+
clients: AzureClients;
|
|
39
|
+
/**
|
|
40
|
+
* Run-scoped metrics cache. Pass through to `getMetric` calls so that
|
|
41
|
+
* concurrent `analyzeAzureResources` invocations stay isolated.
|
|
42
|
+
*/
|
|
43
|
+
metricsCache: MetricsCache;
|
|
44
|
+
preferredLocation: string;
|
|
45
|
+
resource: armResources.GenericResource;
|
|
46
|
+
thresholds: Thresholds;
|
|
47
|
+
timespanDays: number;
|
|
48
|
+
verbose: boolean;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Bundle of Azure SDK clients an analyzer might need. The orchestrator
|
|
52
|
+
* builds them once per subscription and passes the same instances to
|
|
53
|
+
* every analyzer.
|
|
54
|
+
*/
|
|
55
|
+
export type AzureClients = {
|
|
56
|
+
compute: ComputeManagementClient;
|
|
57
|
+
containerApps: ContainerAppsAPIClient;
|
|
58
|
+
monitor: MonitorClient;
|
|
59
|
+
network: NetworkManagementClient;
|
|
60
|
+
webSite: WebSiteManagementClient;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/azure/analyzers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,eAAe,GAAG,OAAO,CAAC;CAC3D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,uBAAuB,CAAC;IACjC,aAAa,EAAE,sBAAsB,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,uBAAuB,CAAC;CAClC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin architecture for Azure resource analyzers.
|
|
3
|
+
*
|
|
4
|
+
* Each `Analyzer` is a self-contained unit that:
|
|
5
|
+
* 1. declares the resource types it can handle (`supports`)
|
|
6
|
+
* 2. produces an `AnalysisResult` for a given resource (`analyze`)
|
|
7
|
+
*
|
|
8
|
+
* The orchestrator in `analyzer.ts` walks the registered analyzers for
|
|
9
|
+
* every resource it encounters. New sources (Azure Advisor, custom
|
|
10
|
+
* checks, pricing-enriched analyzers, …) can be added by implementing
|
|
11
|
+
* the interface and registering them in `registry.ts` without touching
|
|
12
|
+
* the orchestrator.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/azure/analyzers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CA2CtB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9D"}
|
package/dist/azure/config.js
CHANGED
|
@@ -35,6 +35,7 @@ export async function loadAzureConfig(configPath) {
|
|
|
35
35
|
const rawYaml = yaml.load(raw);
|
|
36
36
|
const parsed = ConfigSchema.parse(rawYaml);
|
|
37
37
|
return {
|
|
38
|
+
concurrency: parsed.azure.concurrency,
|
|
38
39
|
preferredLocation: parsed.azure.preferredLocation,
|
|
39
40
|
subscriptionIds: parsed.azure.subscriptionIds,
|
|
40
41
|
thresholds: parsed.azure.thresholds,
|
package/dist/azure/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAmB;IAEnB,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3C,OAAO;gBACL,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;gBACjD,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,eAAe;gBAC7C,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU;gBACnC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY;aACxC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE;oBACjE,KAAK,EAAE,KAAK;iBACb,CAAC,CAAC;YACL,CAAC;YACD,MAAM,IAAI,KAAK,CACb,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAC/E,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,CACT,iEAAiE,CAClE,CAAC;IAEF,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB;QACrD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC;QAC5C,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,4CAA4C,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE5E,OAAO;QACL,iBAAiB,EAAE,YAAY;QAC/B,eAAe;QACf,YAAY,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,QAAgB;IAC3C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;QAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAmB;IAEnB,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3C,OAAO;gBACL,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW;gBACrC,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;gBACjD,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,eAAe;gBAC7C,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU;gBACnC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY;aACxC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE;oBACjE,KAAK,EAAE,KAAK;iBACb,CAAC,CAAC;YACL,CAAC;YACD,MAAM,IAAI,KAAK,CACb,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAC/E,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,CACT,iEAAiE,CAClE,CAAC;IAEF,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB;QACrD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC;QAC5C,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,4CAA4C,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE5E,OAAO;QACL,iBAAiB,EAAE,YAAY;QAC/B,eAAe;QACf,YAAY,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,QAAgB;IAC3C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;QAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -5,6 +5,7 @@ import type { WebSiteManagementClient } from "@azure/arm-appservice";
|
|
|
5
5
|
import type { MonitorClient } from "@azure/arm-monitor";
|
|
6
6
|
import * as armResources from "@azure/arm-resources";
|
|
7
7
|
import type { AnalysisResult, Thresholds } from "../../types.js";
|
|
8
|
+
import { type MetricsCache } from "../utils.js";
|
|
8
9
|
/**
|
|
9
10
|
* Analyzes an Azure App Service Plan for potential cost optimization.
|
|
10
11
|
*
|
|
@@ -14,5 +15,5 @@ import type { AnalysisResult, Thresholds } from "../../types.js";
|
|
|
14
15
|
* @param timespanDays - Number of days to analyze metrics
|
|
15
16
|
* @returns Analysis result with cost risk and reason
|
|
16
17
|
*/
|
|
17
|
-
export declare function analyzeAppServicePlan(resource: armResources.GenericResource, webSiteClient: WebSiteManagementClient, monitorClient: MonitorClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean): Promise<AnalysisResult>;
|
|
18
|
+
export declare function analyzeAppServicePlan(resource: armResources.GenericResource, webSiteClient: WebSiteManagementClient, monitorClient: MonitorClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache): Promise<AnalysisResult>;
|
|
18
19
|
//# sourceMappingURL=app-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-service.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/app-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-service.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/app-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CA2FzB"}
|