underpost 2.89.44 → 2.90.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 -2
- package/cli.md +112 -101
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +8 -24
- package/manifests/kubeadm-calico-config.yaml +1 -1
- package/manifests/lxd/underpost-setup.sh +1 -1
- package/package.json +1 -1
- package/scripts/nvim.sh +1 -1
- package/src/cli/cluster.js +17 -55
- package/src/cli/db.js +34 -10
- package/src/cli/deploy.js +280 -178
- package/src/cli/index.js +18 -4
- package/src/cli/monitor.js +4 -2
- package/src/cli/run.js +218 -13
- 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 +1 -1
- package/src/server/client-build.js +13 -31
- 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.
|
|
@@ -417,68 +418,6 @@ spec:
|
|
|
417
418
|
namespace: '',
|
|
418
419
|
},
|
|
419
420
|
) {
|
|
420
|
-
if (options.infoUtil === true)
|
|
421
|
-
return logger.info(`
|
|
422
|
-
kubectl rollout restart deployment/deployment-name
|
|
423
|
-
kubectl rollout undo deployment/deployment-name
|
|
424
|
-
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
|
|
425
|
-
kubectl get pods -w
|
|
426
|
-
kubectl patch statefulset valkey-service --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"valkey/valkey:latest"}]'
|
|
427
|
-
kubectl patch statefulset valkey-service -p '{"spec":{"template":{"spec":{"containers":[{"name":"valkey-service","imagePullPolicy":"Never"}]}}}}'
|
|
428
|
-
kubectl logs -f <pod-name>
|
|
429
|
-
kubectl describe pod <pod-name>
|
|
430
|
-
kubectl exec -it <pod-name> -- bash
|
|
431
|
-
kubectl exec -it <pod-name> -- sh
|
|
432
|
-
docker exec -it kind-control-plane bash
|
|
433
|
-
curl -4 -v google.com
|
|
434
|
-
kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-
|
|
435
|
-
kubectl run test-pod --image=busybox:latest --restart=Never -- /bin/sh -c "while true; do sleep 30; done;"
|
|
436
|
-
kubectl run test-pod --image=alpine/curl:latest --restart=Never -- sh -c "sleep infinity"
|
|
437
|
-
kubectl get ippools -o yaml
|
|
438
|
-
kubectl get node <node-name> -o jsonpath='{.spec.podCIDR}'
|
|
439
|
-
kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "10.244.0.0/16"}]'
|
|
440
|
-
kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "192.168.0.0/24"}]'
|
|
441
|
-
sudo podman run --rm localhost/<image-name>:<image-version> <command>
|
|
442
|
-
kubectl get configmap kubelet-config -n kube-system -o yaml > kubelet-config.yaml
|
|
443
|
-
kubectl -n kube-system rollout restart daemonset kube-proxy
|
|
444
|
-
kubectl get EndpointSlice -o wide --all-namespaces -w
|
|
445
|
-
kubectl apply -k manifests/deployment/adminer/.
|
|
446
|
-
kubectl wait --for=condition=Ready pod/busybox1
|
|
447
|
-
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/busybox1
|
|
448
|
-
kubectl wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' pod/busybox1
|
|
449
|
-
kubectl wait --for=delete pod/busybox1 --timeout=60s
|
|
450
|
-
|
|
451
|
-
node bin run cluster-build
|
|
452
|
-
node bin run template-deploy
|
|
453
|
-
node bin run ssh-deploy (sync-)engine-core
|
|
454
|
-
node bin run cluster --dev 'express,dd-test+dd-core'
|
|
455
|
-
node bin run dd-container --dev
|
|
456
|
-
node bin run promote dd-default production
|
|
457
|
-
node bin dockerfile-pull-base-images --dev --path 'image-path' --kind-load
|
|
458
|
-
node bin/deploy update-default-conf <deploy-id>
|
|
459
|
-
|
|
460
|
-
fqdn: <service>.<namespace>.<kind(svc/pod)>.<cluster-domain(cluster.local)>
|
|
461
|
-
kubectl run --rm -it test-dns --image=busybox:latest --restart=Never -- /bin/sh -c "
|
|
462
|
-
nslookup kubernetes.default.svc.cluster.local;
|
|
463
|
-
nslookup mongodb-service.default.svc.cluster.local;
|
|
464
|
-
nslookup valkey-service.default.svc.cluster.local;
|
|
465
|
-
nc -vz mongodb-service 27017;
|
|
466
|
-
nc -vz valkey-service 6379;
|
|
467
|
-
echo exit code: \\\$?
|
|
468
|
-
"
|
|
469
|
-
|
|
470
|
-
kubectl apply -f - <<EOF
|
|
471
|
-
apiVersion: apps/v1
|
|
472
|
-
kind: StatefulSet
|
|
473
|
-
metadata:
|
|
474
|
-
name: ...
|
|
475
|
-
EOF
|
|
476
|
-
|
|
477
|
-
https://org.ngc.nvidia.com/setup/api-keys
|
|
478
|
-
docker login nvcr.io
|
|
479
|
-
Username: $oauthtoken
|
|
480
|
-
Password: <Your Key>
|
|
481
|
-
`);
|
|
482
421
|
const namespace = options.namespace ? options.namespace : 'default';
|
|
483
422
|
if (!deployList && options.certHosts) {
|
|
484
423
|
for (const host of options.certHosts.split(',')) {
|
|
@@ -493,21 +432,39 @@ EOF`);
|
|
|
493
432
|
if (options.status === true) {
|
|
494
433
|
for (const _deployId of deployList.split(',')) {
|
|
495
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
|
+
}
|
|
496
452
|
logger.info('', {
|
|
497
453
|
deployId,
|
|
498
454
|
env,
|
|
499
|
-
traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
|
|
455
|
+
traffic: UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace }),
|
|
500
456
|
router: await UnderpostDeploy.API.routerFactory(deployId, env),
|
|
501
457
|
pods: await UnderpostDeploy.API.get(deployId),
|
|
458
|
+
instances,
|
|
502
459
|
});
|
|
503
460
|
}
|
|
504
461
|
const interfaceName = Dns.getDefaultNetworkInterface();
|
|
505
462
|
logger.info('Machine', {
|
|
506
|
-
|
|
463
|
+
hostname: os.hostname(),
|
|
507
464
|
arch: UnderpostBaremetal.API.getHostArch(),
|
|
508
465
|
ipv4Public: await Dns.getPublicIp(),
|
|
509
466
|
ipv4Local: getLocalIPv4Address(),
|
|
510
|
-
resources: UnderpostCluster.API.getResourcesCapacity(),
|
|
467
|
+
resources: UnderpostCluster.API.getResourcesCapacity(options.node),
|
|
511
468
|
defaultInterfaceName: interfaceName,
|
|
512
469
|
defaultInterfaceInfo: os.networkInterfaces()[interfaceName],
|
|
513
470
|
});
|
|
@@ -515,7 +472,10 @@ EOF`);
|
|
|
515
472
|
}
|
|
516
473
|
if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
|
|
517
474
|
if (!options.replicas) options.replicas = 1;
|
|
518
|
-
if (options.sync)
|
|
475
|
+
if (options.sync)
|
|
476
|
+
getDataDeploy({
|
|
477
|
+
buildSingleReplica: true,
|
|
478
|
+
});
|
|
519
479
|
if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
|
|
520
480
|
if (options.infoRouter === true || options.buildManifest === true) {
|
|
521
481
|
logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
|
|
@@ -558,24 +518,15 @@ EOF`);
|
|
|
558
518
|
shellExec(
|
|
559
519
|
`sudo kubectl delete deployment ${deployId}-${env}-${version} -n ${namespace} --ignore-not-found`,
|
|
560
520
|
);
|
|
561
|
-
if (!options.disableUpdateVolume)
|
|
562
|
-
for (const volume of confVolume)
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
571
|
-
${UnderpostDeploy.API.persistentVolumeFactory({
|
|
572
|
-
hostPath: rootVolumeHostPath,
|
|
573
|
-
pvcId,
|
|
574
|
-
})}
|
|
575
|
-
EOF
|
|
576
|
-
`);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
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
|
+
});
|
|
579
530
|
}
|
|
580
531
|
|
|
581
532
|
for (const host of Object.keys(confServer)) {
|
|
@@ -654,24 +605,7 @@ EOF
|
|
|
654
605
|
|
|
655
606
|
return result;
|
|
656
607
|
},
|
|
657
|
-
|
|
658
|
-
* Retrieves the resources factory for a deployment.
|
|
659
|
-
* @returns {object} - Object containing the resources factory for the deployment.
|
|
660
|
-
* @memberof UnderpostDeploy
|
|
661
|
-
*/
|
|
662
|
-
resourcesFactory() {
|
|
663
|
-
return {
|
|
664
|
-
requests: {
|
|
665
|
-
memory: UnderpostRootEnv.API.get('resources.requests.memory'),
|
|
666
|
-
cpu: UnderpostRootEnv.API.get('resources.requests.cpu'),
|
|
667
|
-
},
|
|
668
|
-
limits: {
|
|
669
|
-
memory: UnderpostRootEnv.API.get('resources.limits.memory'),
|
|
670
|
-
cpu: UnderpostRootEnv.API.get('resources.limits.cpu'),
|
|
671
|
-
},
|
|
672
|
-
totalPods: UnderpostRootEnv.API.get('total-pods'),
|
|
673
|
-
};
|
|
674
|
-
},
|
|
608
|
+
|
|
675
609
|
/**
|
|
676
610
|
* Checks if a container file exists in a pod.
|
|
677
611
|
* @param {object} options - Options for the check.
|
|
@@ -768,6 +702,60 @@ EOF
|
|
|
768
702
|
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
|
|
769
703
|
},
|
|
770
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
|
+
|
|
771
759
|
/**
|
|
772
760
|
* Creates volume mounts and volumes for a deployment.
|
|
773
761
|
* @param {Array<volume>} volumes - List of volume configurations.
|
|
@@ -900,19 +888,32 @@ ${renderHosts}`,
|
|
|
900
888
|
* @param {string} env - Environment for which the ready status is being monitored.
|
|
901
889
|
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
902
890
|
* @param {Array<string>} ignorePods - List of pod names to ignore.
|
|
891
|
+
* @returns {object} - Object containing the ready status of the deployment.
|
|
903
892
|
* @memberof UnderpostDeploy
|
|
904
893
|
*/
|
|
905
894
|
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = []) {
|
|
906
895
|
let checkStatusIteration = 0;
|
|
907
896
|
const checkStatusIterationMsDelay = 1000;
|
|
897
|
+
const maxIterations = 500;
|
|
908
898
|
const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
|
|
909
899
|
logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
|
|
910
900
|
const minReadyOk = 3;
|
|
911
901
|
let readyOk = 0;
|
|
902
|
+
let result = {
|
|
903
|
+
ready: false,
|
|
904
|
+
notReadyPods: [],
|
|
905
|
+
readyPods: [],
|
|
906
|
+
};
|
|
912
907
|
|
|
913
908
|
while (readyOk < minReadyOk) {
|
|
914
|
-
|
|
915
|
-
|
|
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) {
|
|
916
917
|
readyOk++;
|
|
917
918
|
logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
|
|
918
919
|
}
|
|
@@ -923,6 +924,7 @@ ${renderHosts}`,
|
|
|
923
924
|
);
|
|
924
925
|
}
|
|
925
926
|
logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
|
|
927
|
+
return result;
|
|
926
928
|
},
|
|
927
929
|
|
|
928
930
|
/**
|
|
@@ -977,6 +979,106 @@ ${renderHosts}`,
|
|
|
977
979
|
}
|
|
978
980
|
return result;
|
|
979
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
|
+
},
|
|
980
1082
|
};
|
|
981
1083
|
}
|
|
982
1084
|
|
package/src/cli/index.js
CHANGED
|
@@ -156,13 +156,20 @@ program
|
|
|
156
156
|
.option('--contour', 'Initializes the cluster with Project Contour base HTTPProxy and Envoy.')
|
|
157
157
|
.option('--cert-manager', "Initializes the cluster with a Let's Encrypt production ClusterIssuer.")
|
|
158
158
|
.option('--dedicated-gpu', 'Initializes the cluster with dedicated GPU base resources and environment settings.')
|
|
159
|
-
.option('--info', 'Retrieves information about all deployed Kubernetes objects.')
|
|
160
159
|
.option('--full', 'Initializes the cluster with all available statefulsets and services.')
|
|
161
160
|
.option(
|
|
162
161
|
'--ns-use <ns-name>',
|
|
163
162
|
"Switches the current Kubernetes context to the specified namespace (creates if it doesn't exist).",
|
|
164
163
|
)
|
|
165
164
|
.option('--kubeadm', 'Initializes the cluster using kubeadm for control plane management.')
|
|
165
|
+
.option(
|
|
166
|
+
'--pod-network-cidr <cidr>',
|
|
167
|
+
'Sets custom pod network CIDR for kubeadm cluster initialization (defaults to "192.168.0.0/16").',
|
|
168
|
+
)
|
|
169
|
+
.option(
|
|
170
|
+
'--control-plane-endpoint <endpoint>',
|
|
171
|
+
'Sets custom control plane endpoint for kubeadm cluster initialization (defaults to "localhost:6443").',
|
|
172
|
+
)
|
|
166
173
|
.option('--grafana', 'Initializes the cluster with a Grafana deployment.')
|
|
167
174
|
.option(
|
|
168
175
|
'--prom [hosts]',
|
|
@@ -170,8 +177,6 @@ program
|
|
|
170
177
|
)
|
|
171
178
|
.option('--dev', 'Initializes a development-specific cluster configuration.')
|
|
172
179
|
.option('--list-pods', 'Displays detailed information about all pods.')
|
|
173
|
-
.option('--info-capacity', 'Displays the current total machine capacity information.')
|
|
174
|
-
.option('--info-capacity-pod', 'Displays the current machine capacity information per pod.')
|
|
175
180
|
.option('--pull-image', 'Sets an optional associated image to pull during initialization.')
|
|
176
181
|
.option('--init-host', 'Installs necessary Kubernetes node CLI tools (e.g., kind, kubeadm, docker, podman, helm).')
|
|
177
182
|
.option('--uninstall-host', 'Uninstalls all host components installed by init-host.')
|
|
@@ -197,7 +202,6 @@ program
|
|
|
197
202
|
.option('--sync', 'Synchronizes deployment environment variables, ports, and replica counts.')
|
|
198
203
|
.option('--info-router', 'Displays the current router structure and configuration.')
|
|
199
204
|
.option('--expose', 'Exposes services matching the provided deployment ID list.')
|
|
200
|
-
.option('--info-util', 'Displays useful `kubectl` utility management commands.')
|
|
201
205
|
.option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
|
|
202
206
|
.option('--cert-hosts <hosts>', 'Resets TLS/SSL certificate secrets for specified hosts.')
|
|
203
207
|
.option('--node <node>', 'Sets optional node for deployment operations.')
|
|
@@ -433,6 +437,16 @@ program
|
|
|
433
437
|
.option('--terminal', 'Enables terminal mode for interactive script execution.')
|
|
434
438
|
.option('--dev-proxy-port-offset <port-offset>', 'Sets a custom port offset for development proxy.')
|
|
435
439
|
.option('--host-network', 'Enables host network mode for the runner execution.')
|
|
440
|
+
.option('--requests-memory <requests-memory>', 'Requests memory limit for the runner execution.')
|
|
441
|
+
.option('--requests-cpu <requests-cpu>', 'Requests CPU limit for the runner execution.')
|
|
442
|
+
.option('--limits-memory <limits-memory>', 'Sets memory limit for the runner execution.')
|
|
443
|
+
.option('--limits-cpu <limits-cpu>', 'Sets CPU limit for the runner execution.')
|
|
444
|
+
.option(
|
|
445
|
+
'--resource-template-id <resource-template-id >',
|
|
446
|
+
'Specifies a resource template ID for the runner execution.',
|
|
447
|
+
)
|
|
448
|
+
.option('--etcHosts', 'Enables /etc/hosts management for the runner execution.')
|
|
449
|
+
.option('--expose', 'Enables service exposure for the runner execution.')
|
|
436
450
|
.option('--conf-server-path <conf-server-path>', 'Sets a custom configuration server path.')
|
|
437
451
|
.option('--underpost-root <underpost-root>', 'Sets a custom Underpost root path.')
|
|
438
452
|
.description('Runs a script from the specified path.')
|