underpost 2.8.86 → 2.8.88
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.development +39 -2
- package/.env.production +42 -2
- package/.env.test +39 -2
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -5
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +3 -3
- package/README.md +76 -2
- package/bin/build.js +5 -0
- package/bin/deploy.js +93 -27
- package/bin/file.js +8 -4
- package/bin/util.js +1 -56
- package/cli.md +16 -5
- package/conf.js +33 -7
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +174 -0
- package/manifests/deployment/dd-test-development/proxy.yaml +51 -0
- package/manifests/deployment/mongo-express/deployment.yaml +12 -12
- package/manifests/maas/nvim.sh +91 -0
- package/package.json +8 -15
- package/src/api/core/core.router.js +2 -1
- package/src/api/default/default.controller.js +6 -1
- package/src/api/default/default.router.js +6 -2
- package/src/api/default/default.service.js +10 -1
- package/src/api/document/document.controller.js +66 -0
- package/src/api/document/document.model.js +51 -0
- package/src/api/document/document.router.js +24 -0
- package/src/api/document/document.service.js +125 -0
- package/src/api/file/file.controller.js +15 -1
- package/src/api/file/file.router.js +2 -1
- package/src/api/file/file.service.js +28 -8
- package/src/api/test/test.router.js +1 -1
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.controller.js +25 -60
- package/src/api/user/user.model.js +29 -7
- package/src/api/user/user.router.js +40 -8
- package/src/api/user/user.service.js +86 -35
- package/src/cli/baremetal.js +33 -3
- package/src/cli/cloud-init.js +11 -0
- package/src/cli/cluster.js +4 -23
- package/src/cli/cron.js +0 -1
- package/src/cli/db.js +0 -19
- package/src/cli/deploy.js +67 -52
- package/src/cli/fs.js +1 -0
- package/src/cli/index.js +9 -1
- package/src/cli/lxd.js +7 -0
- package/src/cli/repository.js +44 -6
- package/src/cli/run.js +56 -9
- package/src/cli/ssh.js +20 -6
- package/src/client/Default.index.js +42 -1
- package/src/client/components/core/Account.js +10 -2
- package/src/client/components/core/AgGrid.js +30 -8
- package/src/client/components/core/Auth.js +99 -56
- package/src/client/components/core/BtnIcon.js +3 -2
- package/src/client/components/core/CalendarCore.js +2 -3
- package/src/client/components/core/CommonJs.js +1 -2
- package/src/client/components/core/Content.js +15 -12
- package/src/client/components/core/Css.js +2 -1
- package/src/client/components/core/CssCore.js +18 -1
- package/src/client/components/core/Docs.js +5 -5
- package/src/client/components/core/FileExplorer.js +3 -3
- package/src/client/components/core/FullScreen.js +19 -28
- package/src/client/components/core/Input.js +22 -16
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/LoadingAnimation.js +2 -2
- package/src/client/components/core/LogIn.js +16 -23
- package/src/client/components/core/LogOut.js +5 -1
- package/src/client/components/core/Logger.js +4 -1
- package/src/client/components/core/Modal.js +102 -87
- package/src/client/components/core/ObjectLayerEngine.js +229 -4
- package/src/client/components/core/ObjectLayerEngineModal.js +442 -0
- package/src/client/components/core/Pagination.js +207 -0
- package/src/client/components/core/Panel.js +10 -10
- package/src/client/components/core/PanelForm.js +130 -33
- package/src/client/components/core/Recover.js +2 -2
- package/src/client/components/core/Router.js +210 -34
- package/src/client/components/core/SignUp.js +1 -2
- package/src/client/components/core/Stream.js +1 -1
- package/src/client/components/core/ToggleSwitch.js +15 -1
- package/src/client/components/core/VanillaJs.js +3 -84
- package/src/client/components/core/Worker.js +2 -2
- package/src/client/components/default/LogInDefault.js +0 -6
- package/src/client/components/default/LogOutDefault.js +0 -16
- package/src/client/components/default/MenuDefault.js +97 -44
- package/src/client/components/default/RoutesDefault.js +5 -2
- package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
- package/src/client/services/core/core.service.js +8 -20
- package/src/client/services/default/default.management.js +115 -18
- package/src/client/services/default/default.service.js +13 -4
- package/src/client/services/document/document.service.js +97 -0
- package/src/client/services/file/file.service.js +2 -0
- package/src/client/services/test/test.service.js +3 -0
- package/src/client/services/user/user.management.js +6 -0
- package/src/client/services/user/user.service.js +15 -4
- package/src/client/ssr/Render.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +3 -0
- package/src/client/ssr/head/Seo.js +1 -0
- package/src/index.js +24 -2
- package/src/runtime/lampp/Lampp.js +89 -2
- package/src/runtime/xampp/Xampp.js +48 -1
- package/src/server/auth.js +519 -155
- package/src/server/backup.js +2 -2
- package/src/server/client-build-docs.js +1 -1
- package/src/server/client-build.js +4 -12
- package/src/server/client-icons.js +6 -78
- package/src/server/conf.js +144 -141
- package/src/server/process.js +2 -1
- package/src/server/proxy.js +1 -1
- package/src/server/runtime.js +136 -288
- package/src/server/ssl.js +1 -2
- package/src/server/ssr.js +85 -0
- package/src/server/start.js +4 -4
- package/src/server/valkey.js +2 -1
- package/test/api.test.js +3 -2
- package/bin/cyberia0.js +0 -78
package/src/cli/db.js
CHANGED
|
@@ -177,25 +177,6 @@ class UnderpostDB {
|
|
|
177
177
|
}`,
|
|
178
178
|
);
|
|
179
179
|
}
|
|
180
|
-
if (false) {
|
|
181
|
-
const containerBaseBackupPath = '/backup';
|
|
182
|
-
let timeFolder = shellExec(
|
|
183
|
-
`sudo kubectl exec -i ${podName} -- sh -c "cd ${containerBaseBackupPath} && ls -a"`,
|
|
184
|
-
{
|
|
185
|
-
stdout: true,
|
|
186
|
-
disableLog: false,
|
|
187
|
-
silent: true,
|
|
188
|
-
},
|
|
189
|
-
).split(`\n`);
|
|
190
|
-
timeFolder = timeFolder[timeFolder.length - 2];
|
|
191
|
-
if (timeFolder === '..') {
|
|
192
|
-
logger.warn(`Cannot backup available`, { timeFolder });
|
|
193
|
-
} else {
|
|
194
|
-
shellExec(
|
|
195
|
-
`sudo kubectl cp ${nameSpace}/${podName}:${containerBaseBackupPath}/${timeFolder}/${dbName} ${_toNewBsonPath}`,
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
180
|
}
|
|
200
181
|
break;
|
|
201
182
|
}
|
package/src/cli/deploy.js
CHANGED
|
@@ -12,7 +12,6 @@ import { loggerFactory } from '../server/logger.js';
|
|
|
12
12
|
import { shellExec } from '../server/process.js';
|
|
13
13
|
import fs from 'fs-extra';
|
|
14
14
|
import dotenv from 'dotenv';
|
|
15
|
-
import { DataBaseProvider } from '../db/DataBaseProvider.js';
|
|
16
15
|
import UnderpostRootEnv from './env.js';
|
|
17
16
|
import UnderpostCluster from './cluster.js';
|
|
18
17
|
import Underpost from '../index.js';
|
|
@@ -22,13 +21,13 @@ const logger = loggerFactory(import.meta);
|
|
|
22
21
|
class UnderpostDeploy {
|
|
23
22
|
static NETWORK = {};
|
|
24
23
|
static API = {
|
|
25
|
-
sync(deployList, { versions, replicas,
|
|
24
|
+
sync(deployList, { versions, replicas, node }) {
|
|
26
25
|
const deployGroupId = 'dd.router';
|
|
27
26
|
fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
|
|
28
27
|
const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
|
|
29
28
|
const limitFactor = 0.8;
|
|
30
29
|
const reserveFactor = 0.05;
|
|
31
|
-
const resources = UnderpostCluster.API.getResourcesCapacity(
|
|
30
|
+
const resources = UnderpostCluster.API.getResourcesCapacity(node);
|
|
32
31
|
const memory = parseInt(resources.memory.value / totalPods);
|
|
33
32
|
const cpu = parseInt(resources.cpu.value / totalPods);
|
|
34
33
|
UnderpostRootEnv.API.set(
|
|
@@ -49,7 +48,7 @@ class UnderpostDeploy {
|
|
|
49
48
|
const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
|
|
50
49
|
process.env.PORT = initEnvObj.PORT;
|
|
51
50
|
process.env.NODE_ENV = env;
|
|
52
|
-
await Config.build(
|
|
51
|
+
await Config.build('proxy', deployList);
|
|
53
52
|
return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
|
|
54
53
|
},
|
|
55
54
|
deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
|
|
@@ -62,7 +61,7 @@ class UnderpostDeploy {
|
|
|
62
61
|
)
|
|
63
62
|
.join('');
|
|
64
63
|
},
|
|
65
|
-
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas }) {
|
|
64
|
+
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image }) {
|
|
66
65
|
return `apiVersion: apps/v1
|
|
67
66
|
kind: Deployment
|
|
68
67
|
metadata:
|
|
@@ -81,7 +80,7 @@ spec:
|
|
|
81
80
|
spec:
|
|
82
81
|
containers:
|
|
83
82
|
- name: ${deployId}-${env}-${suffix}
|
|
84
|
-
image: localhost/rockylinux9-underpost:${Underpost.version}
|
|
83
|
+
image: ${image ?? `localhost/rockylinux9-underpost:${Underpost.version}`}
|
|
85
84
|
# resources:
|
|
86
85
|
# requests:
|
|
87
86
|
# memory: "${resources.requests.memory}"
|
|
@@ -118,6 +117,7 @@ spec:
|
|
|
118
117
|
async buildManifest(deployList, env, options) {
|
|
119
118
|
const resources = UnderpostDeploy.API.resourcesFactory();
|
|
120
119
|
const replicas = options.replicas;
|
|
120
|
+
const image = options.image;
|
|
121
121
|
|
|
122
122
|
for (const _deployId of deployList.split(',')) {
|
|
123
123
|
const deployId = _deployId.trim();
|
|
@@ -144,6 +144,7 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
144
144
|
suffix: deploymentVersion,
|
|
145
145
|
resources,
|
|
146
146
|
replicas,
|
|
147
|
+
image,
|
|
147
148
|
}).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
148
149
|
`;
|
|
149
150
|
}
|
|
@@ -153,21 +154,7 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
153
154
|
let secretYaml = '';
|
|
154
155
|
|
|
155
156
|
for (const host of Object.keys(confServer)) {
|
|
156
|
-
if (env === 'production')
|
|
157
|
-
secretYaml += `
|
|
158
|
-
---
|
|
159
|
-
apiVersion: cert-manager.io/v1
|
|
160
|
-
kind: Certificate
|
|
161
|
-
metadata:
|
|
162
|
-
name: ${host}
|
|
163
|
-
spec:
|
|
164
|
-
commonName: ${host}
|
|
165
|
-
dnsNames:
|
|
166
|
-
- ${host}
|
|
167
|
-
issuerRef:
|
|
168
|
-
name: letsencrypt-prod
|
|
169
|
-
kind: ClusterIssuer
|
|
170
|
-
secretName: ${host}`;
|
|
157
|
+
if (env === 'production') secretYaml += UnderpostDeploy.API.buildCertManagerCertificate({ host });
|
|
171
158
|
|
|
172
159
|
const pathPortAssignment = pathPortAssignmentData[host];
|
|
173
160
|
// logger.info('', { host, pathPortAssignment });
|
|
@@ -221,6 +208,22 @@ spec:
|
|
|
221
208
|
}
|
|
222
209
|
}
|
|
223
210
|
},
|
|
211
|
+
buildCertManagerCertificate({ host }) {
|
|
212
|
+
return `
|
|
213
|
+
---
|
|
214
|
+
apiVersion: cert-manager.io/v1
|
|
215
|
+
kind: Certificate
|
|
216
|
+
metadata:
|
|
217
|
+
name: ${host}
|
|
218
|
+
spec:
|
|
219
|
+
commonName: ${host}
|
|
220
|
+
dnsNames:
|
|
221
|
+
- ${host}
|
|
222
|
+
issuerRef:
|
|
223
|
+
name: letsencrypt-prod
|
|
224
|
+
kind: ClusterIssuer
|
|
225
|
+
secretName: ${host}`;
|
|
226
|
+
},
|
|
224
227
|
getCurrentTraffic(deployId) {
|
|
225
228
|
// kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
|
|
226
229
|
const hostTest = Object.keys(
|
|
@@ -230,7 +233,7 @@ spec:
|
|
|
230
233
|
return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
|
|
231
234
|
},
|
|
232
235
|
async callback(
|
|
233
|
-
deployList = '
|
|
236
|
+
deployList = '',
|
|
234
237
|
env = 'development',
|
|
235
238
|
options = {
|
|
236
239
|
remove: false,
|
|
@@ -240,12 +243,16 @@ spec:
|
|
|
240
243
|
infoUtil: false,
|
|
241
244
|
expose: false,
|
|
242
245
|
cert: false,
|
|
246
|
+
certHosts: '',
|
|
243
247
|
versions: '',
|
|
248
|
+
image: '',
|
|
244
249
|
traffic: '',
|
|
245
250
|
replicas: '',
|
|
251
|
+
node: '',
|
|
246
252
|
restoreHosts: false,
|
|
247
253
|
disableUpdateDeployment: false,
|
|
248
254
|
infoTraffic: false,
|
|
255
|
+
etcHosts: false,
|
|
249
256
|
},
|
|
250
257
|
) {
|
|
251
258
|
if (options.infoUtil === true)
|
|
@@ -300,6 +307,14 @@ docker login nvcr.io
|
|
|
300
307
|
Username: $oauthtoken
|
|
301
308
|
Password: <Your Key>
|
|
302
309
|
`);
|
|
310
|
+
if (!deployList && options.certHosts) {
|
|
311
|
+
for (const host of options.certHosts.split(',')) {
|
|
312
|
+
shellExec(`sudo kubectl apply -f - <<EOF
|
|
313
|
+
${UnderpostDeploy.API.buildCertManagerCertificate({ host })}
|
|
314
|
+
EOF`);
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
} else if (!deployList) deployList = 'dd-default';
|
|
303
318
|
if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
304
319
|
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
305
320
|
if (options.infoTraffic === true) {
|
|
@@ -319,20 +334,16 @@ Password: <Your Key>
|
|
|
319
334
|
if (!options.replicas) options.replicas = 1;
|
|
320
335
|
if (options.sync) UnderpostDeploy.API.sync(deployList, options);
|
|
321
336
|
if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
|
|
322
|
-
if (options.infoRouter === true) {
|
|
337
|
+
if (options.infoRouter === true || options.buildManifest === true) {
|
|
323
338
|
logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
|
|
324
339
|
return;
|
|
325
340
|
}
|
|
326
341
|
UnderpostDeploy.API.configMap(env);
|
|
327
342
|
let renderHosts = '';
|
|
328
|
-
let
|
|
329
|
-
const etcHost = (
|
|
330
|
-
concat,
|
|
331
|
-
) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
332
|
-
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
343
|
+
let etcHosts = [];
|
|
333
344
|
if (options.restoreHosts === true) {
|
|
334
|
-
|
|
335
|
-
|
|
345
|
+
const factoryResult = UnderpostDeploy.API.etcHostFactory(etcHosts);
|
|
346
|
+
renderHosts = factoryResult.renderHosts;
|
|
336
347
|
logger.info(renderHosts);
|
|
337
348
|
return;
|
|
338
349
|
}
|
|
@@ -360,8 +371,9 @@ Password: <Your Key>
|
|
|
360
371
|
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
361
372
|
for (const host of Object.keys(confServer)) {
|
|
362
373
|
shellExec(`sudo kubectl delete HTTPProxy ${host}`);
|
|
363
|
-
if (
|
|
364
|
-
|
|
374
|
+
if (UnderpostDeploy.API.isValidTLSContext({ host, env, options }))
|
|
375
|
+
shellExec(`sudo kubectl delete Certificate ${host}`);
|
|
376
|
+
if (!options.remove === true && env === 'development') etcHosts.push(host);
|
|
365
377
|
}
|
|
366
378
|
|
|
367
379
|
const manifestsPath =
|
|
@@ -372,28 +384,14 @@ Password: <Your Key>
|
|
|
372
384
|
if (!options.remove === true) {
|
|
373
385
|
if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
|
|
374
386
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
|
|
375
|
-
|
|
387
|
+
|
|
388
|
+
if (UnderpostDeploy.API.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }))
|
|
376
389
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
|
|
377
390
|
}
|
|
378
391
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
switch (env) {
|
|
383
|
-
case 'development':
|
|
384
|
-
renderHosts = etcHost(concatHots);
|
|
385
|
-
fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
386
|
-
|
|
387
|
-
break;
|
|
388
|
-
|
|
389
|
-
default:
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
break;
|
|
394
|
-
|
|
395
|
-
default:
|
|
396
|
-
break;
|
|
392
|
+
if (options.etcHosts === true) {
|
|
393
|
+
const factoryResult = UnderpostDeploy.API.etcHostFactory(etcHosts);
|
|
394
|
+
renderHosts = factoryResult.renderHosts;
|
|
397
395
|
}
|
|
398
396
|
if (renderHosts)
|
|
399
397
|
logger.info(
|
|
@@ -472,7 +470,11 @@ Password: <Your Key>
|
|
|
472
470
|
notReadyPods.push(pod);
|
|
473
471
|
}
|
|
474
472
|
}
|
|
475
|
-
return {
|
|
473
|
+
return {
|
|
474
|
+
ready: pods.length > 0 && notReadyPods.length === 0,
|
|
475
|
+
notReadyPods,
|
|
476
|
+
readyPods,
|
|
477
|
+
};
|
|
476
478
|
},
|
|
477
479
|
configMap(env) {
|
|
478
480
|
shellExec(`kubectl delete configmap underpost-config`);
|
|
@@ -487,6 +489,19 @@ Password: <Your Key>
|
|
|
487
489
|
);
|
|
488
490
|
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
|
|
489
491
|
},
|
|
492
|
+
etcHostFactory(hosts = []) {
|
|
493
|
+
const renderHosts = `127.0.0.1 ${hosts.join(
|
|
494
|
+
' ',
|
|
495
|
+
)} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
496
|
+
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
497
|
+
|
|
498
|
+
fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
499
|
+
return { renderHosts };
|
|
500
|
+
},
|
|
501
|
+
isValidTLSContext: ({ host, env, options }) =>
|
|
502
|
+
env === 'production' &&
|
|
503
|
+
options.cert === true &&
|
|
504
|
+
(!options.certHosts || options.certHosts.split(',').includes(host)),
|
|
490
505
|
};
|
|
491
506
|
}
|
|
492
507
|
|
package/src/cli/fs.js
CHANGED
|
@@ -61,6 +61,7 @@ class UnderpostFileStorage {
|
|
|
61
61
|
await UnderpostFileStorage.API.pull(_path, options);
|
|
62
62
|
} else logger.warn(`Pull path already exists`, _path);
|
|
63
63
|
}
|
|
64
|
+
shellExec(`cd ${path} && git init && git add . && git commit -m "Base pull state"`);
|
|
64
65
|
} else {
|
|
65
66
|
const files =
|
|
66
67
|
options.git === true
|
package/src/cli/index.js
CHANGED
|
@@ -22,7 +22,9 @@ program.name('underpost').description(`underpost ci/cd cli ${Underpost.version}`
|
|
|
22
22
|
// 'new' command: Create a new project
|
|
23
23
|
program
|
|
24
24
|
.command('new')
|
|
25
|
-
.argument('<app-name>', 'The name of the application to create.')
|
|
25
|
+
.argument('<app-name>', 'The name or deploy-id of the application to create.')
|
|
26
|
+
.option('--deploy-id', 'Crete deploy ID conf env files')
|
|
27
|
+
.option('--cluster', 'Create deploy ID cluster files and sync to current cluster')
|
|
26
28
|
.option('--dev', 'Sets the development cli context')
|
|
27
29
|
.description('Initializes a new Underpost project with a predefined structure.')
|
|
28
30
|
.action(Underpost.repo.new);
|
|
@@ -157,16 +159,20 @@ program
|
|
|
157
159
|
.option('--expose', 'Exposes services matching the provided deployment ID list.')
|
|
158
160
|
.option('--info-util', 'Displays useful `kubectl` utility management commands.')
|
|
159
161
|
.option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
|
|
162
|
+
.option('--cert-hosts <hosts>', 'Resets TLS/SSL certificate secrets for specified hosts.')
|
|
163
|
+
.option('--node <node>', 'Sets optional node for deployment operations.')
|
|
160
164
|
.option(
|
|
161
165
|
'--build-manifest',
|
|
162
166
|
'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
|
|
163
167
|
)
|
|
164
168
|
.option('--replicas <replicas>', 'Sets a custom number of replicas for deployments.')
|
|
169
|
+
.option('--image <image>', 'Sets a custom image for deployments.')
|
|
165
170
|
.option('--versions <deployment-versions>', 'A comma-separated list of custom deployment versions.')
|
|
166
171
|
.option('--traffic <traffic-versions>', 'A comma-separated list of custom deployment traffic weights.')
|
|
167
172
|
.option('--disable-update-deployment', 'Disables updates to deployments.')
|
|
168
173
|
.option('--info-traffic', 'Retrieves traffic configuration from current resource deployments.')
|
|
169
174
|
.option('--kubeadm', 'Enables the kubeadm context for deployment operations.')
|
|
175
|
+
.option('--etc-hosts', 'Enables the etc-hosts context for deployment operations.')
|
|
170
176
|
.option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
|
|
171
177
|
.description('Manages application deployments, defaulting to deploying development pods.')
|
|
172
178
|
.action(Underpost.deploy.callback);
|
|
@@ -345,6 +351,8 @@ program
|
|
|
345
351
|
.option('--command <command-array>', 'Array of commands to run.')
|
|
346
352
|
.option('--args <args-array>', 'Array of arguments to pass to the command.')
|
|
347
353
|
.option('--dev', 'Sets the development context environment for the script.')
|
|
354
|
+
.option('--build', 'Set builder context runner')
|
|
355
|
+
.option('--replicas <replicas>', 'Sets a custom number of replicas for deployment.')
|
|
348
356
|
.option('--pod-name <pod-name>', 'Optional: Specifies the pod name for test execution.')
|
|
349
357
|
.option('--volume-host-path <volume-host-path>', 'Optional: Specifies the volume host path for test execution.')
|
|
350
358
|
.option('--volume-mount-path <volume-mount-path>', 'Optional: Specifies the volume mount path for test execution.')
|
package/src/cli/lxd.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LXD module for managing LXD virtual machines and networks.
|
|
3
|
+
* @module src/cli/lxd.js
|
|
4
|
+
* @namespace UnderpostLxd
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { getNpmRootPath } from '../server/conf.js';
|
|
2
8
|
import { getLocalIPv4Address } from '../server/dns.js';
|
|
3
9
|
import { pbcopy, shellExec } from '../server/process.js';
|
|
@@ -32,6 +38,7 @@ class UnderpostLxd {
|
|
|
32
38
|
* @param {string} [options.deleteExpose=''] - Delete exposed ports from a VM (format: 'vmName:port1,port2').
|
|
33
39
|
* @param {string} [options.test=''] - Test health, status and network connectivity for a VM.
|
|
34
40
|
* @param {string} [options.autoExposeK8sPorts=''] - Automatically expose common Kubernetes ports for the VM.
|
|
41
|
+
* @memberof UnderpostLxd
|
|
35
42
|
*/
|
|
36
43
|
async callback(
|
|
37
44
|
options = {
|
package/src/cli/repository.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { commitData } from '../client/components/core/CommonJs.js';
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
|
-
import { pbcopy, shellExec } from '../server/process.js';
|
|
3
|
+
import { pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
4
4
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
import { getNpmRootPath } from '../server/conf.js';
|
|
7
7
|
import UnderpostStartUp from '../server/start.js';
|
|
8
|
+
import { Config } from '../server/conf.js';
|
|
8
9
|
|
|
9
10
|
dotenv.config();
|
|
10
11
|
|
|
@@ -12,7 +13,7 @@ const logger = loggerFactory(import.meta);
|
|
|
12
13
|
|
|
13
14
|
class UnderpostRepository {
|
|
14
15
|
static API = {
|
|
15
|
-
clone(gitUri =
|
|
16
|
+
clone(gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`, options = { bare: false, g8: false }) {
|
|
16
17
|
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
17
18
|
const repoName = gitUri.split('/').pop();
|
|
18
19
|
if (fs.existsSync(`./${repoName}`)) fs.removeSync(`./${repoName}`);
|
|
@@ -25,7 +26,11 @@ class UnderpostRepository {
|
|
|
25
26
|
},
|
|
26
27
|
);
|
|
27
28
|
},
|
|
28
|
-
pull(
|
|
29
|
+
pull(
|
|
30
|
+
repoPath = './',
|
|
31
|
+
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
32
|
+
options = { g8: false },
|
|
33
|
+
) {
|
|
29
34
|
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
30
35
|
shellExec(
|
|
31
36
|
`cd ${repoPath} && git pull https://${
|
|
@@ -48,6 +53,7 @@ class UnderpostRepository {
|
|
|
48
53
|
},
|
|
49
54
|
) {
|
|
50
55
|
if (commitType === 'reset') {
|
|
56
|
+
if (options.copy) pbcopy(shellExec(`git --no-pager log -1 --pretty=%B`, { stdout: true }));
|
|
51
57
|
shellExec(`cd ${repoPath} && git reset --soft HEAD~${isNaN(parseInt(subModule)) ? 1 : parseInt(subModule)}`);
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
@@ -59,7 +65,11 @@ class UnderpostRepository {
|
|
|
59
65
|
shellExec(`cd ${repoPath} && git commit ${options?.empty ? `--allow-empty ` : ''}-m "${_message}"`);
|
|
60
66
|
},
|
|
61
67
|
|
|
62
|
-
push(
|
|
68
|
+
push(
|
|
69
|
+
repoPath = './',
|
|
70
|
+
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template}`,
|
|
71
|
+
options = { f: false, g8: false },
|
|
72
|
+
) {
|
|
63
73
|
const gExtension = options.g8 === true || options.G8 === true ? '.g8' : '.git';
|
|
64
74
|
shellExec(
|
|
65
75
|
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
|
|
@@ -80,15 +90,16 @@ class UnderpostRepository {
|
|
|
80
90
|
);
|
|
81
91
|
},
|
|
82
92
|
|
|
83
|
-
new(repositoryName, options = { dev: false }) {
|
|
93
|
+
new(repositoryName, options = { dev: false, deployId: false, cluster: false }) {
|
|
84
94
|
return new Promise(async (resolve, reject) => {
|
|
85
95
|
try {
|
|
86
96
|
await logger.setUpInfo();
|
|
97
|
+
actionInitLog();
|
|
87
98
|
if (repositoryName === 'service')
|
|
88
99
|
return resolve(
|
|
89
100
|
await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
|
|
90
101
|
);
|
|
91
|
-
|
|
102
|
+
if (options.deployId === true) return Config.deployIdFactory(repositoryName, options);
|
|
92
103
|
const npmRoot = getNpmRootPath();
|
|
93
104
|
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
94
105
|
const destFolder = `./${repositoryName}`;
|
|
@@ -135,6 +146,33 @@ class UnderpostRepository {
|
|
|
135
146
|
.concat(diffUntrackOutput.toString().split('\n').filter(Boolean))
|
|
136
147
|
.filter((f) => !deleteFiles.includes(f));
|
|
137
148
|
},
|
|
149
|
+
privateConfUpdate(deployId) {
|
|
150
|
+
shellCd(`/home/dd/engine`);
|
|
151
|
+
const privateRepoName = `engine-${deployId.split('dd-')[1]}-private`;
|
|
152
|
+
const privateRepoPath = `../${privateRepoName}`;
|
|
153
|
+
if (fs.existsSync(privateRepoPath)) fs.removeSync(privateRepoPath);
|
|
154
|
+
shellExec(`cd .. && underpost clone ${process.env.GITHUB_USERNAME}/${privateRepoName}`);
|
|
155
|
+
shellExec(`cd ${privateRepoPath} && underpost pull . ${process.env.GITHUB_USERNAME}/${privateRepoName}`);
|
|
156
|
+
const packageJsonDeploy = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/package.json`, 'utf8'));
|
|
157
|
+
const packageJsonEngine = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
|
|
158
|
+
if (packageJsonDeploy.version !== packageJsonEngine.version) {
|
|
159
|
+
logger.warn(
|
|
160
|
+
`Version mismatch: deploy-version:${packageJsonDeploy.version} !== engine-version:${packageJsonEngine.version},
|
|
161
|
+
Prevent build private config repo.`,
|
|
162
|
+
);
|
|
163
|
+
return {
|
|
164
|
+
validVersion: false,
|
|
165
|
+
engineVersion: packageJsonEngine.version,
|
|
166
|
+
deployVersion: packageJsonDeploy.version,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
shellExec(`node bin/build ${deployId} conf`);
|
|
170
|
+
return {
|
|
171
|
+
validVersion: true,
|
|
172
|
+
engineVersion: packageJsonEngine.version,
|
|
173
|
+
deployVersion: packageJsonDeploy.version,
|
|
174
|
+
};
|
|
175
|
+
},
|
|
138
176
|
};
|
|
139
177
|
}
|
|
140
178
|
|
package/src/cli/run.js
CHANGED
|
@@ -7,6 +7,8 @@ import fs from 'fs-extra';
|
|
|
7
7
|
import { range, setPad, timer } from '../client/components/core/CommonJs.js';
|
|
8
8
|
import UnderpostDeploy from './deploy.js';
|
|
9
9
|
import UnderpostRootEnv from './env.js';
|
|
10
|
+
import UnderpostRepository from './repository.js';
|
|
11
|
+
import os from 'os';
|
|
10
12
|
|
|
11
13
|
const logger = loggerFactory(import.meta);
|
|
12
14
|
|
|
@@ -19,6 +21,8 @@ class UnderpostRun {
|
|
|
19
21
|
imageName: '',
|
|
20
22
|
containerName: '',
|
|
21
23
|
namespace: '',
|
|
24
|
+
build: false,
|
|
25
|
+
replicas: 1,
|
|
22
26
|
};
|
|
23
27
|
static RUNNERS = {
|
|
24
28
|
'spark-template': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
@@ -26,9 +30,9 @@ class UnderpostRun {
|
|
|
26
30
|
shellExec(`sudo rm -rf ${dir}`);
|
|
27
31
|
shellCd('/home/dd');
|
|
28
32
|
|
|
29
|
-
// pbcopy(`cd /home/dd && sbt new
|
|
33
|
+
// pbcopy(`cd /home/dd && sbt new ${process.env.GITHUB_USERNAME}/spark-template.g8`);
|
|
30
34
|
// await read({ prompt: 'Command copy to clipboard, press enter to continue.\n' });
|
|
31
|
-
shellExec(`cd /home/dd && sbt new
|
|
35
|
+
shellExec(`cd /home/dd && sbt new ${process.env.GITHUB_USERNAME}/spark-template.g8 '--name=spark-template'`);
|
|
32
36
|
|
|
33
37
|
shellCd(dir);
|
|
34
38
|
|
|
@@ -72,12 +76,19 @@ class UnderpostRun {
|
|
|
72
76
|
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
73
77
|
shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
|
|
74
78
|
shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
|
|
79
|
+
const mongoHosts = ['mongodb-0.mongodb-service'];
|
|
75
80
|
shellExec(
|
|
76
|
-
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${
|
|
81
|
+
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${mongoHosts.join(
|
|
82
|
+
',',
|
|
83
|
+
)} --pull-image`,
|
|
77
84
|
);
|
|
78
85
|
shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
|
|
79
86
|
shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
|
|
80
87
|
shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
|
|
88
|
+
{
|
|
89
|
+
const hostListenResult = UnderpostDeploy.API.etcHostFactory(mongoHosts);
|
|
90
|
+
logger.info(hostListenResult.renderHosts);
|
|
91
|
+
}
|
|
81
92
|
},
|
|
82
93
|
'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
83
94
|
const { underpostRoot } = options;
|
|
@@ -96,10 +107,12 @@ class UnderpostRun {
|
|
|
96
107
|
},
|
|
97
108
|
'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
98
109
|
const baseCommand = options.dev || true ? 'node bin' : 'underpost';
|
|
110
|
+
shellExec(`${baseCommand} run clean`);
|
|
111
|
+
shellExec(`${baseCommand} push ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
|
|
99
112
|
shellCd('/home/dd/engine');
|
|
100
113
|
shellExec(`git reset`);
|
|
101
114
|
shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
|
|
102
|
-
shellExec(`${baseCommand} push .
|
|
115
|
+
shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
|
|
103
116
|
},
|
|
104
117
|
clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
105
118
|
shellCd(path ?? `/home/dd/engine`);
|
|
@@ -108,8 +121,8 @@ class UnderpostRun {
|
|
|
108
121
|
pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
109
122
|
shellCd(`/home/dd/engine`);
|
|
110
123
|
shellExec(`node bin/deploy clean-core-repo`);
|
|
111
|
-
shellExec(`underpost pull .
|
|
112
|
-
shellExec(`underpost pull engine-private
|
|
124
|
+
shellExec(`underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
|
|
125
|
+
shellExec(`underpost pull ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
|
|
113
126
|
},
|
|
114
127
|
'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
115
128
|
actionInitLog();
|
|
@@ -121,11 +134,12 @@ class UnderpostRun {
|
|
|
121
134
|
}
|
|
122
135
|
},
|
|
123
136
|
'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
137
|
+
actionInitLog();
|
|
124
138
|
const baseCommand = options.dev || true ? 'node bin' : 'underpost';
|
|
125
139
|
shellCd('/home/dd/engine');
|
|
126
140
|
shellExec(`git reset`);
|
|
127
141
|
shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
|
|
128
|
-
shellExec(`${baseCommand} push .
|
|
142
|
+
shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
|
|
129
143
|
},
|
|
130
144
|
ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
131
145
|
const { underpostRoot } = options;
|
|
@@ -141,6 +155,29 @@ class UnderpostRun {
|
|
|
141
155
|
let [deployId, subConf] = path.split(',');
|
|
142
156
|
shellExec(`npm run dev-api ${deployId} ${subConf}`);
|
|
143
157
|
},
|
|
158
|
+
sync: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
159
|
+
const env = options.dev ? 'development' : 'production';
|
|
160
|
+
const baseCommand = options.dev || true ? 'node bin' : 'underpost';
|
|
161
|
+
shellExec(`${baseCommand} run clean`);
|
|
162
|
+
const defaultPath = ['dd-default', 1, ``, ``, 'kind-control-plane'];
|
|
163
|
+
let [deployId, replicas, versions, image, node] = path ? path.split(',') : defaultPath;
|
|
164
|
+
deployId = deployId ?? defaultPath[0];
|
|
165
|
+
replicas = replicas ?? defaultPath[1];
|
|
166
|
+
versions = versions ?? defaultPath[2];
|
|
167
|
+
image = image ?? defaultPath[3];
|
|
168
|
+
node = node ?? defaultPath[4];
|
|
169
|
+
shellExec(
|
|
170
|
+
`${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
|
|
171
|
+
replicas ?? 1
|
|
172
|
+
} --node ${node}${image ? ` --image ${image}` : ''}${
|
|
173
|
+
versions ? ` --versions ${versions.replaceAll('+', ',')}` : ''
|
|
174
|
+
} dd ${env}`,
|
|
175
|
+
);
|
|
176
|
+
if (!options.build) shellExec(`${baseCommand} deploy --kubeadm ${deployId} ${env}`);
|
|
177
|
+
},
|
|
178
|
+
'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
179
|
+
console.table(await UnderpostDeploy.API.get(path, 'deployments'));
|
|
180
|
+
},
|
|
144
181
|
monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
145
182
|
const pid = getTerminalPid();
|
|
146
183
|
logger.info('monitor pid', pid);
|
|
@@ -275,11 +312,21 @@ class UnderpostRun {
|
|
|
275
312
|
},
|
|
276
313
|
deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
277
314
|
const deployId = path;
|
|
315
|
+
const { validVersion, deployVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
|
|
316
|
+
if (!validVersion) throw new Error('Version mismatch');
|
|
278
317
|
const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
|
|
279
318
|
const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
|
|
280
319
|
const env = 'production';
|
|
281
320
|
const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
|
|
282
|
-
|
|
321
|
+
|
|
322
|
+
if (options.build === true) {
|
|
323
|
+
// deployId, replicas, versions, image, node
|
|
324
|
+
shellExec(
|
|
325
|
+
`node bin run sync ${deployId},${options.replicas ?? 1},${targetTraffic},${
|
|
326
|
+
options.imageName ?? `localhost/rockylinux9-underpost:${deployVersion}`
|
|
327
|
+
},${os.hostname()}`,
|
|
328
|
+
);
|
|
329
|
+
} else shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
|
|
283
330
|
|
|
284
331
|
let checkStatusIteration = 0;
|
|
285
332
|
const checkStatusIterationMsDelay = 1000;
|
|
@@ -298,7 +345,7 @@ class UnderpostRun {
|
|
|
298
345
|
|
|
299
346
|
UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
|
|
300
347
|
|
|
301
|
-
shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
|
|
348
|
+
// shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
|
|
302
349
|
},
|
|
303
350
|
'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
304
351
|
const { underpostRoot } = options;
|
package/src/cli/ssh.js
CHANGED
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SSH module for managing SSH key generation and connection setup.
|
|
3
|
+
* @module src/cli/ssh.js
|
|
4
|
+
* @namespace UnderpostSSH
|
|
5
|
+
*/
|
|
6
|
+
|
|
2
7
|
import { shellExec } from '../server/process.js';
|
|
3
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @class UnderpostSSH
|
|
11
|
+
* @description Manages SSH key generation and connection setup.
|
|
12
|
+
* @memberof UnderpostSSH
|
|
13
|
+
*/
|
|
4
14
|
class UnderpostSSH {
|
|
5
15
|
static API = {
|
|
6
16
|
/**
|
|
7
17
|
* @method callback
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
10
|
-
*
|
|
18
|
+
* @description Manages SSH key generation and connection setup based on the default deployment ID.
|
|
19
|
+
* This function will either generate a new SSH key pair or import an existing one,
|
|
20
|
+
* then initiate the SSH connection process.
|
|
21
|
+
* @param {object} [options={ generate: false }] - Options for the SSH callback.
|
|
22
|
+
* @param {boolean} [options.generate=false] - If true, generates a new SSH key pair. Otherwise, it imports the existing one.
|
|
23
|
+
* @memberof UnderpostSSH
|
|
24
|
+
* @returns {Promise<void>}
|
|
11
25
|
*/
|
|
12
26
|
callback: async (
|
|
13
27
|
options = {
|
|
14
28
|
generate: false,
|
|
15
29
|
},
|
|
16
30
|
) => {
|
|
17
|
-
//
|
|
31
|
+
// Example usage for importing an existing key:
|
|
18
32
|
// node bin/deploy ssh root@<host> <password> import
|
|
19
33
|
|
|
20
|
-
//
|
|
34
|
+
// Example usage for generating a new key:
|
|
21
35
|
// node bin/deploy ssh root@<host> <password>
|
|
22
36
|
|
|
23
37
|
shellExec(
|