@underpostnet/underpost 2.99.4 → 2.99.5
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 +2 -2
- package/baremetal/commission-workflows.json +52 -0
- package/bin/deploy.js +53 -45
- package/cli.md +7 -14
- package/examples/static-page/README.md +55 -378
- package/examples/static-page/ssr-components/CustomPage.js +1 -13
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +2 -2
- package/scripts/disk-devices.sh +13 -0
- package/src/cli/baremetal.js +562 -164
- package/src/cli/cloud-init.js +97 -79
- package/src/cli/env.js +4 -1
- package/src/cli/image.js +4 -37
- package/src/cli/index.js +16 -6
- package/src/cli/repository.js +3 -1
- package/src/cli/run.js +13 -0
- package/src/cli/secrets.js +0 -34
- package/src/cli/static.js +0 -26
- package/src/index.js +19 -5
- 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
|
@@ -261,6 +261,12 @@ curl -X POST \\
|
|
|
261
261
|
* @param {boolean} params.ubuntuToolsBuild - Flag to determine if Ubuntu tools should be built.
|
|
262
262
|
* @param {string} [params.bootcmd] - Optional custom commands to run during boot.
|
|
263
263
|
* @param {string} [params.runcmd] - Optional custom commands to run during first boot.
|
|
264
|
+
* @param {object} [params.write_files] - Optional array of files to write during cloud-init, each with path, permissions, owner, and content.
|
|
265
|
+
* @param {string} [params.write_files[].path] - The file path where the content will be written on the target machine.
|
|
266
|
+
* @param {string} [params.write_files[].permissions] - The file permissions to set for the written file (e.g., '0644').
|
|
267
|
+
* @param {string} [params.write_files[].owner] - The owner of the written file (e.g., 'root:root').
|
|
268
|
+
* @param {string} [params.write_files[].content] - The content to write into the file.
|
|
269
|
+
* @param {boolean} [params.write_files[].defer] - Whether to defer writing the file until the 'final' stage of cloud-init.
|
|
264
270
|
* @param {object} [authCredentials={}] - Optional MAAS authentication credentials.
|
|
265
271
|
* @returns {object} The generated cloud-init configuration content.
|
|
266
272
|
* @memberof UnderpostCloudInit
|
|
@@ -278,6 +284,15 @@ curl -X POST \\
|
|
|
278
284
|
ubuntuToolsBuild,
|
|
279
285
|
bootcmd: bootcmdParam,
|
|
280
286
|
runcmd: runcmdParam,
|
|
287
|
+
write_files = [
|
|
288
|
+
{
|
|
289
|
+
path: '',
|
|
290
|
+
permissions: '',
|
|
291
|
+
owner: '',
|
|
292
|
+
content: '',
|
|
293
|
+
defer: false,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
281
296
|
},
|
|
282
297
|
authCredentials = { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
|
|
283
298
|
) {
|
|
@@ -305,6 +320,7 @@ curl -X POST \\
|
|
|
305
320
|
let runcmd = [
|
|
306
321
|
'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
|
|
307
322
|
'echo "Init runcmd"',
|
|
323
|
+
'systemctl enable --now ssh',
|
|
308
324
|
'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
|
|
309
325
|
];
|
|
310
326
|
|
|
@@ -334,12 +350,14 @@ curl -X POST \\
|
|
|
334
350
|
name: process.env.MAAS_ADMIN_USERNAME,
|
|
335
351
|
sudo: ['ALL=(ALL) NOPASSWD:ALL'],
|
|
336
352
|
shell: '/bin/bash',
|
|
337
|
-
lock_passwd:
|
|
353
|
+
lock_passwd: true,
|
|
338
354
|
groups: 'sudo,users,admin,wheel,lxd',
|
|
339
|
-
plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
|
|
355
|
+
// plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
|
|
340
356
|
ssh_authorized_keys: [fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')],
|
|
341
357
|
},
|
|
342
358
|
],
|
|
359
|
+
disable_root: true,
|
|
360
|
+
ssh_pwauth: false,
|
|
343
361
|
timezone,
|
|
344
362
|
ntp: {
|
|
345
363
|
enabled: true,
|
|
@@ -360,6 +378,7 @@ curl -X POST \\
|
|
|
360
378
|
'smartmontools',
|
|
361
379
|
'net-tools',
|
|
362
380
|
'util-linux',
|
|
381
|
+
'openssh-server',
|
|
363
382
|
],
|
|
364
383
|
resize_rootfs: false,
|
|
365
384
|
growpart: { mode: 'off' },
|
|
@@ -380,7 +399,7 @@ curl -X POST \\
|
|
|
380
399
|
final_message: '====== Cloud init finished ======',
|
|
381
400
|
bootcmd,
|
|
382
401
|
runcmd,
|
|
383
|
-
|
|
402
|
+
write_files,
|
|
384
403
|
preserve_hostname: false,
|
|
385
404
|
cloud_init_modules: [
|
|
386
405
|
// minimal for commissioning
|
|
@@ -394,37 +413,37 @@ curl -X POST \\
|
|
|
394
413
|
'ssh', // enable/configure SSH for temporary access
|
|
395
414
|
|
|
396
415
|
// optional modules (commented out by default)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
416
|
+
'migrator',
|
|
417
|
+
'seed_random',
|
|
418
|
+
'growpart',
|
|
419
|
+
'resizefs',
|
|
420
|
+
'users-groups',
|
|
402
421
|
],
|
|
403
422
|
cloud_config_modules: [
|
|
404
423
|
// minimal so MAAS can run commissioning scripts
|
|
405
424
|
'runcmd', // commissioning / final script execution
|
|
406
425
|
'mounts', // mount devices during commissioning if needed
|
|
407
|
-
|
|
426
|
+
'ntp', // optional — enable if you want time sync
|
|
408
427
|
|
|
409
428
|
// 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
|
-
|
|
429
|
+
'emit_upstart',
|
|
430
|
+
'disk_setup',
|
|
431
|
+
'ssh-import-id',
|
|
432
|
+
'locale',
|
|
433
|
+
'set-passwords',
|
|
434
|
+
'grub-dpkg',
|
|
435
|
+
'apt-pipelining',
|
|
436
|
+
'apt-configure',
|
|
437
|
+
'package-update-upgrade-install', // heavy; do NOT enable by default
|
|
438
|
+
'landscape',
|
|
439
|
+
'timezone',
|
|
440
|
+
'puppet',
|
|
441
|
+
'chef',
|
|
442
|
+
'salt-minion',
|
|
443
|
+
'mcollective',
|
|
444
|
+
'disable-ec2-metadata',
|
|
445
|
+
'byobu',
|
|
446
|
+
'ssh-import-id', // duplicate in original list
|
|
428
447
|
],
|
|
429
448
|
cloud_final_modules: [
|
|
430
449
|
// minimal suggestions so final scripts run and node reports status
|
|
@@ -432,64 +451,19 @@ curl -X POST \\
|
|
|
432
451
|
'final-message', // useful for logs/reporting
|
|
433
452
|
|
|
434
453
|
// optional / commented
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
454
|
+
'rightscale_userdata',
|
|
455
|
+
'scripts-vendor',
|
|
456
|
+
'scripts-per-once',
|
|
457
|
+
'scripts-user',
|
|
458
|
+
'ssh-authkey-fingerprints',
|
|
459
|
+
'keys-to-console',
|
|
460
|
+
'power-state-change', // use carefully (can poweroff/reboot)
|
|
442
461
|
],
|
|
443
462
|
});
|
|
444
463
|
|
|
445
464
|
return { cloudConfigSrc };
|
|
446
465
|
},
|
|
447
466
|
|
|
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
467
|
/**
|
|
494
468
|
* @method generateCloudConfig
|
|
495
469
|
* @description Generates a generic cloud-init configuration string.
|
|
@@ -518,6 +492,7 @@ curl -X POST \\
|
|
|
518
492
|
growpart,
|
|
519
493
|
network,
|
|
520
494
|
runcmd,
|
|
495
|
+
write_files,
|
|
521
496
|
final_message,
|
|
522
497
|
bootcmd,
|
|
523
498
|
disable_root,
|
|
@@ -645,6 +620,21 @@ curl -X POST \\
|
|
|
645
620
|
runcmd.forEach((cmd) => yaml.push(` - ${cmd}`));
|
|
646
621
|
}
|
|
647
622
|
|
|
623
|
+
if (write_files) {
|
|
624
|
+
yaml.push('write_files:');
|
|
625
|
+
write_files.forEach((file) => {
|
|
626
|
+
yaml.push(` - path: ${file.path}`);
|
|
627
|
+
if (file.encoding) yaml.push(` encoding: ${file.encoding}`);
|
|
628
|
+
if (file.owner) yaml.push(` owner: ${file.owner}`);
|
|
629
|
+
if (file.permissions) yaml.push(` permissions: '${file.permissions}'`);
|
|
630
|
+
if (file.defer) yaml.push(` defer: ${file.defer}`);
|
|
631
|
+
if (file.content) {
|
|
632
|
+
yaml.push(` content: |`);
|
|
633
|
+
file.content.split('\n').forEach((line) => yaml.push(` ${line}`));
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
648
638
|
if (final_message) yaml.push(`final_message: "${final_message}"`);
|
|
649
639
|
|
|
650
640
|
if (bootcmd) {
|
|
@@ -669,6 +659,34 @@ curl -X POST \\
|
|
|
669
659
|
}
|
|
670
660
|
|
|
671
661
|
return yaml.join('\n');
|
|
662
|
+
} /**
|
|
663
|
+
* @method kernelParamsFactory
|
|
664
|
+
* @description Generates the kernel parameters for the target machine's bootloader configuration,
|
|
665
|
+
* including the necessary parameters to enable cloud-init with a specific configuration URL and logging settings.
|
|
666
|
+
* @param {array} cmd - The existing array of kernel parameters to which cloud-init parameters will be appended.
|
|
667
|
+
* @param {object} options - Options for generating kernel parameters.
|
|
668
|
+
* @param {string} options.ipDhcpServer - The IP address of the DHCP server.
|
|
669
|
+
* @param {object} [options.machine] - The machine information, including system_id for constructing the cloud-init configuration URL.
|
|
670
|
+
* @param {string} [options.machine.system_id] - The unique identifier of the machine, used to fetch the correct cloud-init preseed configuration from MAAS.
|
|
671
|
+
* @return {array} The modified array of kernel parameters with cloud-init parameters included.
|
|
672
|
+
* @memberof UnderpostCloudInit
|
|
673
|
+
*/,
|
|
674
|
+
kernelParamsFactory(
|
|
675
|
+
macAddress,
|
|
676
|
+
cmd = [],
|
|
677
|
+
options = {
|
|
678
|
+
ipDhcpServer: '',
|
|
679
|
+
bootstrapHttpServerPort: 8888,
|
|
680
|
+
hostname: '',
|
|
681
|
+
machine: {
|
|
682
|
+
system_id: '',
|
|
683
|
+
},
|
|
684
|
+
authCredentials: { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
|
|
685
|
+
},
|
|
686
|
+
) {
|
|
687
|
+
const { ipDhcpServer, bootstrapHttpServerPort, hostname } = options;
|
|
688
|
+
const cloudConfigUrl = `http://${ipDhcpServer}:${bootstrapHttpServerPort}/${hostname}/cloud-init/user-data`;
|
|
689
|
+
return cmd.concat([`cloud-config-url=${cloudConfigUrl}`]);
|
|
672
690
|
},
|
|
673
691
|
};
|
|
674
692
|
}
|
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).')
|
|
@@ -165,6 +162,7 @@ program
|
|
|
165
162
|
.option('--filter <keyword>', 'Filters the list by matching key or value (only for list operation).')
|
|
166
163
|
.option('--deploy-id <deploy-id>', 'Sets the deployment configuration ID for the operation context.')
|
|
167
164
|
.option('--build', 'Sets the build context for the operation.')
|
|
165
|
+
.option('--copy', 'Copies the configuration value to the clipboard (only for get operation).')
|
|
168
166
|
.description(`Manages Underpost configurations using various operators.`)
|
|
169
167
|
.action((...args) => Underpost.env[args[0]](args[1], args[2], args[3]));
|
|
170
168
|
|
|
@@ -320,8 +318,6 @@ program
|
|
|
320
318
|
.option('--kubeadm', 'Set kubeadm cluster env image context management.')
|
|
321
319
|
.option('--k3s', 'Set k3s cluster env image context management.')
|
|
322
320
|
.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
321
|
.option('--reset', 'Performs a build without using the cache.')
|
|
326
322
|
.option('--dev', 'Use development mode.')
|
|
327
323
|
.option('--pull-dockerhub <dockerhub-image>', 'Sets a custom Docker Hub image for base image pulls.')
|
|
@@ -629,6 +625,10 @@ program
|
|
|
629
625
|
)
|
|
630
626
|
.option('--ipxe', 'Chainloads iPXE to normalize identity before commissioning.')
|
|
631
627
|
.option('--ipxe-rebuild', 'Forces rebuild of iPXE binary with embedded boot script.')
|
|
628
|
+
.option(
|
|
629
|
+
'--ipxe-build-iso <iso-path>',
|
|
630
|
+
'Builds a standalone iPXE ISO with embedded script for the specified workflow ID.',
|
|
631
|
+
)
|
|
632
632
|
.option('--install-packer', 'Installs Packer CLI.')
|
|
633
633
|
.option(
|
|
634
634
|
'--packer-maas-image-template <template-path>',
|
|
@@ -672,7 +672,17 @@ program
|
|
|
672
672
|
.option('--rocky-tools-test', 'Tests rocky linux tools in chroot environment.')
|
|
673
673
|
.option('--bootcmd <bootcmd-list>', 'Comma-separated list of boot commands to execute.')
|
|
674
674
|
.option('--runcmd <runcmd-list>', 'Comma-separated list of run commands to execute.')
|
|
675
|
-
.option(
|
|
675
|
+
.option(
|
|
676
|
+
'--logs <log-id>',
|
|
677
|
+
`Displays logs for log id: ${[
|
|
678
|
+
'dhcp',
|
|
679
|
+
'dhcp-lease',
|
|
680
|
+
'dhcp-lan',
|
|
681
|
+
'cloud-init',
|
|
682
|
+
'cloud-init-machine',
|
|
683
|
+
'cloud-init-config',
|
|
684
|
+
]}`,
|
|
685
|
+
)
|
|
676
686
|
.option('--dev', 'Sets the development context environment for baremetal operations.')
|
|
677
687
|
.option('--ls', 'Lists available boot resources and machines.')
|
|
678
688
|
.description(
|
package/src/cli/repository.js
CHANGED
|
@@ -152,6 +152,7 @@ class UnderpostRepository {
|
|
|
152
152
|
.map((commitData, i) => `${i === 0 ? '' : ' && '}git ${diffCmd} ${commitData.hash}`)
|
|
153
153
|
.join('');
|
|
154
154
|
if (history[0]) {
|
|
155
|
+
let index = history.length;
|
|
155
156
|
for (const commit of history) {
|
|
156
157
|
console.log(
|
|
157
158
|
shellExec(`git show -s --format=%ci ${commit.hash}`, {
|
|
@@ -160,7 +161,8 @@ class UnderpostRepository {
|
|
|
160
161
|
disableLog: true,
|
|
161
162
|
}).trim().green,
|
|
162
163
|
);
|
|
163
|
-
console.log(commit.hash.yellow, commit.message);
|
|
164
|
+
console.log(`${index}`.magenta, commit.hash.yellow, commit.message);
|
|
165
|
+
index--;
|
|
164
166
|
console.log(
|
|
165
167
|
shellExec(`git show --name-status --pretty="" ${commit.hash}`, {
|
|
166
168
|
stdout: true,
|
package/src/cli/run.js
CHANGED
|
@@ -1234,6 +1234,19 @@ EOF
|
|
|
1234
1234
|
shellExec(`./scripts/disk-clean.sh`);
|
|
1235
1235
|
},
|
|
1236
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* @method disk-devices
|
|
1239
|
+
* @description Executes the `disk-devices.sh` script to display information about disk devices.
|
|
1240
|
+
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1241
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1242
|
+
* @memberof UnderpostRun
|
|
1243
|
+
*/
|
|
1244
|
+
'disk-devices': async (path = '/', options = DEFAULT_OPTION) => {
|
|
1245
|
+
const { underpostRoot } = options;
|
|
1246
|
+
shellExec(`chmod +x ${underpostRoot}/scripts/disk-devices.sh`);
|
|
1247
|
+
shellExec(`${underpostRoot}/scripts/disk-devices.sh`);
|
|
1248
|
+
},
|
|
1249
|
+
|
|
1237
1250
|
/**
|
|
1238
1251
|
* @method disk-usage
|
|
1239
1252
|
* @description Displays disk usage statistics using the `du` command, sorted by size.
|
package/src/cli/secrets.js
CHANGED
|
@@ -18,40 +18,6 @@ dotenv.config();
|
|
|
18
18
|
*/
|
|
19
19
|
class UnderpostSecret {
|
|
20
20
|
static API = {
|
|
21
|
-
/**
|
|
22
|
-
* @method docker
|
|
23
|
-
* @description Manages the secrets of the application.
|
|
24
|
-
* @memberof UnderpostSecret
|
|
25
|
-
*/
|
|
26
|
-
docker: {
|
|
27
|
-
/**
|
|
28
|
-
* @method init
|
|
29
|
-
* @description Initializes the docker secrets.
|
|
30
|
-
* @memberof UnderpostSecret
|
|
31
|
-
*/
|
|
32
|
-
init() {
|
|
33
|
-
shellExec(`docker swarm init`);
|
|
34
|
-
},
|
|
35
|
-
/**
|
|
36
|
-
* @method createFromEnvFile
|
|
37
|
-
* @description Creates a secret from an env file.
|
|
38
|
-
* @param {string} envPath - The path to the env file.
|
|
39
|
-
* @memberof UnderpostSecret
|
|
40
|
-
*/
|
|
41
|
-
createFromEnvFile(envPath) {
|
|
42
|
-
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
43
|
-
for (const key of Object.keys(envObj)) {
|
|
44
|
-
Underpost.secret.docker.set(key, envObj[key]);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
set(key, value) {
|
|
48
|
-
shellExec(`docker secret rm ${key}`);
|
|
49
|
-
shellExec(`echo "${value}" | docker secret create ${key} -`);
|
|
50
|
-
},
|
|
51
|
-
list() {
|
|
52
|
-
shellExec(`docker secret ls`);
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
21
|
/**
|
|
56
22
|
* @method underpost
|
|
57
23
|
* @description Manages the secrets of the application.
|
package/src/cli/static.js
CHANGED
|
@@ -68,11 +68,8 @@ const logger = loggerFactory(import.meta);
|
|
|
68
68
|
* @property {string} [page=''] - SSR component path to render
|
|
69
69
|
* @property {string} [title='Home'] - Page title (deprecated: use metadata.title)
|
|
70
70
|
* @property {string} [outputPath='.'] - Output file path
|
|
71
|
-
* @property {string} [deployId=''] - Deployment identifier
|
|
72
|
-
* @property {string} [buildHost=''] - Build host URL
|
|
73
71
|
* @property {string} [buildPath='/'] - Build path
|
|
74
72
|
* @property {string} [env='production'] - Environment (development/production)
|
|
75
|
-
* @property {boolean} [build=false] - Whether to trigger build
|
|
76
73
|
* @property {boolean} [dev=false] - Development mode flag
|
|
77
74
|
* @property {boolean} [minify=true] - Minify HTML output
|
|
78
75
|
* @property {MetadataOptions} [metadata={}] - Comprehensive metadata options
|
|
@@ -95,11 +92,8 @@ const DefaultStaticGenerationOptions = {
|
|
|
95
92
|
page: '',
|
|
96
93
|
title: '',
|
|
97
94
|
outputPath: '',
|
|
98
|
-
deployId: '',
|
|
99
|
-
buildHost: '',
|
|
100
95
|
buildPath: '/',
|
|
101
96
|
env: 'production',
|
|
102
|
-
build: false,
|
|
103
97
|
dev: false,
|
|
104
98
|
minify: true,
|
|
105
99
|
metadata: {},
|
|
@@ -385,11 +379,8 @@ class UnderpostStatic {
|
|
|
385
379
|
* @param {string} [options.page] - Path to the SSR component to render
|
|
386
380
|
* @param {string} [options.title] - Page title (deprecated: use metadata.title)
|
|
387
381
|
* @param {string} [options.outputPath] - Output file path
|
|
388
|
-
* @param {string} [options.deployId] - Deployment identifier
|
|
389
|
-
* @param {string} [options.buildHost] - Build host URL
|
|
390
382
|
* @param {string} [options.buildPath='/'] - Build path
|
|
391
383
|
* @param {string} [options.env='production'] - Environment (development/production)
|
|
392
|
-
* @param {boolean} [options.build=false] - Whether to trigger build
|
|
393
384
|
* @param {boolean} [options.minify=true] - Minify HTML output
|
|
394
385
|
* @param {MetadataOptions} [options.metadata={}] - Comprehensive metadata options
|
|
395
386
|
* @param {Object} [options.scripts={}] - Script injection options
|
|
@@ -601,23 +592,6 @@ class UnderpostStatic {
|
|
|
601
592
|
throw error;
|
|
602
593
|
}
|
|
603
594
|
}
|
|
604
|
-
|
|
605
|
-
// Trigger build if requested
|
|
606
|
-
if (options.deployId && options.build) {
|
|
607
|
-
try {
|
|
608
|
-
logger.info(`Triggering build for deployment: ${options.deployId}`);
|
|
609
|
-
|
|
610
|
-
shellExec(`underpost env ${options.deployId} ${options.env}`);
|
|
611
|
-
shellExec(
|
|
612
|
-
`npm run build ${options.deployId}${options.buildHost ? ` ${options.buildHost} ${options.buildPath}` : ``}`,
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
logger.info('Build completed successfully');
|
|
616
|
-
} catch (error) {
|
|
617
|
-
logger.error(`Build error: ${error.message}`);
|
|
618
|
-
throw error;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
595
|
},
|
|
622
596
|
|
|
623
597
|
/**
|
package/src/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import UnderpostDns from './server/dns.js';
|
|
|
25
25
|
import UnderpostBackup from './server/backup.js';
|
|
26
26
|
import UnderpostCron from './server/cron.js';
|
|
27
27
|
import UnderpostStartUp from './server/start.js';
|
|
28
|
-
import
|
|
28
|
+
import UnderpostTLS from './server/tls.js';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Underpost main module methods
|
|
@@ -39,7 +39,16 @@ class Underpost {
|
|
|
39
39
|
* @type {String}
|
|
40
40
|
* @memberof Underpost
|
|
41
41
|
*/
|
|
42
|
-
static version = 'v2.99.
|
|
42
|
+
static version = 'v2.99.5';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Required Node.js major version
|
|
46
|
+
* @static
|
|
47
|
+
* @type {String}
|
|
48
|
+
* @memberof Underpost
|
|
49
|
+
*/
|
|
50
|
+
static majorNodejsVersion = 'v24';
|
|
51
|
+
|
|
43
52
|
/**
|
|
44
53
|
* Repository cli API
|
|
45
54
|
* @static
|
|
@@ -233,14 +242,19 @@ class Underpost {
|
|
|
233
242
|
/**
|
|
234
243
|
* TLS/SSL server utilities API
|
|
235
244
|
* @static
|
|
236
|
-
* @type {
|
|
245
|
+
* @type {UnderpostTLS.API}
|
|
237
246
|
* @memberof Underpost
|
|
238
247
|
*/
|
|
239
248
|
static get tls() {
|
|
240
|
-
return
|
|
249
|
+
return UnderpostTLS.API;
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
|
|
253
|
+
if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
|
|
254
|
+
console.warn(
|
|
255
|
+
`${`Underpost Warning: Required Node.js version is `.red}${`${Underpost.majorNodejsVersion}.x`.bgBlue.bold.white}${`, you are using `.red}${process.version.bgRed.bold.white}`,
|
|
256
|
+
);
|
|
257
|
+
|
|
244
258
|
const up = Underpost;
|
|
245
259
|
|
|
246
260
|
const underpost = Underpost;
|
|
@@ -269,7 +283,7 @@ export {
|
|
|
269
283
|
UnderpostBackup,
|
|
270
284
|
UnderpostCron,
|
|
271
285
|
UnderpostStartUp,
|
|
272
|
-
|
|
286
|
+
UnderpostTLS,
|
|
273
287
|
};
|
|
274
288
|
|
|
275
289
|
export default Underpost;
|