underpost 2.8.6 → 2.8.8

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.
Files changed (103) hide show
  1. package/.vscode/extensions.json +36 -3
  2. package/.vscode/settings.json +2 -0
  3. package/CHANGELOG.md +24 -4
  4. package/Dockerfile +9 -10
  5. package/README.md +41 -2
  6. package/bin/build.js +2 -2
  7. package/bin/db.js +1 -0
  8. package/bin/deploy.js +1521 -130
  9. package/bin/file.js +8 -0
  10. package/bin/index.js +1 -218
  11. package/cli.md +530 -0
  12. package/conf.js +4 -0
  13. package/docker-compose.yml +1 -1
  14. package/jsdoc.json +1 -1
  15. package/manifests/deployment/adminer/deployment.yaml +32 -0
  16. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  17. package/manifests/deployment/adminer/service.yaml +13 -0
  18. package/manifests/deployment/dd-template-development/deployment.yaml +167 -0
  19. package/manifests/deployment/dd-template-development/proxy.yaml +46 -0
  20. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  21. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  22. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  23. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  24. package/manifests/deployment/fastapi/initial_data.sh +56 -0
  25. package/manifests/deployment/kafka/deployment.yaml +69 -0
  26. package/manifests/deployment/spark/spark-pi-py.yaml +21 -0
  27. package/manifests/envoy-service-nodeport.yaml +23 -0
  28. package/manifests/kubeadm-calico-config.yaml +119 -0
  29. package/manifests/kubelet-config.yaml +65 -0
  30. package/manifests/lxd/lxd-admin-profile.yaml +17 -0
  31. package/manifests/lxd/lxd-preseed.yaml +30 -0
  32. package/manifests/lxd/underpost-setup.sh +163 -0
  33. package/manifests/maas/lxd-preseed.yaml +32 -0
  34. package/manifests/maas/maas-setup.sh +82 -0
  35. package/manifests/mariadb/statefulset.yaml +2 -1
  36. package/manifests/mariadb/storage-class.yaml +10 -0
  37. package/manifests/mongodb/kustomization.yaml +1 -1
  38. package/manifests/mongodb/statefulset.yaml +12 -11
  39. package/manifests/mongodb/storage-class.yaml +9 -0
  40. package/manifests/mongodb-4.4/service-deployment.yaml +3 -3
  41. package/manifests/mysql/kustomization.yaml +7 -0
  42. package/manifests/mysql/pv-pvc.yaml +27 -0
  43. package/manifests/mysql/statefulset.yaml +55 -0
  44. package/manifests/postgresql/configmap.yaml +9 -0
  45. package/manifests/postgresql/kustomization.yaml +10 -0
  46. package/manifests/postgresql/pv.yaml +15 -0
  47. package/manifests/postgresql/pvc.yaml +13 -0
  48. package/manifests/postgresql/service.yaml +10 -0
  49. package/manifests/postgresql/statefulset.yaml +37 -0
  50. package/manifests/valkey/service.yaml +3 -9
  51. package/manifests/valkey/statefulset.yaml +12 -13
  52. package/package.json +3 -9
  53. package/src/api/default/default.service.js +1 -1
  54. package/src/api/user/user.service.js +14 -11
  55. package/src/cli/baremetal.js +60 -0
  56. package/src/cli/cluster.js +551 -65
  57. package/src/cli/cron.js +39 -8
  58. package/src/cli/db.js +20 -10
  59. package/src/cli/deploy.js +288 -86
  60. package/src/cli/env.js +10 -4
  61. package/src/cli/fs.js +21 -9
  62. package/src/cli/image.js +116 -124
  63. package/src/cli/index.js +319 -0
  64. package/src/cli/lxd.js +395 -0
  65. package/src/cli/monitor.js +236 -0
  66. package/src/cli/repository.js +14 -8
  67. package/src/client/components/core/Account.js +28 -24
  68. package/src/client/components/core/Blockchain.js +1 -1
  69. package/src/client/components/core/CalendarCore.js +14 -84
  70. package/src/client/components/core/CommonJs.js +2 -1
  71. package/src/client/components/core/Css.js +0 -1
  72. package/src/client/components/core/CssCore.js +10 -2
  73. package/src/client/components/core/Docs.js +1 -1
  74. package/src/client/components/core/EventsUI.js +3 -3
  75. package/src/client/components/core/FileExplorer.js +86 -78
  76. package/src/client/components/core/JoyStick.js +2 -2
  77. package/src/client/components/core/LoadingAnimation.js +1 -17
  78. package/src/client/components/core/LogIn.js +3 -3
  79. package/src/client/components/core/LogOut.js +1 -1
  80. package/src/client/components/core/Modal.js +14 -8
  81. package/src/client/components/core/Panel.js +19 -61
  82. package/src/client/components/core/PanelForm.js +13 -22
  83. package/src/client/components/core/Recover.js +3 -3
  84. package/src/client/components/core/RichText.js +1 -11
  85. package/src/client/components/core/Router.js +3 -1
  86. package/src/client/components/core/SignUp.js +2 -2
  87. package/src/client/components/default/RoutesDefault.js +3 -2
  88. package/src/client/services/default/default.management.js +45 -38
  89. package/src/client/ssr/Render.js +2 -0
  90. package/src/index.js +34 -2
  91. package/src/mailer/MailerProvider.js +3 -0
  92. package/src/runtime/lampp/Dockerfile +65 -0
  93. package/src/server/client-build.js +13 -0
  94. package/src/server/conf.js +151 -1
  95. package/src/server/dns.js +56 -18
  96. package/src/server/json-schema.js +77 -0
  97. package/src/server/logger.js +3 -3
  98. package/src/server/network.js +7 -122
  99. package/src/server/peer.js +2 -2
  100. package/src/server/proxy.js +4 -4
  101. package/src/server/runtime.js +24 -11
  102. package/src/server/start.js +122 -0
  103. package/src/server/valkey.js +27 -13
