cyberia 3.2.12 → 3.2.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +127 -68
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
- package/.github/workflows/engine-cyberia.cd.yml +34 -24
- package/.github/workflows/engine-cyberia.ci.yml +27 -5
- package/.github/workflows/ghpkg.ci.yml +89 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +19 -12
- package/.github/workflows/publish.ci.yml +2 -2
- package/.github/workflows/publish.cyberia.ci.yml +5 -16
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +451 -1
- package/CLI-HELP.md +1040 -1130
- package/Dockerfile +141 -43
- package/Dockerfile.dev +143 -0
- package/Dockerfile.test +165 -0
- package/README.md +1 -1
- package/baremetal/commission-workflows.json +1 -0
- package/bin/build.js +128 -136
- package/bin/build.template.js +25 -179
- package/bin/cyberia.js +1089 -188
- package/bin/deploy.js +6 -3
- package/bin/index.js +1089 -188
- package/bump.config.js +1 -0
- package/compose.env +131 -0
- package/conf.js +19 -1
- package/deployment.yaml +74 -2
- package/docker-compose.yml +316 -0
- package/hardhat/hardhat.config.js +2 -2
- package/hardhat/package-lock.json +620 -2041
- package/hardhat/package.json +7 -5
- package/hardhat/scripts/deployObjectLayerToken.js +18 -18
- package/hardhat/test/ObjectLayerToken.js +378 -274
- package/ipfs/configure-ipfs.sh +13 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +74 -2
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +87 -0
- package/package.json +34 -22
- package/pv-pvc.yaml +32 -0
- package/scripts/disk-clean.sh +85 -60
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/rocky-kickstart.sh +877 -185
- package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
- package/scripts/test-monitor.sh +248 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
- package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
- package/src/api/cyberia-action/cyberia-action.model.js +21 -29
- package/src/api/cyberia-action/cyberia-action.router.js +42 -7
- package/src/api/cyberia-action/cyberia-action.service.js +20 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
- package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
- package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
- package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
- package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
- package/src/api/cyberia-map/cyberia-map.router.js +21 -6
- package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
- package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
- package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -122
- package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/ipfs/ipfs.service.js +28 -15
- package/src/api/object-layer/object-layer.router.js +25 -15
- package/src/api/object-layer/object-layer.service.js +15 -20
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
- package/src/cli/baremetal.js +717 -16
- package/src/cli/cluster.js +80 -5
- package/src/cli/deploy.js +326 -292
- package/src/cli/docker-compose.js +648 -0
- package/src/cli/env.js +12 -6
- package/src/cli/fs.js +75 -30
- package/src/cli/image.js +187 -55
- package/src/cli/index.js +157 -6
- package/src/cli/kickstart.js +142 -20
- package/src/cli/monitor.js +387 -6
- package/src/cli/release.js +72 -15
- package/src/cli/repository.js +339 -40
- package/src/cli/run.js +678 -186
- package/src/cli/secrets.js +82 -48
- package/src/cli/ssh.js +234 -0
- package/src/cli/static.js +2 -2
- package/src/client/components/core/PanelForm.js +44 -44
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
- package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +92 -20
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +272 -50
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +28 -12
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +27 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
- package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -6
- package/src/{server → client-builder}/client-build-docs.js +15 -5
- package/src/{server → client-builder}/client-build-live.js +3 -3
- package/src/{server → client-builder}/client-build.js +25 -22
- package/src/{server → client-builder}/client-dev-server.js +3 -3
- package/src/{server → client-builder}/client-icons.js +2 -2
- package/src/{server → client-builder}/ssr.js +5 -5
- package/src/client.build.js +1 -3
- package/src/client.dev.js +1 -1
- package/src/db/mongo/MongoBootstrap.js +12 -12
- package/src/db/mongo/MongooseDB.js +2 -1
- package/src/grpc/cyberia/grpc-server.js +255 -70
- package/src/index.js +12 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
- package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +81 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2107 -0
- package/src/{server → projects/cyberia}/ipfs-client.js +6 -4
- package/src/{server → projects/cyberia}/object-layer.js +12 -108
- package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
- package/src/{server → projects/cyberia}/shape-generator.js +2 -2
- package/src/projects/underpost/catalog-underpost.js +60 -0
- package/src/runtime/cyberia-client/Dockerfile +31 -83
- package/src/runtime/cyberia-client/Dockerfile.dev +23 -30
- package/src/runtime/cyberia-server/Dockerfile +23 -42
- package/src/runtime/cyberia-server/Dockerfile.dev +20 -35
- package/src/runtime/engine-cyberia/Dockerfile +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +131 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +87 -0
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/runtime/wp/Dockerfile +3 -3
- package/src/server/catalog.js +72 -0
- package/src/server/conf.js +415 -60
- package/src/server/runtime-status.js +252 -0
- package/src/server/start.js +42 -11
- package/src/server.js +6 -2
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +267 -0
- package/test/shape-generator.test.js +7 -1
- package/typedoc.dd-cyberia.json +3 -1
- package/typedoc.json +3 -1
- package/manifests/deployment/dd-test-development/deployment.yaml +0 -256
- package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
- /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
- /package/src/{server → client-builder}/client-formatted.js +0 -0
package/src/cli/run.js
CHANGED
|
@@ -15,13 +15,14 @@ import {
|
|
|
15
15
|
getNpmRootPath,
|
|
16
16
|
isDeployRunnerContext,
|
|
17
17
|
loadConfServerJson,
|
|
18
|
+
loadReplicas,
|
|
18
19
|
writeEnv,
|
|
19
20
|
} from '../server/conf.js';
|
|
20
21
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
21
22
|
|
|
22
23
|
import fs from 'fs-extra';
|
|
23
24
|
import net from 'net';
|
|
24
|
-
import { range, setPad, timer } from '../client/components/core/CommonJs.js';
|
|
25
|
+
import { range, s4, setPad, timer } from '../client/components/core/CommonJs.js';
|
|
25
26
|
|
|
26
27
|
import os from 'os';
|
|
27
28
|
import Underpost from '../index.js';
|
|
@@ -51,10 +52,12 @@ const logger = loggerFactory(import.meta);
|
|
|
51
52
|
/**
|
|
52
53
|
* @constant DEFAULT_OPTION
|
|
53
54
|
* @description Default options for the UnderpostRun class.
|
|
55
|
+
* @typedef {Object} UnderpostRunDefaultOptions
|
|
54
56
|
* @type {Object}
|
|
55
57
|
* @property {boolean} dev - Whether to run in development mode.
|
|
56
58
|
* @property {string} podName - The name of the pod to run.
|
|
57
59
|
* @property {string} nodeName - The name of the node to run.
|
|
60
|
+
* @property {string} sshKeyPath - Private key path for node SSH operations, forwarded to volume shipping over SSH.
|
|
58
61
|
* @property {number} port - Custom port to use.
|
|
59
62
|
* @property {string} volumeHostPath - The host path for the volume.
|
|
60
63
|
* @property {string} volumeMountPath - The mount path for the volume.
|
|
@@ -119,12 +122,14 @@ const logger = loggerFactory(import.meta);
|
|
|
119
122
|
* @property {boolean} skipFullBuild - Whether to skip the full client bundle build during deployment (supported by: sync, template-deploy).
|
|
120
123
|
* @property {boolean} pullBundle - Whether to pull the bundle before running. Use together with --skip-full-build to skip the local build entirely (supported by: sync, template-deploy).
|
|
121
124
|
* @property {boolean} remove - Whether to remove/teardown resources instead of creating them (e.g. delete-expose for k3s proxy devices in dev-cluster).
|
|
125
|
+
* @property {boolean} test - Whether to enable test/generic-purpose mode (e.g. use self-signed TLS instead of cert-manager).
|
|
122
126
|
* @memberof UnderpostRun
|
|
123
127
|
*/
|
|
124
128
|
const DEFAULT_OPTION = {
|
|
125
129
|
dev: false,
|
|
126
130
|
podName: '',
|
|
127
131
|
nodeName: '',
|
|
132
|
+
sshKeyPath: '',
|
|
128
133
|
port: 0,
|
|
129
134
|
volumeHostPath: '',
|
|
130
135
|
volumeMountPath: '',
|
|
@@ -187,6 +192,7 @@ const DEFAULT_OPTION = {
|
|
|
187
192
|
skipFullBuild: false,
|
|
188
193
|
pullBundle: false,
|
|
189
194
|
remove: false,
|
|
195
|
+
test: false,
|
|
190
196
|
};
|
|
191
197
|
|
|
192
198
|
/**
|
|
@@ -210,7 +216,7 @@ class UnderpostRun {
|
|
|
210
216
|
* @method dev-cluster
|
|
211
217
|
* @description Resets and deploys a full development cluster including MongoDB, Valkey, exposes services, and updates `/etc/hosts` for local access.
|
|
212
218
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
213
|
-
* @param {
|
|
219
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
214
220
|
* @memberof UnderpostRun
|
|
215
221
|
*/
|
|
216
222
|
'dev-cluster': (path, options = DEFAULT_OPTION) => {
|
|
@@ -222,7 +228,7 @@ class UnderpostRun {
|
|
|
222
228
|
shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
|
|
223
229
|
|
|
224
230
|
shellExec(
|
|
225
|
-
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --
|
|
231
|
+
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --service-host ${mongoHosts.join(
|
|
226
232
|
',',
|
|
227
233
|
)} --pull-image`,
|
|
228
234
|
);
|
|
@@ -264,6 +270,16 @@ class UnderpostRun {
|
|
|
264
270
|
logger.info(hostListenResult.renderHosts);
|
|
265
271
|
},
|
|
266
272
|
|
|
273
|
+
/**
|
|
274
|
+
* @method etc-hosts
|
|
275
|
+
* @description Modifies the `/etc/hosts` file to add entries for local access to services,
|
|
276
|
+
* based on the provided path input.
|
|
277
|
+
* @param {string} path - The input value, identifier, or path for the operation (used to specify the entries to add to /etc/hosts).
|
|
278
|
+
*/
|
|
279
|
+
'etc-hosts': (path = '', options = DEFAULT_OPTION) => {
|
|
280
|
+
etcHostFactory(path.split(','));
|
|
281
|
+
},
|
|
282
|
+
|
|
267
283
|
/**
|
|
268
284
|
* @method ipfs-expose
|
|
269
285
|
* @description Exposes IPFS Cluster services on specified ports for local access.
|
|
@@ -282,7 +298,7 @@ class UnderpostRun {
|
|
|
282
298
|
* @method metadata
|
|
283
299
|
* @description Generates metadata for the specified path after exposing the development cluster.
|
|
284
300
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
285
|
-
* @param {
|
|
301
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
286
302
|
* @memberof UnderpostRun
|
|
287
303
|
*/
|
|
288
304
|
metadata: async (path, options = DEFAULT_OPTION) => {
|
|
@@ -307,7 +323,7 @@ class UnderpostRun {
|
|
|
307
323
|
* @method svc-ls
|
|
308
324
|
* @description Lists systemd services and installed packages, optionally filtering by the provided path.
|
|
309
325
|
* @param {string} path - The input value, identifier, or path for the operation (used as the optional filter for services and packages).
|
|
310
|
-
* @param {
|
|
326
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
311
327
|
* @memberof UnderpostRun
|
|
312
328
|
*/
|
|
313
329
|
'svc-ls': (path, options = DEFAULT_OPTION) => {
|
|
@@ -327,7 +343,7 @@ class UnderpostRun {
|
|
|
327
343
|
* @method svc-rm
|
|
328
344
|
* @description Removes a systemd service by stopping it, disabling it, uninstalling the package, and deleting related files.
|
|
329
345
|
* @param {string} path - The input value, identifier, or path for the operation (used as the service name).
|
|
330
|
-
* @param {
|
|
346
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
331
347
|
* @memberof UnderpostRun
|
|
332
348
|
*/
|
|
333
349
|
'svc-rm': (path, options = DEFAULT_OPTION) => {
|
|
@@ -342,7 +358,7 @@ class UnderpostRun {
|
|
|
342
358
|
* @method ssh-deploy-info
|
|
343
359
|
* @description Retrieves deployment status and pod information from a remote server via SSH.
|
|
344
360
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
345
|
-
* @param {
|
|
361
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
346
362
|
* @memberof UnderpostRun
|
|
347
363
|
*/
|
|
348
364
|
'ssh-deploy-info': async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -360,11 +376,233 @@ class UnderpostRun {
|
|
|
360
376
|
);
|
|
361
377
|
},
|
|
362
378
|
|
|
379
|
+
/**
|
|
380
|
+
* @method node-move
|
|
381
|
+
* @description Abstract runner that relocates any schedulable Kubernetes workload
|
|
382
|
+
* (Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob, ReplicationController)
|
|
383
|
+
* onto a target node by patching its pod-template `nodeSelector` and rolling it out.
|
|
384
|
+
* Resource-kind agnostic: it resolves the kind dynamically and applies the right
|
|
385
|
+
* patch path, so it works for `sts`, `deployment`, etc. without bespoke logic.
|
|
386
|
+
*
|
|
387
|
+
* Selection grammar via `path`:
|
|
388
|
+
* - `<kind>/<name>` -> a single resource (e.g. `deployment/dd-core-production-blue`)
|
|
389
|
+
* - `<kind>` -> every resource of that kind in the namespace (e.g. `statefulset`)
|
|
390
|
+
* - `` -> all movable workloads (deployment, statefulset, daemonset) in the namespace
|
|
391
|
+
*
|
|
392
|
+
* Placement:
|
|
393
|
+
* - default: built-in `kubernetes.io/hostname=<node>` (no node mutation required)
|
|
394
|
+
* - `--labels k=v,...`: label the target node with those pairs and use them as the
|
|
395
|
+
* nodeSelector (matches the "label node + nodeSelector" pattern), enabling reusable
|
|
396
|
+
* workload pools instead of pinning to a single hostname.
|
|
397
|
+
*
|
|
398
|
+
* Flags: `--node-name <node>` (target), `--namespace <ns>`, `--dry-run` (preview only),
|
|
399
|
+
* `--remove` (clear the nodeSelector / unpin placement).
|
|
400
|
+
*
|
|
401
|
+
* Caveats: Services/ConfigMaps and bare Pods are not schedulable controllers and are
|
|
402
|
+
* skipped (move the owning controller). StatefulSets bound to node-local PVs may stay
|
|
403
|
+
* Pending after a move until their volume is available on the target node.
|
|
404
|
+
* @param {string} path - Resource selector (`kind/name`, `kind`, or empty).
|
|
405
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
406
|
+
* @memberof UnderpostRun
|
|
407
|
+
* @returns {Array<{ref:string,kind:string,status:string,node?:string}>} Per-resource outcome.
|
|
408
|
+
*/
|
|
409
|
+
'node-move': (path = '', options = DEFAULT_OPTION) => {
|
|
410
|
+
const node = options.nodeName;
|
|
411
|
+
const ns = options.namespace || 'default';
|
|
412
|
+
const dryRun = options.dryRun === true;
|
|
413
|
+
const remove = options.remove === true;
|
|
414
|
+
|
|
415
|
+
if (!remove && !node) {
|
|
416
|
+
throw new Error('node-move requires --node-name <target-node> (or --remove to clear placement)');
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const normalizeKind = (k) =>
|
|
420
|
+
({
|
|
421
|
+
deploy: 'deployment',
|
|
422
|
+
deployments: 'deployment',
|
|
423
|
+
deployment: 'deployment',
|
|
424
|
+
sts: 'statefulset',
|
|
425
|
+
statefulsets: 'statefulset',
|
|
426
|
+
statefulset: 'statefulset',
|
|
427
|
+
ds: 'daemonset',
|
|
428
|
+
daemonsets: 'daemonset',
|
|
429
|
+
daemonset: 'daemonset',
|
|
430
|
+
rs: 'replicaset',
|
|
431
|
+
replicasets: 'replicaset',
|
|
432
|
+
replicaset: 'replicaset',
|
|
433
|
+
rc: 'replicationcontroller',
|
|
434
|
+
replicationcontroller: 'replicationcontroller',
|
|
435
|
+
job: 'job',
|
|
436
|
+
jobs: 'job',
|
|
437
|
+
cj: 'cronjob',
|
|
438
|
+
cronjob: 'cronjob',
|
|
439
|
+
cronjobs: 'cronjob',
|
|
440
|
+
po: 'pod',
|
|
441
|
+
pod: 'pod',
|
|
442
|
+
pods: 'pod',
|
|
443
|
+
svc: 'service',
|
|
444
|
+
service: 'service',
|
|
445
|
+
services: 'service',
|
|
446
|
+
})[k] || k;
|
|
447
|
+
|
|
448
|
+
// Kinds that own a pod template we can patch; rolloutKinds additionally
|
|
449
|
+
// support `kubectl rollout restart` to reschedule existing pods now.
|
|
450
|
+
const templated = [
|
|
451
|
+
'deployment',
|
|
452
|
+
'statefulset',
|
|
453
|
+
'daemonset',
|
|
454
|
+
'replicaset',
|
|
455
|
+
'job',
|
|
456
|
+
'cronjob',
|
|
457
|
+
'replicationcontroller',
|
|
458
|
+
];
|
|
459
|
+
const rolloutKinds = ['deployment', 'statefulset', 'daemonset'];
|
|
460
|
+
const templateSelectorPath = (kind) =>
|
|
461
|
+
kind === 'cronjob'
|
|
462
|
+
? ['spec', 'jobTemplate', 'spec', 'template', 'spec', 'nodeSelector']
|
|
463
|
+
: ['spec', 'template', 'spec', 'nodeSelector'];
|
|
464
|
+
|
|
465
|
+
// Resolve the desired nodeSelector. Custom --labels enables reusable pools;
|
|
466
|
+
// otherwise pin by the always-present hostname label.
|
|
467
|
+
let selector = { 'kubernetes.io/hostname': node };
|
|
468
|
+
if (!remove && options.labels) {
|
|
469
|
+
selector = {};
|
|
470
|
+
for (const pair of `${options.labels}`
|
|
471
|
+
.split(',')
|
|
472
|
+
.map((s) => s.trim())
|
|
473
|
+
.filter(Boolean)) {
|
|
474
|
+
const eq = pair.indexOf('=');
|
|
475
|
+
if (eq < 0) continue;
|
|
476
|
+
selector[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Verify the target node exists, and apply custom labels to it if provided.
|
|
481
|
+
if (!remove) {
|
|
482
|
+
const found = shellExec(`kubectl get node ${node} -o name`, {
|
|
483
|
+
silent: true,
|
|
484
|
+
stdout: true,
|
|
485
|
+
silentOnError: true,
|
|
486
|
+
}).trim();
|
|
487
|
+
if (!found) throw new Error(`Target node not found: ${node}`);
|
|
488
|
+
if (options.labels) {
|
|
489
|
+
const labelArgs = Object.entries(selector)
|
|
490
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
491
|
+
.join(' ');
|
|
492
|
+
const labelCmd = `kubectl label node ${node} ${labelArgs} --overwrite`;
|
|
493
|
+
if (dryRun) logger.info(`[dry-run] ${labelCmd}`);
|
|
494
|
+
else shellExec(labelCmd);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const kubectlNames = (kind) =>
|
|
499
|
+
(
|
|
500
|
+
shellExec(`kubectl get ${kind} -n ${ns} -o name`, {
|
|
501
|
+
silent: true,
|
|
502
|
+
stdout: true,
|
|
503
|
+
silentOnError: true,
|
|
504
|
+
}).trim() || ''
|
|
505
|
+
)
|
|
506
|
+
.split('\n')
|
|
507
|
+
.map((s) => s.trim())
|
|
508
|
+
.filter(Boolean);
|
|
509
|
+
|
|
510
|
+
// Build the list of "kind/name" targets from the selection grammar.
|
|
511
|
+
let targets = [];
|
|
512
|
+
if (!path) {
|
|
513
|
+
for (const kind of ['deployment', 'statefulset', 'daemonset']) targets.push(...kubectlNames(kind));
|
|
514
|
+
} else if (path.includes('/')) {
|
|
515
|
+
targets = [path];
|
|
516
|
+
} else {
|
|
517
|
+
targets = kubectlNames(path);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (targets.length === 0) {
|
|
521
|
+
logger.warn('node-move: no matching resources found', { path, namespace: ns });
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Merge-patch body that sets (or clears, when --remove) the nodeSelector.
|
|
526
|
+
const buildPatch = (kind) => {
|
|
527
|
+
const keys = templateSelectorPath(kind);
|
|
528
|
+
let obj = remove ? null : selector;
|
|
529
|
+
for (let i = keys.length - 1; i >= 0; i--) obj = { [keys[i]]: obj };
|
|
530
|
+
return JSON.stringify(obj);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const results = [];
|
|
534
|
+
for (const ref of targets) {
|
|
535
|
+
const slash = ref.indexOf('/');
|
|
536
|
+
const rawKind = slash >= 0 ? ref.slice(0, slash) : path && !path.includes('/') ? path : '';
|
|
537
|
+
const name = slash >= 0 ? ref.slice(slash + 1) : ref;
|
|
538
|
+
const kind = normalizeKind(`${rawKind}`.split('.')[0].toLowerCase());
|
|
539
|
+
|
|
540
|
+
if (!templated.includes(kind)) {
|
|
541
|
+
logger.warn(`node-move: ${kind}/${name} is not a schedulable controller; skipping`, {
|
|
542
|
+
hint: 'move its owning controller (deployment/statefulset/daemonset) instead',
|
|
543
|
+
});
|
|
544
|
+
results.push({ ref, kind, status: 'skipped' });
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Idempotency: skip the patch + rollout if the resource is already where
|
|
549
|
+
// we want it. Compares the live pod-template nodeSelector against the
|
|
550
|
+
// desired placement so a repeated run does not trigger an unnecessary
|
|
551
|
+
// rollout restart.
|
|
552
|
+
const basePath = kind === 'cronjob' ? 'spec.jobTemplate.spec.template.spec' : 'spec.template.spec';
|
|
553
|
+
const jsonpath = (expr) =>
|
|
554
|
+
shellExec(`kubectl get ${kind} ${name} -n ${ns} -o jsonpath='${expr}'`, {
|
|
555
|
+
silent: true,
|
|
556
|
+
stdout: true,
|
|
557
|
+
silentOnError: true,
|
|
558
|
+
disableLog: true,
|
|
559
|
+
}).trim();
|
|
560
|
+
|
|
561
|
+
if (remove) {
|
|
562
|
+
const current = jsonpath(`{.${basePath}.nodeSelector}`);
|
|
563
|
+
if (!current || current === 'map[]') {
|
|
564
|
+
logger.info(`node-move: ${kind}/${name} already has no nodeSelector; nothing to clear`);
|
|
565
|
+
results.push({ ref, kind, status: 'already-cleared' });
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
const alreadyOnNode = Object.entries(selector).every(([k, v]) => {
|
|
570
|
+
const esc = k.replace(/\./g, '\\.');
|
|
571
|
+
return jsonpath(`{.${basePath}.nodeSelector.${esc}}`) === v;
|
|
572
|
+
});
|
|
573
|
+
if (alreadyOnNode) {
|
|
574
|
+
logger.info(`node-move: ${kind}/${name} already pinned to ${node}; skipping`, { namespace: ns });
|
|
575
|
+
results.push({ ref, kind, status: 'already-on-node', node });
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const patchCmd = `kubectl patch ${kind} ${name} -n ${ns} --type=merge -p '${buildPatch(kind)}'`;
|
|
581
|
+
const restartCmd = `kubectl rollout restart ${kind} ${name} -n ${ns}`;
|
|
582
|
+
if (dryRun) {
|
|
583
|
+
logger.info(`[dry-run] ${patchCmd}`);
|
|
584
|
+
if (rolloutKinds.includes(kind)) logger.info(`[dry-run] ${restartCmd}`);
|
|
585
|
+
results.push({ ref, kind, status: 'dry-run', node: remove ? undefined : node });
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
shellExec(patchCmd);
|
|
590
|
+
if (rolloutKinds.includes(kind)) shellExec(restartCmd);
|
|
591
|
+
logger.info(remove ? `Cleared node placement: ${kind}/${name}` : `Moved ${kind}/${name} -> ${node}`, {
|
|
592
|
+
namespace: ns,
|
|
593
|
+
});
|
|
594
|
+
results.push({ ref, kind, status: remove ? 'cleared' : 'moved', node: remove ? undefined : node });
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
logger.info('node-move complete', { namespace: ns, node: remove ? null : node, count: results.length });
|
|
598
|
+
return results;
|
|
599
|
+
},
|
|
600
|
+
|
|
363
601
|
/**
|
|
364
602
|
* @method dev-hosts-expose
|
|
365
603
|
* @description Deploys a specified service in development mode with `/etc/hosts` modification for local access.
|
|
366
604
|
* @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID to deploy).
|
|
367
|
-
* @param {
|
|
605
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
368
606
|
* @memberof UnderpostRun
|
|
369
607
|
*/
|
|
370
608
|
'dev-hosts-expose': (path, options = DEFAULT_OPTION) => {
|
|
@@ -377,7 +615,7 @@ class UnderpostRun {
|
|
|
377
615
|
* @method dev-hosts-restore
|
|
378
616
|
* @description Restores the `/etc/hosts` file to its original state after modifications made during development deployments.
|
|
379
617
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
380
|
-
* @param {
|
|
618
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
381
619
|
* @memberof UnderpostRun
|
|
382
620
|
*/
|
|
383
621
|
'dev-hosts-restore': (path, options = DEFAULT_OPTION) => {
|
|
@@ -388,11 +626,13 @@ class UnderpostRun {
|
|
|
388
626
|
* @method cluster-build
|
|
389
627
|
* @description Build configuration for cluster deployment.
|
|
390
628
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
391
|
-
* @param {
|
|
629
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
392
630
|
* @memberof UnderpostRun
|
|
393
631
|
*/
|
|
394
632
|
'cluster-build': (path, options = DEFAULT_OPTION) => {
|
|
395
|
-
const nodeOptions =
|
|
633
|
+
const nodeOptions =
|
|
634
|
+
(options.nodeName ? ` --node-name ${options.nodeName}` : '') +
|
|
635
|
+
(options.sshKeyPath ? ` --ssh-key-path ${options.sshKeyPath}` : '');
|
|
396
636
|
shellExec(`node bin run clean`);
|
|
397
637
|
shellExec(`node bin run --dev sync-replica template-deploy${nodeOptions}`);
|
|
398
638
|
shellExec(`node bin run sync-replica template-deploy${nodeOptions}`);
|
|
@@ -410,7 +650,7 @@ class UnderpostRun {
|
|
|
410
650
|
* and optionally triggers engine-<conf-id> CI with sync/init which in turn dispatches the CD workflow
|
|
411
651
|
* after the build chain completes (template → ghpkg → engine-<conf-id> → CD).
|
|
412
652
|
* @param {string} path - The deployment path identifier (e.g., 'sync-engine-core', 'init-engine-core', or empty for build-only).
|
|
413
|
-
* @param {
|
|
653
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
414
654
|
* @memberof UnderpostRun
|
|
415
655
|
*/
|
|
416
656
|
'template-deploy': (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -422,14 +662,12 @@ class UnderpostRun {
|
|
|
422
662
|
return;
|
|
423
663
|
}
|
|
424
664
|
shellExec(`${baseCommand} run pull`);
|
|
665
|
+
shellExec(`${baseCommand} run shared-dir`);
|
|
425
666
|
|
|
426
|
-
// Capture last N commit
|
|
427
|
-
//
|
|
428
|
-
const fromN =
|
|
429
|
-
|
|
430
|
-
? parseInt(options.fromNCommit)
|
|
431
|
-
: Underpost.repo.getUnpushedCount('.').count;
|
|
432
|
-
const message = shellExec(`node bin cmt --changelog ${fromN} --changelog-no-hash`, {
|
|
667
|
+
// Capture the sanitized message from the last N commits (--from-n-commit, default 1) for
|
|
668
|
+
// propagation to pwa-microservices-template and every engine-* repo.
|
|
669
|
+
const fromN = parseInt(options.fromNCommit) > 0 ? parseInt(options.fromNCommit) : 1;
|
|
670
|
+
const sanitizedMessage = shellExec(`node bin cmt --changelog-msg --from-n-commit ${fromN} --changelog-no-hash`, {
|
|
433
671
|
silent: true,
|
|
434
672
|
stdout: true,
|
|
435
673
|
}).trim();
|
|
@@ -441,8 +679,6 @@ class UnderpostRun {
|
|
|
441
679
|
);
|
|
442
680
|
shellCd('/home/dd/engine');
|
|
443
681
|
|
|
444
|
-
const sanitizedMessage = Underpost.repo.sanitizeChangelogMessage(message);
|
|
445
|
-
|
|
446
682
|
// Push engine repo so workflow YAML changes reach GitHub
|
|
447
683
|
shellExec(`git reset`);
|
|
448
684
|
shellExec(`${baseCommand} push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`);
|
|
@@ -490,7 +726,7 @@ class UnderpostRun {
|
|
|
490
726
|
* @method template-deploy-local
|
|
491
727
|
* @description Similar to `template-deploy` but runs the workflow locally without dispatching GitHub Actions. It pulls the latest changes, pushes to GitHub, builds the template, and optionally triggers a local release with CI push.
|
|
492
728
|
* @param {string} path - The deployment path identifier (e.g., 'sync-engine-core', 'init-engine-core', or empty for build-only).
|
|
493
|
-
* @param {
|
|
729
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
494
730
|
* @memberof UnderpostRun
|
|
495
731
|
*/
|
|
496
732
|
'template-deploy-local': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -502,18 +738,14 @@ class UnderpostRun {
|
|
|
502
738
|
return;
|
|
503
739
|
}
|
|
504
740
|
shellExec(`${baseCommand} run pull`);
|
|
741
|
+
shellExec(`${baseCommand} run shared-dir`);
|
|
505
742
|
|
|
506
|
-
// Capture last N
|
|
507
|
-
|
|
508
|
-
const fromN
|
|
509
|
-
options.fromNCommit && parseInt(options.fromNCommit) > 0
|
|
510
|
-
? parseInt(options.fromNCommit)
|
|
511
|
-
: Underpost.repo.getUnpushedCount('.').count;
|
|
512
|
-
const rawMessage = shellExec(`node bin cmt --changelog ${fromN} --changelog-no-hash`, {
|
|
743
|
+
// Capture the sanitized message from the last N commits (--from-n-commit, default 1).
|
|
744
|
+
const fromN = parseInt(options.fromNCommit) > 0 ? parseInt(options.fromNCommit) : 1;
|
|
745
|
+
const sanitizedMessage = shellExec(`node bin cmt --changelog-msg --from-n-commit ${fromN} --changelog-no-hash`, {
|
|
513
746
|
silent: true,
|
|
514
747
|
stdout: true,
|
|
515
748
|
}).trim();
|
|
516
|
-
const sanitizedMessage = Underpost.repo.sanitizeChangelogMessage(rawMessage);
|
|
517
749
|
|
|
518
750
|
const { triggerCmd } = path
|
|
519
751
|
? await Underpost.release.ci(path, sanitizedMessage, options)
|
|
@@ -525,15 +757,14 @@ class UnderpostRun {
|
|
|
525
757
|
* @description Dispatches the Docker image CI workflow (`docker-image[.<runtime>].ci.yml`) via `workflow_dispatch`.
|
|
526
758
|
* Repository resolution is delegated to `Underpost.repo.resolveInstanceRepo(path)`.
|
|
527
759
|
* @param {string} path - Optional runtime / workflow suffix (e.g. `cyberia-server`, `cyberia-client`).
|
|
528
|
-
* @param {
|
|
760
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
529
761
|
* @memberof UnderpostRun
|
|
530
762
|
*/
|
|
531
763
|
'docker-image': (path, options = DEFAULT_OPTION) => {
|
|
532
|
-
const repo = Underpost.repo.resolveInstanceRepo(path);
|
|
764
|
+
const repo = Underpost.repo.resolveInstanceRepo(path, options.dev);
|
|
533
765
|
Underpost.repo.dispatchWorkflow({
|
|
534
766
|
repo,
|
|
535
|
-
workflowFile: `docker-image${path ? `.${path}` : ''}.ci.yml`,
|
|
536
|
-
ref: 'master',
|
|
767
|
+
workflowFile: `docker-image${path ? `.${path}` : ''}${options.dev ? '.dev' : ''}.ci.yml`,
|
|
537
768
|
inputs: {},
|
|
538
769
|
});
|
|
539
770
|
},
|
|
@@ -541,7 +772,7 @@ class UnderpostRun {
|
|
|
541
772
|
* @method clean
|
|
542
773
|
* @description Changes directory to the provided path (defaulting to `/home/dd/engine`) and runs `node bin/deploy clean-core-repo`.
|
|
543
774
|
* @param {string} path - The input value, identifier, or path for the operation (used as the optional directory path).
|
|
544
|
-
* @param {
|
|
775
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
545
776
|
* @memberof UnderpostRun
|
|
546
777
|
*/
|
|
547
778
|
clean: (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -552,7 +783,7 @@ class UnderpostRun {
|
|
|
552
783
|
* @method pull
|
|
553
784
|
* @description Clones or pulls updates for the `engine` and `engine-private` repositories into `/home/dd/engine` and `/home/dd/engine/engine-private`.
|
|
554
785
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
555
|
-
* @param {
|
|
786
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
556
787
|
* @memberof UnderpostRun
|
|
557
788
|
*/
|
|
558
789
|
pull: (path, options = DEFAULT_OPTION) => {
|
|
@@ -579,7 +810,7 @@ class UnderpostRun {
|
|
|
579
810
|
* @method release-deploy
|
|
580
811
|
* @description Executes deployment (`underpost run deploy`) for all deployment IDs listed in `./engine-private/deploy/dd.router`.
|
|
581
812
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
582
|
-
* @param {
|
|
813
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
583
814
|
* @memberof UnderpostRun
|
|
584
815
|
*/
|
|
585
816
|
'release-deploy': (path, options = DEFAULT_OPTION) => {
|
|
@@ -595,7 +826,7 @@ class UnderpostRun {
|
|
|
595
826
|
* @method ssh-deploy
|
|
596
827
|
* @description Dispatches the corresponding CD workflow for SSH-based deployment, replacing empty commits with workflow_dispatch.
|
|
597
828
|
* @param {string} path - The deployment identifier (e.g., 'engine-core', 'sync-engine-core', 'init-engine-core').
|
|
598
|
-
* @param {
|
|
829
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
599
830
|
* @memberof UnderpostRun
|
|
600
831
|
*/
|
|
601
832
|
'ssh-deploy': (path, options = DEFAULT_OPTION) => {
|
|
@@ -610,9 +841,9 @@ class UnderpostRun {
|
|
|
610
841
|
job = 'init';
|
|
611
842
|
confId = path.replace(/^init-/, '');
|
|
612
843
|
}
|
|
613
|
-
|
|
844
|
+
const repo = Underpost.repo.resolveInstanceRepo(confId, options.dev);
|
|
614
845
|
Underpost.repo.dispatchWorkflow({
|
|
615
|
-
repo
|
|
846
|
+
repo,
|
|
616
847
|
workflowFile: `${confId}.cd.yml`,
|
|
617
848
|
ref: 'master',
|
|
618
849
|
inputs: { job },
|
|
@@ -623,7 +854,7 @@ class UnderpostRun {
|
|
|
623
854
|
* @description Opens a Visual Studio Code (VS Code) session for the specified path using `node ${underpostRoot}/bin/zed ${path}`,
|
|
624
855
|
* or installs Zed and sublime-text IDE if `path` is 'install'.
|
|
625
856
|
* @param {string} path - The input value, identifier, or path for the operation (used as the path to the directory to open in the IDE).
|
|
626
|
-
* @param {
|
|
857
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
627
858
|
* @memberof UnderpostRun
|
|
628
859
|
*/
|
|
629
860
|
ide: (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -650,7 +881,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
650
881
|
* @method crypto-policy
|
|
651
882
|
* @description Sets the system's crypto policies to `DEFAULT:SHA1` using `update-crypto-policies` command.
|
|
652
883
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
653
|
-
* @param {
|
|
884
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
654
885
|
* @memberof UnderpostRun
|
|
655
886
|
*/
|
|
656
887
|
'crypto-policy': (path, options = DEFAULT_OPTION) => {
|
|
@@ -665,7 +896,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
665
896
|
* `deployment.yaml`. Useful when you want to force `Always` so the kubelet re-pulls a mutable tag on every rollout. Example:
|
|
666
897
|
* `node bin run sync dd-core --kubeadm --image-pull-policy Always`
|
|
667
898
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string containing deploy parameters).
|
|
668
|
-
* @param {
|
|
899
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
669
900
|
* @memberof UnderpostRun
|
|
670
901
|
*/
|
|
671
902
|
sync: async (path, options = DEFAULT_OPTION) => {
|
|
@@ -686,7 +917,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
686
917
|
replicas = replicas ? replicas : defaultPath[1];
|
|
687
918
|
versions = versions ? versions.replaceAll('+', ',') : defaultPath[2];
|
|
688
919
|
image = image ? image : defaultPath[3];
|
|
689
|
-
node = node ? node : defaultPath[4];
|
|
920
|
+
node = node ? node : options.nodeName ? options.nodeName : defaultPath[4];
|
|
690
921
|
shellExec(`${baseCommand} cluster --ns-use ${options.namespace}`);
|
|
691
922
|
|
|
692
923
|
if (image && !image.startsWith('localhost'))
|
|
@@ -713,6 +944,11 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
713
944
|
let targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'green';
|
|
714
945
|
if (targetTraffic) versions = versions ? versions : targetTraffic;
|
|
715
946
|
|
|
947
|
+
const ignorePods =
|
|
948
|
+
isDeployRunnerContext(path, options) && targetTraffic
|
|
949
|
+
? Underpost.kubectl.get(`${deployId}-${env}-${targetTraffic}`, 'pods', options.namespace).map((p) => p.NAME)
|
|
950
|
+
: [];
|
|
951
|
+
|
|
716
952
|
const timeoutFlags = Underpost.deploy.timeoutFlagsFactory(options);
|
|
717
953
|
const cmdString = options.cmd
|
|
718
954
|
? ' --cmd ' + (options.cmd.find((c) => c.match('"')) ? '"' + options.cmd + '"' : "'" + options.cmd + "'")
|
|
@@ -722,13 +958,14 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
722
958
|
const skipFullBuildFlag = options.skipFullBuild ? ' --skip-full-build' : '';
|
|
723
959
|
const pullBundleFlag = options.pullBundle ? ' --pull-bundle' : '';
|
|
724
960
|
const imagePullPolicyFlag = options.imagePullPolicy ? ` --image-pull-policy ${options.imagePullPolicy}` : '';
|
|
961
|
+
const sshKeyPathFlag = options.sshKeyPath ? ` --ssh-key-path ${options.sshKeyPath}` : '';
|
|
725
962
|
|
|
726
963
|
shellExec(
|
|
727
964
|
`${baseCommand} deploy${clusterFlag} --build-manifest --sync --info-router --replicas ${replicas} --node ${node}${
|
|
728
965
|
image ? ` --image ${image}` : ''
|
|
729
966
|
}${versions ? ` --versions ${versions}` : ''}${
|
|
730
967
|
options.namespace ? ` --namespace ${options.namespace}` : ''
|
|
731
|
-
}${timeoutFlags}${cmdString}${gitCleanFlag}${skipFullBuildFlag}${pullBundleFlag}${imagePullPolicyFlag} ${deployId} ${env}`,
|
|
968
|
+
}${timeoutFlags}${cmdString}${gitCleanFlag}${skipFullBuildFlag}${pullBundleFlag}${imagePullPolicyFlag}${sshKeyPathFlag} ${deployId} ${env}`,
|
|
732
969
|
);
|
|
733
970
|
|
|
734
971
|
if (isDeployRunnerContext(path, options)) {
|
|
@@ -737,13 +974,13 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
737
974
|
`${baseCommand} db ${deployId} ${clusterFlag}${baseClusterCommand} --repo-backup --primary-pod --git --force-clone --preserveUUID ${options.namespace ? ` --ns ${options.namespace}` : ''}`,
|
|
738
975
|
);
|
|
739
976
|
shellExec(
|
|
740
|
-
`${baseCommand} deploy${clusterFlag}${cmdString} --replicas ${replicas} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
|
|
977
|
+
`${baseCommand} deploy${clusterFlag}${cmdString} --replicas ${replicas} --node ${node} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
|
|
741
978
|
options.namespace ? ` --namespace ${options.namespace}` : ''
|
|
742
|
-
}${timeoutFlags}${gitCleanFlag}${imagePullPolicyFlag}`,
|
|
979
|
+
}${timeoutFlags}${gitCleanFlag}${imagePullPolicyFlag}${sshKeyPathFlag}`,
|
|
743
980
|
);
|
|
744
981
|
if (!targetTraffic)
|
|
745
982
|
targetTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
|
|
746
|
-
await Underpost.
|
|
983
|
+
await Underpost.monitor.monitorReadyRunner(deployId, env, targetTraffic, ignorePods, options.namespace);
|
|
747
984
|
Underpost.deploy.switchTraffic(deployId, env, targetTraffic, replicas, options.namespace, options);
|
|
748
985
|
} else
|
|
749
986
|
logger.info('current traffic', Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }));
|
|
@@ -753,7 +990,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
753
990
|
* @method stop
|
|
754
991
|
* @description Stops a deployment by deleting the corresponding Kubernetes deployment and service resources.
|
|
755
992
|
* @param {string} path - The input value, identifier, or path for the operation (used to determine which traffic to stop).
|
|
756
|
-
* @param {
|
|
993
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
757
994
|
* @memberof UnderpostRun
|
|
758
995
|
*/
|
|
759
996
|
stop: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -777,7 +1014,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
777
1014
|
* @method ssh-deploy-stop
|
|
778
1015
|
* @description Stops a remote deployment via SSH by executing the appropriate Underpost command on the remote server.
|
|
779
1016
|
* @param {string} path - The input value, identifier, or path for the operation (used to determine which traffic to stop).
|
|
780
|
-
* @param {
|
|
1017
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
781
1018
|
* @memberof UnderpostRun
|
|
782
1019
|
*/
|
|
783
1020
|
'ssh-deploy-stop': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -804,7 +1041,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
804
1041
|
* @method ssh-deploy-db-rollback
|
|
805
1042
|
* @description Performs a database rollback on remote deployment via SSH.
|
|
806
1043
|
* @param {string} path - Comma-separated deployId and optional number of commits to reset (format: "deployId,nCommits")
|
|
807
|
-
* @param {
|
|
1044
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
808
1045
|
* @param {string} options.deployId - The deployment identifier
|
|
809
1046
|
* @param {string} options.user - The SSH user for credential lookup
|
|
810
1047
|
* @param {boolean} options.dev - Development mode flag
|
|
@@ -831,7 +1068,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
831
1068
|
* @method ssh-deploy-db
|
|
832
1069
|
* @description Imports/restores a database on remote deployment via SSH.
|
|
833
1070
|
* @param {string} path - The deployment ID for database import
|
|
834
|
-
* @param {
|
|
1071
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
835
1072
|
* @param {string} options.deployId - The deployment identifier
|
|
836
1073
|
* @param {string} options.user - The SSH user for credential lookup
|
|
837
1074
|
* @param {boolean} options.dev - Development mode flag
|
|
@@ -856,7 +1093,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
856
1093
|
* @method ssh-deploy-db-status
|
|
857
1094
|
* @description Retrieves database status/stats for a deployment (or all deployments from dd.router) via SSH.
|
|
858
1095
|
* @param {string} path - Comma-separated deployId(s) or 'dd' to use the dd.router list.
|
|
859
|
-
* @param {
|
|
1096
|
+
* @param {UnderpostRunDefaultOptions} options - Runner options (uses options.deployId for SSH host lookup).
|
|
860
1097
|
* @param {string} options.deployId - Deployment identifier used for SSH config lookup.
|
|
861
1098
|
* @param {string} options.user - SSH user for credential lookup.
|
|
862
1099
|
* @param {boolean} options.dev - Development mode flag.
|
|
@@ -902,7 +1139,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
902
1139
|
* @method tz
|
|
903
1140
|
* @description Sets the system timezone using `timedatectl set-timezone` command.
|
|
904
1141
|
* @param {string} path - The input value, identifier, or path for the operation (used as the timezone string).
|
|
905
|
-
* @param {
|
|
1142
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
906
1143
|
* @memberof UnderpostRun
|
|
907
1144
|
*/
|
|
908
1145
|
tz: (path, options = DEFAULT_OPTION) => {
|
|
@@ -923,7 +1160,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
923
1160
|
* @method get-proxy
|
|
924
1161
|
* @description Retrieves and logs the HTTPProxy resources in the specified namespace using `kubectl get HTTPProxy`.
|
|
925
1162
|
* @param {string} path - The input value, identifier, or path for the operation (used as an optional filter for the HTTPProxy resources).
|
|
926
|
-
* @param {
|
|
1163
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
927
1164
|
* @memberof UnderpostRun
|
|
928
1165
|
*/
|
|
929
1166
|
'get-proxy': async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -989,8 +1226,19 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
989
1226
|
// pathRewritePolicy,
|
|
990
1227
|
});
|
|
991
1228
|
if (options.tls) {
|
|
992
|
-
|
|
993
|
-
|
|
1229
|
+
if (options.test) {
|
|
1230
|
+
const sslDir = `./engine-private/ssl/${_host}`;
|
|
1231
|
+
const nameSafe = _host.replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
1232
|
+
fs.mkdirpSync(sslDir);
|
|
1233
|
+
shellExec(`bash ./scripts/ssl.sh "${sslDir}" "${_host}"`);
|
|
1234
|
+
shellExec(`kubectl delete secret ${_host} -n ${options.namespace} --ignore-not-found`);
|
|
1235
|
+
shellExec(
|
|
1236
|
+
`kubectl create secret tls ${_host} --cert="${sslDir}/${nameSafe}.pem" --key="${sslDir}/${nameSafe}-key.pem" -n ${options.namespace}`,
|
|
1237
|
+
);
|
|
1238
|
+
} else {
|
|
1239
|
+
shellExec(`sudo kubectl delete Certificate ${_host} -n ${options.namespace} --ignore-not-found`);
|
|
1240
|
+
proxyYaml += Underpost.deploy.buildCertManagerCertificate({ ...options, host: _host });
|
|
1241
|
+
}
|
|
994
1242
|
}
|
|
995
1243
|
// console.log(proxyYaml);
|
|
996
1244
|
shellExec(`kubectl delete HTTPProxy ${_host} --namespace ${options.namespace} --ignore-not-found`);
|
|
@@ -1019,7 +1267,7 @@ EOF
|
|
|
1019
1267
|
/**
|
|
1020
1268
|
* @method instance
|
|
1021
1269
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string containing workflow parameters).
|
|
1022
|
-
* @param {
|
|
1270
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1023
1271
|
* @memberof UnderpostRun
|
|
1024
1272
|
*/
|
|
1025
1273
|
instance: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1059,6 +1307,7 @@ EOF
|
|
|
1059
1307
|
// Examples images:
|
|
1060
1308
|
// `underpost/underpost-engine:${Underpost.version}`
|
|
1061
1309
|
// `localhost/rockylinux9-underpost:${Underpost.version}`
|
|
1310
|
+
if (options.imageName) _image = options.imageName;
|
|
1062
1311
|
if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
|
|
1063
1312
|
|
|
1064
1313
|
if (_image && !_image.startsWith('localhost'))
|
|
@@ -1087,9 +1336,16 @@ EOF
|
|
|
1087
1336
|
deployId: _deployId,
|
|
1088
1337
|
env,
|
|
1089
1338
|
version: targetTraffic,
|
|
1090
|
-
nodeName:
|
|
1339
|
+
nodeName: Underpost.deploy.resolveDeployNode({
|
|
1340
|
+
node: options.nodeName,
|
|
1341
|
+
kind: options.kind,
|
|
1342
|
+
kubeadm: options.kubeadm,
|
|
1343
|
+
k3s: options.k3s,
|
|
1344
|
+
env,
|
|
1345
|
+
}),
|
|
1091
1346
|
clusterContext: options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind',
|
|
1092
1347
|
gitClean: options.gitClean || false,
|
|
1348
|
+
sshKeyPath: options.sshKeyPath || '',
|
|
1093
1349
|
});
|
|
1094
1350
|
// Regenerate the parent deploy's gRPC ClusterIP service pointing to the
|
|
1095
1351
|
// parent's current traffic colour and apply it before the instance pod starts so
|
|
@@ -1154,12 +1410,16 @@ EOF
|
|
|
1154
1410
|
`,
|
|
1155
1411
|
{ disableLog: true },
|
|
1156
1412
|
);
|
|
1157
|
-
|
|
1413
|
+
// Custom instances run a bare binary (no `underpost start` / internal
|
|
1414
|
+
// HTTP endpoint): Kubernetes readiness is the running signal and
|
|
1415
|
+
// container-status is read via exec. See `Deploy custom instance to K8S.md`.
|
|
1416
|
+
const { ready, readyPods } = await Underpost.monitor.monitorReadyRunner(
|
|
1158
1417
|
_deployId,
|
|
1159
1418
|
env,
|
|
1160
1419
|
targetTraffic,
|
|
1161
1420
|
ignorePods,
|
|
1162
1421
|
options.namespace,
|
|
1422
|
+
{ readyGate: 'kubernetes', statusTransport: 'exec' },
|
|
1163
1423
|
);
|
|
1164
1424
|
|
|
1165
1425
|
if (!ready) {
|
|
@@ -1169,7 +1429,7 @@ EOF
|
|
|
1169
1429
|
shellExec(
|
|
1170
1430
|
`${baseCommand} run${baseClusterCommand} --namespace ${options.namespace}` +
|
|
1171
1431
|
`${options.nodeName ? ` --node-name ${options.nodeName}` : ''}` +
|
|
1172
|
-
`${options.tls ? ` --tls` : ''}` +
|
|
1432
|
+
`${options.tls ? ` --tls ${options.test ? '--test' : ''}` : ''}` +
|
|
1173
1433
|
` instance-promote '${path}'`,
|
|
1174
1434
|
);
|
|
1175
1435
|
}
|
|
@@ -1179,12 +1439,39 @@ EOF
|
|
|
1179
1439
|
}
|
|
1180
1440
|
},
|
|
1181
1441
|
|
|
1442
|
+
/**
|
|
1443
|
+
* @method deploy-key
|
|
1444
|
+
* @description Copies the deploy key for a specific user and deployId to a temporary location on the local machine.
|
|
1445
|
+
* @param {string} path - The input value, identifier, or path for the operation (not used in this method).
|
|
1446
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1447
|
+
* @param {string} options.user - The user for which to copy the deploy key.
|
|
1448
|
+
* @param {string} options.deployId - The deployment identifier associated with the deploy key.
|
|
1449
|
+
* @memberof UnderpostRun
|
|
1450
|
+
*/
|
|
1451
|
+
'deploy-key': (path, options = DEFAULT_OPTION) => {
|
|
1452
|
+
const prefix = 'dd-key';
|
|
1453
|
+
if (options.reset) {
|
|
1454
|
+
shellExec(`rm -rf /home/dd/tmp/${prefix}_*`);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (!options.user || !options.deployId) {
|
|
1458
|
+
logger.error('Both --user and --deploy-id options are required to copy the deploy key.');
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const targetPath = `/home/dd/tmp/${prefix}_${s4()}${s4()}`;
|
|
1462
|
+
fs.mkdirSync('/home/dd/tmp', { recursive: true });
|
|
1463
|
+
fs.copyFileSync(`./engine-private/conf/${options.deployId}/users/${options.user}/id_rsa`, targetPath);
|
|
1464
|
+
logger.info(`Copied deploy key to ${targetPath}`);
|
|
1465
|
+
if (options.copy) pbcopy(targetPath);
|
|
1466
|
+
},
|
|
1467
|
+
|
|
1182
1468
|
/**
|
|
1183
1469
|
* @method instance-build-manifest
|
|
1184
1470
|
* @description Builds a Kubernetes Deployment + Service manifest for a specific instance entry
|
|
1185
|
-
* from `conf.instances.json` and writes it to a file.
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1471
|
+
* from `conf.instances.json` and writes it to a file. This is a purely local
|
|
1472
|
+
* artifact generator: it never probes a live cluster. Traffic colour defaults
|
|
1473
|
+
* to the canonical initial `blue` and can be overridden with `--traffic`; the
|
|
1474
|
+
* real blue/green swap is resolved at deploy time (`deploy --sync`).
|
|
1188
1475
|
*
|
|
1189
1476
|
* If `--build` is supplied the image is built from the project Dockerfile and loaded into the
|
|
1190
1477
|
* cluster before the manifest is written (kind by default; `--kubeadm` / `--k3s` override).
|
|
@@ -1195,7 +1482,7 @@ EOF
|
|
|
1195
1482
|
* `<projectPath>/manifests/<env>/deployment.yaml`.
|
|
1196
1483
|
* In production, files are also copied to `<projectPath>/Dockerfile` and
|
|
1197
1484
|
* `<projectPath>/deployment.yaml`.
|
|
1198
|
-
* @param {
|
|
1485
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1199
1486
|
* @memberof UnderpostRun
|
|
1200
1487
|
*/
|
|
1201
1488
|
'instance-build-manifest': (path, options = DEFAULT_OPTION) => {
|
|
@@ -1274,7 +1561,7 @@ EOF
|
|
|
1274
1561
|
path: projectPath,
|
|
1275
1562
|
imageName: _image,
|
|
1276
1563
|
podmanSave: true,
|
|
1277
|
-
|
|
1564
|
+
imageOutPath: projectPath,
|
|
1278
1565
|
kind: isKind,
|
|
1279
1566
|
kubeadm: !!options.kubeadm,
|
|
1280
1567
|
k3s: !!options.k3s,
|
|
@@ -1287,15 +1574,8 @@ EOF
|
|
|
1287
1574
|
});
|
|
1288
1575
|
}
|
|
1289
1576
|
|
|
1290
|
-
|
|
1291
|
-
const
|
|
1292
|
-
hostTest: _host,
|
|
1293
|
-
namespace: options.namespace,
|
|
1294
|
-
});
|
|
1295
|
-
const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
|
|
1296
|
-
|
|
1297
|
-
// Resolve {{grpc-service-dns}} using the parent deploy's current (or default) traffic.
|
|
1298
|
-
const parentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
|
|
1577
|
+
const targetTraffic = options.traffic || 'blue';
|
|
1578
|
+
const parentTraffic = targetTraffic;
|
|
1299
1579
|
const resolvedCmd = _cmd[env].map((c) =>
|
|
1300
1580
|
c.replaceAll(
|
|
1301
1581
|
'{{grpc-service-dns}}',
|
|
@@ -1343,11 +1623,86 @@ EOF
|
|
|
1343
1623
|
image: _image,
|
|
1344
1624
|
});
|
|
1345
1625
|
|
|
1626
|
+
// --- Sibling manifests (pv-pvc, proxy, grpc-service) ------------------
|
|
1627
|
+
// Emit the same apply-able set the parent deploy writes to build/<env>,
|
|
1628
|
+
// scoped to this instance so the project repo and engine-private ship
|
|
1629
|
+
// more than just deployment.yaml. Content matches what `run instance`
|
|
1630
|
+
// creates dynamically at deploy time (deployVolume / instance-promote /
|
|
1631
|
+
// the parent gRPC ClusterIP), so a static `kubectl apply` is equivalent.
|
|
1632
|
+
const pvDataNode = Underpost.deploy.resolveDeployNode({
|
|
1633
|
+
node: options.nodeName,
|
|
1634
|
+
kind: options.kind,
|
|
1635
|
+
kubeadm: options.kubeadm,
|
|
1636
|
+
k3s: options.k3s,
|
|
1637
|
+
env,
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
// pv-pvc.yaml — one PV+PVC per instance volume; names mirror deployVolume.
|
|
1641
|
+
let pvPvcYaml = '';
|
|
1642
|
+
for (const volume of _volumes || []) {
|
|
1643
|
+
if (!volume.claimName) continue;
|
|
1644
|
+
const pvcId = `${volume.claimName}-${_deployId}-${env}-${targetTraffic}`;
|
|
1645
|
+
const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${_deployId}-${env}-${targetTraffic}`;
|
|
1646
|
+
pvPvcYaml += `---\n${Underpost.deploy.persistentVolumeFactory({
|
|
1647
|
+
pvcId,
|
|
1648
|
+
namespace: options.namespace,
|
|
1649
|
+
hostPath: `/home/dd/engine/volume/${pvId}`,
|
|
1650
|
+
nodeName: pvDataNode,
|
|
1651
|
+
})}\n`;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// proxy.yaml — HTTPProxy for the instance host (mirrors instance-promote).
|
|
1655
|
+
const proxyYaml =
|
|
1656
|
+
Underpost.deploy.baseProxyYamlFactory({ host: _host, env, options }) +
|
|
1657
|
+
Underpost.deploy.deploymentYamlServiceFactory({
|
|
1658
|
+
path: _path,
|
|
1659
|
+
port: _fromPort,
|
|
1660
|
+
deployId: _deployId,
|
|
1661
|
+
env,
|
|
1662
|
+
deploymentVersions: [targetTraffic],
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
// grpc-service.yaml — the parent deploy's gRPC ClusterIP (shared; the
|
|
1666
|
+
// instance cmd resolves {{grpc-service-dns}} to it). Reuse the parent's
|
|
1667
|
+
// generated manifest when present rather than regenerating it here.
|
|
1668
|
+
const parentGrpcServicePath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
|
|
1669
|
+
const grpcServiceYaml = fs.existsSync(parentGrpcServicePath)
|
|
1670
|
+
? fs.readFileSync(parentGrpcServicePath, 'utf8')
|
|
1671
|
+
: '';
|
|
1672
|
+
|
|
1673
|
+
// Write the sibling set next to deployment.yaml (project) and into the
|
|
1674
|
+
// engine-private per-instance build dir (mirrors instances/<id>/ layout).
|
|
1675
|
+
const instanceBuildDir = `./engine-private/conf/${deployId}/instances/${_id}/build/${env}`;
|
|
1676
|
+
fs.mkdirpSync(instanceBuildDir);
|
|
1677
|
+
fs.writeFileSync(`${instanceBuildDir}/deployment.yaml`, deploymentYaml, 'utf8');
|
|
1678
|
+
const siblingManifests = {
|
|
1679
|
+
'pv-pvc.yaml': pvPvcYaml,
|
|
1680
|
+
'proxy.yaml': proxyYaml,
|
|
1681
|
+
'grpc-service.yaml': grpcServiceYaml,
|
|
1682
|
+
};
|
|
1683
|
+
for (const [name, content] of Object.entries(siblingManifests)) {
|
|
1684
|
+
if (!content) continue;
|
|
1685
|
+
fs.writeFileSync(`${envManifestPath}/${name}`, content, 'utf8');
|
|
1686
|
+
fs.writeFileSync(`${instanceBuildDir}/${name}`, content, 'utf8');
|
|
1687
|
+
}
|
|
1688
|
+
logger.info('[instance-build-manifest] Sibling manifests written', {
|
|
1689
|
+
project: envManifestPath,
|
|
1690
|
+
enginePrivate: instanceBuildDir,
|
|
1691
|
+
pvPvc: !!pvPvcYaml,
|
|
1692
|
+
proxy: !!proxyYaml,
|
|
1693
|
+
grpcService: !!grpcServiceYaml,
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1346
1696
|
if (env === 'production') {
|
|
1347
1697
|
if (fs.existsSync(dockerfileManifestPath)) {
|
|
1348
1698
|
fs.copyFileSync(dockerfileManifestPath, `${rootPath}/Dockerfile`);
|
|
1349
1699
|
}
|
|
1350
1700
|
fs.copyFileSync(outputPath, `${rootPath}/deployment.yaml`);
|
|
1701
|
+
// Sibling manifests alongside deployment.yaml at the project root.
|
|
1702
|
+
for (const name of ['pv-pvc.yaml', 'proxy.yaml', 'grpc-service.yaml']) {
|
|
1703
|
+
const src = `${envManifestPath}/${name}`;
|
|
1704
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, `${rootPath}/${name}`);
|
|
1705
|
+
}
|
|
1351
1706
|
logger.info('[instance-build-manifest] Production artifacts copied to project root', {
|
|
1352
1707
|
rootPath,
|
|
1353
1708
|
dockerfile: `${rootPath}/Dockerfile`,
|
|
@@ -1359,6 +1714,25 @@ EOF
|
|
|
1359
1714
|
fs.copyFileSync(ciSrc, `${rootPath}/.github/workflows/docker-image.${_runtime}.ci.yml`);
|
|
1360
1715
|
logger.info(`[instance-build-manifest] CI workflow copied`, { src: ciSrc });
|
|
1361
1716
|
}
|
|
1717
|
+
|
|
1718
|
+
// Ship the development variant alongside production so the instance repo
|
|
1719
|
+
// is self-contained: the dev Dockerfile (built by the -dev CI workflow
|
|
1720
|
+
// into underpost/<runtime>-dev, consumed by the development compose
|
|
1721
|
+
// stack) and its dispatchable workflow. Both are optional — synced only
|
|
1722
|
+
// when the source-of-truth files exist in the engine repo.
|
|
1723
|
+
if (_runtime) {
|
|
1724
|
+
const devDockerfileSrc = `src/runtime/${_runtime}/Dockerfile.dev`;
|
|
1725
|
+
if (fs.existsSync(devDockerfileSrc)) {
|
|
1726
|
+
fs.copyFileSync(devDockerfileSrc, `${rootPath}/Dockerfile.dev`);
|
|
1727
|
+
logger.info('[instance-build-manifest] Dev Dockerfile copied', { src: devDockerfileSrc });
|
|
1728
|
+
}
|
|
1729
|
+
const devCiSrc = `./.github/workflows/docker-image.${_runtime}.dev.ci.yml`;
|
|
1730
|
+
if (fs.existsSync(devCiSrc)) {
|
|
1731
|
+
if (!fs.existsSync(`${rootPath}/.github/workflows`)) fs.mkdirpSync(`${rootPath}/.github/workflows`);
|
|
1732
|
+
fs.copyFileSync(devCiSrc, `${rootPath}/.github/workflows/docker-image.${_runtime}.dev.ci.yml`);
|
|
1733
|
+
logger.info(`[instance-build-manifest] Dev CI workflow copied`, { src: devCiSrc });
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1362
1736
|
}
|
|
1363
1737
|
},
|
|
1364
1738
|
|
|
@@ -1366,7 +1740,7 @@ EOF
|
|
|
1366
1740
|
* @method ls-deployments
|
|
1367
1741
|
* @description Retrieves and logs a table of Kubernetes deployments using `Underpost.deploy.get`.
|
|
1368
1742
|
* @param {string} path - The input value, identifier, or path for the operation (used as an optional deployment name filter).
|
|
1369
|
-
* @param {
|
|
1743
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1370
1744
|
* @memberof UnderpostRun
|
|
1371
1745
|
*/
|
|
1372
1746
|
'ls-deployments': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1377,7 +1751,7 @@ EOF
|
|
|
1377
1751
|
* @method host-update
|
|
1378
1752
|
* @description Executes the `rocky-setup.sh` script to update the host system configuration.
|
|
1379
1753
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1380
|
-
* @param {
|
|
1754
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1381
1755
|
* @memberof UnderpostRun
|
|
1382
1756
|
*/
|
|
1383
1757
|
'host-update': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1393,7 +1767,7 @@ EOF
|
|
|
1393
1767
|
* the systemd cgroup driver, enables the `crio` service, and writes `/etc/crictl.yaml`
|
|
1394
1768
|
* so that `crictl` targets the CRI-O socket by default.
|
|
1395
1769
|
* @param {string} path - Unused.
|
|
1396
|
-
* @param {
|
|
1770
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
1397
1771
|
* @memberof UnderpostRun
|
|
1398
1772
|
*/
|
|
1399
1773
|
'install-crio': (path, options = DEFAULT_OPTION) => {
|
|
@@ -1407,8 +1781,24 @@ gpgcheck=1
|
|
|
1407
1781
|
gpgkey=https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.33/rpm/repodata/repomd.xml.key
|
|
1408
1782
|
EOF`);
|
|
1409
1783
|
shellExec(`sudo dnf -y install cri-o`);
|
|
1410
|
-
//
|
|
1411
|
-
|
|
1784
|
+
// Add the Kubernetes repo so cri-tools (crictl CLI) is available.
|
|
1785
|
+
// The repo has exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni, so we
|
|
1786
|
+
// use --disableexcludes=kubernetes to override and install cri-tools.
|
|
1787
|
+
shellExec(`cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
|
|
1788
|
+
[kubernetes]
|
|
1789
|
+
name=Kubernetes
|
|
1790
|
+
baseurl=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/
|
|
1791
|
+
enabled=1
|
|
1792
|
+
gpgcheck=1
|
|
1793
|
+
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/repodata/repomd.xml.key
|
|
1794
|
+
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
|
|
1795
|
+
EOF`);
|
|
1796
|
+
shellExec(`sudo yum install -y cri-tools --disableexcludes=kubernetes 2>/dev/null || {
|
|
1797
|
+
CRICTL_VERSION="v1.31.0"
|
|
1798
|
+
ARCH="amd64"
|
|
1799
|
+
log "Kubernetes repo not available, downloading crictl binary directly..."
|
|
1800
|
+
curl -sL "https://github.com/kubernetes-sigs/cri-tools/releases/download/\${CRICTL_VERSION}/crictl-\${CRICTL_VERSION}-\${ARCH}.tar.gz" | sudo tar -C /usr/local/bin -xz
|
|
1801
|
+
}`);
|
|
1412
1802
|
// Ensure CRI-O uses systemd cgroup driver (matches kubelet default)
|
|
1413
1803
|
shellExec(`sudo sed -i 's/^#\?cgroup_manager =.*/cgroup_manager = "systemd"/' /etc/crio/crio.conf`, {
|
|
1414
1804
|
silentOnError: true,
|
|
@@ -1428,7 +1818,7 @@ EOF`);
|
|
|
1428
1818
|
* @method dd-container
|
|
1429
1819
|
* @description Deploys a development or debug container tasks jobs, setting up necessary volumes and images, and running specified commands within the container.
|
|
1430
1820
|
* @param {string} path - The input value, identifier, or path for the operation (used as the command to run inside the container).
|
|
1431
|
-
* @param {
|
|
1821
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1432
1822
|
* @memberof UnderpostRun
|
|
1433
1823
|
*/
|
|
1434
1824
|
'dd-container': async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1436,8 +1826,8 @@ EOF`);
|
|
|
1436
1826
|
const baseClusterCommand = options.dev ? ' --dev' : '';
|
|
1437
1827
|
const currentImage = options.imageName
|
|
1438
1828
|
? options.imageName
|
|
1439
|
-
: Underpost.
|
|
1440
|
-
.
|
|
1829
|
+
: Underpost.image
|
|
1830
|
+
.getCurrentLoaded(options.nodeName ? options.nodeName : 'kind-worker', false)
|
|
1441
1831
|
.find((o) => o.IMAGE.match('underpost'));
|
|
1442
1832
|
const podName = options.podName || `underpost-dev-container`;
|
|
1443
1833
|
const volumeHostPath = options.claimName || '/home/dd';
|
|
@@ -1455,7 +1845,11 @@ EOF`);
|
|
|
1455
1845
|
}
|
|
1456
1846
|
|
|
1457
1847
|
if (!currentImage)
|
|
1458
|
-
shellExec(
|
|
1848
|
+
shellExec(
|
|
1849
|
+
`${baseCommand} image${baseClusterCommand} --pull-base --build --path ${
|
|
1850
|
+
options.dev ? '.' : options.underpostRoot
|
|
1851
|
+
} ${options.dev ? '--kind' : '--kubeadm'}`,
|
|
1852
|
+
);
|
|
1459
1853
|
// shellExec(`kubectl delete pod ${podName} --ignore-not-found`);
|
|
1460
1854
|
|
|
1461
1855
|
const payload = {
|
|
@@ -1483,7 +1877,7 @@ EOF`);
|
|
|
1483
1877
|
* @method ip-info
|
|
1484
1878
|
* @description Executes the `ip-info.sh` script to display IP-related information for the specified path.
|
|
1485
1879
|
* @param {string} path - The input value, identifier, or path for the operation (used as an argument to the script).
|
|
1486
|
-
* @param {
|
|
1880
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1487
1881
|
* @memberof UnderpostRun
|
|
1488
1882
|
*/
|
|
1489
1883
|
'ip-info': (path, options = DEFAULT_OPTION) => {
|
|
@@ -1496,7 +1890,7 @@ EOF`);
|
|
|
1496
1890
|
* @method db-client
|
|
1497
1891
|
* @description Deploys and exposes the Adminer database client application (using `adminer:4.7.6-standalone` image) on the cluster.
|
|
1498
1892
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1499
|
-
* @param {
|
|
1893
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1500
1894
|
* @memberof UnderpostRun
|
|
1501
1895
|
*/
|
|
1502
1896
|
'db-client': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1523,7 +1917,7 @@ EOF`);
|
|
|
1523
1917
|
* @method git-conf
|
|
1524
1918
|
* @description Configures Git global and local user name and email settings based on the provided `path` (formatted as `username,email`), or defaults to environment variables.
|
|
1525
1919
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string: `username,email`).
|
|
1526
|
-
* @param {
|
|
1920
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1527
1921
|
* @memberof UnderpostRun
|
|
1528
1922
|
*/
|
|
1529
1923
|
'git-conf': (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1571,7 +1965,7 @@ EOF`);
|
|
|
1571
1965
|
* TLS config, deletes stale Certificate resources, then reapplies the proxy and secret.yaml
|
|
1572
1966
|
* (cert-manager Certificate resources) for each affected deployment.
|
|
1573
1967
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated string: `deployId,env,replicas`).
|
|
1574
|
-
* @param {
|
|
1968
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1575
1969
|
* @memberof UnderpostRun
|
|
1576
1970
|
*/
|
|
1577
1971
|
promote: async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1625,10 +2019,31 @@ EOF`);
|
|
|
1625
2019
|
* @method metrics
|
|
1626
2020
|
* @description Deploys Prometheus and Grafana for metrics monitoring, targeting the hosts defined in the deployment configuration files.
|
|
1627
2021
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1628
|
-
* @param {
|
|
2022
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1629
2023
|
* @memberof UnderpostRun
|
|
1630
2024
|
*/
|
|
1631
2025
|
metrics: async (path, options = DEFAULT_OPTION) => {
|
|
2026
|
+
if (path === 'server') {
|
|
2027
|
+
shellExec(
|
|
2028
|
+
`kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability-1.21+.yaml`,
|
|
2029
|
+
);
|
|
2030
|
+
await timer(2000);
|
|
2031
|
+
|
|
2032
|
+
shellExec(`kubectl patch deployment metrics-server -n kube-system \
|
|
2033
|
+
--type='json' \
|
|
2034
|
+
-p='[
|
|
2035
|
+
{
|
|
2036
|
+
"op":"add",
|
|
2037
|
+
"path":"/spec/template/spec/containers/0/args/-",
|
|
2038
|
+
"value":"--kubelet-insecure-tls"
|
|
2039
|
+
}
|
|
2040
|
+
]'`);
|
|
2041
|
+
shellExec(`kubectl scale deployment metrics-server \
|
|
2042
|
+
-n kube-system \
|
|
2043
|
+
--replicas=1`);
|
|
2044
|
+
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
1632
2047
|
const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
|
|
1633
2048
|
let hosts = [];
|
|
1634
2049
|
for (const deployId of deployList) {
|
|
@@ -1642,7 +2057,7 @@ EOF`);
|
|
|
1642
2057
|
* @method cluster
|
|
1643
2058
|
* @description Deploys a full production/development ready Kubernetes cluster environment including MongoDB, MariaDB, Valkey, Contour (Ingress), and Cert-Manager, and deploys all services.
|
|
1644
2059
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1645
|
-
* @param {
|
|
2060
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1646
2061
|
* @memberof UnderpostRun
|
|
1647
2062
|
*/
|
|
1648
2063
|
cluster: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1704,7 +2119,7 @@ EOF`);
|
|
|
1704
2119
|
* @method deploy
|
|
1705
2120
|
* @description Deploys a specified service (identified by `path`) using blue/green strategy, monitors its status, and switches traffic upon readiness.
|
|
1706
2121
|
* @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID to deploy).
|
|
1707
|
-
* @param {
|
|
2122
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1708
2123
|
* @memberof UnderpostRun
|
|
1709
2124
|
*/
|
|
1710
2125
|
deploy: async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1714,20 +2129,13 @@ EOF`);
|
|
|
1714
2129
|
const currentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
|
|
1715
2130
|
const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
|
|
1716
2131
|
const env = options.dev ? 'development' : 'production';
|
|
1717
|
-
const ignorePods = Underpost.
|
|
2132
|
+
const ignorePods = Underpost.kubectl
|
|
1718
2133
|
.get(`${deployId}-${env}-${targetTraffic}`, 'pods', options.namespace)
|
|
1719
2134
|
.map((p) => p.NAME);
|
|
1720
2135
|
|
|
1721
2136
|
shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic} -n ${options.namespace}`);
|
|
1722
2137
|
|
|
1723
|
-
await Underpost.
|
|
1724
|
-
deployId,
|
|
1725
|
-
env,
|
|
1726
|
-
targetTraffic,
|
|
1727
|
-
ignorePods,
|
|
1728
|
-
options.namespace,
|
|
1729
|
-
'underpost',
|
|
1730
|
-
);
|
|
2138
|
+
await Underpost.monitor.monitorReadyRunner(deployId, env, targetTraffic, ignorePods, options.namespace);
|
|
1731
2139
|
|
|
1732
2140
|
Underpost.deploy.switchTraffic(deployId, env, targetTraffic, options.replicas, options.namespace, options);
|
|
1733
2141
|
},
|
|
@@ -1736,7 +2144,7 @@ EOF`);
|
|
|
1736
2144
|
* @method disk-clean
|
|
1737
2145
|
* @description Executes the `disk-clean-sh` script to perform disk cleanup operations.
|
|
1738
2146
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1739
|
-
* @param {
|
|
2147
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1740
2148
|
* @memberof UnderpostRun
|
|
1741
2149
|
*/
|
|
1742
2150
|
'disk-clean': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1749,7 +2157,7 @@ EOF`);
|
|
|
1749
2157
|
* @method disk-devices
|
|
1750
2158
|
* @description Executes the `disk-devices.sh` script to display information about disk devices.
|
|
1751
2159
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1752
|
-
* @param {
|
|
2160
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1753
2161
|
* @memberof UnderpostRun
|
|
1754
2162
|
*/
|
|
1755
2163
|
'disk-devices': async (path = '/', options = DEFAULT_OPTION) => {
|
|
@@ -1762,7 +2170,7 @@ EOF`);
|
|
|
1762
2170
|
* @method disk-usage
|
|
1763
2171
|
* @description Displays disk usage statistics using the `du` command, sorted by size.
|
|
1764
2172
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1765
|
-
* @param {
|
|
2173
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1766
2174
|
* @memberof UnderpostRun
|
|
1767
2175
|
*/
|
|
1768
2176
|
'disk-usage': async (path = '/', options = DEFAULT_OPTION) => {
|
|
@@ -1777,7 +2185,7 @@ EOF`);
|
|
|
1777
2185
|
* @method dev
|
|
1778
2186
|
* @description Starts development servers for client, API, and proxy based on provided parameters (deployId, host, path, clientHostPort).
|
|
1779
2187
|
* @param {string} path - The input value, identifier, or path for the operation (formatted as `deployId,subConf,host,path,clientHostPort`).
|
|
1780
|
-
* @param {
|
|
2188
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1781
2189
|
* @memberof UnderpostRun
|
|
1782
2190
|
*/
|
|
1783
2191
|
dev: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1819,7 +2227,7 @@ EOF`);
|
|
|
1819
2227
|
}`;
|
|
1820
2228
|
shellExec(cmd, { async: true });
|
|
1821
2229
|
}
|
|
1822
|
-
await awaitDeployMonitor();
|
|
2230
|
+
if ((await awaitDeployMonitor()) !== true) return;
|
|
1823
2231
|
{
|
|
1824
2232
|
const cmd = `npm run dev:client ${deployId} ${subConf} ${host} ${_path} proxy${options.tls ? ' tls' : ''}`;
|
|
1825
2233
|
|
|
@@ -1827,7 +2235,7 @@ EOF`);
|
|
|
1827
2235
|
async: true,
|
|
1828
2236
|
});
|
|
1829
2237
|
}
|
|
1830
|
-
await awaitDeployMonitor();
|
|
2238
|
+
if ((await awaitDeployMonitor()) !== true) return;
|
|
1831
2239
|
shellExec(
|
|
1832
2240
|
`NODE_ENV=development node src/proxy proxy ${deployId} ${subConf} ${host} ${_path}${options.tls ? ' tls' : ''}`,
|
|
1833
2241
|
);
|
|
@@ -1837,7 +2245,7 @@ EOF`);
|
|
|
1837
2245
|
* @method service
|
|
1838
2246
|
* @description Deploys and exposes specific services (like `mongo-express-service`) on the cluster, updating deployment configurations and monitoring status.
|
|
1839
2247
|
* @param {string} path - The input value, identifier, or path for the operation (formatted as `deployId,serviceId,host,path,replicas,image,node`).
|
|
1840
|
-
* @param {
|
|
2248
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1841
2249
|
* @memberof UnderpostRun
|
|
1842
2250
|
*/
|
|
1843
2251
|
service: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1933,7 +2341,7 @@ EOF`);
|
|
|
1933
2341
|
* @method etc-hosts
|
|
1934
2342
|
* @description Generates and logs the contents for the `/etc/hosts` file based on provided hosts or deployment configurations.
|
|
1935
2343
|
* @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated list of hosts).
|
|
1936
|
-
* @param {
|
|
2344
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1937
2345
|
* @memberof UnderpostRun
|
|
1938
2346
|
*/
|
|
1939
2347
|
'etc-hosts': async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1950,7 +2358,7 @@ EOF`);
|
|
|
1950
2358
|
* @method sh
|
|
1951
2359
|
* @description Enables remote control for the Kitty terminal emulator.
|
|
1952
2360
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1953
|
-
* @param {
|
|
2361
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1954
2362
|
* @memberof UnderpostRun
|
|
1955
2363
|
*/
|
|
1956
2364
|
sh: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -1970,7 +2378,7 @@ EOF`);
|
|
|
1970
2378
|
* @method log
|
|
1971
2379
|
* @description Searches and highlights keywords in a specified log file, optionally showing surrounding lines.
|
|
1972
2380
|
* @param {string} path - The input value, identifier, or path for the operation (formatted as `filePath,keywords,lines`).
|
|
1973
|
-
* @param {
|
|
2381
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1974
2382
|
* @memberof UnderpostRun
|
|
1975
2383
|
*/
|
|
1976
2384
|
log: async (path, options = DEFAULT_OPTION) => {
|
|
@@ -1987,7 +2395,7 @@ EOF`);
|
|
|
1987
2395
|
* @method ps
|
|
1988
2396
|
* @description Displays running processes that match a specified path or keyword.
|
|
1989
2397
|
* @param {string} path - The input value, identifier, or path for the operation (used as a keyword to filter processes).
|
|
1990
|
-
* @param {
|
|
2398
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
1991
2399
|
* @memberof UnderpostRun
|
|
1992
2400
|
*/
|
|
1993
2401
|
ps: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -2012,7 +2420,7 @@ EOF`);
|
|
|
2012
2420
|
* @method pid-info
|
|
2013
2421
|
* @description Displays detailed information about a process by PID, including service details, command line, executable path, working directory, environment variables, and parent process tree.
|
|
2014
2422
|
* @param {string} path - The PID of the process to inspect.
|
|
2015
|
-
* @param {
|
|
2423
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2016
2424
|
* @memberof UnderpostRun
|
|
2017
2425
|
*/
|
|
2018
2426
|
'pid-info': (path, options = DEFAULT_OPTION) => {
|
|
@@ -2052,7 +2460,7 @@ EOF`);
|
|
|
2052
2460
|
* @method background
|
|
2053
2461
|
* @description Runs a custom command in the background using nohup, logging output to `/var/log/<id>.log` and saving the PID to `/var/run/<id>.pid`.
|
|
2054
2462
|
* @param {string} path - The command to run in the background (e.g. 'npm run prod:container dd-cyberia-r3').
|
|
2055
|
-
* @param {
|
|
2463
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2056
2464
|
* @memberof UnderpostRun
|
|
2057
2465
|
*/
|
|
2058
2466
|
background: (path, options = DEFAULT_OPTION) => {
|
|
@@ -2072,7 +2480,7 @@ EOF`);
|
|
|
2072
2480
|
* @method ports
|
|
2073
2481
|
* @description Set on ~/.bashrc alias: ports <port> Command to list listening ports that match the given keyword.
|
|
2074
2482
|
* @param {string} path - The input value, identifier, or path for the operation (used as a keyword to filter listening ports).
|
|
2075
|
-
* @param {
|
|
2483
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2076
2484
|
* @memberof UnderpostRun
|
|
2077
2485
|
*/
|
|
2078
2486
|
ports: async (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -2084,7 +2492,7 @@ EOF`);
|
|
|
2084
2492
|
* @method deploy-test
|
|
2085
2493
|
* @description Deploys a test deployment (`dd-test`) in either development or production mode, setting up necessary secrets and starting the deployment.
|
|
2086
2494
|
* @param {string} path - The input value, identifier, or path for the operation (used as the deployment ID).
|
|
2087
|
-
* @param {
|
|
2495
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2088
2496
|
* @memberof UnderpostRun
|
|
2089
2497
|
*/
|
|
2090
2498
|
'deploy-test': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -2109,7 +2517,7 @@ EOF`);
|
|
|
2109
2517
|
* @method tf-vae-test
|
|
2110
2518
|
* @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.
|
|
2111
2519
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2112
|
-
* @param {
|
|
2520
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2113
2521
|
* @memberof UnderpostRun
|
|
2114
2522
|
*/
|
|
2115
2523
|
'tf-vae-test': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -2209,7 +2617,7 @@ EOF`);
|
|
|
2209
2617
|
* @method spark-template
|
|
2210
2618
|
* @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`.
|
|
2211
2619
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2212
|
-
* @param {
|
|
2620
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2213
2621
|
* @memberof UnderpostRun
|
|
2214
2622
|
*/
|
|
2215
2623
|
'spark-template': (path, options = DEFAULT_OPTION) => {
|
|
@@ -2237,7 +2645,7 @@ EOF`);
|
|
|
2237
2645
|
* @method pull-rocky-image
|
|
2238
2646
|
* @description Pulls the base `rockylinux:9` image from Docker Hub via Podman.
|
|
2239
2647
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2240
|
-
* @param {
|
|
2648
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2241
2649
|
* @memberof UnderpostRun
|
|
2242
2650
|
*/
|
|
2243
2651
|
'pull-rocky-image': (path, options = DEFAULT_OPTION) => {
|
|
@@ -2247,7 +2655,7 @@ EOF`);
|
|
|
2247
2655
|
* @method rmi
|
|
2248
2656
|
* @description Forces the removal of all local Podman images (`podman rmi $(podman images -qa) --force`).
|
|
2249
2657
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2250
|
-
* @param {
|
|
2658
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2251
2659
|
* @memberof UnderpostRun
|
|
2252
2660
|
*/
|
|
2253
2661
|
rmi: (path, options = DEFAULT_OPTION) => {
|
|
@@ -2257,7 +2665,7 @@ EOF`);
|
|
|
2257
2665
|
* @method kill
|
|
2258
2666
|
* @description Kills processes listening on the specified port(s). If the `path` contains a `+`, it treats it as a range of ports to kill.
|
|
2259
2667
|
* @param {string} path - The input value, identifier, or path for the operation (used as the port number).
|
|
2260
|
-
* @param {
|
|
2668
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2261
2669
|
* @memberof UnderpostRun
|
|
2262
2670
|
*/
|
|
2263
2671
|
kill: (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -2289,7 +2697,7 @@ EOF`);
|
|
|
2289
2697
|
* constraints (lowercase, uppercase, digit, special char, min 8 chars). Logs the plain password
|
|
2290
2698
|
* to the console or, when `--copy` is set, copies it to the clipboard via pbcopy.
|
|
2291
2699
|
* @param {string} path - Optional password length (default: 16).
|
|
2292
|
-
* @param {
|
|
2700
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
2293
2701
|
* @param {boolean} options.copy - When true, copies to clipboard instead of logging.
|
|
2294
2702
|
* @memberof UnderpostRun
|
|
2295
2703
|
*/
|
|
@@ -2323,7 +2731,7 @@ EOF`);
|
|
|
2323
2731
|
* @method secret
|
|
2324
2732
|
* @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.
|
|
2325
2733
|
* @param {string} path - The input value, identifier, or path for the operation (used as the optional path to the secret file).
|
|
2326
|
-
* @param {
|
|
2734
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2327
2735
|
* @memberof UnderpostRun
|
|
2328
2736
|
*/
|
|
2329
2737
|
secret: (path, options = DEFAULT_OPTION) => {
|
|
@@ -2336,7 +2744,7 @@ EOF`);
|
|
|
2336
2744
|
* @method underpost-config
|
|
2337
2745
|
* @description Calls `Underpost.deploy.configMap` to create a Kubernetes ConfigMap, defaulting to the 'production' environment.
|
|
2338
2746
|
* @param {string} path - The input value, identifier, or path for the operation (used as the optional configuration name/environment).
|
|
2339
|
-
* @param {
|
|
2747
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2340
2748
|
* @memberof UnderpostRun
|
|
2341
2749
|
*/
|
|
2342
2750
|
'underpost-config': (path = '', options = DEFAULT_OPTION) => {
|
|
@@ -2346,7 +2754,7 @@ EOF`);
|
|
|
2346
2754
|
* @method gpu-env
|
|
2347
2755
|
* @description Sets up a dedicated GPU development environment cluster, resetting and then setting up the cluster with `--dedicated-gpu` and monitoring the pods.
|
|
2348
2756
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2349
|
-
* @param {
|
|
2757
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2350
2758
|
* @memberof UnderpostRun
|
|
2351
2759
|
*/
|
|
2352
2760
|
'gpu-env': (path, options = DEFAULT_OPTION) => {
|
|
@@ -2359,7 +2767,7 @@ EOF`);
|
|
|
2359
2767
|
* @method tf-gpu-test
|
|
2360
2768
|
* @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`.
|
|
2361
2769
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2362
|
-
* @param {
|
|
2770
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2363
2771
|
* @memberof UnderpostRun
|
|
2364
2772
|
*/
|
|
2365
2773
|
'tf-gpu-test': (path, options = DEFAULT_OPTION) => {
|
|
@@ -2373,7 +2781,7 @@ EOF`);
|
|
|
2373
2781
|
* @method deploy-job
|
|
2374
2782
|
* @description Creates and applies a custom Kubernetes Pod manifest (Job) for running arbitrary commands inside a container image (defaulting to a TensorFlow/NVIDIA image).
|
|
2375
2783
|
* @param {string} path - The input value, identifier, or path for the operation (used as the optional script path or job argument).
|
|
2376
|
-
* @param {
|
|
2784
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2377
2785
|
* @memberof UnderpostRun
|
|
2378
2786
|
*/
|
|
2379
2787
|
'deploy-job': async (path, options = DEFAULT_OPTION) => {
|
|
@@ -2504,16 +2912,17 @@ EOF`;
|
|
|
2504
2912
|
/**
|
|
2505
2913
|
* @method push-bundle
|
|
2506
2914
|
* @description Builds the client zip for the specified deployment, splits it into parts, and uploads to file storage.
|
|
2507
|
-
* Steps: set env, build+split zip,
|
|
2915
|
+
* Steps: set env, build+split zip, upload only the zip parts belonging to the deploy-id's hosts (from conf.server.json).
|
|
2916
|
+
* Only files matching `<host>-<route>.zip.part*` or `<host>-<route>.zip` for each non-skipped route are uploaded.
|
|
2508
2917
|
* @param {string} path - Optional `fsPath.splitOption` string.
|
|
2509
2918
|
* Examples: `build` (default split 8), `build.16` (split 16 MB), `build.none-split` (no split flag).
|
|
2510
|
-
* @param {
|
|
2919
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
2511
2920
|
* @param {string} [options.deployId] - Override deploy ID.
|
|
2512
2921
|
* @param {boolean} [options.dev] - Use development environment; defaults to production.
|
|
2513
2922
|
* @memberof UnderpostRun
|
|
2514
2923
|
*/
|
|
2515
2924
|
'push-bundle': (path = '', options = DEFAULT_OPTION) => {
|
|
2516
|
-
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
2925
|
+
const baseCommand = 'node bin'; // options.dev ? 'node bin' : 'underpost';
|
|
2517
2926
|
const env = options.dev ? 'development' : 'production';
|
|
2518
2927
|
const deployId = options.deployId || 'dd-default';
|
|
2519
2928
|
const pathParts = (path || '').split('.');
|
|
@@ -2537,11 +2946,54 @@ EOF`;
|
|
|
2537
2946
|
}
|
|
2538
2947
|
}
|
|
2539
2948
|
|
|
2949
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
2950
|
+
const confServer = fs.existsSync(confServerPath)
|
|
2951
|
+
? loadReplicas(deployId, loadConfServerJson(confServerPath))
|
|
2952
|
+
: {};
|
|
2953
|
+
const storageFilePath = `engine-private/conf/${deployId}/storage.bundle.json`;
|
|
2954
|
+
|
|
2540
2955
|
shellExec(`${baseCommand} env ${deployId} ${env}`);
|
|
2541
2956
|
shellExec(`${baseCommand} client ${deployId} --build-zip${splitFlag ? ` ${splitFlag}` : ''}`);
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2957
|
+
|
|
2958
|
+
const pushBundleFiles = (host, routePath) => {
|
|
2959
|
+
const buildId = `${host}-${routePath.replaceAll('/', '')}`;
|
|
2960
|
+
const buildDir = `./${fsPath}`;
|
|
2961
|
+
if (!fs.existsSync(buildDir)) return;
|
|
2962
|
+
const partFiles = fs
|
|
2963
|
+
.readdirSync(buildDir)
|
|
2964
|
+
.filter(
|
|
2965
|
+
(name) =>
|
|
2966
|
+
name.startsWith(`${buildId}.zip.part`) ||
|
|
2967
|
+
name.startsWith(`${buildId}.zip-part`) ||
|
|
2968
|
+
name === `${buildId}.zip`,
|
|
2969
|
+
)
|
|
2970
|
+
.map((name) => `${fsPath}/${name}`);
|
|
2971
|
+
if (partFiles.length === 0) {
|
|
2972
|
+
logger.warn(`push-bundle: no bundle files found for '${host}${routePath}'`, { buildId });
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
for (const partFile of partFiles) {
|
|
2976
|
+
shellExec(
|
|
2977
|
+
`${baseCommand} fs ${partFile} --deploy-id ${deployId} --storage-file-path ${storageFilePath} --force`,
|
|
2978
|
+
);
|
|
2979
|
+
}
|
|
2980
|
+
};
|
|
2981
|
+
|
|
2982
|
+
for (const host of Object.keys(confServer)) {
|
|
2983
|
+
for (const routePath of Object.keys(confServer[host])) {
|
|
2984
|
+
const routeConf = confServer[host][routePath] || {};
|
|
2985
|
+
if (routeConf.redirect || routeConf.disabledRebuild) continue;
|
|
2986
|
+
if (routeConf.singleReplica) {
|
|
2987
|
+
if (routeConf.replicas) {
|
|
2988
|
+
for (const replica of routeConf.replicas) {
|
|
2989
|
+
pushBundleFiles(host, replica);
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
continue;
|
|
2993
|
+
}
|
|
2994
|
+
pushBundleFiles(host, routePath);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2545
2997
|
},
|
|
2546
2998
|
|
|
2547
2999
|
/**
|
|
@@ -2552,17 +3004,19 @@ EOF`;
|
|
|
2552
3004
|
* so that multi-path deployments are handled correctly.
|
|
2553
3005
|
* @param {string} path - Optional comma-separated host name(s) to restrict processing (e.g. 'underpost.net' or 'a.com,b.com').
|
|
2554
3006
|
* If omitted, all hosts from `engine-private/conf/<deployId>/conf.server.json` are used.
|
|
2555
|
-
* @param {
|
|
3007
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
2556
3008
|
* @param {string} [options.deployId] - Deploy ID for storage lookup (defaults to 'dd-default').
|
|
2557
3009
|
* @param {boolean} [options.dev] - Use development environment; defaults to production.
|
|
2558
3010
|
* @memberof UnderpostRun
|
|
2559
3011
|
*/
|
|
2560
3012
|
'pull-bundle': (path = '', options = DEFAULT_OPTION) => {
|
|
2561
|
-
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
3013
|
+
const baseCommand = 'node bin'; // options.dev ? 'node bin' : 'underpost';
|
|
2562
3014
|
const env = options.dev ? 'development' : 'production';
|
|
2563
3015
|
const deployId = options.deployId || 'dd-default';
|
|
2564
3016
|
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
2565
|
-
const confServer = fs.existsSync(confServerPath)
|
|
3017
|
+
const confServer = fs.existsSync(confServerPath)
|
|
3018
|
+
? loadReplicas(deployId, loadConfServerJson(confServerPath))
|
|
3019
|
+
: {};
|
|
2566
3020
|
const hostsArg = path
|
|
2567
3021
|
? path
|
|
2568
3022
|
.split(',')
|
|
@@ -2581,70 +3035,81 @@ EOF`;
|
|
|
2581
3035
|
`${baseCommand} fs build --recursive --deploy-id ${deployId} --storage-file-path engine-private/conf/${deployId}/storage.bundle.json --pull --omit-unzip`,
|
|
2582
3036
|
);
|
|
2583
3037
|
|
|
3038
|
+
const pullBundleRoute = (host, routePath) => {
|
|
3039
|
+
const buildId = `${host}-${routePath.replaceAll('/', '')}`;
|
|
3040
|
+
const zipPath = `build/${buildId}.zip`;
|
|
3041
|
+
const buildDir = './build';
|
|
3042
|
+
const hasZip = fs.existsSync(zipPath);
|
|
3043
|
+
const hasParts =
|
|
3044
|
+
fs.existsSync(buildDir) &&
|
|
3045
|
+
fs
|
|
3046
|
+
.readdirSync(buildDir)
|
|
3047
|
+
.some((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`));
|
|
3048
|
+
|
|
3049
|
+
if (!hasZip && !hasParts) {
|
|
3050
|
+
logger.warn(`Bundle not found for '${host}${routePath}'. Skipping.`, { zipPath, deployId });
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
if (hasParts) shellExec(`${baseCommand} client --merge-zip ${zipPath}`);
|
|
3055
|
+
shellExec(`${baseCommand} client --unzip ${zipPath}`);
|
|
3056
|
+
shellExec(`sudo rm -rf ${zipPath}`);
|
|
3057
|
+
|
|
3058
|
+
if (fs.existsSync(buildDir)) {
|
|
3059
|
+
fs.readdirSync(buildDir)
|
|
3060
|
+
.filter((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`))
|
|
3061
|
+
.forEach((partFile) => shellExec(`sudo rm -rf ${buildDir}/${partFile}`));
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
const extractedDir = `build/${buildId.replace(/-$/, '')}`;
|
|
3065
|
+
if (!fs.existsSync(extractedDir)) {
|
|
3066
|
+
logger.warn(`Extracted build dir not found: ${extractedDir}. Skipping move for '${host}${routePath}'.`);
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
const publicDestPath = routePath === '/' ? `public/${host}` : `public/${host}${routePath}`;
|
|
3071
|
+
if (fs.existsSync(publicDestPath)) shellExec(`sudo rm -rf ${publicDestPath}`);
|
|
3072
|
+
if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
|
|
3073
|
+
fs.copySync(`${extractedDir}`, `${publicDestPath}`);
|
|
3074
|
+
};
|
|
3075
|
+
|
|
2584
3076
|
for (const host of hostsArg) {
|
|
2585
|
-
// Gather all routes for this host; fall back to root '/' when host is not in confServer
|
|
2586
|
-
// (e.g. when hosts were provided explicitly via the path argument).
|
|
2587
3077
|
const routePaths = confServer[host] ? Object.keys(confServer[host]) : ['/'];
|
|
2588
3078
|
|
|
2589
3079
|
for (const routePath of routePaths) {
|
|
2590
3080
|
const routeConf = confServer[host] ? confServer[host][routePath] || {} : {};
|
|
2591
|
-
|
|
2592
|
-
if (routeConf.singleReplica
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
const zipPath = `build/${buildId}.zip`;
|
|
2599
|
-
const buildDir = './build';
|
|
2600
|
-
const hasZip = fs.existsSync(zipPath);
|
|
2601
|
-
const hasParts =
|
|
2602
|
-
fs.existsSync(buildDir) &&
|
|
2603
|
-
fs
|
|
2604
|
-
.readdirSync(buildDir)
|
|
2605
|
-
.some((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`));
|
|
2606
|
-
|
|
2607
|
-
if (!hasZip && !hasParts) {
|
|
2608
|
-
logger.warn(`Bundle not found for '${host}${routePath}'. Skipping.`, { zipPath, deployId });
|
|
2609
|
-
continue;
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
if (hasParts) shellExec(`${baseCommand} client --merge-zip ${zipPath}`);
|
|
2613
|
-
shellExec(`${baseCommand} client --unzip ${zipPath}`);
|
|
2614
|
-
shellExec(`sudo rm -rf ${zipPath}`);
|
|
2615
|
-
|
|
2616
|
-
// Clean up downloaded part wrapper zips left by --omit-unzip pull
|
|
2617
|
-
if (fs.existsSync(buildDir)) {
|
|
2618
|
-
fs.readdirSync(buildDir)
|
|
2619
|
-
.filter((name) => name.startsWith(`${buildId}.zip.part`) || name.startsWith(`${buildId}.zip-part`))
|
|
2620
|
-
.forEach((partFile) => shellExec(`sudo rm -rf ${buildDir}/${partFile}`));
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
// unzipClientBuild extracts to buildId with trailing '-' stripped
|
|
2624
|
-
// e.g. "build/underpost.net-" → "build/underpost.net"
|
|
2625
|
-
// e.g. "build/app.net-admin" → "build/app.net-admin" (no trailing dash, no change)
|
|
2626
|
-
const extractedDir = `build/${buildId.replace(/-$/, '')}`;
|
|
2627
|
-
if (!fs.existsSync(extractedDir)) {
|
|
2628
|
-
logger.warn(`Extracted build dir not found: ${extractedDir}. Skipping move for '${host}${routePath}'.`);
|
|
3081
|
+
if (routeConf.redirect || routeConf.disabledRebuild) continue;
|
|
3082
|
+
if (routeConf.singleReplica) {
|
|
3083
|
+
if (routeConf.replicas) {
|
|
3084
|
+
for (const replica of routeConf.replicas) {
|
|
3085
|
+
pullBundleRoute(host, replica);
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
2629
3088
|
continue;
|
|
2630
3089
|
}
|
|
2631
|
-
|
|
2632
|
-
// Destination mirrors the public directory layout used by the server
|
|
2633
|
-
const publicDestPath = routePath === '/' ? `public/${host}` : `public/${host}${routePath}`;
|
|
2634
|
-
if (fs.existsSync(publicDestPath)) shellExec(`sudo rm -rf ${publicDestPath}`);
|
|
2635
|
-
// Ensure parent directory exists for sub-paths
|
|
2636
|
-
if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
|
|
2637
|
-
shellExec(`sudo mv ${extractedDir} ${publicDestPath}`);
|
|
3090
|
+
pullBundleRoute(host, routePath);
|
|
2638
3091
|
}
|
|
2639
3092
|
}
|
|
2640
3093
|
},
|
|
2641
3094
|
|
|
3095
|
+
/**
|
|
3096
|
+
* @method build-cluster-deployment-manifests
|
|
3097
|
+
* @description Builds deployment manifests for both production and development environments using `node bin deploy --build-manifest`, syncing them, and setting replicas to 1 for the `dd` deployment.
|
|
3098
|
+
* @param {string} path - Unused.
|
|
3099
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
3100
|
+
* @memberof UnderpostRun
|
|
3101
|
+
*/
|
|
3102
|
+
'build-cluster-deployment-manifests': (path = '', options = DEFAULT_OPTION) => {
|
|
3103
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd development`);
|
|
3104
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd production --cert`);
|
|
3105
|
+
},
|
|
3106
|
+
|
|
2642
3107
|
/**
|
|
2643
3108
|
* @method monitor-ui
|
|
2644
3109
|
* @description Installs and enables the Cockpit KVM Dashboard (cockpit, cockpit-machines, libvirt)
|
|
2645
3110
|
* and opens the cockpit firewall service. With `--remove`, closes the firewall service instead.
|
|
2646
3111
|
* @param {string} path - Unused.
|
|
2647
|
-
* @param {
|
|
3112
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
2648
3113
|
* `options.remove` — when true, removes the cockpit firewall rule instead of adding it.
|
|
2649
3114
|
* @memberof UnderpostRun
|
|
2650
3115
|
*/
|
|
@@ -2668,7 +3133,7 @@ EOF`;
|
|
|
2668
3133
|
* Use `reload-shared-dir` for subsequent permission repairs without recreating the group.
|
|
2669
3134
|
* @param {string} path - Target directory to set up (defaults to `/home/dd/engine`).
|
|
2670
3135
|
* Customise via the `path` argument or leave empty to use the default.
|
|
2671
|
-
* @param {
|
|
3136
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow.
|
|
2672
3137
|
* Key fields: `options.user` (default `'admin'`), `options.group` (default `'engine-dev'`).
|
|
2673
3138
|
* @memberof UnderpostRun
|
|
2674
3139
|
*/
|
|
@@ -2689,6 +3154,33 @@ EOF`;
|
|
|
2689
3154
|
|
|
2690
3155
|
logger.info(`[setup-shared-dir] Shared directory setup complete: ${dir}`);
|
|
2691
3156
|
},
|
|
3157
|
+
/**
|
|
3158
|
+
* @method shared-dir-add-user
|
|
3159
|
+
* @description Add a user to an existing shared directory without changing
|
|
3160
|
+
* file owners. Grants recursive group/ACL access so the user can read and
|
|
3161
|
+
* write throughout the shared workspace while preserving existing ownership.
|
|
3162
|
+
*
|
|
3163
|
+
* @param {string} path - Shared directory (defaults to `/home/dd/engine`).
|
|
3164
|
+
* @param {UnderpostRunDefaultOptions} options - Underpost runner options.
|
|
3165
|
+
* Key fields:
|
|
3166
|
+
* - options.user (default: 'admin')
|
|
3167
|
+
* - options.group (default: 'engine-dev')
|
|
3168
|
+
*
|
|
3169
|
+
* @memberof UnderpostRun
|
|
3170
|
+
*/ 'shared-dir-add-user': (path = '/home/dd/engine', options = DEFAULT_OPTION) => {
|
|
3171
|
+
const dir = path || '/home/dd/engine';
|
|
3172
|
+
const user = options.user || 'admin';
|
|
3173
|
+
|
|
3174
|
+
logger.info(`[shared-dir-add-user] dir=${dir} user=${user}`);
|
|
3175
|
+
|
|
3176
|
+
// Give the user direct access without changing owners or group ownership.
|
|
3177
|
+
shellExec(`sudo setfacl -R -m u:${user}:rwx ${dir}`);
|
|
3178
|
+
|
|
3179
|
+
// Make future files/directories inherit the same user ACL.
|
|
3180
|
+
shellExec(`sudo find ${dir} -type d -exec setfacl -d -m u:${user}:rwx {} \\;`);
|
|
3181
|
+
|
|
3182
|
+
logger.info(`[shared-dir-add-user] User '${user}' added to shared directory: ${dir}`);
|
|
3183
|
+
},
|
|
2692
3184
|
};
|
|
2693
3185
|
|
|
2694
3186
|
static API = {
|
|
@@ -2717,7 +3209,7 @@ EOF`;
|
|
|
2717
3209
|
* @description Executes a specified runner function from the UnderpostRun class with the provided path and options.
|
|
2718
3210
|
* @param {string} runner - The name of the runner to execute.
|
|
2719
3211
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2720
|
-
* @param {
|
|
3212
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2721
3213
|
* @memberof UnderpostRun
|
|
2722
3214
|
* @returns {Promise<any>} The result of the runner execution.
|
|
2723
3215
|
*/
|
|
@@ -2730,7 +3222,7 @@ EOF`;
|
|
|
2730
3222
|
* @description Initiates the execution of a specified CLI command (runner) with the given input value (`path`) and processed options.
|
|
2731
3223
|
* @param {string} runner - The name of the runner to execute.
|
|
2732
3224
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
2733
|
-
* @param {
|
|
3225
|
+
* @param {UnderpostRunDefaultOptions} options - The default underpost runner options for customizing workflow
|
|
2734
3226
|
* @memberof UnderpostRun
|
|
2735
3227
|
* @returns {Promise<any>} The result of the callback execution.
|
|
2736
3228
|
*/
|