underpost 2.89.45 → 2.90.1
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.production +2 -0
- package/README.md +3 -2
- package/bin/build.js +4 -0
- package/cli.md +73 -42
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +8 -24
- package/manifests/lxd/underpost-setup.sh +1 -1
- package/package.json +1 -1
- package/scripts/nvim.sh +1 -1
- package/src/cli/cluster.js +7 -21
- package/src/cli/db.js +34 -10
- package/src/cli/deploy.js +280 -116
- package/src/cli/index.js +27 -2
- package/src/cli/monitor.js +1 -1
- package/src/cli/run.js +216 -12
- package/src/cli/static.js +84 -0
- package/src/client/components/core/CalendarCore.js +1 -7
- package/src/client/components/core/Modal.js +2 -2
- package/src/client/components/core/Panel.js +1 -1
- package/src/client/components/core/PanelForm.js +1 -5
- package/src/client/components/default/MenuDefault.js +5 -30
- package/src/index.js +10 -1
- package/src/server/client-build.js +9 -33
- package/src/server/json-schema.js +0 -77
package/src/cli/deploy.js
CHANGED
|
@@ -37,36 +37,6 @@ const logger = loggerFactory(import.meta);
|
|
|
37
37
|
class UnderpostDeploy {
|
|
38
38
|
static NETWORK = {};
|
|
39
39
|
static API = {
|
|
40
|
-
/**
|
|
41
|
-
* Synchronizes deployment configurations for a list of deployments.
|
|
42
|
-
* @param {string} deployList - List of deployment IDs to synchronize.
|
|
43
|
-
* @param {object} options - Options for the synchronization process.
|
|
44
|
-
* @param {string} options.versions - Comma-separated list of versions to deploy.
|
|
45
|
-
* @param {string} options.replicas - Number of replicas for each deployment.
|
|
46
|
-
* @param {string} options.node - Node name for resource allocation.
|
|
47
|
-
* @returns {object} - Deployment data for the specified deployments.
|
|
48
|
-
* @memberof UnderpostDeploy
|
|
49
|
-
*/
|
|
50
|
-
sync(deployList, { versions, replicas, node }) {
|
|
51
|
-
fs.writeFileSync(`./engine-private/deploy/dd.router`, deployList, 'utf8');
|
|
52
|
-
const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
|
|
53
|
-
const limitFactor = 0.8;
|
|
54
|
-
const reserveFactor = 0.05;
|
|
55
|
-
const resources = UnderpostCluster.API.getResourcesCapacity(node);
|
|
56
|
-
const memory = parseInt(resources.memory.value / totalPods);
|
|
57
|
-
const cpu = parseInt(resources.cpu.value / totalPods);
|
|
58
|
-
UnderpostRootEnv.API.set(
|
|
59
|
-
'resources.requests.memory',
|
|
60
|
-
`${parseInt(memory * reserveFactor)}${resources.memory.unit}`,
|
|
61
|
-
);
|
|
62
|
-
UnderpostRootEnv.API.set('resources.requests.cpu', `${parseInt(cpu * reserveFactor)}${resources.cpu.unit}`);
|
|
63
|
-
UnderpostRootEnv.API.set('resources.limits.memory', `${parseInt(memory * limitFactor)}${resources.memory.unit}`);
|
|
64
|
-
UnderpostRootEnv.API.set('resources.limits.cpu', `${parseInt(cpu * limitFactor)}${resources.cpu.unit}`);
|
|
65
|
-
UnderpostRootEnv.API.set('total-pods', totalPods);
|
|
66
|
-
return getDataDeploy({
|
|
67
|
-
buildSingleReplica: true,
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
40
|
/**
|
|
71
41
|
* Creates a router configuration for a list of deployments.
|
|
72
42
|
* @param {string} deployList - List of deployment IDs to include in the router.
|
|
@@ -130,18 +100,28 @@ class UnderpostDeploy {
|
|
|
130
100
|
* @param {number} replicas - Number of replicas for the deployment.
|
|
131
101
|
* @param {string} image - Docker image for the deployment.
|
|
132
102
|
* @param {string} namespace - Kubernetes namespace for the deployment.
|
|
103
|
+
* @param {Array<object>} volumes - Volume configurations for the deployment.
|
|
104
|
+
* @param {Array<string>} cmd - Command to run in the deployment container.
|
|
133
105
|
* @returns {string} - YAML deployment configuration for the specified deployment.
|
|
134
106
|
* @memberof UnderpostDeploy
|
|
135
107
|
*/
|
|
136
|
-
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image, namespace }) {
|
|
108
|
+
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image, namespace, volumes, cmd }) {
|
|
109
|
+
if (!cmd)
|
|
110
|
+
cmd = [
|
|
111
|
+
`npm install -g npm@11.2.0`,
|
|
112
|
+
`npm install -g underpost`,
|
|
113
|
+
`underpost secret underpost --create-from-file /etc/config/.env.${env}`,
|
|
114
|
+
`underpost start --build --run ${deployId} ${env}`,
|
|
115
|
+
];
|
|
137
116
|
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
117
|
+
if (!volumes)
|
|
118
|
+
volumes = [
|
|
119
|
+
{
|
|
120
|
+
volumeMountPath: '/etc/config',
|
|
121
|
+
volumeName: 'config-volume',
|
|
122
|
+
configMap: 'underpost-config',
|
|
123
|
+
},
|
|
124
|
+
];
|
|
145
125
|
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
146
126
|
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
|
|
147
127
|
: [];
|
|
@@ -166,21 +146,23 @@ spec:
|
|
|
166
146
|
containers:
|
|
167
147
|
- name: ${deployId}-${env}-${suffix}
|
|
168
148
|
image: ${image ?? `localhost/rockylinux9-underpost:v${packageJson.version}`}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
149
|
+
${
|
|
150
|
+
resources
|
|
151
|
+
? ` resources:
|
|
152
|
+
requests:
|
|
153
|
+
memory: "${resources.requests.memory}"
|
|
154
|
+
cpu: "${resources.requests.cpu}"
|
|
155
|
+
limits:
|
|
156
|
+
memory: "${resources.limits.memory}"
|
|
157
|
+
cpu: "${resources.limits.cpu}"`
|
|
158
|
+
: ''
|
|
159
|
+
}
|
|
176
160
|
command:
|
|
177
161
|
- /bin/sh
|
|
178
162
|
- -c
|
|
179
163
|
- >
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
underpost secret underpost --create-from-file /etc/config/.env.${env} &&
|
|
183
|
-
underpost start --build --run ${deployId} ${env}
|
|
164
|
+
${cmd.join(` && `)}
|
|
165
|
+
|
|
184
166
|
${UnderpostDeploy.API.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
|
|
185
167
|
.render.split(`\n`)
|
|
186
168
|
.map((l) => ' ' + l)
|
|
@@ -209,7 +191,6 @@ spec:
|
|
|
209
191
|
* @memberof UnderpostDeploy
|
|
210
192
|
*/
|
|
211
193
|
async buildManifest(deployList, env, options) {
|
|
212
|
-
const resources = UnderpostDeploy.API.resourcesFactory();
|
|
213
194
|
const replicas = options.replicas;
|
|
214
195
|
const image = options.image;
|
|
215
196
|
if (!options.namespace) options.namespace = 'default';
|
|
@@ -237,7 +218,6 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
237
218
|
deployId,
|
|
238
219
|
env,
|
|
239
220
|
suffix: deploymentVersion,
|
|
240
|
-
resources,
|
|
241
221
|
replicas,
|
|
242
222
|
image,
|
|
243
223
|
namespace: options.namespace,
|
|
@@ -253,27 +233,12 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
253
233
|
: [];
|
|
254
234
|
|
|
255
235
|
for (const host of Object.keys(confServer)) {
|
|
256
|
-
if (env === 'production')
|
|
236
|
+
if (env === 'production')
|
|
237
|
+
secretYaml += UnderpostDeploy.API.buildCertManagerCertificate({ host, namespace: options.namespace });
|
|
257
238
|
|
|
258
239
|
const pathPortAssignment = pathPortAssignmentData[host];
|
|
259
240
|
// logger.info('', { host, pathPortAssignment });
|
|
260
|
-
let _proxyYaml =
|
|
261
|
-
---
|
|
262
|
-
apiVersion: projectcontour.io/v1
|
|
263
|
-
kind: HTTPProxy
|
|
264
|
-
metadata:
|
|
265
|
-
name: ${host}
|
|
266
|
-
namespace: ${options.namespace}
|
|
267
|
-
spec:
|
|
268
|
-
virtualhost:
|
|
269
|
-
fqdn: ${host}${
|
|
270
|
-
env === 'development'
|
|
271
|
-
? ''
|
|
272
|
-
: `
|
|
273
|
-
tls:
|
|
274
|
-
secretName: ${host}`
|
|
275
|
-
}
|
|
276
|
-
routes:`;
|
|
241
|
+
let _proxyYaml = UnderpostDeploy.API.baseProxyYamlFactory({ host, env, options });
|
|
277
242
|
const deploymentVersions =
|
|
278
243
|
options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'];
|
|
279
244
|
let proxyRoutes = '';
|
|
@@ -347,18 +312,54 @@ spec:
|
|
|
347
312
|
/**
|
|
348
313
|
* Retrieves the current traffic status for a deployment.
|
|
349
314
|
* @param {string} deployId - Deployment ID for which the traffic status is being retrieved.
|
|
315
|
+
* @param {object} options - Options for the traffic retrieval.
|
|
316
|
+
* @param {string} options.hostTest - Hostname to test for traffic status.
|
|
317
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
350
318
|
* @returns {string|null} - Current traffic status ('blue' or 'green') or null if not found.
|
|
351
319
|
* @memberof UnderpostDeploy
|
|
352
320
|
*/
|
|
353
|
-
getCurrentTraffic(deployId) {
|
|
321
|
+
getCurrentTraffic(deployId, options = { hostTest: '', namespace: '' }) {
|
|
322
|
+
if (!options.namespace) options.namespace = 'default';
|
|
354
323
|
// kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
|
|
355
|
-
const hostTest =
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -o yaml`, {
|
|
324
|
+
const hostTest = options?.hostTest
|
|
325
|
+
? options.hostTest
|
|
326
|
+
: Object.keys(JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')))[0];
|
|
327
|
+
const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -n ${options.namespace} -o yaml`, {
|
|
328
|
+
silent: true,
|
|
329
|
+
stdout: true,
|
|
330
|
+
});
|
|
359
331
|
return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
|
|
360
332
|
},
|
|
361
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Creates a base YAML configuration for an HTTPProxy resource.
|
|
336
|
+
* @param {string} host - Hostname for which the HTTPProxy is being created.
|
|
337
|
+
* @param {string} env - Environment for which the HTTPProxy is being created.
|
|
338
|
+
* @param {object} options - Options for the HTTPProxy creation.
|
|
339
|
+
* @param {string} options.namespace - Kubernetes namespace for the HTTPProxy.
|
|
340
|
+
* @returns {string} - Base YAML configuration for the HTTPProxy resource.
|
|
341
|
+
* @memberof UnderpostDeploy
|
|
342
|
+
*/
|
|
343
|
+
baseProxyYamlFactory({ host, env, options }) {
|
|
344
|
+
return `
|
|
345
|
+
---
|
|
346
|
+
apiVersion: projectcontour.io/v1
|
|
347
|
+
kind: HTTPProxy
|
|
348
|
+
metadata:
|
|
349
|
+
name: ${host}
|
|
350
|
+
namespace: ${options.namespace}
|
|
351
|
+
spec:
|
|
352
|
+
virtualhost:
|
|
353
|
+
fqdn: ${host}${
|
|
354
|
+
env === 'development'
|
|
355
|
+
? ''
|
|
356
|
+
: `
|
|
357
|
+
tls:
|
|
358
|
+
secretName: ${host}`
|
|
359
|
+
}
|
|
360
|
+
routes:`;
|
|
361
|
+
},
|
|
362
|
+
|
|
362
363
|
/**
|
|
363
364
|
* Callback function for handling deployment options.
|
|
364
365
|
* @param {string} deployList - List of deployment IDs to process.
|
|
@@ -431,21 +432,39 @@ EOF`);
|
|
|
431
432
|
if (options.status === true) {
|
|
432
433
|
for (const _deployId of deployList.split(',')) {
|
|
433
434
|
const deployId = _deployId.trim();
|
|
435
|
+
const instances = [];
|
|
436
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/conf.instances.json`)) {
|
|
437
|
+
const confInstances = JSON.parse(
|
|
438
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'),
|
|
439
|
+
);
|
|
440
|
+
for (const instance of confInstances) {
|
|
441
|
+
const _deployId = `${deployId}-${instance.id}`;
|
|
442
|
+
instances.push({
|
|
443
|
+
id: instance.id,
|
|
444
|
+
host: instance.host,
|
|
445
|
+
path: instance.path,
|
|
446
|
+
fromPort: instance.fromPort,
|
|
447
|
+
toPort: instance.toPort,
|
|
448
|
+
traffic: UnderpostDeploy.API.getCurrentTraffic(_deployId, { namespace, hostTest: instance.host }),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
434
452
|
logger.info('', {
|
|
435
453
|
deployId,
|
|
436
454
|
env,
|
|
437
|
-
traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
|
|
455
|
+
traffic: UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace }),
|
|
438
456
|
router: await UnderpostDeploy.API.routerFactory(deployId, env),
|
|
439
457
|
pods: await UnderpostDeploy.API.get(deployId),
|
|
458
|
+
instances,
|
|
440
459
|
});
|
|
441
460
|
}
|
|
442
461
|
const interfaceName = Dns.getDefaultNetworkInterface();
|
|
443
462
|
logger.info('Machine', {
|
|
444
|
-
|
|
463
|
+
hostname: os.hostname(),
|
|
445
464
|
arch: UnderpostBaremetal.API.getHostArch(),
|
|
446
465
|
ipv4Public: await Dns.getPublicIp(),
|
|
447
466
|
ipv4Local: getLocalIPv4Address(),
|
|
448
|
-
resources: UnderpostCluster.API.getResourcesCapacity(),
|
|
467
|
+
resources: UnderpostCluster.API.getResourcesCapacity(options.node),
|
|
449
468
|
defaultInterfaceName: interfaceName,
|
|
450
469
|
defaultInterfaceInfo: os.networkInterfaces()[interfaceName],
|
|
451
470
|
});
|
|
@@ -453,7 +472,10 @@ EOF`);
|
|
|
453
472
|
}
|
|
454
473
|
if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
|
|
455
474
|
if (!options.replicas) options.replicas = 1;
|
|
456
|
-
if (options.sync)
|
|
475
|
+
if (options.sync)
|
|
476
|
+
getDataDeploy({
|
|
477
|
+
buildSingleReplica: true,
|
|
478
|
+
});
|
|
457
479
|
if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
|
|
458
480
|
if (options.infoRouter === true || options.buildManifest === true) {
|
|
459
481
|
logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
|
|
@@ -496,24 +518,15 @@ EOF`);
|
|
|
496
518
|
shellExec(
|
|
497
519
|
`sudo kubectl delete deployment ${deployId}-${env}-${version} -n ${namespace} --ignore-not-found`,
|
|
498
520
|
);
|
|
499
|
-
if (!options.disableUpdateVolume)
|
|
500
|
-
for (const volume of confVolume)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
509
|
-
${UnderpostDeploy.API.persistentVolumeFactory({
|
|
510
|
-
hostPath: rootVolumeHostPath,
|
|
511
|
-
pvcId,
|
|
512
|
-
})}
|
|
513
|
-
EOF
|
|
514
|
-
`);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
521
|
+
if (!options.disableUpdateVolume)
|
|
522
|
+
for (const volume of confVolume)
|
|
523
|
+
UnderpostDeploy.API.deployVolume(volume, {
|
|
524
|
+
deployId,
|
|
525
|
+
env,
|
|
526
|
+
version,
|
|
527
|
+
namespace,
|
|
528
|
+
nodeName: options.node ? options.node : env === 'development' ? 'kind-worker' : os.hostname(),
|
|
529
|
+
});
|
|
517
530
|
}
|
|
518
531
|
|
|
519
532
|
for (const host of Object.keys(confServer)) {
|
|
@@ -592,24 +605,7 @@ EOF
|
|
|
592
605
|
|
|
593
606
|
return result;
|
|
594
607
|
},
|
|
595
|
-
|
|
596
|
-
* Retrieves the resources factory for a deployment.
|
|
597
|
-
* @returns {object} - Object containing the resources factory for the deployment.
|
|
598
|
-
* @memberof UnderpostDeploy
|
|
599
|
-
*/
|
|
600
|
-
resourcesFactory() {
|
|
601
|
-
return {
|
|
602
|
-
requests: {
|
|
603
|
-
memory: UnderpostRootEnv.API.get('resources.requests.memory'),
|
|
604
|
-
cpu: UnderpostRootEnv.API.get('resources.requests.cpu'),
|
|
605
|
-
},
|
|
606
|
-
limits: {
|
|
607
|
-
memory: UnderpostRootEnv.API.get('resources.limits.memory'),
|
|
608
|
-
cpu: UnderpostRootEnv.API.get('resources.limits.cpu'),
|
|
609
|
-
},
|
|
610
|
-
totalPods: UnderpostRootEnv.API.get('total-pods'),
|
|
611
|
-
};
|
|
612
|
-
},
|
|
608
|
+
|
|
613
609
|
/**
|
|
614
610
|
* Checks if a container file exists in a pod.
|
|
615
611
|
* @param {object} options - Options for the check.
|
|
@@ -706,6 +702,60 @@ EOF
|
|
|
706
702
|
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
|
|
707
703
|
},
|
|
708
704
|
|
|
705
|
+
/**
|
|
706
|
+
* Deploys a volume for a deployment.
|
|
707
|
+
* @param {object} volume - Volume configuration.
|
|
708
|
+
* @param {string} volume.claimName - Name of the persistent volume claim.
|
|
709
|
+
* @param {string} volume.volumeMountPath - Mount path of the volume in the container.
|
|
710
|
+
* @param {string} volume.volumeName - Name of the volume.
|
|
711
|
+
* @param {object} options - Options for the volume deployment.
|
|
712
|
+
* @param {string} options.deployId - Deployment ID.
|
|
713
|
+
* @param {string} options.env - Environment for the deployment.
|
|
714
|
+
* @param {string} options.version - Version of the deployment.
|
|
715
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
716
|
+
* @param {string} options.nodeName - Node name for the deployment.
|
|
717
|
+
* @memberof UnderpostDeploy
|
|
718
|
+
*/
|
|
719
|
+
deployVolume(
|
|
720
|
+
volume = { claimName: '', volumeMountPath: '', volumeName: '' },
|
|
721
|
+
options = {
|
|
722
|
+
deployId: '',
|
|
723
|
+
env: '',
|
|
724
|
+
version: '',
|
|
725
|
+
namespace: '',
|
|
726
|
+
nodeName: '',
|
|
727
|
+
},
|
|
728
|
+
) {
|
|
729
|
+
if (!volume.claimName) {
|
|
730
|
+
logger.warn('Volume claimName is required to deploy volume', volume);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const { deployId, env, version, namespace } = options;
|
|
734
|
+
const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
|
|
735
|
+
const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
|
|
736
|
+
const rootVolumeHostPath = `/home/dd/engine/volume/${pvId}`;
|
|
737
|
+
if (options.nodeName) {
|
|
738
|
+
if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
|
|
739
|
+
fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
|
|
740
|
+
} else {
|
|
741
|
+
shellExec(`docker exec -i kind-worker bash -c "mkdir -p ${rootVolumeHostPath}"`);
|
|
742
|
+
// shellExec(`docker cp ${volume.volumeMountPath} kind-worker:${rootVolumeHostPath}`);
|
|
743
|
+
shellExec(`tar -C ${volume.volumeMountPath} -c . | docker cp - kind-worker:${rootVolumeHostPath}`);
|
|
744
|
+
shellExec(
|
|
745
|
+
`docker exec -i kind-worker bash -c "chown -R 1000:1000 ${rootVolumeHostPath} || true; chmod -R 755 ${rootVolumeHostPath}"`,
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
|
|
749
|
+
shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
|
|
750
|
+
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
751
|
+
${UnderpostDeploy.API.persistentVolumeFactory({
|
|
752
|
+
hostPath: rootVolumeHostPath,
|
|
753
|
+
pvcId,
|
|
754
|
+
})}
|
|
755
|
+
EOF
|
|
756
|
+
`);
|
|
757
|
+
},
|
|
758
|
+
|
|
709
759
|
/**
|
|
710
760
|
* Creates volume mounts and volumes for a deployment.
|
|
711
761
|
* @param {Array<volume>} volumes - List of volume configurations.
|
|
@@ -838,19 +888,32 @@ ${renderHosts}`,
|
|
|
838
888
|
* @param {string} env - Environment for which the ready status is being monitored.
|
|
839
889
|
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
840
890
|
* @param {Array<string>} ignorePods - List of pod names to ignore.
|
|
891
|
+
* @returns {object} - Object containing the ready status of the deployment.
|
|
841
892
|
* @memberof UnderpostDeploy
|
|
842
893
|
*/
|
|
843
894
|
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = []) {
|
|
844
895
|
let checkStatusIteration = 0;
|
|
845
896
|
const checkStatusIterationMsDelay = 1000;
|
|
897
|
+
const maxIterations = 500;
|
|
846
898
|
const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
|
|
847
899
|
logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
|
|
848
900
|
const minReadyOk = 3;
|
|
849
901
|
let readyOk = 0;
|
|
902
|
+
let result = {
|
|
903
|
+
ready: false,
|
|
904
|
+
notReadyPods: [],
|
|
905
|
+
readyPods: [],
|
|
906
|
+
};
|
|
850
907
|
|
|
851
908
|
while (readyOk < minReadyOk) {
|
|
852
|
-
|
|
853
|
-
|
|
909
|
+
if (checkStatusIteration >= maxIterations) {
|
|
910
|
+
logger.error(
|
|
911
|
+
`${iteratorTag} | Deployment check ready status timeout. Max iterations reached: ${maxIterations}`,
|
|
912
|
+
);
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
result = UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods);
|
|
916
|
+
if (result.ready === true) {
|
|
854
917
|
readyOk++;
|
|
855
918
|
logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
|
|
856
919
|
}
|
|
@@ -861,6 +924,7 @@ ${renderHosts}`,
|
|
|
861
924
|
);
|
|
862
925
|
}
|
|
863
926
|
logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
|
|
927
|
+
return result;
|
|
864
928
|
},
|
|
865
929
|
|
|
866
930
|
/**
|
|
@@ -915,6 +979,106 @@ ${renderHosts}`,
|
|
|
915
979
|
}
|
|
916
980
|
return result;
|
|
917
981
|
},
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Predefined resource templates for Kubernetes deployments.
|
|
985
|
+
* @memberof UnderpostDeploy
|
|
986
|
+
*/
|
|
987
|
+
resourcesTemplate: {
|
|
988
|
+
dev_small: {
|
|
989
|
+
id: 'dev_small',
|
|
990
|
+
useCase: 'microservice_development',
|
|
991
|
+
resources: {
|
|
992
|
+
requests: {
|
|
993
|
+
memory: '128Mi',
|
|
994
|
+
cpu: '250m',
|
|
995
|
+
},
|
|
996
|
+
limits: {
|
|
997
|
+
memory: '512Mi',
|
|
998
|
+
cpu: '1',
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
},
|
|
1002
|
+
prod_moderate: {
|
|
1003
|
+
id: 'prod_moderate',
|
|
1004
|
+
useCase: 'production_moderate',
|
|
1005
|
+
resources: {
|
|
1006
|
+
requests: {
|
|
1007
|
+
memory: '256Mi',
|
|
1008
|
+
cpu: '500m',
|
|
1009
|
+
},
|
|
1010
|
+
limits: {
|
|
1011
|
+
memory: '512Mi',
|
|
1012
|
+
cpu: '1',
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
memory_heavy: {
|
|
1017
|
+
id: 'memory_heavy',
|
|
1018
|
+
useCase: 'memory_intensive_app',
|
|
1019
|
+
resources: {
|
|
1020
|
+
requests: {
|
|
1021
|
+
memory: '512Mi',
|
|
1022
|
+
cpu: '500m',
|
|
1023
|
+
},
|
|
1024
|
+
limits: {
|
|
1025
|
+
memory: '1Gi',
|
|
1026
|
+
cpu: '1',
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
},
|
|
1030
|
+
cpu_bound: {
|
|
1031
|
+
id: 'cpu_bound',
|
|
1032
|
+
useCase: 'cpu_intensive_job',
|
|
1033
|
+
resources: {
|
|
1034
|
+
requests: {
|
|
1035
|
+
memory: '256Mi',
|
|
1036
|
+
cpu: '1000m',
|
|
1037
|
+
},
|
|
1038
|
+
limits: {
|
|
1039
|
+
memory: '512Mi',
|
|
1040
|
+
cpu: '2000m',
|
|
1041
|
+
},
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Creates a resource object for Kubernetes deployments.
|
|
1048
|
+
* @param {object} resources - Resource specifications.
|
|
1049
|
+
* @param {string} resources.requestsMemory - Memory request for the container.
|
|
1050
|
+
* @param {string} resources.requestsCpu - CPU request for the container.
|
|
1051
|
+
* @param {string} resources.limitsMemory - Memory limit for the container.
|
|
1052
|
+
* @param {string} resources.limitsCpu - CPU limit for the container.
|
|
1053
|
+
* @returns {object|undefined} - Resource object for Kubernetes deployments or undefined if any resource is missing.
|
|
1054
|
+
* @memberof UnderpostDeploy
|
|
1055
|
+
*/
|
|
1056
|
+
resourcesFactory: (
|
|
1057
|
+
resources = {
|
|
1058
|
+
resourceTemplateId: '',
|
|
1059
|
+
requestsMemory: '',
|
|
1060
|
+
requestsCpu: '',
|
|
1061
|
+
limitsMemory: '',
|
|
1062
|
+
limitsCpu: '',
|
|
1063
|
+
},
|
|
1064
|
+
) => {
|
|
1065
|
+
if (resources) {
|
|
1066
|
+
if (resources.resourceTemplateId)
|
|
1067
|
+
return UnderpostDeploy.API.resourcesTemplate[resources.resourceTemplateId].resources;
|
|
1068
|
+
if (resources.requestsMemory && resources.requestsCpu && resources.limitsMemory && resources.limitsCpu)
|
|
1069
|
+
return {
|
|
1070
|
+
requests: {
|
|
1071
|
+
memory: resources.requestsMemory,
|
|
1072
|
+
cpu: resources.requestsCpu,
|
|
1073
|
+
},
|
|
1074
|
+
limits: {
|
|
1075
|
+
memory: resources.limitsMemory,
|
|
1076
|
+
cpu: resources.limitsCpu,
|
|
1077
|
+
},
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
return undefined;
|
|
1081
|
+
},
|
|
918
1082
|
};
|
|
919
1083
|
}
|
|
920
1084
|
|
package/src/cli/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import UnderpostBaremetal from './baremetal.js';
|
|
|
9
9
|
import UnderpostRun from './run.js';
|
|
10
10
|
import Dns from '../server/dns.js';
|
|
11
11
|
import { pbcopy } from '../server/process.js';
|
|
12
|
+
import UnderpostStatic from './static.js';
|
|
12
13
|
|
|
13
14
|
// Load environment variables from .env file
|
|
14
15
|
const underpostRootPath = getUnderpostRootPath();
|
|
@@ -115,6 +116,22 @@ program
|
|
|
115
116
|
loadConf(deployId, subConf);
|
|
116
117
|
});
|
|
117
118
|
|
|
119
|
+
// 'static' command: Manage static configurations
|
|
120
|
+
program
|
|
121
|
+
.command('static')
|
|
122
|
+
.option('--page <ssr-component-path>', 'Build custom static pages.')
|
|
123
|
+
.option('--title <title>', 'Sets a custom title for the static page.')
|
|
124
|
+
.option('--output-path <output-path>', 'Sets the output path for the generated static page.')
|
|
125
|
+
|
|
126
|
+
.option('--deploy-id <deploy-id>', 'Build static assets for a specific deployment ID.')
|
|
127
|
+
.option('--build', 'Triggers the static build process for the specified deployment ID.')
|
|
128
|
+
.option('--build-host <build-host>', 'Sets a custom build host for static documents or assets.')
|
|
129
|
+
.option('--build-path <build-path>', 'Sets a custom build path for static documents or assets.')
|
|
130
|
+
.option('--env <env>', 'Sets the environment for the static build (e.g., "development", "production").')
|
|
131
|
+
.option('--dev', 'Sets the development cli context')
|
|
132
|
+
.description(`Manages static build of page, bundles, and documentation.`)
|
|
133
|
+
.action(UnderpostStatic.API.callback);
|
|
134
|
+
|
|
118
135
|
// 'config' command: Manage Underpost configurations
|
|
119
136
|
program
|
|
120
137
|
.command('config')
|
|
@@ -177,8 +194,6 @@ program
|
|
|
177
194
|
)
|
|
178
195
|
.option('--dev', 'Initializes a development-specific cluster configuration.')
|
|
179
196
|
.option('--list-pods', 'Displays detailed information about all pods.')
|
|
180
|
-
.option('--info-capacity', 'Displays the current total machine capacity information.')
|
|
181
|
-
.option('--info-capacity-pod', 'Displays the current machine capacity information per pod.')
|
|
182
197
|
.option('--pull-image', 'Sets an optional associated image to pull during initialization.')
|
|
183
198
|
.option('--init-host', 'Installs necessary Kubernetes node CLI tools (e.g., kind, kubeadm, docker, podman, helm).')
|
|
184
199
|
.option('--uninstall-host', 'Uninstalls all host components installed by init-host.')
|
|
@@ -439,6 +454,16 @@ program
|
|
|
439
454
|
.option('--terminal', 'Enables terminal mode for interactive script execution.')
|
|
440
455
|
.option('--dev-proxy-port-offset <port-offset>', 'Sets a custom port offset for development proxy.')
|
|
441
456
|
.option('--host-network', 'Enables host network mode for the runner execution.')
|
|
457
|
+
.option('--requests-memory <requests-memory>', 'Requests memory limit for the runner execution.')
|
|
458
|
+
.option('--requests-cpu <requests-cpu>', 'Requests CPU limit for the runner execution.')
|
|
459
|
+
.option('--limits-memory <limits-memory>', 'Sets memory limit for the runner execution.')
|
|
460
|
+
.option('--limits-cpu <limits-cpu>', 'Sets CPU limit for the runner execution.')
|
|
461
|
+
.option(
|
|
462
|
+
'--resource-template-id <resource-template-id >',
|
|
463
|
+
'Specifies a resource template ID for the runner execution.',
|
|
464
|
+
)
|
|
465
|
+
.option('--etcHosts', 'Enables /etc/hosts management for the runner execution.')
|
|
466
|
+
.option('--expose', 'Enables service exposure for the runner execution.')
|
|
442
467
|
.option('--conf-server-path <conf-server-path>', 'Sets a custom configuration server path.')
|
|
443
468
|
.option('--underpost-root <underpost-root>', 'Sets a custom Underpost root path.')
|
|
444
469
|
.description('Runs a script from the specified path.')
|
package/src/cli/monitor.js
CHANGED
|
@@ -75,7 +75,7 @@ class UnderpostMonitor {
|
|
|
75
75
|
|
|
76
76
|
let errorPayloads = [];
|
|
77
77
|
if (options.sync === true) {
|
|
78
|
-
const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
|
|
78
|
+
const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
|
|
79
79
|
if (currentTraffic) UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, currentTraffic);
|
|
80
80
|
}
|
|
81
81
|
let traffic = UnderpostRootEnv.API.get(`${deployId}-${env}-traffic`) ?? 'blue';
|