package/src/cli/cron.js CHANGED
@@ -4,20 +4,24 @@
4
4
  * @namespace UnderpostCron
5
5
  */
6
6
 
7
- import Underpost from '../index.js';
7
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
8
8
  import BackUp from '../server/backup.js';
9
9
  import { Cmd } from '../server/conf.js';
10
10
  import Dns from '../server/dns.js';
11
- import { netWorkCron, saveRuntimeCron } from '../server/network.js';
11
+ import { loggerFactory } from '../server/logger.js';
12
+
12
13
  import { shellExec } from '../server/process.js';
13
14
  import fs from 'fs-extra';
14
15
 
16
+ const logger = loggerFactory(import.meta);
17
+
15
18
  /**
16
19
  * UnderpostCron main module methods
17
20
  * @class
18
21
  * @memberof UnderpostCron
19
22
  */
20
23
  class UnderpostCron {
24
+ static NETWORK = [];
21
25
  static JOB = {
22
26
  /**
23
27
  * DNS cli API
@@ -46,10 +50,10 @@ class UnderpostCron {
46
50
  callback: async function (
47
51
  deployList = 'default',
48
52
  jobList = Object.keys(UnderpostCron.JOB),
49
- options = { itc: false, init: false, git: false },
53
+ options = { itc: false, init: false, git: false, dashboardUpdate: false },
50
54
  ) {
51
55
  if (options.init === true) {
52
- await Underpost.test.setUpInfo();
56
+ UnderpostCron.NETWORK = [];
53
57
  const jobDeployId = fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
54
58
  deployList = fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').trim();
55
59
  const confCronConfig = JSON.parse(fs.readFileSync(`./engine-private/conf/${jobDeployId}/conf.cron.json`));
@@ -57,7 +61,7 @@ class UnderpostCron {
57
61
  for (const job of Object.keys(confCronConfig.jobs)) {
58
62
  const name = `${jobDeployId}-${job}`;
59
63
  let deployId;
60
- shellExec(Cmd.delete(name));
64
+ if (!options.dashboardUpdate) shellExec(Cmd.delete(name));
61
65
  switch (job) {
62
66
  case 'dns':
63
67
  deployId = jobDeployId;
@@ -67,15 +71,16 @@ class UnderpostCron {
67
71
  deployId = deployList;
68
72
  break;
69
73
  }
70
- shellExec(Cmd.cron(deployId, job, name, confCronConfig.jobs[job].expression, options));
71
- netWorkCron.push({
74
+ if (!options.dashboardUpdate)
75
+ shellExec(Cmd.cron(deployId, job, name, confCronConfig.jobs[job].expression, options));
76
+ UnderpostCron.NETWORK.push({
72
77
  deployId,
73
78
  jobId: job,
74
79
  expression: confCronConfig.jobs[job].expression,
75
80
  });
76
81
  }
77
82
  }
78
- await saveRuntimeCron();
83
+ if (options.dashboardUpdate === true) await UnderpostCron.API.updateDashboardData();
79
84
  if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
80
85
  return;
81
86
  }
@@ -84,6 +89,32 @@ class UnderpostCron {
84
89
  if (UnderpostCron.JOB[jobId]) await UnderpostCron.JOB[jobId].callback(deployList, options);
85
90
  }
86
91
  },
92
+ async updateDashboardData() {
93
+ try {
94
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
95
+ const host = process.env.DEFAULT_DEPLOY_HOST;
96
+ const path = process.env.DEFAULT_DEPLOY_PATH;
97
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
98
+ const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
99
+ const { db } = confServer[host][path];
100
+
101
+ await DataBaseProvider.load({ apis: ['cron'], host, path, db });
102
+
103
+ /** @type {import('../api/cron/cron.model.js').CronModel} */
104
+ const Cron = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Cron;
105
+
106
+ await Cron.deleteMany();
107
+
108
+ for (const cronInstance of UnderpostCron.NETWORK) {
109
+ logger.info('save', cronInstance);
110
+ await new Cron(cronInstance).save();
111
+ }
112
+
113
+ await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
114
+ } catch (error) {
115
+ logger.error(error, error.stack);
116
+ }
117
+ },
87
118
  };
