underpost 2.8.78 → 2.8.82
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/.github/workflows/ghpkg.yml +23 -21
- package/.github/workflows/npmpkg.yml +16 -11
- package/.github/workflows/pwa-microservices-template.page.yml +12 -3
- package/.github/workflows/pwa-microservices-template.test.yml +20 -17
- package/.vscode/extensions.json +1 -2
- package/.vscode/settings.json +3 -0
- package/Dockerfile +14 -33
- package/README.md +25 -24
- package/bin/db.js +1 -0
- package/bin/deploy.js +91 -796
- package/bin/vs.js +10 -3
- package/cli.md +340 -191
- package/conf.js +4 -0
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +167 -0
- package/manifests/deployment/dd-template-development/proxy.yaml +46 -0
- package/manifests/lxd/lxd-admin-profile.yaml +17 -0
- package/manifests/lxd/lxd-preseed.yaml +30 -0
- package/manifests/lxd/underpost-setup.sh +163 -0
- package/manifests/maas/device-scan.sh +43 -0
- package/manifests/maas/lxd-preseed.yaml +32 -0
- package/manifests/maas/maas-setup.sh +120 -0
- package/manifests/maas/nat-iptables.sh +26 -0
- package/manifests/mariadb/statefulset.yaml +2 -1
- package/manifests/mariadb/storage-class.yaml +10 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +2 -2
- package/manifests/valkey/service.yaml +3 -9
- package/manifests/valkey/statefulset.yaml +10 -12
- package/package.json +1 -1
- package/src/cli/baremetal.js +1248 -0
- package/src/cli/cloud-init.js +528 -0
- package/src/cli/cluster.js +459 -232
- package/src/cli/deploy.js +34 -10
- package/src/cli/env.js +2 -2
- package/src/cli/image.js +57 -9
- package/src/cli/index.js +256 -218
- package/src/cli/lxd.js +380 -4
- package/src/index.js +40 -14
- package/src/runtime/lampp/Dockerfile +41 -47
- package/src/server/conf.js +58 -0
- package/src/server/logger.js +3 -3
- package/src/server/runtime.js +1 -6
- package/src/server/ssl.js +1 -12
- package/src/server/valkey.js +3 -3
- package/supervisord-openssh-server.conf +0 -5
package/src/cli/lxd.js
CHANGED
|
@@ -1,16 +1,392 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getNpmRootPath } from '../server/conf.js';
|
|
2
|
+
import { getLocalIPv4Address } from '../server/dns.js';
|
|
3
|
+
import { pbcopy, shellExec } from '../server/process.js';
|
|
4
|
+
import fs from 'fs-extra';
|
|
2
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @class UnderpostLxd
|
|
8
|
+
* @description Provides a set of static methods to interact with LXD,
|
|
9
|
+
* encapsulating common LXD operations for VM management and network testing.
|
|
10
|
+
*/
|
|
3
11
|
class UnderpostLxd {
|
|
4
12
|
static API = {
|
|
5
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @method callback
|
|
15
|
+
* @description Main entry point for LXD operations based on provided options.
|
|
16
|
+
* @param {object} options - Configuration options for LXD operations.
|
|
17
|
+
* @param {boolean} [options.init=false] - Initialize LXD.
|
|
18
|
+
* @param {boolean} [options.reset=false] - Reset LXD installation.
|
|
19
|
+
* @param {boolean} [options.dev=false] - Run in development mode (adjusts paths).
|
|
20
|
+
* @param {boolean} [options.install=false] - Install LXD snap.
|
|
21
|
+
* @param {boolean} [options.createVirtualNetwork=false] - Create default LXD bridge network (lxdbr0).
|
|
22
|
+
* @param {boolean} [options.createAdminProfile=false] - Create admin-profile for VMs.
|
|
23
|
+
* @param {boolean} [options.control=false] - Flag for control plane VM initialization.
|
|
24
|
+
* @param {boolean} [options.worker=false] - Flag for worker node VM initialization.
|
|
25
|
+
* @param {boolean} [options.k3s=false] - Flag to indicate K3s cluster type for VM initialization.
|
|
26
|
+
* @param {string} [options.initVm=''] - Initialize a specific VM.
|
|
27
|
+
* @param {string} [options.createVm=''] - Create a new VM with the given name.
|
|
28
|
+
* @param {string} [options.infoVm=''] - Display information about a specific VM.
|
|
29
|
+
* @param {string} [options.rootSize=''] - Root disk size for new VMs (e.g., '32GiB').
|
|
30
|
+
* @param {string} [options.joinNode=''] - Join a worker node to a control plane (format: 'workerName,controlName').
|
|
31
|
+
* @param {string} [options.expose=''] - Expose ports from a VM to the host (format: 'vmName:port1,port2').
|
|
32
|
+
* @param {string} [options.deleteExpose=''] - Delete exposed ports from a VM (format: 'vmName:port1,port2').
|
|
33
|
+
* @param {string} [options.test=''] - Test health, status and network connectivity for a VM.
|
|
34
|
+
* @param {string} [options.autoExposeK8sPorts=''] - Automatically expose common Kubernetes ports for the VM.
|
|
35
|
+
*/
|
|
36
|
+
async callback(
|
|
37
|
+
options = {
|
|
38
|
+
init: false,
|
|
39
|
+
reset: false,
|
|
40
|
+
dev: false,
|
|
41
|
+
install: false,
|
|
42
|
+
createVirtualNetwork: false,
|
|
43
|
+
createAdminProfile: false,
|
|
44
|
+
control: false,
|
|
45
|
+
worker: false,
|
|
46
|
+
k3s: false,
|
|
47
|
+
initVm: '',
|
|
48
|
+
createVm: '',
|
|
49
|
+
infoVm: '',
|
|
50
|
+
rootSize: '',
|
|
51
|
+
joinNode: '',
|
|
52
|
+
expose: '',
|
|
53
|
+
deleteExpose: '',
|
|
54
|
+
test: '',
|
|
55
|
+
autoExposeK8sPorts: '',
|
|
56
|
+
},
|
|
57
|
+
) {
|
|
58
|
+
const npmRoot = getNpmRootPath();
|
|
59
|
+
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
6
60
|
if (options.reset === true) {
|
|
7
|
-
shellExec(`sudo systemctl stop snap.lxd.daemon`);
|
|
8
|
-
shellExec(`sudo snap remove lxd --purge`);
|
|
61
|
+
shellExec(`sudo systemctl stop snap.lxd.daemon || true`);
|
|
62
|
+
shellExec(`sudo snap remove lxd --purge || true`);
|
|
9
63
|
}
|
|
10
64
|
if (options.install === true) shellExec(`sudo snap install lxd`);
|
|
11
65
|
if (options.init === true) {
|
|
12
66
|
shellExec(`sudo systemctl start snap.lxd.daemon`);
|
|
13
67
|
shellExec(`sudo systemctl status snap.lxd.daemon`);
|
|
68
|
+
const lxdPressedContent = fs
|
|
69
|
+
.readFileSync(`${underpostRoot}/manifests/lxd/lxd-preseed.yaml`, 'utf8')
|
|
70
|
+
.replaceAll(`127.0.0.1`, getLocalIPv4Address());
|
|
71
|
+
shellExec(`echo "${lxdPressedContent}" | lxd init --preseed`);
|
|
72
|
+
shellExec(`lxc cluster list`);
|
|
73
|
+
}
|
|
74
|
+
if (options.createVirtualNetwork === true) {
|
|
75
|
+
shellExec(`lxc network create lxdbr0 \
|
|
76
|
+
ipv4.address=10.250.250.1/24 \
|
|
77
|
+
ipv4.nat=true \
|
|
78
|
+
ipv4.dhcp=true \
|
|
79
|
+
ipv6.address=none`);
|
|
80
|
+
}
|
|
81
|
+
if (options.createAdminProfile === true) {
|
|
82
|
+
pbcopy(`lxc profile create admin-profile`);
|
|
83
|
+
shellExec(`cat ${underpostRoot}/manifests/lxd/lxd-admin-profile.yaml | lxc profile edit admin-profile`);
|
|
84
|
+
shellExec(`lxc profile show admin-profile`);
|
|
85
|
+
}
|
|
86
|
+
if (options.createVm && typeof options.createVm === 'string') {
|
|
87
|
+
pbcopy(
|
|
88
|
+
`lxc launch images:rockylinux/9 ${
|
|
89
|
+
options.createVm
|
|
90
|
+
} --vm --target lxd-node1 -c limits.cpu=2 -c limits.memory=4GB --profile admin-profile -d root,size=${
|
|
91
|
+
options.rootSize && typeof options.rootSize === 'string' ? options.rootSize + 'GiB' : '32GiB'
|
|
92
|
+
}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (options.initVm && typeof options.initVm === 'string') {
|
|
96
|
+
let flag = '';
|
|
97
|
+
if (options.control === true) {
|
|
98
|
+
if (options.k3s === true) {
|
|
99
|
+
flag = ' -s -- --k3s';
|
|
100
|
+
} else {
|
|
101
|
+
// Default to kubeadm if not K3s
|
|
102
|
+
flag = ' -s -- --kubeadm';
|
|
103
|
+
}
|
|
104
|
+
shellExec(`lxc exec ${options.initVm} -- bash -c 'mkdir -p /home/dd/engine'`);
|
|
105
|
+
shellExec(`lxc file push /home/dd/engine/engine-private ${options.initVm}/home/dd/engine --recursive`);
|
|
106
|
+
shellExec(`lxc file push /home/dd/engine/manifests ${options.initVm}/home/dd/engine --recursive`);
|
|
107
|
+
} else if (options.worker == true) {
|
|
108
|
+
if (options.k3s === true) {
|
|
109
|
+
flag = ' -s -- --worker --k3s';
|
|
110
|
+
} else {
|
|
111
|
+
// Default to kubeadm worker
|
|
112
|
+
flag = ' -s -- --worker';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
console.log(`Executing underpost-setup.sh on VM: ${options.initVm}`);
|
|
116
|
+
shellExec(`cat ${underpostRoot}/manifests/lxd/underpost-setup.sh | lxc exec ${options.initVm} -- bash${flag}`);
|
|
117
|
+
console.log(`underpost-setup.sh execution completed on VM: ${options.initVm}`);
|
|
118
|
+
}
|
|
119
|
+
// --- Automatic Kubernetes Port Exposure ---
|
|
120
|
+
if (options.autoExposeK8sPorts && typeof options.autoExposeK8sPorts === 'string') {
|
|
121
|
+
console.log(`Automatically exposing Kubernetes ports for VM: ${options.autoExposeK8sPorts}`);
|
|
122
|
+
const vmName = options.autoExposeK8sPorts;
|
|
123
|
+
const hostIp = getLocalIPv4Address();
|
|
124
|
+
let vmIp = '';
|
|
125
|
+
let retries = 0;
|
|
126
|
+
const maxRetries = 10;
|
|
127
|
+
const delayMs = 5000; // 5 seconds
|
|
128
|
+
|
|
129
|
+
// Wait for VM to get an IP address
|
|
130
|
+
while (!vmIp && retries < maxRetries) {
|
|
131
|
+
try {
|
|
132
|
+
console.log(`Attempting to get IPv4 address for ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
|
|
133
|
+
vmIp = shellExec(
|
|
134
|
+
`lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
|
|
135
|
+
{ stdout: true },
|
|
136
|
+
).trim();
|
|
137
|
+
if (vmIp) {
|
|
138
|
+
console.log(`IPv4 address found for ${vmName}: ${vmIp}`);
|
|
139
|
+
} else {
|
|
140
|
+
console.log(`IPv4 address not yet available for ${vmName}. Retrying in ${delayMs / 1000} seconds...`);
|
|
141
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error(`Error getting IPv4 address for exposure: ${error.message}`);
|
|
145
|
+
console.log(`Retrying in ${delayMs / 1000} seconds...`);
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
147
|
+
}
|
|
148
|
+
retries++;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!vmIp) {
|
|
152
|
+
console.error(`Failed to get VM IP for ${vmName} after ${maxRetries} attempts. Cannot expose ports.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let portsToExpose = [];
|
|
157
|
+
if (options.control === true) {
|
|
158
|
+
// Kubernetes API Server (Kubeadm and K3s both use 6443 by default)
|
|
159
|
+
portsToExpose.push('6443');
|
|
160
|
+
// Standard HTTP/HTTPS for Ingress if deployed
|
|
161
|
+
portsToExpose.push('80');
|
|
162
|
+
portsToExpose.push('443');
|
|
163
|
+
}
|
|
164
|
+
// Add common NodePorts if needed, or rely on explicit 'expose'
|
|
165
|
+
portsToExpose.push('30000'); // Example NodePort
|
|
166
|
+
portsToExpose.push('30001'); // Example NodePort
|
|
167
|
+
portsToExpose.push('30002'); // Example NodePort
|
|
168
|
+
|
|
169
|
+
const protocols = ['tcp']; // Most K8s services are TCP, UDP for some like DNS
|
|
170
|
+
|
|
171
|
+
for (const port of portsToExpose) {
|
|
172
|
+
for (const protocol of protocols) {
|
|
173
|
+
const deviceName = `${vmName}-${protocol}-port-${port}`;
|
|
174
|
+
try {
|
|
175
|
+
// Remove existing device first to avoid conflicts if re-running
|
|
176
|
+
shellExec(`lxc config device remove ${vmName} ${deviceName} || true`);
|
|
177
|
+
shellExec(
|
|
178
|
+
`lxc config device add ${vmName} ${deviceName} proxy listen=${protocol}:${hostIp}:${port} connect=${protocol}:${vmIp}:${port} nat=true`,
|
|
179
|
+
);
|
|
180
|
+
console.log(`Exposed ${protocol}:${hostIp}:${port} -> ${vmIp}:${port} for ${vmName}`);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(`Failed to expose port ${port} for ${vmName}: ${error.message}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (options.joinNode && typeof options.joinNode === 'string') {
|
|
188
|
+
const [workerNode, controlNode] = options.joinNode.split(',');
|
|
189
|
+
// Determine if it's a Kubeadm or K3s join
|
|
190
|
+
const isK3sJoin = options.k3s === true;
|
|
191
|
+
|
|
192
|
+
if (isK3sJoin) {
|
|
193
|
+
console.log(`Attempting to join K3s worker node ${workerNode} to control plane ${controlNode}`);
|
|
194
|
+
// Get K3s token from control plane
|
|
195
|
+
const k3sToken = shellExec(
|
|
196
|
+
`lxc exec ${controlNode} -- bash -c 'sudo cat /var/lib/rancher/k3s/server/node-token'`,
|
|
197
|
+
{ stdout: true },
|
|
198
|
+
).trim();
|
|
199
|
+
// Get control plane IP
|
|
200
|
+
const controlPlaneIp = shellExec(
|
|
201
|
+
`lxc list ${controlNode} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
|
|
202
|
+
{ stdout: true },
|
|
203
|
+
).trim();
|
|
204
|
+
|
|
205
|
+
if (!k3sToken || !controlPlaneIp) {
|
|
206
|
+
console.error(`Failed to get K3s token or control plane IP. Cannot join worker.`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const k3sJoinCommand = `K3S_URL=https://${controlPlaneIp}:6443 K3S_TOKEN=${k3sToken} curl -sfL https://get.k3s.io | sh -`;
|
|
210
|
+
shellExec(`lxc exec ${workerNode} -- bash -c '${k3sJoinCommand}'`);
|
|
211
|
+
console.log(`K3s worker node ${workerNode} join command executed.`);
|
|
212
|
+
} else {
|
|
213
|
+
// Kubeadm join
|
|
214
|
+
console.log(`Attempting to join Kubeadm worker node ${workerNode} to control plane ${controlNode}`);
|
|
215
|
+
const token = shellExec(
|
|
216
|
+
`echo "$(lxc exec ${controlNode} -- bash -c 'sudo kubeadm token create --print-join-command')"`,
|
|
217
|
+
{ stdout: true },
|
|
218
|
+
);
|
|
219
|
+
shellExec(`lxc exec ${workerNode} -- bash -c '${token}'`);
|
|
220
|
+
console.log(`Kubeadm worker node ${workerNode} join command executed.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (options.infoVm && typeof options.infoVm === 'string') {
|
|
224
|
+
shellExec(`lxc config show ${options.infoVm}`);
|
|
225
|
+
shellExec(`lxc info --show-log ${options.infoVm}`);
|
|
226
|
+
shellExec(`lxc info ${options.infoVm}`);
|
|
227
|
+
shellExec(`lxc list ${options.infoVm}`);
|
|
228
|
+
}
|
|
229
|
+
if (options.expose && typeof options.expose === 'string') {
|
|
230
|
+
const [vmName, ports] = options.expose.split(':');
|
|
231
|
+
console.log({ vmName, ports });
|
|
232
|
+
const protocols = ['tcp']; // udp
|
|
233
|
+
const hostIp = getLocalIPv4Address();
|
|
234
|
+
const vmIp = shellExec(
|
|
235
|
+
`lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
|
|
236
|
+
{ stdout: true },
|
|
237
|
+
).trim();
|
|
238
|
+
if (!vmIp) {
|
|
239
|
+
console.error(`Could not get VM IP for ${vmName}. Cannot expose ports.`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
for (const port of ports.split(',')) {
|
|
243
|
+
for (const protocol of protocols) {
|
|
244
|
+
const deviceName = `${vmName}-${protocol}-port-${port}`;
|
|
245
|
+
shellExec(`lxc config device remove ${vmName} ${deviceName} || true`); // Use || true to prevent error if device doesn't exist
|
|
246
|
+
shellExec(
|
|
247
|
+
`lxc config device add ${vmName} ${deviceName} proxy listen=${protocol}:${hostIp}:${port} connect=${protocol}:${vmIp}:${port} nat=true`,
|
|
248
|
+
);
|
|
249
|
+
console.log(`Manually exposed ${protocol}:${hostIp}:${port} -> ${vmIp}:${port} for ${vmName}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (options.deleteExpose && typeof options.deleteExpose === 'string') {
|
|
254
|
+
const [controlNode, ports] = options.deleteExpose.split(':');
|
|
255
|
+
console.log({ controlNode, ports });
|
|
256
|
+
const protocols = ['tcp']; // udp
|
|
257
|
+
for (const port of ports.split(',')) {
|
|
258
|
+
for (const protocol of protocols) {
|
|
259
|
+
shellExec(`lxc config device remove ${controlNode} ${controlNode}-${protocol}-port-${port}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (options.test && typeof options.test === 'string') {
|
|
265
|
+
const vmName = options.test;
|
|
266
|
+
console.log(`Starting comprehensive test for VM: ${vmName}`);
|
|
267
|
+
|
|
268
|
+
// 1. Monitor for IPv4 address
|
|
269
|
+
let vmIp = '';
|
|
270
|
+
let retries = 0;
|
|
271
|
+
const maxRetries = 10;
|
|
272
|
+
const delayMs = 5000; // 5 seconds
|
|
273
|
+
|
|
274
|
+
while (!vmIp && retries < maxRetries) {
|
|
275
|
+
try {
|
|
276
|
+
console.log(`Attempting to get IPv4 address for ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
|
|
277
|
+
vmIp = shellExec(
|
|
278
|
+
`lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
|
|
279
|
+
{ stdout: true },
|
|
280
|
+
).trim();
|
|
281
|
+
if (vmIp) {
|
|
282
|
+
console.log(`IPv4 address found for ${vmName}: ${vmIp}`);
|
|
283
|
+
} else {
|
|
284
|
+
console.log(`IPv4 address not yet available for ${vmName}. Retrying in ${delayMs / 1000} seconds...`);
|
|
285
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
286
|
+
}
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(`Error getting IPv4 address: ${error.message}`);
|
|
289
|
+
console.log(`Retrying in ${delayMs / 1000} seconds...`);
|
|
290
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
291
|
+
}
|
|
292
|
+
retries++;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!vmIp) {
|
|
296
|
+
console.error(`Failed to get IPv4 address for ${vmName} after ${maxRetries} attempts. Aborting tests.`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 2. Iteratively check connection to google.com
|
|
301
|
+
let connectedToGoogle = false;
|
|
302
|
+
retries = 0;
|
|
303
|
+
while (!connectedToGoogle && retries < maxRetries) {
|
|
304
|
+
try {
|
|
305
|
+
console.log(`Checking connectivity to google.com from ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
|
|
306
|
+
const curlOutput = shellExec(
|
|
307
|
+
`lxc exec ${vmName} -- bash -c 'curl -s -o /dev/null -w "%{http_code}" http://google.com'`,
|
|
308
|
+
{ stdout: true },
|
|
309
|
+
);
|
|
310
|
+
if (curlOutput.startsWith('2') || curlOutput.startsWith('3')) {
|
|
311
|
+
console.log(`Successfully connected to google.com from ${vmName}.`);
|
|
312
|
+
connectedToGoogle = true;
|
|
313
|
+
} else {
|
|
314
|
+
console.log(`Connectivity to google.com not yet verified. Retrying in ${delayMs / 1000} seconds...`);
|
|
315
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error(`Error checking connectivity to google.com: ${error.message}`);
|
|
319
|
+
console.log(`Retrying in ${delayMs / 1000} seconds...`);
|
|
320
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
321
|
+
}
|
|
322
|
+
retries++;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!connectedToGoogle) {
|
|
326
|
+
console.error(
|
|
327
|
+
`Failed to connect to google.com from ${vmName} after ${maxRetries} attempts. Aborting further tests.`,
|
|
328
|
+
);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 3. Check other connectivity, network, and VM health parameters
|
|
333
|
+
console.log(`\n--- Comprehensive Health Report for ${vmName} ---`);
|
|
334
|
+
|
|
335
|
+
// VM Status
|
|
336
|
+
console.log('\n--- VM Status ---');
|
|
337
|
+
try {
|
|
338
|
+
const vmStatus = shellExec(`lxc list ${vmName} --format json`, { stdout: true, silent: true });
|
|
339
|
+
console.log(JSON.stringify(JSON.parse(vmStatus), null, 2));
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`Error getting VM status: ${error.message}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// CPU Usage
|
|
345
|
+
console.log('\n--- CPU Usage ---');
|
|
346
|
+
try {
|
|
347
|
+
const cpuUsage = shellExec(`lxc exec ${vmName} -- bash -c 'top -bn1 | grep "Cpu(s)"'`, { stdout: true });
|
|
348
|
+
console.log(cpuUsage.trim());
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error(`Error getting CPU usage: ${error.message}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Memory Usage
|
|
354
|
+
console.log('\n--- Memory Usage ---');
|
|
355
|
+
try {
|
|
356
|
+
const memoryUsage = shellExec(`lxc exec ${vmName} -- bash -c 'free -m'`, { stdout: true });
|
|
357
|
+
console.log(memoryUsage.trim());
|
|
358
|
+
} catch (error) {
|
|
359
|
+
console.error(`Error getting memory usage: ${error.message}`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Disk Usage
|
|
363
|
+
console.log('\n--- Disk Usage (Root Partition) ---');
|
|
364
|
+
try {
|
|
365
|
+
const diskUsage = shellExec(`lxc exec ${vmName} -- bash -c 'df -h /'`, { stdout: true });
|
|
366
|
+
console.log(diskUsage.trim());
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error(`Error getting disk usage: ${error.message}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Network Interface Status
|
|
372
|
+
console.log('\n--- Network Interface Status (ip a) ---');
|
|
373
|
+
try {
|
|
374
|
+
const ipA = shellExec(`lxc exec ${vmName} -- bash -c 'ip a'`, { stdout: true });
|
|
375
|
+
console.log(ipA.trim());
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error(`Error getting network interface status: ${error.message}`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// DNS Resolution (resolv.conf)
|
|
381
|
+
console.log('\n--- DNS Configuration (/etc/resolv.conf) ---');
|
|
382
|
+
try {
|
|
383
|
+
const resolvConf = shellExec(`lxc exec ${vmName} -- bash -c 'cat /etc/resolv.conf'`, { stdout: true });
|
|
384
|
+
console.log(resolvConf.trim());
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error(`Error getting DNS configuration: ${error.message}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
console.log(`\nComprehensive test for VM: ${vmName} completed.`);
|
|
14
390
|
}
|
|
15
391
|
},
|
|
16
392
|
};
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @namespace Underpost
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import UnderpostBaremetal from './cli/baremetal.js';
|
|
8
|
+
import UnderpostCloudInit from './cli/cloud-init.js';
|
|
7
9
|
import UnderpostCluster from './cli/cluster.js';
|
|
8
10
|
import UnderpostCron from './cli/cron.js';
|
|
9
11
|
import UnderpostDB from './cli/db.js';
|
|
@@ -11,6 +13,7 @@ import UnderpostDeploy from './cli/deploy.js';
|
|
|
11
13
|
import UnderpostRootEnv from './cli/env.js';
|
|
12
14
|
import UnderpostFileStorage from './cli/fs.js';
|
|
13
15
|
import UnderpostImage from './cli/image.js';
|
|
16
|
+
import UnderpostLxd from './cli/lxd.js';
|
|
14
17
|
import UnderpostMonitor from './cli/monitor.js';
|
|
15
18
|
import UnderpostRepository from './cli/repository.js';
|
|
16
19
|
import UnderpostScript from './cli/script.js';
|
|
@@ -30,98 +33,121 @@ class Underpost {
|
|
|
30
33
|
* @type {String}
|
|
31
34
|
* @memberof Underpost
|
|
32
35
|
*/
|
|
33
|
-
static version = 'v2.8.
|
|
36
|
+
static version = 'v2.8.82';
|
|
34
37
|
/**
|
|
35
38
|
* Repository cli API
|
|
36
39
|
* @static
|
|
37
|
-
* @type {UnderpostRepository
|
|
40
|
+
* @type {UnderpostRepository}
|
|
38
41
|
* @memberof Underpost
|
|
39
42
|
*/
|
|
40
43
|
static repo = UnderpostRepository.API;
|
|
41
44
|
/**
|
|
42
45
|
* Root Env cli API
|
|
43
46
|
* @static
|
|
44
|
-
* @type {UnderpostRootEnv
|
|
47
|
+
* @type {UnderpostRootEnv}
|
|
45
48
|
* @memberof Underpost
|
|
46
49
|
*/
|
|
47
50
|
static env = UnderpostRootEnv.API;
|
|
48
51
|
/**
|
|
49
52
|
* Test cli API
|
|
50
53
|
* @static
|
|
51
|
-
* @type {UnderpostTest
|
|
54
|
+
* @type {UnderpostTest}
|
|
52
55
|
* @memberof Underpost
|
|
53
56
|
*/
|
|
54
57
|
static test = UnderpostTest.API;
|
|
55
58
|
/**
|
|
56
59
|
* Underpost Start Up cli API
|
|
57
60
|
* @static
|
|
58
|
-
* @type {UnderpostStartUp
|
|
61
|
+
* @type {UnderpostStartUp}
|
|
59
62
|
* @memberof Underpost
|
|
60
63
|
*/
|
|
61
64
|
static start = UnderpostStartUp.API;
|
|
62
65
|
/**
|
|
63
66
|
* Cluster cli API
|
|
64
67
|
* @static
|
|
65
|
-
* @type {UnderpostCluster
|
|
68
|
+
* @type {UnderpostCluster}
|
|
66
69
|
* @memberof Underpost
|
|
67
70
|
*/
|
|
68
71
|
static cluster = UnderpostCluster.API;
|
|
69
72
|
/**
|
|
70
73
|
* Image cli API
|
|
71
74
|
* @static
|
|
72
|
-
* @type {UnderpostImage
|
|
75
|
+
* @type {UnderpostImage}
|
|
73
76
|
* @memberof Underpost
|
|
74
77
|
*/
|
|
75
78
|
static image = UnderpostImage.API;
|
|
76
79
|
/**
|
|
77
80
|
* Secrets cli API
|
|
78
81
|
* @static
|
|
79
|
-
* @type {UnderpostSecret
|
|
82
|
+
* @type {UnderpostSecret}
|
|
80
83
|
* @memberof Underpost
|
|
81
84
|
*/
|
|
82
85
|
static secret = UnderpostSecret.API;
|
|
83
86
|
/**
|
|
84
87
|
* Scripts cli API
|
|
85
88
|
* @static
|
|
86
|
-
* @type {UnderpostScript
|
|
89
|
+
* @type {UnderpostScript}
|
|
87
90
|
* @memberof Underpost
|
|
88
91
|
*/
|
|
89
92
|
static script = UnderpostScript.API;
|
|
90
93
|
/**
|
|
91
94
|
* Database cli API
|
|
92
95
|
* @static
|
|
93
|
-
* @type {UnderpostDB
|
|
96
|
+
* @type {UnderpostDB}
|
|
94
97
|
* @memberof Underpost
|
|
95
98
|
*/
|
|
96
99
|
static db = UnderpostDB.API;
|
|
97
100
|
/**
|
|
98
101
|
* Deployment cli API
|
|
99
102
|
* @static
|
|
100
|
-
* @type {UnderpostDeploy
|
|
103
|
+
* @type {UnderpostDeploy}
|
|
101
104
|
* @memberof Underpost
|
|
102
105
|
*/
|
|
103
106
|
static deploy = UnderpostDeploy.API;
|
|
104
107
|
/**
|
|
105
108
|
* Cron cli API
|
|
106
109
|
* @static
|
|
107
|
-
* @type {UnderpostCron
|
|
110
|
+
* @type {UnderpostCron}
|
|
108
111
|
* @memberof Underpost
|
|
109
112
|
*/
|
|
110
113
|
static cron = UnderpostCron.API;
|
|
111
114
|
/**
|
|
112
115
|
* File Storage cli API
|
|
113
116
|
* @static
|
|
114
|
-
* @type {UnderpostFileStorage
|
|
117
|
+
* @type {UnderpostFileStorage}
|
|
115
118
|
* @memberof Underpost
|
|
116
119
|
*/
|
|
117
120
|
static fs = UnderpostFileStorage.API;
|
|
118
121
|
/**
|
|
119
122
|
* Monitor cli API
|
|
120
123
|
* @static
|
|
121
|
-
* @type {UnderpostMonitor
|
|
124
|
+
* @type {UnderpostMonitor}
|
|
122
125
|
* @memberof Underpost
|
|
123
126
|
*/
|
|
124
127
|
static monitor = UnderpostMonitor.API;
|
|
128
|
+
/**
|
|
129
|
+
* LXD cli API
|
|
130
|
+
* @static
|
|
131
|
+
* @type {UnderpostLxd}
|
|
132
|
+
* @memberof Underpost
|
|
133
|
+
*/
|
|
134
|
+
static lxd = UnderpostLxd.API;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Cloud Init cli API
|
|
138
|
+
* @static
|
|
139
|
+
* @type {UnderpostCloudInit}
|
|
140
|
+
* @memberof Underpost
|
|
141
|
+
*/
|
|
142
|
+
static cloudInit = UnderpostCloudInit.API;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Baremetal cli API
|
|
146
|
+
* @static
|
|
147
|
+
* @type {UnderpostBaremetal}
|
|
148
|
+
* @memberof Underpost
|
|
149
|
+
*/
|
|
150
|
+
static baremetal = UnderpostBaremetal.API;
|
|
125
151
|
}
|
|
126
152
|
|
|
127
153
|
const up = Underpost;
|
|
@@ -1,65 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
RUN
|
|
11
|
-
|
|
12
|
-
RUN
|
|
13
|
-
RUN
|
|
14
|
-
|
|
15
|
-
RUN
|
|
16
|
-
|
|
17
|
-
RUN
|
|
18
|
-
RUN
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# install ssh
|
|
24
|
-
RUN mkdir -p /var/run/sshd
|
|
25
|
-
# Allow root login via password
|
|
26
|
-
RUN sed -ri 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
|
27
|
-
|
|
28
|
-
# install open ssl git and others tools
|
|
29
|
-
RUN apt-get install -yq --no-install-recommends libssl-dev curl wget git gnupg
|
|
30
|
-
|
|
31
|
-
# install lampp
|
|
1
|
+
FROM rockylinux:9
|
|
2
|
+
|
|
3
|
+
RUN dnf install -y --allowerasing bzip2
|
|
4
|
+
|
|
5
|
+
RUN dnf update -y
|
|
6
|
+
RUN dnf install -y epel-release
|
|
7
|
+
RUN dnf install -y --allowerasing sudo
|
|
8
|
+
RUN dnf install -y --allowerasing curl
|
|
9
|
+
RUN dnf install -y --allowerasing net-tools
|
|
10
|
+
RUN dnf install -y --allowerasing openssh-server
|
|
11
|
+
RUN dnf install -y --allowerasing nano
|
|
12
|
+
RUN dnf install -y --allowerasing vim-enhanced
|
|
13
|
+
RUN dnf install -y --allowerasing less
|
|
14
|
+
RUN dnf install -y --allowerasing openssl-devel
|
|
15
|
+
RUN dnf install -y --allowerasing wget
|
|
16
|
+
RUN dnf install -y --allowerasing git
|
|
17
|
+
RUN dnf install -y --allowerasing gnupg2
|
|
18
|
+
RUN dnf clean all
|
|
19
|
+
|
|
20
|
+
# Install LAMPP (XAMPP)
|
|
21
|
+
# Download the XAMPP installer for Linux
|
|
32
22
|
RUN curl -Lo xampp-linux-installer.run https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/7.4.33/xampp-linux-x64-7.4.33-0-installer.run?from_af=true
|
|
33
23
|
RUN chmod +x xampp-linux-installer.run
|
|
34
|
-
|
|
24
|
+
# Run the XAMPP installer in silent mode
|
|
25
|
+
RUN bash -c './xampp-linux-installer.run --mode unattended'
|
|
26
|
+
# Create a symbolic link for easy access to lampp command
|
|
35
27
|
RUN ln -sf /opt/lampp/lampp /usr/bin/lampp
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
|
|
29
|
+
# Configure XAMPP
|
|
30
|
+
# Enable XAMPP web interface (remove security checks)
|
|
31
|
+
RUN sed -i.bak 's/Require local/Require all granted/g' /opt/lampp/etc/extra/httpd-xampp.conf
|
|
38
32
|
# Enable error display in php
|
|
39
|
-
RUN sed -i.bak s
|
|
40
|
-
# Enable includes of several configuration files
|
|
41
|
-
RUN mkdir /opt/lampp/apache2/conf.d
|
|
33
|
+
RUN sed -i.bak 's/display_errors=Off/display_errors=On/g' /opt/lampp/etc/php.ini
|
|
34
|
+
# Enable includes of several configuration files for Apache
|
|
35
|
+
RUN mkdir -p /opt/lampp/apache2/conf.d
|
|
42
36
|
RUN echo "IncludeOptional /opt/lampp/apache2/conf.d/*.conf" >>/opt/lampp/etc/httpd.conf
|
|
43
|
-
# Create a /www folder and a symbolic link to it in /opt/lampp/htdocs
|
|
44
|
-
# This is convenient because it doesn't interfere with xampp, phpmyadmin or other tools
|
|
45
|
-
# /opt/lampp/etc/httpd.conf
|
|
37
|
+
# Create a /www folder and a symbolic link to it in /opt/lampp/htdocs
|
|
38
|
+
# This is convenient because it doesn't interfere with xampp, phpmyadmin or other tools
|
|
46
39
|
RUN mkdir /www
|
|
47
40
|
RUN ln -s /www /opt/lampp/htdocs
|
|
48
41
|
|
|
49
|
-
#
|
|
50
|
-
RUN curl -fsSL https://
|
|
51
|
-
RUN
|
|
42
|
+
# Install Node.js
|
|
43
|
+
RUN curl -fsSL https://rpm.nodesource.com/setup_23.x | bash -
|
|
44
|
+
RUN dnf install nodejs -y
|
|
45
|
+
RUN dnf clean all
|
|
46
|
+
|
|
47
|
+
# Verify Node.js and npm versions
|
|
52
48
|
RUN node --version
|
|
53
49
|
RUN npm --version
|
|
54
50
|
|
|
51
|
+
# Set working directory
|
|
55
52
|
WORKDIR /home/dd
|
|
56
53
|
|
|
54
|
+
# Expose necessary ports
|
|
57
55
|
EXPOSE 22
|
|
58
|
-
|
|
59
56
|
EXPOSE 80
|
|
60
|
-
|
|
61
57
|
EXPOSE 443
|
|
62
|
-
|
|
63
58
|
EXPOSE 3000-3100
|
|
64
|
-
|
|
65
59
|
EXPOSE 4000-4100
|