underpost 2.8.0 → 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/.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 +17 -71
- package/.vscode/settings.json +20 -4
- package/AUTHORS.md +16 -5
- package/CHANGELOG.md +103 -3
- package/Dockerfile +24 -66
- package/README.md +1 -28
- package/bin/build.js +186 -0
- package/bin/db.js +2 -24
- package/bin/deploy.js +168 -157
- package/bin/file.js +59 -16
- package/bin/hwt.js +0 -10
- package/bin/index.js +201 -61
- package/bin/ssl.js +19 -11
- package/bin/util.js +24 -101
- package/bin/vs.js +26 -2
- package/conf.js +30 -132
- package/docker-compose.yml +1 -1
- 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/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/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +17 -0
- package/manifests/valkey/statefulset.yaml +39 -0
- package/package.json +133 -134
- package/src/api/core/core.service.js +1 -1
- package/src/api/user/user.model.js +16 -3
- package/src/api/user/user.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/Account.js +4 -2
- package/src/client/components/core/Auth.js +24 -6
- package/src/client/components/core/CalendarCore.js +127 -50
- package/src/client/components/core/CommonJs.js +282 -19
- package/src/client/components/core/Css.js +2 -1
- package/src/client/components/core/CssCore.js +8 -4
- package/src/client/components/core/Docs.js +1 -1
- package/src/client/components/core/DropDown.js +5 -1
- package/src/client/components/core/Input.js +22 -6
- package/src/client/components/core/JoyStick.js +8 -5
- package/src/client/components/core/LoadingAnimation.js +8 -1
- package/src/client/components/core/Modal.js +47 -18
- package/src/client/components/core/Panel.js +93 -31
- package/src/client/components/core/PanelForm.js +27 -19
- package/src/client/components/core/Scroll.js +1 -0
- package/src/client/components/core/SignUp.js +4 -1
- package/src/client/components/core/Translate.js +61 -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/public/default/plantuml/client-conf.svg +1 -1
- package/src/client/public/default/plantuml/server-conf.svg +1 -1
- package/src/client/public/default/plantuml/server-schema.svg +1 -1
- package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
- package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
- package/src/client/services/core/core.service.js +15 -8
- package/src/client/services/default/default.management.js +4 -2
- package/src/client/ssr/Render.js +4 -1
- package/src/client/ssr/body/CacheControl.js +2 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +3 -3
- package/src/client/ssr/offline/Maintenance.js +63 -0
- package/src/client/sw/default.sw.js +26 -6
- package/src/db/mongo/MongooseDB.js +29 -1
- package/src/index.js +91 -17
- 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 +41 -50
- package/src/server/client-formatted.js +6 -3
- package/src/server/client-icons.js +1 -1
- package/src/server/conf.js +207 -57
- package/src/server/dns.js +30 -55
- package/src/server/downloader.js +0 -8
- package/src/server/logger.js +22 -15
- package/src/server/network.js +17 -43
- package/src/server/process.js +25 -2
- package/src/server/proxy.js +4 -26
- package/src/server/runtime.js +30 -30
- package/src/server/ssl.js +1 -1
- package/src/server/valkey.js +3 -0
- 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,202 @@
|
|
|
1
|
+
import { timer } from '../client/components/core/CommonJs.js';
|
|
2
|
+
import { cliSpinner, getNpmRootPath } from '../server/conf.js';
|
|
3
|
+
import { loggerFactory } from '../server/logger.js';
|
|
4
|
+
import { shellExec } from '../server/process.js';
|
|
5
|
+
import UnderpostDeploy from './deploy.js';
|
|
6
|
+
import UnderpostTest from './test.js';
|
|
7
|
+
|
|
8
|
+
const logger = loggerFactory(import.meta);
|
|
9
|
+
|
|
10
|
+
class UnderpostCluster {
|
|
11
|
+
static API = {
|
|
12
|
+
async init(
|
|
13
|
+
podName,
|
|
14
|
+
options = {
|
|
15
|
+
mongodb: false,
|
|
16
|
+
mongodb4: false,
|
|
17
|
+
mariadb: false,
|
|
18
|
+
valkey: false,
|
|
19
|
+
full: false,
|
|
20
|
+
info: false,
|
|
21
|
+
certManager: false,
|
|
22
|
+
listPods: false,
|
|
23
|
+
reset: false,
|
|
24
|
+
dev: false,
|
|
25
|
+
nsUse: '',
|
|
26
|
+
},
|
|
27
|
+
) {
|
|
28
|
+
const npmRoot = getNpmRootPath();
|
|
29
|
+
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
30
|
+
if (options.reset === true) return await UnderpostCluster.API.reset();
|
|
31
|
+
if (options.listPods === true) return console.table(UnderpostDeploy.API.get(podName ?? undefined));
|
|
32
|
+
|
|
33
|
+
if (options.nsUse && typeof options.nsUse === 'string') {
|
|
34
|
+
shellExec(`kubectl config set-context --current --namespace=${options.nsUse}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (options.info === true) {
|
|
38
|
+
shellExec(`kubectl config get-contexts`); // config env persisente for manage multiple clusters
|
|
39
|
+
shellExec(`kubectl config get-clusters`);
|
|
40
|
+
shellExec(`kubectl get nodes -o wide`); // set of nodes of a cluster
|
|
41
|
+
shellExec(`kubectl config view | grep namespace`);
|
|
42
|
+
shellExec(`kubectl get ns -o wide`); // A namespace can have pods of different nodes
|
|
43
|
+
shellExec(`kubectl get pvc --all-namespaces -o wide`); // PersistentVolumeClaim -> request storage service
|
|
44
|
+
shellExec(`kubectl get pv --all-namespaces -o wide`); // PersistentVolume -> real storage
|
|
45
|
+
shellExec(`kubectl get cronjob --all-namespaces -o wide`);
|
|
46
|
+
shellExec(`kubectl get svc --all-namespaces -o wide`); // proxy dns gate way -> deployments, statefulsets, pods
|
|
47
|
+
shellExec(`kubectl get statefulsets --all-namespaces -o wide`); // set pods with data/volume persistence
|
|
48
|
+
shellExec(`kubectl get deployments --all-namespaces -o wide`); // set pods
|
|
49
|
+
shellExec(`kubectl get configmap --all-namespaces -o wide`);
|
|
50
|
+
shellExec(`kubectl get pods --all-namespaces -o wide`);
|
|
51
|
+
shellExec(
|
|
52
|
+
`kubectl get pod --all-namespaces -o="custom-columns=NAME:.metadata.name,INIT-CONTAINERS:.spec.initContainers[*].name,CONTAINERS:.spec.containers[*].name"`,
|
|
53
|
+
);
|
|
54
|
+
shellExec(
|
|
55
|
+
`kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\\n"}{.metadata.name}{":\\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}'`,
|
|
56
|
+
);
|
|
57
|
+
console.log();
|
|
58
|
+
logger.info('contour -------------------------------------------------');
|
|
59
|
+
for (const _k of ['Cluster', 'HTTPProxy', 'ClusterIssuer', 'Certificate']) {
|
|
60
|
+
shellExec(`kubectl get ${_k} --all-namespaces -o wide`);
|
|
61
|
+
}
|
|
62
|
+
logger.info('----------------------------------------------------------------');
|
|
63
|
+
shellExec(`kubectl get secrets --all-namespaces -o wide`);
|
|
64
|
+
shellExec(`docker secret ls`);
|
|
65
|
+
shellExec(`kubectl get crd --all-namespaces -o wide`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!UnderpostDeploy.API.get('kube-apiserver-kind-control-plane')[0]) {
|
|
70
|
+
shellExec(`containerd config default > /etc/containerd/config.toml`);
|
|
71
|
+
shellExec(`sed -i -e "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml`);
|
|
72
|
+
// shellExec(`cp /etc/kubernetes/admin.conf ~/.kube/config`);
|
|
73
|
+
shellExec(`sudo systemctl restart kubelet`);
|
|
74
|
+
shellExec(`sudo service docker restart`);
|
|
75
|
+
shellExec(`sudo systemctl enable --now containerd.service`);
|
|
76
|
+
shellExec(`sudo systemctl restart containerd`);
|
|
77
|
+
shellExec(
|
|
78
|
+
`cd ${underpostRoot}/manifests && kind create cluster --config kind-config${
|
|
79
|
+
options?.dev === true ? '-dev' : ''
|
|
80
|
+
}.yaml`,
|
|
81
|
+
);
|
|
82
|
+
shellExec(`sudo chown $(id -u):$(id -g) $HOME/.kube/config**`);
|
|
83
|
+
} else logger.warn('Cluster already initialized');
|
|
84
|
+
|
|
85
|
+
if (options.full === true || options.valkey === true) {
|
|
86
|
+
shellExec(`kubectl delete statefulset service-valkey`);
|
|
87
|
+
shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey`);
|
|
88
|
+
}
|
|
89
|
+
if (options.full === true || options.mariadb === true) {
|
|
90
|
+
shellExec(
|
|
91
|
+
`sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password`,
|
|
92
|
+
);
|
|
93
|
+
shellExec(
|
|
94
|
+
`sudo kubectl create secret generic github-secret --from-literal=GITHUB_TOKEN=${process.env.GITHUB_TOKEN}`,
|
|
95
|
+
);
|
|
96
|
+
shellExec(`kubectl delete statefulset mariadb-statefulset`);
|
|
97
|
+
shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb`);
|
|
98
|
+
}
|
|
99
|
+
if (options.mongodb4 === true) {
|
|
100
|
+
shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb-4.4`);
|
|
101
|
+
|
|
102
|
+
const deploymentName = 'mongodb-deployment';
|
|
103
|
+
|
|
104
|
+
const successInstance = await UnderpostTest.API.statusMonitor(deploymentName);
|
|
105
|
+
|
|
106
|
+
if (successInstance) {
|
|
107
|
+
const mongoConfig = {
|
|
108
|
+
_id: 'rs0',
|
|
109
|
+
members: [{ _id: 0, host: '127.0.0.1:27017' }],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const [pod] = UnderpostDeploy.API.get(deploymentName);
|
|
113
|
+
|
|
114
|
+
shellExec(
|
|
115
|
+
`sudo kubectl exec -i ${pod.NAME} -- mongo --quiet \
|
|
116
|
+
--eval 'rs.initiate(${JSON.stringify(mongoConfig)})'`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// await UnderpostTest.API.statusMonitor('mongodb-1');
|
|
121
|
+
} else if (options.full === true || options.mongodb === true) {
|
|
122
|
+
shellExec(
|
|
123
|
+
`sudo kubectl create secret generic mongodb-keyfile --from-file=/home/dd/engine/engine-private/mongodb-keyfile`,
|
|
124
|
+
);
|
|
125
|
+
shellExec(
|
|
126
|
+
`sudo kubectl create secret generic mongodb-secret --from-file=username=/home/dd/engine/engine-private/mongodb-username --from-file=password=/home/dd/engine/engine-private/mongodb-password`,
|
|
127
|
+
);
|
|
128
|
+
shellExec(`kubectl delete statefulset mongodb`);
|
|
129
|
+
shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb`);
|
|
130
|
+
|
|
131
|
+
const successInstance = await UnderpostTest.API.statusMonitor('mongodb-1');
|
|
132
|
+
|
|
133
|
+
if (successInstance) {
|
|
134
|
+
const mongoConfig = {
|
|
135
|
+
_id: 'rs0',
|
|
136
|
+
members: [
|
|
137
|
+
{ _id: 0, host: 'mongodb-0.mongodb-service:27017', priority: 1 },
|
|
138
|
+
{ _id: 1, host: 'mongodb-1.mongodb-service:27017', priority: 1 },
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
shellExec(
|
|
143
|
+
`sudo kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
|
|
144
|
+
--eval 'use admin' \
|
|
145
|
+
--eval 'rs.initiate(${JSON.stringify(mongoConfig)})' \
|
|
146
|
+
--eval 'rs.status()'`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (options.full === true || options.contour === true)
|
|
152
|
+
shellExec(`kubectl apply -f https://projectcontour.io/quickstart/contour.yaml`);
|
|
153
|
+
|
|
154
|
+
if (options.full === true || options.certManager === true) {
|
|
155
|
+
if (!UnderpostDeploy.API.get('cert-manager').find((p) => p.STATUS === 'Running')) {
|
|
156
|
+
shellExec(`helm repo add jetstack https://charts.jetstack.io --force-update`);
|
|
157
|
+
shellExec(
|
|
158
|
+
`helm install cert-manager jetstack/cert-manager \
|
|
159
|
+
--namespace cert-manager \
|
|
160
|
+
--create-namespace \
|
|
161
|
+
--version v1.17.0 \
|
|
162
|
+
--set crds.enabled=true`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const letsEncName = 'letsencrypt-prod';
|
|
167
|
+
shellExec(`sudo kubectl delete ClusterIssuer ${letsEncName}`);
|
|
168
|
+
shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml`);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
reset() {
|
|
172
|
+
shellExec(`kind get clusters | xargs -t -n1 kind delete cluster --name`);
|
|
173
|
+
shellExec(`sudo kubeadm reset -f`);
|
|
174
|
+
shellExec('sudo rm -f /etc/cni/net.d/10-flannel.conflist');
|
|
175
|
+
shellExec('sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F && sudo iptables -X');
|
|
176
|
+
shellExec('sudo rm -f $HOME/.kube/config');
|
|
177
|
+
shellExec('sudo rm -rf /root/.local/share/Trash/files/*');
|
|
178
|
+
shellExec('sudo docker system prune -a -f');
|
|
179
|
+
shellExec('sudo service docker stop');
|
|
180
|
+
shellExec(`sudo rm -rf /var/lib/containers/storage/*`);
|
|
181
|
+
shellExec(`sudo rm -rf /var/lib/docker/volumes/*`);
|
|
182
|
+
shellExec(`sudo rm -rf /var/lib/docker~/*`);
|
|
183
|
+
shellExec(`sudo rm -rf /home/containers/storage/*`);
|
|
184
|
+
shellExec(`sudo rm -rf /home/docker/*`);
|
|
185
|
+
shellExec('sudo mv /var/lib/docker /var/lib/docker~');
|
|
186
|
+
shellExec('sudo mkdir /home/docker');
|
|
187
|
+
shellExec('sudo chmod 0711 /home/docker');
|
|
188
|
+
shellExec('sudo ln -s /home/docker /var/lib/docker');
|
|
189
|
+
shellExec(`sudo podman system prune -a -f`);
|
|
190
|
+
shellExec(`sudo podman system prune --all --volumes --force`);
|
|
191
|
+
shellExec(`sudo podman system prune --external --force`);
|
|
192
|
+
shellExec(`sudo podman system prune --all --volumes --force`);
|
|
193
|
+
shellExec(`sudo mkdir -p /home/containers/storage`);
|
|
194
|
+
shellExec('sudo chmod 0711 /home/containers/storage');
|
|
195
|
+
shellExec(
|
|
196
|
+
`sudo sed -i -e "s@/var/lib/containers/storage@/home/containers/storage@g" /etc/containers/storage.conf`,
|
|
197
|
+
);
|
|
198
|
+
shellExec(`sudo podman system reset -f`);
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export default UnderpostCluster;
|
package/src/cli/cron.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UnderpostCron CLI index module
|
|
3
|
+
* @module src/cli/cron.js
|
|
4
|
+
* @namespace UnderpostCron
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Underpost from '../index.js';
|
|
8
|
+
import BackUp from '../server/backup.js';
|
|
9
|
+
import { Cmd } from '../server/conf.js';
|
|
10
|
+
import Dns from '../server/dns.js';
|
|
11
|
+
import { netWorkCron, saveRuntimeCron } from '../server/network.js';
|
|
12
|
+
import { shellExec } from '../server/process.js';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* UnderpostCron main module methods
|
|
17
|
+
* @class
|
|
18
|
+
* @memberof UnderpostCron
|
|
19
|
+
*/
|
|
20
|
+
class UnderpostCron {
|
|
21
|
+
static JOB = {
|
|
22
|
+
/**
|
|
23
|
+
* DNS cli API
|
|
24
|
+
* @static
|
|
25
|
+
* @type {Dns}
|
|
26
|
+
* @memberof UnderpostCron
|
|
27
|
+
*/
|
|
28
|
+
dns: Dns,
|
|
29
|
+
/**
|
|
30
|
+
* BackUp cli API
|
|
31
|
+
* @static
|
|
32
|
+
* @type {BackUp}
|
|
33
|
+
* @memberof UnderpostCron
|
|
34
|
+
*/
|
|
35
|
+
backup: BackUp,
|
|
36
|
+
};
|
|
37
|
+
static API = {
|
|
38
|
+
/**
|
|
39
|
+
* Run the cron jobs
|
|
40
|
+
* @static
|
|
41
|
+
* @param {String} deployList - Comma separated deploy ids
|
|
42
|
+
* @param {String} jobList - Comma separated job ids
|
|
43
|
+
* @return {void}
|
|
44
|
+
* @memberof UnderpostCron
|
|
45
|
+
*/
|
|
46
|
+
callback: async function (
|
|
47
|
+
deployList = 'default',
|
|
48
|
+
jobList = Object.keys(UnderpostCron.JOB),
|
|
49
|
+
options = { itc: false, init: false, git: false },
|
|
50
|
+
) {
|
|
51
|
+
if (options.init === true) {
|
|
52
|
+
await Underpost.test.setUpInfo();
|
|
53
|
+
const jobDeployId = fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
|
|
54
|
+
deployList = fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').trim();
|
|
55
|
+
const confCronConfig = JSON.parse(fs.readFileSync(`./engine-private/conf/${jobDeployId}/conf.cron.json`));
|
|
56
|
+
if (confCronConfig.jobs && Object.keys(confCronConfig.jobs).length > 0) {
|
|
57
|
+
for (const job of Object.keys(confCronConfig.jobs)) {
|
|
58
|
+
const name = `${jobDeployId}-${job}`;
|
|
59
|
+
let deployId;
|
|
60
|
+
shellExec(Cmd.delete(name));
|
|
61
|
+
switch (job) {
|
|
62
|
+
case 'dns':
|
|
63
|
+
deployId = jobDeployId;
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
default:
|
|
67
|
+
deployId = deployList;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
shellExec(Cmd.cron(deployId, job, name, confCronConfig.jobs[job].expression, options));
|
|
71
|
+
netWorkCron.push({
|
|
72
|
+
deployId,
|
|
73
|
+
jobId: job,
|
|
74
|
+
expression: confCronConfig.jobs[job].expression,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await saveRuntimeCron();
|
|
79
|
+
if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const _jobId of jobList.split(',')) {
|
|
83
|
+
const jobId = _jobId.trim();
|
|
84
|
+
if (UnderpostCron.JOB[jobId]) await UnderpostCron.JOB[jobId].callback(deployList, options);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default UnderpostCron;
|
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;
|