underpost 2.8.885 → 2.8.886

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 (66) 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/CHANGELOG.md +145 -1
  8. package/Dockerfile +1 -1
  9. package/README.md +3 -3
  10. package/bin/build.js +18 -9
  11. package/bin/deploy.js +93 -187
  12. package/cli.md +2 -2
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  14. package/manifests/deployment/dd-test-development/deployment.yaml +54 -54
  15. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  16. package/manifests/lxd/underpost-setup.sh +5 -5
  17. package/package.json +3 -3
  18. package/scripts/ssl.sh +164 -0
  19. package/src/cli/baremetal.js +7 -7
  20. package/src/cli/cloud-init.js +1 -1
  21. package/src/cli/cluster.js +10 -3
  22. package/src/cli/cron.js +1 -1
  23. package/src/cli/db.js +1 -1
  24. package/src/cli/deploy.js +33 -1
  25. package/src/cli/fs.js +2 -2
  26. package/src/cli/image.js +7 -0
  27. package/src/cli/monitor.js +33 -1
  28. package/src/cli/run.js +315 -51
  29. package/src/cli/script.js +32 -0
  30. package/src/cli/secrets.js +34 -0
  31. package/src/cli/test.js +42 -1
  32. package/src/client/components/core/Css.js +0 -8
  33. package/src/client/components/core/windowGetDimensions.js +229 -162
  34. package/src/index.js +2 -2
  35. package/src/mailer/MailerProvider.js +1 -0
  36. package/src/runtime/express/Express.js +12 -4
  37. package/src/runtime/lampp/Dockerfile +1 -1
  38. package/src/server/backup.js +20 -0
  39. package/src/server/client-build-live.js +12 -10
  40. package/src/server/client-build.js +136 -91
  41. package/src/server/client-dev-server.js +16 -2
  42. package/src/server/client-icons.js +19 -0
  43. package/src/server/conf.js +470 -60
  44. package/src/server/dns.js +184 -42
  45. package/src/server/downloader.js +65 -24
  46. package/src/server/object-layer.js +260 -162
  47. package/src/server/peer.js +2 -8
  48. package/src/server/proxy.js +93 -76
  49. package/src/server/runtime.js +15 -16
  50. package/src/server/ssr.js +4 -4
  51. package/src/server/tls.js +251 -0
  52. package/src/server/valkey.js +11 -10
  53. package/src/ws/IoInterface.js +2 -1
  54. package/src/ws/IoServer.js +2 -1
  55. package/src/ws/core/core.ws.connection.js +1 -1
  56. package/src/ws/core/core.ws.emit.js +1 -1
  57. package/src/ws/core/core.ws.server.js +1 -1
  58. package/manifests/maas/lxd-preseed.yaml +0 -32
  59. package/src/server/ssl.js +0 -108
  60. /package/{manifests/maas → scripts}/device-scan.sh +0 -0
  61. /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
  62. /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
  63. /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
  64. /package/{manifests/maas → scripts}/nvim.sh +0 -0
  65. /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
  66. /package/{manifests/maas → scripts}/ssh-cluster-info.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,20 +125,48 @@ 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
172
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
@@ -92,21 +185,64 @@ class UnderpostRun {
92
185
  logger.info(hostListenResult.renderHosts);
93
186
  }
94
187
  },
188
+ /**
189
+ * @method ssh-cluster-info
190
+ * @description Executes the `ssh-cluster-info.sh` script to display cluster connection information.
191
+ * @param {string} path - The input value, identifier, or path for the operation.
192
+ * @param {Object} options - The default underpost runner options for customizing workflow
193
+ * @memberof UnderpostRun
194
+ */
95
195
  'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
96
196
  const { underpostRoot } = options;
