@underpostnet/underpost 2.90.4 → 2.95.0

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 (41) hide show
  1. package/.github/workflows/pwa-microservices-template-page.cd.yml +5 -4
  2. package/.github/workflows/release.cd.yml +7 -7
  3. package/README.md +7 -8
  4. package/bin/build.js +6 -1
  5. package/bin/deploy.js +2 -196
  6. package/cli.md +154 -80
  7. package/manifests/deployment/dd-default-development/deployment.yaml +4 -4
  8. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  9. package/package.json +1 -1
  10. package/scripts/disk-clean.sh +216 -0
  11. package/scripts/rocky-setup.sh +1 -0
  12. package/scripts/ssh-cluster-info.sh +4 -3
  13. package/src/cli/cluster.js +1 -1
  14. package/src/cli/db.js +1143 -201
  15. package/src/cli/deploy.js +93 -24
  16. package/src/cli/env.js +2 -2
  17. package/src/cli/image.js +198 -133
  18. package/src/cli/index.js +111 -44
  19. package/src/cli/lxd.js +73 -74
  20. package/src/cli/monitor.js +20 -9
  21. package/src/cli/repository.js +212 -5
  22. package/src/cli/run.js +207 -74
  23. package/src/cli/ssh.js +642 -14
  24. package/src/client/components/core/CommonJs.js +0 -1
  25. package/src/db/mongo/MongooseDB.js +5 -1
  26. package/src/index.js +1 -1
  27. package/src/monitor.js +11 -1
  28. package/src/server/backup.js +1 -1
  29. package/src/server/conf.js +1 -1
  30. package/src/server/dns.js +242 -1
  31. package/src/server/process.js +6 -1
  32. package/src/server/start.js +2 -0
  33. package/scripts/snap-clean.sh +0 -26
  34. package/src/client/public/default/plantuml/client-conf.svg +0 -1
  35. package/src/client/public/default/plantuml/client-schema.svg +0 -1
  36. package/src/client/public/default/plantuml/cron-conf.svg +0 -1
  37. package/src/client/public/default/plantuml/cron-schema.svg +0 -1
  38. package/src/client/public/default/plantuml/server-conf.svg +0 -1
  39. package/src/client/public/default/plantuml/server-schema.svg +0 -1
  40. package/src/client/public/default/plantuml/ssr-conf.svg +0 -1
  41. package/src/client/public/default/plantuml/ssr-schema.svg +0 -1
package/src/cli/run.js CHANGED
@@ -19,10 +19,11 @@ import UnderpostTest from './test.js';
19
19
  import fs from 'fs-extra';
20
20
  import { range, setPad, timer } from '../client/components/core/CommonJs.js';
21
21
  import UnderpostDeploy from './deploy.js';
22
+ import UnderpostDB from './db.js';
22
23
  import UnderpostRootEnv from './env.js';
23
24
  import UnderpostRepository from './repository.js';
24
25
  import os from 'os';
25
- import Underpost from '../index.js';
26
+ import Underpost, { UnderpostSSH } from '../index.js';
26
27
  import dotenv from 'dotenv';
27
28
 
28
29
  const logger = loggerFactory(import.meta);
