underpost 2.8.82 → 2.8.85

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 (115) hide show
  1. package/.env.development +1 -0
  2. package/.env.production +1 -0
  3. package/.env.test +1 -0
  4. package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +5 -5
  5. package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +5 -5
  6. package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
  7. package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +1 -1
  8. package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
  9. package/.vscode/extensions.json +1 -1
  10. package/.vscode/settings.json +0 -44
  11. package/README.md +62 -2
  12. package/bin/build.js +15 -5
  13. package/bin/deploy.js +42 -92
  14. package/bin/file.js +33 -9
  15. package/bin/vs.js +12 -4
  16. package/cli.md +90 -42
  17. package/conf.js +1 -1
  18. package/docker-compose.yml +1 -1
  19. package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
  20. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +65 -0
  21. package/manifests/maas/device-scan.sh +3 -3
  22. package/manifests/maas/gpu-diag.sh +19 -0
  23. package/manifests/maas/maas-setup.sh +10 -10
  24. package/manifests/maas/snap-clean.sh +26 -0
  25. package/package.json +4 -6
  26. package/src/api/user/user.router.js +24 -1
  27. package/src/api/user/user.service.js +1 -4
  28. package/src/cli/baremetal.js +105 -73
  29. package/src/cli/cloud-init.js +21 -12
  30. package/src/cli/cluster.js +227 -133
  31. package/src/cli/deploy.js +34 -0
  32. package/src/cli/index.js +28 -1
  33. package/src/cli/monitor.js +8 -12
  34. package/src/cli/repository.js +7 -4
  35. package/src/cli/run.js +367 -0
  36. package/src/cli/ssh.js +32 -0
  37. package/src/cli/test.js +1 -1
  38. package/src/client/Default.index.js +7 -3
  39. package/src/client/components/core/Account.js +1 -1
  40. package/src/client/components/core/Chat.js +1 -1
  41. package/src/client/components/core/CommonJs.js +24 -22
  42. package/src/client/components/core/Content.js +1 -5
  43. package/src/client/components/core/Css.js +258 -18
  44. package/src/client/components/core/CssCore.js +8 -8
  45. package/src/client/components/core/Docs.js +14 -61
  46. package/src/client/components/core/DropDown.js +137 -82
  47. package/src/client/components/core/EventsUI.js +92 -5
  48. package/src/client/components/core/LoadingAnimation.js +8 -15
  49. package/src/client/components/core/Modal.js +597 -136
  50. package/src/client/components/core/NotificationManager.js +2 -2
  51. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  52. package/src/client/components/core/Panel.js +158 -34
  53. package/src/client/components/core/PanelForm.js +12 -3
  54. package/src/client/components/core/Recover.js +1 -1
  55. package/src/client/components/core/Router.js +77 -17
  56. package/src/client/components/core/SocketIo.js +3 -3
  57. package/src/client/components/core/Translate.js +6 -2
  58. package/src/client/components/core/VanillaJs.js +0 -3
  59. package/src/client/components/core/Worker.js +3 -1
  60. package/src/client/components/default/CssDefault.js +17 -3
  61. package/src/client/components/default/MenuDefault.js +264 -45
  62. package/src/client/components/default/RoutesDefault.js +6 -12
  63. package/src/client/public/default/android-chrome-144x144.png +0 -0
  64. package/src/client/public/default/android-chrome-192x192.png +0 -0
  65. package/src/client/public/default/android-chrome-256x256.png +0 -0
  66. package/src/client/public/default/android-chrome-36x36.png +0 -0
  67. package/src/client/public/default/android-chrome-48x48.png +0 -0
  68. package/src/client/public/default/android-chrome-72x72.png +0 -0
  69. package/src/client/public/default/android-chrome-96x96.png +0 -0
  70. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  71. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  72. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  73. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  74. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  75. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  76. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  77. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  78. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  79. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  80. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  81. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  82. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  83. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  84. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  85. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  86. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  87. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  88. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  89. package/src/client/public/default/apple-touch-icon.png +0 -0
  90. package/src/client/public/default/assets/background/dark.jpg +0 -0
  91. package/src/client/public/default/assets/background/dark.svg +557 -0
  92. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  93. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  94. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  95. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  96. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  97. package/src/client/public/default/favicon-16x16.png +0 -0
  98. package/src/client/public/default/favicon-32x32.png +0 -0
  99. package/src/client/public/default/favicon.ico +0 -0
  100. package/src/client/public/default/mstile-144x144.png +0 -0
  101. package/src/client/public/default/mstile-150x150.png +0 -0
  102. package/src/client/public/default/mstile-310x150.png +0 -0
  103. package/src/client/public/default/mstile-310x310.png +0 -0
  104. package/src/client/public/default/mstile-70x70.png +0 -0
  105. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  106. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  107. package/src/index.js +34 -17
  108. package/src/monitor.js +24 -0
  109. package/src/runtime/lampp/Dockerfile +30 -39
  110. package/src/runtime/lampp/Lampp.js +11 -2
  111. package/src/server/client-build-docs.js +205 -0
  112. package/src/server/client-build.js +16 -166
  113. package/src/server/conf.js +18 -8
  114. package/src/server/process.js +16 -19
  115. package/src/server/valkey.js +102 -41
