@celilo/cli 0.5.0-alpha.11 → 0.5.0-alpha.12
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/package.json
CHANGED
|
@@ -23,14 +23,29 @@ interface TerraformState {
|
|
|
23
23
|
instances?: Array<{
|
|
24
24
|
attributes?: {
|
|
25
25
|
vmid?: number;
|
|
26
|
+
// proxmox_lxc carries the IP here ("10.0.10.12/24" or "dhcp").
|
|
26
27
|
network?: Array<{
|
|
27
28
|
ip?: string;
|
|
28
29
|
}>;
|
|
30
|
+
// proxmox_vm_qemu carries the cloud-init IP here ("ip=10.0.10.12/24,gw=…").
|
|
31
|
+
ipconfig0?: string;
|
|
29
32
|
};
|
|
30
33
|
}>;
|
|
31
34
|
}>;
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Extract the CIDR from a qemu cloud-init `ipconfig0` string
|
|
39
|
+
* ("ip=10.0.10.12/24,gw=10.0.10.1" → "10.0.10.12/24"). Returns undefined for a
|
|
40
|
+
* DHCP config or anything without an explicit `ip=`.
|
|
41
|
+
*/
|
|
42
|
+
function extractIpFromIpconfig(ipconfig0: string | undefined): string | undefined {
|
|
43
|
+
if (!ipconfig0) return undefined;
|
|
44
|
+
const match = ipconfig0.match(/(?:^|,)ip=([^,]+)/);
|
|
45
|
+
const value = match?.[1];
|
|
46
|
+
return value && value !== 'dhcp' ? value : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
34
49
|
/**
|
|
35
50
|
* Ensure module_configs has vmid and target_ip from Terraform state
|
|
36
51
|
* This recovers from state drift scenarios where container was deleted/recreated
|
|
@@ -82,19 +97,23 @@ export async function ensureProxmoxConfigFromState(
|
|
|
82
97
|
);
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
// Find proxmox_lxc
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
// Find the provisioned guest: an LXC (proxmox_lxc) or a VM (proxmox_vm_qemu).
|
|
101
|
+
const guest = state.resources?.find(
|
|
102
|
+
(r) => r.type === 'proxmox_lxc' || r.type === 'proxmox_vm_qemu',
|
|
103
|
+
);
|
|
104
|
+
if (!guest || !guest.instances || guest.instances.length === 0) {
|
|
105
|
+
throw new Error('No proxmox_lxc or proxmox_vm_qemu resource found in Terraform state');
|
|
89
106
|
}
|
|
90
107
|
|
|
91
|
-
const attributes =
|
|
108
|
+
const attributes = guest.instances[0].attributes;
|
|
92
109
|
if (!attributes) {
|
|
93
|
-
throw new Error(
|
|
110
|
+
throw new Error(`No attributes found in ${guest.type} resource`);
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
const vmid = attributes.vmid;
|
|
97
|
-
|
|
114
|
+
// LXC exposes the IP at network[0].ip; a qemu VM exposes it via the cloud-init
|
|
115
|
+
// ipconfig0 ("ip=<cidr>,gw=<gw>"). Use whichever the guest type provides.
|
|
116
|
+
const containerIp = attributes.network?.[0]?.ip ?? extractIpFromIpconfig(attributes.ipconfig0);
|
|
98
117
|
|
|
99
118
|
if (!vmid) {
|
|
100
119
|
throw new Error('vmid not found in Terraform state');
|
|
@@ -790,7 +790,28 @@ describe("targetNodeFromTfState (ISS-0090 — terraform state is celilo's placem
|
|
|
790
790
|
expect(targetNodeFromTfState(state)).toBe('node3');
|
|
791
791
|
});
|
|
792
792
|
|
|
793
|
-
test('
|
|
793
|
+
test('reads the node from a proxmox_vm_qemu target_node attribute (type:vm)', () => {
|
|
794
|
+
const state = {
|
|
795
|
+
resources: [
|
|
796
|
+
{
|
|
797
|
+
type: 'proxmox_vm_qemu',
|
|
798
|
+
instances: [{ attributes: { target_node: 'node3', id: 'node3/qemu/208' } }],
|
|
799
|
+
},
|
|
800
|
+
],
|
|
801
|
+
};
|
|
802
|
+
expect(targetNodeFromTfState(state)).toBe('node3');
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
test('parses the qemu resource id when target_node is absent (type:vm)', () => {
|
|
806
|
+
const state = {
|
|
807
|
+
resources: [
|
|
808
|
+
{ type: 'proxmox_vm_qemu', instances: [{ attributes: { id: 'node3/qemu/208' } }] },
|
|
809
|
+
],
|
|
810
|
+
};
|
|
811
|
+
expect(targetNodeFromTfState(state)).toBe('node3');
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
test('returns null when there is no proxmox_lxc/proxmox_vm_qemu resource (fresh/empty state)', () => {
|
|
794
815
|
expect(targetNodeFromTfState({ resources: [] })).toBeNull();
|
|
795
816
|
expect(targetNodeFromTfState({})).toBeNull();
|
|
796
817
|
});
|
|
@@ -216,10 +216,11 @@ export function injectProxmoxDns(content: string, hasNameserver: boolean): strin
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/**
|
|
219
|
-
* Extract the node a
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* resource id (`<node>/lxc/<vmid>`
|
|
219
|
+
* Extract the node a Proxmox guest is deployed on from a parsed terraform state
|
|
220
|
+
* object — an LXC (proxmox_lxc) or a VM (proxmox_vm_qemu). Pure logic (Rule 10),
|
|
221
|
+
* split from the file read for testability. Prefers the explicit `target_node`
|
|
222
|
+
* attribute; falls back to parsing the resource id (`<node>/lxc/<vmid>` for an
|
|
223
|
+
* LXC, `<node>/qemu/<vmid>` for a VM). Returns null when there's no such
|
|
223
224
|
* resource (e.g. an empty/fresh state).
|
|
224
225
|
*/
|
|
225
226
|
export function targetNodeFromTfState(state: {
|
|
@@ -228,15 +229,17 @@ export function targetNodeFromTfState(state: {
|
|
|
228
229
|
instances?: Array<{ attributes?: { target_node?: string; id?: string } }>;
|
|
229
230
|
}>;
|
|
230
231
|
}): string | null {
|
|
231
|
-
const
|
|
232
|
-
|
|
232
|
+
const guest = state.resources?.find(
|
|
233
|
+
(r) => r.type === 'proxmox_lxc' || r.type === 'proxmox_vm_qemu',
|
|
234
|
+
);
|
|
235
|
+
const attrs = guest?.instances?.[0]?.attributes;
|
|
233
236
|
if (!attrs) {
|
|
234
237
|
return null;
|
|
235
238
|
}
|
|
236
239
|
if (attrs.target_node) {
|
|
237
240
|
return attrs.target_node;
|
|
238
241
|
}
|
|
239
|
-
// id format: "<node>/lxc/<vmid>"
|
|
242
|
+
// id format: "<node>/lxc/<vmid>" (LXC) or "<node>/qemu/<vmid>" (VM)
|
|
240
243
|
return attrs.id?.split('/')[0] || null;
|
|
241
244
|
}
|
|
242
245
|
|