underpost 2.8.1 → 2.8.7
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/.dockerignore +1 -0
- package/.github/workflows/ghpkg.yml +19 -49
- package/.github/workflows/npmpkg.yml +67 -0
- package/.github/workflows/publish.yml +5 -5
- package/.github/workflows/pwa-microservices-template.page.yml +12 -4
- package/.github/workflows/pwa-microservices-template.test.yml +2 -2
- package/.vscode/extensions.json +18 -71
- package/.vscode/settings.json +20 -3
- package/AUTHORS.md +16 -5
- package/CHANGELOG.md +123 -3
- package/Dockerfile +27 -70
- package/README.md +39 -29
- package/bin/build.js +186 -0
- package/bin/db.js +2 -24
- package/bin/deploy.js +1467 -236
- package/bin/file.js +67 -16
- package/bin/hwt.js +0 -10
- package/bin/index.js +1 -77
- package/bin/ssl.js +19 -11
- package/bin/util.js +9 -104
- package/bin/vs.js +26 -2
- package/cli.md +451 -0
- package/conf.js +29 -138
- package/docker-compose.yml +1 -1
- package/jsdoc.json +1 -1
- package/manifests/calico-custom-resources.yaml +25 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/kafka/deployment.yaml +69 -0
- package/manifests/deployment/mongo-express/deployment.yaml +60 -0
- package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
- package/manifests/kind-config-dev.yaml +12 -0
- package/manifests/kind-config.yaml +12 -0
- package/manifests/kubeadm-calico-config.yaml +119 -0
- package/manifests/letsencrypt-prod.yaml +15 -0
- package/manifests/mariadb/config.yaml +10 -0
- package/manifests/mariadb/kustomization.yaml +9 -0
- package/manifests/mariadb/pv.yaml +12 -0
- package/manifests/mariadb/pvc.yaml +10 -0
- package/manifests/mariadb/secret.yaml +8 -0
- package/manifests/mariadb/service.yaml +10 -0
- package/manifests/mariadb/statefulset.yaml +55 -0
- package/manifests/mongodb/backup-access.yaml +16 -0
- package/manifests/mongodb/backup-cronjob.yaml +42 -0
- package/manifests/mongodb/backup-pv-pvc.yaml +22 -0
- package/manifests/mongodb/configmap.yaml +26 -0
- package/manifests/mongodb/headless-service.yaml +10 -0
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +23 -0
- package/manifests/mongodb/statefulset.yaml +125 -0
- package/manifests/mongodb-4.4/kustomization.yaml +7 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +23 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +17 -0
- package/manifests/valkey/statefulset.yaml +41 -0
- package/package.json +127 -136
- package/src/api/core/core.service.js +1 -1
- package/src/api/default/default.service.js +1 -1
- package/src/api/user/user.model.js +16 -3
- package/src/api/user/user.service.js +15 -12
- package/src/cli/cluster.js +389 -0
- package/src/cli/cron.js +121 -0
- package/src/cli/db.js +222 -0
- package/src/cli/deploy.js +487 -0
- package/src/cli/env.js +58 -0
- package/src/cli/fs.js +161 -0
- package/src/cli/image.js +66 -0
- package/src/cli/index.js +312 -0
- package/src/cli/monitor.js +236 -0
- package/src/cli/repository.js +128 -0
- package/src/cli/script.js +53 -0
- package/src/cli/secrets.js +37 -0
- package/src/cli/test.js +118 -0
- package/src/client/components/core/Account.js +28 -24
- package/src/client/components/core/Auth.js +22 -4
- package/src/client/components/core/Blockchain.js +1 -1
- package/src/client/components/core/CalendarCore.js +128 -121
- package/src/client/components/core/CommonJs.js +283 -19
- package/src/client/components/core/CssCore.js +16 -4
- package/src/client/components/core/Docs.js +1 -2
- package/src/client/components/core/DropDown.js +5 -1
- package/src/client/components/core/EventsUI.js +3 -3
- package/src/client/components/core/FileExplorer.js +86 -78
- package/src/client/components/core/Input.js +22 -6
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/LoadingAnimation.js +3 -12
- package/src/client/components/core/LogIn.js +3 -3
- package/src/client/components/core/LogOut.js +1 -1
- package/src/client/components/core/Modal.js +54 -20
- package/src/client/components/core/Panel.js +109 -90
- package/src/client/components/core/PanelForm.js +23 -30
- package/src/client/components/core/Recover.js +3 -3
- package/src/client/components/core/RichText.js +1 -11
- package/src/client/components/core/Router.js +3 -1
- package/src/client/components/core/Scroll.js +1 -0
- package/src/client/components/core/SignUp.js +2 -2
- package/src/client/components/core/Translate.js +47 -9
- package/src/client/components/core/Validator.js +9 -1
- package/src/client/components/core/VanillaJs.js +0 -9
- package/src/client/components/core/Worker.js +34 -31
- package/src/client/components/default/RoutesDefault.js +3 -2
- package/src/client/services/core/core.service.js +15 -10
- package/src/client/services/default/default.management.js +46 -37
- package/src/client/ssr/Render.js +6 -1
- package/src/client/ssr/body/CacheControl.js +2 -3
- package/src/client/sw/default.sw.js +3 -3
- package/src/db/mongo/MongooseDB.js +29 -1
- package/src/index.js +101 -19
- package/src/mailer/MailerProvider.js +3 -0
- package/src/runtime/lampp/Dockerfile +65 -0
- package/src/runtime/lampp/Lampp.js +1 -13
- package/src/runtime/xampp/Xampp.js +0 -13
- package/src/server/auth.js +3 -3
- package/src/server/backup.js +49 -93
- package/src/server/client-build.js +49 -46
- package/src/server/client-formatted.js +6 -3
- package/src/server/conf.js +297 -55
- package/src/server/dns.js +75 -62
- package/src/server/downloader.js +0 -8
- package/src/server/json-schema.js +77 -0
- package/src/server/logger.js +15 -10
- package/src/server/network.js +20 -161
- package/src/server/peer.js +2 -2
- package/src/server/process.js +25 -2
- package/src/server/proxy.js +7 -29
- package/src/server/runtime.js +53 -40
- package/src/server/ssl.js +1 -1
- package/src/server/start.js +122 -0
- package/src/server/valkey.js +27 -11
- package/test/api.test.js +0 -8
- package/src/dns.js +0 -22
- package/src/server/prompt-optimizer.js +0 -28
- package/startup.js +0 -11
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildKindPorts,
|
|
3
|
+
buildPortProxyRouter,
|
|
4
|
+
buildProxyRouter,
|
|
5
|
+
Config,
|
|
6
|
+
deployRangePortFactory,
|
|
7
|
+
getDataDeploy,
|
|
8
|
+
loadReplicas,
|
|
9
|
+
pathPortAssignmentFactory,
|
|
10
|
+
} from '../server/conf.js';
|
|
11
|
+
import { loggerFactory } from '../server/logger.js';
|
|
12
|
+
import { shellExec } from '../server/process.js';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
import dotenv from 'dotenv';
|
|
15
|
+
import { DataBaseProvider } from '../db/DataBaseProvider.js';
|
|
16
|
+
import UnderpostRootEnv from './env.js';
|
|
17
|
+
import UnderpostCluster from './cluster.js';
|
|
18
|
+
|
|
19
|
+
const logger = loggerFactory(import.meta);
|
|
20
|
+
|
|
21
|
+
class UnderpostDeploy {
|
|
22
|
+
static NETWORK = {};
|
|
23
|
+
static API = {
|
|
24
|
+
sync(deployList, { versions, replicas }) {
|
|
25
|
+
const deployGroupId = 'dd.router';
|
|
26
|
+
fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
|
|
27
|
+
const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
|
|
28
|
+
const limitFactor = 0.8;
|
|
29
|
+
const reserveFactor = 0.05;
|
|
30
|
+
const resources = UnderpostCluster.API.getResourcesCapacity();
|
|
31
|
+
const memory = parseInt(resources.memory.value / totalPods);
|
|
32
|
+
const cpu = parseInt(resources.cpu.value / totalPods);
|
|
33
|
+
UnderpostRootEnv.API.set(
|
|
34
|
+
'resources.requests.memory',
|
|
35
|
+
`${parseInt(memory * reserveFactor)}${resources.memory.unit}`,
|
|
36
|
+
);
|
|
37
|
+
UnderpostRootEnv.API.set('resources.requests.cpu', `${parseInt(cpu * reserveFactor)}${resources.cpu.unit}`);
|
|
38
|
+
UnderpostRootEnv.API.set('resources.limits.memory', `${parseInt(memory * limitFactor)}${resources.memory.unit}`);
|
|
39
|
+
UnderpostRootEnv.API.set('resources.limits.cpu', `${parseInt(cpu * limitFactor)}${resources.cpu.unit}`);
|
|
40
|
+
UnderpostRootEnv.API.set('total-pods', totalPods);
|
|
41
|
+
return getDataDeploy({
|
|
42
|
+
buildSingleReplica: true,
|
|
43
|
+
deployGroupId,
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
async routerFactory(deployList, env) {
|
|
47
|
+
const initEnvPath = `./engine-private/conf/${deployList.split(',')[0]}/.env.${env}`;
|
|
48
|
+
const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
|
|
49
|
+
process.env.PORT = initEnvObj.PORT;
|
|
50
|
+
process.env.NODE_ENV = env;
|
|
51
|
+
await Config.build(undefined, 'proxy', deployList);
|
|
52
|
+
return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
|
|
53
|
+
},
|
|
54
|
+
deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
|
|
55
|
+
return deploymentVersions
|
|
56
|
+
.map(
|
|
57
|
+
(version, i) => ` - name: ${deployId}-${env}-${version}-service
|
|
58
|
+
port: ${port}
|
|
59
|
+
weight: ${i === 0 ? 100 : 0}
|
|
60
|
+
`,
|
|
61
|
+
)
|
|
62
|
+
.join('');
|
|
63
|
+
},
|
|
64
|
+
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas }) {
|
|
65
|
+
return `apiVersion: apps/v1
|
|
66
|
+
kind: Deployment
|
|
67
|
+
metadata:
|
|
68
|
+
name: ${deployId}-${env}-${suffix}
|
|
69
|
+
labels:
|
|
70
|
+
app: ${deployId}-${env}-${suffix}
|
|
71
|
+
spec:
|
|
72
|
+
replicas: ${replicas}
|
|
73
|
+
selector:
|
|
74
|
+
matchLabels:
|
|
75
|
+
app: ${deployId}-${env}-${suffix}
|
|
76
|
+
template:
|
|
77
|
+
metadata:
|
|
78
|
+
labels:
|
|
79
|
+
app: ${deployId}-${env}-${suffix}
|
|
80
|
+
spec:
|
|
81
|
+
containers:
|
|
82
|
+
- name: ${deployId}-${env}-${suffix}
|
|
83
|
+
image: localhost/debian:underpost
|
|
84
|
+
resources:
|
|
85
|
+
requests:
|
|
86
|
+
memory: "${resources.requests.memory}"
|
|
87
|
+
cpu: "${resources.requests.cpu}"
|
|
88
|
+
limits:
|
|
89
|
+
memory: "${resources.limits.memory}"
|
|
90
|
+
cpu: "${resources.limits.cpu}"
|
|
91
|
+
command:
|
|
92
|
+
- /bin/sh
|
|
93
|
+
- -c
|
|
94
|
+
- >
|
|
95
|
+
npm install -g npm@11.2.0 &&
|
|
96
|
+
npm install -g underpost &&
|
|
97
|
+
underpost secret underpost --create-from-file /etc/config/.env.${env} &&
|
|
98
|
+
underpost start --build --run ${deployId} ${env}
|
|
99
|
+
volumeMounts:
|
|
100
|
+
- name: config-volume
|
|
101
|
+
mountPath: /etc/config
|
|
102
|
+
volumes:
|
|
103
|
+
- name: config-volume
|
|
104
|
+
configMap:
|
|
105
|
+
name: underpost-config
|
|
106
|
+
---
|
|
107
|
+
apiVersion: v1
|
|
108
|
+
kind: Service
|
|
109
|
+
metadata:
|
|
110
|
+
name: ${deployId}-${env}-${suffix}-service
|
|
111
|
+
spec:
|
|
112
|
+
selector:
|
|
113
|
+
app: ${deployId}-${env}-${suffix}
|
|
114
|
+
ports:
|
|
115
|
+
{{ports}} type: LoadBalancer`;
|
|
116
|
+
},
|
|
117
|
+
async buildManifest(deployList, env, options) {
|
|
118
|
+
const resources = UnderpostDeploy.API.resourcesFactory();
|
|
119
|
+
const replicas = options.replicas;
|
|
120
|
+
|
|
121
|
+
for (const _deployId of deployList.split(',')) {
|
|
122
|
+
const deployId = _deployId.trim();
|
|
123
|
+
if (!deployId) continue;
|
|
124
|
+
const confServer = loadReplicas(
|
|
125
|
+
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
126
|
+
'proxy',
|
|
127
|
+
);
|
|
128
|
+
const router = await UnderpostDeploy.API.routerFactory(deployId, env);
|
|
129
|
+
const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
|
|
130
|
+
const { fromPort, toPort } = deployRangePortFactory(router);
|
|
131
|
+
const deploymentVersions = options.versions.split(',');
|
|
132
|
+
fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
|
|
133
|
+
if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
|
|
134
|
+
|
|
135
|
+
logger.info('port range', { deployId, fromPort, toPort });
|
|
136
|
+
|
|
137
|
+
let deploymentYamlParts = '';
|
|
138
|
+
for (const deploymentVersion of deploymentVersions) {
|
|
139
|
+
deploymentYamlParts += `---
|
|
140
|
+
${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
141
|
+
deployId,
|
|
142
|
+
env,
|
|
143
|
+
suffix: deploymentVersion,
|
|
144
|
+
resources,
|
|
145
|
+
replicas,
|
|
146
|
+
}).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
147
|
+
`;
|
|
148
|
+
}
|
|
149
|
+
fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
|
|
150
|
+
|
|
151
|
+
let proxyYaml = '';
|
|
152
|
+
let secretYaml = '';
|
|
153
|
+
|
|
154
|
+
for (const host of Object.keys(confServer)) {
|
|
155
|
+
if (env === 'production')
|
|
156
|
+
secretYaml += `
|
|
157
|
+
---
|
|
158
|
+
apiVersion: cert-manager.io/v1
|
|
159
|
+
kind: Certificate
|
|
160
|
+
metadata:
|
|
161
|
+
name: ${host}
|
|
162
|
+
spec:
|
|
163
|
+
commonName: ${host}
|
|
164
|
+
dnsNames:
|
|
165
|
+
- ${host}
|
|
166
|
+
issuerRef:
|
|
167
|
+
name: letsencrypt-prod
|
|
168
|
+
kind: ClusterIssuer
|
|
169
|
+
secretName: ${host}`;
|
|
170
|
+
|
|
171
|
+
const pathPortAssignment = pathPortAssignmentData[host];
|
|
172
|
+
// logger.info('', { host, pathPortAssignment });
|
|
173
|
+
proxyYaml += `
|
|
174
|
+
---
|
|
175
|
+
apiVersion: projectcontour.io/v1
|
|
176
|
+
kind: HTTPProxy
|
|
177
|
+
metadata:
|
|
178
|
+
name: ${host}
|
|
179
|
+
spec:
|
|
180
|
+
virtualhost:
|
|
181
|
+
fqdn: ${host}${
|
|
182
|
+
env === 'development'
|
|
183
|
+
? ''
|
|
184
|
+
: `
|
|
185
|
+
tls:
|
|
186
|
+
secretName: ${host}`
|
|
187
|
+
}
|
|
188
|
+
routes:`;
|
|
189
|
+
for (const conditionObj of pathPortAssignment) {
|
|
190
|
+
const { path, port } = conditionObj;
|
|
191
|
+
proxyYaml += `
|
|
192
|
+
- conditions:
|
|
193
|
+
- prefix: ${path}
|
|
194
|
+
enableWebsockets: true
|
|
195
|
+
services:
|
|
196
|
+
${UnderpostDeploy.API.deploymentYamlServiceFactory({
|
|
197
|
+
deployId,
|
|
198
|
+
env,
|
|
199
|
+
port,
|
|
200
|
+
deploymentVersions:
|
|
201
|
+
options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
|
|
202
|
+
})}`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
|
|
206
|
+
fs.writeFileSync(yamlPath, proxyYaml, 'utf8');
|
|
207
|
+
if (env === 'production') {
|
|
208
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
|
|
209
|
+
fs.writeFileSync(yamlPath, secretYaml, 'utf8');
|
|
210
|
+
} else {
|
|
211
|
+
const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml'];
|
|
212
|
+
for (const file of deploymentsFiles) {
|
|
213
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
|
|
214
|
+
fs.copyFileSync(
|
|
215
|
+
`./engine-private/conf/${deployId}/build/${env}/${file}`,
|
|
216
|
+
`./manifests/deployment/${deployId}-${env}/${file}`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
getCurrentTraffic(deployId) {
|
|
224
|
+
// kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
|
|
225
|
+
const hostTest = Object.keys(
|
|
226
|
+
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
227
|
+
)[0];
|
|
228
|
+
const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -o yaml`, { silent: true, stdout: true });
|
|
229
|
+
return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
|
|
230
|
+
},
|
|
231
|
+
async callback(
|
|
232
|
+
deployList = 'default',
|
|
233
|
+
env = 'development',
|
|
234
|
+
options = {
|
|
235
|
+
remove: false,
|
|
236
|
+
infoRouter: false,
|
|
237
|
+
sync: false,
|
|
238
|
+
buildManifest: false,
|
|
239
|
+
infoUtil: false,
|
|
240
|
+
expose: false,
|
|
241
|
+
cert: false,
|
|
242
|
+
versions: '',
|
|
243
|
+
traffic: '',
|
|
244
|
+
dashboardUpdate: false,
|
|
245
|
+
replicas: '',
|
|
246
|
+
disableUpdateDeployment: false,
|
|
247
|
+
infoTraffic: false,
|
|
248
|
+
rebuildClientsBundle: false,
|
|
249
|
+
},
|
|
250
|
+
) {
|
|
251
|
+
if (options.infoUtil === true)
|
|
252
|
+
return logger.info(`
|
|
253
|
+
kubectl rollout restart deployment/deployment-name
|
|
254
|
+
kubectl rollout undo deployment/deployment-name
|
|
255
|
+
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
|
|
256
|
+
kubectl get pods -w
|
|
257
|
+
kubectl patch statefulset service-valkey --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"valkey/valkey:latest"}]'
|
|
258
|
+
kubectl patch statefulset service-valkey -p '{"spec":{"template":{"spec":{"containers":[{"name":"service-valkey","imagePullPolicy":"Never"}]}}}}'
|
|
259
|
+
kubectl logs -f <pod-name>
|
|
260
|
+
kubectl describe pod <pod-name>
|
|
261
|
+
kubectl exec -it <pod-name> -- bash
|
|
262
|
+
kubectl exec -it <pod-name> -- sh
|
|
263
|
+
docker exec -it kind-control-plane bash
|
|
264
|
+
curl -4 -v google.com
|
|
265
|
+
kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-
|
|
266
|
+
kubectl run test-pod --image=busybox:latest --restart=Never -- /bin/sh -c "while true; do sleep 30; done;"
|
|
267
|
+
kubectl run test-pod --image=alpine/curl:latest --restart=Never -- sh -c "sleep infinity"
|
|
268
|
+
kubectl get ippools -o yaml
|
|
269
|
+
kubectl get node <node-name> -o jsonpath='{.spec.podCIDR}'
|
|
270
|
+
kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "10.244.0.0/16"}]'
|
|
271
|
+
kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "192.168.0.0/24"}]'
|
|
272
|
+
`);
|
|
273
|
+
if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
274
|
+
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
275
|
+
if (options.infoTraffic === true) {
|
|
276
|
+
for (const _deployId of deployList.split(',')) {
|
|
277
|
+
const deployId = _deployId.trim();
|
|
278
|
+
logger.info('', {
|
|
279
|
+
deployId,
|
|
280
|
+
env,
|
|
281
|
+
traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (options.rebuildClientsBundle === true) await UnderpostDeploy.API.rebuildClientsBundle(deployList);
|
|
287
|
+
if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
|
|
288
|
+
if (!options.replicas) options.replicas = 1;
|
|
289
|
+
if (options.sync) UnderpostDeploy.API.sync(deployList, options);
|
|
290
|
+
if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
|
|
291
|
+
if (options.infoRouter === true) logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
|
|
292
|
+
if (options.dashboardUpdate === true) await UnderpostDeploy.API.updateDashboardData(deployList, env, options);
|
|
293
|
+
if (options.infoRouter === true) return;
|
|
294
|
+
shellExec(`kubectl delete configmap underpost-config`);
|
|
295
|
+
shellExec(
|
|
296
|
+
`kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
|
|
297
|
+
);
|
|
298
|
+
const etcHost = (
|
|
299
|
+
concat,
|
|
300
|
+
) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
301
|
+
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
302
|
+
let concatHots = '';
|
|
303
|
+
|
|
304
|
+
for (const _deployId of deployList.split(',')) {
|
|
305
|
+
const deployId = _deployId.trim();
|
|
306
|
+
if (!deployId) continue;
|
|
307
|
+
if (options.expose === true) {
|
|
308
|
+
const svc = UnderpostDeploy.API.get(deployId, 'svc')[0];
|
|
309
|
+
const port = parseInt(svc[`PORT(S)`].split('/TCP')[0]);
|
|
310
|
+
logger.info(deployId, {
|
|
311
|
+
svc,
|
|
312
|
+
port,
|
|
313
|
+
});
|
|
314
|
+
shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!options.disableUpdateDeployment)
|
|
319
|
+
for (const version of options.versions.split(',')) {
|
|
320
|
+
shellExec(`sudo kubectl delete svc ${deployId}-${env}-${version}-service`);
|
|
321
|
+
shellExec(`sudo kubectl delete deployment ${deployId}-${env}-${version}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
325
|
+
for (const host of Object.keys(confServer)) {
|
|
326
|
+
shellExec(`sudo kubectl delete HTTPProxy ${host}`);
|
|
327
|
+
if (env === 'production' && options.cert === true) shellExec(`sudo kubectl delete Certificate ${host}`);
|
|
328
|
+
if (!options.remove === true && env === 'development') concatHots += ` ${host}`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const manifestsPath =
|
|
332
|
+
env === 'production'
|
|
333
|
+
? `engine-private/conf/${deployId}/build/production`
|
|
334
|
+
: `manifests/deployment/${deployId}-${env}`;
|
|
335
|
+
|
|
336
|
+
if (!options.remove === true) {
|
|
337
|
+
if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
|
|
338
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
|
|
339
|
+
if (env === 'production' && options.cert === true)
|
|
340
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
let renderHosts;
|
|
344
|
+
switch (process.platform) {
|
|
345
|
+
case 'linux':
|
|
346
|
+
{
|
|
347
|
+
switch (env) {
|
|
348
|
+
case 'development':
|
|
349
|
+
renderHosts = etcHost(concatHots);
|
|
350
|
+
fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
351
|
+
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
default:
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
|
|
360
|
+
default:
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
if (renderHosts)
|
|
364
|
+
logger.info(
|
|
365
|
+
`
|
|
366
|
+
` + renderHosts,
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
get(deployId, kindType = 'pods') {
|
|
370
|
+
const raw = shellExec(`sudo kubectl get ${kindType} --all-namespaces -o wide`, {
|
|
371
|
+
stdout: true,
|
|
372
|
+
disableLog: true,
|
|
373
|
+
silent: true,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const heads = raw
|
|
377
|
+
.split(`\n`)[0]
|
|
378
|
+
.split(' ')
|
|
379
|
+
.filter((_r) => _r.trim());
|
|
380
|
+
|
|
381
|
+
const pods = raw
|
|
382
|
+
.split(`\n`)
|
|
383
|
+
.filter((r) => (deployId ? r.match(deployId) : r.trim() && !r.match('NAME')))
|
|
384
|
+
.map((r) => r.split(' ').filter((_r) => _r.trim()));
|
|
385
|
+
|
|
386
|
+
const result = [];
|
|
387
|
+
|
|
388
|
+
for (const row of pods) {
|
|
389
|
+
const pod = {};
|
|
390
|
+
let index = -1;
|
|
391
|
+
for (const head of heads) {
|
|
392
|
+
index++;
|
|
393
|
+
pod[head] = row[index];
|
|
394
|
+
}
|
|
395
|
+
result.push(pod);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return result;
|
|
399
|
+
},
|
|
400
|
+
rebuildClientsBundle(deployList) {
|
|
401
|
+
for (const _deployId of deployList.split(',')) {
|
|
402
|
+
const deployId = _deployId.trim();
|
|
403
|
+
const repoName = `engine-${deployId.split('-')[1]}`;
|
|
404
|
+
|
|
405
|
+
shellExec(`underpost script set ${deployId}-client-build '
|
|
406
|
+
cd /home/dd/engine &&
|
|
407
|
+
git checkout . &&
|
|
408
|
+
underpost pull . underpostnet/${repoName} &&
|
|
409
|
+
underpost pull ./engine-private underpostnet/${repoName}-private &&
|
|
410
|
+
underpost env ${deployId} production &&
|
|
411
|
+
node bin/deploy build-full-client ${deployId}
|
|
412
|
+
'`);
|
|
413
|
+
|
|
414
|
+
shellExec(`node bin script run ${deployId}-client-build --itc --pod-name ${deployId}`);
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
resourcesFactory() {
|
|
418
|
+
return {
|
|
419
|
+
requests: {
|
|
420
|
+
memory: UnderpostRootEnv.API.get('resources.requests.memory'),
|
|
421
|
+
cpu: UnderpostRootEnv.API.get('resources.requests.cpu'),
|
|
422
|
+
},
|
|
423
|
+
limits: {
|
|
424
|
+
memory: UnderpostRootEnv.API.get('resources.limits.memory'),
|
|
425
|
+
cpu: UnderpostRootEnv.API.get('resources.limits.cpu'),
|
|
426
|
+
},
|
|
427
|
+
totalPods: UnderpostRootEnv.API.get('total-pods'),
|
|
428
|
+
};
|
|
429
|
+
},
|
|
430
|
+
async updateDashboardData(deployList, env, options) {
|
|
431
|
+
try {
|
|
432
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
433
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
434
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
435
|
+
const { db } = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'))[host][
|
|
436
|
+
path
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
await DataBaseProvider.load({ apis: ['instance'], host, path, db });
|
|
440
|
+
|
|
441
|
+
/** @type {import('../api/instance/instance.model.js').InstanceModel} */
|
|
442
|
+
const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
|
|
443
|
+
|
|
444
|
+
await Instance.deleteMany();
|
|
445
|
+
|
|
446
|
+
for (const _deployId of deployList.split(',')) {
|
|
447
|
+
const deployId = _deployId.trim();
|
|
448
|
+
if (!deployId) continue;
|
|
449
|
+
const confServer = loadReplicas(
|
|
450
|
+
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
451
|
+
'proxy',
|
|
452
|
+
);
|
|
453
|
+
const router = await UnderpostDeploy.API.routerFactory(deployId, env);
|
|
454
|
+
const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
|
|
455
|
+
|
|
456
|
+
for (const host of Object.keys(confServer)) {
|
|
457
|
+
for (const { path, port } of pathPortAssignmentData[host]) {
|
|
458
|
+
if (!confServer[host][path]) continue;
|
|
459
|
+
|
|
460
|
+
const { client, runtime, apis } = confServer[host][path];
|
|
461
|
+
|
|
462
|
+
const body = {
|
|
463
|
+
deployId,
|
|
464
|
+
host,
|
|
465
|
+
path,
|
|
466
|
+
port,
|
|
467
|
+
client,
|
|
468
|
+
runtime,
|
|
469
|
+
apis,
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
logger.info('save', body);
|
|
473
|
+
|
|
474
|
+
await new Instance(body).save();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
|
|
480
|
+
} catch (error) {
|
|
481
|
+
logger.error(error, error.stack);
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export default UnderpostDeploy;
|
package/src/cli/env.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getNpmRootPath, writeEnv } from '../server/conf.js';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { loggerFactory } from '../server/logger.js';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
const logger = loggerFactory(import.meta);
|
|
9
|
+
|
|
10
|
+
class UnderpostRootEnv {
|
|
11
|
+
static API = {
|
|
12
|
+
set(key, value) {
|
|
13
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
14
|
+
const envPath = `${exeRootPath}/.env`;
|
|
15
|
+
let env = {};
|
|
16
|
+
if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
17
|
+
env[key] = value;
|
|
18
|
+
writeEnv(envPath, env);
|
|
19
|
+
},
|
|
20
|
+
delete(key) {
|
|
21
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
22
|
+
const envPath = `${exeRootPath}/.env`;
|
|
23
|
+
let env = {};
|
|
24
|
+
if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
25
|
+
delete env[key];
|
|
26
|
+
writeEnv(envPath, env);
|
|
27
|
+
},
|
|
28
|
+
get(key) {
|
|
29
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
30
|
+
const envPath = `${exeRootPath}/.env`;
|
|
31
|
+
if (!fs.existsSync(envPath)) {
|
|
32
|
+
logger.error(`Unable to find underpost root environment`);
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
36
|
+
logger.info(`${key}(${typeof env[key]})`, env[key]);
|
|
37
|
+
return env[key];
|
|
38
|
+
},
|
|
39
|
+
list() {
|
|
40
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
41
|
+
const envPath = `${exeRootPath}/.env`;
|
|
42
|
+
if (!fs.existsSync(envPath)) {
|
|
43
|
+
logger.error(`Unable to find underpost root environment`);
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
47
|
+
logger.info('underpost root', env);
|
|
48
|
+
return env;
|
|
49
|
+
},
|
|
50
|
+
clean() {
|
|
51
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
52
|
+
const envPath = `${exeRootPath}/.env`;
|
|
53
|
+
fs.removeSync(envPath);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default UnderpostRootEnv;
|