@pagopa/dx-savemoney 0.1.6 → 0.2.1
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 +104 -27
- package/dist/azure/analyzer.d.ts +4 -4
- package/dist/azure/analyzer.d.ts.map +1 -1
- package/dist/azure/analyzer.js +16 -10
- package/dist/azure/analyzer.js.map +1 -1
- package/dist/azure/config.d.ts +15 -3
- package/dist/azure/config.d.ts.map +1 -1
- package/dist/azure/config.js +35 -17
- package/dist/azure/config.js.map +1 -1
- package/dist/azure/report.d.ts +2 -2
- package/dist/azure/report.d.ts.map +1 -1
- package/dist/azure/report.js +68 -3
- package/dist/azure/report.js.map +1 -1
- package/dist/azure/resources/app-service.d.ts +2 -2
- package/dist/azure/resources/app-service.d.ts.map +1 -1
- package/dist/azure/resources/app-service.js +8 -5
- package/dist/azure/resources/app-service.js.map +1 -1
- package/dist/azure/resources/container-app.d.ts +2 -2
- package/dist/azure/resources/container-app.d.ts.map +1 -1
- package/dist/azure/resources/container-app.js +10 -8
- package/dist/azure/resources/container-app.js.map +1 -1
- package/dist/azure/resources/public-ip.d.ts +2 -2
- package/dist/azure/resources/public-ip.d.ts.map +1 -1
- package/dist/azure/resources/public-ip.js +3 -3
- package/dist/azure/resources/public-ip.js.map +1 -1
- package/dist/azure/resources/static-web-app.d.ts +2 -2
- package/dist/azure/resources/static-web-app.d.ts.map +1 -1
- package/dist/azure/resources/static-web-app.js +4 -5
- package/dist/azure/resources/static-web-app.js.map +1 -1
- package/dist/azure/resources/storage.d.ts +2 -2
- package/dist/azure/resources/storage.d.ts.map +1 -1
- package/dist/azure/resources/storage.js +4 -3
- package/dist/azure/resources/storage.js.map +1 -1
- package/dist/azure/resources/vm.d.ts +2 -2
- package/dist/azure/resources/vm.d.ts.map +1 -1
- package/dist/azure/resources/vm.js +4 -5
- package/dist/azure/resources/vm.js.map +1 -1
- package/dist/azure/types.d.ts +10 -2
- package/dist/azure/types.d.ts.map +1 -1
- package/dist/azure/utils.d.ts +9 -0
- package/dist/azure/utils.d.ts.map +1 -1
- package/dist/azure/utils.js +14 -0
- package/dist/azure/utils.js.map +1 -1
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -54
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +210 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +97 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -1
- package/package.json +10 -4
- package/src/azure/__tests__/config.test.ts +97 -0
- package/src/azure/__tests__/fixtures/full-override.yaml +25 -0
- package/src/azure/__tests__/fixtures/partial-override.yaml +10 -0
- package/src/azure/__tests__/report.test.ts +138 -0
- package/src/azure/__tests__/utils.test.ts +124 -0
- package/src/azure/analyzer.ts +24 -3
- package/src/azure/config.ts +36 -21
- package/src/azure/report.ts +81 -4
- package/src/azure/resources/__tests__/storage.test.ts +185 -0
- package/src/azure/resources/app-service.ts +13 -5
- package/src/azure/resources/container-app.ts +13 -4
- package/src/azure/resources/public-ip.ts +4 -3
- package/src/azure/resources/static-web-app.ts +5 -5
- package/src/azure/resources/storage.ts +7 -3
- package/src/azure/resources/vm.ts +5 -5
- package/src/azure/types.ts +15 -2
- package/src/azure/utils.ts +21 -0
- package/src/index.ts +19 -69
- package/src/schema.ts +134 -0
- package/src/types.ts +14 -0
package/dist/index.js
CHANGED
|
@@ -12,66 +12,30 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This tool does NOT modify, tag, or delete any resources.
|
|
14
14
|
*/
|
|
15
|
-
export * from "./types.js";
|
|
16
15
|
// Export Azure module
|
|
17
16
|
import * as azureModule from "./azure/index.js";
|
|
18
17
|
export const azure = azureModule;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import * as fs from "fs";
|
|
22
|
-
import * as readline from "readline";
|
|
18
|
+
export * from "./types.js";
|
|
19
|
+
import { loadAzureConfig } from "./azure/config.js";
|
|
23
20
|
/**
|
|
24
|
-
* Loads configuration from file, environment variables, or interactive prompts.
|
|
21
|
+
* Loads configuration from a YAML file, environment variables, or interactive prompts.
|
|
25
22
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
throw new Error("Config file must contain 'tenantId' and 'subscriptionIds'");
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
...config,
|
|
41
|
-
preferredLocation: config.preferredLocation || "italynorth",
|
|
42
|
-
timespanDays: config.timespanDays || 30,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
throw new Error(`Failed to load config file: ${error instanceof Error ? error.message : error}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
logger.info("Configuration file not found. Checking environment variables...");
|
|
50
|
-
const tenantId = process.env.ARM_TENANT_ID || (await prompt("Enter Tenant ID: "));
|
|
51
|
-
const subscriptionIds = process.env.ARM_SUBSCRIPTION_ID
|
|
52
|
-
? process.env.ARM_SUBSCRIPTION_ID.split(",")
|
|
53
|
-
: (await prompt("Enter Subscription IDs (comma-separated): ")).split(",");
|
|
54
|
-
return {
|
|
55
|
-
preferredLocation: "italynorth",
|
|
56
|
-
subscriptionIds,
|
|
57
|
-
tenantId,
|
|
58
|
-
timespanDays: 30,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Prompts user for input via stdin.
|
|
23
|
+
* The YAML file should have an `azure` top-level key:
|
|
24
|
+
* ```yaml
|
|
25
|
+
* azure:
|
|
26
|
+
* subscriptionIds:
|
|
27
|
+
* - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
28
|
+
* preferredLocation: italynorth
|
|
29
|
+
* timespanDays: 30
|
|
30
|
+
* thresholds:
|
|
31
|
+
* vm:
|
|
32
|
+
* cpuPercent: 5
|
|
33
|
+
* ```
|
|
63
34
|
*
|
|
64
|
-
* @param
|
|
65
|
-
* @returns
|
|
35
|
+
* @param configPath - Optional path to a YAML configuration file
|
|
36
|
+
* @returns Configuration object with subscription IDs, settings and thresholds
|
|
66
37
|
*/
|
|
67
|
-
export async function
|
|
68
|
-
|
|
69
|
-
input: process.stdin,
|
|
70
|
-
output: process.stdout,
|
|
71
|
-
});
|
|
72
|
-
return new Promise((resolve) => rl.question(question, (answer) => {
|
|
73
|
-
rl.close();
|
|
74
|
-
resolve(answer);
|
|
75
|
-
}));
|
|
38
|
+
export async function loadConfig(configPath) {
|
|
39
|
+
return loadAzureConfig(configPath);
|
|
76
40
|
}
|
|
77
41
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,sBAAsB;AACtB,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;AAEjC,cAAc,YAAY,CAAC;AAI3B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAmB;IAClD,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for the SaveMoney YAML configuration file.
|
|
3
|
+
*
|
|
4
|
+
* The entire config is represented by a single `ConfigSchema` composed of
|
|
5
|
+
* sub-schemas. All types used at runtime are derived from these schemas via
|
|
6
|
+
* `z.infer<>` so the source of truth is always the schema.
|
|
7
|
+
*
|
|
8
|
+
* YAML structure:
|
|
9
|
+
* ```yaml
|
|
10
|
+
* azure:
|
|
11
|
+
* subscriptionIds:
|
|
12
|
+
* - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
13
|
+
* preferredLocation: italynorth # optional
|
|
14
|
+
* timespanDays: 30 # optional
|
|
15
|
+
* thresholds: # optional – omit to keep built-in defaults
|
|
16
|
+
* vm:
|
|
17
|
+
* cpuPercent: 5
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
export declare const ThresholdsSchema: z.ZodObject<{
|
|
22
|
+
appService: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
23
|
+
cpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
24
|
+
memoryPercent: z.ZodDefault<z.ZodNumber>;
|
|
25
|
+
premiumCpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
27
|
+
cpuPercent: number;
|
|
28
|
+
memoryPercent: number;
|
|
29
|
+
premiumCpuPercent: number;
|
|
30
|
+
}, {
|
|
31
|
+
cpuPercent: number;
|
|
32
|
+
memoryPercent: number;
|
|
33
|
+
premiumCpuPercent: number;
|
|
34
|
+
} | undefined>>;
|
|
35
|
+
containerApp: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
36
|
+
cpuNanoCores: z.ZodDefault<z.ZodNumber>;
|
|
37
|
+
memoryBytes: z.ZodDefault<z.ZodNumber>;
|
|
38
|
+
networkBytes: z.ZodDefault<z.ZodNumber>;
|
|
39
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
40
|
+
cpuNanoCores: number;
|
|
41
|
+
memoryBytes: number;
|
|
42
|
+
networkBytes: number;
|
|
43
|
+
}, {
|
|
44
|
+
cpuNanoCores: number;
|
|
45
|
+
memoryBytes: number;
|
|
46
|
+
networkBytes: number;
|
|
47
|
+
} | undefined>>;
|
|
48
|
+
publicIp: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
49
|
+
bytesInDDoS: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
51
|
+
bytesInDDoS: number;
|
|
52
|
+
}, {
|
|
53
|
+
bytesInDDoS: number;
|
|
54
|
+
} | undefined>>;
|
|
55
|
+
staticSite: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
56
|
+
bytesSent: z.ZodDefault<z.ZodNumber>;
|
|
57
|
+
siteHits: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
59
|
+
bytesSent: number;
|
|
60
|
+
siteHits: number;
|
|
61
|
+
}, {
|
|
62
|
+
bytesSent: number;
|
|
63
|
+
siteHits: number;
|
|
64
|
+
} | undefined>>;
|
|
65
|
+
storage: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
66
|
+
transactionsPerDay: z.ZodDefault<z.ZodNumber>;
|
|
67
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
68
|
+
transactionsPerDay: number;
|
|
69
|
+
}, {
|
|
70
|
+
transactionsPerDay: number;
|
|
71
|
+
} | undefined>>;
|
|
72
|
+
vm: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
73
|
+
cpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
networkInBytesPerDay: z.ZodDefault<z.ZodNumber>;
|
|
75
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
76
|
+
cpuPercent: number;
|
|
77
|
+
networkInBytesPerDay: number;
|
|
78
|
+
}, {
|
|
79
|
+
cpuPercent: number;
|
|
80
|
+
networkInBytesPerDay: number;
|
|
81
|
+
} | undefined>>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
/**
|
|
84
|
+
* Single Zod schema representing the entire YAML configuration file.
|
|
85
|
+
* Use `ConfigSchema.parse(rawYaml)` to validate and apply defaults.
|
|
86
|
+
*/
|
|
87
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
88
|
+
azure: z.ZodObject<{
|
|
89
|
+
preferredLocation: z.ZodDefault<z.ZodString>;
|
|
90
|
+
subscriptionIds: z.ZodArray<z.ZodString>;
|
|
91
|
+
thresholds: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
92
|
+
appService: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
93
|
+
cpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
94
|
+
memoryPercent: z.ZodDefault<z.ZodNumber>;
|
|
95
|
+
premiumCpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
96
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
97
|
+
cpuPercent: number;
|
|
98
|
+
memoryPercent: number;
|
|
99
|
+
premiumCpuPercent: number;
|
|
100
|
+
}, {
|
|
101
|
+
cpuPercent: number;
|
|
102
|
+
memoryPercent: number;
|
|
103
|
+
premiumCpuPercent: number;
|
|
104
|
+
} | undefined>>;
|
|
105
|
+
containerApp: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
106
|
+
cpuNanoCores: z.ZodDefault<z.ZodNumber>;
|
|
107
|
+
memoryBytes: z.ZodDefault<z.ZodNumber>;
|
|
108
|
+
networkBytes: z.ZodDefault<z.ZodNumber>;
|
|
109
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
110
|
+
cpuNanoCores: number;
|
|
111
|
+
memoryBytes: number;
|
|
112
|
+
networkBytes: number;
|
|
113
|
+
}, {
|
|
114
|
+
cpuNanoCores: number;
|
|
115
|
+
memoryBytes: number;
|
|
116
|
+
networkBytes: number;
|
|
117
|
+
} | undefined>>;
|
|
118
|
+
publicIp: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
119
|
+
bytesInDDoS: z.ZodDefault<z.ZodNumber>;
|
|
120
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
121
|
+
bytesInDDoS: number;
|
|
122
|
+
}, {
|
|
123
|
+
bytesInDDoS: number;
|
|
124
|
+
} | undefined>>;
|
|
125
|
+
staticSite: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
126
|
+
bytesSent: z.ZodDefault<z.ZodNumber>;
|
|
127
|
+
siteHits: z.ZodDefault<z.ZodNumber>;
|
|
128
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
129
|
+
bytesSent: number;
|
|
130
|
+
siteHits: number;
|
|
131
|
+
}, {
|
|
132
|
+
bytesSent: number;
|
|
133
|
+
siteHits: number;
|
|
134
|
+
} | undefined>>;
|
|
135
|
+
storage: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
136
|
+
transactionsPerDay: z.ZodDefault<z.ZodNumber>;
|
|
137
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
138
|
+
transactionsPerDay: number;
|
|
139
|
+
}, {
|
|
140
|
+
transactionsPerDay: number;
|
|
141
|
+
} | undefined>>;
|
|
142
|
+
vm: z.ZodPipe<z.ZodOptional<z.ZodObject<{
|
|
143
|
+
cpuPercent: z.ZodDefault<z.ZodNumber>;
|
|
144
|
+
networkInBytesPerDay: z.ZodDefault<z.ZodNumber>;
|
|
145
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
146
|
+
cpuPercent: number;
|
|
147
|
+
networkInBytesPerDay: number;
|
|
148
|
+
}, {
|
|
149
|
+
cpuPercent: number;
|
|
150
|
+
networkInBytesPerDay: number;
|
|
151
|
+
} | undefined>>;
|
|
152
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
153
|
+
appService: {
|
|
154
|
+
cpuPercent: number;
|
|
155
|
+
memoryPercent: number;
|
|
156
|
+
premiumCpuPercent: number;
|
|
157
|
+
};
|
|
158
|
+
containerApp: {
|
|
159
|
+
cpuNanoCores: number;
|
|
160
|
+
memoryBytes: number;
|
|
161
|
+
networkBytes: number;
|
|
162
|
+
};
|
|
163
|
+
publicIp: {
|
|
164
|
+
bytesInDDoS: number;
|
|
165
|
+
};
|
|
166
|
+
staticSite: {
|
|
167
|
+
bytesSent: number;
|
|
168
|
+
siteHits: number;
|
|
169
|
+
};
|
|
170
|
+
storage: {
|
|
171
|
+
transactionsPerDay: number;
|
|
172
|
+
};
|
|
173
|
+
vm: {
|
|
174
|
+
cpuPercent: number;
|
|
175
|
+
networkInBytesPerDay: number;
|
|
176
|
+
};
|
|
177
|
+
}, {
|
|
178
|
+
appService: {
|
|
179
|
+
cpuPercent: number;
|
|
180
|
+
memoryPercent: number;
|
|
181
|
+
premiumCpuPercent: number;
|
|
182
|
+
};
|
|
183
|
+
containerApp: {
|
|
184
|
+
cpuNanoCores: number;
|
|
185
|
+
memoryBytes: number;
|
|
186
|
+
networkBytes: number;
|
|
187
|
+
};
|
|
188
|
+
publicIp: {
|
|
189
|
+
bytesInDDoS: number;
|
|
190
|
+
};
|
|
191
|
+
staticSite: {
|
|
192
|
+
bytesSent: number;
|
|
193
|
+
siteHits: number;
|
|
194
|
+
};
|
|
195
|
+
storage: {
|
|
196
|
+
transactionsPerDay: number;
|
|
197
|
+
};
|
|
198
|
+
vm: {
|
|
199
|
+
cpuPercent: number;
|
|
200
|
+
networkInBytesPerDay: number;
|
|
201
|
+
};
|
|
202
|
+
} | undefined>>;
|
|
203
|
+
timespanDays: z.ZodDefault<z.ZodNumber>;
|
|
204
|
+
}, z.core.$strict>;
|
|
205
|
+
}, z.core.$strict>;
|
|
206
|
+
/** Fully-resolved configuration (all defaults applied). */
|
|
207
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
208
|
+
/** Fully-resolved thresholds (all defaults applied). */
|
|
209
|
+
export type Thresholds = z.infer<typeof ThresholdsSchema>;
|
|
210
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4DxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqBlB,CAAC;AAoBZ;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAmD,CAAC;AAI7E,2DAA2D;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for the SaveMoney YAML configuration file.
|
|
3
|
+
*
|
|
4
|
+
* The entire config is represented by a single `ConfigSchema` composed of
|
|
5
|
+
* sub-schemas. All types used at runtime are derived from these schemas via
|
|
6
|
+
* `z.infer<>` so the source of truth is always the schema.
|
|
7
|
+
*
|
|
8
|
+
* YAML structure:
|
|
9
|
+
* ```yaml
|
|
10
|
+
* azure:
|
|
11
|
+
* subscriptionIds:
|
|
12
|
+
* - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
13
|
+
* preferredLocation: italynorth # optional
|
|
14
|
+
* timespanDays: 30 # optional
|
|
15
|
+
* thresholds: # optional – omit to keep built-in defaults
|
|
16
|
+
* vm:
|
|
17
|
+
* cpuPercent: 5
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
// ── per-resource threshold sub-schemas ──────────────────────────────────────
|
|
22
|
+
const VmThresholdsSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
/** CPU threshold (%) below which usage is flagged as low. Default: 1 */
|
|
25
|
+
cpuPercent: z.number().default(1),
|
|
26
|
+
/** Network inbound threshold (bytes/day) below which traffic is flagged as low. Default: 3 MB */
|
|
27
|
+
networkInBytesPerDay: z.number().default(1024 * 1024 * 3),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
const AppServiceThresholdsSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
/** CPU threshold (%) below which usage is flagged as very low. Default: 5 */
|
|
33
|
+
cpuPercent: z.number().default(5),
|
|
34
|
+
/** Memory threshold (%) below which usage is flagged as very low. Default: 10 */
|
|
35
|
+
memoryPercent: z.number().default(10),
|
|
36
|
+
/** CPU threshold (%) for Premium-tier plans below which over-provisioning is flagged. Default: 10 */
|
|
37
|
+
premiumCpuPercent: z.number().default(10),
|
|
38
|
+
})
|
|
39
|
+
.strict();
|
|
40
|
+
const ContainerAppThresholdsSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
/** CPU threshold (nanoCores) below which usage is flagged as very low. Default: 1 000 000 (0.001 cores) */
|
|
43
|
+
cpuNanoCores: z.number().default(1_000_000),
|
|
44
|
+
/** Memory threshold (bytes) below which usage is flagged as very low. Default: 10 MB */
|
|
45
|
+
memoryBytes: z.number().default(10_485_760),
|
|
46
|
+
/** Combined Rx+Tx network threshold (bytes/day) below which traffic is flagged as very low. Default: ~33 KB */
|
|
47
|
+
networkBytes: z.number().default(34_000),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
50
|
+
const StorageThresholdsSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
/** Average daily transaction count below which the account is flagged. Default: 10 */
|
|
53
|
+
transactionsPerDay: z.number().default(10),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
const PublicIpThresholdsSchema = z
|
|
57
|
+
.object({
|
|
58
|
+
/** DDoS inbound bytes/day threshold below which traffic is flagged as very low. Default: ~332 KB */
|
|
59
|
+
bytesInDDoS: z.number().default(340_000),
|
|
60
|
+
})
|
|
61
|
+
.strict();
|
|
62
|
+
const StaticSiteThresholdsSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
/** Total bytes sent below which data transfer is flagged as very low. Default: 1 MB */
|
|
65
|
+
bytesSent: z.number().default(1_048_576),
|
|
66
|
+
/** Total site hits below which traffic is flagged as very low. Default: 100 */
|
|
67
|
+
siteHits: z.number().default(100),
|
|
68
|
+
})
|
|
69
|
+
.strict();
|
|
70
|
+
// ── composed thresholds schema ───────────────────────────────────────────────
|
|
71
|
+
export const ThresholdsSchema = z
|
|
72
|
+
.object({
|
|
73
|
+
appService: AppServiceThresholdsSchema.optional().transform((v) => AppServiceThresholdsSchema.parse(v ?? {})),
|
|
74
|
+
containerApp: ContainerAppThresholdsSchema.optional().transform((v) => ContainerAppThresholdsSchema.parse(v ?? {})),
|
|
75
|
+
publicIp: PublicIpThresholdsSchema.optional().transform((v) => PublicIpThresholdsSchema.parse(v ?? {})),
|
|
76
|
+
staticSite: StaticSiteThresholdsSchema.optional().transform((v) => StaticSiteThresholdsSchema.parse(v ?? {})),
|
|
77
|
+
storage: StorageThresholdsSchema.optional().transform((v) => StorageThresholdsSchema.parse(v ?? {})),
|
|
78
|
+
vm: VmThresholdsSchema.optional().transform((v) => VmThresholdsSchema.parse(v ?? {})),
|
|
79
|
+
})
|
|
80
|
+
.strict();
|
|
81
|
+
// ── top-level config schema ──────────────────────────────────────────────────
|
|
82
|
+
const AzureSectionSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
preferredLocation: z.string().default("italynorth"),
|
|
85
|
+
subscriptionIds: z
|
|
86
|
+
.array(z.string())
|
|
87
|
+
.min(1, "Config file must contain at least one entry in 'azure.subscriptionIds'"),
|
|
88
|
+
thresholds: ThresholdsSchema.optional().transform((v) => ThresholdsSchema.parse(v ?? {})),
|
|
89
|
+
timespanDays: z.number().int().positive().default(30),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
/**
|
|
93
|
+
* Single Zod schema representing the entire YAML configuration file.
|
|
94
|
+
* Use `ConfigSchema.parse(rawYaml)` to validate and apply defaults.
|
|
95
|
+
*/
|
|
96
|
+
export const ConfigSchema = z.object({ azure: AzureSectionSchema }).strict();
|
|
97
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -14,6 +14,41 @@ export type BaseConfig = {
|
|
|
14
14
|
timespanDays: number;
|
|
15
15
|
};
|
|
16
16
|
export type CostRisk = "high" | "low" | "medium";
|
|
17
|
+
/**
|
|
18
|
+
* Configurable thresholds used during resource analysis.
|
|
19
|
+
* Derived from `ThresholdsSchema` — the schema is the single source of truth.
|
|
20
|
+
*/
|
|
21
|
+
export type { Thresholds } from "./schema.js";
|
|
22
|
+
/**
|
|
23
|
+
* Default threshold values — produced by parsing an empty object through
|
|
24
|
+
* `ThresholdsSchema`, which applies all schema-defined defaults.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DEFAULT_THRESHOLDS: {
|
|
27
|
+
appService: {
|
|
28
|
+
cpuPercent: number;
|
|
29
|
+
memoryPercent: number;
|
|
30
|
+
premiumCpuPercent: number;
|
|
31
|
+
};
|
|
32
|
+
containerApp: {
|
|
33
|
+
cpuNanoCores: number;
|
|
34
|
+
memoryBytes: number;
|
|
35
|
+
networkBytes: number;
|
|
36
|
+
};
|
|
37
|
+
publicIp: {
|
|
38
|
+
bytesInDDoS: number;
|
|
39
|
+
};
|
|
40
|
+
staticSite: {
|
|
41
|
+
bytesSent: number;
|
|
42
|
+
siteHits: number;
|
|
43
|
+
};
|
|
44
|
+
storage: {
|
|
45
|
+
transactionsPerDay: number;
|
|
46
|
+
};
|
|
47
|
+
vm: {
|
|
48
|
+
cpuPercent: number;
|
|
49
|
+
networkInBytesPerDay: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
17
52
|
/**
|
|
18
53
|
* Merges analysis results, preserving existing reasons and combining suspectedUnused flags.
|
|
19
54
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEjD;;;GAGG;AACH,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;CAA6B,CAAC;AAE7D;;GAEG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,cAAc,GAC7B,cAAc,CAOhB"}
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Common types shared across all cloud providers
|
|
3
3
|
*/
|
|
4
|
+
import { ThresholdsSchema } from "./schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* Default threshold values — produced by parsing an empty object through
|
|
7
|
+
* `ThresholdsSchema`, which applies all schema-defined defaults.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_THRESHOLDS = ThresholdsSchema.parse({});
|
|
4
10
|
/**
|
|
5
11
|
* Merges analysis results, preserving existing reasons and combining suspectedUnused flags.
|
|
6
12
|
*/
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAwB/C;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE7D;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA0B,EAC1B,cAA8B;IAE9B,OAAO;QACL,QAAQ,EAAE,cAAc,CAAC,QAAQ;QACjC,MAAM,EAAE,UAAU,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM;QACjD,eAAe,EACb,UAAU,CAAC,eAAe,IAAI,cAAc,CAAC,eAAe;KAC/D,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagopa/dx-savemoney",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Azure resource analyzer for finding unused or cost-inefficient resources.",
|
|
6
6
|
"repository": {
|
|
@@ -34,23 +34,29 @@
|
|
|
34
34
|
"@azure/arm-network": "^34.1.0",
|
|
35
35
|
"@azure/arm-resources": "^7.0.0",
|
|
36
36
|
"@azure/identity": "^4.13.0",
|
|
37
|
-
"@logtape/logtape": "^1.3.7"
|
|
37
|
+
"@logtape/logtape": "^1.3.7",
|
|
38
|
+
"js-yaml": "^4.1.1",
|
|
39
|
+
"zod": "^4.3.6"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@tsconfig/node24": "24.0.4",
|
|
43
|
+
"@types/js-yaml": "^4.0.9",
|
|
41
44
|
"@types/node": "^22.19.15",
|
|
42
45
|
"@vitest/coverage-v8": "^3.2.4",
|
|
43
|
-
"eslint": "^
|
|
46
|
+
"eslint": "^10.1.0",
|
|
44
47
|
"prettier": "3.8.1",
|
|
45
48
|
"typescript": "~5.9.3",
|
|
46
49
|
"vitest": "^3.2.4",
|
|
47
|
-
"@pagopa/eslint-config": "^
|
|
50
|
+
"@pagopa/eslint-config": "^6.0.0"
|
|
48
51
|
},
|
|
49
52
|
"scripts": {
|
|
50
53
|
"build": "rm -rf dist && tsc",
|
|
51
54
|
"dev": "tsc --watch",
|
|
52
55
|
"format": "prettier --write .",
|
|
53
56
|
"format:check": "prettier --check .",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest",
|
|
59
|
+
"test:coverage": "vitest run --coverage",
|
|
54
60
|
"typecheck": "tsc --noEmit"
|
|
55
61
|
}
|
|
56
62
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for loadConfig() — verifies that:
|
|
3
|
+
* 1. Returns default thresholds and prompts for subscriptionIds when no file is given.
|
|
4
|
+
* 2. Loads subscriptionIds, location, timespanDays and thresholds from a YAML file.
|
|
5
|
+
* 3. Partial threshold overrides keep defaults for non-overridden fields.
|
|
6
|
+
* 4. Throws a clear error for a non-existent explicit path.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
|
|
13
|
+
import { loadConfig } from "../../index.js";
|
|
14
|
+
import { DEFAULT_THRESHOLDS } from "../../types.js";
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
/** Absolute paths used in tests */
|
|
19
|
+
const FIXTURE_PARTIAL = path.resolve(
|
|
20
|
+
__dirname,
|
|
21
|
+
"fixtures/partial-override.yaml",
|
|
22
|
+
);
|
|
23
|
+
const FIXTURE_FULL = path.resolve(__dirname, "fixtures/full-override.yaml");
|
|
24
|
+
|
|
25
|
+
describe("loadConfig", () => {
|
|
26
|
+
it("throws when explicit path does not exist", async () => {
|
|
27
|
+
await expect(loadConfig("/nonexistent/config.yaml")).rejects.toThrow(
|
|
28
|
+
"Config file not found",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("loads subscriptionIds and defaults from a partial YAML file", async () => {
|
|
33
|
+
const result = await loadConfig(FIXTURE_PARTIAL);
|
|
34
|
+
|
|
35
|
+
expect(result.subscriptionIds).toEqual([
|
|
36
|
+
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
37
|
+
]);
|
|
38
|
+
expect(result.preferredLocation).toBe("italynorth");
|
|
39
|
+
expect(result.timespanDays).toBe(30);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("loads partial threshold overrides and keeps defaults for missing fields", async () => {
|
|
43
|
+
const result = await loadConfig(FIXTURE_PARTIAL);
|
|
44
|
+
|
|
45
|
+
// Overridden values
|
|
46
|
+
expect(result.thresholds?.vm.cpuPercent).toBe(5);
|
|
47
|
+
expect(result.thresholds?.storage.transactionsPerDay).toBe(50);
|
|
48
|
+
|
|
49
|
+
// Non-overridden vm field keeps default
|
|
50
|
+
expect(result.thresholds?.vm.networkInBytesPerDay).toBe(
|
|
51
|
+
DEFAULT_THRESHOLDS.vm.networkInBytesPerDay,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Entire non-overridden sections keep defaults
|
|
55
|
+
expect(result.thresholds?.appService).toEqual(
|
|
56
|
+
DEFAULT_THRESHOLDS.appService,
|
|
57
|
+
);
|
|
58
|
+
expect(result.thresholds?.containerApp).toEqual(
|
|
59
|
+
DEFAULT_THRESHOLDS.containerApp,
|
|
60
|
+
);
|
|
61
|
+
expect(result.thresholds?.publicIp).toEqual(DEFAULT_THRESHOLDS.publicIp);
|
|
62
|
+
expect(result.thresholds?.staticSite).toEqual(
|
|
63
|
+
DEFAULT_THRESHOLDS.staticSite,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("loads all values from a full YAML file", async () => {
|
|
68
|
+
const result = await loadConfig(FIXTURE_FULL);
|
|
69
|
+
|
|
70
|
+
expect(result.subscriptionIds).toHaveLength(2);
|
|
71
|
+
expect(result.preferredLocation).toBe("westeurope");
|
|
72
|
+
expect(result.timespanDays).toBe(60);
|
|
73
|
+
|
|
74
|
+
expect(result.thresholds?.vm.cpuPercent).toBe(5);
|
|
75
|
+
expect(result.thresholds?.vm.networkInBytesPerDay).toBe(10485760);
|
|
76
|
+
expect(result.thresholds?.appService.cpuPercent).toBe(10);
|
|
77
|
+
expect(result.thresholds?.appService.memoryPercent).toBe(20);
|
|
78
|
+
expect(result.thresholds?.appService.premiumCpuPercent).toBe(15);
|
|
79
|
+
expect(result.thresholds?.containerApp.cpuNanoCores).toBe(5000000);
|
|
80
|
+
expect(result.thresholds?.containerApp.memoryBytes).toBe(52428800);
|
|
81
|
+
expect(result.thresholds?.containerApp.networkBytes).toBe(100000);
|
|
82
|
+
expect(result.thresholds?.storage.transactionsPerDay).toBe(50);
|
|
83
|
+
expect(result.thresholds?.publicIp.bytesInDDoS).toBe(1048576);
|
|
84
|
+
expect(result.thresholds?.staticSite.siteHits).toBe(500);
|
|
85
|
+
expect(result.thresholds?.staticSite.bytesSent).toBe(5242880);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns a complete Thresholds object (all required keys present)", async () => {
|
|
89
|
+
const result = await loadConfig(FIXTURE_PARTIAL);
|
|
90
|
+
expect(result.thresholds).toHaveProperty("vm");
|
|
91
|
+
expect(result.thresholds).toHaveProperty("appService");
|
|
92
|
+
expect(result.thresholds).toHaveProperty("containerApp");
|
|
93
|
+
expect(result.thresholds).toHaveProperty("storage");
|
|
94
|
+
expect(result.thresholds).toHaveProperty("publicIp");
|
|
95
|
+
expect(result.thresholds).toHaveProperty("staticSite");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
azure:
|
|
2
|
+
subscriptionIds:
|
|
3
|
+
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
4
|
+
- yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
|
|
5
|
+
preferredLocation: westeurope
|
|
6
|
+
timespanDays: 60
|
|
7
|
+
thresholds:
|
|
8
|
+
vm:
|
|
9
|
+
cpuPercent: 5
|
|
10
|
+
networkInBytesPerDay: 10485760
|
|
11
|
+
appService:
|
|
12
|
+
cpuPercent: 10
|
|
13
|
+
memoryPercent: 20
|
|
14
|
+
premiumCpuPercent: 15
|
|
15
|
+
containerApp:
|
|
16
|
+
cpuNanoCores: 5000000
|
|
17
|
+
memoryBytes: 52428800
|
|
18
|
+
networkBytes: 100000
|
|
19
|
+
storage:
|
|
20
|
+
transactionsPerDay: 50
|
|
21
|
+
publicIp:
|
|
22
|
+
bytesInDDoS: 1048576
|
|
23
|
+
staticSite:
|
|
24
|
+
siteHits: 500
|
|
25
|
+
bytesSent: 5242880
|