97
- shellExec(`chmod +x ${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
98
- shellExec(`${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
197
+ shellExec(`chmod +x ${underpostRoot}/scripts/ssh-cluster-info.sh`);
198
+ shellExec(`${underpostRoot}/scripts/ssh-cluster-info.sh`);
99
199
  },
200
+ /**
201
+ * @method cyberia-ide
202
+ * @description Starts the development environment (IDE) for both `cyberia-server` and `cyberia-client` repositories.
203
+ * @param {string} path - The input value, identifier, or path for the operation.
204
+ * @param {Object} options - The default underpost runner options for customizing workflow
205
+ * @memberof UnderpostRun
206
+ */
100
207
  'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
101
208
  const baseCommand = options.dev ? 'node bin' : 'underpost';
102
209
  shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
103
210
  shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
104
211
  },
212
+ /**
213
+ * @method engine-ide
214
+ * @description Starts the development environment (IDE) for the `engine` and `engine-private` repositories.
215
+ * @param {string} path - The input value, identifier, or path for the operation.
216
+ * @param {Object} options - The default underpost runner options for customizing workflow
217
+ * @memberof UnderpostRun
218
+ */
105
219
  'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
106
220
  const baseCommand = options.dev ? 'node bin' : 'underpost';
107
221
  shellExec(`${baseCommand} run ide /home/dd/engine`);
108
222
  shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
109
223
  },
224
+ /**
225
+ * @method cluster-build
226
+ * @description Build configuration for cluster deployment.
227
+ * @param {string} path - The input value, identifier, or path for the operation.
228
+ * @param {Object} options - The default underpost runner options for customizing workflow
229
+ * @memberof UnderpostRun
230
+ */
231
+ 'cluster-build': (path, options = UnderpostRun.DEFAULT_OPTION) => {
232
+ shellExec(`node bin run clean`);
233
+ shellExec(`node bin run --dev sync-replica template-deploy`);
234
+ shellExec(`node bin run sync-replica template-deploy`);
235
+ shellExec(`node bin env clean`);
236
+ shellExec(`git add . && underpost cmt . build cluster-build`);
237
+ shellExec(`cd engine-private && git add . && underpost cmt . build cluster-build`);
238
+ },
239
+ /**
240
+ * @method template-deploy
241
+ * @description Cleans up, pushes `engine-private` and `engine` repositories with a commit tag `ci package-pwa-microservices-template`.
242
+ * @param {string} path - The input value, identifier, or path for the operation.
243
+ * @param {Object} options - The default underpost runner options for customizing workflow
244
+ * @memberof UnderpostRun
245
+ */
110
246
  'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
111
247
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
112
248
  shellExec(`${baseCommand} run clean`);
@@ -116,16 +252,37 @@ class UnderpostRun {
116
252
  shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
117
253
  shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
118
254
  },
255
+ /**
256
+ * @method clean
257
+ * @description Changes directory to the provided path (defaulting to `/home/dd/engine`) and runs `node bin/deploy clean-core-repo`.
258
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional directory path).
259
+ * @param {Object} options - The default underpost runner options for customizing workflow
260
+ * @memberof UnderpostRun
261
+ */
119
262
  clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
120
263
  shellCd(path ?? `/home/dd/engine`);
121
264
  shellExec(`node bin/deploy clean-core-repo`);
122
265
  },
266
+ /**
267
+ * @method pull
268
+ * @description Cleans the core repository and pulls the latest content for `engine` and `engine-private` repositories from the remote.
269
+ * @param {string} path - The input value, identifier, or path for the operation.
270
+ * @param {Object} options - The default underpost runner options for customizing workflow
271
+ * @memberof UnderpostRun
272
+ */
123
273
  pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
124
274
  shellCd(`/home/dd/engine`);
125
275
  shellExec(`node bin/deploy clean-core-repo`);
126
276
  shellExec(`underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
127
277
  shellExec(`underpost pull ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
128
278
  },
279
+ /**
280
+ * @method release-deploy
281
+ * @description Executes deployment (`underpost run deploy`) for all deployment IDs listed in `./engine-private/deploy/dd.router`.
282
+ * @param {string} path - The input value, identifier, or path for the operation.
283
+ * @param {Object} options - The default underpost runner options for customizing workflow
284
+ * @memberof UnderpostRun
285
+ */
129
286
  'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