88
119
  }
89
120
 
package/src/cli/db.js CHANGED
@@ -15,11 +15,13 @@ class UnderpostDB {
15
15
  export: false,
16
16
  podName: false,
17
17
  ns: false,
18
- collection: '',
18
+ collections: '',
19
19
  outPath: '',
20
20
  drop: false,
21
21
  preserveUUID: false,
22
22
  git: false,
23
+ hosts: '',
24
+ paths: '',
23
25
  },
24
26
  ) {
25
27
  const newBackupTimestamp = new Date().getTime();
@@ -39,20 +41,28 @@ class UnderpostDB {
39
41
  if (!dbs[provider]) dbs[provider] = {};
40
42
 
41
43
  if (!(name in dbs[provider]))
42
- dbs[provider][name] = { user, password, hostFolder: host + path.replaceAll('/', '-') };
44
+ dbs[provider][name] = { user, password, hostFolder: host + path.replaceAll('/', '-'), host, path };
43
45
  }
44
46
  }
45
47
  }
46
48
 
47
- if (!fs.existsSync(`../${repoName}`)) {
48
- shellExec(`cd .. && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}`);
49
- } else {
50
- shellExec(`cd ../${repoName} && underpost pull . ${process.env.GITHUB_USERNAME}/${repoName}`);
49
+ if (options.git === true) {
50
+ if (!fs.existsSync(`../${repoName}`)) {
51
+ shellExec(`cd .. && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}`);
52
+ } else {
53
+ shellExec(`cd ../${repoName} && git checkout . && git clean -f -d`);
54
+ shellExec(`cd ../${repoName} && underpost pull . ${process.env.GITHUB_USERNAME}/${repoName}`);
55
+ }
51
56
  }
52
57
 
53
58
  for (const provider of Object.keys(dbs)) {
54
59
  for (const dbName of Object.keys(dbs[provider])) {
55
- const { hostFolder, user, password } = dbs[provider][dbName];
60
+ const { hostFolder, user, password, host, path } = dbs[provider][dbName];
61
+ if (
62
+ (options.hosts && !options.hosts.split(',').includes(host)) ||
63
+ (options.paths && !options.paths.split(',').includes(path))
64
+ )
65
+ continue;
56
66
  if (hostFolder) {
57
67
  logger.info('', { hostFolder, provider, dbName });
58
68
 
@@ -153,11 +163,11 @@ class UnderpostDB {
153
163
  const podName = podNameData.NAME;
154
164
  shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf /${dbName}"`);
155
165
  if (options.collections)
156
- for (const collection of options.collections)
166
+ for (const collection of options.collections.split(','))
157
167
  shellExec(
158
- `sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} --collection ${collection} -o /${dbName}"`,
168
+ `sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} --collection ${collection} -o /"`,
159
169
  );
160
- else shellExec(`sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} -o /${dbName}"`);
170
+ else shellExec(`sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} -o /"`);
161
171
  shellExec(
162
172
  `sudo kubectl cp ${nameSpace}/${podName}:/${dbName} ${
163
173
  options.outPath ? options.outPath : _toNewBsonPath
package/src/cli/deploy.js CHANGED
@@ -3,22 +3,42 @@ import {
3
3
  buildPortProxyRouter,
4
4
  buildProxyRouter,
5
5
  Config,
6
+ deployRangePortFactory,
6
7
  getDataDeploy,
7
8
  loadReplicas,
9
+ pathPortAssignmentFactory,
8
10
  } from '../server/conf.js';
9
11
  import { loggerFactory } from '../server/logger.js';
10
12
  import { shellExec } from '../server/process.js';
11
13
  import fs from 'fs-extra';
12
14
  import dotenv from 'dotenv';
15
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
16
+ import UnderpostRootEnv from './env.js';
17
+ import UnderpostCluster from './cluster.js';
13
18
  import Underpost from '../index.js';
14
19
 
15
20
  const logger = loggerFactory(import.meta);
16
21
 
17
22
  class UnderpostDeploy {
23
+ static NETWORK = {};
18
24
  static API = {
19
- sync(deployList) {
20
- const deployGroupId = 'dd.tmp';
25
+ sync(deployList, { versions, replicas, kubeadm = false }) {
26
+ const deployGroupId = 'dd.router';
21
27
  fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
28
+ const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
29
+ const limitFactor = 0.8;
30
+ const reserveFactor = 0.05;
31
+ const resources = UnderpostCluster.API.getResourcesCapacity(kubeadm);
32
+ const memory = parseInt(resources.memory.value / totalPods);
33
+ const cpu = parseInt(resources.cpu.value / totalPods);
34
+ UnderpostRootEnv.API.set(
35
+ 'resources.requests.memory',
36
+ `${parseInt(memory * reserveFactor)}${resources.memory.unit}`,
37
+ );
38
+ UnderpostRootEnv.API.set('resources.requests.cpu', `${parseInt(cpu * reserveFactor)}${resources.cpu.unit}`);
39
+ UnderpostRootEnv.API.set('resources.limits.memory', `${parseInt(memory * limitFactor)}${resources.memory.unit}`);
40
+ UnderpostRootEnv.API.set('resources.limits.cpu', `${parseInt(cpu * limitFactor)}${resources.cpu.unit}`);
41
+ UnderpostRootEnv.API.set('total-pods', totalPods);
22
42
  return getDataDeploy({
23
43
  buildSingleReplica: true,
24
44
  deployGroupId,
@@ -32,75 +52,102 @@ class UnderpostDeploy {
32
52
  await Config.build(undefined, 'proxy', deployList);
33
53
  return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
34
54
  },
35
- async buildManifest(deployList, env, version) {
36
- for (const _deployId of deployList.split(',')) {
37
- const deployId = _deployId.trim();
38
- if (!deployId) continue;
39
-
40
- const router = await UnderpostDeploy.API.routerFactory(deployId, env);
41
- const ports = Object.values(router).map((p) => parseInt(p.split(':')[2]));
42
- const fromPort = Math.min(...ports);
43
- const toPort = Math.max(...ports);
44
- const confServer = loadReplicas(
45
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
46
- 'proxy',
47
- );
48
-
49
- fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
50
- if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
51
-
52
- logger.info('port range', { deployId, fromPort, toPort });
53
-
54
- const deploymentYamlParts = `apiVersion: apps/v1
55
+ deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
56
+ return deploymentVersions
57
+ .map(
58
+ (version, i) => ` - name: ${deployId}-${env}-${version}-service
59
+ port: ${port}
60
+ weight: ${i === 0 ? 100 : 0}
61
+ `,
62
+ )
63
+ .join('');
64
+ },
65
+ deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas }) {
66
+ return `apiVersion: apps/v1
55
67
  kind: Deployment
56
68
  metadata:
57
- name: ${deployId}-${env}
69
+ name: ${deployId}-${env}-${suffix}
58
70
  labels:
59
- app: ${deployId}-${env}
71
+ app: ${deployId}-${env}-${suffix}
60
72
  spec:
61
- replicas: 2
73
+ replicas: ${replicas}
62
74
  selector:
63
75
  matchLabels:
64
- app: ${deployId}-${env}
76
+ app: ${deployId}-${env}-${suffix}
65
77
  template:
66
78
  metadata:
67
79
  labels:
68
- app: ${deployId}-${env}
80
+ app: ${deployId}-${env}-${suffix}
69
81
  spec:
70
82
  containers:
71
- - name: ${deployId}-${env}
72
- image: localhost/underpost-engine:${version && typeof version === 'string' ? version : Underpost.version}
73
- lifecycle:
74
- postStart:
75
- exec:
76
- command:
77
- - /bin/sh
78
- - -c
79
- - >
80
- sleep 60 &&
81
- underpost config set deploy-id ${deployId} &&
82
- underpost config set deploy-env ${env}
83
- # image: localhost/${deployId}-${env}:${version && typeof version === 'string' ? version : Underpost.version}
83
+ - name: ${deployId}-${env}-${suffix}
84
+ image: localhost/debian-underpost:${Underpost.version}
85
+ # resources:
86
+ # requests:
87
+ # memory: "${resources.requests.memory}"
88
+ # cpu: "${resources.requests.cpu}"
89
+ # limits:
90
+ # memory: "${resources.limits.memory}"
91
+ # cpu: "${resources.limits.cpu}"
92
+ command:
93
+ - /bin/sh
94
+ - -c
95
+ - >
96
+ npm install -g npm@11.2.0 &&
97
+ npm install -g underpost &&
98
+ underpost secret underpost --create-from-file /etc/config/.env.${env} &&
99
+ underpost start --build --run ${deployId} ${env}
100
+ volumeMounts:
101
+ - name: config-volume
102
+ mountPath: /etc/config
103
+ volumes:
104
+ - name: config-volume
105
+ configMap:
106
+ name: underpost-config
84
107
  ---
85
108
  apiVersion: v1
86
109
  kind: Service
87
110
  metadata:
88
- name: ${deployId}-${env}-service
111
+ name: ${deployId}-${env}-${suffix}-service
89
112
  spec:
90
113
  selector:
91
- app: ${deployId}-${env}
114
+ app: ${deployId}-${env}-${suffix}
92
115
  ports:
93
- type: LoadBalancer`.split('ports:');
94
- deploymentYamlParts[1] =
95
- buildKindPorts(fromPort, toPort) +
96
- ` type: LoadBalancer
97
- `;
116
+ {{ports}} type: LoadBalancer`;
117
+ },
118
+ async buildManifest(deployList, env, options) {
119
+ const resources = UnderpostDeploy.API.resourcesFactory();
120
+ const replicas = options.replicas;
98
121
 
99
- fs.writeFileSync(
100
- `./engine-private/conf/${deployId}/build/${env}/deployment.yaml`,
101
- deploymentYamlParts.join(`ports:
102
- `),
122
+ for (const _deployId of deployList.split(',')) {
123
+ const deployId = _deployId.trim();
124
+ if (!deployId) continue;
125
+ const confServer = loadReplicas(
126
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
127
+ 'proxy',
103
128
  );
129
+ const router = await UnderpostDeploy.API.routerFactory(deployId, env);
130
+ const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
131
+ const { fromPort, toPort } = deployRangePortFactory(router);
132
+ const deploymentVersions = options.versions.split(',');
133
+ fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
134
+ if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
135
+
136
+ logger.info('port range', { deployId, fromPort, toPort });
137
+
138
+ let deploymentYamlParts = '';
139
+ for (const deploymentVersion of deploymentVersions) {
140
+ deploymentYamlParts += `---
141
+ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
142
+ deployId,
143
+ env,
144
+ suffix: deploymentVersion,
145
+ resources,
146
+ replicas,
147
+ }).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
148
+ `;
149
+ }
150
+ fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
104
151
 
105
152
  let proxyYaml = '';
106
153
  let secretYaml = '';
@@ -122,27 +169,8 @@ spec:
122
169
  kind: ClusterIssuer
123
170
  secretName: ${host}`;
124
171
 
125
- const pathPortConditions = [];
126
- for (const path of Object.keys(confServer[host])) {
127
- const { peer } = confServer[host][path];
128
- if (!router[`${host}${path === '/' ? '' : path}`]) continue;
129
- const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
130
- // logger.info('', { host, port, path });
131
- pathPortConditions.push({
132
- port,
133
- path,
134
- });
135
-
136
- if (peer) {
137
- // logger.info('', { host, port: port + 1, path: '/peer' });
138
- pathPortConditions.push({
139
- port: port + 1,
140
- path: '/peer',
141
- });
142
- }
143
- }
144
-
145
- // logger.info('', { host, pathPortConditions });
172
+ const pathPortAssignment = pathPortAssignmentData[host];
173
+ // logger.info('', { host, pathPortAssignment });
146
174
  proxyYaml += `
147
175
  ---
148
176
  apiVersion: projectcontour.io/v1
@@ -159,15 +187,20 @@ spec:
159
187
  secretName: ${host}`
160
188
  }
161
189
  routes:`;
162
- for (const conditionObj of pathPortConditions) {
190
+ for (const conditionObj of pathPortAssignment) {
163
191
  const { path, port } = conditionObj;
164
192
  proxyYaml += `
165
193
  - conditions:
166
194
  - prefix: ${path}
167
195
  enableWebsockets: true
168
196
  services:
169
- - name: ${deployId}-${env}-service
170
- port: ${port}`;
197
+ ${UnderpostDeploy.API.deploymentYamlServiceFactory({
198
+ deployId,
199
+ env,
200
+ port,
201
+ deploymentVersions:
202
+ options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
203
+ })}`;
171
204
  }
