@underpostnet/underpost 2.8.4 → 2.8.6
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/.github/workflows/ghpkg.yml +12 -45
- package/.github/workflows/npmpkg.yml +67 -0
- package/.github/workflows/publish.yml +5 -5
- package/.github/workflows/pwa-microservices-template.page.yml +2 -1
- package/.github/workflows/pwa-microservices-template.test.yml +2 -2
- package/.vscode/settings.json +10 -1
- package/CHANGELOG.md +40 -0
- package/Dockerfile +6 -27
- package/bin/build.js +73 -165
- package/bin/deploy.js +76 -101
- package/bin/file.js +29 -15
- package/bin/hwt.js +0 -10
- package/bin/index.js +191 -32
- package/bin/util.js +0 -15
- package/bin/vs.js +1 -0
- package/conf.js +0 -2
- package/docker-compose.yml +1 -1
- package/manifests/kind-config-dev.yaml +12 -0
- package/manifests/{core/underpost-engine-mongodb-backup-cronjob.yaml → mongodb/backup-cronjob.yaml} +14 -12
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +23 -0
- package/manifests/{core/underpost-engine-statefulset.yaml → mongodb/statefulset.yaml} +34 -0
- package/manifests/mongodb-4.4/kustomization.yaml +7 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
- package/manifests/valkey/kustomization.yaml +2 -2
- package/package.json +22 -4
- package/src/api/core/core.service.js +1 -1
- package/src/cli/cluster.js +202 -0
- package/src/cli/cron.js +90 -0
- package/src/cli/db.js +212 -0
- package/src/cli/deploy.js +318 -0
- package/src/cli/env.js +52 -0
- package/src/cli/fs.js +149 -0
- package/src/cli/image.js +148 -0
- package/src/cli/repository.js +125 -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/Auth.js +22 -4
- package/src/client/components/core/CalendarCore.js +12 -1
- package/src/client/components/core/CommonJs.js +134 -2
- package/src/client/components/core/Css.js +1 -0
- package/src/client/components/core/CssCore.js +2 -4
- package/src/client/components/core/Docs.js +1 -2
- package/src/client/components/core/Input.js +5 -3
- package/src/client/components/core/LoadingAnimation.js +8 -1
- package/src/client/components/core/Modal.js +30 -7
- package/src/client/components/core/Panel.js +8 -6
- package/src/client/components/core/PanelForm.js +23 -7
- package/src/client/components/core/Scroll.js +1 -0
- package/src/client/components/core/Translate.js +4 -0
- package/src/client/components/core/VanillaJs.js +0 -9
- package/src/client/components/core/Worker.js +34 -31
- package/src/client/services/core/core.service.js +15 -10
- package/src/client/ssr/Render.js +4 -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 +17 -1
- package/src/index.js +85 -26
- package/src/server/backup.js +49 -93
- package/src/server/client-build.js +33 -33
- package/src/server/client-formatted.js +6 -3
- package/src/server/conf.js +82 -199
- package/src/server/dns.js +29 -53
- package/src/server/downloader.js +0 -8
- package/src/server/logger.js +7 -7
- package/src/server/network.js +17 -7
- package/src/server/runtime.js +24 -23
- package/test/api.test.js +0 -8
- package/manifests/core/kustomization.yaml +0 -11
- package/src/dns.js +0 -22
- package/src/server/project.js +0 -39
- package/startup.cjs +0 -12
- /package/manifests/deployment/{mongo-express.yaml → mongo-express/deployment.yaml} +0 -0
- /package/manifests/deployment/{phpmyadmin.yaml → phpmyadmin/deployment.yaml} +0 -0
- /package/manifests/{core/underpost-engine-backup-access.yaml → mongodb/backup-access.yaml} +0 -0
- /package/manifests/{core/underpost-engine-backup-pv-pvc.yaml → mongodb/backup-pv-pvc.yaml} +0 -0
- /package/manifests/{core/underpost-engine-mongodb-configmap.yaml → mongodb/configmap.yaml} +0 -0
- /package/manifests/{core/underpost-engine-headless-service.yaml → mongodb/headless-service.yaml} +0 -0
- /package/manifests/{core/underpost-engine-pv-pvc.yaml → mongodb-4.4/pv-pvc.yaml} +0 -0
- /package/manifests/valkey/{underpost-engine-valkey-service.yaml → service.yaml} +0 -0
- /package/manifests/valkey/{underpost-engine-valkey-statefulset.yaml → statefulset.yaml} +0 -0
package/src/cli/db.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { mergeFile, splitFileFactory } from '../server/conf.js';
|
|
2
|
+
import { loggerFactory } from '../server/logger.js';
|
|
3
|
+
import { shellExec } from '../server/process.js';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import UnderpostDeploy from './deploy.js';
|
|
6
|
+
|
|
7
|
+
const logger = loggerFactory(import.meta);
|
|
8
|
+
|
|
9
|
+
class UnderpostDB {
|
|
10
|
+
static API = {
|
|
11
|
+
async callback(
|
|
12
|
+
deployList = 'default',
|
|
13
|
+
options = {
|
|
14
|
+
import: false,
|
|
15
|
+
export: false,
|
|
16
|
+
podName: false,
|
|
17
|
+
ns: false,
|
|
18
|
+
collection: '',
|
|
19
|
+
outPath: '',
|
|
20
|
+
drop: false,
|
|
21
|
+
preserveUUID: false,
|
|
22
|
+
git: false,
|
|
23
|
+
},
|
|
24
|
+
) {
|
|
25
|
+
const newBackupTimestamp = new Date().getTime();
|
|
26
|
+
const nameSpace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
|
|
27
|
+
for (const _deployId of deployList.split(',')) {
|
|
28
|
+
const deployId = _deployId.trim();
|
|
29
|
+
if (!deployId) continue;
|
|
30
|
+
const dbs = {};
|
|
31
|
+
const repoName = `engine-${deployId.split('dd-')[1]}-cron-backups`;
|
|
32
|
+
|
|
33
|
+
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
34
|
+
for (const host of Object.keys(confServer)) {
|
|
35
|
+
for (const path of Object.keys(confServer[host])) {
|
|
36
|
+
const { db } = confServer[host][path];
|
|
37
|
+
if (db) {
|
|
38
|
+
const { provider, name, user, password } = db;
|
|
39
|
+
if (!dbs[provider]) dbs[provider] = {};
|
|
40
|
+
|
|
41
|
+
if (!(name in dbs[provider]))
|
|
42
|
+
dbs[provider][name] = { user, password, hostFolder: host + path.replaceAll('/', '-') };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
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}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const provider of Object.keys(dbs)) {
|
|
54
|
+
for (const dbName of Object.keys(dbs[provider])) {
|
|
55
|
+
const { hostFolder, user, password } = dbs[provider][dbName];
|
|
56
|
+
if (hostFolder) {
|
|
57
|
+
logger.info('', { hostFolder, provider, dbName });
|
|
58
|
+
|
|
59
|
+
const backUpPath = `../${repoName}/${hostFolder}`;
|
|
60
|
+
if (!fs.existsSync(backUpPath)) fs.mkdirSync(backUpPath, { recursive: true });
|
|
61
|
+
shellExec(`cd ${backUpPath} && find . -type d -empty -delete`); // delete empty folders
|
|
62
|
+
const times = await fs.readdir(backUpPath);
|
|
63
|
+
const currentBackupTimestamp = Math.max(...times.map((t) => parseInt(t)).filter((t) => !isNaN(t)));
|
|
64
|
+
dbs[provider][dbName].currentBackupTimestamp = currentBackupTimestamp;
|
|
65
|
+
const removeBackupTimestamp = Math.min(...times.map((t) => parseInt(t)).filter((t) => !isNaN(t)));
|
|
66
|
+
|
|
67
|
+
const sqlContainerPath = `/home/${dbName}.sql`;
|
|
68
|
+
const _fromPartsParts = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}-parths.json`;
|
|
69
|
+
const _toSqlPath = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}.sql`;
|
|
70
|
+
const _toNewSqlPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}.sql`;
|
|
71
|
+
const _toBsonPath = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}`;
|
|
72
|
+
const _toNewBsonPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}`;
|
|
73
|
+
|
|
74
|
+
if (options.import === true && fs.existsSync(_fromPartsParts) && !fs.existsSync(_toSqlPath)) {
|
|
75
|
+
const names = JSON.parse(fs.readFileSync(_fromPartsParts, 'utf8')).map((_path) => {
|
|
76
|
+
return `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${_path.split('/').pop()}`;
|
|
77
|
+
});
|
|
78
|
+
logger.info('merge Back Up paths', {
|
|
79
|
+
_fromPartsParts,
|
|
80
|
+
_toSqlPath,
|
|
81
|
+
names,
|
|
82
|
+
});
|
|
83
|
+
await mergeFile(names, _toSqlPath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.export === true && times.length >= 5) {
|
|
87
|
+
logger.info('remove', `../${repoName}/${hostFolder}/${removeBackupTimestamp}`);
|
|
88
|
+
fs.removeSync(`../${repoName}/${hostFolder}/${removeBackupTimestamp}`);
|
|
89
|
+
logger.info('create', `../${repoName}/${hostFolder}/${newBackupTimestamp}`);
|
|
90
|
+
fs.mkdirSync(`../${repoName}/${hostFolder}/${newBackupTimestamp}`, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
switch (provider) {
|
|
94
|
+
case 'mariadb': {
|
|
95
|
+
const podNames =
|
|
96
|
+
options.podName && typeof options.podName === 'string'
|
|
97
|
+
? options.podName.split(',')
|
|
98
|
+
: UnderpostDeploy.API.get('mariadb'); // `mariadb-statefulset-0`;
|
|
99
|
+
const serviceName = 'mariadb';
|
|
100
|
+
for (const podNameData of [podNames[0]]) {
|
|
101
|
+
const podName = podNameData.NAME;
|
|
102
|
+
if (options.import === true) {
|
|
103
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf /${dbName}.sql"`);
|
|
104
|
+
shellExec(`sudo kubectl cp ${_toSqlPath} ${nameSpace}/${podName}:/${dbName}.sql`);
|
|
105
|
+
const cmd = `mariadb -u ${user} -p${password} ${dbName} < /${dbName}.sql`;
|
|
106
|
+
shellExec(
|
|
107
|
+
`kubectl exec -i ${podName} -- ${serviceName} -p${password} -e 'CREATE DATABASE ${dbName};'`,
|
|
108
|
+
);
|
|
109
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
110
|
+
}
|
|
111
|
+
if (options.export === true) {
|
|
112
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf ${sqlContainerPath}"`);
|
|
113
|
+
const cmd = `mariadb-dump --user=${user} --password=${password} --lock-tables ${dbName} > ${sqlContainerPath}`;
|
|
114
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
115
|
+
shellExec(
|
|
116
|
+
`sudo kubectl cp ${nameSpace}/${podName}:${sqlContainerPath} ${
|
|
117
|
+
options.outPath ? options.outPath : _toNewSqlPath
|
|
118
|
+
}`,
|
|
119
|
+
);
|
|
120
|
+
await splitFileFactory(dbName, options.outPath ? options.outPath : _toNewSqlPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case 'mongoose': {
|
|
127
|
+
if (options.import === true) {
|
|
128
|
+
const podNames =
|
|
129
|
+
options.podName && typeof options.podName === 'string'
|
|
130
|
+
? options.podName.split(',')
|
|
131
|
+
: UnderpostDeploy.API.get('mongo');
|
|
132
|
+
// `mongodb-0`;
|
|
133
|
+
for (const podNameData of [podNames[0]]) {
|
|
134
|
+
const podName = podNameData.NAME;
|
|
135
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf /${dbName}"`);
|
|
136
|
+
shellExec(
|
|
137
|
+
`sudo kubectl cp ${
|
|
138
|
+
options.outPath ? options.outPath : _toBsonPath
|
|
139
|
+
} ${nameSpace}/${podName}:/${dbName}`,
|
|
140
|
+
);
|
|
141
|
+
const cmd = `mongorestore -d ${dbName} /${dbName}${options.drop ? ' --drop' : ''}${
|
|
142
|
+
options.preserveUUID ? ' --preserveUUID' : ''
|
|
143
|
+
}`;
|
|
144
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (options.export === true) {
|
|
148
|
+
const podNames =
|
|
149
|
+
options.podName && typeof options.podName === 'string'
|
|
150
|
+
? options.podName.split(',')
|
|
151
|
+
: UnderpostDeploy.API.get('mongo'); // `backup-access`;
|
|
152
|
+
for (const podNameData of [podNames[0]]) {
|
|
153
|
+
const podName = podNameData.NAME;
|
|
154
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf /${dbName}"`);
|
|
155
|
+
if (options.collections)
|
|
156
|
+
for (const collection of options.collections)
|
|
157
|
+
shellExec(
|
|
158
|
+
`sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} --collection ${collection} -o /${dbName}"`,
|
|
159
|
+
);
|
|
160
|
+
else shellExec(`sudo kubectl exec -i ${podName} -- sh -c "mongodump -d ${dbName} -o /${dbName}"`);
|
|
161
|
+
shellExec(
|
|
162
|
+
`sudo kubectl cp ${nameSpace}/${podName}:/${dbName} ${
|
|
163
|
+
options.outPath ? options.outPath : _toNewBsonPath
|
|
164
|
+
}`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (false) {
|
|
168
|
+
const containerBaseBackupPath = '/backup';
|
|
169
|
+
let timeFolder = shellExec(
|
|
170
|
+
`sudo kubectl exec -i ${podName} -- sh -c "cd ${containerBaseBackupPath} && ls -a"`,
|
|
171
|
+
{
|
|
172
|
+
stdout: true,
|
|
173
|
+
disableLog: false,
|
|
174
|
+
silent: true,
|
|
175
|
+
},
|
|
176
|
+
).split(`\n`);
|
|
177
|
+
timeFolder = timeFolder[timeFolder.length - 2];
|
|
178
|
+
if (timeFolder === '..') {
|
|
179
|
+
logger.warn(`Cannot backup available`, { timeFolder });
|
|
180
|
+
} else {
|
|
181
|
+
shellExec(
|
|
182
|
+
`sudo kubectl cp ${nameSpace}/${podName}:${containerBaseBackupPath}/${timeFolder}/${dbName} ${_toNewBsonPath}`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
default:
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (options.export === true && options.git === true) {
|
|
197
|
+
shellExec(`cd ../${repoName} && git add .`);
|
|
198
|
+
shellExec(
|
|
199
|
+
`underpost cmt ../${repoName} backup '' '${new Date(newBackupTimestamp).toLocaleDateString()} ${new Date(
|
|
200
|
+
newBackupTimestamp,
|
|
201
|
+
).toLocaleTimeString()}'`,
|
|
202
|
+
);
|
|
203
|
+
shellExec(`cd ../${repoName} && underpost push . ${process.env.GITHUB_USERNAME}/${repoName}`, {
|
|
204
|
+
disableLog: true,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export default UnderpostDB;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildKindPorts,
|
|
3
|
+
buildPortProxyRouter,
|
|
4
|
+
buildProxyRouter,
|
|
5
|
+
Config,
|
|
6
|
+
getDataDeploy,
|
|
7
|
+
loadReplicas,
|
|
8
|
+
} from '../server/conf.js';
|
|
9
|
+
import { loggerFactory } from '../server/logger.js';
|
|
10
|
+
import { shellExec } from '../server/process.js';
|
|
11
|
+
import fs from 'fs-extra';
|
|
12
|
+
import dotenv from 'dotenv';
|
|
13
|
+
import Underpost from '../index.js';
|
|
14
|
+
|
|
15
|
+
const logger = loggerFactory(import.meta);
|
|
16
|
+
|
|
17
|
+
class UnderpostDeploy {
|
|
18
|
+
static API = {
|
|
19
|
+
sync(deployList) {
|
|
20
|
+
const deployGroupId = 'dd.tmp';
|
|
21
|
+
fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
|
|
22
|
+
return getDataDeploy({
|
|
23
|
+
buildSingleReplica: true,
|
|
24
|
+
deployGroupId,
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
async routerFactory(deployList, env) {
|
|
28
|
+
const initEnvPath = `./engine-private/conf/${deployList.split(',')[0]}/.env.${env}`;
|
|
29
|
+
const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
|
|
30
|
+
process.env.PORT = initEnvObj.PORT;
|
|
31
|
+
process.env.NODE_ENV = env;
|
|
32
|
+
await Config.build(undefined, 'proxy', deployList);
|
|
33
|
+
return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
|
|
34
|
+
},
|
|
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
|
+
kind: Deployment
|
|
56
|
+
metadata:
|
|
57
|
+
name: ${deployId}-${env}
|
|
58
|
+
labels:
|
|
59
|
+
app: ${deployId}-${env}
|
|
60
|
+
spec:
|
|
61
|
+
replicas: 2
|
|
62
|
+
selector:
|
|
63
|
+
matchLabels:
|
|
64
|
+
app: ${deployId}-${env}
|
|
65
|
+
template:
|
|
66
|
+
metadata:
|
|
67
|
+
labels:
|
|
68
|
+
app: ${deployId}-${env}
|
|
69
|
+
spec:
|
|
70
|
+
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}
|
|
84
|
+
---
|
|
85
|
+
apiVersion: v1
|
|
86
|
+
kind: Service
|
|
87
|
+
metadata:
|
|
88
|
+
name: ${deployId}-${env}-service
|
|
89
|
+
spec:
|
|
90
|
+
selector:
|
|
91
|
+
app: ${deployId}-${env}
|
|
92
|
+
ports:
|
|
93
|
+
type: LoadBalancer`.split('ports:');
|
|
94
|
+
deploymentYamlParts[1] =
|
|
95
|
+
buildKindPorts(fromPort, toPort) +
|
|
96
|
+
` type: LoadBalancer
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
fs.writeFileSync(
|
|
100
|
+
`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`,
|
|
101
|
+
deploymentYamlParts.join(`ports:
|
|
102
|
+
`),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
let proxyYaml = '';
|
|
106
|
+
let secretYaml = '';
|
|
107
|
+
|
|
108
|
+
for (const host of Object.keys(confServer)) {
|
|
109
|
+
if (env === 'production')
|
|
110
|
+
secretYaml += `
|
|
111
|
+
---
|
|
112
|
+
apiVersion: cert-manager.io/v1
|
|
113
|
+
kind: Certificate
|
|
114
|
+
metadata:
|
|
115
|
+
name: ${host}
|
|
116
|
+
spec:
|
|
117
|
+
commonName: ${host}
|
|
118
|
+
dnsNames:
|
|
119
|
+
- ${host}
|
|
120
|
+
issuerRef:
|
|
121
|
+
name: letsencrypt-prod
|
|
122
|
+
kind: ClusterIssuer
|
|
123
|
+
secretName: ${host}`;
|
|
124
|
+
|
|
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 });
|
|
146
|
+
proxyYaml += `
|
|
147
|
+
---
|
|
148
|
+
apiVersion: projectcontour.io/v1
|
|
149
|
+
kind: HTTPProxy
|
|
150
|
+
metadata:
|
|
151
|
+
name: ${host}
|
|
152
|
+
spec:
|
|
153
|
+
virtualhost:
|
|
154
|
+
fqdn: ${host}${
|
|
155
|
+
env === 'development'
|
|
156
|
+
? ''
|
|
157
|
+
: `
|
|
158
|
+
tls:
|
|
159
|
+
secretName: ${host}`
|
|
160
|
+
}
|
|
161
|
+
routes:`;
|
|
162
|
+
for (const conditionObj of pathPortConditions) {
|
|
163
|
+
const { path, port } = conditionObj;
|
|
164
|
+
proxyYaml += `
|
|
165
|
+
- conditions:
|
|
166
|
+
- prefix: ${path}
|
|
167
|
+
enableWebsockets: true
|
|
168
|
+
services:
|
|
169
|
+
- name: ${deployId}-${env}-service
|
|
170
|
+
port: ${port}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
|
|
174
|
+
fs.writeFileSync(yamlPath, proxyYaml, 'utf8');
|
|
175
|
+
if (env === 'production') {
|
|
176
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
|
|
177
|
+
fs.writeFileSync(yamlPath, secretYaml, 'utf8');
|
|
178
|
+
} else {
|
|
179
|
+
const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml'];
|
|
180
|
+
for (const file of deploymentsFiles) {
|
|
181
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
|
|
182
|
+
fs.copyFileSync(
|
|
183
|
+
`./engine-private/conf/${deployId}/build/${env}/${file}`,
|
|
184
|
+
`./manifests/deployment/${deployId}-${env}/${file}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
async callback(
|
|
192
|
+
deployList = 'default',
|
|
193
|
+
env = 'development',
|
|
194
|
+
options = {
|
|
195
|
+
remove: false,
|
|
196
|
+
infoRouter: false,
|
|
197
|
+
sync: false,
|
|
198
|
+
buildManifest: false,
|
|
199
|
+
infoUtil: false,
|
|
200
|
+
expose: false,
|
|
201
|
+
cert: false,
|
|
202
|
+
version: '',
|
|
203
|
+
},
|
|
204
|
+
) {
|
|
205
|
+
if (options.infoUtil === true)
|
|
206
|
+
return logger.info(`
|
|
207
|
+
kubectl rollout restart deployment/deployment-name
|
|
208
|
+
kubectl rollout undo deployment/deployment-name
|
|
209
|
+
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
|
|
210
|
+
`);
|
|
211
|
+
if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
212
|
+
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));
|
|
217
|
+
const etcHost = (
|
|
218
|
+
concat,
|
|
219
|
+
) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
220
|
+
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
221
|
+
let concatHots = '';
|
|
222
|
+
|
|
223
|
+
for (const _deployId of deployList.split(',')) {
|
|
224
|
+
const deployId = _deployId.trim();
|
|
225
|
+
if (!deployId) continue;
|
|
226
|
+
if (options.expose === true) {
|
|
227
|
+
const svc = UnderpostDeploy.API.get(deployId, 'svc')[0];
|
|
228
|
+
const port = parseInt(svc[`PORT(S)`].split('/TCP')[0]);
|
|
229
|
+
logger.info(deployId, {
|
|
230
|
+
svc,
|
|
231
|
+
port,
|
|
232
|
+
});
|
|
233
|
+
shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
shellExec(`sudo kubectl delete svc ${deployId}-${env}-service`);
|
|
237
|
+
shellExec(`sudo kubectl delete deployment ${deployId}-${env}`);
|
|
238
|
+
|
|
239
|
+
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
240
|
+
for (const host of Object.keys(confServer)) {
|
|
241
|
+
shellExec(`sudo kubectl delete HTTPProxy ${host}`);
|
|
242
|
+
if (env === 'production' && options.cert === true) shellExec(`sudo kubectl delete Certificate ${host}`);
|
|
243
|
+
if (!options.remove === true && env === 'development') concatHots += ` ${host}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const manifestsPath =
|
|
247
|
+
env === 'production'
|
|
248
|
+
? `engine-private/conf/${deployId}/build/production`
|
|
249
|
+
: `manifests/deployment/${deployId}-${env}`;
|
|
250
|
+
|
|
251
|
+
if (!options.remove === true) {
|
|
252
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
|
|
253
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
|
|
254
|
+
if (env === 'production' && options.cert === true)
|
|
255
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
let renderHosts;
|
|
259
|
+
switch (process.platform) {
|
|
260
|
+
case 'linux':
|
|
261
|
+
{
|
|
262
|
+
switch (env) {
|
|
263
|
+
case 'development':
|
|
264
|
+
renderHosts = etcHost(concatHots);
|
|
265
|
+
fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
266
|
+
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
default:
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
default:
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
if (renderHosts)
|
|
279
|
+
logger.info(
|
|
280
|
+
`
|
|
281
|
+
` + renderHosts,
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
get(deployId, kindType = 'pods') {
|
|
285
|
+
const raw = shellExec(`sudo kubectl get ${kindType} --all-namespaces -o wide`, {
|
|
286
|
+
stdout: true,
|
|
287
|
+
disableLog: true,
|
|
288
|
+
silent: true,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const heads = raw
|
|
292
|
+
.split(`\n`)[0]
|
|
293
|
+
.split(' ')
|
|
294
|
+
.filter((_r) => _r.trim());
|
|
295
|
+
|
|
296
|
+
const pods = raw
|
|
297
|
+
.split(`\n`)
|
|
298
|
+
.filter((r) => (deployId ? r.match(deployId) : r.trim() && !r.match('NAME')))
|
|
299
|
+
.map((r) => r.split(' ').filter((_r) => _r.trim()));
|
|
300
|
+
|
|
301
|
+
const result = [];
|
|
302
|
+
|
|
303
|
+
for (const row of pods) {
|
|
304
|
+
const pod = {};
|
|
305
|
+
let index = -1;
|
|
306
|
+
for (const head of heads) {
|
|
307
|
+
index++;
|
|
308
|
+
pod[head] = row[index];
|
|
309
|
+
}
|
|
310
|
+
result.push(pod);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return result;
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export default UnderpostDeploy;
|
package/src/cli/env.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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)) return logger.error(`Unable to find underpost root environment`);
|
|
32
|
+
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
33
|
+
logger.info('underpost root', { [key]: env[key] });
|
|
34
|
+
return env[key];
|
|
35
|
+
},
|
|
36
|
+
list() {
|
|
37
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
38
|
+
const envPath = `${exeRootPath}/.env`;
|
|
39
|
+
if (!fs.existsSync(envPath)) return logger.error(`Unable to find underpost root environment`);
|
|
40
|
+
const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
41
|
+
logger.info('underpost root', env);
|
|
42
|
+
return env;
|
|
43
|
+
},
|
|
44
|
+
clean() {
|
|
45
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
46
|
+
const envPath = `${exeRootPath}/.env`;
|
|
47
|
+
fs.removeSync(envPath);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default UnderpostRootEnv;
|