underpost 2.8.885 → 2.81.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 (72) hide show
  1. package/.env.production +3 -0
  2. package/.github/workflows/ghpkg.ci.yml +1 -1
  3. package/.github/workflows/npmpkg.ci.yml +1 -1
  4. package/.github/workflows/publish.ci.yml +5 -5
  5. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  6. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  7. package/.vscode/zed.keymap.json +17 -0
  8. package/.vscode/zed.settings.json +20 -0
  9. package/CHANGELOG.md +145 -1
  10. package/Dockerfile +20 -3
  11. package/README.md +6 -6
  12. package/bin/build.js +18 -9
  13. package/bin/deploy.js +130 -195
  14. package/bin/zed.js +20 -0
  15. package/cli.md +13 -7
  16. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  17. package/manifests/deployment/dd-test-development/deployment.yaml +50 -50
  18. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  19. package/manifests/lxd/underpost-setup.sh +5 -5
  20. package/package.json +3 -4
  21. package/{manifests/maas → scripts}/ssh-cluster-info.sh +1 -1
  22. package/scripts/ssl.sh +164 -0
  23. package/src/cli/baremetal.js +8 -8
  24. package/src/cli/cloud-init.js +1 -1
  25. package/src/cli/cluster.js +15 -4
  26. package/src/cli/cron.js +1 -1
  27. package/src/cli/db.js +2 -1
  28. package/src/cli/deploy.js +65 -14
  29. package/src/cli/fs.js +2 -2
  30. package/src/cli/image.js +19 -2
  31. package/src/cli/index.js +11 -4
  32. package/src/cli/monitor.js +34 -1
  33. package/src/cli/repository.js +42 -1
  34. package/src/cli/run.js +396 -86
  35. package/src/cli/script.js +32 -0
  36. package/src/cli/secrets.js +34 -0
  37. package/src/cli/test.js +42 -1
  38. package/src/client/components/core/Css.js +0 -8
  39. package/src/client/components/core/windowGetDimensions.js +229 -162
  40. package/src/index.js +2 -2
  41. package/src/mailer/MailerProvider.js +1 -0
  42. package/src/runtime/express/Dockerfile +41 -0
  43. package/src/runtime/express/Express.js +12 -4
  44. package/src/runtime/lampp/Dockerfile +1 -1
  45. package/src/server/backup.js +20 -0
  46. package/src/server/client-build-live.js +12 -10
  47. package/src/server/client-build.js +136 -91
  48. package/src/server/client-dev-server.js +16 -2
  49. package/src/server/client-icons.js +19 -0
  50. package/src/server/conf.js +495 -69
  51. package/src/server/dns.js +169 -46
  52. package/src/server/downloader.js +65 -24
  53. package/src/server/object-layer.js +260 -162
  54. package/src/server/peer.js +2 -8
  55. package/src/server/proxy.js +93 -76
  56. package/src/server/runtime.js +15 -16
  57. package/src/server/ssr.js +4 -4
  58. package/src/server/tls.js +251 -0
  59. package/src/server/valkey.js +11 -10
  60. package/src/ws/IoInterface.js +2 -1
  61. package/src/ws/IoServer.js +2 -1
  62. package/src/ws/core/core.ws.connection.js +1 -1
  63. package/src/ws/core/core.ws.emit.js +1 -1
  64. package/src/ws/core/core.ws.server.js +1 -1
  65. package/manifests/maas/lxd-preseed.yaml +0 -32
  66. package/src/server/ssl.js +0 -108
  67. /package/{manifests/maas → scripts}/device-scan.sh +0 -0
  68. /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
  69. /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
  70. /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
  71. /package/{manifests/maas → scripts}/nvim.sh +0 -0
  72. /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
