@underpostnet/underpost 2.99.4 → 2.99.6
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/.env.development +0 -3
- package/.env.production +1 -3
- package/.env.test +0 -3
- package/README.md +3 -3
- package/baremetal/commission-workflows.json +93 -4
- package/bin/deploy.js +56 -45
- package/cli.md +45 -28
- package/examples/static-page/README.md +101 -357
- package/examples/static-page/ssr-components/CustomPage.js +1 -13
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +40 -0
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +40 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +3 -4
- package/scripts/disk-devices.sh +13 -0
- package/scripts/maas-setup.sh +13 -9
- package/scripts/rocky-kickstart.sh +294 -0
- package/src/cli/baremetal.js +657 -263
- package/src/cli/cloud-init.js +120 -120
- package/src/cli/env.js +4 -1
- package/src/cli/image.js +4 -37
- package/src/cli/index.js +56 -11
- package/src/cli/kickstart.js +149 -0
- package/src/cli/repository.js +3 -1
- package/src/cli/run.js +56 -10
- package/src/cli/secrets.js +0 -34
- package/src/cli/static.js +23 -23
- package/src/client/components/core/Docs.js +22 -3
- package/src/index.js +30 -5
- package/src/server/backup.js +11 -4
- package/src/server/client-build-docs.js +1 -1
- package/src/server/conf.js +0 -22
- package/src/server/cron.js +339 -130
- package/src/server/dns.js +10 -0
- package/src/server/logger.js +22 -27
- package/src/server/tls.js +14 -14
- package/examples/static-page/QUICK-REFERENCE.md +0 -481
- package/examples/static-page/STATIC-GENERATOR-GUIDE.md +0 -757
package/src/cli/cloud-init.js
CHANGED
|
@@ -122,8 +122,6 @@ ${Underpost.baremetal.stepsRender(
|
|
|
122
122
|
`cloud-init modules --mode=config`,
|
|
123
123
|
`sleep 3`,
|
|
124
124
|
`cloud-init modules --mode=final`,
|
|
125
|
-
`sleep 3`,
|
|
126
|
-
`/underpost/enlistment.sh`,
|
|
127
125
|
],
|
|
128
126
|
false,
|
|
129
127
|
)}`,
|
|
@@ -190,45 +188,6 @@ cat /etc/default/keyboard`,
|
|
|
190
188
|
logger.info('Build', `${nfsHostToolsPath}/device_scan.sh`);
|
|
191
189
|
fs.copySync(`${underpostRoot}/scripts/device-scan.sh`, `${nfsHostToolsPath}/device_scan.sh`);
|
|
192
190
|
|
|
193
|
-
// Build and write the MAAS enlistment script.
|
|
194
|
-
logger.info('Build', `${nfsHostToolsPath}/enlistment.sh`);
|
|
195
|
-
fs.writeFileSync(
|
|
196
|
-
`${nfsHostToolsPath}/enlistment.sh`,
|
|
197
|
-
`#!/bin/bash
|
|
198
|
-
set -x
|
|
199
|
-
|
|
200
|
-
# ------------------------------------------------------------
|
|
201
|
-
# Step: Commission a machine in MAAS using OAuth1 authentication
|
|
202
|
-
# ------------------------------------------------------------
|
|
203
|
-
|
|
204
|
-
MACHINE_ID=$(cat /underpost/system-id)
|
|
205
|
-
CONSUMER_KEY=$(cat /underpost/consumer-key)
|
|
206
|
-
TOKEN_KEY=$(cat /underpost/token-key)
|
|
207
|
-
TOKEN_SECRET=$(cat /underpost/token-secret)
|
|
208
|
-
|
|
209
|
-
echo ">>> Starting MAAS machine commissioning for system_id: $MACHINE_ID …"
|
|
210
|
-
|
|
211
|
-
curl -X POST \\
|
|
212
|
-
--fail --location --verbose --include --raw --trace-ascii /dev/stdout\\
|
|
213
|
-
--header "Authorization:\\
|
|
214
|
-
OAuth oauth_version=1.0,\\
|
|
215
|
-
oauth_signature_method=PLAINTEXT,\\
|
|
216
|
-
oauth_consumer_key=$CONSUMER_KEY,\\
|
|
217
|
-
oauth_token=$TOKEN_KEY,\\
|
|
218
|
-
oauth_signature=&$TOKEN_SECRET,\\
|
|
219
|
-
oauth_nonce=$(uuidgen),\\
|
|
220
|
-
oauth_timestamp=$(date +%s)"\\
|
|
221
|
-
-F "commissioning_scripts=20-maas-01-install-lldpd"\\
|
|
222
|
-
-F "enable_ssh=1"\\
|
|
223
|
-
-F "skip_bmc_config=1"\\
|
|
224
|
-
-F "skip_networking=1"\\
|
|
225
|
-
-F "skip_storage=1"\\
|
|
226
|
-
-F "testing_scripts=none"\\
|
|
227
|
-
http://${callbackMetaData.runnerHost.ip}:5240/MAAS/api/2.0/machines/$MACHINE_ID/op-commission \\
|
|
228
|
-
2>&1 | tee /underpost/enlistment.log || echo "ERROR: MAAS commissioning returned code $?"`,
|
|
229
|
-
'utf8',
|
|
230
|
-
);
|
|
231
|
-
|
|
232
191
|
// Import SSH keys for root user.
|
|
233
192
|
logger.info('Import ssh keys');
|
|
234
193
|
shellExec(`sudo rm -rf ${nfsHostPath}/root/.ssh`);
|
|
@@ -261,6 +220,12 @@ curl -X POST \\
|
|
|
261
220
|
* @param {boolean} params.ubuntuToolsBuild - Flag to determine if Ubuntu tools should be built.
|
|
262
221
|
* @param {string} [params.bootcmd] - Optional custom commands to run during boot.
|
|
263
222
|
* @param {string} [params.runcmd] - Optional custom commands to run during first boot.
|
|
223
|
+
* @param {object} [params.write_files] - Optional array of files to write during cloud-init, each with path, permissions, owner, and content.
|
|
224
|
+
* @param {string} [params.write_files[].path] - The file path where the content will be written on the target machine.
|
|
225
|
+
* @param {string} [params.write_files[].permissions] - The file permissions to set for the written file (e.g., '0644').
|
|
226
|
+
* @param {string} [params.write_files[].owner] - The owner of the written file (e.g., 'root:root').
|
|
227
|
+
* @param {string} [params.write_files[].content] - The content to write into the file.
|
|
228
|
+
* @param {boolean} [params.write_files[].defer] - Whether to defer writing the file until the 'final' stage of cloud-init.
|
|
264
229
|
* @param {object} [authCredentials={}] - Optional MAAS authentication credentials.
|
|
265
230
|
* @returns {object} The generated cloud-init configuration content.
|
|
266
231
|
* @memberof UnderpostCloudInit
|
|
@@ -278,6 +243,15 @@ curl -X POST \\
|
|
|
278
243
|
ubuntuToolsBuild,
|
|
279
244
|
bootcmd: bootcmdParam,
|
|
280
245
|
runcmd: runcmdParam,
|
|
246
|
+
write_files = [
|
|
247
|
+
{
|
|
248
|
+
path: '',
|
|
249
|
+
permissions: '',
|
|
250
|
+
owner: '',
|
|
251
|
+
content: '',
|
|
252
|
+
defer: false,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
281
255
|
},
|
|
282
256
|
authCredentials = { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
|
|
283
257
|
) {
|
|
@@ -305,6 +279,7 @@ curl -X POST \\
|
|
|
305
279
|
let runcmd = [
|
|
306
280
|
'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
|
|
307
281
|
'echo "Init runcmd"',
|
|
282
|
+
'systemctl enable --now ssh',
|
|
308
283
|
'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
|
|
309
284
|
];
|
|
310
285
|
|
|
@@ -334,12 +309,14 @@ curl -X POST \\
|
|
|
334
309
|
name: process.env.MAAS_ADMIN_USERNAME,
|
|
335
310
|
sudo: ['ALL=(ALL) NOPASSWD:ALL'],
|
|
336
311
|
shell: '/bin/bash',
|
|
337
|
-
lock_passwd:
|
|
312
|
+
lock_passwd: true,
|
|
338
313
|
groups: 'sudo,users,admin,wheel,lxd',
|
|
339
|
-
plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
|
|
314
|
+
// plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
|
|
340
315
|
ssh_authorized_keys: [fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')],
|
|
341
316
|
},
|
|
342
317
|
],
|
|
318
|
+
disable_root: true,
|
|
319
|
+
ssh_pwauth: false,
|
|
343
320
|
timezone,
|
|
344
321
|
ntp: {
|
|
345
322
|
enabled: true,
|
|
@@ -360,6 +337,7 @@ curl -X POST \\
|
|
|
360
337
|
'smartmontools',
|
|
361
338
|
'net-tools',
|
|
362
339
|
'util-linux',
|
|
340
|
+
'openssh-server',
|
|
363
341
|
],
|
|
364
342
|
resize_rootfs: false,
|
|
365
343
|
growpart: { mode: 'off' },
|
|
@@ -380,7 +358,7 @@ curl -X POST \\
|
|
|
380
358
|
final_message: '====== Cloud init finished ======',
|
|
381
359
|
bootcmd,
|
|
382
360
|
runcmd,
|
|
383
|
-
|
|
361
|
+
write_files,
|
|
384
362
|
preserve_hostname: false,
|
|
385
363
|
cloud_init_modules: [
|
|
386
364
|
// minimal for commissioning
|
|
@@ -394,37 +372,37 @@ curl -X POST \\
|
|
|
394
372
|
'ssh', // enable/configure SSH for temporary access
|
|
395
373
|
|
|
396
374
|
// optional modules (commented out by default)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
375
|
+
'migrator',
|
|
376
|
+
'seed_random',
|
|
377
|
+
'growpart',
|
|
378
|
+
'resizefs',
|
|
379
|
+
'users-groups',
|
|
402
380
|
],
|
|
403
381
|
cloud_config_modules: [
|
|
404
382
|
// minimal so MAAS can run commissioning scripts
|
|
405
383
|
'runcmd', // commissioning / final script execution
|
|
406
384
|
'mounts', // mount devices during commissioning if needed
|
|
407
|
-
|
|
385
|
+
'ntp', // optional — enable if you want time sync
|
|
408
386
|
|
|
409
387
|
// typically not required for basic commissioning (commented)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
388
|
+
'emit_upstart',
|
|
389
|
+
'disk_setup',
|
|
390
|
+
'ssh-import-id',
|
|
391
|
+
'locale',
|
|
392
|
+
'set-passwords',
|
|
393
|
+
'grub-dpkg',
|
|
394
|
+
'apt-pipelining',
|
|
395
|
+
'apt-configure',
|
|
396
|
+
'package-update-upgrade-install', // heavy; do NOT enable by default
|
|
397
|
+
'landscape',
|
|
398
|
+
'timezone',
|
|
399
|
+
'puppet',
|
|
400
|
+
'chef',
|
|
401
|
+
'salt-minion',
|
|
402
|
+
'mcollective',
|
|
403
|
+
'disable-ec2-metadata',
|
|
404
|
+
'byobu',
|
|
405
|
+
'ssh-import-id', // duplicate in original list
|
|
428
406
|
],
|
|
429
407
|
cloud_final_modules: [
|
|
430
408
|
// minimal suggestions so final scripts run and node reports status
|
|
@@ -432,64 +410,19 @@ curl -X POST \\
|
|
|
432
410
|
'final-message', // useful for logs/reporting
|
|
433
411
|
|
|
434
412
|
// optional / commented
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
413
|
+
'rightscale_userdata',
|
|
414
|
+
'scripts-vendor',
|
|
415
|
+
'scripts-per-once',
|
|
416
|
+
'scripts-user',
|
|
417
|
+
'ssh-authkey-fingerprints',
|
|
418
|
+
'keys-to-console',
|
|
419
|
+
'power-state-change', // use carefully (can poweroff/reboot)
|
|
442
420
|
],
|
|
443
421
|
});
|
|
444
422
|
|
|
445
423
|
return { cloudConfigSrc };
|
|
446
424
|
},
|
|
447
425
|
|
|
448
|
-
/**
|
|
449
|
-
* @method authCredentialsFactory
|
|
450
|
-
* @description Retrieves MAAS API key credentials from the MAAS CLI.
|
|
451
|
-
* This method parses the output of `maas apikey` to extract the consumer key,
|
|
452
|
-
* consumer secret, token key, and token secret.
|
|
453
|
-
* @returns {object} An object containing the MAAS authentication credentials.
|
|
454
|
-
* @memberof UnderpostCloudInit
|
|
455
|
-
* @throws {Error} If the MAAS API key format is invalid.
|
|
456
|
-
*/
|
|
457
|
-
authCredentialsFactory() {
|
|
458
|
-
// Expected formats:
|
|
459
|
-
// <consumer_key>:<consumer_token>:<secret> (older format)
|
|
460
|
-
// <consumer_key>:<consumer_secret>:<token_key>:<token_secret> (newer format)
|
|
461
|
-
// Commands used to generate API keys:
|
|
462
|
-
// maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}
|
|
463
|
-
// maas ${process.env.MAAS_ADMIN_USERNAME} account create-authorisation-token
|
|
464
|
-
// maas apikey --generate --username ${process.env.MAAS_ADMIN_USERNAME}
|
|
465
|
-
// Reference: https://github.com/CanonicalLtd/maas-docs/issues/647
|
|
466
|
-
|
|
467
|
-
const parts = shellExec(`maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}`, {
|
|
468
|
-
stdout: true,
|
|
469
|
-
})
|
|
470
|
-
.trim()
|
|
471
|
-
.split(`\n`)[0] // Take only the first line of output.
|
|
472
|
-
.split(':'); // Split by colon to get individual parts.
|
|
473
|
-
|
|
474
|
-
let consumer_key, consumer_secret, token_key, token_secret;
|
|
475
|
-
|
|
476
|
-
// Determine the format of the API key and assign parts accordingly.
|
|
477
|
-
if (parts.length === 4) {
|
|
478
|
-
[consumer_key, consumer_secret, token_key, token_secret] = parts;
|
|
479
|
-
} else if (parts.length === 3) {
|
|
480
|
-
// Handle older 3-part format, setting consumer_secret as empty.
|
|
481
|
-
[consumer_key, token_key, token_secret] = parts;
|
|
482
|
-
consumer_secret = '""';
|
|
483
|
-
token_secret = token_secret.split(' MAAS consumer')[0].trim(); // Clean up token secret.
|
|
484
|
-
} else {
|
|
485
|
-
// Throw an error if the format is not recognized.
|
|
486
|
-
throw new Error('Invalid token format');
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
logger.info('Maas api token generated', { consumer_key, consumer_secret, token_key, token_secret });
|
|
490
|
-
return { consumer_key, consumer_secret, token_key, token_secret };
|
|
491
|
-
},
|
|
492
|
-
|
|
493
426
|
/**
|
|
494
427
|
* @method generateCloudConfig
|
|
495
428
|
* @description Generates a generic cloud-init configuration string.
|
|
@@ -518,6 +451,7 @@ curl -X POST \\
|
|
|
518
451
|
growpart,
|
|
519
452
|
network,
|
|
520
453
|
runcmd,
|
|
454
|
+
write_files,
|
|
521
455
|
final_message,
|
|
522
456
|
bootcmd,
|
|
523
457
|
disable_root,
|
|
@@ -645,6 +579,21 @@ curl -X POST \\
|
|
|
645
579
|
runcmd.forEach((cmd) => yaml.push(` - ${cmd}`));
|
|
646
580
|
}
|
|
647
581
|
|
|
582
|
+
if (write_files) {
|
|
583
|
+
yaml.push('write_files:');
|
|
584
|
+
write_files.forEach((file) => {
|
|
585
|
+
yaml.push(` - path: ${file.path}`);
|
|
586
|
+
if (file.encoding) yaml.push(` encoding: ${file.encoding}`);
|
|
587
|
+
if (file.owner) yaml.push(` owner: ${file.owner}`);
|
|
588
|
+
if (file.permissions) yaml.push(` permissions: '${file.permissions}'`);
|
|
589
|
+
if (file.defer) yaml.push(` defer: ${file.defer}`);
|
|
590
|
+
if (file.content) {
|
|
591
|
+
yaml.push(` content: |`);
|
|
592
|
+
file.content.split('\n').forEach((line) => yaml.push(` ${line}`));
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
648
597
|
if (final_message) yaml.push(`final_message: "${final_message}"`);
|
|
649
598
|
|
|
650
599
|
if (bootcmd) {
|
|
@@ -670,6 +619,57 @@ curl -X POST \\
|
|
|
670
619
|
|
|
671
620
|
return yaml.join('\n');
|
|
672
621
|
},
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* @method httpServerStaticFactory
|
|
625
|
+
* @description Writes cloud-init files (user-data, meta-data, vendor-data) to the bootstrap HTTP server path.
|
|
626
|
+
* @param {object} params
|
|
627
|
+
* @param {string} params.bootstrapHttpServerPath
|
|
628
|
+
* @param {string} params.hostname
|
|
629
|
+
* @param {string} params.cloudConfigSrc
|
|
630
|
+
* @param {string} [params.vendorData='']
|
|
631
|
+
* @memberof UnderpostCloudInit
|
|
632
|
+
* @returns {void}
|
|
633
|
+
*/
|
|
634
|
+
httpServerStaticFactory({ bootstrapHttpServerPath, hostname, cloudConfigSrc, vendorData = '' }) {
|
|
635
|
+
if (!cloudConfigSrc) return;
|
|
636
|
+
const dir = `${bootstrapHttpServerPath}/${hostname}/cloud-init`;
|
|
637
|
+
shellExec(`mkdir -p ${dir}`);
|
|
638
|
+
fs.writeFileSync(`${dir}/user-data`, cloudConfigSrc, 'utf8');
|
|
639
|
+
fs.writeFileSync(`${dir}/meta-data`, `instance-id: ${hostname}\nlocal-hostname: ${hostname}`, 'utf8');
|
|
640
|
+
fs.writeFileSync(`${dir}/vendor-data`, vendorData, 'utf8');
|
|
641
|
+
logger.info(`Cloud-init files written to ${dir}`);
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* @method kernelParamsFactory
|
|
646
|
+
* @description Generates the kernel parameters for the target machine's bootloader configuration,
|
|
647
|
+
* including the necessary parameters to enable cloud-init with a specific configuration URL and logging settings.
|
|
648
|
+
* @param {array} cmd - The existing array of kernel parameters to which cloud-init parameters will be appended.
|
|
649
|
+
* @param {object} options - Options for generating kernel parameters.
|
|
650
|
+
* @param {string} options.ipDhcpServer - The IP address of the DHCP server.
|
|
651
|
+
* @param {object} [options.machine] - The machine information, including system_id for constructing the cloud-init configuration URL.
|
|
652
|
+
* @param {string} [options.machine.system_id] - The unique identifier of the machine, used to fetch the correct cloud-init preseed configuration from MAAS.
|
|
653
|
+
* @return {array} The modified array of kernel parameters with cloud-init parameters included.
|
|
654
|
+
* @memberof UnderpostCloudInit
|
|
655
|
+
*/
|
|
656
|
+
kernelParamsFactory(
|
|
657
|
+
macAddress,
|
|
658
|
+
cmd = [],
|
|
659
|
+
options = {
|
|
660
|
+
ipDhcpServer: '',
|
|
661
|
+
bootstrapHttpServerPort: 8888,
|
|
662
|
+
hostname: '',
|
|
663
|
+
machine: {
|
|
664
|
+
system_id: '',
|
|
665
|
+
},
|
|
666
|
+
authCredentials: { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
|
|
667
|
+
},
|
|
668
|
+
) {
|
|
669
|
+
const { ipDhcpServer, bootstrapHttpServerPort, hostname } = options;
|
|
670
|
+
const cloudConfigUrl = `http://${ipDhcpServer}:${bootstrapHttpServerPort}/${hostname}/cloud-init/user-data`;
|
|
671
|
+
return cmd.concat([`cloud-config-url=${cloudConfigUrl}`]);
|
|
672
|
+
},
|
|
673
673
|
};
|
|
674
674
|
}
|
|
675
675
|
|
package/src/cli/env.js
CHANGED
|
@@ -8,6 +8,7 @@ import { getNpmRootPath, writeEnv } from '../server/conf.js';
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
import { loggerFactory } from '../server/logger.js';
|
|
10
10
|
import dotenv from 'dotenv';
|
|
11
|
+
import { pbcopy } from '../server/process.js';
|
|
11
12
|
|
|
12
13
|
dotenv.config();
|
|
13
14
|
|
|
@@ -72,9 +73,10 @@ class UnderpostRootEnv {
|
|
|
72
73
|
* @param {object} options - Options for getting the environment variable.
|
|
73
74
|
* @param {boolean} [options.plain=false] - If true, returns the environment variable value as a string.
|
|
74
75
|
* @param {boolean} [options.disableLog=false] - If true, disables logging of the environment variable value.
|
|
76
|
+
* @param {boolean} [options.copy=false] - If true, copies the environment variable value to the clipboard.
|
|
75
77
|
* @memberof UnderpostEnv
|
|
76
78
|
*/
|
|
77
|
-
get(key, value, options = { plain: false, disableLog: false }) {
|
|
79
|
+
get(key, value, options = { plain: false, disableLog: false, copy: false }) {
|
|
78
80
|
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
79
81
|
const envPath = `${exeRootPath}/.env`;
|
|
80
82
|
if (!fs.existsSync(envPath)) {
|
|
@@ -84,6 +86,7 @@ class UnderpostRootEnv {
|
|
|
84
86
|
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
85
87
|
if (!options.disableLog)
|
|
86
88
|
options?.plain === true ? console.log(env[key]) : logger.info(`${key}(${typeof env[key]})`, env[key]);
|
|
89
|
+
if (options.copy === true) pbcopy(env[key]);
|
|
87
90
|
return env[key];
|
|
88
91
|
},
|
|
89
92
|
/**
|
package/src/cli/image.js
CHANGED
|
@@ -79,8 +79,6 @@ class UnderpostImage {
|
|
|
79
79
|
* @param {boolean} [options.kind=false] - If true, load the image archive into a Kind cluster.
|
|
80
80
|
* @param {boolean} [options.kubeadm=false] - If true, load the image archive into a Kubeadm cluster (uses 'ctr').
|
|
81
81
|
* @param {boolean} [options.k3s=false] - If true, load the image archive into a K3s cluster (uses 'k3s ctr').
|
|
82
|
-
* @param {boolean} [options.secrets=false] - If true, load secrets from the .env file for the build.
|
|
83
|
-
* @param {string} [options.secretsPath=''] - Custom path to the .env file for secrets.
|
|
84
82
|
* @param {boolean} [options.reset=false] - If true, perform a no-cache build.
|
|
85
83
|
* @param {boolean} [options.dev=false] - If true, use development mode.
|
|
86
84
|
* @memberof UnderpostImage
|
|
@@ -96,27 +94,11 @@ class UnderpostImage {
|
|
|
96
94
|
kind: false,
|
|
97
95
|
kubeadm: false,
|
|
98
96
|
k3s: false,
|
|
99
|
-
secrets: false,
|
|
100
|
-
secretsPath: '',
|
|
101
97
|
reset: false,
|
|
102
98
|
dev: false,
|
|
103
99
|
},
|
|
104
100
|
) {
|
|
105
|
-
let {
|
|
106
|
-
path,
|
|
107
|
-
imageName,
|
|
108
|
-
version,
|
|
109
|
-
imagePath,
|
|
110
|
-
dockerfileName,
|
|
111
|
-
podmanSave,
|
|
112
|
-
secrets,
|
|
113
|
-
secretsPath,
|
|
114
|
-
kind,
|
|
115
|
-
kubeadm,
|
|
116
|
-
k3s,
|
|
117
|
-
reset,
|
|
118
|
-
dev,
|
|
119
|
-
} = options;
|
|
101
|
+
let { path, imageName, version, imagePath, dockerfileName, podmanSave, kind, kubeadm, k3s, reset, dev } = options;
|
|
120
102
|
if (!path) path = '.';
|
|
121
103
|
if (!imageName) imageName = `rockylinux9-underpost:${Underpost.version}`;
|
|
122
104
|
if (!imagePath) imagePath = '.';
|
|
@@ -126,29 +108,14 @@ class UnderpostImage {
|
|
|
126
108
|
if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
|
|
127
109
|
fs.mkdirSync(imagePath, { recursive: true });
|
|
128
110
|
const tarFile = `${imagePath}/${imageName.replace(':', '_')}.tar`;
|
|
129
|
-
let secretsInput = ' ';
|
|
130
|
-
let secretDockerInput = '';
|
|
131
111
|
let cache = '';
|
|
132
|
-
if (secrets === true) {
|
|
133
|
-
const envObj = dotenv.parse(
|
|
134
|
-
fs.readFileSync(
|
|
135
|
-
secretsPath && typeof secretsPath === 'string' ? secretsPath : `${getNpmRootPath()}/underpost/.env`,
|
|
136
|
-
'utf8',
|
|
137
|
-
),
|
|
138
|
-
);
|
|
139
|
-
for (const key of Object.keys(envObj)) {
|
|
140
|
-
secretsInput += ` && export ${key}="${envObj[key]}" `;
|
|
141
|
-
secretDockerInput += ` --secret id=${key},env=${key} \ `;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
112
|
if (reset === true) cache += ' --rm --no-cache';
|
|
145
|
-
if (path
|
|
113
|
+
if (path)
|
|
146
114
|
shellExec(
|
|
147
|
-
`cd ${path}
|
|
115
|
+
`cd ${path} && sudo podman build -f ./${
|
|
148
116
|
dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
|
|
149
|
-
} -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}
|
|
117
|
+
} -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache} --network host`,
|
|
150
118
|
);
|
|
151
|
-
|
|
152
119
|
if (podmanSave === true) {
|
|
153
120
|
if (fs.existsSync(tarFile)) fs.removeSync(tarFile);
|
|
154
121
|
shellExec(`podman save -o ${tarFile} ${podManImg}`);
|
package/src/cli/index.js
CHANGED
|
@@ -138,9 +138,6 @@ program
|
|
|
138
138
|
.option('--head-components <paths>', 'Comma-separated SSR head component paths.')
|
|
139
139
|
.option('--body-components <paths>', 'Comma-separated SSR body component paths.')
|
|
140
140
|
|
|
141
|
-
.option('--deploy-id <deploy-id>', 'Build static assets for a specific deployment ID.')
|
|
142
|
-
.option('--build', 'Triggers the static build process for the specified deployment ID.')
|
|
143
|
-
.option('--build-host <build-host>', 'Sets a custom build host for static documents or assets.')
|
|
144
141
|
.option('--build-path <build-path>', 'Sets a custom build path for static documents or assets.')
|
|
145
142
|
.option('--env <env>', 'Sets the environment for the static build (e.g., "development", "production").')
|
|
146
143
|
.option('--minify', 'Minify HTML output (default: true for production).')
|
|
@@ -153,6 +150,11 @@ program
|
|
|
153
150
|
.option('--dir <dir>', 'HTML dir attribute (default: ltr).')
|
|
154
151
|
.option('--dev', 'Sets the development cli context')
|
|
155
152
|
|
|
153
|
+
.option(
|
|
154
|
+
'--run-sv [port]',
|
|
155
|
+
'Start a standalone Express static server to preview the static build (default port: 5000).',
|
|
156
|
+
)
|
|
157
|
+
|
|
156
158
|
.description(`Manages static build of page, bundles, and documentation with comprehensive customization options.`)
|
|
157
159
|
.action(Underpost.static.callback);
|
|
158
160
|
|
|
@@ -165,6 +167,7 @@ program
|
|
|
165
167
|
.option('--filter <keyword>', 'Filters the list by matching key or value (only for list operation).')
|
|
166
168
|
.option('--deploy-id <deploy-id>', 'Sets the deployment configuration ID for the operation context.')
|
|
167
169
|
.option('--build', 'Sets the build context for the operation.')
|
|
170
|
+
.option('--copy', 'Copies the configuration value to the clipboard (only for get operation).')
|
|
168
171
|
.description(`Manages Underpost configurations using various operators.`)
|
|
169
172
|
.action((...args) => Underpost.env[args[0]](args[1], args[2], args[3]));
|
|
170
173
|
|
|
@@ -320,8 +323,6 @@ program
|
|
|
320
323
|
.option('--kubeadm', 'Set kubeadm cluster env image context management.')
|
|
321
324
|
.option('--k3s', 'Set k3s cluster env image context management.')
|
|
322
325
|
.option('--node-name', 'Set node name for kubeadm or k3s cluster env image context management.')
|
|
323
|
-
.option('--secrets', 'Includes Dockerfile environment secrets during the build.')
|
|
324
|
-
.option('--secrets-path [secrets-path]', 'Specifies a custom path for Dockerfile environment secrets.')
|
|
325
326
|
.option('--reset', 'Performs a build without using the cache.')
|
|
326
327
|
.option('--dev', 'Use development mode.')
|
|
327
328
|
.option('--pull-dockerhub <dockerhub-image>', 'Sets a custom Docker Hub image for base image pulls.')
|
|
@@ -406,10 +407,26 @@ program
|
|
|
406
407
|
'[job-list]',
|
|
407
408
|
`A comma-separated list of job IDs. Options: ${Underpost.cron.getJobsIDs()}. Defaults to all available jobs.`,
|
|
408
409
|
)
|
|
409
|
-
.option('--
|
|
410
|
-
.option('--
|
|
411
|
-
.option(
|
|
412
|
-
|
|
410
|
+
.option('--generate-k8s-cronjobs', 'Generates Kubernetes CronJob YAML manifests from cron configuration.')
|
|
411
|
+
.option('--apply', 'Applies generated K8s CronJob manifests to the cluster via kubectl.')
|
|
412
|
+
.option(
|
|
413
|
+
'--setup-start [deploy-id]',
|
|
414
|
+
'Updates deploy-id package.json start script and generates+applies its K8s CronJob manifests.',
|
|
415
|
+
)
|
|
416
|
+
.option('--namespace <namespace>', 'Kubernetes namespace for the CronJob resources (default: "default").')
|
|
417
|
+
.option('--image <image>', 'Custom container image for the CronJob pods.')
|
|
418
|
+
.option('--git', 'Pass --git flag to cron job execution.')
|
|
419
|
+
.option('--cmd <cmd>', 'Optional pre-script commands to run before cron execution.')
|
|
420
|
+
.option('--dev', 'Use local ./ base path instead of global underpost installation.')
|
|
421
|
+
.option('--k3s', 'Use k3s cluster context (apply directly on host).')
|
|
422
|
+
.option('--kind', 'Use kind cluster context (apply via kind-worker container).')
|
|
423
|
+
.option('--kubeadm', 'Use kubeadm cluster context (apply directly on host).')
|
|
424
|
+
.option('--dry-run', 'Preview cron jobs without executing them.')
|
|
425
|
+
.option(
|
|
426
|
+
'--create-job-now',
|
|
427
|
+
'After applying manifests, immediately create a Job from each CronJob (requires --apply).',
|
|
428
|
+
)
|
|
429
|
+
.description('Manages cron jobs: execute jobs directly or generate and apply K8s CronJob manifests.')
|
|
413
430
|
.action(Underpost.cron.callback);
|
|
414
431
|
|
|
415
432
|
program
|
|
@@ -538,7 +555,11 @@ program
|
|
|
538
555
|
.option('--expose', 'Enables service exposure for the runner execution.')
|
|
539
556
|
.option('--conf-server-path <conf-server-path>', 'Sets a custom configuration server path.')
|
|
540
557
|
.option('--underpost-root <underpost-root>', 'Sets a custom Underpost root path.')
|
|
541
|
-
.option('--cron-jobs <jobs>', '
|
|
558
|
+
.option('--cmd-cron-jobs <cmd-cron-jobs>', 'Pre-script commands to run before cron job execution.')
|
|
559
|
+
.option(
|
|
560
|
+
'--deploy-id-cron-jobs <deploy-id-cron-jobs>',
|
|
561
|
+
'Specifies deployment IDs to synchronize cron jobs with during execution.',
|
|
562
|
+
)
|
|
542
563
|
.option('--timezone <timezone>', 'Sets the timezone for the runner execution.')
|
|
543
564
|
.option('--kubeadm', 'Sets the kubeadm cluster context for the runner execution.')
|
|
544
565
|
.option('--k3s', 'Sets the k3s cluster context for the runner execution.')
|
|
@@ -571,6 +592,11 @@ program
|
|
|
571
592
|
'--monitor-status-max-attempts <attempts>',
|
|
572
593
|
'Sets the maximum number of status check attempts (default: 600).',
|
|
573
594
|
)
|
|
595
|
+
.option('--dry-run', 'Preview operations without executing them.')
|
|
596
|
+
.option(
|
|
597
|
+
'--create-job-now',
|
|
598
|
+
'After applying cron manifests, immediately create a Job from each CronJob (forwarded to cron runner).',
|
|
599
|
+
)
|
|
574
600
|
.description('Runs specified scripts using various runners.')
|
|
575
601
|
.action(Underpost.run.callback);
|
|
576
602
|
|
|
@@ -629,6 +655,10 @@ program
|
|
|
629
655
|
)
|
|
630
656
|
.option('--ipxe', 'Chainloads iPXE to normalize identity before commissioning.')
|
|
631
657
|
.option('--ipxe-rebuild', 'Forces rebuild of iPXE binary with embedded boot script.')
|
|
658
|
+
.option(
|
|
659
|
+
'--ipxe-build-iso <iso-path>',
|
|
660
|
+
'Builds a standalone iPXE ISO with embedded script for the specified workflow ID.',
|
|
661
|
+
)
|
|
632
662
|
.option('--install-packer', 'Installs Packer CLI.')
|
|
633
663
|
.option(
|
|
634
664
|
'--packer-maas-image-template <template-path>',
|
|
@@ -650,6 +680,10 @@ program
|
|
|
650
680
|
.option('--remove-machines <system-ids>', 'Removes baremetal machines by comma-separated system IDs, or use "all"')
|
|
651
681
|
.option('--clear-discovered', 'Clears all discovered baremetal machines from the database.')
|
|
652
682
|
.option('--commission', 'Init workflow for commissioning a physical machine.')
|
|
683
|
+
.option(
|
|
684
|
+
'--bootstrap-http-server-run',
|
|
685
|
+
'Runs a temporary bootstrap HTTP server for generic purposes such as serving iPXE scripts or ISO images during commissioning.',
|
|
686
|
+
)
|
|
653
687
|
.option(
|
|
654
688
|
'--bootstrap-http-server-path <path>',
|
|
655
689
|
'Sets a custom bootstrap HTTP server path for baremetal commissioning.',
|
|
@@ -661,6 +695,7 @@ program
|
|
|
661
695
|
.option('--iso-url <url>', 'Uses a custom ISO URL for baremetal machine commissioning.')
|
|
662
696
|
.option('--nfs-build', 'Builds an NFS root filesystem for a workflow id config architecture using QEMU emulation.')
|
|
663
697
|
.option('--nfs-mount', 'Mounts the NFS root filesystem for a workflow id config architecture.')
|
|
698
|
+
.option('--nfs-reset', 'Resets the NFS server completely, closing all connections before reloading exports.')
|
|
664
699
|
.option('--nfs-unmount', 'Unmounts the NFS root filesystem for a workflow id config architecture.')
|
|
665
700
|
.option('--nfs-build-server', 'Builds the NFS server for a workflow id config architecture.')
|
|
666
701
|
.option('--nfs-sh', 'Copies QEMU emulation root entrypoint shell command to the clipboard.')
|
|
@@ -672,7 +707,17 @@ program
|
|
|
672
707
|
.option('--rocky-tools-test', 'Tests rocky linux tools in chroot environment.')
|
|
673
708
|
.option('--bootcmd <bootcmd-list>', 'Comma-separated list of boot commands to execute.')
|
|
674
709
|
.option('--runcmd <runcmd-list>', 'Comma-separated list of run commands to execute.')
|
|
675
|
-
.option(
|
|
710
|
+
.option(
|
|
711
|
+
'--logs <log-id>',
|
|
712
|
+
`Displays logs for log id: ${[
|
|
713
|
+
'dhcp',
|
|
714
|
+
'dhcp-lease',
|
|
715
|
+
'dhcp-lan',
|
|
716
|
+
'cloud-init',
|
|
717
|
+
'cloud-init-machine',
|
|
718
|
+
'cloud-init-config',
|
|
719
|
+
]}`,
|
|
720
|
+
)
|
|
676
721
|
.option('--dev', 'Sets the development context environment for baremetal operations.')
|
|
677
722
|
.option('--ls', 'Lists available boot resources and machines.')
|
|
678
723
|
.description(
|