172
205
  }
173
206
  const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
@@ -188,6 +221,14 @@ spec:
188
221
  }
189
222
  }
190
223
  },
224
+ getCurrentTraffic(deployId) {
225
+ // kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
226
+ const hostTest = Object.keys(
227
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
228
+ )[0];
229
+ const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -o yaml`, { silent: true, stdout: true });
230
+ return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
231
+ },
191
232
  async callback(
192
233
  deployList = 'default',
193
234
  env = 'development',
@@ -199,7 +240,14 @@ spec:
199
240
  infoUtil: false,
200
241
  expose: false,
201
242
  cert: false,
202
- version: '',
243
+ versions: '',
244
+ traffic: '',
245
+ dashboardUpdate: false,
246
+ replicas: '',
247
+ restoreHosts: false,
248
+ disableUpdateDeployment: false,
249
+ infoTraffic: false,
250
+ rebuildClientsBundle: false,
203
251
  },
204
252
  ) {
205
253
  if (options.infoUtil === true)
@@ -207,18 +255,85 @@ spec:
207
255
  kubectl rollout restart deployment/deployment-name
208
256
  kubectl rollout undo deployment/deployment-name
209
257
  kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
210
- `);
258
+ kubectl get pods -w
259
+ kubectl patch statefulset valkey-service --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"valkey/valkey:latest"}]'
260
+ kubectl patch statefulset valkey-service -p '{"spec":{"template":{"spec":{"containers":[{"name":"valkey-service","imagePullPolicy":"Never"}]}}}}'
261
+ kubectl logs -f <pod-name>
262
+ kubectl describe pod <pod-name>
263
+ kubectl exec -it <pod-name> -- bash
264
+ kubectl exec -it <pod-name> -- sh
265
+ docker exec -it kind-control-plane bash
266
+ curl -4 -v google.com
267
+ kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-
268
+ kubectl run test-pod --image=busybox:latest --restart=Never -- /bin/sh -c "while true; do sleep 30; done;"
269
+ kubectl run test-pod --image=alpine/curl:latest --restart=Never -- sh -c "sleep infinity"
270
+ kubectl get ippools -o yaml
271
+ kubectl get node <node-name> -o jsonpath='{.spec.podCIDR}'
272
+ kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "10.244.0.0/16"}]'
273
+ kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "192.168.0.0/24"}]'
274
+ sudo podman run --rm localhost/<image-name>:<image-version> <command>
275
+ kubectl get configmap kubelet-config -n kube-system -o yaml > kubelet-config.yaml
276
+ kubectl -n kube-system rollout restart daemonset kube-proxy
277
+ kubectl get EndpointSlice -o wide --all-namespaces -w
278
+ kubectl apply -k manifests/deployment/adminer/.
279
+ kubectl wait --for=condition=Ready pod/busybox1
280
+ kubectl wait --for=jsonpath='{.status.phase}'=Running pod/busybox1
281
+ kubectl wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' pod/busybox1
282
+ kubectl wait --for=delete pod/busybox1 --timeout=60s
283
+
284
+ kubectl run --rm -it test-dns --image=busybox:latest --restart=Never -- /bin/sh -c "
285
+ nslookup kubernetes.default.svc.cluster.local;
286
+ nslookup mongodb-service.default.svc.cluster.local;
287
+ nslookup valkey-service.default.svc.cluster.local;
288
+ nc -vz mongodb-service 27017;
289
+ nc -vz valkey-service 6379;
290
+ echo exit code: \\\$?
291
+ "
292
+
293
+ kubectl apply -f - <<EOF
294
+ apiVersion: apps/v1
295
+ kind: StatefulSet
296
+ metadata:
297
+ name: ...
298
+ EOF
299
+ `);
211
300
  if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
212
301
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
213
- if (options.sync) UnderpostDeploy.API.sync(deployList);
214
- if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options.version);
215
- if (options.infoRouter === true)
216
- return logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
302
+ if (options.infoTraffic === true) {
303
+ for (const _deployId of deployList.split(',')) {
304
+ const deployId = _deployId.trim();
305
+ logger.info('', {
306
+ deployId,
307
+ env,
308
+ traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
309
+ });
310
+ }
311
+ return;
312
+ }
313
+ if (options.rebuildClientsBundle === true) await UnderpostDeploy.API.rebuildClientsBundle(deployList);
314
+ if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
315
+ if (!options.replicas) options.replicas = 1;
316
+ if (options.sync) UnderpostDeploy.API.sync(deployList, options);
317
+ if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
318
+ if (options.infoRouter === true) logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
319
+ if (options.dashboardUpdate === true) await UnderpostDeploy.API.updateDashboardData(deployList, env, options);
320
+ if (options.infoRouter === true) return;
321
+ shellExec(`kubectl delete configmap underpost-config`);
322
+ shellExec(
323
+ `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
324
+ );
325
+ let renderHosts = '';
326
+ let concatHots = '';
217
327
  const etcHost = (
218
328
  concat,
219
329
  ) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