130
287
  actionInitLog();
131
288
  shellExec(`underpost --version`);
@@ -135,6 +292,13 @@ class UnderpostRun {
135
292
  shellExec(`underpost run deploy ${deployId}`, { async: true });
136
293
  }
137
294
  },
295
+ /**
296
+ * @method ssh-deploy
297
+ * @description Performs a Git reset, commits with a message `cd ssh-${path}`, and pushes the `engine` repository, likely triggering an SSH-based CD pipeline.
298
+ * @param {string} path - The input value, identifier, or path for the operation (used as the deployment identifier for the commit message).
299
+ * @param {Object} options - The default underpost runner options for customizing workflow
300
+ * @memberof UnderpostRun
301
+ */
138
302
  'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
139
303
  actionInitLog();
140
304
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
@@ -143,31 +307,53 @@ class UnderpostRun {
143
307
  shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
144
308
  shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
145
309
  },
310
+ /**
311
+ * @method ide
312
+ * @description Opens a Visual Studio Code (VS Code) session for the specified path using `node ${underpostRoot}/bin/vs ${path}`.
313
+ * @param {string} path - The input value, identifier, or path for the operation (used as the path to the directory to open in the IDE).
314
+ * @param {Object} options - The default underpost runner options for customizing workflow
315
+ * @memberof UnderpostRun
316
+ */
146
317
  ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
147
318
  const { underpostRoot } = options;
148
319
  shellExec(`node ${underpostRoot}/bin/vs ${path}`);
149
320
  },
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' : ''}`);
155
- },
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) => {
321
+ /**
322
+ * @method sync
323
+ * @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).
324
+ * @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string containing deploy parameters).
325
+ * @param {Object} options - The default underpost runner options for customizing workflow
326
+ * @memberof UnderpostRun
327
+ */
328
+ sync: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
329
+ // Dev usage: node bin run --dev --build sync dd-default
161
330
  const env = options.dev ? 'development' : 'production';
162
331
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
163
- shellExec(`${baseCommand} run clean`);
164
- const defaultPath = ['dd-default', 1, ``, ``, 'kind-control-plane'];
332
+ const defaultPath = [
333
+ 'dd-default',
334
+ 1,
335
+ ``,
336
+ ``,
337
+ options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname(),
338
+ ];
165
339
  let [deployId, replicas, versions, image, node] = path ? path.split(',') : defaultPath;
166
340
  deployId = deployId ?? defaultPath[0];
167
341
  replicas = replicas ?? defaultPath[1];
168
342
  versions = versions ?? defaultPath[2];
169
343
  image = image ?? defaultPath[3];
170
344
  node = node ?? defaultPath[4];
345
+
346
+ if (isDeployRunnerContext(path, options)) {
347
+ const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
348
+ if (!validVersion) throw new Error('Version mismatch');
349
+ }
350
+
351
+ const currentTraffic = isDeployRunnerContext(path, options)
352
+ ? UnderpostDeploy.API.getCurrentTraffic(deployId)
353
+ : '';
354
+ let targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : '';
355
+ if (targetTraffic) versions = targetTraffic;
356
+
171
357
  shellExec(
172
358
  `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
173
359
  replicas ?? 1
@@ -175,11 +361,31 @@ class UnderpostRun {
175
361
  versions ? ` --versions ${versions.replaceAll('+', ',')}` : ''
176
362
  } dd ${env}`,
177
363
  );
178
- if (!options.build) shellExec(`${baseCommand} deploy --kubeadm ${deployId} ${env}`);
364
+
365
+ if (isDeployRunnerContext(path, options)) {
366
+ shellExec(`${baseCommand} deploy --kubeadm ${deployId} ${env}`);
367
+ if (!targetTraffic) targetTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
368
+ await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic);
369
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
370
+ } else logger.info('current traffic', UnderpostDeploy.API.getCurrentTraffic(deployId));
179
371
  },
