underpost 2.8.79 → 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.
Files changed (45) hide show
  1. package/.github/workflows/ghpkg.yml +23 -21
  2. package/.github/workflows/npmpkg.yml +16 -11
  3. package/.github/workflows/pwa-microservices-template.page.yml +12 -3
  4. package/.github/workflows/pwa-microservices-template.test.yml +20 -17
  5. package/.vscode/extensions.json +1 -2
  6. package/.vscode/settings.json +3 -0
  7. package/Dockerfile +14 -33
  8. package/README.md +25 -24
  9. package/bin/db.js +1 -0
  10. package/bin/deploy.js +88 -796
  11. package/bin/vs.js +10 -3
  12. package/cli.md +340 -207
  13. package/conf.js +4 -0
  14. package/docker-compose.yml +1 -1
  15. package/manifests/deployment/dd-template-development/deployment.yaml +167 -0
  16. package/manifests/deployment/dd-template-development/proxy.yaml +46 -0
  17. package/manifests/lxd/lxd-admin-profile.yaml +1 -0
  18. package/manifests/lxd/lxd-preseed.yaml +9 -37
  19. package/manifests/lxd/underpost-setup.sh +98 -81
  20. package/manifests/maas/device-scan.sh +43 -0
  21. package/manifests/maas/lxd-preseed.yaml +32 -0
  22. package/manifests/maas/maas-setup.sh +120 -0
  23. package/manifests/maas/nat-iptables.sh +26 -0
  24. package/manifests/mariadb/statefulset.yaml +2 -1
  25. package/manifests/mariadb/storage-class.yaml +10 -0
  26. package/manifests/mongodb-4.4/service-deployment.yaml +2 -2
  27. package/manifests/valkey/service.yaml +3 -9
  28. package/manifests/valkey/statefulset.yaml +10 -12
  29. package/package.json +1 -1
  30. package/src/cli/baremetal.js +1248 -0
  31. package/src/cli/cloud-init.js +528 -0
  32. package/src/cli/cluster.js +424 -240
  33. package/src/cli/deploy.js +27 -3
  34. package/src/cli/env.js +2 -2
  35. package/src/cli/image.js +57 -9
  36. package/src/cli/index.js +252 -231
  37. package/src/cli/lxd.js +314 -81
  38. package/src/index.js +33 -15
  39. package/src/runtime/lampp/Dockerfile +41 -47
  40. package/src/server/conf.js +58 -0
  41. package/src/server/logger.js +3 -3
  42. package/src/server/runtime.js +1 -6
  43. package/src/server/ssl.js +1 -12
  44. package/src/server/valkey.js +3 -3
  45. package/supervisord-openssh-server.conf +0 -5
package/bin/deploy.js CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  setUpProxyMaintenanceServer,
28
28
  writeEnv,
29
29
  getUnderpostRootPath,
30
+ buildCliDoc,
30
31
  } from '../src/server/conf.js';
31
32
  import { buildClient } from '../src/server/client-build.js';
32
33
  import { range, s4, setPad, timer, uniqueArray } from '../src/client/components/core/CommonJs.js';
@@ -37,10 +38,10 @@ import { JSONweb } from '../src/server/client-formatted.js';
37
38
 
38
39
  import { Xampp } from '../src/runtime/xampp/Xampp.js';
39
40
  import { ejs } from '../src/server/json-schema.js';
40
- import { buildCliDoc } from '../src/cli/index.js';
41
41
  import { getLocalIPv4Address, ip } from '../src/server/dns.js';
42
42
  import { Downloader } from '../src/server/downloader.js';
43
43
  import colors from 'colors';
44
+ import { program } from '../src/cli/index.js';
44
45
 
45
46
  colors.enable();
46
47
 
@@ -50,82 +51,6 @@ logger.info('argv', process.argv);
50
51
 
51
52
  const [exe, dir, operator] = process.argv;