package/src/cli/index.js CHANGED
@@ -4,9 +4,9 @@ import Underpost from '../index.js';
4
4
  import { getNpmRootPath, getUnderpostRootPath, loadConf } from '../server/conf.js';
5
5
  import fs from 'fs-extra';
6
6
  import { commitData } from '../client/components/core/CommonJs.js';
7
- import { shellExec } from '../server/process.js';
8
7
  import UnderpostLxd from './lxd.js';
9
8
  import UnderpostBaremetal from './baremetal.js';
9
+ import UnderpostRun from './run.js';
10
10
 
11
11
  // Load environment variables from .env file
12
12
  const underpostRootPath = getUnderpostRootPath();
@@ -112,6 +112,7 @@ program
112
112
  .option('--mariadb', 'Initializes the cluster with a MariaDB statefulset.')
113
113
  .option('--mysql', 'Initializes the cluster with a MySQL statefulset.')
114
114
  .option('--mongodb', 'Initializes the cluster with a MongoDB statefulset.')
115
+ .option('--mongo-db-host <host>', 'Set custom mongo db host')
115
116
  .option('--postgresql', 'Initializes the cluster with a PostgreSQL statefulset.')
116
117
  .option('--mongodb4', 'Initializes the cluster with a MongoDB 4.4 service.')
117
118
  .option('--valkey', 'Initializes the cluster with a Valkey service.')
@@ -128,6 +129,7 @@ program
128
129
  .option('--info-capacity-pod', 'Displays the current machine capacity information per pod.')
129
130
  .option('--pull-image', 'Sets an optional associated image to pull during initialization.')
130
131
  .option('--init-host', 'Installs necessary Kubernetes node CLI tools (e.g., kind, kubeadm, docker, podman, helm).')
132
+ .option('--uninstall-host', 'Uninstalls all host components installed by init-host.')
131
133
  .option('--config', 'Sets the base Kubernetes node configuration.')
132
134
  .option('--worker', 'Sets the context for a worker node.')
133
135
  .option('--chown', 'Sets the appropriate ownership for Kubernetes kubeconfig files.')
@@ -314,6 +316,31 @@ program
314
316
  .description('Manages health server monitoring for specified deployments.')
315
317
  .action(Underpost.monitor.callback);
316
318
 
319
+ // 'ssh' command: SSH management
320
+ program
321
+ .command('ssh')
322
+ .option('--generate', 'Generates new ssh credential and stores it in current private keys file storage.')
323
+ .description('Import and start ssh server and client based on current default deployment ID.')
324
+ .action(Underpost.ssh.callback);
325
+
326
+ // 'run' command: Run a script
327
+ program
328
+ .command('run')
329
+ .argument('<runner-id>', `The runner ID to run. Options: ${Object.keys(UnderpostRun.RUNNERS).join(', ')}.`)
330
+ .argument('[path]', 'The absolute or relative directory path where the script is located.')
331
+ .option('--command <command-array>', 'Array of commands to run.')
332
+ .option('--args <args-array>', 'Array of arguments to pass to the command.')
333
+ .option('--dev', 'Sets the development context environment for the script.')
334
+ .option('--pod-name <pod-name>', 'Optional: Specifies the pod name for test execution.')
335
+ .option('--volume-host-path <volume-host-path>', 'Optional: Specifies the volume host path for test execution.')
336
+ .option('--volume-mount-path <volume-mount-path>', 'Optional: Specifies the volume mount path for test execution.')
337
+ .option('--volume-type <volume-type>', 'Optional: Specifies the volume type for test execution.')
338
+ .option('--image-name <image-name>', 'Optional: Specifies the image name for test execution.')
339
+ .option('--container-name <container-name>', 'Optional: Specifies the container name for test execution.')
340
+ .option('--namespace <namespace>', 'Optional: Specifies the namespace for test execution.')
341
+ .description('Runs a script from the specified path.')
342
+ .action(UnderpostRun.API.callback);
343
+
317
344
  // 'lxd' command: LXD management
