@underpostnet/underpost 2.98.3 → 2.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/bin/deploy.js +1 -1
- package/cli.md +109 -110
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +4 -4
- package/package.json +1 -2
- package/src/api/user/user.router.js +7 -40
- package/src/cli/baremetal.js +67 -71
- package/src/cli/cloud-init.js +11 -12
- package/src/cli/cluster.js +22 -24
- package/src/cli/db.js +43 -50
- package/src/cli/deploy.js +162 -60
- package/src/cli/env.js +20 -5
- package/src/cli/fs.js +19 -21
- package/src/cli/index.js +35 -32
- package/src/cli/lxd.js +5 -5
- package/src/cli/monitor.js +66 -88
- package/src/cli/repository.js +7 -6
- package/src/cli/run.js +369 -261
- package/src/cli/secrets.js +3 -3
- package/src/cli/ssh.js +31 -32
- package/src/cli/static.js +1 -1
- package/src/cli/test.js +6 -7
- package/src/index.js +49 -32
- package/src/runtime/express/Express.js +7 -6
- package/src/server/auth.js +6 -1
- package/src/server/backup.js +11 -1
- package/src/server/conf.js +4 -4
- package/src/{cli → server}/cron.js +56 -29
- package/src/server/dns.js +39 -31
- package/src/server/peer.js +2 -2
- package/src/server/process.js +2 -2
- package/src/server/proxy.js +8 -7
- package/src/server/runtime.js +4 -7
- package/src/server/start.js +28 -15
- package/src/ws/IoServer.js +2 -3
- package/src/cli/script.js +0 -85
- package/src/monitor.js +0 -34
package/src/cli/deploy.js
CHANGED
|
@@ -18,12 +18,9 @@ import { loggerFactory } from '../server/logger.js';
|
|
|
18
18
|
import { shellExec } from '../server/process.js';
|
|
19
19
|
import fs from 'fs-extra';
|
|
20
20
|
import dotenv from 'dotenv';
|
|
21
|
-
import UnderpostRootEnv from './env.js';
|
|
22
|
-
import UnderpostCluster from './cluster.js';
|
|
23
21
|
import { timer } from '../client/components/core/CommonJs.js';
|
|
24
22
|
import os from 'node:os';
|
|
25
|
-
import
|
|
26
|
-
import UnderpostBaremetal from './baremetal.js';
|
|
23
|
+
import Underpost from '../index.js';
|
|
27
24
|
|
|
28
25
|
const logger = loggerFactory(import.meta);
|
|
29
26
|
|
|
@@ -35,7 +32,6 @@ const logger = loggerFactory(import.meta);
|
|
|
35
32
|
* @memberof UnderpostDeploy
|
|
36
33
|
*/
|
|
37
34
|
class UnderpostDeploy {
|
|
38
|
-
static NETWORK = {};
|
|
39
35
|
static API = {
|
|
40
36
|
/**
|
|
41
37
|
* Creates a router configuration for a list of deployments.
|
|
@@ -59,12 +55,24 @@ class UnderpostDeploy {
|
|
|
59
55
|
* @param {string} env - Environment for which the service is being created.
|
|
60
56
|
* @param {number} port - Port number for the service.
|
|
61
57
|
* @param {Array<string>} deploymentVersions - List of deployment versions.
|
|
58
|
+
* @param {string} serviceId - Custom service name (optional).
|
|
59
|
+
* @param {Array} pathRewritePolicy - Path rewrite policy (optional).
|
|
60
|
+
* @param {object} timeoutPolicy - Timeout policy (optional).
|
|
61
|
+
* @param {object} retryPolicy - Retry policy (optional).
|
|
62
62
|
* @returns {string} - YAML service configuration for the specified deployment.
|
|
63
|
-
* @param {string} [serviceId] - Custom service name (optional).
|
|
64
|
-
* @param {Array} [pathRewritePolicy] - Path rewrite policy (optional).
|
|
65
63
|
* @memberof UnderpostDeploy
|
|
66
64
|
*/
|
|
67
|
-
deploymentYamlServiceFactory({
|
|
65
|
+
deploymentYamlServiceFactory({
|
|
66
|
+
deployId,
|
|
67
|
+
path,
|
|
68
|
+
env,
|
|
69
|
+
port,
|
|
70
|
+
deploymentVersions,
|
|
71
|
+
serviceId,
|
|
72
|
+
pathRewritePolicy,
|
|
73
|
+
timeoutPolicy,
|
|
74
|
+
retryPolicy,
|
|
75
|
+
}) {
|
|
68
76
|
return `
|
|
69
77
|
- conditions:
|
|
70
78
|
- prefix: ${path}
|
|
@@ -79,12 +87,25 @@ class UnderpostDeploy {
|
|
|
79
87
|
).join(`
|
|
80
88
|
`)}`
|
|
81
89
|
: ''
|
|
90
|
+
}${
|
|
91
|
+
timeoutPolicy
|
|
92
|
+
? `\n timeoutPolicy:\n${timeoutPolicy.response ? ` response: ${timeoutPolicy.response}\n` : ''}${
|
|
93
|
+
timeoutPolicy.idle ? ` idle: ${timeoutPolicy.idle}\n` : ''
|
|
94
|
+
}`
|
|
95
|
+
: ''
|
|
96
|
+
}${
|
|
97
|
+
retryPolicy
|
|
98
|
+
? `\n retryPolicy:\n${retryPolicy.count !== undefined ? ` count: ${retryPolicy.count}\n` : ''}${
|
|
99
|
+
retryPolicy.perTryTimeout ? ` perTryTimeout: ${retryPolicy.perTryTimeout}\n` : ''
|
|
100
|
+
}`
|
|
101
|
+
: ''
|
|
82
102
|
}
|
|
83
103
|
enableWebsockets: true
|
|
84
104
|
services:
|
|
85
105
|
${deploymentVersions
|
|
86
106
|
.map(
|
|
87
|
-
(version, i) =>
|
|
107
|
+
(version, i) =>
|
|
108
|
+
` - name: ${serviceId ? serviceId : `${deployId}-${env}-${version}-service`}
|
|
88
109
|
port: ${port}
|
|
89
110
|
weight: ${i === 0 ? 100 : 0}
|
|
90
111
|
`,
|
|
@@ -106,13 +127,12 @@ class UnderpostDeploy {
|
|
|
106
127
|
* @memberof UnderpostDeploy
|
|
107
128
|
*/
|
|
108
129
|
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image, namespace, volumes, cmd }) {
|
|
109
|
-
const baseCommand = env === 'development' ? 'node bin' : 'underpost';
|
|
110
130
|
if (!cmd)
|
|
111
131
|
cmd = [
|
|
112
132
|
`npm install -g npm@11.2.0`,
|
|
113
133
|
`npm install -g underpost`,
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
`underpost secret underpost --create-from-file /etc/config/.env.${env}`,
|
|
135
|
+
`underpost start --build --run --underpost-quickly-install ${deployId} ${env}`,
|
|
116
136
|
];
|
|
117
137
|
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
118
138
|
if (!volumes)
|
|
@@ -164,7 +184,8 @@ ${
|
|
|
164
184
|
- >
|
|
165
185
|
${cmd.join(` && `)}
|
|
166
186
|
|
|
167
|
-
${
|
|
187
|
+
${Underpost.deploy
|
|
188
|
+
.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
|
|
168
189
|
.render.split(`\n`)
|
|
169
190
|
.map((l) => ' ' + l)
|
|
170
191
|
.join(`\n`)}
|
|
@@ -188,6 +209,14 @@ spec:
|
|
|
188
209
|
* @param {string} options.replicas - Number of replicas for each deployment.
|
|
189
210
|
* @param {string} options.image - Docker image for the deployment.
|
|
190
211
|
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
212
|
+
* @param {string} [options.versions] - Comma-separated list of versions to deploy.
|
|
213
|
+
* @param {string} [options.cmd] - Custom initialization command for deploymentYamlPartsFactory (comma-separated commands).
|
|
214
|
+
* @param {string} [options.timeoutResponse] - Timeout response setting for the deployment.
|
|
215
|
+
* @param {string} [options.timeoutIdle] - Timeout idle setting for the deployment.
|
|
216
|
+
* @param {string} [options.retryCount] - Retry count setting for the deployment.
|
|
217
|
+
* @param {string} [options.retryPerTryTimeout] - Retry per-try timeout setting for the deployment.
|
|
218
|
+
* @param {boolean} [options.disableDeploymentProxy] - Whether to disable deployment proxy.
|
|
219
|
+
* @param {string} [options.traffic] - Traffic status for the deployment.
|
|
191
220
|
* @returns {Promise<void>} - Promise that resolves when the manifest is built.
|
|
192
221
|
* @memberof UnderpostDeploy
|
|
193
222
|
*/
|
|
@@ -203,7 +232,7 @@ spec:
|
|
|
203
232
|
deployId,
|
|
204
233
|
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
205
234
|
);
|
|
206
|
-
const router = await
|
|
235
|
+
const router = await Underpost.deploy.routerFactory(deployId, env);
|
|
207
236
|
const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
|
|
208
237
|
const { fromPort, toPort } = deployRangePortFactory(router);
|
|
209
238
|
const deploymentVersions = options.versions.split(',');
|
|
@@ -215,15 +244,17 @@ spec:
|
|
|
215
244
|
let deploymentYamlParts = '';
|
|
216
245
|
for (const deploymentVersion of deploymentVersions) {
|
|
217
246
|
deploymentYamlParts += `---
|
|
218
|
-
${
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
247
|
+
${Underpost.deploy
|
|
248
|
+
.deploymentYamlPartsFactory({
|
|
249
|
+
deployId,
|
|
250
|
+
env,
|
|
251
|
+
suffix: deploymentVersion,
|
|
252
|
+
replicas,
|
|
253
|
+
image,
|
|
254
|
+
namespace: options.namespace,
|
|
255
|
+
cmd: options.cmd ? options.cmd.split(',').map((c) => c.trim()) : undefined,
|
|
256
|
+
})
|
|
257
|
+
.replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
227
258
|
`;
|
|
228
259
|
}
|
|
229
260
|
fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
|
|
@@ -236,34 +267,63 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
236
267
|
|
|
237
268
|
for (const host of Object.keys(confServer)) {
|
|
238
269
|
if (env === 'production')
|
|
239
|
-
secretYaml +=
|
|
270
|
+
secretYaml += Underpost.deploy.buildCertManagerCertificate({ host, namespace: options.namespace });
|
|
240
271
|
|
|
241
272
|
const pathPortAssignment = pathPortAssignmentData[host];
|
|
242
273
|
// logger.info('', { host, pathPortAssignment });
|
|
243
|
-
let _proxyYaml =
|
|
274
|
+
let _proxyYaml = Underpost.deploy.baseProxyYamlFactory({ host, env, options });
|
|
244
275
|
const deploymentVersions =
|
|
245
276
|
options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'];
|
|
246
277
|
let proxyRoutes = '';
|
|
278
|
+
const globalTimeoutPolicy =
|
|
279
|
+
(options.timeoutResponse && options.timeoutResponse !== '') ||
|
|
280
|
+
(options.timeoutIdle && options.timeoutIdle !== '')
|
|
281
|
+
? {
|
|
282
|
+
response: options.timeoutResponse,
|
|
283
|
+
idle: options.timeoutIdle,
|
|
284
|
+
}
|
|
285
|
+
: undefined;
|
|
286
|
+
const globalRetryPolicy =
|
|
287
|
+
options.retryCount ||
|
|
288
|
+
options.retryCount === 0 ||
|
|
289
|
+
(options.retryPerTryTimeout && options.retryPerTryTimeout !== '')
|
|
290
|
+
? {
|
|
291
|
+
count: options.retryCount,
|
|
292
|
+
perTryTimeout: options.retryPerTryTimeout,
|
|
293
|
+
}
|
|
294
|
+
: undefined;
|
|
247
295
|
if (!options.disableDeploymentProxy)
|
|
248
296
|
for (const conditionObj of pathPortAssignment) {
|
|
249
297
|
const { path, port } = conditionObj;
|
|
250
|
-
proxyRoutes +=
|
|
298
|
+
proxyRoutes += Underpost.deploy.deploymentYamlServiceFactory({
|
|
251
299
|
path,
|
|
252
300
|
deployId,
|
|
253
301
|
env,
|
|
254
302
|
port,
|
|
255
303
|
deploymentVersions,
|
|
304
|
+
timeoutPolicy: globalTimeoutPolicy,
|
|
305
|
+
retryPolicy: globalRetryPolicy,
|
|
256
306
|
});
|
|
257
307
|
}
|
|
258
308
|
for (const customService of customServices) {
|
|
259
|
-
const {
|
|
309
|
+
const {
|
|
310
|
+
path: _path,
|
|
311
|
+
port,
|
|
312
|
+
serviceId,
|
|
313
|
+
host: _host,
|
|
314
|
+
pathRewritePolicy,
|
|
315
|
+
timeoutPolicy: _timeoutPolicy,
|
|
316
|
+
retryPolicy: _retryPolicy,
|
|
317
|
+
} = customService;
|
|
260
318
|
if (host === _host) {
|
|
261
|
-
proxyRoutes +=
|
|
319
|
+
proxyRoutes += Underpost.deploy.deploymentYamlServiceFactory({
|
|
262
320
|
path: _path,
|
|
263
321
|
port,
|
|
264
322
|
serviceId,
|
|
265
323
|
deploymentVersions,
|
|
266
324
|
pathRewritePolicy,
|
|
325
|
+
timeoutPolicy: _timeoutPolicy ? _timeoutPolicy : globalTimeoutPolicy,
|
|
326
|
+
retryPolicy: _retryPolicy ? _retryPolicy : globalRetryPolicy,
|
|
267
327
|
});
|
|
268
328
|
}
|
|
269
329
|
}
|
|
@@ -389,6 +449,10 @@ spec:
|
|
|
389
449
|
* @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
|
|
390
450
|
* @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
|
|
391
451
|
* @param {string} [options.namespace] - Kubernetes namespace for the deployment.
|
|
452
|
+
* @param {string} [options.timeoutResponse] - Timeout response setting for the deployment.
|
|
453
|
+
* @param {string} [options.timeoutIdle] - Timeout idle setting for the deployment.
|
|
454
|
+
* @param {string} [options.retryCount] - Retry count setting for the deployment.
|
|
455
|
+
* @param {string} [options.retryPerTryTimeout] - Retry per-try timeout setting for the deployment.
|
|
392
456
|
* @param {string} [options.kindType] - Type of Kubernetes resource to retrieve information for.
|
|
393
457
|
* @param {number} [options.port] - Port number for exposing the deployment.
|
|
394
458
|
* @param {string} [options.cmd] - Custom initialization command for deploymentYamlPartsFactory (comma-separated commands).
|
|
@@ -421,6 +485,10 @@ spec:
|
|
|
421
485
|
etcHosts: false,
|
|
422
486
|
disableUpdateUnderpostConfig: false,
|
|
423
487
|
namespace: '',
|
|
488
|
+
timeoutResponse: '',
|
|
489
|
+
timeoutIdle: '',
|
|
490
|
+
retryCount: '',
|
|
491
|
+
retryPerTryTimeout: '',
|
|
424
492
|
kindType: '',
|
|
425
493
|
port: 0,
|
|
426
494
|
cmd: '',
|
|
@@ -430,7 +498,7 @@ spec:
|
|
|
430
498
|
if (!deployList && options.certHosts) {
|
|
431
499
|
for (const host of options.certHosts.split(',')) {
|
|
432
500
|
shellExec(`sudo kubectl apply -f - -n ${namespace} <<EOF
|
|
433
|
-
${
|
|
501
|
+
${Underpost.deploy.buildCertManagerCertificate({ host, namespace })}
|
|
434
502
|
EOF`);
|
|
435
503
|
}
|
|
436
504
|
return;
|
|
@@ -453,26 +521,26 @@ EOF`);
|
|
|
453
521
|
path: instance.path,
|
|
454
522
|
fromPort: instance.fromPort,
|
|
455
523
|
toPort: instance.toPort,
|
|
456
|
-
traffic:
|
|
524
|
+
traffic: Underpost.deploy.getCurrentTraffic(_deployId, { namespace, hostTest: instance.host }),
|
|
457
525
|
});
|
|
458
526
|
}
|
|
459
527
|
}
|
|
460
528
|
logger.info('', {
|
|
461
529
|
deployId,
|
|
462
530
|
env,
|
|
463
|
-
traffic:
|
|
464
|
-
router: await
|
|
465
|
-
pods: await
|
|
531
|
+
traffic: Underpost.deploy.getCurrentTraffic(deployId, { namespace }),
|
|
532
|
+
router: await Underpost.deploy.routerFactory(deployId, env),
|
|
533
|
+
pods: await Underpost.deploy.get(deployId),
|
|
466
534
|
instances,
|
|
467
535
|
});
|
|
468
536
|
}
|
|
469
|
-
const interfaceName =
|
|
537
|
+
const interfaceName = Underpost.dns.getDefaultNetworkInterface();
|
|
470
538
|
logger.info('Machine', {
|
|
471
539
|
hostname: os.hostname(),
|
|
472
|
-
arch:
|
|
473
|
-
ipv4Public: await
|
|
474
|
-
ipv4Local: getLocalIPv4Address(),
|
|
475
|
-
resources:
|
|
540
|
+
arch: Underpost.baremetal.getHostArch(),
|
|
541
|
+
ipv4Public: await Underpost.dns.getPublicIp(),
|
|
542
|
+
ipv4Local: Underpost.dns.getLocalIPv4Address(),
|
|
543
|
+
resources: Underpost.cluster.getResourcesCapacity(options.node),
|
|
476
544
|
defaultInterfaceName: interfaceName,
|
|
477
545
|
defaultInterfaceInfo: os.networkInterfaces()[interfaceName],
|
|
478
546
|
});
|
|
@@ -484,16 +552,16 @@ EOF`);
|
|
|
484
552
|
getDataDeploy({
|
|
485
553
|
buildSingleReplica: true,
|
|
486
554
|
});
|
|
487
|
-
if (options.buildManifest === true) await
|
|
555
|
+
if (options.buildManifest === true) await Underpost.deploy.buildManifest(deployList, env, options);
|
|
488
556
|
if (options.infoRouter === true || options.buildManifest === true) {
|
|
489
|
-
logger.info('router', await
|
|
557
|
+
logger.info('router', await Underpost.deploy.routerFactory(deployList, env));
|
|
490
558
|
return;
|
|
491
559
|
}
|
|
492
|
-
if (!options.disableUpdateUnderpostConfig)
|
|
560
|
+
if (!options.disableUpdateUnderpostConfig) Underpost.deploy.configMap(env);
|
|
493
561
|
let renderHosts = '';
|
|
494
562
|
let etcHosts = [];
|
|
495
563
|
if (options.restoreHosts === true) {
|
|
496
|
-
const factoryResult =
|
|
564
|
+
const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
|
|
497
565
|
renderHosts = factoryResult.renderHosts;
|
|
498
566
|
logger.info(renderHosts);
|
|
499
567
|
return;
|
|
@@ -504,7 +572,7 @@ EOF`);
|
|
|
504
572
|
if (!deployId) continue;
|
|
505
573
|
if (options.expose === true) {
|
|
506
574
|
const kindType = options.kindType ? options.kindType : 'svc';
|
|
507
|
-
const svc =
|
|
575
|
+
const svc = Underpost.deploy.get(deployId, kindType)[0];
|
|
508
576
|
const port = options.port
|
|
509
577
|
? options.port
|
|
510
578
|
: kindType !== 'svc'
|
|
@@ -535,7 +603,7 @@ EOF`);
|
|
|
535
603
|
);
|
|
536
604
|
if (!options.disableUpdateVolume)
|
|
537
605
|
for (const volume of confVolume)
|
|
538
|
-
|
|
606
|
+
Underpost.deploy.deployVolume(volume, {
|
|
539
607
|
deployId,
|
|
540
608
|
env,
|
|
541
609
|
version,
|
|
@@ -547,7 +615,7 @@ EOF`);
|
|
|
547
615
|
for (const host of Object.keys(confServer)) {
|
|
548
616
|
if (!options.disableUpdateProxy) {
|
|
549
617
|
shellExec(`sudo kubectl delete HTTPProxy ${host} -n ${namespace} --ignore-not-found`);
|
|
550
|
-
if (
|
|
618
|
+
if (Underpost.deploy.isValidTLSContext({ host, env, options }))
|
|
551
619
|
shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
|
|
552
620
|
}
|
|
553
621
|
if (!options.remove) etcHosts.push(host);
|
|
@@ -564,12 +632,12 @@ EOF`);
|
|
|
564
632
|
if (!options.disableUpdateProxy)
|
|
565
633
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
|
|
566
634
|
|
|
567
|
-
if (
|
|
635
|
+
if (Underpost.deploy.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }))
|
|
568
636
|
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
|
|
569
637
|
}
|
|
570
638
|
}
|
|
571
639
|
if (options.etcHosts === true) {
|
|
572
|
-
const factoryResult =
|
|
640
|
+
const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
|
|
573
641
|
renderHosts = factoryResult.renderHosts;
|
|
574
642
|
}
|
|
575
643
|
if (renderHosts)
|
|
@@ -667,7 +735,7 @@ EOF`);
|
|
|
667
735
|
*/
|
|
668
736
|
async checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = [], namespace = 'default') {
|
|
669
737
|
const cmd = `underpost config get container-status`;
|
|
670
|
-
const pods =
|
|
738
|
+
const pods = Underpost.deploy.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
|
|
671
739
|
const readyPods = [];
|
|
672
740
|
const notReadyPods = [];
|
|
673
741
|
for (const pod of pods) {
|
|
@@ -711,14 +779,35 @@ EOF`);
|
|
|
711
779
|
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
712
780
|
* @param {number} replicas - Number of replicas for the deployment.
|
|
713
781
|
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
782
|
+
* @param {object} options - Options for the traffic switch.
|
|
783
|
+
* @param {string} options.timeoutResponse - Timeout response setting for the deployment.
|
|
784
|
+
* @param {string} options.timeoutIdle - Timeout idle setting for the deployment.
|
|
785
|
+
* @param {string} options.retryCount - Retry count setting for the deployment.
|
|
786
|
+
* @param {string} options.retryPerTryTimeout - Retry per-try timeout setting for the deployment.
|
|
714
787
|
* @memberof UnderpostDeploy
|
|
715
788
|
*/
|
|
716
|
-
switchTraffic(
|
|
717
|
-
|
|
789
|
+
switchTraffic(
|
|
790
|
+
deployId,
|
|
791
|
+
env,
|
|
792
|
+
targetTraffic,
|
|
793
|
+
replicas = 1,
|
|
794
|
+
namespace = 'default',
|
|
795
|
+
options = {
|
|
796
|
+
timeoutResponse: '',
|
|
797
|
+
timeoutIdle: '',
|
|
798
|
+
retryCount: '',
|
|
799
|
+
retryPerTryTimeout: '',
|
|
800
|
+
},
|
|
801
|
+
) {
|
|
802
|
+
const timeoutFlags = Underpost.deploy.timeoutFlagsFactory(options);
|
|
803
|
+
|
|
718
804
|
shellExec(
|
|
719
|
-
`node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} --namespace ${namespace} ${deployId} ${env}`,
|
|
805
|
+
`node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} --namespace ${namespace}${timeoutFlags} ${deployId} ${env}`,
|
|
720
806
|
);
|
|
807
|
+
|
|
721
808
|
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
|
|
809
|
+
|
|
810
|
+
Underpost.env.set(`${deployId}-${env}-traffic`, targetTraffic);
|
|
722
811
|
},
|
|
723
812
|
|
|
724
813
|
/**
|
|
@@ -767,7 +856,7 @@ EOF`);
|
|
|
767
856
|
shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
|
|
768
857
|
shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
|
|
769
858
|
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
770
|
-
${
|
|
859
|
+
${Underpost.deploy.persistentVolumeFactory({
|
|
771
860
|
hostPath: rootVolumeHostPath,
|
|
772
861
|
pvcId,
|
|
773
862
|
})}
|
|
@@ -934,13 +1023,7 @@ ${renderHosts}`,
|
|
|
934
1023
|
);
|
|
935
1024
|
break;
|
|
936
1025
|
}
|
|
937
|
-
result = await
|
|
938
|
-
deployId,
|
|
939
|
-
env,
|
|
940
|
-
targetTraffic,
|
|
941
|
-
ignorePods,
|
|
942
|
-
namespace,
|
|
943
|
-
);
|
|
1026
|
+
result = await Underpost.deploy.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods, namespace);
|
|
944
1027
|
if (result.ready === true) {
|
|
945
1028
|
readyOk++;
|
|
946
1029
|
logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
|
|
@@ -1137,7 +1220,7 @@ ${renderHosts}`,
|
|
|
1137
1220
|
) => {
|
|
1138
1221
|
if (resources) {
|
|
1139
1222
|
if (resources.resourceTemplateId)
|
|
1140
|
-
return
|
|
1223
|
+
return Underpost.deploy.resourcesTemplate[resources.resourceTemplateId].resources;
|
|
1141
1224
|
if (resources.requestsMemory && resources.requestsCpu && resources.limitsMemory && resources.limitsCpu)
|
|
1142
1225
|
return {
|
|
1143
1226
|
requests: {
|
|
@@ -1152,6 +1235,25 @@ ${renderHosts}`,
|
|
|
1152
1235
|
}
|
|
1153
1236
|
return undefined;
|
|
1154
1237
|
},
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Generates timeout flags string for deployment commands.
|
|
1241
|
+
* @param {object} options - Options containing timeout settings.
|
|
1242
|
+
* @param {string|number} [options.timeoutResponse] - Timeout response value.
|
|
1243
|
+
* @param {string|number} [options.timeoutIdle] - Timeout idle value.
|
|
1244
|
+
* @param {string|number} [options.retryCount] - Retry count value.
|
|
1245
|
+
* @param {string|number} [options.retryPerTryTimeout] - Retry per try timeout value.
|
|
1246
|
+
* @returns {string} The timeout flags string.
|
|
1247
|
+
* @memberof UnderpostDeploy
|
|
1248
|
+
*/
|
|
1249
|
+
timeoutFlagsFactory: (options = {}) => {
|
|
1250
|
+
return (
|
|
1251
|
+
`${options.timeoutResponse ? ` --timeout-response ${options.timeoutResponse}` : ''}` +
|
|
1252
|
+
`${options.timeoutIdle ? ` --timeout-idle ${options.timeoutIdle}` : ''}` +
|
|
1253
|
+
`${options.retryCount || options.retryCount === 0 ? ` --retry-count ${options.retryCount}` : ''}` +
|
|
1254
|
+
`${options.retryPerTryTimeout ? ` --retry-per-try-timeout ${options.retryPerTryTimeout}` : ''}`
|
|
1255
|
+
);
|
|
1256
|
+
},
|
|
1155
1257
|
};
|
|
1156
1258
|
}
|
|
1157
1259
|
|
package/src/cli/env.js
CHANGED
|
@@ -25,15 +25,30 @@ class UnderpostRootEnv {
|
|
|
25
25
|
* @description Sets an environment variable in the underpost root environment.
|
|
26
26
|
* @param {string} key - The key of the environment variable to set.
|
|
27
27
|
* @param {string} value - The value of the environment variable to set.
|
|
28
|
+
* @param {object} options - Options for setting the environment variable.
|
|
29
|
+
* @param {string} [options.deployId=''] - Deployment ID associated with the environment variable.
|
|
30
|
+
* @param {boolean} [options.build=false] - If true, triggers a build after setting the environment variable.
|
|
28
31
|
* @memberof UnderpostEnv
|
|
29
32
|
*/
|
|
30
|
-
set(key, value) {
|
|
33
|
+
set(key, value, options = { deployId: '', build: false }) {
|
|
34
|
+
const _set = (envPath, key, value) => {
|
|
35
|
+
let env = {};
|
|
36
|
+
if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
37
|
+
env[key] = value;
|
|
38
|
+
writeEnv(envPath, env);
|
|
39
|
+
};
|
|
40
|
+
if (options.build) {
|
|
41
|
+
const deployIdList = options.deployId
|
|
42
|
+
? [options.deployId]
|
|
43
|
+
: fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
|
|
44
|
+
for (const deployId of deployIdList)
|
|
45
|
+
for (const envFile of ['test', 'development', 'production'])
|
|
46
|
+
_set(`./engine-private/conf/${deployId}/.env.${envFile}`, key, value);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
31
49
|
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
32
50
|
const envPath = `${exeRootPath}/.env`;
|
|
33
|
-
|
|
34
|
-
if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
35
|
-
env[key] = value;
|
|
36
|
-
writeEnv(envPath, env);
|
|
51
|
+
_set(envPath, key, value);
|
|
37
52
|
},
|
|
38
53
|
/**
|
|
39
54
|
* @method delete
|
package/src/cli/fs.js
CHANGED
|
@@ -11,8 +11,8 @@ import AdmZip from 'adm-zip';
|
|
|
11
11
|
import * as dir from 'path';
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
13
|
import Downloader from '../server/downloader.js';
|
|
14
|
-
import UnderpostRepository from './repository.js';
|
|
15
14
|
import { shellExec } from '../server/process.js';
|
|
15
|
+
import Underpost from '../index.js';
|
|
16
16
|
dotenv.config();
|
|
17
17
|
|
|
18
18
|
const logger = loggerFactory(import.meta);
|
|
@@ -93,12 +93,12 @@ class UnderpostFileStorage {
|
|
|
93
93
|
storageFilePath: '',
|
|
94
94
|
},
|
|
95
95
|
) {
|
|
96
|
-
const { storage, storageConf } =
|
|
97
|
-
const deleteFiles = options.pull === true ? [] :
|
|
96
|
+
const { storage, storageConf } = Underpost.fs.getStorageConf(options);
|
|
97
|
+
const deleteFiles = options.pull === true ? [] : Underpost.repo.getDeleteFiles(path);
|
|
98
98
|
for (const relativePath of deleteFiles) {
|
|
99
99
|
const _path = path + '/' + relativePath;
|
|
100
100
|
if (_path in storage) {
|
|
101
|
-
await
|
|
101
|
+
await Underpost.fs.delete(_path);
|
|
102
102
|
delete storage[_path];
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -106,27 +106,25 @@ class UnderpostFileStorage {
|
|
|
106
106
|
for (const _path of Object.keys(storage)) {
|
|
107
107
|
if (!fs.existsSync(_path) || options.force === true) {
|
|
108
108
|
if (options.force === true && fs.existsSync(_path)) fs.removeSync(_path);
|
|
109
|
-
await
|
|
109
|
+
await Underpost.fs.pull(_path, options);
|
|
110
110
|
} else logger.warn(`Pull path already exists`, _path);
|
|
111
111
|
}
|
|
112
112
|
shellExec(`cd ${path} && git init && git add . && git commit -m "Base pull state"`);
|
|
113
113
|
} else {
|
|
114
114
|
const files =
|
|
115
|
-
options.git === true
|
|
116
|
-
? UnderpostRepository.API.getChangedFiles(path)
|
|
117
|
-
: await fs.readdir(path, { recursive: true });
|
|
115
|
+
options.git === true ? Underpost.repo.getChangedFiles(path) : await fs.readdir(path, { recursive: true });
|
|
118
116
|
for (const relativePath of files) {
|
|
119
117
|
const _path = path + '/' + relativePath;
|
|
120
118
|
if (fs.statSync(_path).isDirectory()) {
|
|
121
119
|
if (options.pull === true && !fs.existsSync(_path)) fs.mkdirSync(_path, { recursive: true });
|
|
122
120
|
continue;
|
|
123
121
|
} else if (!(_path in storage) || options.force === true) {
|
|
124
|
-
await
|
|
122
|
+
await Underpost.fs.upload(_path, options);
|
|
125
123
|
if (storage) storage[_path] = {};
|
|
126
124
|
} else logger.warn('File already exists', _path);
|
|
127
125
|
}
|
|
128
126
|
}
|
|
129
|
-
|
|
127
|
+
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
130
128
|
if (options.git === true) {
|
|
131
129
|
shellExec(`cd ${path} && git add .`);
|
|
132
130
|
shellExec(`underpost cmt ${path} feat`);
|
|
@@ -152,10 +150,10 @@ class UnderpostFileStorage {
|
|
|
152
150
|
options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
|
|
153
151
|
) {
|
|
154
152
|
if (options.recursive === true || options.git === true)
|
|
155
|
-
return await
|
|
156
|
-
if (options.pull === true) return await
|
|
157
|
-
if (options.rm === true) return await
|
|
158
|
-
return await
|
|
153
|
+
return await Underpost.fs.recursiveCallback(path, options);
|
|
154
|
+
if (options.pull === true) return await Underpost.fs.pull(path, options);
|
|
155
|
+
if (options.rm === true) return await Underpost.fs.delete(path, options);
|
|
156
|
+
return await Underpost.fs.upload(path, options);
|
|
159
157
|
},
|
|
160
158
|
/**
|
|
161
159
|
* @method upload
|
|
@@ -171,9 +169,9 @@ class UnderpostFileStorage {
|
|
|
171
169
|
path,
|
|
172
170
|
options = { rm: false, recursive: false, deployId: '', force: false, pull: false, storageFilePath: '' },
|
|
173
171
|
) {
|
|
174
|
-
|
|
175
|
-
const { storage, storageConf } =
|
|
176
|
-
// path =
|
|
172
|
+
Underpost.fs.cloudinaryConfig();
|
|
173
|
+
const { storage, storageConf } = Underpost.fs.getStorageConf(options);
|
|
174
|
+
// path = Underpost.fs.file2Zip(path);
|
|
177
175
|
const uploadResult = await cloudinary.uploader
|
|
178
176
|
.upload(path, {
|
|
179
177
|
public_id: path,
|
|
@@ -185,7 +183,7 @@ class UnderpostFileStorage {
|
|
|
185
183
|
});
|
|
186
184
|
logger.info('upload result', uploadResult);
|
|
187
185
|
if (storage) storage[path] = {};
|
|
188
|
-
|
|
186
|
+
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
189
187
|
return uploadResult;
|
|
190
188
|
},
|
|
191
189
|
/**
|
|
@@ -196,7 +194,7 @@ class UnderpostFileStorage {
|
|
|
196
194
|
* @memberof UnderpostFileStorage
|
|
197
195
|
*/
|
|
198
196
|
async pull(path) {
|
|
199
|
-
|
|
197
|
+
Underpost.fs.cloudinaryConfig();
|
|
200
198
|
const folder = dir.dirname(path);
|
|
201
199
|
if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
|
|
202
200
|
const downloadResult = await cloudinary.utils.download_archive_url({
|
|
@@ -205,11 +203,11 @@ class UnderpostFileStorage {
|
|
|
205
203
|
});
|
|
206
204
|
logger.info('download result', downloadResult);
|
|
207
205
|
await Downloader.downloadFile(downloadResult, path + '.zip');
|
|
208
|
-
path =
|
|
206
|
+
path = Underpost.fs.zip2File(path + '.zip');
|
|
209
207
|
fs.removeSync(path + '.zip');
|
|
210
208
|
},
|
|
211
209
|
async delete(path) {
|
|
212
|
-
|
|
210
|
+
Underpost.fs.cloudinaryConfig();
|
|
213
211
|
const deleteResult = await cloudinary.api
|
|
214
212
|
.delete_resources([path], { type: 'upload', resource_type: 'raw' })
|
|
215
213
|
.catch((error) => {
|