52
53
 
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
-
129
54
  try {
130
55
  switch (operator) {
131
56
  case 'save':
@@ -797,6 +722,13 @@ try {
797
722
  .replaceAll(`engine.version: '${version}'`, `engine.version: '${newVersion}'`),
798
723
  'utf8',
799
724
  );
725
+ fs.writeFileSync(
726
+ `./manifests/deployment/dd-template-development/deployment.yaml`,
727
+ fs
728
+ .readFileSync(`./manifests/deployment/dd-template-development/deployment.yaml`, 'utf8')
729
+ .replaceAll(`underpost:v${version}`, `underpost:v${newVersion}`),
730
+ 'utf8',
731
+ );
800
732
 
801
733
  if (fs.existsSync(`./.github/workflows/docker-image.yml`))
802
734
  fs.writeFileSync(
@@ -921,6 +853,16 @@ ${shellExec(`git log | grep Author: | sort -u`, { stdout: true }).split(`\n`).jo
921
853
  };
922
854
  DefaultConf.server[host][path].apiBaseProxyPath = '/';
923
855
  DefaultConf.server[host][path].apiBaseHost = 'www.nexodev.org';
856
+ } else if (confName === 'template') {
857
+ const host = 'default.net';
858
+ const path = '/';
859
+ DefaultConf.server[host][path].valkey = {
860
+ port: 6379,
861
+ host: 'valkey-service.default.svc.cluster.local',
862
+ };
863
+ // mongodb-0.mongodb-service
864
+ DefaultConf.server[host][path].db.host = 'mongodb://mongodb-service:27017';
865
+ confName = '';
924
866
  } else if (confName) {
925
867
  DefaultConf.client = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.client.json`, 'utf8'));
926
868
  DefaultConf.server = JSON.parse(fs.readFileSync(`./engine-private/conf/${confName}/conf.server.json`, 'utf8'));
@@ -1141,7 +1083,7 @@ EOF`);
1141
1083
  }
1142
1084
 
1143
1085
  case 'cli-docs': {
1144
- buildCliDoc();
1086
+ buildCliDoc(program);
1145
1087
  break;
1146
1088
  }
1147
1089
 
@@ -1193,12 +1135,63 @@ EOF`);
1193
1135
  break;
1194
1136
  }
1195
1137
 
1138
+ case 'postgresql-17': {
1139
+ if (process.argv.includes('install')) {
1140
+ shellExec(`sudo dnf module reset postgresql -y`);
1141
+ shellExec(`sudo dnf -qy module disable postgresql`);
1142
+ shellExec(
1143
+ `sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm`,
1144
+ );
1145
+ shellExec(`sudo dnf -qy module disable postgresql`);
1146
+ shellExec(`sudo dnf install -y postgresql17 postgresql17-server postgresql17-contrib`);
1147
+
1148
+ shellExec(`sudo /usr/pgsql-17/bin/postgresql-17-setup initdb`);
1149
+ }
1150
+ if (process.argv.includes('uninstall')) {
1151
+ shellExec(`sudo systemctl stop postgresql-17`);
1152
+ shellExec(`sudo systemctl disable postgresql-17`);
1153
+
1154
+ // Remove PostgreSQL 17 packages and repo
1155
+ shellExec(`sudo dnf remove -y postgresql17 postgresql17-server postgresql17-contrib`);
1156
+ shellExec(`sudo rpm -e pgdg-redhat-repo-$(rpm -q pgdg-redhat-repo --qf '%{VERSION}-%{RELEASE}') || true`);
1157
+ shellExec(`sudo rm -f /etc/yum.repos.d/pgdg-redhat-*.repo`);
1158
+
1159
+ // Clean up data, logs, config, and the postgres user
1160
+ shellExec(`sudo rm -rf /var/lib/pgsql/17 /var/log/pgsql`);
1161
+ shellExec(`sudo rm -rf /etc/postgresql`);
1162
+ } else {
1163
+ shellExec(`sudo systemctl enable postgresql-17`);
1164
+ shellExec(`sudo systemctl start postgresql-17`);
1165
+ }
1166
+ break;
1167
+ }
1168
+
1196
1169
  case 'postgresql-14': {
1197
- shellExec(`sudo /usr/pgsql-14/bin/postgresql-14-setup initdb`);
1198
- shellExec(`sudo systemctl start postgresql-14`);
1199
- shellExec(`sudo systemctl enable postgresql-14`);
1200
- shellExec(`sudo systemctl status postgresql-14`);
1201
- // sudo dnf install postgresql14-contrib
1170
+ if (process.argv.includes('install')) {
1171
+ shellExec(`sudo dnf module reset postgresql -y`);
1172
+ shellExec(`sudo dnf -qy module disable postgresql`);
1173
+
1174
+ shellExec(`sudo systemctl stop postgresql-14`);
1175
+ shellExec(`sudo systemctl disable postgresql-14`);
1176
+
1177
+ shellExec(`sudo dnf remove -y postgresql14 postgresql14-server postgresql14-contrib`);
1178
+ shellExec(`sudo rm -rf /var/lib/pgsql`);
1179
+
1180
+ shellExec(`sudo dnf install postgresql14 postgresql14-server postgresql14-contrib -y`);
1181
+ }
1182
+ if (process.argv.includes('uninstall')) {
1183
+ shellExec(`sudo systemctl stop postgresql-14`);
1184
+ shellExec(`sudo systemctl disable postgresql-14`);
1185
+ shellExec(`sudo dnf remove -y postgresql14 postgresql14-server postgresql14-contrib`);
1186
+ shellExec(`sudo rm -rf /var/lib/pgsql /var/log/pgsql /etc/postgresql`);
1187
+ } else {
1188
+ shellExec(`sudo /usr/pgsql-14/bin/postgresql-14-setup initdb`);
1189
+ shellExec(`sudo systemctl start postgresql-14`);
1190
+ shellExec(`sudo systemctl enable postgresql-14`);
1191
+ shellExec(`sudo systemctl status postgresql-14`);
1192
+ // sudo dnf install postgresql14-contrib
1193
+ }
1194
+
1202
1195
  break;
1203
1196
  }
1204
1197
 
@@ -1234,592 +1227,6 @@ EOF`);
1234
1227
  break;
1235
1228
  }
1236
1229
 