318
345
  program
319
346
  .command('lxd')
@@ -173,19 +173,15 @@ class UnderpostMonitor {
173
173
  monitorTrafficName = undefined;
174
174
  monitorPodName = undefined;
175
175
  }
176
- const cmd = `underpost config get container-status`;
177
176
  const checkDeploymentReadyStatus = () => {
178
- const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
179
- if (pods && pods[0]) {
180
- const { NAME } = pods[0];
181
- if (
182
- shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
183
- `${deployId}-${env}-running-deployment`,
184
- )
185
- ) {
186
- monitorPodName = NAME;
187
- monitorTrafficName = `${traffic}`;
188
- }
177
+ const { ready, notReadyPods, readyPods } = UnderpostDeploy.API.checkDeploymentReadyStatus(
178
+ deployId,
179
+ env,
180
+ traffic,
181
+ );
182
+ if (ready) {
183
+ monitorPodName = readyPods[0].NAME;
184
+ monitorTrafficName = `${traffic}`;
189
185
  }
190
186
  };
191
187
  if (!monitorPodName) {
@@ -60,7 +60,7 @@ class UnderpostRepository {
60
60
  },
61
61
 
62
62
  push(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template', options = { f: false, g8: false }) {
63
- const gExtension = options.g8 === true ? '.g8' : '.git';
63
+ const gExtension = options.g8 === true || options.G8 === true ? '.g8' : '.git';
64
64
  shellExec(
65
65
  `cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
66
66
  options?.f === true ? ' --force' : ''
@@ -71,9 +71,12 @@ class UnderpostRepository {
71
71
  );
72
72
  logger.info(
73
73
  'commit url',
74
- `http://github.com/${gitUri}/commit/${shellExec(`cd ${repoPath} && git rev-parse --verify HEAD`, {
75
- stdout: true,
76
- }).trim()}`,
74
+ `http://github.com/${gitUri}${gExtension === '.g8' ? '.g8' : ''}/commit/${shellExec(
75
+ `cd ${repoPath} && git rev-parse --verify HEAD`,
76
+ {
77
+ stdout: true,
78
+ },
79
+ ).trim()}`,
77
80
  );
78
81
  },
79
82
 
package/src/cli/run.js ADDED
@@ -0,0 +1,367 @@
1
+ import { daemonProcess, getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
2
+ import read from 'read';
3
+ import { getNpmRootPath } from '../server/conf.js';
4
+ import { loggerFactory } from '../server/logger.js';
5
+ import UnderpostTest from './test.js';
6
+ import fs from 'fs-extra';
7
+ import { range, setPad, timer } from '../client/components/core/CommonJs.js';
8
+ import UnderpostDeploy from './deploy.js';
9
+ import UnderpostRootEnv from './env.js';
10
+
11
+ const logger = loggerFactory(import.meta);
12
+
13
+ class UnderpostRun {
14
+ static DEFAULT_OPTION = {
15
+ dev: false,
16
+ podName: '',
17
+ volumeHostPath: '',
18
+ volumeMountPath: '',
19
+ imageName: '',
20
+ containerName: '',
21
+ namespace: '',
22
+ };
23
+ static RUNNERS = {
24
+ 'spark-template': (path, options = UnderpostRun.DEFAULT_OPTION) => {
25
+ const dir = '/home/dd/spark-template';
26
+ shellExec(`sudo rm -rf ${dir}`);
27
+ shellCd('/home/dd');
28
+
29
+ // pbcopy(`cd /home/dd && sbt new underpostnet/spark-template.g8`);
30
+ // await read({ prompt: 'Command copy to clipboard, press enter to continue.\n' });
31
+ shellExec(`cd /home/dd && sbt new underpostnet/spark-template.g8 '--name=spark-template'`);
32
+
33
+ shellCd(dir);
34
+
35
+ shellExec(`git init && git add . && git commit -m "Base implementation"`);
36
+ shellExec(`chmod +x ./replace_params.sh`);
37
+ shellExec(`chmod +x ./build.sh`);
38
+
39
+ shellExec(`./replace_params.sh`);
40
+ shellExec(`./build.sh`);
41
+
42
+ shellCd('/home/dd/engine');
43
+ },
44
+ rmi: (path, options = UnderpostRun.DEFAULT_OPTION) => {
45
+ shellExec(`podman rmi $(podman images -qa) --force`);
46
+ },
47
+ kill: (path, options = UnderpostRun.DEFAULT_OPTION) => {
48
+ shellExec(`sudo kill -9 $(lsof -t -i:${path})`);
49
+ },
50
+ secret: (path, options = UnderpostRun.DEFAULT_OPTION) => {
51
+ shellExec(
52
+ `underpost secret underpost --create-from-file ${
53
+ path ? path : `/home/dd/engine/engine-private/conf/dd-cron/.env.production`
54
+ }`,
55
+ );
56
+ },
57
+ 'gpu-env': (path, options = UnderpostRun.DEFAULT_OPTION) => {
58
+ shellExec(
59
+ `node bin cluster --dev --reset && node bin cluster --dev --dedicated-gpu --kubeadm && kubectl get pods --all-namespaces -o wide -w`,
60
+ );
61
+ },
62
+ 'tf-gpu-test': (path, options = UnderpostRun.DEFAULT_OPTION) => {
63
+ const { underpostRoot } = options;
64
+ shellExec(`kubectl delete configmap tf-gpu-test-script`);
65
+ shellExec(`kubectl delete pod tf-gpu-test-pod`);
66
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/deployment/tensorflow/tf-gpu-test.yaml`);
67
+ },
68
+ 'dev-cluster': (path, options = UnderpostRun.DEFAULT_OPTION) => {
69
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
70
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
71
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
72
+ shellExec(
73
+ `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${'127.0.0.1'} --pull-image`,
74
+ );
75
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
76
+ shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
77
+ shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
78
+ },
79
+ 'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
80
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
81
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
82
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
83
+ },
84
+ 'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
85
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
86
+ shellExec(`${baseCommand} run ide /home/dd/engine`);
87
+ shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
88
+ },
89
+ 'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
90
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
91
+ shellCd('/home/dd/engine');
92
+ shellExec(`git reset`);
93
+ shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
94
+ shellExec(`${baseCommand} push . underpostnet/engine`);
95
+ },
96
+ 'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
97
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
98
+ shellCd('/home/dd/engine');
99
+ shellExec(`git reset`);
100
+ shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
101
+ shellExec(`${baseCommand} push . underpostnet/engine`);
102
+ },
103
+ ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
104
+ const { underpostRoot } = options;
105
+ shellExec(`node ${underpostRoot}/bin/vs ${path}`);
106
+ },
107
+ monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
108
+ const pid = getTerminalPid();
109
+ logger.info('monitor pid', pid);
110
+ const checkPath = '/await';
111
+ const _monitor = async () => {
112
+ const result = UnderpostDeploy.API.existsContainerFile({ podName: path, path: checkPath });
113
+ logger.info('monitor', result);
114
+ if (result === true) {
115
+ switch (path) {
116
+ case 'tf-vae-test':
117
+ {
118
+ const nameSpace = 'default';
119
+ const podName = path;
120
+ const basePath = '/home/dd';
121
+ const scriptPath = '/site/en/tutorials/generative/cvae.py';
122
+ // shellExec(
123
+ // `sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${scriptPath} ${basePath}/lab/src/${scriptPath
124
+ // .split('/')
125
+ // .pop()}`,
126
+ // );
127
+ // const file = fs.readFileSync(`${basePath}/lab/src/${scriptPath.split('/').pop()}`, 'utf8');
128
+ // fs.writeFileSync(
129
+ // `${basePath}/lab/src/${scriptPath.split('/').pop()}`,
130
+ // file.replace(
131
+ // `import time`,
132
+ // `import time
133
+ // print('=== SCRIPT UPDATE TEST ===')`,
134
+ // ),
135
+ // 'utf8',
136
+ // );
137
+ shellExec(
138
+ `sudo kubectl cp ${basePath}/lab/src/${scriptPath
139
+ .split('/')
140
+ .pop()} ${nameSpace}/${podName}:${basePath}/docs${scriptPath}`,
141
+ );
142
+ // shellExec(`sudo kubectl exec -i ${podName} -- sh -c "ipython ${basePath}/docs${scriptPath}"`);
143
+ shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf ${checkPath}"`);
144
+
145
+ {
146
+ const checkPath = `/latent_space_plot.png`;
147
+ const outsPaths = [];
148
+ logger.info('monitor', checkPath);
149
+ while (!UnderpostDeploy.API.existsContainerFile({ podName, path: `/home/dd/docs${checkPath}` }))
150
+ await timer(1000);
151
+
152
+ {
153
+ const toPath = `${basePath}/lab${checkPath}`;
154
+ outsPaths.push(toPath);
155
+ shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${checkPath} ${toPath}`);
156
+ }
157
+
158
+ for (let i of range(1, 10)) {
159
+ i = `/image_at_epoch_${setPad(i, '0', 4)}.png`;
160
+ const toPath = `${basePath}/lab/${i}`;
161
+ outsPaths.push(toPath);
162
+ shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${i} ${toPath}`);
163
+ }
164
+ openTerminal(`firefox ${outsPaths.join(' ')}`, { single: true });
165
+ }
166
+ shellExec(`sudo kill -9 ${pid}`);
167
+ }
168
+ break;
169
+
170
+ default:
171
+ break;
172
+ }
173
+ return;
174
+ }
175
+ await timer(1000);
176
+ _monitor();
177
+ };
178
+ _monitor();
179
+ },
180
+ 'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
181
+ const { underpostRoot } = options;
182
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
183
+ },
184
+ cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
185
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
186
+ const env = 'production';
187
+ shellCd(`/home/dd/engine`);
188
+ shellExec(`underpost cluster --reset`);
189
+ await timer(5000);
190
+ shellExec(`underpost cluster --kubeadm`);
191
+ await timer(5000);
192
+ shellExec(`underpost dockerfile-pull-base-images --path /home/dd/engine/src/runtime/lampp --kubeadm-load`);
193
+ await timer(5000);
194
+ shellExec(`underpost cluster --kubeadm --pull-image --mongodb`);
195
+ await timer(5000);
196
+ shellExec(`underpost cluster --kubeadm --pull-image --mariadb`);
197
+ await timer(5000);
198
+ for (const deployId of deployList) {
199
+ shellExec(`underpost db ${deployId} --import --git`);
200
+ }
201
+ await timer(5000);
202
+ shellExec(`underpost cluster --kubeadm --pull-image --valkey`);
203
+ await timer(5000);
204
+ shellExec(`underpost cluster --kubeadm --contour`);
205
+ await timer(5000);
206
+ shellExec(`underpost cluster --kubeadm --cert-manager`);
207
+ for (const deployId of deployList) {
208
+ shellExec(`underpost deploy ${deployId} ${env} --kubeadm --cert`);
209
+ }
210
+ },
211
+ deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
212
+ const deployId = path;
213
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
214
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
215
+ const env = 'production';
216
+ const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
217
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
218
+
219
+ let secondsElapsed = 0;
220
+ logger.info('Deployment init', { deployId, env, targetTraffic });
221
+
222
+ while (!UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready) {
223
+ await timer(1000);
224
+ secondsElapsed++;
225
+ logger.info(`Deployment in progress, seconds elapsed: ${secondsElapsed}`);
226
+ }
227
+
228
+ logger.info(`Deployment ready, seconds elapsed: ${secondsElapsed}`);
229
+
230
+ UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
231
+
232
+ shellExec(
233
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${
234
+ options.replicas ? options.replicas : 1
235
+ } ${deployId} ${env}`,
236
+ );
237
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
238
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
239
+ },
240
+ 'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
241
+ const { underpostRoot } = options;
242
+ const podName = 'tf-vae-test';
243
+ await UnderpostRun.RUNNERS['deploy-job']('', {
244
+ podName,
245
+ // volumeMountPath: '/custom_images',
246
+ // volumeHostPath: '/home/dd/engine/src/client/public/cyberia/assets/skin',
247
+ on: {
248
+ init: async () => {
249
+ openTerminal(`node bin run --dev monitor ${podName}`);
250
+ },
251
+ },
252
+ args: [
253
+ `pip install --upgrade \
254
+ nbconvert \
255
+ tensorflow-probability==0.23.0 \
256
+ imageio \
257
+ git+https://github.com/tensorflow/docs \
258
+ matplotlib \
259
+ "numpy<1.25,>=1.21"`,
260
+ 'mkdir -p /home/dd',
261
+ 'cd /home/dd',
262
+ 'git clone https://github.com/tensorflow/docs.git',
263
+ 'cd docs',
264
+ 'jupyter nbconvert --to python site/en/tutorials/generative/cvae.ipynb',
265
+ `echo '' > /await`,
266
+ `echo '=== WAITING SCRIPT LAUNCH ==='`,
267
+ `while [ -f /await ]; do sleep 1; done`,
268
+ `echo '=== FINISHED ==='`,
269
+ daemonProcess(`ipython site/en/tutorials/generative/cvae.py`),
270
+ ],
271
+ });
272
+ },
273
+ 'deploy-job': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
274
+ const podName = options.podName || 'deploy-job';
275
+ const volumeName = `${podName}-volume`;
276
+ const args = (options.args ? options.args : path ? [`python ${path}`] : []).filter((c) => c.trim());
277
+ const imageName = options.imageName || 'nvcr.io/nvidia/tensorflow:24.04-tf2-py3';
278
+ const containerName = options.containerName || `${podName}-container`;
279
+ const gpuEnable = imageName.match('nvidia');
280
+ const runtimeClassName = gpuEnable ? 'nvidia' : '';
281
+ const namespace = options.namespace || 'default';
282
+ const volumeMountPath = options.volumeMountPath || path;
283
+ const volumeHostPath = options.volumeHostPath || path;
284
+ const enableVolumeMount = volumeHostPath && volumeMountPath;
285
+
286
+ if (options.volumeType === 'dev') options.volumeType = 'FileOrCreate';
287
+ const volumeType =
288
+ options.volumeType || (enableVolumeMount && fs.statSync(volumeHostPath).isDirectory()) ? 'Directory' : 'File';
289
+
290
+ const envs = UnderpostRootEnv.API.list();
291
+
292
+ const cmd = `kubectl apply -f - <<EOF
293
+ apiVersion: v1
294
+ kind: Pod
295
+ metadata:
296
+ name: ${podName}
297
+ namespace: ${namespace}
298
+ spec:
299
+ restartPolicy: Never
300
+ ${runtimeClassName ? ` runtimeClassName: ${runtimeClassName}` : ''}
301
+ containers:
302
+ - name: ${containerName}
303
+ image: ${imageName}
304
+ imagePullPolicy: IfNotPresent
305
+ tty: true
306
+ stdin: true
307
+ command: ${JSON.stringify(options.command ? options.command : ['/bin/bash', '-c'])}
308
+ ${
309
+ args.length > 0
310
+ ? ` args:
311
+ - |
312
+ ${args.map((arg) => ` ${arg}`).join('\n')}`
313
+ : ''
314
+ }
315
+ ${`${
316
+ gpuEnable
317
+ ? ` resources:
318
+ limits:
319
+ nvidia.com/gpu: '1'
320
+ `
321
+ : ''
322
+ } env:
323
+ ${Object.keys(envs)
324
+ .map((key) => ({ key, value: typeof envs[key] === 'number' ? envs[key] : `"${envs[key]}"` }))
325
+ .concat(gpuEnable ? [{ key: 'NVIDIA_VISIBLE_DEVICES', value: 'all' }] : [])
326
+ .map((env) => ` - name: ${env.key}\n value: ${env.value}`)
327
+ .join('\n')}`}
328
+ ${
329
+ enableVolumeMount
330
+ ? `
331
+ volumeMounts:
332
+ - name: ${volumeName}
333
+ mountPath: ${volumeMountPath}
334
+ volumes:
335
+ - name: ${volumeName}
336
+ hostPath:
337
+ path: ${volumeHostPath}
338
+ type: ${volumeType}`
339
+ : ''
340
+ }
341
+ EOF`;
342
+ shellExec(`kubectl delete pod ${podName}`);
343
+ console.log(cmd);
344
+ shellExec(cmd, { disableLog: true });
345
+ const successInstance = await UnderpostTest.API.statusMonitor(podName);
346
+ if (successInstance) {
347
+ options.on?.init ? await options.on.init() : null;
348
+ shellExec(`kubectl logs -f ${podName}`);
349
+ }
350
+ },
351
+ };
352
+ static API = {
353
+ async callback(runner, path, options = UnderpostRun.DEFAULT_OPTION) {
354
+ const npmRoot = getNpmRootPath();
355
+ const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
356
+ if (options.command) options.command = options.command.split(',');
357
+ if (options.args) options.args = options.args.split(',');
358
+ options.underpostRoot = underpostRoot;
359
+ options.npmRoot = npmRoot;
360
+ logger.info('callback', { path, options });
361
+ const result = await UnderpostRun.RUNNERS[runner](path, options);
362
+ return result;
363
+ },
364
+ };
365
+ }
366
+
367
+ export default UnderpostRun;
package/src/cli/ssh.js ADDED
@@ -0,0 +1,32 @@
1
+ import { getNpmRootPath } from '../server/conf.js';
2
+ import { shellExec } from '../server/process.js';
3
+
4
+ class UnderpostSSH {
5
+ static API = {
6
+ /**
7
+ * @method callback
8
+ * @param {object} options
9
+ * @param {boolean} options.generate - Generates new ssh credential and stores it in current private keys file storage.
10
+ * @description Import and start ssh server and client based on current default deployment ID.
11
+ */
12
+ callback: async (
13
+ options = {
14
+ generate: false,
15
+ },
16
+ ) => {
17
+ // only import + start
18
+ // node bin/deploy ssh root@<host> <password> import
19
+
20
+ // generate + import + start
21
+ // node bin/deploy ssh root@<host> <password>
22
+
23
+ shellExec(
24
+ `node bin/deploy ssh root@${process.env.DEFAULT_DEPLOY_HOST} ${process.env.DEFAULT_DEPLOY_PASSWORD ?? `''`}${
25
+ options.generate === true ? '' : ' import'
26
+ }`,
27
+ );
28
+ },
29
+ };
30
+ }
31
+
32
+ export default UnderpostSSH;
package/src/cli/test.js CHANGED
@@ -94,7 +94,7 @@ class UnderpostTest {
94
94
  const _monitor = async () => {
95
95
  await timer(deltaMs);
96
96
  const pods = UnderpostDeploy.API.get(podName, kindType);
97
- const result = pods.find((p) => p.STATUS === status);
97
+ let result = pods.find((p) => p.STATUS === status || (status === 'Running' && p.STATUS === 'Completed'));
98
98
  logger.info(
99
99
  `Testing pod ${podName}... ${result ? 1 : 0}/1 - elapsed time ${deltaMs * (index + 1)}s - attempt ${
100
100
  index + 1
@@ -16,21 +16,25 @@ import { SocketIo } from './components/core/SocketIo.js';
16
16
  import { SocketIoDefault } from './components/default/SocketIoDefault.js';
17
17
  import { ElementsDefault } from './components/default/ElementsDefault.js';
18
18
  import { Scroll } from './components/core/Scroll.js';
19
+ import { CssDefaultDark, CssDefaultLight } from './components/default/CssDefault.js';
19
20
 
20
21
  const htmlMainBody = async () => {
21
- return html`<span style="color: black; padding: 5px">Hello World!!</span>`;
22
+ return html`<span>Hello World!!</span>`;
22
23
  };
23
24
 
24
25
  window.onload = () =>
25
26
  Worker.instance({
26
27
  router: RouterDefault,
27
28
  render: async () => {
28
- await Css.loadThemes();
29
+ await Css.loadThemes([CssDefaultLight, CssDefaultDark]);
29
30
  await TranslateCore.Init();
30
31
  await TranslateDefault.Init();
31
32
  await Responsive.Init();
32
33
  await MenuDefault.Render({ htmlMainBody });
33
- await SocketIo.Init({ channels: ElementsDefault.Data });
34
+ await SocketIo.Init({
35
+ channels: ElementsDefault.Data,
36
+ path: `/`,
37
+ });
34
38
  await SocketIoDefault.Init();
35
39
  await LogInDefault();
36
40
  await LogOutDefault();
@@ -44,7 +44,7 @@ const Account = {
44
44
  { model: 'email', id: `account-email`, rules: [{ type: 'isEmpty' }, { type: 'isEmail' }] },
45
45
  {
46
46
  model: 'password',
47
- defaultValue: '*******',
47
+ defaultValue: '#Changethis123',
48
48
  id: `account-password`,
49
49
  rules: [{ type: 'isStrongPassword' }],
50
50
  },
@@ -53,7 +53,7 @@ const Chat = {
53
53
  html`
54
54
  <div class="in">
55
55
  <span class="chat-message-header">${getIsoDate(new Date())} | ${id}:</span><br />
56
- ${message}
56
+ <span class="chat-message-body"> ${message}</span>
57
57
  </div>
58
58
  `,
59
59
  );
@@ -822,70 +822,72 @@ const generateRandomPasswordSelection = (length) => {
822
822
 
823
823
  const commitData = {
824
824
  feat: {
825
- description: 'A new feature',
825
+ description: 'New feature or enhancement (frontend, backend, API, or UX)',
826
826
  title: 'Features',
827
827
  emoji: '✨',
828
828
  },
829
829
  fix: {
830
- description: 'A bug fix',
830
+ description: 'Fix a bug',
831
831
  title: 'Bug Fixes',
832
832
  emoji: '🐛',
833
833
  },
834
834
  docs: {
835
- description: 'Documentation only changes',
835
+ description: 'Documentation changes',
836
836
  title: 'Documentation',
837
837
  emoji: '📚',
838
838
  },
839
839
  style: {
840
- description:
841
- 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
840
+ description: 'Formatting, whitespace, semicolons, code style',
842
841
  title: 'Styles',
843
842
  emoji: '💎',
844
843
  },
845
844
  refactor: {
846
- description: 'A code change that neither fixes a bug nor adds a feature',
845
+ description: 'Code refactor.',
847
846
  title: 'Code Refactoring',
848
847
  emoji: '📦',
849
848
  },
850
849
  perf: {
851
- description: 'A code change that improves performance',
850
+ description: 'Performance improvements across the stack.',
852
851
  title: 'Performance Improvements',
853
852
  emoji: '⚡️',
854
853
  },
854
+ ci: {
855
+ description: 'CI pipeline changes (GitHub Actions, runners, caching)',
856
+ title: 'Continuous Integration',
857
+ emoji: '⚙️',
858
+ },
855
859
  cd: {
856
- description:
857
- 'Changes to our Continuous Delivery configuration files and scripts (example scopes: Jenkins, Spinnaker, ArgoCD)',
860
+ description: 'CD / deployment changes (Remote ssh deployment scripts)',
858
861
  title: 'Continuous Delivery',
859
862
  emoji: '🚀',
860
863
  },
861
- test: {
862
- description: 'Adding missing tests or correcting existing tests',
863
- title: 'Tests',
864
- emoji: '🚨',
864
+ infra: {
865
+ description: 'Infrastructure changes (MAAS, LXD, cloud infra, networking, provisioning).',
866
+ title: 'Infrastructure',
867
+ emoji: '🏗️',
865
868
  },
866
869
  build: {
867
- description: 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
870
+ description: 'Build system or dependency changes (tooling, bundler, build scripts).',
868
871
  title: 'Builds',
869
872
  emoji: '🛠',
870
873
  },
871
- ci: {
872
- description:
873
- 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
874
- title: 'Continuous Integrations',
875
- emoji: '⚙️',
874
+ test: {
875
+ description: 'Unit or integration tests added/updated, test helpers, flake fixes.',
876
+ title: 'Tests',
877
+ emoji: '🚨',
876
878
  },
877
879
  chore: {
878
- description: "Other changes that don't modify src or test files",
880
+ description: "Other changes that don't modify src or tests (automation, housekeeping).",
879
881
  title: 'Chores',
880
882
  emoji: '♻️',
881
883
  },
882
884
  revert: {
883
- description: 'Reverts a previous commit',
885
+ description: 'Revert a previous commit or change.',
884
886
  title: 'Reverts',
885
887
  emoji: '🗑',
886
888
  },
887
889
  backup: {
888
- description: 'Changes related to backups, including creation, restoration, and maintenance.',
890
+ description: 'Backups, snapshotting, restore scripts, or backup docs.',
889
891
  title: 'Backups',
890
892
  emoji: '💾',
891
893
  },