package/src/cli/run.js CHANGED
@@ -1,6 +1,11 @@
1
+ /**
2
+ * @description The main entry point for the Underpost CLI applications.
3
+ * @module src/cli/run.js
4
+ * @namespace UnderpostRun
5
+ */
6
+
1
7
  import { daemonProcess, getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
2
- import read from 'read';
3
- import { getNpmRootPath } from '../server/conf.js';
8
+ import { getNpmRootPath, isDeployRunnerContext } from '../server/conf.js';
4
9
  import { actionInitLog, loggerFactory } from '../server/logger.js';
5
10
  import UnderpostTest from './test.js';
6
11
  import fs from 'fs-extra';
@@ -12,7 +17,33 @@ import os from 'os';
12
17
 
13
18
  const logger = loggerFactory(import.meta);
14
19
 
20
+ /**
21
+ * @class UnderpostRun
22
+ * @description Manages the execution of various CLI commands and operations.
23
+ * This class provides a set of static methods to perform different tasks
24
+ * such as running tests, deploying applications, managing environment variables,
25
+ * and more. It also includes a default option configuration and a collection of
26
+ * runners for executing specific commands.
27
+ * @memberof UnderpostRun
28
+ */
15
29
  class UnderpostRun {
30
+ /**
31
+ * @static
32
+ * @description Default options for the UnderpostRun class.
33
+ * @type {Object}
34
+ * @property {boolean} dev - Whether to run in development mode.
35
+ * @property {string} podName - The name of the pod to run.
36
+ * @property {string} volumeHostPath - The host path for the volume.
37
+ * @property {string} volumeMountPath - The mount path for the volume.
38
+ * @property {string} imageName - The name of the image to run.
39
+ * @property {string} containerName - The name of the container to run.
40
+ * @property {string} namespace - The namespace to run in.
41
+ * @property {boolean} build - Whether to build the image.
42
+ * @property {number} replicas - The number of replicas to run.
43
+ * @property {boolean} k3s - Whether to run in k3s mode.
44
+ * @property {boolean} kubeadm - Whether to run in kubeadm mode.
45
+ * @memberof UnderpostRun
46
+ */
16
47
  static DEFAULT_OPTION = {
17
48
  dev: false,
18
49
  podName: '',
@@ -26,7 +57,20 @@ class UnderpostRun {
26
57
  k3s: false,
27
58
  kubeadm: false,
28
59
  };
60
+ /**
61
+ * @static
62
+ * @description Collection of runners for executing specific commands.
63
+ * @type {Object}
64
+ * @memberof UnderpostRun
65
+ */
29
66
  static RUNNERS = {
67
+ /**
68
+ * @method spark-template
69
+ * @description Creates a new Spark template project using `sbt new` in `/home/dd/spark-template`, initializes a Git repository, and runs `replace_params.sh` and `build.sh`.
70
+ * @param {string} path - The input value, identifier, or path for the operation.
71
+ * @param {Object} options - The default underpost runner options for customizing workflow
72
+ * @memberof UnderpostRun
73
+ */
30
74
  'spark-template': (path, options = UnderpostRun.DEFAULT_OPTION) => {
31
75
  const dir = '/home/dd/spark-template';
32
76
  shellExec(`sudo rm -rf ${dir}`);
@@ -47,12 +91,33 @@ class UnderpostRun {
47
91
 
48
92
  shellCd('/home/dd/engine');
49
93
  },
94
+ /**
95
+ * @method rmi
96
+ * @description Forces the removal of all local Podman images (`podman rmi $(podman images -qa) --force`).
97
+ * @param {string} path - The input value, identifier, or path for the operation.
98
+ * @param {Object} options - The default underpost runner options for customizing workflow
99
+ * @memberof UnderpostRun
100
+ */
50
101
  rmi: (path, options = UnderpostRun.DEFAULT_OPTION) => {
51
102
  shellExec(`podman rmi $(podman images -qa) --force`);
52
103
  },
104
+ /**
105
+ * @method kill
106
+ * @description Kills the process running on the specified port by finding its PID using `lsof -t -i:${path}`.
107
+ * @param {string} path - The input value, identifier, or path for the operation (used as the port number).
108
+ * @param {Object} options - The default underpost runner options for customizing workflow
109
+ * @memberof UnderpostRun
110
+ */
53
111
  kill: (path, options = UnderpostRun.DEFAULT_OPTION) => {
54
112
  shellExec(`sudo kill -9 $(lsof -t -i:${path})`);
55
113
  },
114
+ /**
115
+ * @method secret
116
+ * @description Creates an Underpost secret named 'underpost' from a file, defaulting to `/home/dd/engine/engine-private/conf/dd-cron/.env.production` if no path is provided.
117
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional path to the secret file).
118
+ * @param {Object} options - The default underpost runner options for customizing workflow
119
+ * @memberof UnderpostRun
120
+ */
56
121
  secret: (path, options = UnderpostRun.DEFAULT_OPTION) => {
57
122
  shellExec(
58
123
  `underpost secret underpost --create-from-file ${
@@ -60,31 +125,62 @@ class UnderpostRun {
60
125
  }`,
61
126
  );
62
127
  },
128
+ /**
129
+ * @method underpost-config
130
+ * @description Calls `UnderpostDeploy.API.configMap` to create a Kubernetes ConfigMap, defaulting to the 'production' environment.
131
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional configuration name/environment).
132
+ * @param {Object} options - The default underpost runner options for customizing workflow
133
+ * @memberof UnderpostRun
134
+ */
63
135
  'underpost-config': (path, options = UnderpostRun.DEFAULT_OPTION) => {
64
136
  UnderpostDeploy.API.configMap(path ?? 'production');
65
137
  },
138
+ /**
139
+ * @method gpu-env
140
+ * @description Sets up a dedicated GPU development environment cluster, resetting and then setting up the cluster with `--dedicated-gpu` and monitoring the pods.
141
+ * @param {string} path - The input value, identifier, or path for the operation.
142
+ * @param {Object} options - The default underpost runner options for customizing workflow
143
+ * @memberof UnderpostRun
144
+ */
66
145
  'gpu-env': (path, options = UnderpostRun.DEFAULT_OPTION) => {
67
146
  shellExec(
68
147
  `node bin cluster --dev --reset && node bin cluster --dev --dedicated-gpu --kubeadm && kubectl get pods --all-namespaces -o wide -w`,
69
148
  );
70
149
  },
150
+ /**
151
+ * @method tf-gpu-test
152
+ * @description Deletes existing `tf-gpu-test-script` ConfigMap and `tf-gpu-test-pod`, and applies the test manifest from `manifests/deployment/tensorflow/tf-gpu-test.yaml`.
153
+ * @param {string} path - The input value, identifier, or path for the operation.
154
+ * @param {Object} options - The default underpost runner options for customizing workflow
155
+ * @memberof UnderpostRun
156
+ */
71
157
  'tf-gpu-test': (path, options = UnderpostRun.DEFAULT_OPTION) => {
72
158
  const { underpostRoot } = options;
73
159
  shellExec(`kubectl delete configmap tf-gpu-test-script`);
74
160
  shellExec(`kubectl delete pod tf-gpu-test-pod`);
75
161
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/deployment/tensorflow/tf-gpu-test.yaml`);
76
162
  },
163
+ /**
164
+ * @method dev-cluster
165
+ * @description Resets and deploys a full development cluster including MongoDB, Valkey, exposes services, and updates `/etc/hosts` for local access.
166
+ * @param {string} path - The input value, identifier, or path for the operation.
167
+ * @param {Object} options - The default underpost runner options for customizing workflow
168
+ * @memberof UnderpostRun
169
+ */
77
170
  'dev-cluster': (path, options = UnderpostRun.DEFAULT_OPTION) => {
78
171
  const baseCommand = options.dev ? 'node bin' : 'underpost';
79
- shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
80
- shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
81
172
  const mongoHosts = ['mongodb-0.mongodb-service'];
82
- shellExec(
83
- `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${mongoHosts.join(
84
- ',',
85
- )} --pull-image`,
86
- );
87
- shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
173
+ if (path !== 'expose') {
174
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
175
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
176
+
177
+ shellExec(
178
+ `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${mongoHosts.join(
179
+ ',',
180
+ )} --pull-image`,
181
+ );
182
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
183
+ }
88
184
  shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
89
185
  shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
90
186
  {
@@ -92,23 +188,91 @@ class UnderpostRun {
92
188
  logger.info(hostListenResult.renderHosts);
93
189
  }
94
190
  },
191
+ /**
192
+ * @method ssh-cluster-info
193
+ * @description Executes the `ssh-cluster-info.sh` script to display cluster connection information.
194
+ * @param {string} path - The input value, identifier, or path for the operation.
195
+ * @param {Object} options - The default underpost runner options for customizing workflow
196
+ * @memberof UnderpostRun
197
+ */
95
198
  'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
96
199
  const { underpostRoot } = options;
97
- shellExec(`chmod +x ${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
98
- shellExec(`${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
200
+ shellExec(`chmod +x ${underpostRoot}/scripts/ssh-cluster-info.sh`);
201
+ shellExec(`${underpostRoot}/scripts/ssh-cluster-info.sh`);
99
202
  },
203
+
204
+ /**
205
+ * @method dev-hosts-expose
206
+ * @description Deploys a specified service in development mode with `/etc/hosts` modification for local access.
207
+ * @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID to deploy).
208
+ * @param {Object} options - The default underpost runner options for customizing workflow
209
+ * @memberof UnderpostRun
210
+ */
211
+ 'dev-hosts-expose': (path, options = UnderpostRun.DEFAULT_OPTION) => {
212
+ shellExec(
213
+ `node bin deploy ${path} development --disable-update-deployment --disable-update-proxy --kubeadm --etc-hosts`,
214
+ );
215
+ },
216
+
217
+ /**
218
+ * @method dev-hosts-restore
219
+ * @description Restores the `/etc/hosts` file to its original state after modifications made during development deployments.
220
+ * @param {string} path - The input value, identifier, or path for the operation.
221
+ * @param {Object} options - The default underpost runner options for customizing workflow
222
+ * @memberof UnderpostRun
223
+ */
224
+ 'dev-hosts-restore': (path, options = UnderpostRun.DEFAULT_OPTION) => {
225
+ shellExec(`node bin deploy --restore-hosts`);
226
+ },
227
+
228
+ /**
229
+ * @method cyberia-ide
230
+ * @description Starts the development environment (IDE) for both `cyberia-server` and `cyberia-client` repositories.
231
+ * @param {string} path - The input value, identifier, or path for the operation.
232
+ * @param {Object} options - The default underpost runner options for customizing workflow
233
+ * @memberof UnderpostRun
234
+ */
100
235
  'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
101
236
  const baseCommand = options.dev ? 'node bin' : 'underpost';
102
237
  shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
103
238
  shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
104
239
  },
240
+ /**
241
+ * @method engine-ide
242
+ * @description Starts the development environment (IDE) for the `engine` and `engine-private` repositories.
243
+ * @param {string} path - The input value, identifier, or path for the operation.
244
+ * @param {Object} options - The default underpost runner options for customizing workflow
245
+ * @memberof UnderpostRun
246
+ */
105
247
  'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
106
248
  const baseCommand = options.dev ? 'node bin' : 'underpost';
107
249
  shellExec(`${baseCommand} run ide /home/dd/engine`);
108
250
  shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
109
251
  },
252
+ /**
253
+ * @method cluster-build
254
+ * @description Build configuration for cluster deployment.
255
+ * @param {string} path - The input value, identifier, or path for the operation.
256
+ * @param {Object} options - The default underpost runner options for customizing workflow
257
+ * @memberof UnderpostRun
258
+ */
259
+ 'cluster-build': (path, options = UnderpostRun.DEFAULT_OPTION) => {
260
+ shellExec(`node bin run clean`);
261
+ shellExec(`node bin run --dev sync-replica template-deploy`);
262
+ shellExec(`node bin run sync-replica template-deploy`);
263
+ shellExec(`node bin env clean`);
264
+ shellExec(`git add . && underpost cmt . build cluster-build`);
265
+ shellExec(`cd engine-private && git add . && underpost cmt . build cluster-build`);
266
+ },
267
+ /**
268
+ * @method template-deploy
269
+ * @description Cleans up, pushes `engine-private` and `engine` repositories with a commit tag `ci package-pwa-microservices-template`.
270
+ * @param {string} path - The input value, identifier, or path for the operation.
271
+ * @param {Object} options - The default underpost runner options for customizing workflow
272
+ * @memberof UnderpostRun
273
+ */
110
274
  'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
111
- const baseCommand = options.dev || true ? 'node bin' : 'underpost';
275
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
112
276
  shellExec(`${baseCommand} run clean`);
113
277
  shellExec(`${baseCommand} push ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
114
278
  shellCd('/home/dd/engine');
@@ -116,16 +280,37 @@ class UnderpostRun {
116
280
  shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
117
281
  shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
118
282
  },
283
+ /**
284
+ * @method clean
285
+ * @description Changes directory to the provided path (defaulting to `/home/dd/engine`) and runs `node bin/deploy clean-core-repo`.
286
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional directory path).
287
+ * @param {Object} options - The default underpost runner options for customizing workflow
288
+ * @memberof UnderpostRun
289
+ */
119
290
  clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
120
291
  shellCd(path ?? `/home/dd/engine`);
121
292
  shellExec(`node bin/deploy clean-core-repo`);
122
293
  },
294
+ /**
295
+ * @method pull
296
+ * @description Cleans the core repository and pulls the latest content for `engine` and `engine-private` repositories from the remote.
297
+ * @param {string} path - The input value, identifier, or path for the operation.
298
+ * @param {Object} options - The default underpost runner options for customizing workflow
299
+ * @memberof UnderpostRun
300
+ */
123
301
  pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
124
302
  shellCd(`/home/dd/engine`);
125
303
  shellExec(`node bin/deploy clean-core-repo`);
126
304
  shellExec(`underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
127
305
  shellExec(`underpost pull ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
128
306
  },
307
+ /**
308
+ * @method release-deploy
309
+ * @description Executes deployment (`underpost run deploy`) for all deployment IDs listed in `./engine-private/deploy/dd.router`.
310
+ * @param {string} path - The input value, identifier, or path for the operation.
311
+ * @param {Object} options - The default underpost runner options for customizing workflow
312
+ * @memberof UnderpostRun
313
+ */
129
314
  'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
130
315
  actionInitLog();
131
316
  shellExec(`underpost --version`);
@@ -135,51 +320,98 @@ class UnderpostRun {
135
320
  shellExec(`underpost run deploy ${deployId}`, { async: true });
136
321
  }
137
322
  },
323
+ /**
324
+ * @method ssh-deploy
325
+ * @description Performs a Git reset, commits with a message `cd ssh-${path}`, and pushes the `engine` repository, likely triggering an SSH-based CD pipeline.
326
+ * @param {string} path - The input value, identifier, or path for the operation (used as the deployment identifier for the commit message).
327
+ * @param {Object} options - The default underpost runner options for customizing workflow
328
+ * @memberof UnderpostRun
329
+ */
138
330
  'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
139
331
  actionInitLog();
140
- const baseCommand = options.dev || true ? 'node bin' : 'underpost';
332
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
141
333
  shellCd('/home/dd/engine');
142
334
  shellExec(`git reset`);
143
335
  shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
144
336
  shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
145
337
  },
338
+ /**
339
+ * @method ide
340
+ * @description Opens a Visual Studio Code (VS Code) session for the specified path using `node ${underpostRoot}/bin/zed ${path}`.
341
+ * @param {string} path - The input value, identifier, or path for the operation (used as the path to the directory to open in the IDE).
342
+ * @param {Object} options - The default underpost runner options for customizing workflow
343
+ * @memberof UnderpostRun
344
+ */
146
345
  ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
147
346
  const { underpostRoot } = options;
148
- shellExec(`node ${underpostRoot}/bin/vs ${path}`);
149
- },
150
- 'dev-client': (_path, options = UnderpostRun.DEFAULT_OPTION) => {
151
- let [deployId, hostpath, subConf, lite] = _path.split(',');
152
- let [host, path] = hostpath.split('/');
153
- if (!path) path = '/';
154
- shellExec(`npm run dev-client ${deployId} ${host} ${path} ${subConf} static${lite === 'l' ? ' l' : ''}`);
347
+ shellExec(`node ${underpostRoot}/bin/zed ${path}`);
155
348
  },
156
- 'dev-api': (path, options = UnderpostRun.DEFAULT_OPTION) => {
157
- let [deployId, subConf] = path.split(',');
158
- shellExec(`npm run dev-api ${deployId} ${subConf}`);
159
- },
160
- sync: (path, options = UnderpostRun.DEFAULT_OPTION) => {
349
+ /**
350
+ * @method sync
351
+ * @description Cleans up, and then runs a deployment synchronization command (`underpost deploy --kubeadm --build-manifest --sync...`) using parameters parsed from `path` (deployId, replicas, versions, image, node).
352
+ * @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string containing deploy parameters).
353
+ * @param {Object} options - The default underpost runner options for customizing workflow
354
+ * @memberof UnderpostRun
355
+ */
356
+ sync: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
357
+ // Dev usage: node bin run --dev --build sync dd-default
161
358
  const env = options.dev ? 'development' : 'production';
162
- const baseCommand = options.dev || true ? 'node bin' : 'underpost';
163
- shellExec(`${baseCommand} run clean`);
164
- const defaultPath = ['dd-default', 1, ``, ``, 'kind-control-plane'];
359
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
360
+ const defaultPath = [
361
+ 'dd-default',
362
+ 1,
363
+ ``,
364
+ ``,
365
+ options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname(),
366
+ ];
165
367
  let [deployId, replicas, versions, image, node] = path ? path.split(',') : defaultPath;
166
- deployId = deployId ?? defaultPath[0];
167
- replicas = replicas ?? defaultPath[1];
168
- versions = versions ?? defaultPath[2];
169
- image = image ?? defaultPath[3];
170
- node = node ?? defaultPath[4];
368
+ deployId = deployId ? deployId : defaultPath[0];
369
+ replicas = replicas ? replicas : defaultPath[1];
370
+ versions = versions ? versions.replaceAll('+', ',') : defaultPath[2];
371
+ image = image ? image : defaultPath[3];
372
+ node = node ? node : defaultPath[4];
373
+
374
+ if (isDeployRunnerContext(path, options)) {
375
+ const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
376
+ if (!validVersion) throw new Error('Version mismatch');
377
+ }
378
+
379
+ const currentTraffic = isDeployRunnerContext(path, options)
380
+ ? UnderpostDeploy.API.getCurrentTraffic(deployId)
381
+ : '';
382
+ let targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : '';
383
+ if (targetTraffic) versions = targetTraffic;
384
+
171
385
  shellExec(
172
386
  `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
173
387
  replicas ?? 1
174
- } --node ${node}${image ? ` --image ${image}` : ''}${
175
- versions ? ` --versions ${versions.replaceAll('+', ',')}` : ''
176
- } dd ${env}`,
388
+ } --node ${node}${image ? ` --image ${image}` : ''}${versions ? ` --versions ${versions}` : ''} dd ${env}`,
177
389
  );
178
- if (!options.build) shellExec(`${baseCommand} deploy --kubeadm ${deployId} ${env}`);
390
+
391
+ if (isDeployRunnerContext(path, options)) {
392
+ shellExec(`${baseCommand} deploy --kubeadm --disable-update-proxy ${deployId} ${env} --versions ${versions}`);
393
+ if (!targetTraffic) targetTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
394
+ await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic);
395
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
396
+ } else logger.info('current traffic', UnderpostDeploy.API.getCurrentTraffic(deployId));
179
397
  },
398
+ /**
399
+ * @method ls-deployments
400
+ * @description Retrieves and logs a table of Kubernetes deployments using `UnderpostDeploy.API.get`.
401
+ * @param {string} path - The input value, identifier, or path for the operation (used as an optional deployment name filter).
402
+ * @param {Object} options - The default underpost runner options for customizing workflow
403
+ * @memberof UnderpostRun
404
+ */
180
405
  'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
181
406
  console.table(await UnderpostDeploy.API.get(path, 'deployments'));
182
407
  },
408
+ /**
409
+ * @method monitor
410
+ * @description Monitors a specific pod (identified by `path`) for the existence of a file (`/await`), and performs conditional actions (like file copying and opening Firefox) when the file is removed.
411
+ * @param {string} path - The input value, identifier, or path for the operation (used as the name of the pod to monitor).
412
+ * @param {Object} options - The default underpost runner options for customizing workflow
413
+ * @memberof UnderpostRun
414
+ */
183
415
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
184
416
  const pid = getTerminalPid();
185
417
  logger.info('monitor pid', pid);
@@ -253,6 +485,13 @@ class UnderpostRun {
253
485
  };
254
486
  _monitor();
255
487
  },
488
+ /**
489
+ * @method db-client
490
+ * @description Deploys and exposes the Adminer database client application (using `adminer:4.7.6-standalone` image) on the cluster.
491
+ * @param {string} path - The input value, identifier, or path for the operation.
492
+ * @param {Object} options - The default underpost runner options for customizing workflow
493
+ * @memberof UnderpostRun
494
+ */
256
495
  'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
257
496
  const { underpostRoot } = options;
258
497
 
@@ -274,6 +513,13 @@ class UnderpostRun {
274
513
  shellExec(`underpost deploy --expose adminer`);
275
514
  }
276
515
  },
516
+ /**
517
+ * @method promote
518
+ * @description Switches traffic between blue/green deployments for a specified deployment ID(s) (uses `dd.router` for 'dd', or a specific ID).
519
+ * @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string: `deployId,env,replicas`).
520
+ * @param {Object} options - The default underpost runner options for customizing workflow
521
+ * @memberof UnderpostRun
522
+ */
277
523
  promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
278
524
  let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
279
525
  if (!inputEnv) inputEnv = 'production';
@@ -290,7 +536,13 @@ class UnderpostRun {
290
536
  UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
291
537
  }
292
538
  },
293
-
539
+ /**
540
+ * @method metrics
541
+ * @description Deploys Prometheus and Grafana for metrics monitoring, targeting the hosts defined in the deployment configuration files.
542
+ * @param {string} path - The input value, identifier, or path for the operation.
543
+ * @param {Object} options - The default underpost runner options for customizing workflow
544
+ * @memberof UnderpostRun
545
+ */
294
546
  metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
295
547
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
296
548
  let hosts = [];
@@ -301,78 +553,119 @@ class UnderpostRun {
301
553
  shellExec(`node bin cluster --prom ${hosts.join(',')}`);
302
554
  shellExec(`node bin cluster --grafana`);
303
555
  },
304
-
305
- cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
306
- const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
307
- const env = 'production';
556
+ /**
557
+ * @method cluster
558
+ * @description Deploys a full production/development ready Kubernetes cluster environment including MongoDB, MariaDB, Valkey, Contour (Ingress), and Cert-Manager, and deploys all services.
559
+ * @param {string} path - The input value, identifier, or path for the operation.
560
+ * @param {Object} options - The default underpost runner options for customizing workflow
561
+ * @memberof UnderpostRun
562
+ */
563
+ cluster: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
564
+ const env = options.dev ? 'development' : 'production';
565
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
566
+ const baseClusterCommand = options.dev ? ' --dev' : '';
308
567
  shellCd(`/home/dd/engine`);
309
- shellExec(`underpost cluster --reset`);
310
- await timer(5000);
311
- shellExec(`underpost cluster --kubeadm`);
568
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --reset`);
312
569
  await timer(5000);
313
- shellExec(`underpost dockerfile-pull-base-images --path /home/dd/engine/src/runtime/lampp --kubeadm-load`);
570
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm`);
314
571
  await timer(5000);
315
- shellExec(`underpost cluster --kubeadm --pull-image --mongodb`);
572
+ let [runtimeImage, deployList] = path.split(',')
573
+ ? path.split(',')
574
+ : ['lampp', fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').replaceAll(',', '+')];
575
+ shellExec(
576
+ `${baseCommand} dockerfile-pull-base-images${baseClusterCommand}${
577
+ runtimeImage ? ` --path /home/dd/engine/src/runtime/${runtimeImage}` : ''
578
+ } --kubeadm-load`,
579
+ );
580
+ if (!deployList) {
581
+ deployList = [];
582
+ logger.warn('No deploy list provided');
583
+ } else deployList = deployList.split('+');
316
584
  await timer(5000);
317
- shellExec(`underpost cluster --kubeadm --pull-image --mariadb`);
585
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --mongodb`);
586
+ if (runtimeImage === 'lampp') {
587
+ await timer(5000);
588
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --mariadb`);
589
+ }
318
590
  await timer(5000);
319
591
  for (const deployId of deployList) {
320
- shellExec(`underpost db ${deployId} --import --git`);
592
+ shellExec(`${baseCommand} db ${deployId} --import --git`);
321
593
  }
322
594
  await timer(5000);
323
- shellExec(`underpost cluster --kubeadm --pull-image --valkey`);
595
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --pull-image --valkey`);
324
596
  await timer(5000);
325
- shellExec(`underpost cluster --kubeadm --contour`);
326
- await timer(5000);
327
- shellExec(`underpost cluster --kubeadm --cert-manager`);
597
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --contour`);
598
+ if (env === 'production') {
599
+ await timer(5000);
600
+ shellExec(`${baseCommand} cluster${baseClusterCommand} --kubeadm --cert-manager`);
601
+ }
328
602
  for (const deployId of deployList) {
329
- shellExec(`underpost deploy ${deployId} ${env} --kubeadm --cert`);
603
+ shellExec(
604
+ `${baseCommand} deploy ${deployId} ${env} --kubeadm${env === 'production' ? ' --cert' : ''}${
605
+ env === 'development' ? ' --etc-hosts' : ''
606
+ }`,
607
+ );
330
608
  }
331
609
  },
610
+ /**
611
+ * @method deploy
612
+ * @description Deploys a specified service (identified by `path`) using blue/green strategy, monitors its status, and switches traffic upon readiness.
613
+ * @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID to deploy).
614
+ * @param {Object} options - The default underpost runner options for customizing workflow
615
+ * @memberof UnderpostRun
616
+ */
332
617
  deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
333
618
  const deployId = path;
334
- const { validVersion, deployVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
619
+ const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
335
620
  if (!validVersion) throw new Error('Version mismatch');
336
621
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
337
622
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
338
623
  const env = 'production';
339
624
  const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
340
625
 
341
- if (options.build === true) {
342
- // deployId, replicas, versions, image, node
343
- shellExec(
344
- `node bin run sync ${deployId},${options.replicas ?? 1},${targetTraffic},${
345
- options.imageName ?? `localhost/rockylinux9-underpost:${deployVersion}`
346
- },${os.hostname()}`,
347
- );
348
- } else shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
349
-
350
- let checkStatusIteration = 0;
351
- const checkStatusIterationMsDelay = 1000;
352
- const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
353
- logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
354
- const minReadyOk = 3;
355
- let readyOk = 0;
356
-
357
- while (readyOk < minReadyOk) {
358
- const ready = UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready;
359
- if (ready === true) {
360
- readyOk++;
361
- logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
362
- }
363
- await timer(checkStatusIterationMsDelay);
364
- checkStatusIteration++;
365
- logger.info(
366
- `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
367
- );
368
- }
626
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
369
627
 
370
- logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
628
+ await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic, ignorePods);
371
629
 