1237
- case 'maas': {
1238
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
1239
- const IP_ADDRESS = getLocalIPv4Address();
1240
- const serverip = IP_ADDRESS;
1241
- const tftpRoot = process.env.TFTP_ROOT;
1242
- const ipaddr = process.env.RPI4_IP;
1243
- const netmask = process.env.NETMASK;
1244
- const gatewayip = process.env.GATEWAY_IP;
1245
-
1246
- let resources;
1247
- try {
1248
- resources = JSON.parse(
1249
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resources read`, {
1250
- silent: true,
1251
- stdout: true,
1252
- }),
1253
- ).map((o) => ({
1254
- id: o.id,
1255
- name: o.name,
1256
- architecture: o.architecture,
1257
- }));
1258
- } catch (error) {
1259
- logger.error(error);
1260
- }
1261
-
1262
- const machineFactory = (m) => ({
1263
- system_id: m.interface_set[0].system_id,
1264
- mac_address: m.interface_set[0].mac_address,
1265
- hostname: m.hostname,
1266
- status_name: m.status_name,
1267
- });
1268
-
1269
- let machines;
1270
- try {
1271
- machines = JSON.parse(
1272
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machines read`, {
1273
- stdout: true,
1274
- silent: true,
1275
- }),
1276
- ).map((m) => machineFactory(m));
1277
- } catch (error) {
1278
- logger.error(error);
1279
- }
1280
-
1281
- if (process.argv.includes('db')) {
1282
- // DROP, ALTER, CREATE, WITH ENCRYPTED
1283
- // sudo -u <user> -h <host> psql <db-name>
1284
- shellExec(`DB_PG_MAAS_NAME=${process.env.DB_PG_MAAS_NAME}`);
1285
- shellExec(`DB_PG_MAAS_PASS=${process.env.DB_PG_MAAS_PASS}`);
1286
- shellExec(`DB_PG_MAAS_USER=${process.env.DB_PG_MAAS_USER}`);
1287
- shellExec(`DB_PG_MAAS_HOST=${process.env.DB_PG_MAAS_HOST}`);
1288
- shellExec(
1289
- `sudo -i -u postgres psql -c "CREATE USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
1290
- );
1291
- shellExec(
1292
- `sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
1293
- );
1294
- const actions = ['LOGIN', 'SUPERUSER', 'INHERIT', 'CREATEDB', 'CREATEROLE', 'REPLICATION'];
1295
- shellExec(`sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ${actions.join(' ')}"`);
1296
- shellExec(`sudo -i -u postgres psql -c "\\du"`);
1297
-
1298
- shellExec(`sudo -i -u postgres createdb -O "$DB_PG_MAAS_USER" "$DB_PG_MAAS_NAME"`);
1299
-
1300
- shellExec(`sudo -i -u postgres psql -c "\\l"`);
1301
- }
1302
-
1303
- if (process.argv.includes('ls')) {
1304
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-sources read`);
1305
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} commissioning-scripts read`);
1306
- // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-source-selections read 60`);
1307
- console.table(resources);
1308
- console.table(machines);
1309
- process.exit(0);
1310
- }
1311
-
1312
- // TODO: - Disable maas proxy (egress forwarding to public dns)
1313
- // - Configure maas dns forwarding ${process.env.MAAS_DNS}
1314
- // - Enable DNSSEC validation of upstream zones: Automatic (use default root key)
1315
-
1316
- if (process.argv.includes('clear')) {
1317
- for (const machine of machines) {
1318
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
1319
- }
1320
- // machines = [];
1321
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries clear all=true`);
1322
- if (process.argv.includes('force')) {
1323
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries scan force=true`);
1324
- }
1325
- process.exit(0);
1326
- }
1327
- if (process.argv.includes('grub-arm64')) {
1328
- shellExec(`sudo dnf install grub2-efi-aa64-modules`);
1329
- shellExec(`sudo dnf install grub2-efi-x64-modules`);
1330
- // sudo grub2-mknetdir --net-directory=${tftpRoot} --subdir=/boot/grub --module-path=/usr/lib/grub/arm64-efi arm64-efi
1331
- process.exit(0);
1332
- }
1333
-
1334
- if (process.argv.includes('psql')) {
1335
- const cmd = `psql -U ${process.env.DB_PG_MAAS_USER} -h ${process.env.DB_PG_MAAS_HOST} -W ${process.env.DB_PG_MAAS_NAME}`;
1336
- pbcopy(cmd);
1337
- process.exit(0);
1338
- }
1339
- if (process.argv.includes('logs')) {
1340
- shellExec(`maas status`);
1341
- const cmd = `journalctl -f -t dhcpd -u snap.maas.pebble.service`;
1342
- pbcopy(cmd);
1343
- process.exit(0);
1344
- }
1345
- if (process.argv.includes('reset')) {
1346
- // shellExec(
1347
- // `maas init region+rack --database-uri "postgres://$DB_PG_MAAS_USER:$DB_PG_MAAS_PASS@$DB_PG_MAAS_HOST/$DB_PG_MAAS_NAME"` +
1348
- // ` --maas-url http://${IP_ADDRESS}:5240/MAAS`,
1349
- // );
1350
- const cmd =
1351
- `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}"` +
1352
- ` --maas-url http://${IP_ADDRESS}:5240/MAAS`;
1353
- pbcopy(cmd);
1354
- process.exit(0);
1355
- }
1356
- if (process.argv.includes('dhcp')) {
1357
- const snippets = JSON.parse(
1358
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} dhcpsnippets read`, {
1359
- stdout: true,
1360
- silent: true,
1361
- disableLog: true,
1362
- }),
1363
- );
1364
- for (const snippet of snippets) {
1365
- switch (snippet.name) {
1366
- case 'arm64':
1367
- snippet.value = snippet.value.split(`\n`);
1368
- snippet.value[1] = ` filename "http://${IP_ADDRESS}:5248/images/bootloaders/uefi/arm64/grubaa64.efi";`;
1369
- snippet.value[5] = ` filename "http://${IP_ADDRESS}:5248/images/bootloaders/uefi/arm64/grubaa64.efi";`;
1370
- snippet.value = snippet.value.join(`\n`);
1371
- shellExec(
1372
- `maas ${process.env.MAAS_ADMIN_USERNAME} dhcpsnippet update ${snippet.name} value='${snippet.value}'`,
1373
- );
1374
- break;
1375
-
1376
- default:
1377
- break;
1378
- }
1379
- }
1380
-
1381
- console.log(snippets);
1382
-
1383
- process.exit(0);
1384
- }
1385
- // shellExec(`MAAS_ADMIN_USERNAME=${process.env.MAAS_ADMIN_USERNAME}`);
1386
- // shellExec(`MAAS_ADMIN_EMAIL=${process.env.MAAS_ADMIN_EMAIL}`);
1387
- // shellExec(`maas createadmin --username $MAAS_ADMIN_USERNAME --email $MAAS_ADMIN_EMAIL`);
1388
-
1389
- // MaaS admin CLI:
1390
- // maas login <maas-admin-username> http://localhost:5240/MAAS
1391
- // paste GUI API KEY (profile section)
1392
-
1393
- // Import custom image
1394
- // maas <maas-admin-username> boot-resources create name='custom/RockyLinuxRpi4' \
1395
- // title='RockyLinuxRpi4' \
1396
- // architecture='arm64/generic' \
1397
- // filetype='tgz' \
1398
- // content@=/home/RockyLinuxRpi_9-latest.tar.gz
1399
-
1400
- // Image boot resource:
1401
- // /var/snap/maas/current/root/snap/maas
1402
- // /var/snap/maas/common/maas/tftp_root
1403
- // sudo chmod 755 /var/snap/maas/common/maas/tftp_root
1404
-
1405
- // /var/snap/maas/common/maas/dhcpd.conf
1406
- // sudo snap restart maas.pebble
1407
-
1408
- // PXE Linux files:
1409
- // /var/snap/maas/common/maas/image-storage/bootloaders/pxe/i386
1410
- // sudo nmcli con modify <interface-device-name-connection-id> ethtool.feature-rx on ethtool.feature-tx off
1411
- // sudo nmcli connection up <interface-device-name-connection-id>
1412
-
1413
- // man nm-settings |grep feature-tx-checksum
1414
-
1415
- // nmcli c modify <interface-device-name-connection-id> \
1416
- // ethtool.feature-tx-checksum-fcoe-crc off \
1417
- // ethtool.feature-tx-checksum-ip-generic off \
1418
- // ethtool.feature-tx-checksum-ipv4 off \
1419
- // ethtool.feature-tx-checksum-ipv6 off \
1420
- // ethtool.feature-tx-checksum-sctp off
1421
-
1422
- // Ensure Rocky NFS server and /etc/exports configured
1423
- // sudo systemctl restart nfs-server
1424
- // Check mounts: showmount -e <server-ip>
1425
- // Check nfs ports: rpcinfo -p
1426
- // sudo chown -R root:root ${process.env.NFS_EXPORT_PATH}/rpi4mb
1427
- // sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/rpi4mb
1428
-
1429
- // tftp server
1430
- // sudo chown -R root:root /var/snap/maas/common/maas/tftp_root/rpi4mb
1431
-
1432
- // tftp client
1433
- // sudo dnf install tftp
1434
- // tftp <server-ip> -c get <path>
1435
-
1436
- // Check firewall-cmd
1437
- // firewall-cmd --permanent --add-service=rpc-bind
1438
- // firewall-cmd --reload
1439
- // systemctl disable firewalld
1440
- // sudo firewall-cmd --permanent --add-port=10259/tcp --zone=public
1441
-
1442
- // Image extension transform (.img.xz to .tar.gz):
1443
- // tar -cvzf image-name.tar.gz image-name.img.xz
1444
-
1445
- // Rocky network configuration:
1446
- // /etc/NetworkManager/system-connections
1447
-
1448
- // Rocky kernel params update
1449
- // sudo grubby --args="<key>=<value> <key>=<value>" --update-kernel=ALL
1450
- // sudo reboot now
1451
-
1452
- // Temporal:
1453
- // sudo snap install temporal
1454
- // journalctl -u snap.maas.pebble -t maas-regiond
1455
- // journalctl -u snap.maas.pebble -t maas-temporal -n 100 --no-pager -f
1456
-
1457
- // Remove:
1458
- // sudo dnf remove <package> -y; sudo dnf autoremove -y; sudo dnf clean packages
1459
- // check: ~
1460
- // check: ~./cache
1461
- // check: ~./config
1462
-
1463
- // Check file logs
1464
- // grep -i -E -C 1 '<key-a>|<key-b>' /example.log | tail -n 600
1465
-
1466
- // Back into your firmware setup (UEFI or BIOS config screen).
1467
- // grub> fwsetup
1468
-
1469
- // Poweroff:
1470
- // grub > halt
1471
- // initramfs > poweroff
1472
-
1473
- // Check interface
1474
- // ip link show
1475
- // nmcli con show
1476
-
1477
- let firmwarePath,
1478
- tftpSubDir,
1479
- kernelFilesPaths,
1480
- name,
1481
- architecture,
1482
- resource,
1483
- nfsConnectStr,
1484
- etcExports,
1485
- nfsServerRootPath,
1486
- bootConf,
1487
- zipFirmwareFileName,
1488
- zipFirmwareName,
1489
- zipFirmwareUrl,
1490
- interfaceName,
1491
- nfsHost;
1492
-
1493
- switch (process.argv[3]) {
1494
- case 'rpi4mb':
1495
- const resourceId = process.argv[4] ?? '39';
1496
- tftpSubDir = '/rpi4mb';
1497
- zipFirmwareFileName = `RPi4_UEFI_Firmware_v1.41.zip`;
1498
- zipFirmwareName = zipFirmwareFileName.split('.zip')[0];
1499
- zipFirmwareUrl = `https://github.com/pftf/RPi4/releases/download/v1.41/RPi4_UEFI_Firmware_v1.41.zip`;
1500
- firmwarePath = `../${zipFirmwareName}`;
1501
- interfaceName = process.env.RPI4_INTERFACE_NAME;
1502
- nfsHost = 'rpi4mb';
1503
- if (!fs.existsSync(firmwarePath)) {
1504
- await Downloader(zipFirmwareUrl, `../${zipFirmwareFileName}`);
1505
- shellExec(`cd .. && mkdir ${zipFirmwareName} && cd ${zipFirmwareName} && unzip ../${zipFirmwareFileName}`);
1506
- }
1507
- resource = resources.find((o) => o.id == resourceId);
1508
- name = resource.name;
1509
- architecture = resource.architecture;
1510
- resource = resources.find((o) => o.name === name && o.architecture === architecture);
1511
- nfsServerRootPath = `${process.env.NFS_EXPORT_PATH}/rpi4mb`;
1512
- // ,anonuid=1001,anongid=100
1513
- // etcExports = `${nfsServerRootPath} *(rw,all_squash,sync,no_root_squash,insecure)`;
1514
- etcExports = `${nfsServerRootPath} 192.168.1.0/24(${[
1515
- 'rw',
1516
- // 'all_squash',
1517
- 'sync',
1518
- 'no_root_squash',
1519
- 'no_subtree_check',
1520
- 'insecure',
1521
- ]})`;
1522
- const resourceData = JSON.parse(
1523
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resource read ${resource.id}`, {
1524
- stdout: true,
1525
- silent: true,
1526
- disableLog: true,
1527
- }),
1528
- );
1529
- const bootFiles = resourceData.sets[Object.keys(resourceData.sets)[0]].files;
1530
- const suffix = architecture.match('xgene') ? '.xgene' : '';
1531
-
1532
- kernelFilesPaths = {
1533
- 'vmlinuz-efi': bootFiles['boot-kernel' + suffix].filename_on_disk,
1534
- 'initrd.img': bootFiles['boot-initrd' + suffix].filename_on_disk,
1535
- squashfs: bootFiles['squashfs'].filename_on_disk,
1536
- };
1537
- const protocol = 'tcp'; // v3 -> tcp, v4 -> udp
1538
-
1539
- const mountOptions = [
1540
- protocol,
1541
- 'vers=3',
1542
- 'nfsvers=3',
1543
- 'nolock',
1544
- // 'protocol=tcp',
1545
- // 'hard=true',
1546
- 'port=2049',
1547
- // 'sec=none',
1548
- 'rw',
1549
- 'hard',
1550
- 'intr',
1551
- 'rsize=32768',
1552
- 'wsize=32768',
1553
- 'acregmin=0',
1554
- 'acregmax=0',
1555
- 'acdirmin=0',
1556
- 'acdirmax=0',
1557
- 'noac',
1558
- // 'nodev',
1559
- // 'nosuid',
1560
- ];
1561
- const cmd = [
1562
- `console=serial0,115200`,
1563
- `console=tty1`,
1564
- // `initrd=-1`,
1565
- // `net.ifnames=0`,
1566
- // `dwc_otg.lpm_enable=0`,
1567
- // `elevator=deadline`,
1568
- `root=/dev/nfs`,
1569
- `nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb,${mountOptions}`,
1570
- // `nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb`,
1571
- `ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${nfsHost}:${interfaceName}:static`,
1572
- `rootfstype=nfs`,
1573
- `rw`,
1574
- `rootwait`,
1575
- `fixrtc`,
1576
- 'initrd=initrd.img',
1577
- // 'boot=casper',
1578
- // 'ro',
1579
- 'netboot=nfs',
1580
- `cloud-config-url=/dev/null`,
1581
- // 'ip=dhcp',
1582
- // 'ip=dfcp',
1583
- // 'autoinstall',
1584
- // 'rd.break',
1585
- ];
1586
-
1587
- nfsConnectStr = cmd.join(' ');
1588
- bootConf = `[all]
1589
- MAC_ADDRESS=00:00:00:00:00:00
1590
- MAC_ADDRESS_OTP=0,1
1591
- BOOT_UART=0
1592
- WAKE_ON_GPIO=1
1593
- POWER_OFF_ON_HALT=0
1594
- ENABLE_SELF_UPDATE=1
1595
- DISABLE_HDMI=0
1596
- TFTP_IP=${serverip}
1597
- TFTP_PREFIX=1
1598
- TFTP_PREFIX_STR=${tftpSubDir.slice(1)}/
1599
- NET_INSTALL_ENABLED=1
1600
- DHCP_TIMEOUT=45000
1601
- DHCP_REQ_TIMEOUT=4000
1602
- TFTP_FILE_TIMEOUT=30000
1603
- BOOT_ORDER=0x21`;
1604
-
1605
- break;
1606
-
1607
- default:
1608
- break;
1609
- }
1610
- shellExec(`sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/${nfsHost}`);
1611
-
1612
- shellExec(`sudo rm -rf ${tftpRoot}${tftpSubDir}`);
1613
- shellExec(`sudo cp -a ${firmwarePath} ${tftpRoot}${tftpSubDir}`);
1614
- shellExec(`mkdir -p ${tftpRoot}${tftpSubDir}/pxe`);
1615
-
1616
- fs.writeFileSync(`/etc/exports`, etcExports, 'utf8');
1617
- if (bootConf) fs.writeFileSync(`${tftpRoot}${tftpSubDir}/boot.conf`, bootConf, 'utf8');
1618
-
1619
- shellExec(`node bin/deploy nfs`);
1620
-
1621
- if (process.argv.includes('restart')) {
1622
- shellExec(`sudo snap restart maas.pebble`);
1623
- let secs = 0;
1624
- while (
1625
- !(
1626
- shellExec(`maas status`, { silent: true, disableLog: true, stdout: true })
1627
- .split(' ')
1628
- .filter((l) => l.match('inactive')).length === 1
1629
- )
1630
- ) {
1631
- await timer(1000);
1632
- console.log(`Waiting... (${++secs}s)`);
1633
- }
1634
- }
1635
-
1636
- switch (process.argv[3]) {
1637
- case 'rpi4mb':
1638
- {
1639
- // subnet DHCP snippets
1640
- // # UEFI ARM64
1641
- // if option arch = 00:0B {
1642
- // filename "rpi4mb/pxe/grubaa64.efi";
1643
- // }
1644
- // elsif option arch = 00:13 {
1645
- // filename "http://<IP_ADDRESS>:5248/images/bootloaders/uefi/arm64/grubaa64.efi";
1646
- // option vendor-class-identifier "HTTPClient";
1647
- // }
1648
- for (const file of ['bootaa64.efi', 'grubaa64.efi']) {
1649
- shellExec(
1650
- `sudo cp -a /var/snap/maas/common/maas/image-storage/bootloaders/uefi/arm64/${file} ${tftpRoot}${tftpSubDir}/pxe/${file}`,
1651
- );
1652
- }
1653
- // const file = 'bcm2711-rpi-4-b.dtb';
1654
- // shellExec(
1655
- // `sudo cp -a ${firmwarePath}/${file} /var/snap/maas/common/maas/image-storage/bootloaders/uefi/arm64/${file}`,
1656
- // );
1657
-
1658
- // const ipxeSrc = fs
1659
- // .readFileSync(`${tftpRoot}/ipxe.cfg`, 'utf8')
1660
- // .replaceAll('amd64', 'arm64')
1661
- // .replaceAll('${next-server}', IP_ADDRESS);
1662
- // fs.writeFileSync(`${tftpRoot}/ipxe.cfg`, ipxeSrc, 'utf8');
1663
-
1664
- {
1665
- for (const file of Object.keys(kernelFilesPaths)) {
1666
- shellExec(
1667
- `sudo cp -a /var/snap/maas/common/maas/image-storage/${kernelFilesPaths[file]} ${tftpRoot}${tftpSubDir}/pxe/${file}`,
1668
- );
1669
- }
1670
- // const configTxtSrc = fs.readFileSync(`${firmwarePath}/config.txt`, 'utf8');
1671
- // fs.writeFileSync(
1672
- // `${tftpRoot}${tftpSubDir}/config.txt`,
1673
- // configTxtSrc
1674
- // .replace(`kernel=kernel8.img`, `kernel=vmlinuz`)
1675
- // .replace(`# max_framebuffers=2`, `max_framebuffers=2`)
1676
- // .replace(`initramfs initramfs8 followkernel`, `initramfs initrd.img followkernel`),
1677
- // 'utf8',
1678
- // );
1679
-
1680
- // grub:
1681
- // set root=(pxe)
1682
-
1683
- // UNDERPOST.NET UEFI/GRUB/MAAS RPi4 commissioning (ARM64)
1684
- const menuentryStr = 'underpost.net rpi4mb maas commissioning (ARM64)';
1685
- const grubCfgPath = `${tftpRoot}/grub/grub.cfg`;
1686
- fs.writeFileSync(
1687
- grubCfgPath,
1688
- `
1689
- insmod gzio
1690
- insmod http
1691
- insmod nfs
1692
- set timeout=5
1693
- set default=0
1694
-
1695
- menuentry '${menuentryStr}' {
1696
- set root=(tftp,${serverip})
1697
- linux ${tftpSubDir}/pxe/vmlinuz-efi ${nfsConnectStr}
1698
- initrd ${tftpSubDir}/pxe/initrd.img
1699
- boot
1700
- }
1701
-
1702
- `,
1703
- 'utf8',
1704
- );
1705
- }
1706
- const arm64EfiPath = `${tftpRoot}/grub/arm64-efi`;
1707
- if (fs.existsSync(arm64EfiPath)) shellExec(`sudo rm -rf ${arm64EfiPath}`);
1708
- shellExec(`sudo cp -a /usr/lib/grub/arm64-efi ${arm64EfiPath}`);
1709
- }
1710
-
1711
- break;
1712
-
1713
- default:
1714
- break;
1715
- }
1716
-
1717
- logger.info('succes maas deploy', {
1718
- resource,
1719
- kernelFilesPaths,
1720
- tftpRoot,
1721
- tftpSubDir,
1722
- firmwarePath,
1723
- etcExports,
1724
- nfsServerRootPath,
1725
- nfsConnectStr,
1726
- });
1727
- if (process.argv.includes('restart')) {
1728
- if (fs.existsSync(`node engine-private/r.js`)) shellExec(`node engine-private/r`);
1729
- shellExec(`node bin/deploy maas dhcp`);
1730
- shellExec(`sudo chown -R root:root ${tftpRoot}`);
1731
- shellExec(`sudo sudo chmod 755 ${tftpRoot}`);
1732
- }
1733
- // for (const machine of machines) {
1734
- // // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
1735
- // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${machine.system_id}`, {
1736
- // silent: true,
1737
- // });
1738
- // }
1739
- // machines = [];
1740
-
1741
- const monitor = async () => {
1742
- // discoveries Query observed discoveries.
1743
- // discovery Read or delete an observed discovery.
1744
-
1745
- const discoveries = JSON.parse(
1746
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries read`, {
1747
- silent: true,
1748
- stdout: true,
1749
- }),
1750
- ).filter(
1751
- (o) => o.ip !== IP_ADDRESS && o.ip !== gatewayip && !machines.find((_o) => _o.mac_address === o.mac_address),
1752
- );
1753
-
1754
- // {
1755
- // "discovery_id": "",
1756
- // "ip": "192.168.1.189",
1757
- // "mac_address": "00:00:00:00:00:00",
1758
- // "last_seen": "2025-05-05T14:17:37.354",
1759
- // "hostname": null,
1760
- // "fabric_name": "",
1761
- // "vid": null,
1762
- // "mac_organization": "",
1763
- // "observer": {
1764
- // "system_id": "",
1765
- // "hostname": "",
1766
- // "interface_id": 1,
1767
- // "interface_name": ""
1768
- // },
1769
- // "resource_uri": "/MAAS/api/2.0/discovery/MTkyLjE2OC4xLjE4OSwwMDowMDowMDowMDowMDowMA==/"
1770
- // },
1771
-
1772
- for (const discovery of discoveries) {
1773
- const machine = {
1774
- architecture: architecture.match('amd') ? 'amd64/generic' : 'arm64/generic',
1775
- mac_address: discovery.mac_address,
1776
- hostname: discovery.hostname ?? discovery.mac_organization ?? discovery.domain ?? `generic-host-${s4()}`,
1777
- // discovery.ip.match(ipaddr)
1778
- // ? nfsHost
1779
- // : `unknown-${s4()}`,
1780
- // description: '',
1781
- // https://maas.io/docs/reference-power-drivers
1782
- power_type: 'manual', // manual
1783
- // power_parameters_power_address: discovery.ip,
1784
- mac_addresses: discovery.mac_address,
1785
- };
1786
- machine.hostname = machine.hostname.replaceAll(' ', '').replaceAll('.', '');
1787
-
1788
- try {
1789
- let newMachine = shellExec(
1790
- `maas ${process.env.MAAS_ADMIN_USERNAME} machines create ${Object.keys(machine)
1791
- .map((k) => `${k}="${machine[k]}"`)
1792
- .join(' ')}`,
1793
- {
1794
- silent: true,
1795
- stdout: true,
1796
- },
1797
- );
1798
- newMachine = machineFactory(JSON.parse(newMachine));
1799
- machines.push(newMachine);
1800
- console.log(newMachine);
1801
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${newMachine.system_id}`, {
1802
- silent: true,
1803
- });
1804
- } catch (error) {
1805
- logger.error(error, error.stack);
1806
- }
1807
- }
1808
- // if (discoveries.length > 0) {
1809
- // shellExec(
1810
- // `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}'`,
1811
- // );
1812
- // }
1813
- await timer(1000);
1814
- monitor();
1815
- };
1816
- // shellExec(`node bin/deploy open-virtual-root ${architecture.match('amd') ? 'amd64' : 'arm64'} ${nfsHost}`);
1817
- machines = [];
1818
- shellExec(`node bin/deploy maas clear`);
1819
- monitor();
1820
- break;
1821
- }
1822
-
1823
1230
  case 'nfs': {
1824
1231
  // Daemon RPC NFSv3. ports:
1825
1232
 
@@ -1895,129 +1302,6 @@ udp-port = 32766
1895
1302
  shellExec(`sudo systemctl restart nfs-server`);
1896
1303
  break;
1897
1304
  }
