underpost 2.8.1 → 2.8.7
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/.dockerignore +1 -0
- package/.github/workflows/ghpkg.yml +19 -49
- package/.github/workflows/npmpkg.yml +67 -0
- package/.github/workflows/publish.yml +5 -5
- package/.github/workflows/pwa-microservices-template.page.yml +12 -4
- package/.github/workflows/pwa-microservices-template.test.yml +2 -2
- package/.vscode/extensions.json +18 -71
- package/.vscode/settings.json +20 -3
- package/AUTHORS.md +16 -5
- package/CHANGELOG.md +123 -3
- package/Dockerfile +27 -70
- package/README.md +39 -29
- package/bin/build.js +186 -0
- package/bin/db.js +2 -24
- package/bin/deploy.js +1467 -236
- package/bin/file.js +67 -16
- package/bin/hwt.js +0 -10
- package/bin/index.js +1 -77
- package/bin/ssl.js +19 -11
- package/bin/util.js +9 -104
- package/bin/vs.js +26 -2
- package/cli.md +451 -0
- package/conf.js +29 -138
- package/docker-compose.yml +1 -1
- package/jsdoc.json +1 -1
- package/manifests/calico-custom-resources.yaml +25 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/kafka/deployment.yaml +69 -0
- package/manifests/deployment/mongo-express/deployment.yaml +60 -0
- package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
- package/manifests/kind-config-dev.yaml +12 -0
- package/manifests/kind-config.yaml +12 -0
- package/manifests/kubeadm-calico-config.yaml +119 -0
- package/manifests/letsencrypt-prod.yaml +15 -0
- package/manifests/mariadb/config.yaml +10 -0
- package/manifests/mariadb/kustomization.yaml +9 -0
- package/manifests/mariadb/pv.yaml +12 -0
- package/manifests/mariadb/pvc.yaml +10 -0
- package/manifests/mariadb/secret.yaml +8 -0
- package/manifests/mariadb/service.yaml +10 -0
- package/manifests/mariadb/statefulset.yaml +55 -0
- package/manifests/mongodb/backup-access.yaml +16 -0
- package/manifests/mongodb/backup-cronjob.yaml +42 -0
- package/manifests/mongodb/backup-pv-pvc.yaml +22 -0
- package/manifests/mongodb/configmap.yaml +26 -0
- package/manifests/mongodb/headless-service.yaml +10 -0
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +23 -0
- package/manifests/mongodb/statefulset.yaml +125 -0
- package/manifests/mongodb-4.4/kustomization.yaml +7 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +23 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +17 -0
- package/manifests/valkey/statefulset.yaml +41 -0
- package/package.json +127 -136
- package/src/api/core/core.service.js +1 -1
- package/src/api/default/default.service.js +1 -1
- package/src/api/user/user.model.js +16 -3
- package/src/api/user/user.service.js +15 -12
- package/src/cli/cluster.js +389 -0
- package/src/cli/cron.js +121 -0
- package/src/cli/db.js +222 -0
- package/src/cli/deploy.js +487 -0
- package/src/cli/env.js +58 -0
- package/src/cli/fs.js +161 -0
- package/src/cli/image.js +66 -0
- package/src/cli/index.js +312 -0
- package/src/cli/monitor.js +236 -0
- package/src/cli/repository.js +128 -0
- package/src/cli/script.js +53 -0
- package/src/cli/secrets.js +37 -0
- package/src/cli/test.js +118 -0
- package/src/client/components/core/Account.js +28 -24
- package/src/client/components/core/Auth.js +22 -4
- package/src/client/components/core/Blockchain.js +1 -1
- package/src/client/components/core/CalendarCore.js +128 -121
- package/src/client/components/core/CommonJs.js +283 -19
- package/src/client/components/core/CssCore.js +16 -4
- package/src/client/components/core/Docs.js +1 -2
- package/src/client/components/core/DropDown.js +5 -1
- package/src/client/components/core/EventsUI.js +3 -3
- package/src/client/components/core/FileExplorer.js +86 -78
- package/src/client/components/core/Input.js +22 -6
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/LoadingAnimation.js +3 -12
- package/src/client/components/core/LogIn.js +3 -3
- package/src/client/components/core/LogOut.js +1 -1
- package/src/client/components/core/Modal.js +54 -20
- package/src/client/components/core/Panel.js +109 -90
- package/src/client/components/core/PanelForm.js +23 -30
- package/src/client/components/core/Recover.js +3 -3
- package/src/client/components/core/RichText.js +1 -11
- package/src/client/components/core/Router.js +3 -1
- package/src/client/components/core/Scroll.js +1 -0
- package/src/client/components/core/SignUp.js +2 -2
- package/src/client/components/core/Translate.js +47 -9
- package/src/client/components/core/Validator.js +9 -1
- package/src/client/components/core/VanillaJs.js +0 -9
- package/src/client/components/core/Worker.js +34 -31
- package/src/client/components/default/RoutesDefault.js +3 -2
- package/src/client/services/core/core.service.js +15 -10
- package/src/client/services/default/default.management.js +46 -37
- package/src/client/ssr/Render.js +6 -1
- package/src/client/ssr/body/CacheControl.js +2 -3
- package/src/client/sw/default.sw.js +3 -3
- package/src/db/mongo/MongooseDB.js +29 -1
- package/src/index.js +101 -19
- package/src/mailer/MailerProvider.js +3 -0
- package/src/runtime/lampp/Dockerfile +65 -0
- package/src/runtime/lampp/Lampp.js +1 -13
- package/src/runtime/xampp/Xampp.js +0 -13
- package/src/server/auth.js +3 -3
- package/src/server/backup.js +49 -93
- package/src/server/client-build.js +49 -46
- package/src/server/client-formatted.js +6 -3
- package/src/server/conf.js +297 -55
- package/src/server/dns.js +75 -62
- package/src/server/downloader.js +0 -8
- package/src/server/json-schema.js +77 -0
- package/src/server/logger.js +15 -10
- package/src/server/network.js +20 -161
- package/src/server/peer.js +2 -2
- package/src/server/process.js +25 -2
- package/src/server/proxy.js +7 -29
- package/src/server/runtime.js +53 -40
- package/src/server/ssl.js +1 -1
- package/src/server/start.js +122 -0
- package/src/server/valkey.js +27 -11
- package/test/api.test.js +0 -8
- package/src/dns.js +0 -22
- package/src/server/prompt-optimizer.js +0 -28
- package/startup.js +0 -11
package/bin/deploy.js
CHANGED
|
@@ -2,9 +2,8 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
|
-
import plantuml from 'plantuml';
|
|
6
5
|
|
|
7
|
-
import { shellCd, shellExec } from '../src/server/process.js';
|
|
6
|
+
import { pbcopy, shellCd, shellExec } from '../src/server/process.js';
|
|
8
7
|
import { loggerFactory } from '../src/server/logger.js';
|
|
9
8
|
import {
|
|
10
9
|
Config,
|
|
@@ -26,16 +25,24 @@ import {
|
|
|
26
25
|
restoreMacroDb,
|
|
27
26
|
fixDependencies,
|
|
28
27
|
setUpProxyMaintenanceServer,
|
|
28
|
+
writeEnv,
|
|
29
|
+
getUnderpostRootPath,
|
|
29
30
|
} from '../src/server/conf.js';
|
|
30
31
|
import { buildClient } from '../src/server/client-build.js';
|
|
31
|
-
import { range, setPad, timer, uniqueArray } from '../src/client/components/core/CommonJs.js';
|
|
32
|
-
import simpleGit from 'simple-git';
|
|
32
|
+
import { range, s4, setPad, timer, uniqueArray } from '../src/client/components/core/CommonJs.js';
|
|
33
33
|
import { MongooseDB } from '../src/db/mongo/MongooseDB.js';
|
|
34
34
|
import { Lampp } from '../src/runtime/lampp/Lampp.js';
|
|
35
35
|
import { DefaultConf } from '../conf.js';
|
|
36
36
|
import { JSONweb } from '../src/server/client-formatted.js';
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
import { Xampp } from '../src/runtime/xampp/Xampp.js';
|
|
39
|
+
import { ejs } from '../src/server/json-schema.js';
|
|
40
|
+
import { buildCliDoc } from '../src/cli/index.js';
|
|
41
|
+
import { getLocalIPv4Address, ip } from '../src/server/dns.js';
|
|
42
|
+
import { Downloader } from '../src/server/downloader.js';
|
|
43
|
+
import colors from 'colors';
|
|
44
|
+
|
|
45
|
+
colors.enable();
|
|
39
46
|
|
|
40
47
|
const logger = loggerFactory(import.meta);
|
|
41
48
|
|
|
@@ -43,6 +50,82 @@ logger.info('argv', process.argv);
|
|
|
43
50
|
|
|
44
51
|
const [exe, dir, operator] = process.argv;
|
|
45
52
|
|
|
53
|
+
const updateVirtualRoot = async ({ nfsHostPath, IP_ADDRESS, ipaddr }) => {
|
|
54
|
+
const steps = [
|
|
55
|
+
`apt update`,
|
|
56
|
+
`ln -sf /lib/systemd/systemd /sbin/init`,
|
|
57
|
+
// `sudo apt install linux-modules-extra-6.8.0-31-generic`,
|
|
58
|
+
`apt install -y sudo`,
|
|
59
|
+
`apt install -y ntp`,
|
|
60
|
+
`apt install -y openssh-server`,
|
|
61
|
+
`apt install -y iptables`,
|
|
62
|
+
`update-alternatives --set iptables /usr/sbin/iptables-legacy`,
|
|
63
|
+
`update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy`,
|
|
64
|
+
`apt install -y locales`,
|
|
65
|
+
`apt install -y cloud-init`,
|
|
66
|
+
`mkdir -p /var/lib/cloud`,
|
|
67
|
+
`chown -R root:root /var/lib/cloud`,
|
|
68
|
+
`chmod -R 0755 /var/lib/cloud`,
|
|
69
|
+
`mkdir -p /home/root/.ssh`,
|
|
70
|
+
`echo '${fs.readFileSync(
|
|
71
|
+
`/home/dd/engine/engine-private/deploy/id_rsa.pub`,
|
|
72
|
+
'utf8',
|
|
73
|
+
)}' >> /home/root/.ssh/authorized_keys`,
|
|
74
|
+
`chmod 700 /home/root/.ssh`,
|
|
75
|
+
`chmod 600 /home/root/.ssh/authorized_keys`,
|
|
76
|
+
`systemctl enable ssh`,
|
|
77
|
+
`systemctl enable ntp`,
|
|
78
|
+
`apt install -y linux-generic-hwe-24.04`,
|
|
79
|
+
`modprobe ip_tables`,
|
|
80
|
+
`cat <<EOF_MAAS_CFG > /etc/cloud/cloud.cfg.d/90_maas.cfg
|
|
81
|
+
datasource_list: [ MAAS ]
|
|
82
|
+
datasource:
|
|
83
|
+
MAAS:
|
|
84
|
+
metadata_url: http://${IP_ADDRESS}:5248/MAAS/metadata
|
|
85
|
+
users:
|
|
86
|
+
- name: ${process.env.MAAS_ADMIN_USERNAME}
|
|
87
|
+
ssh_authorized_keys:
|
|
88
|
+
- ${fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')}
|
|
89
|
+
sudo: "ALL=(ALL) NOPASSWD:ALL"
|
|
90
|
+
groups: sudo
|
|
91
|
+
shell: /bin/bash
|
|
92
|
+
packages:
|
|
93
|
+
- git
|
|
94
|
+
- htop
|
|
95
|
+
- ufw
|
|
96
|
+
# package_update: true
|
|
97
|
+
runcmd:
|
|
98
|
+
- ufw enable
|
|
99
|
+
- ufw allow ssh
|
|
100
|
+
resize_rootfs: false
|
|
101
|
+
growpart:
|
|
102
|
+
mode: off
|
|
103
|
+
network:
|
|
104
|
+
version: 2
|
|
105
|
+
ethernets:
|
|
106
|
+
${process.env.RPI4_INTERFACE_NAME}:
|
|
107
|
+
dhcp4: true
|
|
108
|
+
addresses:
|
|
109
|
+
- ${ipaddr}/24
|
|
110
|
+
EOF_MAAS_CFG`,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
|
|
114
|
+
${steps
|
|
115
|
+
.map(
|
|
116
|
+
(s, i) => `echo "step ${i + 1}/${steps.length}: ${s.split('\n')[0]}"
|
|
117
|
+
${s}
|
|
118
|
+
`,
|
|
119
|
+
)
|
|
120
|
+
.join(``)}
|
|
121
|
+
EOF`);
|
|
122
|
+
|
|
123
|
+
shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
|
|
124
|
+
echo "nameserver ${process.env.MAAS_DNS}" | tee /etc/resolv.conf > /dev/null
|
|
125
|
+
apt update
|
|
126
|
+
EOF`);
|
|
127
|
+
};
|
|
128
|
+
|
|
46
129
|
try {
|
|
47
130
|
switch (operator) {
|
|
48
131
|
case 'save':
|
|
@@ -139,32 +222,10 @@ try {
|
|
|
139
222
|
}
|
|
140
223
|
break;
|
|
141
224
|
case 'conf': {
|
|
142
|
-
loadConf(process.argv[3]);
|
|
143
|
-
if (process.argv[4]) fs.writeFileSync(`.env`, fs.readFileSync(`.env.${process.argv[4]}`, 'utf8'), 'utf8');
|
|
225
|
+
loadConf(process.argv[3], process.argv[4]);
|
|
144
226
|
break;
|
|
145
227
|
}
|
|
146
|
-
case 'run':
|
|
147
|
-
{
|
|
148
|
-
if (process.argv.includes('replicas')) {
|
|
149
|
-
const deployGroupId = getDeployGroupId();
|
|
150
|
-
const dataDeploy = getDataDeploy({
|
|
151
|
-
deployId: process.argv[3],
|
|
152
|
-
buildSingleReplica: true,
|
|
153
|
-
deployGroupId,
|
|
154
|
-
});
|
|
155
|
-
if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
|
|
156
|
-
await deployRun(dataDeploy);
|
|
157
|
-
} else {
|
|
158
|
-
loadConf(process.argv[3]);
|
|
159
|
-
shellExec(`npm start ${process.argv.includes('maintenance') ? 'maintenance' : ''}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
break;
|
|
163
228
|
|
|
164
|
-
case 'remove-await-deploy': {
|
|
165
|
-
if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
229
|
case 'new-nodejs-app':
|
|
169
230
|
{
|
|
170
231
|
const deployId = process.argv[3];
|
|
@@ -231,6 +292,7 @@ try {
|
|
|
231
292
|
break;
|
|
232
293
|
case 'build-full-client':
|
|
233
294
|
{
|
|
295
|
+
dotenv.config({ override: true });
|
|
234
296
|
if (!process.argv[3]) process.argv[3] = 'default';
|
|
235
297
|
const { deployId, folder } = loadConf(process.argv[3]);
|
|
236
298
|
|
|
@@ -256,20 +318,7 @@ try {
|
|
|
256
318
|
serverConf[host][path].replicas.map((replica) => buildReplicaId({ deployId, replica })),
|
|
257
319
|
);
|
|
258
320
|
|
|
259
|
-
shellExec(Cmd.replica(deployId, host, path));
|
|
260
|
-
}
|
|
261
|
-
if (serverConf[host][path].db) {
|
|
262
|
-
switch (serverConf[host][path].db.provider) {
|
|
263
|
-
case 'mariadb':
|
|
264
|
-
{
|
|
265
|
-
shellExec(`node bin/db ${host}${path} create ${deployId}`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
break;
|
|
269
|
-
|
|
270
|
-
default:
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
321
|
+
// shellExec(Cmd.replica(deployId, host, path));
|
|
273
322
|
}
|
|
274
323
|
}
|
|
275
324
|
}
|
|
@@ -278,7 +327,7 @@ try {
|
|
|
278
327
|
await buildClient();
|
|
279
328
|
|
|
280
329
|
for (const replicaDeployId of deployIdSingleReplicas) {
|
|
281
|
-
shellExec(Cmd.conf(replicaDeployId));
|
|
330
|
+
shellExec(Cmd.conf(replicaDeployId, process.env.NODE_ENV));
|
|
282
331
|
shellExec(Cmd.build(replicaDeployId));
|
|
283
332
|
}
|
|
284
333
|
}
|
|
@@ -307,7 +356,7 @@ try {
|
|
|
307
356
|
}
|
|
308
357
|
|
|
309
358
|
case 'adminer': {
|
|
310
|
-
const directory = '/dd/engine/public/adminer';
|
|
359
|
+
const directory = '/home/dd/engine/public/adminer';
|
|
311
360
|
// const host = '127.0.0.1';
|
|
312
361
|
const host = 'localhost';
|
|
313
362
|
const port = 80;
|
|
@@ -337,7 +386,7 @@ try {
|
|
|
337
386
|
|
|
338
387
|
case 'pma':
|
|
339
388
|
{
|
|
340
|
-
const directory = '/dd/engine/public/phpmyadmin';
|
|
389
|
+
const directory = '/home/dd/engine/public/phpmyadmin';
|
|
341
390
|
// const host = '127.0.0.1';
|
|
342
391
|
const host = 'localhost';
|
|
343
392
|
const port = 80;
|
|
@@ -454,14 +503,16 @@ try {
|
|
|
454
503
|
}
|
|
455
504
|
break;
|
|
456
505
|
|
|
457
|
-
case 'update-
|
|
506
|
+
case 'update-dependencies':
|
|
458
507
|
const files = await fs.readdir(`./engine-private/conf`, { recursive: true });
|
|
459
508
|
const originPackage = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
|
|
460
509
|
for (const relativePath of files) {
|
|
461
510
|
const filePah = `./engine-private/conf/${relativePath.replaceAll(`\\`, '/')}`;
|
|
462
511
|
if (filePah.split('/').pop() === 'package.json') {
|
|
463
|
-
|
|
464
|
-
|
|
512
|
+
const deployPackage = JSON.parse(fs.readFileSync(filePah), 'utf8');
|
|
513
|
+
deployPackage.dependencies = originPackage.dependencies;
|
|
514
|
+
deployPackage.devDependencies = originPackage.devDependencies;
|
|
515
|
+
fs.writeFileSync(filePah, JSON.stringify(deployPackage, null, 4), 'utf8');
|
|
465
516
|
}
|
|
466
517
|
}
|
|
467
518
|
break;
|
|
@@ -469,12 +520,13 @@ try {
|
|
|
469
520
|
case 'run-macro':
|
|
470
521
|
{
|
|
471
522
|
if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
|
|
472
|
-
const dataDeploy = getDataDeploy({
|
|
523
|
+
const dataDeploy = getDataDeploy({
|
|
524
|
+
deployGroupId: process.argv[3],
|
|
525
|
+
buildSingleReplica: true,
|
|
526
|
+
deployIdConcat: ['dd-proxy', 'dd-cron'],
|
|
527
|
+
});
|
|
473
528
|
if (!process.argv[4]) await setUpProxyMaintenanceServer({ deployGroupId: process.argv[3] });
|
|
474
|
-
await deployRun(
|
|
475
|
-
process.argv[4] ? dataDeploy.filter((d) => d.deployId.match(process.argv[4])) : dataDeploy,
|
|
476
|
-
true,
|
|
477
|
-
);
|
|
529
|
+
await deployRun(process.argv[4] ? dataDeploy.filter((d) => d.deployId.match(process.argv[4])) : dataDeploy);
|
|
478
530
|
}
|
|
479
531
|
break;
|
|
480
532
|
|
|
@@ -546,13 +598,7 @@ try {
|
|
|
546
598
|
? envInstanceObj.port
|
|
547
599
|
: envInstanceObj.port + port - singleReplicaHosts.length - (replicaHost ? 1 : 0);
|
|
548
600
|
|
|
549
|
-
|
|
550
|
-
envPath,
|
|
551
|
-
Object.keys(envObj)
|
|
552
|
-
.map((key) => `${key}=${envObj[key]}`)
|
|
553
|
-
.join(`\n`),
|
|
554
|
-
'utf8',
|
|
555
|
-
);
|
|
601
|
+
writeEnv(envPath, envObj);
|
|
556
602
|
}
|
|
557
603
|
const serverConf = loadReplicas(
|
|
558
604
|
JSON.parse(fs.readFileSync(`${baseConfPath}/${deployId}/conf.server.json`, 'utf8')),
|
|
@@ -596,6 +642,7 @@ try {
|
|
|
596
642
|
}
|
|
597
643
|
case 'build-uml':
|
|
598
644
|
{
|
|
645
|
+
const plantuml = await import('plantuml');
|
|
599
646
|
const folder = process.argv[3] ? process.argv[3] : `./src/client/public/default/plantuml`;
|
|
600
647
|
const confData = Config.default;
|
|
601
648
|
|
|
@@ -709,89 +756,102 @@ try {
|
|
|
709
756
|
break;
|
|
710
757
|
}
|
|
711
758
|
|
|
712
|
-
case '
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
759
|
+
case 'version-build': {
|
|
760
|
+
const originPackageJson = JSON.parse(fs.readFileSync(`package.json`, 'utf8'));
|
|
761
|
+
const newVersion = process.argv[3] ?? originPackageJson.version;
|
|
762
|
+
const { version } = originPackageJson;
|
|
763
|
+
originPackageJson.version = newVersion;
|
|
764
|
+
fs.writeFileSync(`package.json`, JSON.stringify(originPackageJson, null, 4), 'utf8');
|
|
765
|
+
|
|
766
|
+
const originPackageLockJson = JSON.parse(fs.readFileSync(`package-lock.json`, 'utf8'));
|
|
767
|
+
originPackageLockJson.version = newVersion;
|
|
768
|
+
originPackageLockJson.packages[''].version = newVersion;
|
|
769
|
+
fs.writeFileSync(`package-lock.json`, JSON.stringify(originPackageLockJson, null, 4), 'utf8');
|
|
770
|
+
|
|
771
|
+
if (fs.existsSync(`./engine-private/conf`)) {
|
|
772
|
+
const files = await fs.readdir(`./engine-private/conf`, { recursive: true });
|
|
773
|
+
for (const relativePath of files) {
|
|
774
|
+
const filePah = `./engine-private/conf/${relativePath.replaceAll(`\\`, '/')}`;
|
|
775
|
+
if (filePah.split('/').pop() === 'package.json') {
|
|
776
|
+
const originPackage = JSON.parse(fs.readFileSync(filePah, 'utf8'));
|
|
777
|
+
originPackage.version = newVersion;
|
|
778
|
+
fs.writeFileSync(filePah, JSON.stringify(originPackage, null, 4), 'utf8');
|
|
779
|
+
}
|
|
780
|
+
if (filePah.split('/').pop() === 'deployment.yaml') {
|
|
781
|
+
fs.writeFileSync(
|
|
782
|
+
filePah,
|
|
783
|
+
fs
|
|
784
|
+
.readFileSync(filePah, 'utf8')
|
|
785
|
+
.replaceAll(`v${version}`, `v${newVersion}`)
|
|
786
|
+
.replaceAll(`engine.version: ${version}`, `engine.version: ${newVersion}`),
|
|
787
|
+
'utf8',
|
|
788
|
+
);
|
|
734
789
|
}
|
|
735
790
|
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
fs.writeFileSync(
|
|
794
|
+
`./docker-compose.yml`,
|
|
795
|
+
fs
|
|
796
|
+
.readFileSync(`./docker-compose.yml`, 'utf8')
|
|
797
|
+
.replaceAll(`engine.version: '${version}'`, `engine.version: '${newVersion}'`),
|
|
798
|
+
'utf8',
|
|
799
|
+
);
|
|
736
800
|
|
|
801
|
+
if (fs.existsSync(`./.github/workflows/docker-image.yml`))
|
|
737
802
|
fs.writeFileSync(
|
|
738
|
-
|
|
803
|
+
`./.github/workflows/docker-image.yml`,
|
|
739
804
|
fs
|
|
740
|
-
.readFileSync(
|
|
741
|
-
.replaceAll(`engine
|
|
805
|
+
.readFileSync(`./.github/workflows/docker-image.yml`, 'utf8')
|
|
806
|
+
.replaceAll(`underpost-engine:v${version}`, `underpost-engine:v${newVersion}`),
|
|
742
807
|
'utf8',
|
|
743
808
|
);
|
|
744
809
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
);
|
|
810
|
+
fs.writeFileSync(
|
|
811
|
+
`./src/index.js`,
|
|
812
|
+
fs.readFileSync(`./src/index.js`, 'utf8').replaceAll(`${version}`, `${newVersion}`),
|
|
813
|
+
'utf8',
|
|
814
|
+
);
|
|
815
|
+
shellExec(`node bin/deploy cli-docs`);
|
|
816
|
+
shellExec(`node bin/deploy update-dependencies`);
|
|
817
|
+
shellExec(`auto-changelog`);
|
|
818
|
+
shellExec(`node bin/build dd`);
|
|
819
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd`);
|
|
820
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd production`);
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
759
823
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
824
|
+
case 'version-deploy': {
|
|
825
|
+
shellExec(`node bin/build dd conf`);
|
|
826
|
+
shellExec(`git add . && cd ./engine-private && git add .`);
|
|
827
|
+
shellExec(`node bin cmt . ci package-pwa-microservices-template`);
|
|
828
|
+
shellExec(`node bin cmt ./engine-private ci package-pwa-microservices-template`);
|
|
829
|
+
shellExec(`node bin push . underpostnet/engine`);
|
|
830
|
+
shellExec(`cd ./engine-private && node ../bin push . underpostnet/engine-private`);
|
|
763
831
|
break;
|
|
832
|
+
}
|
|
764
833
|
|
|
765
834
|
case 'update-authors': {
|
|
766
|
-
//
|
|
767
|
-
const logs = await simpleGit().log();
|
|
768
|
-
|
|
835
|
+
// #### Ordered by first contribution.
|
|
769
836
|
fs.writeFileSync(
|
|
770
837
|
'./AUTHORS.md',
|
|
771
838
|
`# Authors
|
|
772
839
|
|
|
773
|
-
#### Ordered by first contribution.
|
|
774
840
|
|
|
775
|
-
${
|
|
776
|
-
`)}
|
|
841
|
+
${shellExec(`git log | grep Author: | sort -u`, { stdout: true }).split(`\n`).join(`\n\n\n`)}
|
|
777
842
|
|
|
778
843
|
#### Generated by [underpost.net](https://underpost.net)`,
|
|
779
844
|
'utf8',
|
|
780
845
|
);
|
|
781
846
|
|
|
782
|
-
|
|
783
|
-
// date: '2024-09-16T17:10:13-03:00',
|
|
784
|
-
// message: 'update',
|
|
785
|
-
// refs: '',
|
|
786
|
-
// body: '',
|
|
787
|
-
// author_name: 'fcoverdugo',
|
|
788
|
-
// author_email: 'fcoverdugoa@underpost.net'
|
|
847
|
+
break;
|
|
789
848
|
}
|
|
790
849
|
|
|
791
850
|
case 'restore-macro-db':
|
|
792
851
|
{
|
|
793
852
|
const deployGroupId = process.argv[3];
|
|
794
|
-
|
|
853
|
+
const deployId = process.argv[4];
|
|
854
|
+
await restoreMacroDb(deployGroupId, deployId);
|
|
795
855
|
}
|
|
796
856
|
|
|
797
857
|
break;
|
|
@@ -826,10 +886,10 @@ ${uniqueArray(logs.all.map((log) => `- ${log.author_name} ([${log.author_email}]
|
|
|
826
886
|
shellExec(`bin/besu --help`);
|
|
827
887
|
|
|
828
888
|
// Set env path
|
|
829
|
-
// export PATH=$PATH:/dd/besu-24.9.1/bin
|
|
889
|
+
// export PATH=$PATH:/home/dd/besu-24.9.1/bin
|
|
830
890
|
|
|
831
891
|
// Open src
|
|
832
|
-
// shellExec(`sudo code /dd/besu-24.9.1 --user-data-dir="/root/.vscode-root" --no-sandbox`);
|
|
892
|
+
// shellExec(`sudo code /home/dd/besu-24.9.1 --user-data-dir="/root/.vscode-root" --no-sandbox`);
|
|
833
893
|
}
|
|
834
894
|
|
|
835
895
|
break;
|
|
@@ -847,184 +907,1355 @@ ${uniqueArray(logs.all.map((log) => `- ${log.author_name} ([${log.author_email}]
|
|
|
847
907
|
}
|
|
848
908
|
|
|
849
909
|
case 'update-default-conf': {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
910
|
+
const defaultServer = DefaultConf.server['default.net']['/'];
|
|
911
|
+
let confName = process.argv[3];
|
|
912
|
+
if (confName === 'ghpkg') {
|
|
913
|
+
confName = undefined;
|
|
914
|
+
const host = 'underpostnet.github.io';
|
|
915
|
+
const path = '/pwa-microservices-template-ghpkg';
|
|
916
|
+
DefaultConf.server = {
|
|
917
|
+
[host]: { [path]: defaultServer },
|
|
918
|
+
};
|
|
919
|
+
DefaultConf.server[host][path].apiBaseProxyPath = '/';
|
|
920
|
+
DefaultConf.server[host][path].apiBaseHost = 'www.nexodev.org';
|
|
921
|
+
} else if (confName) {
|
|
922
|
+
DefaultConf.client = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.client.json`, 'utf8'));
|
|
923
|
+
DefaultConf.server = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.server.json`, 'utf8'));
|
|
924
|
+
DefaultConf.ssr = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.ssr.json`, 'utf8'));
|
|
925
|
+
// DefaultConf.cron = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.cron.json`, 'utf8'));
|
|
926
|
+
|
|
927
|
+
for (const host of Object.keys(DefaultConf.server)) {
|
|
928
|
+
for (const path of Object.keys(DefaultConf.server[host])) {
|
|
929
|
+
DefaultConf.server[host][path].db = defaultServer.db;
|
|
930
|
+
DefaultConf.server[host][path].mailer = defaultServer.mailer;
|
|
931
|
+
|
|
932
|
+
delete DefaultConf.server[host][path]._wp_client;
|
|
933
|
+
delete DefaultConf.server[host][path]._wp_git;
|
|
934
|
+
delete DefaultConf.server[host][path]._wp_directory;
|
|
935
|
+
delete DefaultConf.server[host][path].wp;
|
|
936
|
+
delete DefaultConf.server[host][path].git;
|
|
937
|
+
delete DefaultConf.server[host][path].directory;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
const sepRender = '/**/';
|
|
942
|
+
const confRawPaths = fs.readFileSync('./conf.js', 'utf8').split(sepRender);
|
|
943
|
+
confRawPaths[1] = `${JSON.stringify(DefaultConf)};`;
|
|
944
|
+
const targetConfPath = `./conf${confName ? `.${confName}` : ''}.js`;
|
|
945
|
+
fs.writeFileSync(targetConfPath, confRawPaths.join(sepRender), 'utf8');
|
|
946
|
+
shellExec(`prettier --write ${targetConfPath}`);
|
|
947
|
+
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
case 'ssh': {
|
|
952
|
+
const host = process.argv[3] ?? `root@${await ip.public.ipv4()}`;
|
|
953
|
+
const domain = host.split('@')[1];
|
|
954
|
+
const user = 'root'; // host.split('@')[0];
|
|
955
|
+
const password = process.argv[4] ?? '';
|
|
956
|
+
const port = 22;
|
|
957
|
+
|
|
958
|
+
const setUpSSH = () => {
|
|
959
|
+
// Required port forwarding mapping
|
|
960
|
+
// ssh TCP 2222 22 <local-server-ip>
|
|
961
|
+
// ssh UDP 2222 22 <local-server-ip>
|
|
962
|
+
|
|
963
|
+
// Remote connect via public key
|
|
964
|
+
// ssh -i <key-path> <user>@<host>:2222
|
|
965
|
+
|
|
966
|
+
shellExec(`cat ./engine-private/deploy/id_rsa.pub > ~/.ssh/authorized_keys`);
|
|
967
|
+
|
|
968
|
+
// local trust on first use validator
|
|
969
|
+
// check ~/.ssh/known_hosts
|
|
970
|
+
|
|
971
|
+
// shellExec(`sudo sed -i -e "s@#PasswordAuthentication yes@PasswordAuthentication no@g" /etc/ssh/sshd_config`);
|
|
972
|
+
// shellExec(`sudo sed -i -e "s@#UsePAM no@UsePAM yes@g" /etc/ssh/sshd_config`);
|
|
973
|
+
|
|
974
|
+
// Include /etc/ssh/sshd_config.d/*.conf
|
|
975
|
+
// sudo tee /etc/ssh/sshd_config.d/99-custom.conf
|
|
976
|
+
shellExec(`sudo tee /etc/ssh/sshd_config <<EOF
|
|
977
|
+
PasswordAuthentication no
|
|
978
|
+
ChallengeResponseAuthentication yes
|
|
979
|
+
UsePAM yes
|
|
980
|
+
PubkeyAuthentication Yes
|
|
981
|
+
RSAAuthentication Yes
|
|
982
|
+
PermitRootLogin Yes
|
|
983
|
+
X11Forwarding yes
|
|
984
|
+
X11DisplayOffset 10
|
|
985
|
+
LoginGraceTime 120
|
|
986
|
+
StrictModes yes
|
|
987
|
+
SyslogFacility AUTH
|
|
988
|
+
LogLevel INFO
|
|
989
|
+
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
|
990
|
+
HostKey /etc/ssh/ssh_host_ed25519_key
|
|
991
|
+
#HostKey /etc/ssh/ssh_host_rsa_key
|
|
992
|
+
AuthorizedKeysFile ~/.ssh/authorized_keys
|
|
993
|
+
Subsystem sftp /usr/libexec/openssh/sftp-server
|
|
994
|
+
ListenAddress 0.0.0.0
|
|
995
|
+
ListenAddress ::
|
|
996
|
+
ListenAddress ${domain}
|
|
997
|
+
ListenAddress ${domain}:22
|
|
998
|
+
EOF`);
|
|
999
|
+
|
|
1000
|
+
shellExec(`sudo chmod 700 ~/.ssh/`);
|
|
1001
|
+
shellExec(`sudo chmod 600 ~/.ssh/authorized_keys`);
|
|
1002
|
+
shellExec(`sudo chmod 644 ~/.ssh/known_hosts`);
|
|
1003
|
+
shellExec(`chown -R ${user}:${user} ~/.ssh`);
|
|
1004
|
+
|
|
1005
|
+
shellExec(`ufw allow ${port}/tcp`);
|
|
1006
|
+
shellExec(`ufw allow ${port}/udp`);
|
|
1007
|
+
shellExec(`ufw allow ssh`);
|
|
1008
|
+
shellExec(`ufw allow from 192.168.0.0/16 to any port 22`);
|
|
1009
|
+
|
|
1010
|
+
// active ssh-agent
|
|
1011
|
+
shellExec('eval `ssh-agent -s`' + ` && ssh-add ~/.ssh/id_rsa` + ` && ssh-add -l`);
|
|
1012
|
+
// remove all
|
|
1013
|
+
// shellExec(`ssh-add -D`);
|
|
1014
|
+
// remove single
|
|
1015
|
+
// shellExec(`ssh-add -d ~/.ssh/id_rsa`);
|
|
1016
|
+
|
|
1017
|
+
// shellExec(`echo "@${host.split(`@`)[1]} * $(cat ~/.ssh/id_rsa.pub)" > ~/.ssh/known_hosts`);
|
|
1018
|
+
shellExec('eval `ssh-agent -s`' + `&& ssh-keyscan -H -t ed25519 ${host.split(`@`)[1]} > ~/.ssh/known_hosts`);
|
|
1019
|
+
// shellExec(`sudo echo "" > ~/.ssh/known_hosts`);
|
|
1020
|
+
|
|
1021
|
+
// ssh-copy-id -i ~/.ssh/id_rsa.pub -p <port_number> <username>@<host>
|
|
1022
|
+
shellExec(`ssh-copy-id -i ~/.ssh/id_rsa.pub -p ${port} ${host}`);
|
|
1023
|
+
// debug:
|
|
1024
|
+
// shellExec(`ssh -vvv ${host}`);
|
|
1025
|
+
|
|
1026
|
+
shellExec(`sudo cp ./engine-private/deploy/id_rsa ~/.ssh/id_rsa`);
|
|
1027
|
+
shellExec(`sudo cp ./engine-private/deploy/id_rsa.pub ~/.ssh/id_rsa.pub`);
|
|
1028
|
+
|
|
1029
|
+
shellExec(`sudo echo "" > /etc/ssh/ssh_host_ecdsa_key`);
|
|
1030
|
+
shellExec(`sudo cp ./engine-private/deploy/id_rsa /etc/ssh/ssh_host_ed25519_key`);
|
|
1031
|
+
shellExec(`sudo echo "" > /etc/ssh/ssh_host_rsa_key`);
|
|
1032
|
+
|
|
1033
|
+
shellExec(`sudo echo "" > /etc/ssh/ssh_host_ecdsa_key.pub`);
|
|
1034
|
+
shellExec(`sudo cp ./engine-private/deploy/id_rsa.pub /etc/ssh/ssh_host_ed25519_key.pub`);
|
|
1035
|
+
shellExec(`sudo echo "" > /etc/ssh/ssh_host_rsa_key.pub`);
|
|
1036
|
+
|
|
1037
|
+
shellExec(`sudo systemctl enable sshd`);
|
|
1038
|
+
shellExec(`sudo systemctl restart sshd`);
|
|
1039
|
+
|
|
1040
|
+
const status = shellExec(`sudo systemctl status sshd`, { silent: true, stdout: true });
|
|
1041
|
+
console.log(
|
|
1042
|
+
status.match('running') ? status.replaceAll(`running`, `running`.green) : `ssh service not running`.red,
|
|
1043
|
+
);
|
|
854
1044
|
};
|
|
855
|
-
DefaultConf.server[host][path].apiBaseProxyPath = '/';
|
|
856
|
-
DefaultConf.server[host][path].apiBaseHost = 'www.nexodev.org';
|
|
857
|
-
fs.writeFileSync(
|
|
858
|
-
'./conf.js',
|
|
859
|
-
`
|
|
860
|
-
const DefaultConf = ${JSONweb(DefaultConf)};
|
|
861
|
-
|
|
862
|
-
export { DefaultConf }
|
|
863
1045
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1046
|
+
if (process.argv.includes('import')) {
|
|
1047
|
+
setUpSSH();
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
shellExec(`sudo rm -rf ./id_rsa`);
|
|
1052
|
+
shellExec(`sudo rm -rf ./id_rsa.pub`);
|
|
1053
|
+
|
|
1054
|
+
if (process.argv.includes('legacy'))
|
|
1055
|
+
shellExec(`ssh-keygen -t rsa -b 4096 -f id_rsa -N "${password}" -q -C "${host}"`);
|
|
1056
|
+
else shellExec(`ssh-keygen -t ed25519 -f id_rsa -N "${password}" -q -C "${host}"`);
|
|
1057
|
+
|
|
1058
|
+
shellExec(`sudo cp ./id_rsa ~/.ssh/id_rsa`);
|
|
1059
|
+
shellExec(`sudo cp ./id_rsa.pub ~/.ssh/id_rsa.pub`);
|
|
1060
|
+
|
|
1061
|
+
shellExec(`sudo cp ./id_rsa ./engine-private/deploy/id_rsa`);
|
|
1062
|
+
shellExec(`sudo cp ./id_rsa.pub ./engine-private/deploy/id_rsa.pub`);
|
|
867
1063
|
|
|
1064
|
+
shellExec(`sudo rm -rf ./id_rsa`);
|
|
1065
|
+
shellExec(`sudo rm -rf ./id_rsa.pub`);
|
|
1066
|
+
setUpSSH();
|
|
868
1067
|
break;
|
|
869
1068
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1069
|
+
|
|
1070
|
+
case 'valkey': {
|
|
1071
|
+
if (!process.argv.includes('server')) {
|
|
1072
|
+
if (process.argv.includes('rocky')) {
|
|
1073
|
+
// shellExec(`yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm`);
|
|
1074
|
+
// shellExec(`sudo percona-release enable valkey experimental`);
|
|
1075
|
+
shellExec(`sudo dnf install valkey`);
|
|
1076
|
+
shellExec(`chown -R valkey:valkey /etc/valkey`);
|
|
1077
|
+
shellExec(`chown -R valkey:valkey /var/lib/valkey`);
|
|
1078
|
+
shellExec(`chown -R valkey:valkey /var/log/valkey`);
|
|
1079
|
+
shellExec(`sudo systemctl enable valkey.service`);
|
|
1080
|
+
shellExec(`sudo systemctl start valkey`);
|
|
1081
|
+
shellExec(`valkey-cli ping`);
|
|
1082
|
+
} else {
|
|
1083
|
+
shellExec(`cd /home/dd && git clone https://github.com/valkey-io/valkey.git`);
|
|
1084
|
+
shellExec(`cd /home/dd/valkey && make`);
|
|
1085
|
+
shellExec(`apt install valkey-tools`); // valkey-cli
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
if (process.argv.includes('rocky')) {
|
|
1089
|
+
shellExec(`sudo systemctl stop valkey`);
|
|
1090
|
+
shellExec(`sudo systemctl start valkey`);
|
|
1091
|
+
} else shellExec(`cd /home/dd/valkey && ./src/valkey-server`);
|
|
1092
|
+
|
|
873
1093
|
break;
|
|
874
1094
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1095
|
+
|
|
1096
|
+
case 'valkey-service': {
|
|
1097
|
+
shellExec(`pm2 start bin/deploy.js --node-args=\"--max-old-space-size=8192\" --name valkey -- valkey server`);
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
case 'update-instances': {
|
|
1102
|
+
shellExec(`node bin deploy dd production --sync --build-manifest --info-router --dashboard-update`);
|
|
1103
|
+
shellExec(`node bin cron --dashboard-update --init`);
|
|
1104
|
+
const deployId = 'dd-core';
|
|
1105
|
+
const host = 'www.nexodev.org';
|
|
1106
|
+
const path = '/';
|
|
1107
|
+
|
|
1108
|
+
{
|
|
1109
|
+
const outputPath = './engine-private/instances';
|
|
1110
|
+
if (fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true });
|
|
1111
|
+
const collection = 'instances';
|
|
1112
|
+
if (process.argv.includes('export'))
|
|
1113
|
+
shellExec(
|
|
1114
|
+
`node bin db --export --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1115
|
+
);
|
|
1116
|
+
if (process.argv.includes('import'))
|
|
1117
|
+
shellExec(
|
|
1118
|
+
`node bin db --import --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
{
|
|
1122
|
+
const outputPath = './engine-private/crons';
|
|
1123
|
+
if (fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true });
|
|
1124
|
+
const collection = 'crons';
|
|
1125
|
+
if (process.argv.includes('export'))
|
|
1126
|
+
shellExec(
|
|
1127
|
+
`node bin db --export --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1128
|
+
);
|
|
1129
|
+
if (process.argv.includes('import'))
|
|
1130
|
+
shellExec(
|
|
1131
|
+
`node bin db --import --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
878
1135
|
break;
|
|
879
1136
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1137
|
+
|
|
1138
|
+
case 'cli-docs': {
|
|
1139
|
+
buildCliDoc();
|
|
883
1140
|
break;
|
|
884
1141
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1142
|
+
|
|
1143
|
+
case 'monitor': {
|
|
1144
|
+
shellExec(
|
|
1145
|
+
`node bin monitor ${process.argv[6] === 'sync' ? '--sync ' : ''}--type ${process.argv[3]} ${process.argv[4]} ${
|
|
1146
|
+
process.argv[5]
|
|
1147
|
+
}`,
|
|
1148
|
+
{
|
|
1149
|
+
async: true,
|
|
1150
|
+
},
|
|
1151
|
+
);
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
case 'postgresql': {
|
|
1156
|
+
if (process.argv.includes('install')) {
|
|
1157
|
+
shellExec(`sudo dnf install -y postgresql-server postgresql`);
|
|
1158
|
+
shellExec(`sudo postgresql-setup --initdb`);
|
|
1159
|
+
shellExec(`chown postgres /var/lib/pgsql/data`);
|
|
1160
|
+
shellExec(`sudo systemctl enable postgresql.service`);
|
|
1161
|
+
shellExec(`sudo systemctl start postgresql.service`);
|
|
1162
|
+
} else {
|
|
1163
|
+
shellExec(`sudo systemctl enable postgresql.service`);
|
|
1164
|
+
shellExec(`sudo systemctl restart postgresql.service`);
|
|
893
1165
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1166
|
+
|
|
1167
|
+
shellExec(`sudo systemctl status postgresql.service`);
|
|
1168
|
+
|
|
1169
|
+
// sudo systemctl stop postgresql
|
|
1170
|
+
// sudo systemctl disable postgresql
|
|
1171
|
+
|
|
1172
|
+
// psql login
|
|
1173
|
+
// psql -U <user> -h 127.0.0.1 -W <db-name>
|
|
1174
|
+
|
|
1175
|
+
// gedit /var/lib/pgsql/data/pg_hba.conf
|
|
1176
|
+
// host <db-name> <db-user> <db-host> md5
|
|
1177
|
+
// local all postgres trust
|
|
1178
|
+
// # "local" is for Unix domain socket connections only
|
|
1179
|
+
// local all all md5
|
|
1180
|
+
// # IPv4 local connections:
|
|
1181
|
+
// host all all 127.0.0.1/32 md5
|
|
1182
|
+
// # IPv6 local connections:
|
|
1183
|
+
// host all all ::1/128 md5
|
|
1184
|
+
|
|
1185
|
+
// gedit /var/lib/pgsql/data/postgresql.conf
|
|
1186
|
+
// listen_addresses = '*'
|
|
1187
|
+
|
|
897
1188
|
break;
|
|
898
1189
|
}
|
|
899
1190
|
|
|
900
|
-
case '
|
|
901
|
-
|
|
1191
|
+
case 'postgresql-14': {
|
|
1192
|
+
shellExec(`sudo /usr/pgsql-14/bin/postgresql-14-setup initdb`);
|
|
1193
|
+
shellExec(`sudo systemctl start postgresql-14`);
|
|
1194
|
+
shellExec(`sudo systemctl enable postgresql-14`);
|
|
1195
|
+
shellExec(`sudo systemctl status postgresql-14`);
|
|
1196
|
+
// sudo dnf install postgresql14-contrib
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
case 'pg-stop': {
|
|
1201
|
+
shellExec(`sudo systemctl stop postgresql-14`);
|
|
1202
|
+
shellExec(`sudo systemctl disable postgresql-14`);
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
1205
|
+
case 'pg-start': {
|
|
1206
|
+
shellExec(`sudo systemctl enable postgresql-14`);
|
|
1207
|
+
shellExec(`sudo systemctl restart postgresql-14`);
|
|
1208
|
+
break;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
case 'pg-list-db': {
|
|
1212
|
+
shellExec(`sudo -i -u postgres psql -c "\\l"`);
|
|
1213
|
+
break;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
case 'pg-list-table': {
|
|
1217
|
+
shellExec(`sudo -i -u postgres psql -c "\\dt *.*"`);
|
|
1218
|
+
// schema_name.*
|
|
1219
|
+
break;
|
|
1220
|
+
}
|
|
1221
|
+
case 'pg-drop-db': {
|
|
1222
|
+
shellExec(`sudo -i -u postgres psql -c "DROP DATABASE ${process.argv[3]} WITH (FORCE)"`);
|
|
1223
|
+
shellExec(`sudo -i -u postgres psql -c "DROP USER ${process.argv[4]}"`);
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
case 'maas-stop': {
|
|
1228
|
+
shellExec(`sudo snap stop maas`);
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
case 'maas': {
|
|
1233
|
+
dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
|
|
1234
|
+
const IP_ADDRESS = getLocalIPv4Address();
|
|
1235
|
+
const serverip = IP_ADDRESS;
|
|
1236
|
+
const tftpRoot = process.env.TFTP_ROOT;
|
|
1237
|
+
const ipaddr = process.env.RPI4_IP;
|
|
1238
|
+
const netmask = process.env.NETMASK;
|
|
1239
|
+
const gatewayip = process.env.GATEWAY_IP;
|
|
1240
|
+
|
|
1241
|
+
let resources;
|
|
1242
|
+
try {
|
|
1243
|
+
resources = JSON.parse(
|
|
1244
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resources read`, {
|
|
1245
|
+
silent: true,
|
|
1246
|
+
stdout: true,
|
|
1247
|
+
}),
|
|
1248
|
+
).map((o) => ({
|
|
1249
|
+
id: o.id,
|
|
1250
|
+
name: o.name,
|
|
1251
|
+
architecture: o.architecture,
|
|
1252
|
+
}));
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
logger.error(error);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const machineFactory = (m) => ({
|
|
1258
|
+
system_id: m.interface_set[0].system_id,
|
|
1259
|
+
mac_address: m.interface_set[0].mac_address,
|
|
1260
|
+
hostname: m.hostname,
|
|
1261
|
+
status_name: m.status_name,
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
let machines;
|
|
1265
|
+
try {
|
|
1266
|
+
machines = JSON.parse(
|
|
1267
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machines read`, {
|
|
1268
|
+
stdout: true,
|
|
1269
|
+
silent: true,
|
|
1270
|
+
}),
|
|
1271
|
+
).map((m) => machineFactory(m));
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
logger.error(error);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
if (process.argv.includes('db')) {
|
|
1277
|
+
// DROP, ALTER, CREATE, WITH ENCRYPTED
|
|
1278
|
+
// sudo -u <user> -h <host> psql <db-name>
|
|
1279
|
+
shellExec(`DB_PG_MAAS_NAME=${process.env.DB_PG_MAAS_NAME}`);
|
|
1280
|
+
shellExec(`DB_PG_MAAS_PASS=${process.env.DB_PG_MAAS_PASS}`);
|
|
1281
|
+
shellExec(`DB_PG_MAAS_USER=${process.env.DB_PG_MAAS_USER}`);
|
|
1282
|
+
shellExec(`DB_PG_MAAS_HOST=${process.env.DB_PG_MAAS_HOST}`);
|
|
1283
|
+
shellExec(
|
|
1284
|
+
`sudo -i -u postgres psql -c "CREATE USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
|
|
1285
|
+
);
|
|
1286
|
+
shellExec(
|
|
1287
|
+
`sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
|
|
1288
|
+
);
|
|
1289
|
+
const actions = ['LOGIN', 'SUPERUSER', 'INHERIT', 'CREATEDB', 'CREATEROLE', 'REPLICATION'];
|
|
1290
|
+
shellExec(`sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ${actions.join(' ')}"`);
|
|
1291
|
+
shellExec(`sudo -i -u postgres psql -c "\\du"`);
|
|
1292
|
+
|
|
1293
|
+
shellExec(`sudo -i -u postgres createdb -O "$DB_PG_MAAS_USER" "$DB_PG_MAAS_NAME"`);
|
|
1294
|
+
|
|
1295
|
+
shellExec(`sudo -i -u postgres psql -c "\\l"`);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (process.argv.includes('ls')) {
|
|
1299
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-sources read`);
|
|
1300
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} commissioning-scripts read`);
|
|
1301
|
+
// shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-source-selections read 60`);
|
|
1302
|
+
console.table(resources);
|
|
1303
|
+
console.table(machines);
|
|
1304
|
+
process.exit(0);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// TODO: - Disable maas proxy (egress forwarding to public dns)
|
|
1308
|
+
// - Configure maas dns forwarding ${process.env.MAAS_DNS}
|
|
1309
|
+
// - Enable DNSSEC validation of upstream zones: Automatic (use default root key)
|
|
1310
|
+
|
|
1311
|
+
if (process.argv.includes('clear')) {
|
|
1312
|
+
for (const machine of machines) {
|
|
1313
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
|
|
1314
|
+
}
|
|
1315
|
+
// machines = [];
|
|
1316
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries clear all=true`);
|
|
1317
|
+
if (process.argv.includes('force')) {
|
|
1318
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries scan force=true`);
|
|
1319
|
+
}
|
|
1320
|
+
process.exit(0);
|
|
1321
|
+
}
|
|
1322
|
+
if (process.argv.includes('grub-arm64')) {
|
|
1323
|
+
shellExec(`sudo dnf install grub2-efi-aa64-modules`);
|
|
1324
|
+
shellExec(`sudo dnf install grub2-efi-x64-modules`);
|
|
1325
|
+
// sudo grub2-mknetdir --net-directory=${tftpRoot} --subdir=/boot/grub --module-path=/usr/lib/grub/arm64-efi arm64-efi
|
|
1326
|
+
process.exit(0);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
if (process.argv.includes('psql')) {
|
|
1330
|
+
const cmd = `psql -U ${process.env.DB_PG_MAAS_USER} -h ${process.env.DB_PG_MAAS_HOST} -W ${process.env.DB_PG_MAAS_NAME}`;
|
|
1331
|
+
pbcopy(cmd);
|
|
1332
|
+
process.exit(0);
|
|
1333
|
+
}
|
|
1334
|
+
if (process.argv.includes('logs')) {
|
|
1335
|
+
shellExec(`maas status`);
|
|
1336
|
+
const cmd = `journalctl -f -t dhcpd -u snap.maas.pebble.service`;
|
|
1337
|
+
pbcopy(cmd);
|
|
1338
|
+
process.exit(0);
|
|
1339
|
+
}
|
|
1340
|
+
if (process.argv.includes('reset')) {
|
|
1341
|
+
// shellExec(
|
|
1342
|
+
// `maas init region+rack --database-uri "postgres://$DB_PG_MAAS_USER:$DB_PG_MAAS_PASS@$DB_PG_MAAS_HOST/$DB_PG_MAAS_NAME"` +
|
|
1343
|
+
// ` --maas-url http://${IP_ADDRESS}:5240/MAAS`,
|
|
1344
|
+
// );
|
|
1345
|
+
const cmd =
|
|
1346
|
+
`maas init region+rack --database-uri "postgres://${process.env.DB_PG_MAAS_USER}:${process.env.DB_PG_MAAS_PASS}@${process.env.DB_PG_MAAS_HOST}/${process.env.DB_PG_MAAS_NAME}"` +
|
|
1347
|
+
` --maas-url http://${IP_ADDRESS}:5240/MAAS`;
|
|
1348
|
+
pbcopy(cmd);
|
|
1349
|
+
process.exit(0);
|
|
1350
|
+
}
|
|
1351
|
+
if (process.argv.includes('dhcp')) {
|
|
1352
|
+
const snippets = JSON.parse(
|
|
1353
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} dhcpsnippets read`, {
|
|
1354
|
+
stdout: true,
|
|
1355
|
+
silent: true,
|
|
1356
|
+
disableLog: true,
|
|
1357
|
+
}),
|
|
1358
|
+
);
|
|
1359
|
+
for (const snippet of snippets) {
|
|
1360
|
+
switch (snippet.name) {
|
|
1361
|
+
case 'arm64':
|
|
1362
|
+
snippet.value = snippet.value.split(`\n`);
|
|
1363
|
+
snippet.value[1] = ` filename "http://${IP_ADDRESS}:5248/images/bootloaders/uefi/arm64/grubaa64.efi";`;
|
|
1364
|
+
snippet.value[5] = ` filename "http://${IP_ADDRESS}:5248/images/bootloaders/uefi/arm64/grubaa64.efi";`;
|
|
1365
|
+
snippet.value = snippet.value.join(`\n`);
|
|
1366
|
+
shellExec(
|
|
1367
|
+
`maas ${process.env.MAAS_ADMIN_USERNAME} dhcpsnippet update ${snippet.name} value='${snippet.value}'`,
|
|
1368
|
+
);
|
|
1369
|
+
break;
|
|
1370
|
+
|
|
1371
|
+
default:
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
902
1375
|
|
|
903
|
-
|
|
904
|
-
// >> append
|
|
1376
|
+
console.log(snippets);
|
|
905
1377
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1378
|
+
process.exit(0);
|
|
1379
|
+
}
|
|
1380
|
+
// shellExec(`MAAS_ADMIN_USERNAME=${process.env.MAAS_ADMIN_USERNAME}`);
|
|
1381
|
+
// shellExec(`MAAS_ADMIN_EMAIL=${process.env.MAAS_ADMIN_EMAIL}`);
|
|
1382
|
+
// shellExec(`maas createadmin --username $MAAS_ADMIN_USERNAME --email $MAAS_ADMIN_EMAIL`);
|
|
1383
|
+
|
|
1384
|
+
// MaaS admin CLI:
|
|
1385
|
+
// maas login <maas-admin-username> http://localhost:5240/MAAS
|
|
1386
|
+
// paste GUI API KEY (profile section)
|
|
1387
|
+
|
|
1388
|
+
// Import custom image
|
|
1389
|
+
// maas <maas-admin-username> boot-resources create name='custom/RockyLinuxRpi4' \
|
|
1390
|
+
// title='RockyLinuxRpi4' \
|
|
1391
|
+
// architecture='arm64/generic' \
|
|
1392
|
+
// filetype='tgz' \
|
|
1393
|
+
// content@=/home/RockyLinuxRpi_9-latest.tar.gz
|
|
1394
|
+
|
|
1395
|
+
// Image boot resource:
|
|
1396
|
+
// /var/snap/maas/current/root/snap/maas
|
|
1397
|
+
// /var/snap/maas/common/maas/tftp_root
|
|
1398
|
+
// sudo chmod 755 /var/snap/maas/common/maas/tftp_root
|
|
1399
|
+
|
|
1400
|
+
// /var/snap/maas/common/maas/dhcpd.conf
|
|
1401
|
+
// sudo snap restart maas.pebble
|
|
1402
|
+
|
|
1403
|
+
// PXE Linux files:
|
|
1404
|
+
// /var/snap/maas/common/maas/image-storage/bootloaders/pxe/i386
|
|
1405
|
+
// sudo nmcli con modify <interface-device-name-connection-id> ethtool.feature-rx on ethtool.feature-tx off
|
|
1406
|
+
// sudo nmcli connection up <interface-device-name-connection-id>
|
|
1407
|
+
|
|
1408
|
+
// man nm-settings |grep feature-tx-checksum
|
|
1409
|
+
|
|
1410
|
+
// nmcli c modify <interface-device-name-connection-id> \
|
|
1411
|
+
// ethtool.feature-tx-checksum-fcoe-crc off \
|
|
1412
|
+
// ethtool.feature-tx-checksum-ip-generic off \
|
|
1413
|
+
// ethtool.feature-tx-checksum-ipv4 off \
|
|
1414
|
+
// ethtool.feature-tx-checksum-ipv6 off \
|
|
1415
|
+
// ethtool.feature-tx-checksum-sctp off
|
|
1416
|
+
|
|
1417
|
+
// Ensure Rocky NFS server and /etc/exports configured
|
|
1418
|
+
// sudo systemctl restart nfs-server
|
|
1419
|
+
// Check mounts: showmount -e <server-ip>
|
|
1420
|
+
// Check nfs ports: rpcinfo -p
|
|
1421
|
+
// sudo chown -R root:root ${process.env.NFS_EXPORT_PATH}/rpi4mb
|
|
1422
|
+
// sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/rpi4mb
|
|
1423
|
+
|
|
1424
|
+
// tftp server
|
|
1425
|
+
// sudo chown -R root:root /var/snap/maas/common/maas/tftp_root/rpi4mb
|
|
1426
|
+
|
|
1427
|
+
// tftp client
|
|
1428
|
+
// sudo dnf install tftp
|
|
1429
|
+
// tftp <server-ip> -c get <path>
|
|
1430
|
+
|
|
1431
|
+
// Check firewall-cmd
|
|
1432
|
+
// firewall-cmd --permanent --add-service=rpc-bind
|
|
1433
|
+
// firewall-cmd --reload
|
|
1434
|
+
// systemctl disable firewalld
|
|
1435
|
+
// sudo firewall-cmd --permanent --add-port=10259/tcp --zone=public
|
|
1436
|
+
|
|
1437
|
+
// Image extension transform (.img.xz to .tar.gz):
|
|
1438
|
+
// tar -cvzf image-name.tar.gz image-name.img.xz
|
|
1439
|
+
|
|
1440
|
+
// Rocky network configuration:
|
|
1441
|
+
// /etc/NetworkManager/system-connections
|
|
1442
|
+
|
|
1443
|
+
// Rocky kernel params update
|
|
1444
|
+
// sudo grubby --args="<key>=<value> <key>=<value>" --update-kernel=ALL
|
|
1445
|
+
// sudo reboot now
|
|
1446
|
+
|
|
1447
|
+
// Temporal:
|
|
1448
|
+
// sudo snap install temporal
|
|
1449
|
+
// journalctl -u snap.maas.pebble -t maas-regiond
|
|
1450
|
+
// journalctl -u snap.maas.pebble -t maas-temporal -n 100 --no-pager -f
|
|
1451
|
+
|
|
1452
|
+
// Remove:
|
|
1453
|
+
// sudo dnf remove <package> -y; sudo dnf autoremove -y; sudo dnf clean packages
|
|
1454
|
+
// check: ~
|
|
1455
|
+
// check: ~./cache
|
|
1456
|
+
// check: ~./config
|
|
1457
|
+
|
|
1458
|
+
// Check file logs
|
|
1459
|
+
// grep -i -E -C 1 '<key-a>|<key-b>' /example.log | tail -n 600
|
|
1460
|
+
|
|
1461
|
+
// Back into your firmware setup (UEFI or BIOS config screen).
|
|
1462
|
+
// grub> fwsetup
|
|
1463
|
+
|
|
1464
|
+
// Poweroff:
|
|
1465
|
+
// grub > halt
|
|
1466
|
+
// initramfs > poweroff
|
|
1467
|
+
|
|
1468
|
+
// Check interface
|
|
1469
|
+
// ip link show
|
|
1470
|
+
// nmcli con show
|
|
1471
|
+
|
|
1472
|
+
let firmwarePath,
|
|
1473
|
+
tftpSubDir,
|
|
1474
|
+
kernelFilesPaths,
|
|
1475
|
+
name,
|
|
1476
|
+
architecture,
|
|
1477
|
+
resource,
|
|
1478
|
+
nfsConnectStr,
|
|
1479
|
+
etcExports,
|
|
1480
|
+
nfsServerRootPath,
|
|
1481
|
+
bootConf,
|
|
1482
|
+
zipFirmwareFileName,
|
|
1483
|
+
zipFirmwareName,
|
|
1484
|
+
zipFirmwareUrl,
|
|
1485
|
+
interfaceName,
|
|
1486
|
+
nfsHost;
|
|
1487
|
+
|
|
1488
|
+
switch (process.argv[3]) {
|
|
1489
|
+
case 'rpi4mb':
|
|
1490
|
+
const resourceId = process.argv[4] ?? '39';
|
|
1491
|
+
tftpSubDir = '/rpi4mb';
|
|
1492
|
+
zipFirmwareFileName = `RPi4_UEFI_Firmware_v1.41.zip`;
|
|
1493
|
+
zipFirmwareName = zipFirmwareFileName.split('.zip')[0];
|
|
1494
|
+
zipFirmwareUrl = `https://github.com/pftf/RPi4/releases/download/v1.41/RPi4_UEFI_Firmware_v1.41.zip`;
|
|
1495
|
+
firmwarePath = `../${zipFirmwareName}`;
|
|
1496
|
+
interfaceName = process.env.RPI4_INTERFACE_NAME;
|
|
1497
|
+
nfsHost = 'rpi4mb';
|
|
1498
|
+
if (!fs.existsSync(firmwarePath)) {
|
|
1499
|
+
await Downloader(zipFirmwareUrl, `../${zipFirmwareFileName}`);
|
|
1500
|
+
shellExec(`cd .. && mkdir ${zipFirmwareName} && cd ${zipFirmwareName} && unzip ../${zipFirmwareFileName}`);
|
|
913
1501
|
}
|
|
914
|
-
|
|
1502
|
+
resource = resources.find((o) => o.id == resourceId);
|
|
1503
|
+
name = resource.name;
|
|
1504
|
+
architecture = resource.architecture;
|
|
1505
|
+
resource = resources.find((o) => o.name === name && o.architecture === architecture);
|
|
1506
|
+
nfsServerRootPath = `${process.env.NFS_EXPORT_PATH}/rpi4mb`;
|
|
1507
|
+
// ,anonuid=1001,anongid=100
|
|
1508
|
+
// etcExports = `${nfsServerRootPath} *(rw,all_squash,sync,no_root_squash,insecure)`;
|
|
1509
|
+
etcExports = `${nfsServerRootPath} 192.168.1.0/24(${[
|
|
1510
|
+
'rw',
|
|
1511
|
+
// 'all_squash',
|
|
1512
|
+
'sync',
|
|
1513
|
+
'no_root_squash',
|
|
1514
|
+
'no_subtree_check',
|
|
1515
|
+
'insecure',
|
|
1516
|
+
]})`;
|
|
1517
|
+
const resourceData = JSON.parse(
|
|
1518
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resource read ${resource.id}`, {
|
|
1519
|
+
stdout: true,
|
|
1520
|
+
silent: true,
|
|
1521
|
+
disableLog: true,
|
|
1522
|
+
}),
|
|
1523
|
+
);
|
|
1524
|
+
const bootFiles = resourceData.sets[Object.keys(resourceData.sets)[0]].files;
|
|
1525
|
+
const suffix = architecture.match('xgene') ? '.xgene' : '';
|
|
1526
|
+
|
|
1527
|
+
kernelFilesPaths = {
|
|
1528
|
+
'vmlinuz-efi': bootFiles['boot-kernel' + suffix].filename_on_disk,
|
|
1529
|
+
'initrd.img': bootFiles['boot-initrd' + suffix].filename_on_disk,
|
|
1530
|
+
squashfs: bootFiles['squashfs'].filename_on_disk,
|
|
1531
|
+
};
|
|
1532
|
+
const protocol = 'tcp'; // v3 -> tcp, v4 -> udp
|
|
1533
|
+
|
|
1534
|
+
const mountOptions = [
|
|
1535
|
+
protocol,
|
|
1536
|
+
'vers=3',
|
|
1537
|
+
'nfsvers=3',
|
|
1538
|
+
'nolock',
|
|
1539
|
+
// 'protocol=tcp',
|
|
1540
|
+
// 'hard=true',
|
|
1541
|
+
'port=2049',
|
|
1542
|
+
// 'sec=none',
|
|
1543
|
+
'rw',
|
|
1544
|
+
'hard',
|
|
1545
|
+
'intr',
|
|
1546
|
+
'rsize=32768',
|
|
1547
|
+
'wsize=32768',
|
|
1548
|
+
'acregmin=0',
|
|
1549
|
+
'acregmax=0',
|
|
1550
|
+
'acdirmin=0',
|
|
1551
|
+
'acdirmax=0',
|
|
1552
|
+
'noac',
|
|
1553
|
+
// 'nodev',
|
|
1554
|
+
// 'nosuid',
|
|
1555
|
+
];
|
|
1556
|
+
const cmd = [
|
|
1557
|
+
`console=serial0,115200`,
|
|
1558
|
+
`console=tty1`,
|
|
1559
|
+
// `initrd=-1`,
|
|
1560
|
+
// `net.ifnames=0`,
|
|
1561
|
+
// `dwc_otg.lpm_enable=0`,
|
|
1562
|
+
// `elevator=deadline`,
|
|
1563
|
+
`root=/dev/nfs`,
|
|
1564
|
+
`nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb,${mountOptions}`,
|
|
1565
|
+
// `nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb`,
|
|
1566
|
+
`ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${nfsHost}:${interfaceName}:static`,
|
|
1567
|
+
`rootfstype=nfs`,
|
|
1568
|
+
`rw`,
|
|
1569
|
+
`rootwait`,
|
|
1570
|
+
`fixrtc`,
|
|
1571
|
+
'initrd=initrd.img',
|
|
1572
|
+
// 'boot=casper',
|
|
1573
|
+
// 'ro',
|
|
1574
|
+
'netboot=nfs',
|
|
1575
|
+
`cloud-config-url=/dev/null`,
|
|
1576
|
+
// 'ip=dhcp',
|
|
1577
|
+
// 'ip=dfcp',
|
|
1578
|
+
// 'autoinstall',
|
|
1579
|
+
// 'rd.break',
|
|
1580
|
+
];
|
|
1581
|
+
|
|
1582
|
+
nfsConnectStr = cmd.join(' ');
|
|
1583
|
+
bootConf = `[all]
|
|
1584
|
+
MAC_ADDRESS=00:00:00:00:00:00
|
|
1585
|
+
MAC_ADDRESS_OTP=0,1
|
|
1586
|
+
BOOT_UART=0
|
|
1587
|
+
WAKE_ON_GPIO=1
|
|
1588
|
+
POWER_OFF_ON_HALT=0
|
|
1589
|
+
ENABLE_SELF_UPDATE=1
|
|
1590
|
+
DISABLE_HDMI=0
|
|
1591
|
+
TFTP_IP=${serverip}
|
|
1592
|
+
TFTP_PREFIX=1
|
|
1593
|
+
TFTP_PREFIX_STR=${tftpSubDir.slice(1)}/
|
|
1594
|
+
NET_INSTALL_ENABLED=1
|
|
1595
|
+
DHCP_TIMEOUT=45000
|
|
1596
|
+
DHCP_REQ_TIMEOUT=4000
|
|
1597
|
+
TFTP_FILE_TIMEOUT=30000
|
|
1598
|
+
BOOT_ORDER=0x21`;
|
|
1599
|
+
|
|
1600
|
+
break;
|
|
1601
|
+
|
|
1602
|
+
default:
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
shellExec(`sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/${nfsHost}`);
|
|
1606
|
+
|
|
1607
|
+
shellExec(`sudo rm -rf ${tftpRoot}${tftpSubDir}`);
|
|
1608
|
+
shellExec(`sudo cp -a ${firmwarePath} ${tftpRoot}${tftpSubDir}`);
|
|
1609
|
+
shellExec(`mkdir -p ${tftpRoot}${tftpSubDir}/pxe`);
|
|
1610
|
+
|
|
1611
|
+
fs.writeFileSync(`/etc/exports`, etcExports, 'utf8');
|
|
1612
|
+
if (bootConf) fs.writeFileSync(`${tftpRoot}${tftpSubDir}/boot.conf`, bootConf, 'utf8');
|
|
1613
|
+
|
|
1614
|
+
shellExec(`node bin/deploy nfs`);
|
|
1615
|
+
|
|
1616
|
+
if (process.argv.includes('restart')) {
|
|
1617
|
+
shellExec(`sudo snap restart maas.pebble`);
|
|
1618
|
+
let secs = 0;
|
|
1619
|
+
while (
|
|
1620
|
+
!(
|
|
1621
|
+
shellExec(`maas status`, { silent: true, disableLog: true, stdout: true })
|
|
1622
|
+
.split(' ')
|
|
1623
|
+
.filter((l) => l.match('inactive')).length === 1
|
|
1624
|
+
)
|
|
1625
|
+
) {
|
|
1626
|
+
await timer(1000);
|
|
1627
|
+
console.log(`Waiting... (${++secs}s)`);
|
|
915
1628
|
}
|
|
916
|
-
shellExec('eval `ssh-agent -s`' + ` && ssh-add -D`);
|
|
917
1629
|
}
|
|
918
1630
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1631
|
+
switch (process.argv[3]) {
|
|
1632
|
+
case 'rpi4mb':
|
|
1633
|
+
{
|
|
1634
|
+
// subnet DHCP snippets
|
|
1635
|
+
// # UEFI ARM64
|
|
1636
|
+
// if option arch = 00:0B {
|
|
1637
|
+
// filename "rpi4mb/pxe/grubaa64.efi";
|
|
1638
|
+
// }
|
|
1639
|
+
// elsif option arch = 00:13 {
|
|
1640
|
+
// filename "http://<IP_ADDRESS>:5248/images/bootloaders/uefi/arm64/grubaa64.efi";
|
|
1641
|
+
// option vendor-class-identifier "HTTPClient";
|
|
1642
|
+
// }
|
|
1643
|
+
for (const file of ['bootaa64.efi', 'grubaa64.efi']) {
|
|
1644
|
+
shellExec(
|
|
1645
|
+
`sudo cp -a /var/snap/maas/common/maas/image-storage/bootloaders/uefi/arm64/${file} ${tftpRoot}${tftpSubDir}/pxe/${file}`,
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
// const file = 'bcm2711-rpi-4-b.dtb';
|
|
1649
|
+
// shellExec(
|
|
1650
|
+
// `sudo cp -a ${firmwarePath}/${file} /var/snap/maas/common/maas/image-storage/bootloaders/uefi/arm64/${file}`,
|
|
1651
|
+
// );
|
|
1652
|
+
|
|
1653
|
+
// const ipxeSrc = fs
|
|
1654
|
+
// .readFileSync(`${tftpRoot}/ipxe.cfg`, 'utf8')
|
|
1655
|
+
// .replaceAll('amd64', 'arm64')
|
|
1656
|
+
// .replaceAll('${next-server}', IP_ADDRESS);
|
|
1657
|
+
// fs.writeFileSync(`${tftpRoot}/ipxe.cfg`, ipxeSrc, 'utf8');
|
|
1658
|
+
|
|
1659
|
+
{
|
|
1660
|
+
for (const file of Object.keys(kernelFilesPaths)) {
|
|
1661
|
+
shellExec(
|
|
1662
|
+
`sudo cp -a /var/snap/maas/common/maas/image-storage/${kernelFilesPaths[file]} ${tftpRoot}${tftpSubDir}/pxe/${file}`,
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
// const configTxtSrc = fs.readFileSync(`${firmwarePath}/config.txt`, 'utf8');
|
|
1666
|
+
// fs.writeFileSync(
|
|
1667
|
+
// `${tftpRoot}${tftpSubDir}/config.txt`,
|
|
1668
|
+
// configTxtSrc
|
|
1669
|
+
// .replace(`kernel=kernel8.img`, `kernel=vmlinuz`)
|
|
1670
|
+
// .replace(`# max_framebuffers=2`, `max_framebuffers=2`)
|
|
1671
|
+
// .replace(`initramfs initramfs8 followkernel`, `initramfs initrd.img followkernel`),
|
|
1672
|
+
// 'utf8',
|
|
1673
|
+
// );
|
|
1674
|
+
|
|
1675
|
+
// grub:
|
|
1676
|
+
// set root=(pxe)
|
|
1677
|
+
|
|
1678
|
+
// UNDERPOST.NET UEFI/GRUB/MAAS RPi4 commissioning (ARM64)
|
|
1679
|
+
const menuentryStr = 'underpost.net rpi4mb maas commissioning (ARM64)';
|
|
1680
|
+
const grubCfgPath = `${tftpRoot}/grub/grub.cfg`;
|
|
1681
|
+
fs.writeFileSync(
|
|
1682
|
+
grubCfgPath,
|
|
1683
|
+
`
|
|
1684
|
+
insmod gzio
|
|
1685
|
+
insmod http
|
|
1686
|
+
insmod nfs
|
|
1687
|
+
set timeout=5
|
|
1688
|
+
set default=0
|
|
1689
|
+
|
|
1690
|
+
menuentry '${menuentryStr}' {
|
|
1691
|
+
set root=(tftp,${serverip})
|
|
1692
|
+
linux ${tftpSubDir}/pxe/vmlinuz-efi ${nfsConnectStr}
|
|
1693
|
+
initrd ${tftpSubDir}/pxe/initrd.img
|
|
1694
|
+
boot
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
`,
|
|
1698
|
+
'utf8',
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
const arm64EfiPath = `${tftpRoot}/grub/arm64-efi`;
|
|
1702
|
+
if (fs.existsSync(arm64EfiPath)) shellExec(`sudo rm -rf ${arm64EfiPath}`);
|
|
1703
|
+
shellExec(`sudo cp -a /usr/lib/grub/arm64-efi ${arm64EfiPath}`);
|
|
1704
|
+
}
|
|
924
1705
|
|
|
925
|
-
|
|
926
|
-
shellExec(`cat ${destPath} > ${'/root/.ssh/id_rsa'}`);
|
|
1706
|
+
break;
|
|
927
1707
|
|
|
928
|
-
|
|
929
|
-
|
|
1708
|
+
default:
|
|
1709
|
+
break;
|
|
1710
|
+
}
|
|
930
1711
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1712
|
+
logger.info('succes maas deploy', {
|
|
1713
|
+
resource,
|
|
1714
|
+
kernelFilesPaths,
|
|
1715
|
+
tftpRoot,
|
|
1716
|
+
tftpSubDir,
|
|
1717
|
+
firmwarePath,
|
|
1718
|
+
etcExports,
|
|
1719
|
+
nfsServerRootPath,
|
|
1720
|
+
nfsConnectStr,
|
|
1721
|
+
});
|
|
1722
|
+
if (process.argv.includes('restart')) {
|
|
1723
|
+
if (fs.existsSync(`node engine-private/r.js`)) shellExec(`node engine-private/r`);
|
|
1724
|
+
shellExec(`node bin/deploy maas dhcp`);
|
|
1725
|
+
shellExec(`sudo chown -R root:root ${tftpRoot}`);
|
|
1726
|
+
shellExec(`sudo sudo chmod 755 ${tftpRoot}`);
|
|
934
1727
|
}
|
|
935
|
-
const
|
|
936
|
-
//
|
|
937
|
-
shellExec(
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1728
|
+
// for (const machine of machines) {
|
|
1729
|
+
// // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
|
|
1730
|
+
// shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${machine.system_id}`, {
|
|
1731
|
+
// silent: true,
|
|
1732
|
+
// });
|
|
1733
|
+
// }
|
|
1734
|
+
// machines = [];
|
|
1735
|
+
|
|
1736
|
+
const monitor = async () => {
|
|
1737
|
+
// discoveries Query observed discoveries.
|
|
1738
|
+
// discovery Read or delete an observed discovery.
|
|
1739
|
+
|
|
1740
|
+
const discoveries = JSON.parse(
|
|
1741
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries read`, {
|
|
1742
|
+
silent: true,
|
|
1743
|
+
stdout: true,
|
|
1744
|
+
}),
|
|
1745
|
+
).filter(
|
|
1746
|
+
(o) => o.ip !== IP_ADDRESS && o.ip !== gatewayip && !machines.find((_o) => _o.mac_address === o.mac_address),
|
|
1747
|
+
);
|
|
942
1748
|
|
|
1749
|
+
// {
|
|
1750
|
+
// "discovery_id": "",
|
|
1751
|
+
// "ip": "192.168.1.189",
|
|
1752
|
+
// "mac_address": "00:00:00:00:00:00",
|
|
1753
|
+
// "last_seen": "2025-05-05T14:17:37.354",
|
|
1754
|
+
// "hostname": null,
|
|
1755
|
+
// "fabric_name": "",
|
|
1756
|
+
// "vid": null,
|
|
1757
|
+
// "mac_organization": "",
|
|
1758
|
+
// "observer": {
|
|
1759
|
+
// "system_id": "",
|
|
1760
|
+
// "hostname": "",
|
|
1761
|
+
// "interface_id": 1,
|
|
1762
|
+
// "interface_name": ""
|
|
1763
|
+
// },
|
|
1764
|
+
// "resource_uri": "/MAAS/api/2.0/discovery/MTkyLjE2OC4xLjE4OSwwMDowMDowMDowMDowMDowMA==/"
|
|
1765
|
+
// },
|
|
1766
|
+
|
|
1767
|
+
for (const discovery of discoveries) {
|
|
1768
|
+
const machine = {
|
|
1769
|
+
architecture: architecture.match('amd') ? 'amd64/generic' : 'arm64/generic',
|
|
1770
|
+
mac_address: discovery.mac_address,
|
|
1771
|
+
hostname: discovery.hostname ?? discovery.mac_organization ?? discovery.domain ?? `generic-host-${s4()}`,
|
|
1772
|
+
// discovery.ip.match(ipaddr)
|
|
1773
|
+
// ? nfsHost
|
|
1774
|
+
// : `unknown-${s4()}`,
|
|
1775
|
+
// description: '',
|
|
1776
|
+
// https://maas.io/docs/reference-power-drivers
|
|
1777
|
+
power_type: 'manual', // manual
|
|
1778
|
+
// power_parameters_power_address: discovery.ip,
|
|
1779
|
+
mac_addresses: discovery.mac_address,
|
|
1780
|
+
};
|
|
1781
|
+
machine.hostname = machine.hostname.replaceAll(' ', '').replaceAll('.', '');
|
|
1782
|
+
|
|
1783
|
+
try {
|
|
1784
|
+
let newMachine = shellExec(
|
|
1785
|
+
`maas ${process.env.MAAS_ADMIN_USERNAME} machines create ${Object.keys(machine)
|
|
1786
|
+
.map((k) => `${k}="${machine[k]}"`)
|
|
1787
|
+
.join(' ')}`,
|
|
1788
|
+
{
|
|
1789
|
+
silent: true,
|
|
1790
|
+
stdout: true,
|
|
1791
|
+
},
|
|
1792
|
+
);
|
|
1793
|
+
newMachine = machineFactory(JSON.parse(newMachine));
|
|
1794
|
+
machines.push(newMachine);
|
|
1795
|
+
console.log(newMachine);
|
|
1796
|
+
shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${newMachine.system_id}`, {
|
|
1797
|
+
silent: true,
|
|
1798
|
+
});
|
|
1799
|
+
} catch (error) {
|
|
1800
|
+
logger.error(error, error.stack);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
// if (discoveries.length > 0) {
|
|
1804
|
+
// shellExec(
|
|
1805
|
+
// `maas ${process.env.MAAS_ADMIN_USERNAME} machines read | jq '.[] | {system_id: .interface_set[0].system_id, hostname, status_name, mac_address: .interface_set[0].mac_address}'`,
|
|
1806
|
+
// );
|
|
1807
|
+
// }
|
|
1808
|
+
await timer(1000);
|
|
1809
|
+
monitor();
|
|
1810
|
+
};
|
|
1811
|
+
// shellExec(`node bin/deploy open-virtual-root ${architecture.match('amd') ? 'amd64' : 'arm64'} ${nfsHost}`);
|
|
1812
|
+
machines = [];
|
|
1813
|
+
shellExec(`node bin/deploy maas clear`);
|
|
1814
|
+
monitor();
|
|
943
1815
|
break;
|
|
944
1816
|
}
|
|
945
1817
|
|
|
946
|
-
case '
|
|
947
|
-
|
|
948
|
-
shellExec(`sudo apt update`);
|
|
949
|
-
shellExec(`sudo apt install openssh-server -y`);
|
|
950
|
-
shellExec(`sudo apt install ssh-askpass`);
|
|
951
|
-
}
|
|
952
|
-
shellExec(`sudo systemctl enable ssh`);
|
|
953
|
-
shellExec(`sudo systemctl restart ssh`);
|
|
954
|
-
shellExec(`sudo systemctl status ssh`);
|
|
955
|
-
// sudo service ssh restart
|
|
956
|
-
shellExec(`ip a`);
|
|
1818
|
+
case 'nfs': {
|
|
1819
|
+
// Daemon RPC NFSv3. ports:
|
|
957
1820
|
|
|
958
|
-
//
|
|
959
|
-
//
|
|
1821
|
+
// 2049 (TCP/UDP) – nfsd standard port.
|
|
1822
|
+
// 111 (TCP/UDP) – rpcbind/portmapper.
|
|
1823
|
+
// 20048 (TCP/UDP) – rpc.mountd.
|
|
1824
|
+
// 32765 (TCP/UDP) – rpc.statd.
|
|
1825
|
+
// 32766 (TCP/UDP) – lockd (NLM).
|
|
960
1826
|
|
|
961
|
-
//
|
|
1827
|
+
// Configure export and permissions:
|
|
1828
|
+
// /etc/exports
|
|
962
1829
|
|
|
963
|
-
//
|
|
1830
|
+
// Configure ports:
|
|
1831
|
+
// /etc/nfs.conf
|
|
964
1832
|
|
|
965
|
-
|
|
1833
|
+
fs.writeFileSync(
|
|
1834
|
+
`/etc/nfs.conf`,
|
|
1835
|
+
`
|
|
1836
|
+
[mountd]
|
|
1837
|
+
port = 20048
|
|
966
1838
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1839
|
+
[statd]
|
|
1840
|
+
port = 32765
|
|
1841
|
+
outgoing-port = 32765
|
|
970
1842
|
|
|
971
|
-
|
|
1843
|
+
[nfsd]
|
|
1844
|
+
rdma=y
|
|
1845
|
+
rdma-port=20049
|
|
972
1846
|
|
|
973
|
-
|
|
974
|
-
|
|
1847
|
+
[lockd]
|
|
1848
|
+
port = 32766
|
|
1849
|
+
udp-port = 32766
|
|
1850
|
+
`,
|
|
1851
|
+
'utf8',
|
|
1852
|
+
);
|
|
975
1853
|
|
|
976
|
-
//
|
|
977
|
-
//
|
|
1854
|
+
// Client users have read-only access to resources and are identified as anonymous on the server.
|
|
1855
|
+
// /share ip-client(ro,all_squash)
|
|
978
1856
|
|
|
979
|
-
//
|
|
980
|
-
//
|
|
981
|
-
// PasswordAuthentication yes
|
|
1857
|
+
// Client users can modify resources and keep their UID on the server. Only root is identified as anonymous.
|
|
1858
|
+
// /share ip-client(rw)
|
|
982
1859
|
|
|
983
|
-
//
|
|
984
|
-
//
|
|
1860
|
+
// Users on client workstation 1 can modify resources, while those on client workstation 2 have read-only access.
|
|
1861
|
+
// UIDs are kept on the server, and only root is identified as anonymous.
|
|
1862
|
+
// /share ip-client1(rw) ip-client2(ro)
|
|
985
1863
|
|
|
986
|
-
//
|
|
987
|
-
//
|
|
1864
|
+
// Client1 users can modify resources. Their UID is changed to 1001 and their GID to 100 on the server.
|
|
1865
|
+
// /share ip-client(rw,all_squash,anonuid=1001,anongid=100)
|
|
988
1866
|
|
|
989
|
-
//
|
|
990
|
-
//
|
|
1867
|
+
// sudo dnf install nfs-utils
|
|
1868
|
+
// sudo systemctl enable --now rpcbind // RPC map service
|
|
1869
|
+
// sudo systemctl enable --now nfs-server // nfs domains nfsd
|
|
991
1870
|
|
|
992
|
-
//
|
|
993
|
-
//
|
|
1871
|
+
// Update exports:
|
|
1872
|
+
// shellExec(`sudo exportfs -a -r`);
|
|
1873
|
+
// shellExec(`sudo exportfs -v`);
|
|
994
1874
|
|
|
995
|
-
//
|
|
996
|
-
|
|
1875
|
+
// Active nfs
|
|
1876
|
+
shellExec(`sudo exportfs -s`);
|
|
997
1877
|
|
|
998
|
-
|
|
999
|
-
// ssh-add -D
|
|
1878
|
+
shellExec(`sudo exportfs -rav`);
|
|
1000
1879
|
|
|
1001
|
-
//
|
|
1880
|
+
// Rocky enable virt_use_nfs
|
|
1881
|
+
// sudo setsebool -P virt_use_nfs 1
|
|
1002
1882
|
|
|
1003
|
-
//
|
|
1004
|
-
//
|
|
1005
|
-
// ssh-copy-id "user@hostname.example.com -p <port-number>"
|
|
1883
|
+
// Disable share:
|
|
1884
|
+
// sudo exportfs -u <client-ip>:${process.env.NFS_EXPORT_PATH}/rpi4mb
|
|
1006
1885
|
|
|
1886
|
+
// Nfs client:
|
|
1887
|
+
// mount -t nfs <server-ip>:/server-mnt /mnt
|
|
1888
|
+
// umount /mnt
|
|
1889
|
+
|
|
1890
|
+
shellExec(`sudo systemctl restart nfs-server`);
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
case 'update-virtual-root': {
|
|
1894
|
+
dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
|
|
1895
|
+
const IP_ADDRESS = getLocalIPv4Address();
|
|
1896
|
+
const architecture = process.argv[3];
|
|
1897
|
+
const host = process.argv[4];
|
|
1898
|
+
const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
|
|
1899
|
+
const ipaddr = process.env.RPI4_IP;
|
|
1900
|
+
await updateVirtualRoot({
|
|
1901
|
+
IP_ADDRESS,
|
|
1902
|
+
architecture,
|
|
1903
|
+
host,
|
|
1904
|
+
nfsHostPath,
|
|
1905
|
+
ipaddr,
|
|
1906
|
+
});
|
|
1007
1907
|
break;
|
|
1008
1908
|
}
|
|
1909
|
+
case 'open-virtual-root': {
|
|
1910
|
+
dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
|
|
1911
|
+
const IP_ADDRESS = getLocalIPv4Address();
|
|
1912
|
+
const architecture = process.argv[3];
|
|
1913
|
+
const host = process.argv[4];
|
|
1914
|
+
const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
|
|
1915
|
+
shellExec(`sudo dnf install -y iptables-legacy`);
|
|
1916
|
+
shellExec(`sudo dnf install -y debootstrap`);
|
|
1917
|
+
shellExec(`sudo dnf install kernel-modules-extra-$(uname -r)`);
|
|
1918
|
+
switch (architecture) {
|
|
1919
|
+
case 'arm64':
|
|
1920
|
+
shellExec(`sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes`);
|
|
1009
1921
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1922
|
+
break;
|
|
1923
|
+
|
|
1924
|
+
default:
|
|
1925
|
+
break;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
shellExec(`sudo modprobe binfmt_misc`);
|
|
1929
|
+
shellExec(`sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc`);
|
|
1930
|
+
|
|
1931
|
+
if (process.argv.includes('build')) {
|
|
1932
|
+
// shellExec(`depmod -a`);
|
|
1933
|
+
shellExec(`mkdir -p ${nfsHostPath}`);
|
|
1934
|
+
let cmd;
|
|
1935
|
+
switch (host) {
|
|
1936
|
+
case 'rpi4mb':
|
|
1937
|
+
shellExec(`sudo rm -rf ${nfsHostPath}/*`);
|
|
1938
|
+
shellExec(`sudo chown -R root:root ${nfsHostPath}`);
|
|
1939
|
+
cmd = [
|
|
1940
|
+
`sudo debootstrap`,
|
|
1941
|
+
`--arch=arm64`,
|
|
1942
|
+
`--variant=minbase`,
|
|
1943
|
+
`--foreign`, // arm64 on amd64
|
|
1944
|
+
`noble`,
|
|
1945
|
+
nfsHostPath,
|
|
1946
|
+
`http://ports.ubuntu.com/ubuntu-ports/`,
|
|
1947
|
+
];
|
|
1948
|
+
break;
|
|
1949
|
+
|
|
1950
|
+
default:
|
|
1951
|
+
break;
|
|
1952
|
+
}
|
|
1953
|
+
shellExec(cmd.join(' '));
|
|
1954
|
+
|
|
1955
|
+
shellExec(`sudo podman create --name extract multiarch/qemu-user-static`);
|
|
1956
|
+
shellExec(`podman ps -a`);
|
|
1957
|
+
shellExec(`sudo podman cp extract:/usr/bin/qemu-aarch64-static ${nfsHostPath}/usr/bin/`);
|
|
1958
|
+
shellExec(`sudo podman rm extract`);
|
|
1959
|
+
shellExec(`podman ps -a`);
|
|
1960
|
+
|
|
1961
|
+
switch (host) {
|
|
1962
|
+
case 'rpi4mb':
|
|
1963
|
+
shellExec(`file ${nfsHostPath}/bin/bash`); // expected: ELF 64-bit LSB pie executable, ARM aarch64 …
|
|
1964
|
+
break;
|
|
1965
|
+
|
|
1966
|
+
default:
|
|
1967
|
+
break;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
|
|
1971
|
+
/debootstrap/debootstrap --second-stage
|
|
1972
|
+
EOF`);
|
|
1973
|
+
}
|
|
1974
|
+
if (process.argv.includes('mount')) {
|
|
1975
|
+
shellExec(`sudo mount --bind /proc ${nfsHostPath}/proc`);
|
|
1976
|
+
shellExec(`sudo mount --bind /sys ${nfsHostPath}/sys`);
|
|
1977
|
+
shellExec(`sudo mount --rbind /dev ${nfsHostPath}/dev`);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
if (process.argv.includes('build')) {
|
|
1981
|
+
switch (host) {
|
|
1982
|
+
case 'rpi4mb':
|
|
1983
|
+
const ipaddr = process.env.RPI4_IP;
|
|
1984
|
+
|
|
1985
|
+
await updateVirtualRoot({
|
|
1986
|
+
IP_ADDRESS,
|
|
1987
|
+
architecture,
|
|
1988
|
+
host,
|
|
1989
|
+
nfsHostPath,
|
|
1990
|
+
ipaddr,
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
break;
|
|
1994
|
+
|
|
1995
|
+
default:
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1015
1998
|
}
|
|
1016
|
-
|
|
1999
|
+
// if (process.argv.includes('mount')) {
|
|
2000
|
+
// shellExec(`sudo mount --bind /lib/modules ${nfsHostPath}/lib/modules`);
|
|
2001
|
+
// }
|
|
1017
2002
|
|
|
1018
2003
|
break;
|
|
1019
2004
|
}
|
|
1020
2005
|
|
|
1021
|
-
case '
|
|
1022
|
-
|
|
2006
|
+
case 'close-virtual-root': {
|
|
2007
|
+
const architecture = process.argv[3];
|
|
2008
|
+
const host = process.argv[4];
|
|
2009
|
+
const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
|
|
2010
|
+
shellExec(`sudo umount ${nfsHostPath}/proc`);
|
|
2011
|
+
shellExec(`sudo umount ${nfsHostPath}/sys`);
|
|
2012
|
+
shellExec(`sudo umount ${nfsHostPath}/dev`);
|
|
2013
|
+
// shellExec(`sudo umount ${nfsHostPath}/lib/modules`);
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
case 'mount': {
|
|
2018
|
+
const mounts = shellExec(`mount`).split(`\n`);
|
|
2019
|
+
console.table(
|
|
2020
|
+
mounts
|
|
2021
|
+
.filter((l) => l.trim())
|
|
2022
|
+
.map(
|
|
2023
|
+
(o) => (
|
|
2024
|
+
(o = o.split(' ')),
|
|
2025
|
+
{
|
|
2026
|
+
path: o[2],
|
|
2027
|
+
type: o[4],
|
|
2028
|
+
permissions: o[5],
|
|
2029
|
+
}
|
|
2030
|
+
),
|
|
2031
|
+
),
|
|
2032
|
+
);
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
case 'create-ports': {
|
|
2037
|
+
const cmd = [];
|
|
2038
|
+
const ipaddr = getLocalIPv4Address();
|
|
2039
|
+
for (const port of ['5240']) {
|
|
2040
|
+
const name = 'maas';
|
|
2041
|
+
cmd.push(`${name}:${port}-${port}:${ipaddr}`);
|
|
2042
|
+
}
|
|
2043
|
+
pbcopy(`node engine-private/r create-port ${cmd}`);
|
|
2044
|
+
break;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
case 'maas-ports': {
|
|
2048
|
+
// Configure firewall:
|
|
2049
|
+
|
|
2050
|
+
// systemctl stop firewalld
|
|
2051
|
+
// systemctl mask firewalld
|
|
2052
|
+
|
|
2053
|
+
// ufw disable
|
|
2054
|
+
// ufw enable
|
|
2055
|
+
|
|
2056
|
+
// sudo snap install ufw
|
|
2057
|
+
// const ports = ['80', '443', '22', '3000-3100'];
|
|
2058
|
+
const ports = [
|
|
2059
|
+
'43',
|
|
2060
|
+
'53',
|
|
2061
|
+
'60',
|
|
2062
|
+
'66',
|
|
2063
|
+
'67',
|
|
2064
|
+
'69',
|
|
2065
|
+
'4011',
|
|
2066
|
+
'111',
|
|
2067
|
+
'2049',
|
|
2068
|
+
'20048',
|
|
2069
|
+
'20049',
|
|
2070
|
+
'32765',
|
|
2071
|
+
'32766',
|
|
2072
|
+
'5248',
|
|
2073
|
+
'5240',
|
|
2074
|
+
];
|
|
2075
|
+
for (const port of ports) {
|
|
2076
|
+
shellExec(`ufw allow ${port}/tcp`);
|
|
2077
|
+
shellExec(`ufw allow ${port}/udp`);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
shellExec(`sudo systemctl mask firewalld`);
|
|
2081
|
+
|
|
2082
|
+
break;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
case 'iptables': {
|
|
2086
|
+
shellExec(`sudo systemctl enable nftables`);
|
|
2087
|
+
shellExec(`sudo systemctl restart nftables`);
|
|
2088
|
+
|
|
2089
|
+
shellExec(`sudo tee /etc/nftables.conf <<EOF
|
|
2090
|
+
table inet filter {
|
|
2091
|
+
chain input {
|
|
2092
|
+
type filter hook input priority 0;
|
|
2093
|
+
policy drop;
|
|
2094
|
+
tcp dport 22 accept
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
EOF`);
|
|
2098
|
+
shellExec(`sudo nft -f /etc/nftables.conf`);
|
|
2099
|
+
|
|
2100
|
+
// sudo systemctl stop nftables
|
|
2101
|
+
// sudo systemctl disable nftables
|
|
2102
|
+
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
case 'rpi4': {
|
|
2107
|
+
// Rpi4 Run Bootloader:
|
|
2108
|
+
|
|
2109
|
+
// 1) create boot.conf
|
|
2110
|
+
|
|
2111
|
+
// 2) Run lite RPiOs from rpi-imager
|
|
2112
|
+
// with boot.conf files in root disk path
|
|
2113
|
+
|
|
2114
|
+
// 3) cd /boot/firmware && sudo rpi-eeprom-config --apply boot.conf
|
|
2115
|
+
|
|
2116
|
+
// 4) sudo reboot
|
|
2117
|
+
|
|
2118
|
+
// 5) check: 'vcgencmd bootloader_version'
|
|
2119
|
+
// 6) check: 'vcgencmd bootloader_config'
|
|
2120
|
+
|
|
2121
|
+
// 7) shutdown and restart without sd card
|
|
2122
|
+
|
|
2123
|
+
// sudo apt update
|
|
2124
|
+
// sudo apt install git
|
|
2125
|
+
|
|
2126
|
+
break;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
case 'blue': {
|
|
2130
|
+
// lsusb | grep blue -i
|
|
2131
|
+
// rfkill list
|
|
2132
|
+
// sudo service bluetooth start
|
|
2133
|
+
// bluetoothctl show
|
|
2134
|
+
// sudo rfkill unblock bluetooth
|
|
2135
|
+
// dmesg | grep -i bluetooth
|
|
2136
|
+
// journalctl -u bluetooth -f
|
|
2137
|
+
// sudo dnf update bluez bluez-libs bluez-utils
|
|
2138
|
+
// sudo rmmod btusb
|
|
2139
|
+
// sudo modprobe btusb
|
|
1023
2140
|
break;
|
|
1024
2141
|
}
|
|
1025
2142
|
|
|
1026
2143
|
default:
|
|
1027
2144
|
break;
|
|
2145
|
+
|
|
2146
|
+
case 'fastapi': {
|
|
2147
|
+
// https://github.com/NonsoEchendu/full-stack-fastapi-project
|
|
2148
|
+
// https://github.com/fastapi/full-stack-fastapi-template
|
|
2149
|
+
const path = `../full-stack-fastapi-template`;
|
|
2150
|
+
if (process.argv.includes('env')) {
|
|
2151
|
+
const password = fs.readFileSync(`/home/dd/engine/engine-private/postgresql-password`, 'utf8');
|
|
2152
|
+
|
|
2153
|
+
fs.writeFileSync(
|
|
2154
|
+
`${path}/.env`,
|
|
2155
|
+
fs
|
|
2156
|
+
.readFileSync(`${path}/.env`, 'utf8')
|
|
2157
|
+
.replace(`FIRST_SUPERUSER=admin@example.com`, `FIRST_SUPERUSER=development@underpost.net`)
|
|
2158
|
+
.replace(`FIRST_SUPERUSER_PASSWORD=changethis`, `FIRST_SUPERUSER_PASSWORD=${password}`)
|
|
2159
|
+
.replace(`SECRET_KEY=changethis`, `SECRET_KEY=${password}`)
|
|
2160
|
+
.replace(`POSTGRES_DB=app`, `POSTGRES_DB=postgresdb`)
|
|
2161
|
+
.replace(`POSTGRES_USER=postgres`, `POSTGRES_USER=admin`)
|
|
2162
|
+
.replace(`POSTGRES_PASSWORD=changethis`, `POSTGRES_PASSWORD=${password}`),
|
|
2163
|
+
'utf8',
|
|
2164
|
+
);
|
|
2165
|
+
fs.writeFileSync(
|
|
2166
|
+
`${path}/backend/app/core/db.py`,
|
|
2167
|
+
fs
|
|
2168
|
+
.readFileSync(`${path}/backend/app/core/db.py`, 'utf8')
|
|
2169
|
+
.replace(` # from sqlmodel import SQLModel`, ` from sqlmodel import SQLModel`)
|
|
2170
|
+
.replace(` # SQLModel.metadata.create_all(engine)`, ` SQLModel.metadata.create_all(engine)`),
|
|
2171
|
+
|
|
2172
|
+
'utf8',
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
if (process.argv.includes('build-back')) {
|
|
2176
|
+
const imageName = `fastapi-backend:latest`;
|
|
2177
|
+
shellExec(`sudo podman pull docker.io/library/python:3.10`);
|
|
2178
|
+
shellExec(`sudo podman pull ghcr.io/astral-sh/uv:0.5.11`);
|
|
2179
|
+
shellExec(`sudo rm -rf ${path}/${imageName.replace(':', '_')}.tar`);
|
|
2180
|
+
const args = [
|
|
2181
|
+
`node bin dockerfile-image-build --path ${path}/backend/`,
|
|
2182
|
+
`--image-name=${imageName} --image-path=${path}`,
|
|
2183
|
+
`--podman-save --kind-load --no-cache`,
|
|
2184
|
+
];
|
|
2185
|
+
shellExec(args.join(' '));
|
|
2186
|
+
}
|
|
2187
|
+
if (process.argv.includes('build-front')) {
|
|
2188
|
+
const imageName = `fastapi-frontend:latest`;
|
|
2189
|
+
shellExec(`sudo podman pull docker.io/library/node:20`);
|
|
2190
|
+
shellExec(`sudo podman pull docker.io/library/nginx:1`);
|
|
2191
|
+
shellExec(`sudo rm -rf ${path}/${imageName.replace(':', '_')}.tar`);
|
|
2192
|
+
const args = [
|
|
2193
|
+
`node bin dockerfile-image-build --path ${path}/frontend/`,
|
|
2194
|
+
`--image-name=${imageName} --image-path=${path}`,
|
|
2195
|
+
`--podman-save --kind-load --no-cache`,
|
|
2196
|
+
];
|
|
2197
|
+
shellExec(args.join(' '));
|
|
2198
|
+
}
|
|
2199
|
+
if (process.argv.includes('build') || process.argv.includes('secret')) {
|
|
2200
|
+
{
|
|
2201
|
+
const secretSelector = `fastapi-postgres-credentials`;
|
|
2202
|
+
shellExec(`sudo kubectl delete secret ${secretSelector}`);
|
|
2203
|
+
shellExec(
|
|
2204
|
+
`sudo kubectl create secret generic ${secretSelector}` +
|
|
2205
|
+
` --from-literal=POSTGRES_DB=postgresdb` +
|
|
2206
|
+
` --from-literal=POSTGRES_USER=admin` +
|
|
2207
|
+
` --from-file=POSTGRES_PASSWORD=/home/dd/engine/engine-private/postgresql-password`,
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
{
|
|
2211
|
+
const secretSelector = `fastapi-backend-config-secret`;
|
|
2212
|
+
shellExec(`sudo kubectl delete secret ${secretSelector}`);
|
|
2213
|
+
shellExec(
|
|
2214
|
+
`sudo kubectl create secret generic ${secretSelector}` +
|
|
2215
|
+
` --from-file=SECRET_KEY=/home/dd/engine/engine-private/postgresql-password` +
|
|
2216
|
+
` --from-literal=FIRST_SUPERUSER=development@underpost.net` +
|
|
2217
|
+
` --from-file=FIRST_SUPERUSER_PASSWORD=/home/dd/engine/engine-private/postgresql-password`,
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
if (process.argv.includes('run-back')) {
|
|
2222
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/fastapi/backend-deployment.yml`);
|
|
2223
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/fastapi/backend-service.yml`);
|
|
2224
|
+
}
|
|
2225
|
+
if (process.argv.includes('run-front')) {
|
|
2226
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/fastapi/frontend-deployment.yml`);
|
|
2227
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/fastapi/frontend-service.yml`);
|
|
2228
|
+
}
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
case 'conda': {
|
|
2233
|
+
shellExec(
|
|
2234
|
+
`export PATH="/root/miniconda3/bin:$PATH" && conda init && conda config --set auto_activate_base false`,
|
|
2235
|
+
);
|
|
2236
|
+
shellExec(`conda env list`);
|
|
2237
|
+
break;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
case 'kafka': {
|
|
2241
|
+
// https://medium.com/@martin.hodges/deploying-kafka-on-a-kind-kubernetes-cluster-for-development-and-testing-purposes-ed7adefe03cb
|
|
2242
|
+
const imageName = `doughgle/kafka-kraft`;
|
|
2243
|
+
shellExec(`docker pull ${imageName}`);
|
|
2244
|
+
shellExec(`kind load docker-image ${imageName}`);
|
|
2245
|
+
shellExec(`kubectl create namespace kafka`);
|
|
2246
|
+
shellExec(`kubectl apply -f ./manifests/deployment/kafka/deployment.yaml`);
|
|
2247
|
+
// kubectl logs kafka-0 -n kafka | grep STARTED
|
|
2248
|
+
// kubectl logs kafka-1 -n kafka | grep STARTED
|
|
2249
|
+
// kubectl logs kafka-2 -n kafka | grep STARTED
|
|
2250
|
+
|
|
2251
|
+
// kafka-topics.sh --create --topic my-topic --bootstrap-server kafka-svc:9092
|
|
2252
|
+
// kafka-topics.sh --list --topic my-topic --bootstrap-server kafka-svc:9092
|
|
2253
|
+
// kafka-topics.sh --delete --topic my-topic --bootstrap-server kafka-svc:9092
|
|
2254
|
+
|
|
2255
|
+
// kafka-console-producer.sh --bootstrap-server kafka-svc:9092 --topic my-topic
|
|
2256
|
+
// kafka-console-consumer.sh --bootstrap-server kafka-svc:9092 --topic my-topic
|
|
2257
|
+
break;
|
|
2258
|
+
}
|
|
1028
2259
|
}
|
|
1029
2260
|
} catch (error) {
|
|
1030
2261
|
logger.error(error, error.stack);
|