372
+ /**
373
+ * @method ls-deployments
374
+ * @description Retrieves and logs a table of Kubernetes deployments using `UnderpostDeploy.API.get`.
375
+ * @param {string} path - The input value, identifier, or path for the operation (used as an optional deployment name filter).
376
+ * @param {Object} options - The default underpost runner options for customizing workflow
377
+ * @memberof UnderpostRun
378
+ */
180
379
  'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
181
380
  console.table(await UnderpostDeploy.API.get(path, 'deployments'));
182
381
  },
382
+ /**
383
+ * @method monitor
384
+ * @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.
385
+ * @param {string} path - The input value, identifier, or path for the operation (used as the name of the pod to monitor).
386
+ * @param {Object} options - The default underpost runner options for customizing workflow
387
+ * @memberof UnderpostRun
388
+ */
183
389
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
184
390
  const pid = getTerminalPid();
185
391
  logger.info('monitor pid', pid);
@@ -253,6 +459,13 @@ class UnderpostRun {
253
459
  };
254
460
  _monitor();
255
461
  },
462
+ /**
463
+ * @method db-client
464
+ * @description Deploys and exposes the Adminer database client application (using `adminer:4.7.6-standalone` image) on the cluster.
465
+ * @param {string} path - The input value, identifier, or path for the operation.
466
+ * @param {Object} options - The default underpost runner options for customizing workflow
467
+ * @memberof UnderpostRun
468
+ */
256
469
  'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
257
470
  const { underpostRoot } = options;
258
471
 
@@ -274,6 +487,13 @@ class UnderpostRun {
274
487
  shellExec(`underpost deploy --expose adminer`);
275
488
  }
276
489
  },
490
+ /**
491
+ * @method promote
492
+ * @description Switches traffic between blue/green deployments for a specified deployment ID(s) (uses `dd.router` for 'dd', or a specific ID).
493
+ * @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string: `deployId,env,replicas`).
494
+ * @param {Object} options - The default underpost runner options for customizing workflow
495
+ * @memberof UnderpostRun
496
+ */
277
497
  promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
278
498
  let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
279
499
  if (!inputEnv) inputEnv = 'production';
@@ -290,7 +510,13 @@ class UnderpostRun {
290
510
  UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
291
511
  }
292
512
  },
293
-
513
+ /**
514
+ * @method metrics
515
+ * @description Deploys Prometheus and Grafana for metrics monitoring, targeting the hosts defined in the deployment configuration files.
516
+ * @param {string} path - The input value, identifier, or path for the operation.
517
+ * @param {Object} options - The default underpost runner options for customizing workflow
518
+ * @memberof UnderpostRun
519
+ */
294
520
  metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
295
521
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
296
522
  let hosts = [];
@@ -301,7 +527,13 @@ class UnderpostRun {
301
527
  shellExec(`node bin cluster --prom ${hosts.join(',')}`);
302
528
  shellExec(`node bin cluster --grafana`);
303
529
  },
304
-
530
+ /**
531
+ * @method cluster
532
+ * @description Deploys a full production-ready Kubernetes cluster environment including MongoDB, MariaDB, Valkey, Contour (Ingress), and Cert-Manager, and deploys all services.
533
+ * @param {string} path - The input value, identifier, or path for the operation.
534
+ * @param {Object} options - The default underpost runner options for customizing workflow
535
+ * @memberof UnderpostRun
536
+ */
305
537
  cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
306
538
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
307
539
  const env = 'production';
@@ -329,50 +561,65 @@ class UnderpostRun {
329
561
  shellExec(`underpost deploy ${deployId} ${env} --kubeadm --cert`);
330
562
  }
331
563
  },
564
+ /**
565
+ * @method deploy
566
+ * @description Deploys a specified service (identified by `path`) using blue/green strategy, monitors its status, and switches traffic upon readiness.
567
+ * @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID to deploy).
568
+ * @param {Object} options - The default underpost runner options for customizing workflow
569
+ * @memberof UnderpostRun
570
+ */
332
571
  deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
