underpost 2.8.452 → 2.8.481
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 +6 -50
- package/.github/workflows/npmpkg.yml +67 -0
- package/.github/workflows/pwa-microservices-template.page.yml +2 -1
- package/.github/workflows/pwa-microservices-template.test.yml +2 -2
- package/Dockerfile +3 -3
- package/bin/build.js +32 -191
- package/bin/deploy.js +3 -19
- package/bin/file.js +13 -2
- package/bin/index.js +30 -3
- package/docker-compose.yml +1 -1
- package/package.json +12 -3
- package/src/cli/cluster.js +3 -3
- package/src/cli/db.js +148 -0
- package/src/cli/deploy.js +277 -0
- package/src/cli/image.js +21 -14
- package/src/cli/repository.js +11 -17
- package/src/cli/test.js +52 -1
- package/src/client/components/core/Auth.js +22 -4
- package/src/client/components/core/CommonJs.js +9 -0
- package/src/client/components/core/Modal.js +0 -1
- 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/index.js +17 -1
- package/src/server/conf.js +25 -15
- package/src/server/logger.js +1 -0
- package/src/server/network.js +16 -6
package/src/cli/cluster.js
CHANGED
|
@@ -43,7 +43,7 @@ class UnderpostCluster {
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
const testClusterInit = shellExec(`kubectl get pods --all-namespaces -o wide`, {
|
|
46
|
-
|
|
46
|
+
disableLog: true,
|
|
47
47
|
silent: true,
|
|
48
48
|
stdout: true,
|
|
49
49
|
});
|
|
@@ -93,7 +93,7 @@ class UnderpostCluster {
|
|
|
93
93
|
silent: true,
|
|
94
94
|
stdout: true,
|
|
95
95
|
disableLog: true,
|
|
96
|
-
}).match(`mongodb-
|
|
96
|
+
}).match(`mongodb-1 1/1 Running`)
|
|
97
97
|
)
|
|
98
98
|
return resolve();
|
|
99
99
|
return monitor();
|
|
@@ -110,7 +110,7 @@ class UnderpostCluster {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
shellExec(
|
|
113
|
-
`kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
|
|
113
|
+
`sudo kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
|
|
114
114
|
--eval 'use admin' \
|
|
115
115
|
--eval 'rs.initiate(${JSON.stringify(mongoConfig)})' \
|
|
116
116
|
--eval 'rs.status()'`,
|
package/src/cli/db.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
|
|
6
|
+
const logger = loggerFactory(import.meta);
|
|
7
|
+
|
|
8
|
+
class UnderpostDB {
|
|
9
|
+
static API = {
|
|
10
|
+
async callback(deployList = 'default', options = { import: false, export: false }) {
|
|
11
|
+
const newBackupTimestamp = new Date().getTime();
|
|
12
|
+
const nameSpace = 'default';
|
|
13
|
+
for (const _deployId of deployList.split(',')) {
|
|
14
|
+
const deployId = _deployId.trim();
|
|
15
|
+
if (!deployId) continue;
|
|
16
|
+
const dbs = {};
|
|
17
|
+
const repoName = `engine-${deployId.split('dd-')[1]}-cron-backups`;
|
|
18
|
+
|
|
19
|
+
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
20
|
+
for (const host of Object.keys(confServer)) {
|
|
21
|
+
for (const path of Object.keys(confServer[host])) {
|
|
22
|
+
const { db } = confServer[host][path];
|
|
23
|
+
if (db) {
|
|
24
|
+
const { provider, name, user, password } = db;
|
|
25
|
+
if (!dbs[provider]) dbs[provider] = {};
|
|
26
|
+
|
|
27
|
+
if (!(name in dbs[provider]))
|
|
28
|
+
dbs[provider][name] = { user, password, hostFolder: host + path.replaceAll('/', '-') };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(`../${repoName}`)) {
|
|
34
|
+
shellExec(`cd .. && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}`);
|
|
35
|
+
} else {
|
|
36
|
+
shellExec(`cd ../${repoName} && underpost pull . ${process.env.GITHUB_USERNAME}/${repoName}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const provider of Object.keys(dbs)) {
|
|
40
|
+
for (const dbName of Object.keys(dbs[provider])) {
|
|
41
|
+
const { hostFolder, user, password } = dbs[provider][dbName];
|
|
42
|
+
if (hostFolder) {
|
|
43
|
+
logger.info('', { hostFolder, provider, dbName });
|
|
44
|
+
|
|
45
|
+
const backUpPath = `../${repoName}/${hostFolder}`;
|
|
46
|
+
const times = await fs.readdir(backUpPath);
|
|
47
|
+
const currentBackupTimestamp = Math.max(...times.map((t) => parseInt(t)));
|
|
48
|
+
dbs[provider][dbName].currentBackupTimestamp = currentBackupTimestamp;
|
|
49
|
+
const removeBackupTimestamp = Math.min(...times.map((t) => parseInt(t)));
|
|
50
|
+
|
|
51
|
+
const sqlContainerPath = `/home/${dbName}.sql`;
|
|
52
|
+
const _fromPartsParts = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}-parths.json`;
|
|
53
|
+
const _toSqlPath = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}.sql`;
|
|
54
|
+
const _toNewSqlPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}.sql`;
|
|
55
|
+
const _toBsonPath = `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${dbName}`;
|
|
56
|
+
const _toNewBsonPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}`;
|
|
57
|
+
|
|
58
|
+
if (options.import === true && fs.existsSync(_fromPartsParts) && !fs.existsSync(_toSqlPath)) {
|
|
59
|
+
const names = JSON.parse(fs.readFileSync(_fromPartsParts, 'utf8')).map((_path) => {
|
|
60
|
+
return `../${repoName}/${hostFolder}/${currentBackupTimestamp}/${_path.split('/').pop()}`;
|
|
61
|
+
});
|
|
62
|
+
logger.info('merge Back Up paths', {
|
|
63
|
+
_fromPartsParts,
|
|
64
|
+
_toSqlPath,
|
|
65
|
+
names,
|
|
66
|
+
});
|
|
67
|
+
await mergeFile(names, _toSqlPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (options.export === true && times.length >= 5) {
|
|
71
|
+
fs.removeSync(`../${repoName}/${hostFolder}/${removeBackupTimestamp}`);
|
|
72
|
+
fs.mkdirSync(`../${repoName}/${hostFolder}/${newBackupTimestamp}`, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
switch (provider) {
|
|
76
|
+
case 'mariadb': {
|
|
77
|
+
const podName = `mariadb-statefulset-0`;
|
|
78
|
+
const serviceName = 'mariadb';
|
|
79
|
+
if (options.import === true) {
|
|
80
|
+
shellExec(`sudo kubectl cp ${_toSqlPath} ${nameSpace}/${podName}:/${dbName}.sql`);
|
|
81
|
+
const cmd = `mariadb -u ${user} -p${password} ${dbName} < /${dbName}.sql`;
|
|
82
|
+
shellExec(
|
|
83
|
+
`kubectl exec -i ${podName} -- ${serviceName} -p${password} -e 'CREATE DATABASE ${dbName};'`,
|
|
84
|
+
);
|
|
85
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
86
|
+
}
|
|
87
|
+
if (options.export === true) {
|
|
88
|
+
const cmd = `mariadb-dump --user=${user} --password=${password} --lock-tables ${dbName} > ${sqlContainerPath}`;
|
|
89
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
90
|
+
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${sqlContainerPath} ${_toNewSqlPath}`);
|
|
91
|
+
await splitFileFactory(dbName, _toNewSqlPath);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'mongoose': {
|
|
97
|
+
if (options.import === true) {
|
|
98
|
+
const podName = `mongodb-0`;
|
|
99
|
+
shellExec(`sudo kubectl cp ${_toBsonPath} ${nameSpace}/${podName}:/${dbName}`);
|
|
100
|
+
const cmd = `mongorestore -d ${dbName} /${dbName}`;
|
|
101
|
+
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
|
|
102
|
+
}
|
|
103
|
+
if (options.export === true) {
|
|
104
|
+
const podName = `backup-access`;
|
|
105
|
+
const containerBaseBackupPath = '/backup';
|
|
106
|
+
let timeFolder = shellExec(
|
|
107
|
+
`sudo kubectl exec -i ${podName} -- sh -c "cd ${containerBaseBackupPath} && ls -a"`,
|
|
108
|
+
{
|
|
109
|
+
stdout: true,
|
|
110
|
+
disableLog: false,
|
|
111
|
+
silent: true,
|
|
112
|
+
},
|
|
113
|
+
).split(`\n`);
|
|
114
|
+
timeFolder = timeFolder[timeFolder.length - 2];
|
|
115
|
+
if (timeFolder === '..') {
|
|
116
|
+
logger.warn(`Cannot backup available`, { timeFolder });
|
|
117
|
+
} else {
|
|
118
|
+
shellExec(
|
|
119
|
+
`sudo kubectl cp ${nameSpace}/${podName}:${containerBaseBackupPath}/${timeFolder}/${dbName} ${_toNewBsonPath}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
default:
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (options.export === true) {
|
|
133
|
+
shellExec(`cd ../${repoName} && git add .`);
|
|
134
|
+
shellExec(
|
|
135
|
+
`underpost cmt ../${repoName} backup '' '${new Date(newBackupTimestamp).toLocaleDateString()} ${new Date(
|
|
136
|
+
newBackupTimestamp,
|
|
137
|
+
).toLocaleTimeString()}'`,
|
|
138
|
+
);
|
|
139
|
+
shellExec(`cd ../${repoName} && underpost push . ${process.env.GITHUB_USERNAME}/${repoName}`, {
|
|
140
|
+
disableLog: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export default UnderpostDB;
|
|
@@ -0,0 +1,277 @@
|
|
|
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';
|
|
21
|
+
fs.writeFileSync(`./engine-private/deploy/${deployGroupId}.json`, JSON.stringify(deployList.split(',')), '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) {
|
|
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/${deployId}-${env}:${Underpost.version}
|
|
73
|
+
---
|
|
74
|
+
apiVersion: v1
|
|
75
|
+
kind: Service
|
|
76
|
+
metadata:
|
|
77
|
+
name: ${deployId}-${env}-service
|
|
78
|
+
spec:
|
|
79
|
+
selector:
|
|
80
|
+
app: ${deployId}-${env}
|
|
81
|
+
ports:
|
|
82
|
+
type: LoadBalancer`.split('ports:');
|
|
83
|
+
deploymentYamlParts[1] =
|
|
84
|
+
buildKindPorts(fromPort, toPort) +
|
|
85
|
+
` type: LoadBalancer
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`,
|
|
90
|
+
deploymentYamlParts.join(`ports:
|
|
91
|
+
`),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
let proxyYaml = '';
|
|
95
|
+
let secretYaml = '';
|
|
96
|
+
|
|
97
|
+
for (const host of Object.keys(confServer)) {
|
|
98
|
+
if (env === 'production')
|
|
99
|
+
secretYaml += `
|
|
100
|
+
---
|
|
101
|
+
apiVersion: cert-manager.io/v1
|
|
102
|
+
kind: Certificate
|
|
103
|
+
metadata:
|
|
104
|
+
name: ${host}
|
|
105
|
+
spec:
|
|
106
|
+
commonName: ${host}
|
|
107
|
+
dnsNames:
|
|
108
|
+
- ${host}
|
|
109
|
+
issuerRef:
|
|
110
|
+
name: letsencrypt-prod
|
|
111
|
+
kind: ClusterIssuer
|
|
112
|
+
secretName: ${host}`;
|
|
113
|
+
|
|
114
|
+
const pathPortConditions = [];
|
|
115
|
+
for (const path of Object.keys(confServer[host])) {
|
|
116
|
+
const { peer } = confServer[host][path];
|
|
117
|
+
if (!router[`${host}${path === '/' ? '' : path}`]) continue;
|
|
118
|
+
const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
|
|
119
|
+
// logger.info('', { host, port, path });
|
|
120
|
+
pathPortConditions.push({
|
|
121
|
+
port,
|
|
122
|
+
path,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (peer) {
|
|
126
|
+
// logger.info('', { host, port: port + 1, path: '/peer' });
|
|
127
|
+
pathPortConditions.push({
|
|
128
|
+
port: port + 1,
|
|
129
|
+
path: '/peer',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// logger.info('', { host, pathPortConditions });
|
|
135
|
+
proxyYaml += `
|
|
136
|
+
---
|
|
137
|
+
apiVersion: projectcontour.io/v1
|
|
138
|
+
kind: HTTPProxy
|
|
139
|
+
metadata:
|
|
140
|
+
name: ${host}
|
|
141
|
+
spec:
|
|
142
|
+
virtualhost:
|
|
143
|
+
fqdn: ${host}${
|
|
144
|
+
env === 'development'
|
|
145
|
+
? ''
|
|
146
|
+
: `
|
|
147
|
+
tls:
|
|
148
|
+
secretName: ${host}`
|
|
149
|
+
}
|
|
150
|
+
routes:`;
|
|
151
|
+
for (const conditionObj of pathPortConditions) {
|
|
152
|
+
const { path, port } = conditionObj;
|
|
153
|
+
proxyYaml += `
|
|
154
|
+
- conditions:
|
|
155
|
+
- prefix: ${path}
|
|
156
|
+
enableWebsockets: true
|
|
157
|
+
services:
|
|
158
|
+
- name: ${deployId}-${env}-service
|
|
159
|
+
port: ${port}`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
|
|
163
|
+
fs.writeFileSync(yamlPath, proxyYaml, 'utf8');
|
|
164
|
+
if (env === 'production') {
|
|
165
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
|
|
166
|
+
fs.writeFileSync(yamlPath, secretYaml, 'utf8');
|
|
167
|
+
} else {
|
|
168
|
+
const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml'];
|
|
169
|
+
for (const file of deploymentsFiles) {
|
|
170
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
|
|
171
|
+
fs.copyFileSync(
|
|
172
|
+
`./engine-private/conf/${deployId}/build/${env}/${file}`,
|
|
173
|
+
`./manifests/deployment/${deployId}-${env}/${file}`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
async callback(
|
|
181
|
+
deployList = 'default',
|
|
182
|
+
env = 'development',
|
|
183
|
+
options = { remove: false, infoRouter: false, sync: false, buildManifest: false },
|
|
184
|
+
) {
|
|
185
|
+
if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd-router`))
|
|
186
|
+
deployList = fs.readFileSync(`./engine-private/deploy/dd-router`, 'utf8');
|
|
187
|
+
if (options.sync) UnderpostDeploy.API.sync(deployList);
|
|
188
|
+
if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env);
|
|
189
|
+
if (options.infoRouter === true)
|
|
190
|
+
return logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
|
|
191
|
+
|
|
192
|
+
for (const _deployId of deployList.split(',')) {
|
|
193
|
+
const deployId = _deployId.trim();
|
|
194
|
+
if (!deployId) continue;
|
|
195
|
+
|
|
196
|
+
shellExec(`sudo kubectl delete svc ${deployId}-${env}-service`);
|
|
197
|
+
shellExec(`sudo kubectl delete deployment ${deployId}-${env}`);
|
|
198
|
+
|
|
199
|
+
const etcHost = (
|
|
200
|
+
concat,
|
|
201
|
+
) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
202
|
+
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
|
|
203
|
+
let concatHots = '';
|
|
204
|
+
|
|
205
|
+
const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
206
|
+
for (const host of Object.keys(confServer)) {
|
|
207
|
+
shellExec(`sudo kubectl delete HTTPProxy ${host}`);
|
|
208
|
+
if (!options.remove === true && env === 'development') concatHots += ` ${host}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!options.remove === true) {
|
|
212
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/${deployId}-${env}/deployment.yaml`);
|
|
213
|
+
shellExec(`sudo kubectl apply -f ./manifests/deployment/${deployId}-${env}/proxy.yaml`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let renderHosts;
|
|
217
|
+
|
|
218
|
+
switch (process.platform) {
|
|
219
|
+
case 'linux':
|
|
220
|
+
{
|
|
221
|
+
switch (env) {
|
|
222
|
+
case 'development':
|
|
223
|
+
renderHosts = etcHost(concatHots);
|
|
224
|
+
fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
|
|
225
|
+
|
|
226
|
+
break;
|
|
227
|
+
|
|
228
|
+
default:
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
logger.info(
|
|
238
|
+
`
|
|
239
|
+
` + renderHosts,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
getPods(deployId) {
|
|
244
|
+
const raw = shellExec(`sudo kubectl get pods --all-namespaces -o wide`, {
|
|
245
|
+
stdout: true,
|
|
246
|
+
disableLog: false,
|
|
247
|
+
silent: true,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const heads = raw
|
|
251
|
+
.split(`\n`)[0]
|
|
252
|
+
.split(' ')
|
|
253
|
+
.filter((_r) => _r.trim());
|
|
254
|
+
|
|
255
|
+
const pods = raw
|
|
256
|
+
.split(`\n`)
|
|
257
|
+
.filter((r) => (deployId ? r.match(deployId) : r.trim() && !r.match('NAME')))
|
|
258
|
+
.map((r) => r.split(' ').filter((_r) => _r.trim()));
|
|
259
|
+
|
|
260
|
+
const result = [];
|
|
261
|
+
|
|
262
|
+
for (const row of pods) {
|
|
263
|
+
const pod = {};
|
|
264
|
+
let index = -1;
|
|
265
|
+
for (const head of heads) {
|
|
266
|
+
index++;
|
|
267
|
+
pod[head] = row[index];
|
|
268
|
+
}
|
|
269
|
+
result.push(pod);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return result;
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export default UnderpostDeploy;
|
package/src/cli/image.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import Underpost from '../index.js';
|
|
3
3
|
import { shellExec } from '../server/process.js';
|
|
4
|
-
import { MariaDB } from '../db/mariadb/MariaDB.js';
|
|
5
4
|
import dotenv from 'dotenv';
|
|
6
5
|
import { getNpmRootPath } from '../server/conf.js';
|
|
6
|
+
import { timer } from '../client/components/core/CommonJs.js';
|
|
7
7
|
|
|
8
8
|
dotenv.config();
|
|
9
9
|
|
|
@@ -19,7 +19,7 @@ class UnderpostImage {
|
|
|
19
19
|
const imagesStoragePath = `./images`;
|
|
20
20
|
const tarFile = `${imagesStoragePath}/${imgName.replace(':', '_')}.tar`;
|
|
21
21
|
|
|
22
|
-
let secrets = '';
|
|
22
|
+
let secrets = ' ';
|
|
23
23
|
let secretDockerInput = '';
|
|
24
24
|
|
|
25
25
|
const envObj = dotenv.parse(fs.readFileSync(`${getNpmRootPath()}/underpost/.env`, 'utf8'));
|
|
@@ -40,22 +40,11 @@ class UnderpostImage {
|
|
|
40
40
|
}
|
|
41
41
|
shellExec(`cd ${path} && sudo kind load image-archive ${tarFile}`);
|
|
42
42
|
},
|
|
43
|
-
async script(deployId = 'default', env = 'development') {
|
|
43
|
+
async script(deployId = 'default', env = 'development', options = { run: false }) {
|
|
44
44
|
switch (deployId) {
|
|
45
45
|
case 'dd-lampp':
|
|
46
46
|
{
|
|
47
47
|
const lamppPublicPath = '/xampp/htdocs/online';
|
|
48
|
-
if (process.argv.includes('test')) {
|
|
49
|
-
const { MARIADB_HOST, MARIADB_USER, MARIADB_PASSWORD, DD_LAMPP_TEST_DB_0 } = process.env;
|
|
50
|
-
|
|
51
|
-
await MariaDB.query({
|
|
52
|
-
host: MARIADB_HOST,
|
|
53
|
-
user: MARIADB_USER,
|
|
54
|
-
password: MARIADB_PASSWORD,
|
|
55
|
-
query: `SHOW TABLES FROM ${DD_LAMPP_TEST_DB_0}`,
|
|
56
|
-
});
|
|
57
|
-
process.exit(0);
|
|
58
|
-
}
|
|
59
48
|
shellExec(`sudo mkdir -p ${lamppPublicPath}`);
|
|
60
49
|
|
|
61
50
|
{
|
|
@@ -111,6 +100,24 @@ class UnderpostImage {
|
|
|
111
100
|
}
|
|
112
101
|
shellExec(`node bin/deploy conf ${deployId} ${env}`);
|
|
113
102
|
shellExec(`node bin/deploy build-full-client ${deployId}`);
|
|
103
|
+
if (options.run === true) {
|
|
104
|
+
const runCmd = env === 'production' ? 'prod-img' : 'dev-img';
|
|
105
|
+
if (fs.existsSync(`./engine-private/replica`)) {
|
|
106
|
+
const replicas = await fs.readdir(`./engine-private/replica`);
|
|
107
|
+
for (const replica of replicas) {
|
|
108
|
+
shellExec(`node bin/deploy conf ${replica} ${env}`);
|
|
109
|
+
shellExec(`npm run ${runCmd} ${replica} deploy`, { async: true });
|
|
110
|
+
fs.writeFileSync(`./tmp/await-deploy`, '', 'utf8');
|
|
111
|
+
const monitor = async () => {
|
|
112
|
+
await timer(1000);
|
|
113
|
+
if (fs.existsSync(`./tmp/await-deploy`)) return await monitor();
|
|
114
|
+
};
|
|
115
|
+
await monitor();
|
|
116
|
+
}
|
|
117
|
+
shellExec(`node bin/deploy conf ${deployId} ${env}`);
|
|
118
|
+
}
|
|
119
|
+
shellExec(`npm run ${runCmd} ${deployId} deploy`);
|
|
120
|
+
}
|
|
114
121
|
},
|
|
115
122
|
},
|
|
116
123
|
};
|
package/src/cli/repository.js
CHANGED
|
@@ -4,6 +4,7 @@ import { pbcopy, shellExec } from '../server/process.js';
|
|
|
4
4
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
import { getNpmRootPath } from '../server/conf.js';
|
|
7
|
+
import { listenPortController, listenServerFactory } from '../server/network.js';
|
|
7
8
|
|
|
8
9
|
dotenv.config();
|
|
9
10
|
|
|
@@ -14,7 +15,7 @@ class UnderpostRepository {
|
|
|
14
15
|
clone(gitUri = 'underpostnet/pwa-microservices-template', options = { bare: false }) {
|
|
15
16
|
const repoName = gitUri.split('/').pop();
|
|
16
17
|
if (fs.existsSync(`./${repoName}`)) fs.removeSync(`./${repoName}`);
|
|
17
|
-
|
|
18
|
+
shellExec(
|
|
18
19
|
`git clone ${options?.bare === true ? ` --bare ` : ''}https://${
|
|
19
20
|
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
20
21
|
}github.com/${gitUri}.git`,
|
|
@@ -22,16 +23,6 @@ class UnderpostRepository {
|
|
|
22
23
|
disableLog: true,
|
|
23
24
|
},
|
|
24
25
|
);
|
|
25
|
-
if (process.env.GITHUB_TOKEN) {
|
|
26
|
-
shellExec(
|
|
27
|
-
`git clone https://${
|
|
28
|
-
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
29
|
-
}github.com/${gitUri}-private.git`,
|
|
30
|
-
);
|
|
31
|
-
fs.moveSync(`./${repoName}-private`, `./${repoName}/engine-private`, {
|
|
32
|
-
overwrite: true,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
26
|
},
|
|
36
27
|
pull(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template') {
|
|
37
28
|
shellExec(
|
|
@@ -54,6 +45,10 @@ class UnderpostRepository {
|
|
|
54
45
|
empty: false,
|
|
55
46
|
},
|
|
56
47
|
) {
|
|
48
|
+
if (commitType === 'reset') {
|
|
49
|
+
shellExec(`cd ${repoPath} && git reset --soft HEAD~${isNaN(parseInt(subModule)) ? 1 : parseInt(subModule)}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
57
52
|
if (options.info) return logger.info('', commitData);
|
|
58
53
|
const _message = `${commitType}${subModule ? `(${subModule})` : ''}${process.argv.includes('!') ? '!' : ''}: ${
|
|
59
54
|
commitData[commitType].emoji
|
|
@@ -82,19 +77,18 @@ class UnderpostRepository {
|
|
|
82
77
|
new(repositoryName) {
|
|
83
78
|
return new Promise(async (resolve, reject) => {
|
|
84
79
|
try {
|
|
85
|
-
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
86
|
-
// const exeRootPath = '/home/dd/pwa-microservices-template';
|
|
87
|
-
actionInitLog();
|
|
88
80
|
await logger.setUpInfo();
|
|
89
|
-
|
|
81
|
+
if (repositoryName === 'service') return resolve(await listenPortController(listenServerFactory(), ':'));
|
|
82
|
+
else actionInitLog();
|
|
83
|
+
const exeRootPath = `${getNpmRootPath()}/underpost`;
|
|
84
|
+
const destFolder = `./${repositoryName}`;
|
|
90
85
|
logger.info('Note: This process may take several minutes to complete');
|
|
91
86
|
logger.info('build app', { destFolder });
|
|
87
|
+
if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
|
|
92
88
|
fs.mkdirSync(destFolder, { recursive: true });
|
|
93
89
|
fs.copySync(exeRootPath, destFolder);
|
|
94
|
-
if (fs.existsSync(`${destFolder}/node_modules`)) fs.removeSync(`${destFolder}/node_modules`);
|
|
95
90
|
fs.writeFileSync(`${destFolder}/.gitignore`, fs.readFileSync(`${exeRootPath}/.dockerignore`, 'utf8'), 'utf8');
|
|
96
91
|
shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
|
|
97
|
-
shellExec(`cd ${destFolder} && npm install`);
|
|
98
92
|
shellExec(`cd ${destFolder} && npm run build`);
|
|
99
93
|
shellExec(`cd ${destFolder} && npm run dev`);
|
|
100
94
|
return resolve();
|
package/src/cli/test.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { MariaDB } from '../db/mariadb/MariaDB.js';
|
|
1
2
|
import { getNpmRootPath } from '../server/conf.js';
|
|
2
3
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
3
|
-
import { shellExec } from '../server/process.js';
|
|
4
|
+
import { pbcopy, shellExec } from '../server/process.js';
|
|
5
|
+
import UnderpostDeploy from './deploy.js';
|
|
4
6
|
|
|
5
7
|
const logger = loggerFactory(import.meta);
|
|
6
8
|
|
|
@@ -26,6 +28,55 @@ class UnderpostTest {
|
|
|
26
28
|
actionInitLog();
|
|
27
29
|
shellExec(`cd ${getNpmRootPath()}/underpost && npm run test`);
|
|
28
30
|
},
|
|
31
|
+
async callback(deployList = '', options = { insideContainer: false, sh: false, logs: false }) {
|
|
32
|
+
if (options.sh === true || options.logs === true) {
|
|
33
|
+
const [pod] = UnderpostDeploy.API.getPods(deployList);
|
|
34
|
+
if (pod) {
|
|
35
|
+
if (options.sh) return pbcopy(`sudo kubectl exec -it ${pod.NAME} -- sh`);
|
|
36
|
+
if (options.logs) return shellExec(`sudo kubectl logs -f ${pod.NAME}`);
|
|
37
|
+
}
|
|
38
|
+
return logger.warn(`Couldn't find pods in deployment`, deployList);
|
|
39
|
+
}
|
|
40
|
+
if (deployList) {
|
|
41
|
+
for (const _deployId of deployList.split(',')) {
|
|
42
|
+
const deployId = _deployId.trim();
|
|
43
|
+
if (!deployId) continue;
|
|
44
|
+
if (options.insideContainer === true)
|
|
45
|
+
switch (deployId) {
|
|
46
|
+
case 'dd-lampp':
|
|
47
|
+
{
|
|
48
|
+
const { MARIADB_HOST, MARIADB_USER, MARIADB_PASSWORD, DD_LAMPP_TEST_DB_0 } = process.env;
|
|
49
|
+
|
|
50
|
+
await MariaDB.query({
|
|
51
|
+
host: MARIADB_HOST,
|
|
52
|
+
user: MARIADB_USER,
|
|
53
|
+
password: MARIADB_PASSWORD,
|
|
54
|
+
query: `SHOW TABLES FROM ${DD_LAMPP_TEST_DB_0}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
default:
|
|
60
|
+
{
|
|
61
|
+
shellExec('npm run test');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const pods = UnderpostDeploy.API.getPods(deployId);
|
|
68
|
+
if (pods.length > 0)
|
|
69
|
+
for (const deployData of pods) {
|
|
70
|
+
const { NAME } = deployData;
|
|
71
|
+
shellExec(
|
|
72
|
+
`sudo kubectl exec -i ${NAME} -- sh -c "cd /home/dd/engine && underpost test ${deployId} --inside-container"`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
else logger.warn(`Couldn't find pods in deployment`, { deployId });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else return UnderpostTest.API.run();
|
|
79
|
+
},
|
|
29
80
|
};
|
|
30
81
|
}
|
|
31
82
|
|