puls-dev 0.3.3 → 0.3.5
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/bin/install-shell.d.ts +2 -0
- package/dist/bin/install-shell.js +136 -0
- package/dist/bin/puls.d.ts +1 -0
- package/dist/bin/puls.js +145 -0
- package/dist/core/checker.js +74 -0
- package/dist/core/config.d.ts +3 -0
- package/dist/core/context.d.ts +1 -0
- package/dist/core/decorators.d.ts +1 -0
- package/dist/core/decorators.js +39 -5
- package/dist/core/output.js +8 -1
- package/dist/core/production.test.js +1 -0
- package/dist/core/resource.d.ts +35 -0
- package/dist/core/resource.js +57 -1
- package/dist/core/secret.d.ts +1 -0
- package/dist/core/secret.js +5 -0
- package/dist/core/stack.d.ts +11 -0
- package/dist/core/stack.js +141 -90
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/providers/aws/api.js +3 -0
- package/dist/providers/aws/ec2.d.ts +5 -0
- package/dist/providers/aws/ec2.js +7 -0
- package/dist/providers/aws/lambda.d.ts +5 -0
- package/dist/providers/aws/lambda.js +24 -0
- package/dist/providers/aws/list.js +15 -3
- package/dist/providers/aws/rds.d.ts +9 -0
- package/dist/providers/aws/rds.js +19 -0
- package/dist/providers/do/database.d.ts +9 -0
- package/dist/providers/do/database.js +19 -0
- package/dist/providers/do/domain.js +1 -1
- package/dist/providers/do/droplet.d.ts +10 -0
- package/dist/providers/do/droplet.js +28 -3
- package/dist/providers/do/droplet.test.js +1 -1
- package/dist/providers/do/list.js +25 -2
- package/dist/providers/do/load_balancer.d.ts +5 -0
- package/dist/providers/do/load_balancer.js +7 -0
- package/dist/providers/do/vpc.d.ts +5 -0
- package/dist/providers/do/vpc.js +8 -0
- package/dist/providers/firebase/functions.d.ts +9 -0
- package/dist/providers/firebase/functions.js +28 -0
- package/dist/providers/firebase/list.js +34 -2
- package/dist/providers/gcp/api.js +6 -0
- package/dist/providers/gcp/cloudrun.d.ts +13 -0
- package/dist/providers/gcp/cloudrun.js +30 -0
- package/dist/providers/gcp/cloudsql.d.ts +9 -0
- package/dist/providers/gcp/cloudsql.js +20 -0
- package/dist/providers/gcp/list.js +12 -2
- package/dist/providers/gcp/template.d.ts +3 -0
- package/dist/providers/gcp/template.js +13 -1
- package/dist/providers/gcp/vm.d.ts +8 -0
- package/dist/providers/gcp/vm.js +22 -2
- package/dist/providers/proxmox/api.d.ts +1 -0
- package/dist/providers/proxmox/api.js +18 -3
- package/dist/providers/proxmox/base.d.ts +16 -0
- package/dist/providers/proxmox/base.js +121 -0
- package/dist/providers/proxmox/list.js +8 -1
- package/dist/providers/proxmox/template.d.ts +3 -10
- package/dist/providers/proxmox/template.js +51 -139
- package/dist/providers/proxmox/vm.d.ts +18 -10
- package/dist/providers/proxmox/vm.js +73 -152
- package/dist/types/diff.d.ts +17 -0
- package/dist/types/diff.js +1 -0
- package/dist/types/inventory.d.ts +65 -0
- package/package.json +7 -22
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { BaseBuilder } from "../../core/resource.js";
|
|
1
|
+
import { ProxmoxBaseBuilder } from "./base.js";
|
|
5
2
|
import { Config } from "../../core/config.js";
|
|
6
3
|
import { Output } from "../../core/output.js";
|
|
7
|
-
import { getPMClient } from "./api.js";
|
|
4
|
+
import { getPMClient, withVmidAllocation } from "./api.js";
|
|
8
5
|
import { getFileHash, parseProvisionMetadata, mergeProvisionMetadata } from "./hash.js";
|
|
9
|
-
import { checkPort, runProvisioner } from "../../core/provisioner.js";
|
|
10
6
|
import { resourceContextStorage } from "../../core/context.js";
|
|
11
|
-
export class VMBuilder extends
|
|
7
|
+
export class VMBuilder extends ProxmoxBaseBuilder {
|
|
12
8
|
out = {
|
|
13
9
|
ip: new Output(),
|
|
14
10
|
vmid: new Output(),
|
|
@@ -26,7 +22,7 @@ export class VMBuilder extends BaseBuilder {
|
|
|
26
22
|
_storage;
|
|
27
23
|
_vlan;
|
|
28
24
|
_ip;
|
|
29
|
-
|
|
25
|
+
_gateway;
|
|
30
26
|
_machine = "q35";
|
|
31
27
|
_forceConfigCheck = false;
|
|
32
28
|
constructor(name) {
|
|
@@ -42,6 +38,9 @@ export class VMBuilder extends BaseBuilder {
|
|
|
42
38
|
try {
|
|
43
39
|
const config = await pm.get(`/nodes/${match.node}/qemu/${match.vmid}/config`);
|
|
44
40
|
match.description = config.description ?? "";
|
|
41
|
+
match.cfgCores = config.cores;
|
|
42
|
+
match.cfgMemory = config.memory;
|
|
43
|
+
match.cfgMachine = config.machine ?? "q35";
|
|
45
44
|
}
|
|
46
45
|
catch (err) {
|
|
47
46
|
console.warn(` ⚠️ Could not fetch VM config for ${match.vmid}: ${err.message}`);
|
|
@@ -97,8 +96,8 @@ export class VMBuilder extends BaseBuilder {
|
|
|
97
96
|
this._ip = address;
|
|
98
97
|
return this;
|
|
99
98
|
}
|
|
100
|
-
|
|
101
|
-
this.
|
|
99
|
+
gateway(gw) {
|
|
100
|
+
this._gateway = gw;
|
|
102
101
|
return this;
|
|
103
102
|
}
|
|
104
103
|
machine(type) {
|
|
@@ -109,7 +108,30 @@ export class VMBuilder extends BaseBuilder {
|
|
|
109
108
|
this._forceConfigCheck = true;
|
|
110
109
|
return this;
|
|
111
110
|
}
|
|
111
|
+
getDiff(existing) {
|
|
112
|
+
const diffs = [];
|
|
113
|
+
if (existing.cfgCores !== undefined && existing.cfgCores !== this._cores) {
|
|
114
|
+
diffs.push({ field: "cores", declared: this._cores, live: existing.cfgCores });
|
|
115
|
+
}
|
|
116
|
+
if (existing.cfgMemory !== undefined && existing.cfgMemory !== this._memory) {
|
|
117
|
+
diffs.push({ field: "memory", declared: `${this._memory} MB`, live: `${existing.cfgMemory} MB` });
|
|
118
|
+
}
|
|
119
|
+
if (existing.cfgMachine !== undefined && this._machine !== existing.cfgMachine) {
|
|
120
|
+
diffs.push({ field: "machine", declared: this._machine, live: existing.cfgMachine });
|
|
121
|
+
}
|
|
122
|
+
return diffs;
|
|
123
|
+
}
|
|
112
124
|
async deploy() {
|
|
125
|
+
try {
|
|
126
|
+
return await this._deploy();
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
this.out.vmid.reject(err);
|
|
130
|
+
this.out.ip.reject(err);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async _deploy() {
|
|
113
135
|
const dryRun = this.isDryRunActive();
|
|
114
136
|
const existing = await this.discoveryPromise;
|
|
115
137
|
const pm = getPMClient();
|
|
@@ -121,8 +143,8 @@ export class VMBuilder extends BaseBuilder {
|
|
|
121
143
|
this.resolvedIp = await this.resolveExistingIp(existing.node, existing.vmid, pm);
|
|
122
144
|
if (this.resolvedIp) {
|
|
123
145
|
this.out.ip.resolve(this.resolvedIp);
|
|
146
|
+
this.registerHost();
|
|
124
147
|
}
|
|
125
|
-
this.registerHost();
|
|
126
148
|
const activeIp = this.resolvedIp ?? "0.0.0.0";
|
|
127
149
|
// 1. Calculate hashes and check if playbooks need to run
|
|
128
150
|
const appliedHashes = parseProvisionMetadata(existing.description ?? "");
|
|
@@ -146,10 +168,10 @@ export class VMBuilder extends BaseBuilder {
|
|
|
146
168
|
}
|
|
147
169
|
}
|
|
148
170
|
else {
|
|
149
|
-
console.log(` 🔄 Running ${playbooksToRun.length} playbook changes → ${activeIp}`);
|
|
150
171
|
if (activeIp === "0.0.0.0") {
|
|
151
172
|
throw new Error(`Failed to resolve IP for existing VM "${this.name}" to run playbooks`);
|
|
152
173
|
}
|
|
174
|
+
console.log(` 🔄 Running ${playbooksToRun.length} playbook changes → ${activeIp}`);
|
|
153
175
|
// Wait for SSH
|
|
154
176
|
await this.waitFor(`SSH on ${activeIp} to be ready`, () => this.checkPort(activeIp, 22), { intervalMs: 10_000, timeoutMs: 300_000 });
|
|
155
177
|
// Execute each playbook
|
|
@@ -171,7 +193,7 @@ export class VMBuilder extends BaseBuilder {
|
|
|
171
193
|
ip: activeIp,
|
|
172
194
|
};
|
|
173
195
|
}
|
|
174
|
-
// No playbook changes
|
|
196
|
+
// No playbook changes
|
|
175
197
|
console.log(`\n🖥️ Finalizing Proxmox VM "${this.name}"...`);
|
|
176
198
|
console.log(` ✅ VM "${this.name}" already exists (vmid=${existing.vmid}, node=${existing.node}, status=${existing.status})`);
|
|
177
199
|
console.log(` ✅ Configuration and playbooks are up to date.`);
|
|
@@ -225,34 +247,7 @@ export class VMBuilder extends BaseBuilder {
|
|
|
225
247
|
: `Create a template whose name contains "${sourceVmid}".`));
|
|
226
248
|
}
|
|
227
249
|
// Resolve target node: explicit → cluster-aware (online & max free RAM) → configured nodes list → template's node → API discovery
|
|
228
|
-
let node = this._node;
|
|
229
|
-
if (!node) {
|
|
230
|
-
try {
|
|
231
|
-
const nodesList = await pm.get("/nodes");
|
|
232
|
-
const configuredNodes = Config.get().providers.proxmox?.nodes;
|
|
233
|
-
const onlineNodes = (nodesList ?? []).filter((n) => {
|
|
234
|
-
if (n.status !== "online")
|
|
235
|
-
return false;
|
|
236
|
-
if (configuredNodes && configuredNodes.length > 0) {
|
|
237
|
-
return configuredNodes.includes(n.node);
|
|
238
|
-
}
|
|
239
|
-
return true;
|
|
240
|
-
});
|
|
241
|
-
if (onlineNodes.length > 0) {
|
|
242
|
-
// Sort descending by free memory (maxmem - mem)
|
|
243
|
-
onlineNodes.sort((a, b) => {
|
|
244
|
-
const freeA = (a.maxmem ?? 0) - (a.mem ?? 0);
|
|
245
|
-
const freeB = (b.maxmem ?? 0) - (b.mem ?? 0);
|
|
246
|
-
return freeB - freeA;
|
|
247
|
-
});
|
|
248
|
-
node = onlineNodes[0].node;
|
|
249
|
-
console.log(` 🧠 Cluster-aware node selection: picked "${node}" with the most free RAM (${Math.round((((onlineNodes[0].maxmem ?? 0) - (onlineNodes[0].mem ?? 0)) / 1024 / 1024 / 1024) * 10) / 10} GB free)`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
catch (err) {
|
|
253
|
-
// Fallback silently to configured nodes list or discovery
|
|
254
|
-
}
|
|
255
|
-
}
|
|
250
|
+
let node = this._node ?? await this.selectBestNode(pm);
|
|
256
251
|
if (!node) {
|
|
257
252
|
const configuredNodes = Config.get().providers.proxmox?.nodes;
|
|
258
253
|
node = configuredNodes?.[0] ?? template?.node;
|
|
@@ -263,31 +258,38 @@ export class VMBuilder extends BaseBuilder {
|
|
|
263
258
|
}
|
|
264
259
|
if (!node)
|
|
265
260
|
throw new Error("No Proxmox nodes available");
|
|
266
|
-
const newVmid = await pm.get("/cluster/nextid");
|
|
267
261
|
const storage = this._storage ?? "rbd_pool";
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
262
|
+
// Allocate VMID and immediately issue the create/clone request while holding the lock
|
|
263
|
+
// to prevent parallel VMs from claiming the same VMID.
|
|
264
|
+
const { newVmid, cloneTaskId, cloneNode } = await withVmidAllocation(async () => {
|
|
265
|
+
const vmid = await pm.get("/cluster/nextid");
|
|
266
|
+
if (template) {
|
|
267
|
+
console.log(` 📋 Cloning template "${template.name}" (vmid=${template.vmid}) → "${this.name}" (vmid=${vmid})`);
|
|
268
|
+
const taskId = await pm.post(`/nodes/${template.node || node}/qemu/${template.vmid}/clone`, {
|
|
269
|
+
newid: vmid,
|
|
270
|
+
name: this.name,
|
|
271
|
+
full: 1,
|
|
272
|
+
storage,
|
|
273
|
+
format: "raw",
|
|
274
|
+
target: node,
|
|
275
|
+
});
|
|
276
|
+
return { newVmid: vmid, cloneTaskId: taskId, cloneNode: template.node || node };
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console.log(` 🆕 Creating blank VM "${this.name}" (vmid=${vmid})`);
|
|
280
|
+
await pm.post(`/nodes/${node}/qemu`, {
|
|
281
|
+
vmid,
|
|
282
|
+
name: this.name,
|
|
283
|
+
cores: this._cores,
|
|
284
|
+
memory: this._memory,
|
|
285
|
+
net0: `virtio,bridge=vmbr1${this._vlan ? `,tag=${this._vlan}` : ""}`,
|
|
286
|
+
ostype: "l26",
|
|
287
|
+
});
|
|
288
|
+
return { newVmid: vmid, cloneTaskId: null, cloneNode: null };
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
if (cloneTaskId && cloneNode) {
|
|
292
|
+
await this.waitForTask(cloneNode, cloneTaskId, pm);
|
|
291
293
|
}
|
|
292
294
|
this.resolvedVmid = newVmid;
|
|
293
295
|
this.resolvedNode = node;
|
|
@@ -318,7 +320,7 @@ export class VMBuilder extends BaseBuilder {
|
|
|
318
320
|
ipconfig0: this._ip
|
|
319
321
|
? (() => {
|
|
320
322
|
const [addr, prefix = "24"] = this._ip.split("/");
|
|
321
|
-
const gw = addr.split(".").slice(0, 3).join(".") + ".1";
|
|
323
|
+
const gw = this._gateway ?? (addr.split(".").slice(0, 3).join(".") + ".1");
|
|
322
324
|
return `gw=${gw},ip=${addr}/${prefix}`;
|
|
323
325
|
})()
|
|
324
326
|
: "ip=dhcp",
|
|
@@ -356,8 +358,9 @@ export class VMBuilder extends BaseBuilder {
|
|
|
356
358
|
return false;
|
|
357
359
|
}
|
|
358
360
|
}, { intervalMs: 10_000, timeoutMs: 300_000 });
|
|
359
|
-
if (this.resolvedIp)
|
|
361
|
+
if (this.resolvedIp) {
|
|
360
362
|
this.out.ip.resolve(this.resolvedIp);
|
|
363
|
+
}
|
|
361
364
|
console.log(` 🌐 IP: ${this.resolvedIp}`);
|
|
362
365
|
}
|
|
363
366
|
this.registerHost();
|
|
@@ -413,13 +416,12 @@ export class VMBuilder extends BaseBuilder {
|
|
|
413
416
|
}
|
|
414
417
|
registerHost() {
|
|
415
418
|
const context = resourceContextStorage.getStore();
|
|
416
|
-
if (context && context.hosts) {
|
|
417
|
-
const activeIp = this.resolvedIp ?? "0.0.0.0";
|
|
419
|
+
if (context && context.hosts && this.resolvedIp) {
|
|
418
420
|
if (!context.hosts.some((h) => h.name === this.name)) {
|
|
419
421
|
context.hosts.push({
|
|
420
422
|
name: this.name,
|
|
421
|
-
ip:
|
|
422
|
-
user:
|
|
423
|
+
ip: this.resolvedIp,
|
|
424
|
+
user: this.resolveUser(),
|
|
423
425
|
sshKey: this.sshKeyPath(),
|
|
424
426
|
provider: "proxmox",
|
|
425
427
|
});
|
|
@@ -442,19 +444,6 @@ export class VMBuilder extends BaseBuilder {
|
|
|
442
444
|
await this.destroyVmByName(this.name, pm);
|
|
443
445
|
return { destroyed: this.name };
|
|
444
446
|
}
|
|
445
|
-
// Poll a Proxmox task UPID until it exits, then throw if it failed
|
|
446
|
-
async waitForTask(node, upid, pm) {
|
|
447
|
-
const encoded = encodeURIComponent(upid);
|
|
448
|
-
await this.waitFor(`clone task to complete`, async () => {
|
|
449
|
-
const status = await pm.get(`/nodes/${node}/tasks/${encoded}/status`);
|
|
450
|
-
if (status?.status !== "stopped")
|
|
451
|
-
return false;
|
|
452
|
-
if (status.exitstatus && status.exitstatus !== "OK") {
|
|
453
|
-
throw new Error(`Clone task failed: ${status.exitstatus}`);
|
|
454
|
-
}
|
|
455
|
-
return true;
|
|
456
|
-
}, { intervalMs: 5_000, timeoutMs: 300_000 });
|
|
457
|
-
}
|
|
458
447
|
async destroyVmByName(name, pm) {
|
|
459
448
|
const resources = await pm.get("/cluster/resources?type=vm");
|
|
460
449
|
const vm = (resources ?? []).find((r) => r.name === name && !r.template);
|
|
@@ -472,72 +461,4 @@ export class VMBuilder extends BaseBuilder {
|
|
|
472
461
|
await pm.delete(`/nodes/${vm.node}/qemu/${vm.vmid}?purge=1&destroy-unreferenced-disks=1`);
|
|
473
462
|
console.log(` 🗑️ Removed VM "${name}" (vmid=${vm.vmid})`);
|
|
474
463
|
}
|
|
475
|
-
resolvePublicKeys() {
|
|
476
|
-
const input = this._sshKeys;
|
|
477
|
-
if (!input) {
|
|
478
|
-
// Default: read ~/.ssh/id_rsa.pub if it exists
|
|
479
|
-
try {
|
|
480
|
-
return [
|
|
481
|
-
readFileSync(`${homedir()}/.ssh/id_ed25519.pub`, "utf-8").trim(),
|
|
482
|
-
];
|
|
483
|
-
}
|
|
484
|
-
catch {
|
|
485
|
-
return [];
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (Array.isArray(input))
|
|
489
|
-
return input.map((k) => k.trim()).filter(Boolean);
|
|
490
|
-
// Single string: key literal (starts with ssh-) or file path
|
|
491
|
-
if (input.startsWith("ssh-") ||
|
|
492
|
-
input.startsWith("ecdsa-") ||
|
|
493
|
-
input.startsWith("sk-")) {
|
|
494
|
-
return [input.trim()];
|
|
495
|
-
}
|
|
496
|
-
try {
|
|
497
|
-
return [
|
|
498
|
-
readFileSync(input.replace(/^~/, homedir()), "utf-8").trim(),
|
|
499
|
-
];
|
|
500
|
-
}
|
|
501
|
-
catch {
|
|
502
|
-
return [];
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
checkCloudInit(ip) {
|
|
506
|
-
const keyPath = this.sshKeyPath();
|
|
507
|
-
return new Promise((resolve) => {
|
|
508
|
-
const proc = spawn("ssh", [
|
|
509
|
-
"-i",
|
|
510
|
-
keyPath,
|
|
511
|
-
"-o",
|
|
512
|
-
"StrictHostKeyChecking=no",
|
|
513
|
-
"-o",
|
|
514
|
-
"ConnectTimeout=10",
|
|
515
|
-
"-o",
|
|
516
|
-
"BatchMode=yes",
|
|
517
|
-
`root@${ip}`,
|
|
518
|
-
"cloud-init status",
|
|
519
|
-
], { stdio: ["ignore", "pipe", "ignore"] });
|
|
520
|
-
let out = "";
|
|
521
|
-
proc.stdout.on("data", (d) => (out += d.toString()));
|
|
522
|
-
proc.on("close", () => resolve(out.includes("done") || out.includes("error")));
|
|
523
|
-
proc.on("error", () => resolve(false));
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
async checkPort(ip, port) {
|
|
527
|
-
return checkPort(ip, port);
|
|
528
|
-
}
|
|
529
|
-
async runProvisioner(ip, script) {
|
|
530
|
-
return runProvisioner(ip, "root", this._sshKeys, script);
|
|
531
|
-
}
|
|
532
|
-
sshKeyPath() {
|
|
533
|
-
const keyInput = Array.isArray(this._sshKeys)
|
|
534
|
-
? null
|
|
535
|
-
: this._sshKeys;
|
|
536
|
-
return (keyInput &&
|
|
537
|
-
!keyInput.startsWith("ssh-") &&
|
|
538
|
-
!keyInput.startsWith("ecdsa-") &&
|
|
539
|
-
!keyInput.startsWith("sk-")
|
|
540
|
-
? keyInput.replace(/\.pub$/, "")
|
|
541
|
-
: `${homedir()}/.ssh/id_ed25519`).replace(/^~/, homedir());
|
|
542
|
-
}
|
|
543
464
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface FieldDiff {
|
|
2
|
+
field: string;
|
|
3
|
+
declared: any;
|
|
4
|
+
live: any;
|
|
5
|
+
}
|
|
6
|
+
export type ResourceStatus = "missing" | "in-sync" | "drift" | "adopted";
|
|
7
|
+
export interface ResourceDiff {
|
|
8
|
+
prop: string;
|
|
9
|
+
resource: string;
|
|
10
|
+
status: ResourceStatus;
|
|
11
|
+
changes: FieldDiff[];
|
|
12
|
+
}
|
|
13
|
+
export interface StackDiff {
|
|
14
|
+
stackName: string;
|
|
15
|
+
resources: ResourceDiff[];
|
|
16
|
+
hasDrift: boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,8 +6,14 @@ export interface ProxmoxVm {
|
|
|
6
6
|
maxmem: number;
|
|
7
7
|
maxdisk: number;
|
|
8
8
|
}
|
|
9
|
+
export interface ProxmoxTemplate {
|
|
10
|
+
name: string;
|
|
11
|
+
vmid: number;
|
|
12
|
+
node: string;
|
|
13
|
+
}
|
|
9
14
|
export interface ProxmoxInventory {
|
|
10
15
|
vms: ProxmoxVm[];
|
|
16
|
+
templates: ProxmoxTemplate[];
|
|
11
17
|
}
|
|
12
18
|
export interface DoDroplet {
|
|
13
19
|
id: number;
|
|
@@ -34,11 +40,34 @@ export interface DoDomain {
|
|
|
34
40
|
name: string;
|
|
35
41
|
ttl: number;
|
|
36
42
|
}
|
|
43
|
+
export interface DoDatabase {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
engine: string;
|
|
47
|
+
region: string;
|
|
48
|
+
status: string;
|
|
49
|
+
nodeCount: number;
|
|
50
|
+
}
|
|
51
|
+
export interface DoApp {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
liveUrl: string;
|
|
55
|
+
status: string;
|
|
56
|
+
}
|
|
57
|
+
export interface DoVpc {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
region: string;
|
|
61
|
+
ipRange: string;
|
|
62
|
+
}
|
|
37
63
|
export interface DoInventory {
|
|
38
64
|
droplets: DoDroplet[];
|
|
39
65
|
firewalls: DoFirewall[];
|
|
40
66
|
loadBalancers: DoLoadBalancer[];
|
|
41
67
|
domains: DoDomain[];
|
|
68
|
+
databases: DoDatabase[];
|
|
69
|
+
apps: DoApp[];
|
|
70
|
+
vpcs: DoVpc[];
|
|
42
71
|
totalMonthlyCost: number;
|
|
43
72
|
}
|
|
44
73
|
export interface AwsDistribution {
|
|
@@ -67,6 +96,13 @@ export interface AwsHostedZone {
|
|
|
67
96
|
id: string;
|
|
68
97
|
recordCount: number;
|
|
69
98
|
}
|
|
99
|
+
export interface AwsEc2Instance {
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
type: string;
|
|
103
|
+
state: string;
|
|
104
|
+
publicIp?: string;
|
|
105
|
+
}
|
|
70
106
|
export interface AwsInventory {
|
|
71
107
|
region: string;
|
|
72
108
|
distributions: AwsDistribution[];
|
|
@@ -74,6 +110,7 @@ export interface AwsInventory {
|
|
|
74
110
|
lambdas: AwsLambdaFn[];
|
|
75
111
|
rdsInstances: AwsRdsInstance[];
|
|
76
112
|
hostedZones: AwsHostedZone[];
|
|
113
|
+
ec2Instances: AwsEc2Instance[];
|
|
77
114
|
}
|
|
78
115
|
export interface GcpVM {
|
|
79
116
|
name: string;
|
|
@@ -97,11 +134,19 @@ export interface GcpCloudDNS {
|
|
|
97
134
|
name: string;
|
|
98
135
|
dnsName: string;
|
|
99
136
|
}
|
|
137
|
+
export interface GcpPubSubTopic {
|
|
138
|
+
name: string;
|
|
139
|
+
}
|
|
140
|
+
export interface GcpSecret {
|
|
141
|
+
name: string;
|
|
142
|
+
}
|
|
100
143
|
export interface GcpInventory {
|
|
101
144
|
vms: GcpVM[];
|
|
102
145
|
rdsInstances: GcpCloudSQL[];
|
|
103
146
|
distributions: GcpCloudRun[];
|
|
104
147
|
hostedZones: GcpCloudDNS[];
|
|
148
|
+
pubSubTopics: GcpPubSubTopic[];
|
|
149
|
+
secrets: GcpSecret[];
|
|
105
150
|
}
|
|
106
151
|
export interface FirebaseHosting {
|
|
107
152
|
site: string;
|
|
@@ -112,9 +157,29 @@ export interface FirebaseFunction {
|
|
|
112
157
|
entryPoint: string;
|
|
113
158
|
runtime: string;
|
|
114
159
|
}
|
|
160
|
+
export interface FirebaseFirestoreDb {
|
|
161
|
+
name: string;
|
|
162
|
+
type: string;
|
|
163
|
+
state: string;
|
|
164
|
+
}
|
|
165
|
+
export interface FirebaseStorageBucket {
|
|
166
|
+
name: string;
|
|
167
|
+
location: string;
|
|
168
|
+
}
|
|
169
|
+
export interface FirebaseAuthProvider {
|
|
170
|
+
providerId: string;
|
|
171
|
+
}
|
|
172
|
+
export interface FirebaseRemoteConfig {
|
|
173
|
+
parameterCount: number;
|
|
174
|
+
version: string;
|
|
175
|
+
}
|
|
115
176
|
export interface FirebaseInventory {
|
|
116
177
|
hostingSites: FirebaseHosting[];
|
|
117
178
|
functions: FirebaseFunction[];
|
|
179
|
+
firestoreDbs: FirebaseFirestoreDb[];
|
|
180
|
+
storageBuckets: FirebaseStorageBucket[];
|
|
181
|
+
authProviders: FirebaseAuthProvider[];
|
|
182
|
+
remoteConfig?: FirebaseRemoteConfig;
|
|
118
183
|
}
|
|
119
184
|
export interface InventoryError {
|
|
120
185
|
provider: "proxmox" | "do" | "aws" | "gcp" | "firebase";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "puls-dev",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Intent-driven infrastructure-as-code with eager discovery and no state files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,8 +37,12 @@
|
|
|
37
37
|
"README.md",
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
|
+
"bin": {
|
|
41
|
+
"puls": "dist/bin/puls.js"
|
|
42
|
+
},
|
|
40
43
|
"scripts": {
|
|
41
44
|
"build": "tsc",
|
|
45
|
+
"postbuild": "node -e \"const fs=require('fs'),f='dist/bin/puls.js',c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);\" && chmod +x dist/bin/puls.js",
|
|
42
46
|
"prepublishOnly": "npm run build",
|
|
43
47
|
"test": "tsx --test \"src/**/*.test.ts\""
|
|
44
48
|
},
|
|
@@ -54,29 +58,10 @@
|
|
|
54
58
|
"author": "Bia",
|
|
55
59
|
"license": "ISC",
|
|
56
60
|
"devDependencies": {
|
|
57
|
-
"@aws-sdk/client-acm": "^3.1053.0",
|
|
58
|
-
"@aws-sdk/client-apigatewayv2": "^3.1053.0",
|
|
59
|
-
"@aws-sdk/client-cloudfront": "^3.1053.0",
|
|
60
|
-
"@aws-sdk/client-cloudwatch": "^3.1053.0",
|
|
61
|
-
"@aws-sdk/client-cloudwatch-logs": "^3.1053.0",
|
|
62
|
-
"@aws-sdk/client-ec2": "^3.1053.0",
|
|
63
|
-
"@aws-sdk/client-ecs": "^3.1053.0",
|
|
64
|
-
"@aws-sdk/client-iam": "^3.1053.0",
|
|
65
|
-
"@aws-sdk/client-lambda": "^3.1053.0",
|
|
66
|
-
"@aws-sdk/client-rds": "^3.1053.0",
|
|
67
|
-
"@aws-sdk/client-route-53": "^3.1053.0",
|
|
68
|
-
"@aws-sdk/client-route-53-domains": "^3.1053.0",
|
|
69
|
-
"@aws-sdk/client-s3": "^3.1053.0",
|
|
70
|
-
"@aws-sdk/client-secrets-manager": "^3.1053.0",
|
|
71
|
-
"@aws-sdk/client-sns": "^3.1053.0",
|
|
72
|
-
"@aws-sdk/client-sqs": "^3.1053.0",
|
|
73
|
-
"@aws-sdk/client-ssm": "^3.1053.0",
|
|
74
61
|
"@types/node": "^25.6.2",
|
|
75
|
-
"google-auth-library": "^10.6.2",
|
|
76
62
|
"ts-node": "^10.9.2",
|
|
77
|
-
"tsx": "^4.
|
|
78
|
-
"typescript": "^6.0.3"
|
|
79
|
-
"undici": "^8.3.0"
|
|
63
|
+
"tsx": "^4.22.4",
|
|
64
|
+
"typescript": "^6.0.3"
|
|
80
65
|
},
|
|
81
66
|
"dependencies": {
|
|
82
67
|
"@aws-sdk/client-acm": "^3.1053.0",
|