333
572
  const deployId = path;
334
- const { validVersion, deployVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
573
+ const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
335
574
  if (!validVersion) throw new Error('Version mismatch');
336
575
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
337
576
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
338
577
  const env = 'production';
339
578
  const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
340
579
 
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}`);
580
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
349
581
 
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
- }
369
-
370
- logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
582
+ await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic, ignorePods);
371
583
 
372
584
  UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
585
+ },
586
+
587
+ /**
588
+ * @method sync-replica
589
+ * @description Syncs a replica for the dd.router
590
+ * @param {string} path - The input value, identifier, or path for the operation.
591
+ * @param {Object} options - The default underpost runner options for customizing workflow
592
+ * @memberof UnderpostRun
593
+ */
594
+ 'sync-replica': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
595
+ const env = options.dev ? 'development' : 'production';
596
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
373
597
 
374
- // shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
598
+ for (let deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
599
+ deployId = deployId.trim();
600
+ const _path = '/single-replica';
601
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
602
+ shellExec(`${baseCommand} env ${deployId} ${env}`);
603
+ for (const host of Object.keys(confServer)) {
604
+ if (!(_path in confServer[host])) continue;
605
+ shellExec(`node bin/deploy build-single-replica ${deployId} ${host} ${_path}`);
606
+ shellExec(`node bin/deploy build-full-client ${deployId}`);
607
+ const node = options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname();
608
+ // deployId, replicas, versions, image, node
609
+ let defaultPath = [deployId, 1, ``, ``, node];
610
+ shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
611
+ }
612
+ }
613
+ if (isDeployRunnerContext(path, options)) shellExec(`${baseCommand} run promote ${path} production`);
375
614
  },
615
+
616
+ /**
617
+ * @method tf-vae-test
618
+ * @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.
619
+ * @param {string} path - The input value, identifier, or path for the operation.
620
+ * @param {Object} options - The default underpost runner options for customizing workflow
621
+ * @memberof UnderpostRun
622
+ */
376
623
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
377
624
  const { underpostRoot } = options;
378
625
  const podName = 'tf-vae-test';
@@ -406,6 +653,13 @@ class UnderpostRun {
406
653
  ],
407
654
  });
408
655
  },
656
+ /**
657
+ * @method deploy-job
658
+ * @description Creates and applies a custom Kubernetes Pod manifest (Job) for running arbitrary commands inside a container image (defaulting to a TensorFlow/NVIDIA image).
659
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional script path or job argument).
660
+ * @param {Object} options - The default underpost runner options for customizing workflow
661
+ * @memberof UnderpostRun
662
+ */
409
663
  'deploy-job': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
410
664
  const podName = options.podName || 'deploy-job';
411
665
  const volumeName = `${podName}-volume`;
@@ -485,7 +739,17 @@ EOF`;
485
739
  }
486
740
  },
487
741
  };