372
630
  UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
631
+ },
373
632
 
374
- // shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
633
+ /**
634
+ * @method sync-replica
635
+ * @description Syncs a replica for the dd.router
636
+ * @param {string} path - The input value, identifier, or path for the operation.
637
+ * @param {Object} options - The default underpost runner options for customizing workflow
638
+ * @memberof UnderpostRun
639
+ */
640
+ 'sync-replica': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
641
+ const env = options.dev ? 'development' : 'production';
642
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
643
+
644
+ for (let deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
645
+ deployId = deployId.trim();
646
+ const _path = '/single-replica';
647
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
648
+ shellExec(`${baseCommand} env ${deployId} ${env}`);
649
+ for (const host of Object.keys(confServer)) {
650
+ if (!(_path in confServer[host])) continue;
651
+ shellExec(`node bin/deploy build-single-replica ${deployId} ${host} ${_path}`);
652
+ shellExec(`node bin/deploy build-full-client ${deployId}`);
653
+ const node = options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname();
654
+ // deployId, replicas, versions, image, node
655
+ let defaultPath = [deployId, 1, ``, ``, node];
656
+ shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
657
+ }
658
+ }
659
+ if (isDeployRunnerContext(path, options)) shellExec(`${baseCommand} run promote ${path} production`);
375
660
  },