220
330
  ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
221
- let concatHots = '';
331
+ if (options.restoreHosts === true) {
332
+ renderHosts = etcHost(concatHots);
333
+ fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
334
+ logger.info(renderHosts);
335
+ return;
336
+ }
222
337
 
223
338
  for (const _deployId of deployList.split(',')) {
224
339
  const deployId = _deployId.trim();
@@ -233,8 +348,12 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
233
348
  shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
234
349
  continue;
235
350
  }
236
- shellExec(`sudo kubectl delete svc ${deployId}-${env}-service`);
237
- shellExec(`sudo kubectl delete deployment ${deployId}-${env}`);
351
+
352
+ if (!options.disableUpdateDeployment)
353
+ for (const version of options.versions.split(',')) {
354
+ shellExec(`sudo kubectl delete svc ${deployId}-${env}-${version}-service`);
355
+ shellExec(`sudo kubectl delete deployment ${deployId}-${env}-${version}`);
356
+ }
238
357
 
239
358
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
240
359
  for (const host of Object.keys(confServer)) {
@@ -249,13 +368,12 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
249
368
  : `manifests/deployment/${deployId}-${env}`;
250
369
 
251
370
  if (!options.remove === true) {
252
- shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
371
+ if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
253
372
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
254
373
  if (env === 'production' && options.cert === true)
255
374
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
256
375
  }
257
376
  }