742
+
488
743
  static API = {
744
+ /**
745
+ * @method callback
746
+ * @description Initiates the execution of a specified CLI command (runner) with the given input value (`path`) and processed options.
747
+ * @param {string} runner - The name of the runner to execute.
748
+ * @param {string} path - The input value, identifier, or path for the operation.
749
+ * @param {Object} options - The default underpost runner options for customizing workflow
750
+ * @memberof UnderpostRun
751
+ * @returns {Promise<any>} The result of the callback execution.
752
+ */
489
753
  async callback(runner, path, options = UnderpostRun.DEFAULT_OPTION) {
490
754
  const npmRoot = getNpmRootPath();
491
755
  const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
package/src/cli/script.js CHANGED
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Script module for managing the execution of scripts.
3
+ * @module src/cli/script.js
4
+ * @namespace UnderpostScript
5
+ */
6
+
1
7
  import { getNpmRootPath } from '../server/conf.js';
2
8
  import { loggerFactory } from '../server/logger.js';
3
9
  import { shellExec } from '../server/process.js';
@@ -6,14 +12,34 @@ import UnderpostDeploy from './deploy.js';
6
12
 
7
13
  const logger = loggerFactory(import.meta);
8
14
 
15
+ /**
16
+ * @class UnderpostScript
17
+ * @description Manages the execution of scripts.
18
+ * @memberof UnderpostScript
19
+ */
9
20
  class UnderpostScript {
10
21
  static API = {
22
+ /**
23
+ * @method set
24
+ * @description Sets a script in the package.json file.
25
+ * @param {string} key - The key for the script.
26
+ * @param {string} value - The value for the script.
27
+ * @memberof UnderpostScript
28
+ */
11
29
  set(key, value) {
12
30
  const npmRoot = `${getNpmRootPath()}/underpost`;
13
31
  const packageJson = JSON.parse(fs.readFileSync(`${npmRoot}/package.json`, 'utf8'));
14
32
  packageJson.scripts[key] = value;
15
33
  fs.writeFileSync(`${npmRoot}/package.json`, JSON.stringify(packageJson, null, 4));
16
34
  },
35
+ /**
36
+ * @method run
37
+ * @description Runs a script.
38
+ * @param {string} key - The key for the script.
39
+ * @param {string} value - The value for the script.
40
+ * @param {object} options - The options for the script.
41
+ * @memberof UnderpostScript
42
+ */
17
43
  run(key, value, options) {
18
44
  const npmRoot = `${getNpmRootPath()}/underpost`;
19
45
  const packageJson = JSON.parse(fs.readFileSync(`${npmRoot}/package.json`, 'utf8'));
@@ -41,6 +67,12 @@ class UnderpostScript {
41
67
  }
42
68
  shellExec(`cd ${npmRoot} && npm run ${key}`);
43
69
  },
70
+ /**
71
+ * @method get
72
+ * @description Gets a script from the package.json file.
73
+ * @param {string} key - The key for the script.
74
+ * @memberof UnderpostScript
75
+ */
44
76
  get(key) {
45
77
  const npmRoot = `${getNpmRootPath()}/underpost`;
46
78
  const packageJson = JSON.parse(fs.readFileSync(`${npmRoot}/package.json`, 'utf8'));
@@ -1,14 +1,43 @@
1
+ /**
2
+ * Secrets module for managing the secrets of the application.
3
+ * @module src/cli/secrets.js
4
+ * @namespace UnderpostSecret
5
+ */
6
+
1
7
  import dotenv from 'dotenv';
2
8
  import { shellExec } from '../server/process.js';
3
9
  import fs from 'fs-extra';
4
10
  import UnderpostRootEnv from './env.js';
5
11
 
12
+ dotenv.config();
13
+
14
+ /**
15
+ * @class UnderpostSecret
16
+ * @description Manages the secrets of the application.
17
+ * @memberof UnderpostSecret
18
+ */
6
19
  class UnderpostSecret {
7
20
  static API = {
21
+ /**
22
+ * @method docker
23
+ * @description Manages the secrets of the application.
24
+ * @memberof UnderpostSecret
25
+ */
8
26
  docker: {
27
+ /**
28
+ * @method init
29
+ * @description Initializes the docker secrets.
30
+ * @memberof UnderpostSecret
31
+ */
9
32
  init() {
10
33
  shellExec(`docker swarm init`);
11
34
  },
35
+ /**
36
+ * @method createFromEnvFile
37
+ * @description Creates a secret from an env file.
38
+ * @param {string} envPath - The path to the env file.
39
+ * @memberof UnderpostSecret
40
+ */
12
41
  createFromEnvFile(envPath) {
13
42
  const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
14
43
  for (const key of Object.keys(envObj)) {
@@ -23,6 +52,11 @@ class UnderpostSecret {
23
52
  shellExec(`docker secret ls`);
24
53
  },
25
54
  },
55
+ /**
56
+ * @method underpost
57
+ * @description Manages the secrets of the application.
58
+ * @memberof UnderpostSecret
59
+ */
26
60
  underpost: {
27
61
  createFromEnvFile(envPath) {
28
62
  const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));