@@ -53,8 +54,6 @@ class UnderpostRun {
53
54
  * @property {string} namespace - The namespace to run in.
54
55
  * @property {boolean} build - Whether to build the image.
55
56
  * @property {number} replicas - The number of replicas to run.
56
- * @property {boolean} k3s - Whether to run in k3s mode.
57
- * @property {boolean} kubeadm - Whether to run in kubeadm mode.
58
57
  * @property {boolean} force - Whether to force the operation.
59
58
  * @property {boolean} reset - Whether to reset the operation.
60
59
  * @property {boolean} tls - Whether to use TLS.
@@ -65,7 +64,7 @@ class UnderpostRun {
65
64
  * @property {string} imagePullPolicy - The image pull policy for the container.
66
65
  * @property {string} apiVersion - The API version for the container.
67
66
  * @property {string} claimName - The claim name for the volume.
68
- * @property {string} kind - The kind of resource to create.
67
+ * @property {string} kindType - The kind of resource to create.
69
68
  * @property {boolean} terminal - Whether to open a terminal.
70
69
  * @property {number} devProxyPortOffset - The port offset for the development proxy.
71
70
  * @property {boolean} hostNetwork - Whether to use host networking.
@@ -78,6 +77,16 @@ class UnderpostRun {
78
77
  * @property {boolean} etcHosts - Whether to modify /etc/hosts.
79
78
  * @property {string} confServerPath - The configuration server path.
80
79
  * @property {string} underpostRoot - The root path of the Underpost installation.
80
+ * @property {string} cronJobs - The cron jobs to run.
81
+ * @property {string} timezone - The timezone to set.
82
+ * @property {boolean} kubeadm - Whether to run in kubeadm mode.
83
+ * @property {boolean} kind - Whether to run in kind mode.
84
+ * @property {boolean} k3s - Whether to run in k3s mode.
85
+ * @property {string} logType - The type of log to generate.
86
+ * @property {string} hosts - The hosts to use.
87
+ * @property {string} deployId - The deployment ID.
88
+ * @property {string} instanceId - The instance ID.
89
+ * @property {string} user - The user to run as.
81
90
  * @memberof UnderpostRun
82
91
  */
83
92
  static DEFAULT_OPTION = {
@@ -92,8 +101,6 @@ class UnderpostRun {
92
101
  namespace: 'default',
93
102
  build: false,
94
103
  replicas: 1,
95
- k3s: false,
96
- kubeadm: false,
97
104
  force: false,
98
105
  reset: false,
99
106
  tls: false,
@@ -104,7 +111,7 @@ class UnderpostRun {
104
111
  imagePullPolicy: '',
105
112
  apiVersion: '',
106
113
  claimName: '',
107
- kind: '',
114
+ kindType: '',
108
115
  terminal: false,
109
116
  devProxyPortOffset: 0,
110
117
  hostNetwork: false,
@@ -117,6 +124,16 @@ class UnderpostRun {
117
124
  etcHosts: false,
118
125
  confServerPath: '',
119
126
  underpostRoot: '',
127
+ cronJobs: '',
128
+ timezone: '',
129
+ kubeadm: false,
130
+ kind: false,
131
+ k3s: false,
132
+ logType: '',
133
+ hosts: '',
134
+ deployId: '',
135
+ instanceId: '',
136
+ user: '',
120
137
  };
121
138
  /**
122
139
  * @static
@@ -250,10 +267,29 @@ class UnderpostRun {
250
267
  );
251
268
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
252
269
  }
253
- shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config mongo`, { async: true });
254
- shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config valkey`, { async: true });
270
+
255
271
  {
256
- const hostListenResult = UnderpostDeploy.API.etcHostFactory(mongoHosts);
272
+ // Detect MongoDB primary pod using centralized method
273
+ let primaryMongoHost = 'mongodb-0.mongodb-service';
274
+ try {
275
+ const primaryPodName = UnderpostDB.API.getMongoPrimaryPodName({
276
+ namespace: options.namespace,
277
+ podName: 'mongodb-0',
278
+ });
279
+ // shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config mongo`, { async: true });
280
+ shellExec(`kubectl port-forward -n ${options.namespace} pod/${primaryPodName} 27017:27017`, { async: true });
281
+ shellExec(
282
+ `${baseCommand} deploy --expose --namespace ${options.namespace} --disable-update-underpost-config valkey`,
283
+ { async: true },
284
+ );
285
+ } catch (error) {
286
+ logger.warn('Failed to detect MongoDB primary pod, using default', {
287
+ error: error.message,
288
+ default: primaryMongoHost,
289
+ });
290
+ }
291
+
292
+ const hostListenResult = UnderpostDeploy.API.etcHostFactory([primaryMongoHost]);
257
293
  logger.info(hostListenResult.renderHosts);
258
294
  }
259
295
  },
@@ -268,7 +304,7 @@ class UnderpostRun {
268
304
  metadata: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
269
305
  const ports = '6379,27017';
270
306
  shellExec(`node bin run kill '${ports}'`);
271
- shellExec(`node bin run dev-cluster --dev --expose`, { async: true });
307
+ shellExec(`node bin run dev-cluster --dev --expose --namespace ${options.namespace}`, { async: true });
272
308
  console.log('Loading fordward services...');
273
309
  await timer(5000);
274
310
  shellExec(`node bin metadata --generate ${path}`);
@@ -317,8 +353,9 @@ class UnderpostRun {
317
353
  * @param {Object} options - The default underpost runner options for customizing workflow
318
354
  * @memberof UnderpostRun
319
355
  */
320
- 'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
356
+ 'ssh-cluster-info': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
321
357
  const { underpostRoot } = options;
358
+ if (options.deployId && options.user) await UnderpostSSH.API.setDefautlSshCredentials(options);
322
359
  shellExec(`chmod +x ${underpostRoot}/scripts/ssh-cluster-info.sh`);
323
360
  shellExec(`${underpostRoot}/scripts/ssh-cluster-info.sh`);
324
361
  },
@@ -361,7 +398,7 @@ class UnderpostRun {
361
398
  shellExec(`node bin run sync-replica template-deploy${nodeOptions}`);
362
399
  shellExec(`node bin env clean`);
363
400
  for (const deployId of fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').split(','))
364
- shellExec(`node bin/deploy update-default-conf ${deployId.trim()}`);
401
+ shellExec(`node bin new --default-conf --deploy-id ${deployId.trim()}`);
365
402
  if (path === 'cmt') {
366
403
  shellExec(`git add . && underpost cmt . build cluster-build`);
367
404
  shellExec(`cd engine-private && git add . && underpost cmt . build cluster-build`);
@@ -408,8 +445,8 @@ class UnderpostRun {
408
445
  * @param {Object} options - The default underpost runner options for customizing workflow
409
446
  * @memberof UnderpostRun
410
447
  */
411
- clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
412
- shellCd(path ?? `/home/dd/engine`);
448
+ clean: (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
449
+ shellCd(path ? path : `/home/dd/engine`);
413
450
  shellExec(`node bin/deploy clean-core-repo`);
414
451
  },
415
452
  /**
@@ -422,16 +459,25 @@ class UnderpostRun {
422
459
  pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
423
460
  if (!fs.existsSync(`/home/dd`) || !fs.existsSync(`/home/dd/engine`)) {
424
461
  fs.mkdirSync(`/home/dd`, { recursive: true });
425
- shellExec(`cd /home/dd && underpost clone ${process.env.GITHUB_USERNAME}/engine`);
462
+ shellExec(`cd /home/dd && underpost clone ${process.env.GITHUB_USERNAME}/engine`, {
463
+ silent: true,
464
+ });
426
465
  } else {
427
466
  shellExec(`underpost run clean`);
428
- shellExec(`cd /home/dd/engine && underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
467
+ shellExec(`cd /home/dd/engine && underpost pull . ${process.env.GITHUB_USERNAME}/engine`, {
468
+ silent: true,
469
+ });
429
470
  }
430
471
  if (!fs.existsSync(`/home/dd/engine/engine-private`))
431
- shellExec(`cd /home/dd/engine && underpost clone ${process.env.GITHUB_USERNAME}/engine-private`);
472
+ shellExec(`cd /home/dd/engine && underpost clone ${process.env.GITHUB_USERNAME}/engine-private`, {
473
+ silent: true,
474
+ });
432
475
  else
433
476
  shellExec(
434
477
  `cd /home/dd/engine/engine-private && underpost pull . ${process.env.GITHUB_USERNAME}/engine-private`,
478
+ {
479
+ silent: true,
480
+ },
435
481
  );
436
482
  },
437
483
  /**
@@ -497,7 +543,7 @@ class UnderpostRun {
497
543
  const baseClusterCommand = options.dev ? ' --dev' : '';
498
544
  const defaultPath = [
499
545
  'dd-default',
500
- 1,
546
+ options.replicas,
501
547
  ``,
502
548
  ``,
503
549
  options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname(),
@@ -512,8 +558,8 @@ class UnderpostRun {
512
558
  if (isDeployRunnerContext(path, options)) {
513
559
  const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
514
560
  if (!validVersion) throw new Error('Version mismatch');
515
- shellExec(`${baseCommand} run${baseClusterCommand} tz`);
516
- shellExec(`${baseCommand} run${baseClusterCommand} cron`);
561
+ if (options.timezone !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} tz`);
562
+ if (options.cronJobs !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} cron`);
517
563
  }
518
564
 
519
565
  const currentTraffic = isDeployRunnerContext(path, options)
@@ -524,7 +570,7 @@ class UnderpostRun {
524
570
 
525
571
  shellExec(
526
572
  `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
527
- replicas ? replicas : 1
573
+ replicas
528
574
  } --node ${node}${image ? ` --image ${image}` : ''}${versions ? ` --versions ${versions}` : ''}${options.namespace ? ` --namespace ${options.namespace}` : ''} dd ${env}`,
529
575
  );
530
576
 
@@ -534,8 +580,8 @@ class UnderpostRun {
534
580
  );
535
581
  if (!targetTraffic)
536
582
  targetTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
537
- await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic);
538
- UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
583
+ await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic, [], options.namespace, 'underpost');
584
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic, replicas, options.namespace);
539
585
  } else
540
586
  logger.info(
541
587
  'current traffic',
@@ -543,6 +589,61 @@ class UnderpostRun {
543
589
  );
544
590
  },
545
591
 
592
+ /**
593
+ * @method stop
594
+ * @description Stops a deployment by deleting the corresponding Kubernetes deployment and service resources.
595
+ * @param {string} path - The input value, identifier, or path for the operation (used to determine which traffic to stop).
596
+ * @param {Object} options - The default underpost runner options for customizing workflow
597
+ * @memberof UnderpostRun
598
+ */
599
+ stop: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
600
+ let currentTraffic = UnderpostDeploy.API.getCurrentTraffic(options.deployId, {
601
+ namespace: options.namespace,
602
+ hostTest: options.hosts,
603
+ });
604
+ const env = options.dev ? 'development' : 'production';
605
+
606
+ if (!path.match('current')) currentTraffic === 'blue' ? (currentTraffic = 'green') : (currentTraffic = 'blue');
607
+ const [_deployId] = path.split(',');
608
+ const deploymentId = `${_deployId ? _deployId : options.deployId}${options.instanceId ? `-${options.instanceId}` : ''}-${env}-${currentTraffic}`;
609
+
610
+ shellExec(`kubectl delete deployment ${deploymentId} -n ${options.namespace}`);
611
+ shellExec(`kubectl delete svc ${deploymentId}-service -n ${options.namespace}`);
612
+ },
613
+
614
+ /**
615
+ * @method ssh-deploy-stop
616
+ * @description Stops a remote deployment via SSH by executing the appropriate Underpost command on the remote server.
617
+ * @param {string} path - The input value, identifier, or path for the operation (used to determine which traffic to stop).
618
+ * @param {Object} options - The default underpost runner options for customizing workflow
619
+ * @memberof UnderpostRun
620
+ */
621
+ 'ssh-deploy-stop': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
622
+ const env = options.dev ? 'development' : 'production';
623
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
624
+ const baseClusterCommand = options.dev ? ' --dev' : '';
625
+ await UnderpostSSH.API.setDefautlSshCredentials(options);
626
+ shellExec(`#!/usr/bin/env bash
627
+ set -euo pipefail
628
+
629
+ REMOTE_USER=$(node bin config get --plain DEFAULT_SSH_USER)
630
+ REMOTE_HOST=$(node bin config get --plain DEFAULT_SSH_HOST)
631
+ REMOTE_PORT=$(node bin config get --plain DEFAULT_SSH_PORT)
632
+ SSH_KEY=$(node bin config get --plain DEFAULT_SSH_KEY_PATH)
633
+
634
+ chmod 600 "$SSH_KEY"
635
+
636
+ ssh -i "$SSH_KEY" -o BatchMode=yes "$REMOTE_USER@$REMOTE_HOST" -p $REMOTE_PORT sh <<EOF
637
+ cd /home/dd/engine
638
+ sudo -n -- /bin/bash -lc "${[
639
+ `${baseCommand} run${baseClusterCommand} stop${path ? ` ${path}` : ''}`,
640
+ ` --deploy-id ${options.deployId}${options.instanceId ? ` --instance-id ${options.instanceId}` : ''}`,
641
+ ` --namespace ${options.namespace}`,
642
+ ].join('')}"
643
+ EOF
644
+ `);
645
+ },
646
+
546
647
  /**
547
648
  * @method tz
548
649
  * @description Sets the system timezone using `timedatectl set-timezone` command.
@@ -551,13 +652,16 @@ class UnderpostRun {
551
652
  * @memberof UnderpostRun
552
653
  */
553
654
  tz: (path, options = UnderpostRun.DEFAULT_OPTION) => {
554
- const tz = path
555
- ? path
556
- : UnderpostRootEnv.API.get('TIME_ZONE', undefined, { disableLog: true })
557
- ? UnderpostRootEnv.API.get('TIME_ZONE')
558
- : process.env.TIME_ZONE
559
- ? process.env.TIME_ZONE
560
- : 'America/New_York';
655
+ const tz =
656
+ options.timezone && options.timezone !== 'none'
657
+ ? options.timezone
658
+ : path
659
+ ? path
660
+ : UnderpostRootEnv.API.get('TIME_ZONE', undefined, { disableLog: true })
661
+ ? UnderpostRootEnv.API.get('TIME_ZONE')
662
+ : process.env.TIME_ZONE
663
+ ? process.env.TIME_ZONE
664
+ : 'America/New_York';
561
665
  shellExec(`sudo timedatectl set-timezone ${tz}`);
562
666
  },
563
667
 
@@ -623,6 +727,7 @@ class UnderpostRun {
623
727
  const _deployId = `${deployId}-${_id}`;
624
728
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(_deployId, {
625
729
  hostTest: _host,
730
+ namespace: options.namespace,
626
731
  });
627
732
  const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
628
733
  let proxyYaml =
@@ -662,7 +767,8 @@ EOF
662
767
  const env = options.dev ? 'development' : 'production';
663
768
  const baseCommand = options.dev ? 'node bin' : 'underpost';
664
769
  const baseClusterCommand = options.dev ? ' --dev' : '';
665
- let [deployId, id, replicas = 1] = path.split(',');
770
+ let [deployId, id, replicas] = path.split(',');
771
+ if (!replicas) replicas = options.replicas;
666
772
  const confInstances = JSON.parse(
667
773
  fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'),
668
774
  );
@@ -697,11 +803,12 @@ EOF
697
803
 
698
804
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(_deployId, {
699
805
  hostTest: _host,
806
+ namespace: options.namespace,
700
807
  });
701
808
 
702
809
  const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
703
810
  const podId = `${_deployId}-${env}-${targetTraffic}`;
704
- const ignorePods = UnderpostDeploy.API.get(podId).map((p) => p.NAME);
811
+ const ignorePods = UnderpostDeploy.API.get(podId, 'pods', options.namespace).map((p) => p.NAME);
705
812
  UnderpostDeploy.API.configMap(env, options.namespace);
706
813
  shellExec(`kubectl delete service ${podId}-service --namespace ${options.namespace} --ignore-not-found`);
707
814
  shellExec(`kubectl delete deployment ${podId} --namespace ${options.namespace} --ignore-not-found`);
@@ -740,6 +847,8 @@ EOF
740
847
  env,
741
848
  targetTraffic,
742
849
  ignorePods,
850
+ options.namespace,
851
+ options.logType,
743
852
  );
744
853
 
745
854
  if (!ready) {
@@ -769,21 +878,6 @@ EOF
769
878
  'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
770
879
  console.table(await UnderpostDeploy.API.get(path, 'deployments', options.namespace));
771
880
  },
772
- /**
773
- * @method ls-images
774
- * @description Retrieves and logs a table of currently loaded Docker images in the 'kind-worker' node using `UnderpostDeploy.API.getCurrentLoadedImages`.
775
- * @param {string} path - The input value, identifier, or path for the operation.
776
- * @param {Object} options - The default underpost runner options for customizing workflow
777
- * @memberof UnderpostRun
778
- */
779
- 'ls-images': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
780
- console.table(
781
- UnderpostDeploy.API.getCurrentLoadedImages(
782
- options.nodeName ? options.nodeName : 'kind-worker',
783
- path === 'spec' ? true : false,
784
- ),
785
- );
786
- },
787
881
 
788
882
  /**
789
883
  * @method host-update
@@ -829,9 +923,7 @@ EOF
829
923
  }
830
924
 
831
925
  if (!currentImage)
832
- shellExec(
833
- `${baseCommand} dockerfile-pull-base-images${baseClusterCommand} ${options.dev ? '--kind-load' : '--kubeadm-load'}`,
834
- );
926
+ shellExec(`${baseCommand} image${baseClusterCommand} --pull-base ${options.dev ? '--kind' : '--kubeadm'}`);
835
927
  // shellExec(`kubectl delete pod ${podName} --ignore-not-found`);
836
928
 
837
929
  const payload = {
@@ -888,7 +980,7 @@ EOF
888
980
  switch (path) {
889
981
  case 'tf-vae-test':
890
982
  {
891
- const nameSpace = options.namespace || 'default';
983
+ const nameSpace = options.namespace;
892
984
  const podName = path;
893
985
  const basePath = '/home/dd';
894
986
  const scriptPath = '/site/en/tutorials/generative/cvae.py';
@@ -975,7 +1067,7 @@ EOF
975
1067
  const successInstance = await UnderpostTest.API.statusMonitor('adminer', 'Running', 'pods', 1000, 60 * 10);
976
1068
 
977
1069
  if (successInstance) {
978
- shellExec(`underpost deploy --expose adminer`);
1070
+ shellExec(`underpost deploy --expose adminer --namespace ${options.namespace}`);
979
1071
  }
980
1072
  },
981
1073
 
@@ -1033,12 +1125,12 @@ EOF
1033
1125
  for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
1034
1126
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
1035
1127
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1036
- UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas);
1128
+ UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas, options.namespace);
1037
1129
  }
1038
1130
  } else {
1039
1131
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(inputDeployId, { namespace: options.namespace });
1040
1132
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1041
- UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
1133
+ UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas, options.namespace);
1042
1134
  }
1043
1135
  },
1044
1136
  /**
@@ -1069,44 +1161,45 @@ EOF
1069
1161
  const env = options.dev ? 'development' : 'production';
1070
1162
  const baseCommand = options.dev ? 'node bin' : 'underpost';
1071
1163
  const baseClusterCommand = options.dev ? ' --dev' : '';
1164
+ const clusterType = options.k3s ? 'k3s' : 'kubeadm';
1072
1165
  shellCd(`/home/dd/engine`);
1073
1166
  shellExec(`${baseCommand} cluster${baseClusterCommand} --reset`);
1074
1167
  await timer(5000);
1075
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm`);
1168
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType}`);
1076
1169
  await timer(5000);
1077
1170
  let [runtimeImage, deployList] = path.split(',')
1078
1171
  ? path.split(',')
1079
- : ['lampp', fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').replaceAll(',', '+')];
1172
+ : ['express', fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').replaceAll(',', '+')];
1080
1173
  shellExec(
1081
- `${baseCommand} dockerfile-pull-base-images${baseClusterCommand}${
1082
- runtimeImage ? ` --path /home/dd/engine/src/runtime/${runtimeImage}` : ''
1083
- } --kubeadm-load`,
1174
+ `${baseCommand} image${baseClusterCommand}${
1175
+ runtimeImage ? ` --pull-base --path /home/dd/engine/src/runtime/${runtimeImage}` : ''
1176
+ } --${clusterType}`,
1084
1177
  );
1085
1178
  if (!deployList) {
1086
1179
  deployList = [];
1087
1180
  logger.warn('No deploy list provided');
1088
1181
  } else deployList = deployList.split('+');
1089
1182
  await timer(5000);
1090
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --mongodb`);
1183
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType} --pull-image --mongodb`);
1091
1184
  if (runtimeImage === 'lampp') {
1092
1185
  await timer(5000);
1093
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --mariadb`);
1186
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType} --pull-image --mariadb`);
1094
1187
  }
1095
1188
  await timer(5000);
1096
1189
  for (const deployId of deployList) {
1097
1190
  shellExec(`${baseCommand} db ${deployId} --import --git`);
1098
1191
  }
1099
1192
  await timer(5000);
1100
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --valkey`);
1193
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType} --pull-image --valkey`);
1101
1194
  await timer(5000);
1102
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --contour`);
1195
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType} --contour`);
1103
1196
  if (env === 'production') {
1104
1197
  await timer(5000);
1105
- shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --cert-manager`);
1198
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --${clusterType} --cert-manager`);
1106
1199
  }
1107
1200
  for (const deployId of deployList) {
1108
1201
  shellExec(
1109
- `${baseCommand} deploy ${deployId} ${env} --kubeadm${env === 'production' ? ' --cert' : ''}${
1202
+ `${baseCommand} deploy ${deployId} ${env} --${clusterType}${env === 'production' ? ' --cert' : ''}${
1110
1203
  env === 'development' ? ' --etc-hosts' : ''
1111
1204
  }`,
1112
1205
  );
@@ -1126,13 +1219,50 @@ EOF
1126
1219
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
1127
1220
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1128
1221
  const env = 'production';
1129
- const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
1222
+ const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`, 'pods', options.namespace).map(
1223
+ (p) => p.NAME,
1224
+ );
1130
1225
 
1131
1226
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic} -n ${options.namespace}`);
1132
1227
 
1133
- await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic, ignorePods);
1228
+ await UnderpostDeploy.API.monitorReadyRunner(
1229
+ deployId,
1230
+ env,
1231
+ targetTraffic,
1232
+ ignorePods,
1233
+ options.namespace,
1234
+ 'underpost',
1235
+ );
1134
1236
 
1135
- UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
1237
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic, options.replicas, options.namespace);
1238
+ },
1239
+
1240
+ /**
1241
+ * @method disk-clean
1242
+ * @description Executes the `disk-clean-sh` script to perform disk cleanup operations.
1243
+ * @param {string} path - The input value, identifier, or path for the operation.
1244
+ * @param {Object} options - The default underpost runner options for customizing workflow
1245
+ * @memberof UnderpostRun
1246
+ */
1247
+ 'disk-clean': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1248
+ const { underpostRoot } = options;
1249
+ shellExec(`chmod +x ${underpostRoot}/scripts/disk-clean.sh`);
1250
+ shellExec(`./scripts/disk-clean.sh --yes --aggressive`);
1251
+ },
1252
+
1253
+ /**
1254
+ * @method disk-usage
1255
+ * @description Displays disk usage statistics using the `du` command, sorted by size.
1256
+ * @param {string} path - The input value, identifier, or path for the operation.
1257
+ * @param {Object} options - The default underpost runner options for customizing workflow
1258
+ * @memberof UnderpostRun
1259
+ */
1260
+ 'disk-usage': async (path = '/', options = UnderpostRun.DEFAULT_OPTION) => {
1261
+ if (!path) path = '/';
1262
+ logger.info('Mount filesystem');
1263
+ shellExec(`df -h${path === '/' ? '' : ` ${path}`}`);
1264
+ logger.info('Files disks usage');
1265
+ shellExec(`du -xh ${path} --max-depth=1 | sort -h`);
1136
1266
  },
1137
1267
 
1138
1268
  /**
@@ -1174,7 +1304,7 @@ EOF
1174
1304
  envObj.DEV_PROXY_PORT_OFFSET = options.devProxyPortOffset;
1175
1305
  writeEnv(envPath, envObj);
1176
1306
  }
1177
- shellExec(`node bin run dev-cluster --expose`, { async: true });
1307
+ shellExec(`node bin run dev-cluster --expose --namespace ${options.namespace}`, { async: true });
1178
1308
  {
1179
1309
  const cmd = `npm run dev-api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort}${options.tls ? ' tls' : ''}`;
1180
1310
  options.terminal ? openTerminal(cmd) : shellExec(cmd, { async: true });
@@ -1205,6 +1335,7 @@ EOF
1205
1335
  const baseClusterCommand = options.dev ? ' --dev' : '';
1206
1336
  shellCd(`/home/dd/engine`);
1207
1337
  let [deployId, serviceId, host, _path, replicas, image, node] = path.split(',');
1338
+ if (!replicas) replicas = options.replicas;
1208
1339
  // const confClient = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.client.json`, 'utf8'));
1209
1340
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
1210
1341
  // const confSSR = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.ssr.json`, 'utf8'));
@@ -1269,7 +1400,7 @@ EOF
1269
1400
  if (!node) node = os.hostname();
1270
1401
  shellExec(
1271
1402
  `${baseCommand} deploy${options.dev ? '' : ' --kubeadm'}${options.devProxyPortOffset ? ' --disable-deployment-proxy' : ''} --build-manifest --sync --info-router --replicas ${
1272
- replicas ? replicas : 1
1403
+ replicas
1273
1404
  } --node ${node}${image ? ` --image ${image}` : ''}${versions ? ` --versions ${versions}` : ''} dd ${env}`,
1274
1405
  );
1275
1406
  shellExec(
@@ -1329,7 +1460,7 @@ EOF
1329
1460
  shellExec(`underpost run secret`);
1330
1461
  shellCd(`/home/dd/engine`);
1331
1462
  shellExec(`underpost cmt --empty . ci engine ' New engine release $(underpost --version)'`);
1332
- shellExec(`underpost push . ${process.env.GITHUB_USERNAME}/engine`);
1463
+ shellExec(`underpost push . ${process.env.GITHUB_USERNAME}/engine`, { silent: true });
1333
1464
  },
1334
1465
 
1335
1466
  /**
@@ -1427,7 +1558,7 @@ EOF
1427
1558
  const tty = options.tty ? 'true' : 'false';
1428
1559
  const stdin = options.stdin ? 'true' : 'false';
1429
1560
  const restartPolicy = options.restartPolicy || 'Never';
1430
- const kind = options.kind || 'Pod';
1561
+ const kindType = options.kindType || 'Pod';
1431
1562
  const imagePullPolicy = options.imagePullPolicy || 'IfNotPresent';
1432
1563
  const hostNetwork = options.hostNetwork ? options.hostNetwork : '';
1433
1564
  const apiVersion = options.apiVersion || 'v1';
@@ -1451,7 +1582,7 @@ EOF
1451
1582
 
1452
1583
  const cmd = `kubectl apply -f - <<EOF
1453
1584
  apiVersion: ${apiVersion}
1454
- kind: ${kind}
1585
+ kind: ${kindType}
1455
1586
  metadata:
1456
1587
  name: ${podName}
1457
1588
  namespace: ${namespace}
@@ -1523,6 +1654,8 @@ EOF`;
1523
1654
  if (options.args) options.args = options.args.split(',');
1524
1655
  if (!options.underpostRoot) options.underpostRoot = underpostRoot;
1525
1656
  if (!options.namespace) options.namespace = 'default';
1657
+ if (options.replicas === '' || options.replicas === null || options.replicas === undefined)
1658
+ options.replicas = 1;
1526
1659
  options.npmRoot = npmRoot;
1527
1660
  logger.info('callback', { path, options });
1528
1661
  if (!(runner in UnderpostRun.RUNNERS)) throw new Error(`Runner not found: ${runner}`);