661
+
662
+ /**
663
+ * @method tf-vae-test
664
+ * @description Creates and runs a job pod (`tf-vae-test`) that installs TensorFlow dependencies, clones the TensorFlow docs, and runs the CVAE tutorial script, with a terminal monitor attached.
665
+ * @param {string} path - The input value, identifier, or path for the operation.
666
+ * @param {Object} options - The default underpost runner options for customizing workflow
667
+ * @memberof UnderpostRun
668
+ */
376
669
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
377
670
  const { underpostRoot } = options;
378
671
  const podName = 'tf-vae-test';
@@ -406,6 +699,13 @@ class UnderpostRun {
406
699
  ],
407
700
  });
408
701
  },
702
+ /**
703
+ * @method deploy-job
704
+ * @description Creates and applies a custom Kubernetes Pod manifest (Job) for running arbitrary commands inside a container image (defaulting to a TensorFlow/NVIDIA image).
705
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional script path or job argument).
706
+ * @param {Object} options - The default underpost runner options for customizing workflow
707
+ * @memberof UnderpostRun
708
+ */
409
709
  'deploy-job': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
410
710
  const podName = options.podName || 'deploy-job';
411
711
  const volumeName = `${podName}-volume`;
@@ -485,7 +785,17 @@ EOF`;
485
785
  }
486
786
  },
487
787
  };
788
+
488
789
  static API = {
790
+ /**
791
+ * @method callback
792
+ * @description Initiates the execution of a specified CLI command (runner) with the given input value (`path`) and processed options.
793
+ * @param {string} runner - The name of the runner to execute.
794
+ * @param {string} path - The input value, identifier, or path for the operation.
795
+ * @param {Object} options - The default underpost runner options for customizing workflow
796
+ * @memberof UnderpostRun
797
+ * @returns {Promise<any>} The result of the callback execution.
798
+ */
489
799
  async callback(runner, path, options = UnderpostRun.DEFAULT_OPTION) {
490
800
  const npmRoot = getNpmRootPath();
491
801
  const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;