1898
- case 'update-virtual-root': {
1899
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
1900
- const IP_ADDRESS = getLocalIPv4Address();
1901
- const architecture = process.argv[3];
1902
- const host = process.argv[4];
1903
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
1904
- const ipaddr = process.env.RPI4_IP;
1905
- await updateVirtualRoot({
1906
- IP_ADDRESS,
1907
- architecture,
1908
- host,
1909
- nfsHostPath,
1910
- ipaddr,
1911
- });
1912
- break;
1913
- }
1914
- case 'open-virtual-root': {
1915
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
1916
- const IP_ADDRESS = getLocalIPv4Address();
1917
- const architecture = process.argv[3];
1918
- const host = process.argv[4];
1919
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
1920
- shellExec(`sudo dnf install -y iptables-legacy`);
1921
- shellExec(`sudo dnf install -y debootstrap`);
1922
- shellExec(`sudo dnf install kernel-modules-extra-$(uname -r)`);
1923
- switch (architecture) {
1924
- case 'arm64':
1925
- shellExec(`sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes`);
1926
-
1927
- break;
1928
-
1929
- default:
1930
- break;
1931
- }
1932
-
1933
- shellExec(`sudo modprobe binfmt_misc`);
1934
- shellExec(`sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc`);
1935
-
1936
- if (process.argv.includes('build')) {
1937
- // shellExec(`depmod -a`);
1938
- shellExec(`mkdir -p ${nfsHostPath}`);
1939
- let cmd;
1940
- switch (host) {
1941
- case 'rpi4mb':
1942
- shellExec(`sudo rm -rf ${nfsHostPath}/*`);
1943
- shellExec(`sudo chown -R root:root ${nfsHostPath}`);
1944
- cmd = [
1945
- `sudo debootstrap`,
1946
- `--arch=arm64`,
1947
- `--variant=minbase`,
1948
- `--foreign`, // arm64 on amd64
1949
- `noble`,
1950
- nfsHostPath,
1951
- `http://ports.ubuntu.com/ubuntu-ports/`,
1952
- ];
1953
- break;
1954
-
1955
- default:
1956
- break;
1957
- }
1958
- shellExec(cmd.join(' '));
1959
-
1960
- shellExec(`sudo podman create --name extract multiarch/qemu-user-static`);
1961
- shellExec(`podman ps -a`);
1962
- shellExec(`sudo podman cp extract:/usr/bin/qemu-aarch64-static ${nfsHostPath}/usr/bin/`);
1963
- shellExec(`sudo podman rm extract`);
1964
- shellExec(`podman ps -a`);
1965
-
1966
- switch (host) {
1967
- case 'rpi4mb':
1968
- shellExec(`file ${nfsHostPath}/bin/bash`); // expected: ELF 64-bit LSB pie executable, ARM aarch64 …
1969
- break;
1970
-
1971
- default:
1972
- break;
1973
- }
1974
-
1975
- shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
1976
- /debootstrap/debootstrap --second-stage
1977
- EOF`);
1978
- }
1979
- if (process.argv.includes('mount')) {
1980
- shellExec(`sudo mount --bind /proc ${nfsHostPath}/proc`);
1981
- shellExec(`sudo mount --bind /sys ${nfsHostPath}/sys`);
1982
- shellExec(`sudo mount --rbind /dev ${nfsHostPath}/dev`);
1983
- }
1984
-
1985
- if (process.argv.includes('build')) {
1986
- switch (host) {
1987
- case 'rpi4mb':
1988
- const ipaddr = process.env.RPI4_IP;
1989
-
1990
- await updateVirtualRoot({
1991
- IP_ADDRESS,
1992
- architecture,
1993
- host,
1994
- nfsHostPath,
1995
- ipaddr,
1996
- });
1997
-
1998
- break;
1999
-
2000
- default:
2001
- break;
2002
- }
2003
- }
2004
- // if (process.argv.includes('mount')) {
2005
- // shellExec(`sudo mount --bind /lib/modules ${nfsHostPath}/lib/modules`);
2006
- // }
2007
-
2008
- break;
2009
- }
2010
-
2011
- case 'close-virtual-root': {
2012
- const architecture = process.argv[3];
2013
- const host = process.argv[4];
2014
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
2015
- shellExec(`sudo umount ${nfsHostPath}/proc`);
2016
- shellExec(`sudo umount ${nfsHostPath}/sys`);
2017
- shellExec(`sudo umount ${nfsHostPath}/dev`);
2018
- // shellExec(`sudo umount ${nfsHostPath}/lib/modules`);
2019
- break;
2020
- }
2021
1305
 
2022
1306
  case 'mount': {
2023
1307
  const mounts = shellExec(`mount`).split(`\n`);
@@ -2040,10 +1324,10 @@ EOF`);
2040
1324
 
2041
1325
  case 'create-ports': {
2042
1326
  const cmd = [];
2043
- const ipaddr = getLocalIPv4Address();
1327
+ const commissioningDeviceIp = getLocalIPv4Address();
2044
1328
  for (const port of ['5240']) {
2045
1329
  const name = 'maas';
2046
- cmd.push(`${name}:${port}-${port}:${ipaddr}`);
1330
+ cmd.push(`${name}:${port}-${port}:${commissioningDeviceIp}`);
2047
1331
  }
2048
1332
  pbcopy(`node engine-private/r create-port ${cmd}`);
2049
1333
  break;
@@ -2223,7 +1507,7 @@ EOF`);
2223
1507
  const args = [
2224
1508
  `node bin dockerfile-image-build --path ${path}/backend/`,
2225
1509
  `--image-name=${imageName} --image-path=${path}`,
2226
- `--podman-save --${process.argv.includes('kubeadm') ? 'kubeadm' : 'kind'}-load --no-cache`,
1510
+ `--podman-save --${process.argv.includes('kubeadm') ? 'kubeadm' : 'kind'}-load --reset`,
2227
1511
  ];
2228
1512
  shellExec(args.join(' '));
2229
1513
  }
@@ -2235,7 +1519,7 @@ EOF`);
2235
1519
  const args = [
2236
1520
  `node bin dockerfile-image-build --path ${path}/frontend/`,
2237
1521
  `--image-name=${imageName} --image-path=${path}`,
2238
- `--podman-save --${process.argv.includes('kubeadm') ? 'kubeadm' : 'kind'}-load --no-cache`,
1522
+ `--podman-save --${process.argv.includes('kubeadm') ? 'kubeadm' : 'kind'}-load --reset`,
2239
1523
  ];
2240
1524
  shellExec(args.join(' '));
2241
1525
  }
@@ -2408,6 +1692,14 @@ nvidia/gpu-operator \
2408
1692
  // sudo yum install sbt
2409
1693
  break;
2410
1694
  }
1695
+
1696
+ case 'chrony': {
1697
+ shellExec(`sudo dnf install chrony -y`);
1698
+ // debian chroot: sudo apt install chrony
1699
+ for (const cmd of chronySetUp(`/etc/chrony.conf`)) shellExec(cmd);
1700
+
1701
+ break;
1702
+ }
2411
1703
  }
2412
1704
  } catch (error) {
2413
1705
  logger.error(error, error.stack);