258
- let renderHosts;
259
377
  switch (process.platform) {
260
378
  case 'linux':
261
379
  {
@@ -312,6 +430,90 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
312
430
 
313
431
  return result;
314
432
  },
433
+ rebuildClientsBundle(deployList) {
434
+ for (const _deployId of deployList.split(',')) {
435
+ const deployId = _deployId.trim();
436
+ const repoName = `engine-${deployId.split('-')[1]}`;
437
+
438
+ shellExec(`underpost script set ${deployId}-client-build '
439
+ cd /home/dd/engine &&
440
+ git checkout . &&
441
+ underpost pull . underpostnet/${repoName} &&
442
+ underpost pull ./engine-private underpostnet/${repoName}-private &&
443
+ underpost env ${deployId} production &&
444
+ node bin/deploy build-full-client ${deployId}
445
+ '`);
446
+
447
+ shellExec(`node bin script run ${deployId}-client-build --itc --pod-name ${deployId}`);
448
+ }
449
+ },
450
+ resourcesFactory() {
451
+ return {
452
+ requests: {
453
+ memory: UnderpostRootEnv.API.get('resources.requests.memory'),
454
+ cpu: UnderpostRootEnv.API.get('resources.requests.cpu'),
455
+ },
456
+ limits: {
457
+ memory: UnderpostRootEnv.API.get('resources.limits.memory'),
458
+ cpu: UnderpostRootEnv.API.get('resources.limits.cpu'),
459
+ },
460
+ totalPods: UnderpostRootEnv.API.get('total-pods'),
461
+ };
462
+ },
463
+ async updateDashboardData(deployList, env, options) {
464
+ try {
465
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
466
+ const host = process.env.DEFAULT_DEPLOY_HOST;
467
+ const path = process.env.DEFAULT_DEPLOY_PATH;
468
+ const { db } = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'))[host][
469
+ path
470
+ ];
471
+
472
+ await DataBaseProvider.load({ apis: ['instance'], host, path, db });
473
+
474
+ /** @type {import('../api/instance/instance.model.js').InstanceModel} */
475
+ const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
476
+
477
+ await Instance.deleteMany();
478
+
479
+ for (const _deployId of deployList.split(',')) {
480
+ const deployId = _deployId.trim();
481
+ if (!deployId) continue;
482
+ const confServer = loadReplicas(
483
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
484
+ 'proxy',
485
+ );
486
+ const router = await UnderpostDeploy.API.routerFactory(deployId, env);
487
+ const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
488
+
489
+ for (const host of Object.keys(confServer)) {
490
+ for (const { path, port } of pathPortAssignmentData[host]) {
491
+ if (!confServer[host][path]) continue;
492
+
493
+ const { client, runtime, apis } = confServer[host][path];
494
+
495
+ const body = {
496
+ deployId,
497
+ host,
498
+ path,
499
+ port,
500
+ client,
501
+ runtime,
502
+ apis,
503
+ };
504
+
505
+ logger.info('save', body);
506
+
507
+ await new Instance(body).save();
508
+ }
509
+ }
510
+ }
511
+
512
+ await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
513
+ } catch (error) {
514
+ logger.error(error, error.stack);
515
+ }
516
+ },
315
517
  };
316
518
  }
317
519