underpost 3.2.8 → 3.2.10
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/npmpkg.ci.yml +1 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +1 -0
- package/.vscode/settings.json +10 -5
- package/CHANGELOG.md +223 -2
- package/CLI-HELP.md +36 -7
- package/README.md +38 -9
- package/bin/build.js +27 -11
- package/bin/deploy.js +20 -21
- package/bin/file.js +32 -13
- package/bin/index.js +2 -1
- package/bin/vs.js +1 -1
- package/bump.config.js +26 -0
- package/conf.js +20 -4
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
- package/manifests/kind-config-dev.yaml +8 -0
- package/manifests/mongodb/pv-pvc.yaml +44 -8
- package/manifests/mongodb/statefulset.yaml +55 -68
- package/package.json +40 -25
- package/scripts/k3s-node-setup.sh +30 -11
- package/scripts/nat-iptables.sh +103 -18
- package/src/api/core/core.router.js +19 -14
- package/src/api/core/core.service.js +5 -5
- package/src/api/default/default.router.js +22 -18
- package/src/api/default/default.service.js +5 -5
- package/src/api/document/document.router.js +28 -23
- package/src/api/document/document.service.js +100 -23
- package/src/api/file/file.router.js +19 -13
- package/src/api/file/file.service.js +9 -7
- package/src/api/test/test.router.js +17 -12
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +5 -4
- package/src/api/user/user.router.js +297 -288
- package/src/api/user/user.service.js +100 -35
- package/src/cli/baremetal.js +20 -11
- package/src/cli/cluster.js +243 -55
- package/src/cli/db.js +106 -62
- package/src/cli/deploy.js +297 -154
- package/src/cli/fs.js +19 -3
- package/src/cli/index.js +37 -9
- package/src/cli/ipfs.js +4 -6
- package/src/cli/kubectl.js +4 -1
- package/src/cli/lxd.js +217 -135
- package/src/cli/release.js +289 -131
- package/src/cli/repository.js +91 -34
- package/src/cli/run.js +297 -56
- package/src/cli/test.js +9 -3
- package/src/client/Default.index.js +9 -3
- package/src/client/components/core/Auth.js +19 -5
- package/src/client/components/core/Docs.js +6 -34
- package/src/client/components/core/FileExplorer.js +6 -6
- package/src/client/components/core/Modal.js +65 -2
- package/src/client/components/core/PanelForm.js +56 -52
- package/src/client/components/core/Recover.js +4 -4
- package/src/client/components/core/Worker.js +170 -350
- package/src/client/services/default/default.management.js +20 -25
- package/src/client/services/user/guest.service.js +10 -3
- package/src/client/sw/core.sw.js +174 -112
- package/src/db/DataBaseProvider.js +120 -20
- package/src/db/mongo/MongoBootstrap.js +587 -0
- package/src/db/mongo/MongooseDB.js +126 -22
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/wp/Wp.js +8 -5
- package/src/server/auth.js +2 -2
- package/src/server/client-build-docs.js +1 -1
- package/src/server/client-build.js +94 -129
- package/src/server/conf.js +20 -65
- package/src/server/data-query.js +32 -20
- package/src/server/dns.js +22 -0
- package/src/server/process.js +180 -19
- package/src/server/runtime.js +1 -1
- package/src/server/start.js +26 -7
- package/src/server/valkey.js +9 -2
- package/src/ws/IoInterface.js +16 -16
- package/src/ws/core/channels/core.ws.chat.js +11 -11
- package/src/ws/core/channels/core.ws.mailer.js +29 -29
- package/src/ws/core/channels/core.ws.stream.js +19 -19
- package/src/ws/core/core.ws.connection.js +8 -8
- package/src/ws/core/core.ws.server.js +6 -5
- package/src/ws/default/channels/default.ws.main.js +10 -10
- package/src/ws/default/default.ws.connection.js +4 -4
- package/src/ws/default/default.ws.server.js +4 -3
- package/typedoc.json +10 -1
- package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
- package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
- /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
- /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
- /package/src/client/ssr/{pages → views}/Test.js +0 -0
package/src/cli/cluster.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import { getNpmRootPath } from '../server/conf.js';
|
|
8
8
|
import { loggerFactory } from '../server/logger.js';
|
|
9
9
|
import { shellExec } from '../server/process.js';
|
|
10
|
+
import { MONGODB_DEFAULT_REPLICA_COUNT } from '../db/mongo/MongooseDB.js';
|
|
11
|
+
import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
|
|
10
12
|
import os from 'os';
|
|
11
13
|
import fs from 'fs-extra';
|
|
12
14
|
import Underpost from '../index.js';
|
|
@@ -22,6 +24,7 @@ const logger = loggerFactory(import.meta);
|
|
|
22
24
|
*/
|
|
23
25
|
class UnderpostCluster {
|
|
24
26
|
static API = {
|
|
27
|
+
|
|
25
28
|
/**
|
|
26
29
|
* @method init
|
|
27
30
|
* @description Initializes and configures the Kubernetes cluster based on provided options.
|
|
@@ -41,6 +44,7 @@ class UnderpostCluster {
|
|
|
41
44
|
* @param {boolean} [options.certManager=false] - Deploy Cert-Manager for certificate management.
|
|
42
45
|
* @param {boolean} [options.listPods=false] - List Kubernetes pods.
|
|
43
46
|
* @param {boolean} [options.reset=false] - Perform a comprehensive reset of Kubernetes and container environments.
|
|
47
|
+
* @param {boolean} [options.resetMongodb=false] - Perform a targeted reset of MongoDB components without restarting the entire cluster.
|
|
44
48
|
* @param {boolean} [options.dev=false] - Run in development mode (adjusts paths).
|
|
45
49
|
* @param {string} [options.nsUse=''] - Set the current kubectl namespace (creates namespace if it doesn't exist).
|
|
46
50
|
* @param {string} [options.namespace='default'] - Kubernetes namespace for cluster operations.
|
|
@@ -78,6 +82,7 @@ class UnderpostCluster {
|
|
|
78
82
|
certManager: false,
|
|
79
83
|
listPods: false,
|
|
80
84
|
reset: false,
|
|
85
|
+
resetMongodb: false,
|
|
81
86
|
dev: false,
|
|
82
87
|
nsUse: '',
|
|
83
88
|
namespace: 'default',
|
|
@@ -120,6 +125,7 @@ class UnderpostCluster {
|
|
|
120
125
|
const namespaceExists = shellExec(`kubectl get namespace ${options.nsUse} --ignore-not-found -o name`, {
|
|
121
126
|
stdout: true,
|
|
122
127
|
silent: true,
|
|
128
|
+
silentOnError: true,
|
|
123
129
|
}).trim();
|
|
124
130
|
|
|
125
131
|
if (!namespaceExists) {
|
|
@@ -145,6 +151,16 @@ class UnderpostCluster {
|
|
|
145
151
|
});
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
// Targeted MongoDB-only reset (does not restart the whole node)
|
|
155
|
+
if (options.resetMongodb) {
|
|
156
|
+
const clusterType = options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind';
|
|
157
|
+
return await MongoBootstrap.reset({
|
|
158
|
+
namespace: options.namespace,
|
|
159
|
+
clusterType,
|
|
160
|
+
underpostRoot,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
148
164
|
// Check if a cluster (Kind, Kubeadm, or K3s) is already initialized
|
|
149
165
|
const alreadyKubeadmCluster = Underpost.kubectl.get('calico-kube-controllers')[0];
|
|
150
166
|
const alreadyKindCluster = Underpost.kubectl.get('kube-apiserver-kind-control-plane')[0];
|
|
@@ -172,9 +188,19 @@ class UnderpostCluster {
|
|
|
172
188
|
const podNetworkCidr = options.podNetworkCidr || '192.168.0.0/16';
|
|
173
189
|
const controlPlaneEndpoint = options.controlPlaneEndpoint || `${os.hostname()}:6443`;
|
|
174
190
|
|
|
175
|
-
// Initialize kubeadm control plane
|
|
191
|
+
// Initialize kubeadm control plane.
|
|
192
|
+
// Use CRI-O socket when available, otherwise fall back to containerd.
|
|
193
|
+
const crioSocket = 'unix:///var/run/crio/crio.sock';
|
|
194
|
+
const containerdSocket = 'unix:///run/containerd/containerd.sock';
|
|
195
|
+
const criSocket =
|
|
196
|
+
shellExec(`test -S /var/run/crio/crio.sock && echo crio || echo containerd`, {
|
|
197
|
+
stdout: true,
|
|
198
|
+
silent: true,
|
|
199
|
+
}).trim() === 'crio'
|
|
200
|
+
? crioSocket
|
|
201
|
+
: containerdSocket;
|
|
176
202
|
shellExec(
|
|
177
|
-
`sudo kubeadm init --pod-network-cidr=${podNetworkCidr} --control-plane-endpoint="${controlPlaneEndpoint}"`,
|
|
203
|
+
`sudo kubeadm init --pod-network-cidr=${podNetworkCidr} --control-plane-endpoint="${controlPlaneEndpoint}" --cri-socket=${criSocket}`,
|
|
178
204
|
);
|
|
179
205
|
// Configure kubectl for the current user
|
|
180
206
|
Underpost.cluster.chown('kubeadm'); // Pass 'kubeadm' to chown
|
|
@@ -198,15 +224,40 @@ class UnderpostCluster {
|
|
|
198
224
|
`kubectl apply -f https://cdn.jsdelivr.net/gh/rancher/local-path-provisioner@master/deploy/local-path-storage.yaml`,
|
|
199
225
|
);
|
|
200
226
|
} else {
|
|
201
|
-
// Kind cluster initialization (
|
|
227
|
+
// Kind cluster initialization (default for development)
|
|
202
228
|
logger.info('Initializing Kind cluster...');
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
229
|
+
const devReplicaCount = Math.max(Number(options.replicas) || MONGODB_DEFAULT_REPLICA_COUNT, 3);
|
|
230
|
+
shellExec(`sudo mkdir -p /data/mongodb`);
|
|
231
|
+
for (let index = 0; index < devReplicaCount; index++) {
|
|
232
|
+
shellExec(`sudo mkdir -p /data/mongodb/v${index}`);
|
|
233
|
+
}
|
|
234
|
+
const kindCreateCmd = `cd ${underpostRoot}/manifests && kind create cluster --config kind-config-dev.yaml`;
|
|
235
|
+
try {
|
|
236
|
+
shellExec(kindCreateCmd);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const kindCreateErrText = `${error?.message || ''}\n${error?.stderr || ''}`;
|
|
239
|
+
if (kindCreateErrText.includes('all predefined address pools have been fully subnetted')) {
|
|
240
|
+
logger.warn('Docker address pool exhausted while creating Kind cluster. Running cleanup and retrying once...');
|
|
241
|
+
Underpost.cluster.recoverKindDockerNetworks();
|
|
242
|
+
try {
|
|
243
|
+
shellExec(kindCreateCmd);
|
|
244
|
+
} catch (retryError) {
|
|
245
|
+
const retryErrText = `${retryError?.message || ''}\n${retryError?.stderr || ''}`;
|
|
246
|
+
if (retryErrText.includes('all predefined address pools have been fully subnetted')) {
|
|
247
|
+
logger.warn('Kind retry still failed from pool exhaustion. Applying Docker daemon address-pool config and retrying once more...');
|
|
248
|
+
Underpost.cluster.ensureDockerDefaultAddressPools();
|
|
249
|
+
shellExec(kindCreateCmd);
|
|
250
|
+
} else {
|
|
251
|
+
throw retryError;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
208
258
|
Underpost.cluster.chown('kind'); // Pass 'kind' to chown
|
|
209
259
|
}
|
|
260
|
+
Underpost.cluster.natSetup({ underpostRoot });
|
|
210
261
|
}
|
|
211
262
|
|
|
212
263
|
// --- Optional Component Deployments (Databases, Ingress, Cert-Manager) ---
|
|
@@ -307,33 +358,16 @@ EOF
|
|
|
307
358
|
);
|
|
308
359
|
}
|
|
309
360
|
} else if (options.mongodb) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const successInstance = await Underpost.test.statusMonitor('mongodb-0', 'Running', 'pods', 1000, 60 * 10);
|
|
322
|
-
|
|
323
|
-
if (successInstance) {
|
|
324
|
-
if (!options.mongoDbHost) options.mongoDbHost = 'mongodb-0.mongodb-service';
|
|
325
|
-
const mongoConfig = {
|
|
326
|
-
_id: 'rs0',
|
|
327
|
-
members: options.mongoDbHost.split(',').map((host, index) => ({ _id: index, host: `${host}:27017` })),
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
shellExec(
|
|
331
|
-
`sudo kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
|
|
332
|
-
--eval 'use admin' \
|
|
333
|
-
--eval 'rs.initiate(${JSON.stringify(mongoConfig)})' \
|
|
334
|
-
--eval 'rs.status()'`,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
361
|
+
const clusterType = options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind';
|
|
362
|
+
await MongoBootstrap.initReplicaSet({
|
|
363
|
+
namespace: options.namespace,
|
|
364
|
+
replicaCount: Number(options.replicas) || MONGODB_DEFAULT_REPLICA_COUNT,
|
|
365
|
+
mongoDbHost: options.mongoDbHost || '',
|
|
366
|
+
pullImage: options.pullImage,
|
|
367
|
+
reset: options.reset,
|
|
368
|
+
clusterType,
|
|
369
|
+
underpostRoot,
|
|
370
|
+
});
|
|
337
371
|
}
|
|
338
372
|
|
|
339
373
|
if (options.contour) {
|
|
@@ -389,8 +423,17 @@ EOF
|
|
|
389
423
|
);
|
|
390
424
|
shellExec(`rm -f ${tarPath}`);
|
|
391
425
|
} else if (options.kubeadm || options.k3s) {
|
|
392
|
-
// Kubeadm / K3s: use crictl to pull directly into
|
|
393
|
-
|
|
426
|
+
// Kubeadm / K3s: use crictl to pull directly into the active CRI runtime.
|
|
427
|
+
// crictl is not in sudo's secure_path; pass full PATH through env.
|
|
428
|
+
// Point crictl at CRI-O when the socket exists, otherwise fall back to containerd.
|
|
429
|
+
const criSock =
|
|
430
|
+
shellExec(`test -S /var/run/crio/crio.sock && echo crio || echo containerd`, {
|
|
431
|
+
stdout: true,
|
|
432
|
+
silent: true,
|
|
433
|
+
}).trim() === 'crio'
|
|
434
|
+
? 'unix:///var/run/crio/crio.sock'
|
|
435
|
+
: 'unix:///run/containerd/containerd.sock';
|
|
436
|
+
shellExec(`sudo env PATH="$PATH:/usr/local/bin:/usr/bin" crictl --runtime-endpoint ${criSock} pull ${image}`);
|
|
394
437
|
}
|
|
395
438
|
},
|
|
396
439
|
|
|
@@ -400,7 +443,8 @@ EOF
|
|
|
400
443
|
* This method ensures proper SELinux, Docker, Containerd, and Sysctl settings
|
|
401
444
|
* are applied for a healthy Kubernetes environment. It explicitly avoids
|
|
402
445
|
* iptables flushing commands to prevent conflicts with Kubernetes' own network management.
|
|
403
|
-
* @param {
|
|
446
|
+
* @param {object} [options] - Configuration options for host setup.
|
|
447
|
+
* @param {string} [options.underpostRoot] - The root path of the underpost project, used for locating scripts if needed.
|
|
404
448
|
* @memberof UnderpostCluster
|
|
405
449
|
*/
|
|
406
450
|
config(options = { underpostRoot: '.' }) {
|
|
@@ -432,6 +476,27 @@ EOF
|
|
|
432
476
|
// Reload systemd daemon to pick up new unit files/changes
|
|
433
477
|
shellExec(`sudo systemctl daemon-reload`);
|
|
434
478
|
|
|
479
|
+
// Increase inotify limits
|
|
480
|
+
shellExec(`sudo sysctl -w fs.inotify.max_user_watches=2099999999`);
|
|
481
|
+
shellExec(`sudo sysctl -w fs.inotify.max_user_instances=2099999999`);
|
|
482
|
+
shellExec(`sudo sysctl -w fs.inotify.max_queued_events=2099999999`);
|
|
483
|
+
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @method natSetup
|
|
488
|
+
* @description Configures NAT and iptables settings for Kubernetes networking.
|
|
489
|
+
* This method enables necessary sysctl settings for bridge networking and applies iptables rules
|
|
490
|
+
* required for Kubernetes cluster communication. It is designed to work with kubeadm and k3s clusters, ensuring that
|
|
491
|
+
* traffic through Linux bridges is processed by iptables, which is crucial for CNI plugins to function correctly.
|
|
492
|
+
* The method also applies NAT iptables rules and configures firewalld for Kubernetes, which is required for multi-machine kubeadm inter-node communication.
|
|
493
|
+
* Note: This method should be called after the cluster is initialized and before deploying any workloads that require network communication.
|
|
494
|
+
* @param {object} [options] - Configuration options for NAT setup.
|
|
495
|
+
* @param {string} [options.underpostRoot] - The root path of the underpost project, used to locate the nat-iptables.sh script.
|
|
496
|
+
* @memberof UnderpostCluster
|
|
497
|
+
*/
|
|
498
|
+
natSetup(options = { underpostRoot: '.' }) {
|
|
499
|
+
const { underpostRoot } = options;
|
|
435
500
|
// Enable bridge-nf-call-iptables for Kubernetes networking
|
|
436
501
|
// This ensures traffic through Linux bridges is processed by iptables (crucial for CNI)
|
|
437
502
|
for (const iptableConfPath of [
|
|
@@ -446,19 +511,12 @@ net.bridge.bridge-nf-call-arptables = 1
|
|
|
446
511
|
net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
447
512
|
{ silent: true },
|
|
448
513
|
);
|
|
449
|
-
|
|
450
|
-
// Increase inotify limits
|
|
451
|
-
shellExec(`sudo sysctl -w fs.inotify.max_user_watches=2099999999`);
|
|
452
|
-
shellExec(`sudo sysctl -w fs.inotify.max_user_instances=2099999999`);
|
|
453
|
-
shellExec(`sudo sysctl -w fs.inotify.max_queued_events=2099999999`);
|
|
454
|
-
|
|
455
514
|
// shellExec(`sudo sysctl --system`); // Apply sysctl changes immediately
|
|
456
|
-
// Apply NAT iptables rules.
|
|
515
|
+
// Apply NAT iptables rules and configure firewalld for Kubernetes.
|
|
516
|
+
// nat-iptables.sh enables firewalld and opens all required ports; do NOT stop it
|
|
517
|
+
// afterwards — keeping firewalld running with these rules is required for
|
|
518
|
+
// multi-machine kubeadm inter-node communication.
|
|
457
519
|
shellExec(`${underpostRoot}/scripts/nat-iptables.sh`, { silent: true });
|
|
458
|
-
|
|
459
|
-
// Disable firewalld (common cause of network issues in Kubernetes)
|
|
460
|
-
shellExec(`sudo systemctl stop firewalld`); // Stop if running
|
|
461
|
-
shellExec(`sudo systemctl disable firewalld`); // Disable from starting on boot
|
|
462
520
|
},
|
|
463
521
|
|
|
464
522
|
/**
|
|
@@ -535,6 +593,10 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
535
593
|
// Phase 1: Clean up Persistent Volumes with hostPath
|
|
536
594
|
// This targets data created by Kubernetes Persistent Volumes that use hostPath.
|
|
537
595
|
logger.info('Phase 1/7: Cleaning Kubernetes hostPath volumes...');
|
|
596
|
+
if ((options.clusterType || 'kind') === 'kind') {
|
|
597
|
+
logger.info(' -> Kind detected: cleaning node-local MongoDB hostPath directories...');
|
|
598
|
+
Underpost.cluster.cleanKindMongoHostPaths({ basePath: '/data/mongodb', replicaCount: 3 });
|
|
599
|
+
}
|
|
538
600
|
if (options.removeVolumeHostPaths)
|
|
539
601
|
try {
|
|
540
602
|
const pvListJson = shellExec(`kubectl get pv -o json || echo '{"items":[]}'`, {
|
|
@@ -575,8 +637,11 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
575
637
|
shellExec('sudo systemctl stop kubelet');
|
|
576
638
|
shellExec('sudo systemctl stop docker');
|
|
577
639
|
shellExec('sudo systemctl stop podman');
|
|
578
|
-
//
|
|
579
|
-
shellExec(
|
|
640
|
+
// Lazy-unmount all kubelet pod mounts to avoid 'Device or resource busy' on rm.
|
|
641
|
+
shellExec(
|
|
642
|
+
`sudo sh -c 'findmnt --raw --noheadings -o TARGET | grep /var/lib/kubelet | sort -r | xargs -r umount -l'`,
|
|
643
|
+
{ silentOnError: true },
|
|
644
|
+
);
|
|
580
645
|
|
|
581
646
|
// Phase 3: Execute official uninstallation commands (type-specific)
|
|
582
647
|
const clusterType = options.clusterType || 'kind';
|
|
@@ -584,6 +649,14 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
584
649
|
`Phase 3/7: Executing official reset/uninstallation commands for cluster type: '${clusterType}'...`,
|
|
585
650
|
);
|
|
586
651
|
if (clusterType === 'kubeadm') {
|
|
652
|
+
// Kill control plane processes that hold ports (6443, 10257, 10259, 2379, 2380)
|
|
653
|
+
// so the next `kubeadm init` does not fail with [ERROR Port-xxxx].
|
|
654
|
+
logger.info(' -> Stopping and killing control plane containers and processes...');
|
|
655
|
+
shellExec('sudo crictl rm -a -f', { silentOnError: true });
|
|
656
|
+
shellExec('sudo crictl rmp -a -f', { silentOnError: true });
|
|
657
|
+
shellExec('sudo systemctl stop etcd', { silentOnError: true });
|
|
658
|
+
for (const port of [6443, 10259, 10257, 2379, 2380])
|
|
659
|
+
shellExec(`sudo fuser -k ${port}/tcp`, { silentOnError: true });
|
|
587
660
|
logger.info(' -> Executing kubeadm reset...');
|
|
588
661
|
shellExec('sudo kubeadm reset --force');
|
|
589
662
|
} else if (clusterType === 'k3s') {
|
|
@@ -592,7 +665,13 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
592
665
|
} else {
|
|
593
666
|
// Default: kind
|
|
594
667
|
logger.info(' -> Deleting Kind clusters...');
|
|
595
|
-
shellExec(
|
|
668
|
+
shellExec(`clusters=$(kind get clusters)
|
|
669
|
+
if [ -n "$clusters" ]; then
|
|
670
|
+
for c in $clusters; do
|
|
671
|
+
echo "Deleting cluster: $c"
|
|
672
|
+
kind delete cluster --name "$c"
|
|
673
|
+
done
|
|
674
|
+
fi`);
|
|
596
675
|
}
|
|
597
676
|
|
|
598
677
|
// Phase 4: File system cleanup
|
|
@@ -600,7 +679,13 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
600
679
|
// Remove any leftover configurations and data.
|
|
601
680
|
shellExec('sudo rm -rf /etc/kubernetes/*');
|
|
602
681
|
shellExec('sudo rm -rf /etc/cni/net.d/*');
|
|
682
|
+
// Second-pass lazy umount before rm to clear any remaining busy mounts.
|
|
683
|
+
shellExec(
|
|
684
|
+
`sudo sh -c 'findmnt --raw --noheadings -o TARGET | grep /var/lib/kubelet | sort -r | xargs -r umount -l'`,
|
|
685
|
+
{ silentOnError: true },
|
|
686
|
+
);
|
|
603
687
|
shellExec('sudo rm -rf /var/lib/kubelet/*');
|
|
688
|
+
shellExec('sudo rm -rf /var/lib/etcd');
|
|
604
689
|
shellExec('sudo rm -rf /var/lib/cni/*');
|
|
605
690
|
shellExec('sudo rm -rf /var/lib/docker/*');
|
|
606
691
|
shellExec('sudo rm -rf /var/lib/containerd/*');
|
|
@@ -613,11 +698,14 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
613
698
|
// Remove iptables rules and CNI network interfaces.
|
|
614
699
|
shellExec('sudo iptables -F');
|
|
615
700
|
shellExec('sudo iptables -t nat -F');
|
|
616
|
-
shellExec('sudo ip link del cni0');
|
|
617
|
-
shellExec('sudo ip link del flannel.1');
|
|
701
|
+
shellExec('sudo ip link del cni0', { silentOnError: true });
|
|
702
|
+
shellExec('sudo ip link del flannel.1', { silentOnError: true });
|
|
703
|
+
shellExec('sudo ip link del vxlan.calico', { silentOnError: true });
|
|
704
|
+
shellExec('sudo ip link del tunl0', { silentOnError: true });
|
|
618
705
|
|
|
619
706
|
logger.info('Phase 6/7: Clean up images');
|
|
620
|
-
shellExec(
|
|
707
|
+
shellExec('sudo podman rmi --all --force', { silentOnError: true });
|
|
708
|
+
shellExec('sudo crictl rmi --prune', { silentOnError: true });
|
|
621
709
|
|
|
622
710
|
// Phase 6: Reload daemon and finalize
|
|
623
711
|
logger.info('Phase 7/7: Reloading the system daemon and finalizing...');
|
|
@@ -687,6 +775,9 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
|
|
|
687
775
|
// Install Podman
|
|
688
776
|
shellExec(`sudo dnf -y install podman`);
|
|
689
777
|
|
|
778
|
+
// Install CRI-O (required for kubeadm with CRI-O socket)
|
|
779
|
+
shellExec(`node bin run install-crio`);
|
|
780
|
+
|
|
690
781
|
// Install Kind (Kubernetes in Docker)
|
|
691
782
|
shellExec(`[ $(uname -m) = ${archData.name} ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-${archData.alias}
|
|
692
783
|
chmod +x ./kind
|
|
@@ -744,6 +835,14 @@ EOF`);
|
|
|
744
835
|
console.log('Removing Podman...');
|
|
745
836
|
shellExec(`sudo dnf -y remove podman`);
|
|
746
837
|
|
|
838
|
+
// Remove CRI-O
|
|
839
|
+
console.log('Removing CRI-O...');
|
|
840
|
+
shellExec('sudo systemctl stop crio', { silentOnError: true });
|
|
841
|
+
shellExec('sudo systemctl disable crio', { silentOnError: true });
|
|
842
|
+
shellExec(`sudo dnf -y remove cri-o`);
|
|
843
|
+
shellExec(`sudo rm -f /etc/yum.repos.d/cri-o.repo`);
|
|
844
|
+
shellExec(`sudo rm -f /etc/crictl.yaml`);
|
|
845
|
+
|
|
747
846
|
// Remove Kubeadm, Kubelet, and Kubectl
|
|
748
847
|
console.log('Removing Kubernetes tools...');
|
|
749
848
|
shellExec(`sudo yum remove -y kubelet kubeadm kubectl`);
|
|
@@ -780,6 +879,95 @@ EOF`);
|
|
|
780
879
|
|
|
781
880
|
console.log('Uninstall process completed.');
|
|
782
881
|
},
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* @method cleanKindMongoHostPaths
|
|
885
|
+
* @description Best-effort cleanup of MongoDB hostPath directories inside Kind node containers.
|
|
886
|
+
* This prevents stale replica/auth state when hostPath data lives in node-local container filesystems.
|
|
887
|
+
* @param {object} [options]
|
|
888
|
+
* @param {string} [options.basePath='/data/mongodb'] - Node-internal base path for MongoDB data.
|
|
889
|
+
* @param {number} [options.replicaCount=3] - Number of replica ordinal directories (v0..vN-1).
|
|
890
|
+
* @memberof UnderpostCluster
|
|
891
|
+
*/
|
|
892
|
+
cleanKindMongoHostPaths(options = { basePath: '/data/mongodb', replicaCount: 3 }) {
|
|
893
|
+
const basePath = options.basePath || '/data/mongodb';
|
|
894
|
+
const replicaCount = Math.max(Number(options.replicaCount) || 3, 1);
|
|
895
|
+
const nodesRaw = shellExec('kind get nodes', {
|
|
896
|
+
stdout: true,
|
|
897
|
+
silent: true,
|
|
898
|
+
silentOnError: true,
|
|
899
|
+
});
|
|
900
|
+
const nodes = nodesRaw
|
|
901
|
+
.split('\n')
|
|
902
|
+
.map((node) => node.trim())
|
|
903
|
+
.filter((node) => !!node);
|
|
904
|
+
|
|
905
|
+
if (nodes.length === 0) {
|
|
906
|
+
logger.info('No Kind nodes detected for node-local MongoDB hostPath cleanup.');
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
for (const node of nodes) {
|
|
911
|
+
logger.info(
|
|
912
|
+
`Cleaning Kind node-local MongoDB paths '${basePath}/v0..v${replicaCount - 1}' on node '${node}'...`,
|
|
913
|
+
);
|
|
914
|
+
const prepareReplicaDirsCmd = Array.from(
|
|
915
|
+
{ length: replicaCount },
|
|
916
|
+
(_, index) => `mkdir -p ${basePath}/v${index}; rm -rf ${basePath}/v${index}/*;`,
|
|
917
|
+
).join(' ');
|
|
918
|
+
const verifyReplicaDirsCmd = Array.from(
|
|
919
|
+
{ length: replicaCount },
|
|
920
|
+
(_, index) => `test -d ${basePath}/v${index};`,
|
|
921
|
+
).join(' ');
|
|
922
|
+
shellExec(
|
|
923
|
+
`sudo docker exec ${node} sh -lc 'mkdir -p ${basePath}; ${prepareReplicaDirsCmd}'`,
|
|
924
|
+
{ silentOnError: true },
|
|
925
|
+
);
|
|
926
|
+
shellExec(`sudo docker exec ${node} sh -lc '${verifyReplicaDirsCmd}'`);
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* @method recoverKindDockerNetworks
|
|
932
|
+
* @description Best-effort cleanup of stale Kind Docker resources when Docker bridge
|
|
933
|
+
* address pools are exhausted and new networks cannot be allocated.
|
|
934
|
+
* @memberof UnderpostCluster
|
|
935
|
+
*/
|
|
936
|
+
recoverKindDockerNetworks() {
|
|
937
|
+
logger.warn('Attempting Docker network recovery for Kind (address pool exhaustion detected)...');
|
|
938
|
+
shellExec(`sudo docker ps -aq --filter label=io.x-k8s.kind.cluster | xargs -r sudo docker rm -f`, {
|
|
939
|
+
silentOnError: true,
|
|
940
|
+
});
|
|
941
|
+
shellExec(`sudo docker network ls -q --filter label=io.x-k8s.kind.cluster | xargs -r sudo docker network rm`, {
|
|
942
|
+
silentOnError: true,
|
|
943
|
+
});
|
|
944
|
+
shellExec(`sudo docker network rm kind`, { silentOnError: true });
|
|
945
|
+
shellExec(`sudo docker network prune -f`, { silentOnError: true });
|
|
946
|
+
},
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* @method ensureDockerDefaultAddressPools
|
|
950
|
+
* @description Writes a sane Docker default-address-pools config to reduce
|
|
951
|
+
* Kind network allocation failures on hosts with exhausted predefined pools.
|
|
952
|
+
* @memberof UnderpostCluster
|
|
953
|
+
*/
|
|
954
|
+
ensureDockerDefaultAddressPools() {
|
|
955
|
+
logger.warn('Applying Docker default-address-pools workaround for Kind network creation...');
|
|
956
|
+
shellExec(`cat <<'EOF' | sudo tee /etc/docker/daemon.json
|
|
957
|
+
{
|
|
958
|
+
"default-address-pools": [
|
|
959
|
+
{"base": "172.17.0.0/16", "size": 24},
|
|
960
|
+
{"base": "172.18.0.0/16", "size": 24},
|
|
961
|
+
{"base": "172.19.0.0/16", "size": 24},
|
|
962
|
+
{"base": "172.20.0.0/14", "size": 24},
|
|
963
|
+
{"base": "172.24.0.0/14", "size": 24}
|
|
964
|
+
]
|
|
965
|
+
}
|
|
966
|
+
EOF`);
|
|
967
|
+
shellExec('sudo systemctl restart docker');
|
|
968
|
+
shellExec('sudo docker network prune -f', { silentOnError: true });
|
|
969
|
+
},
|
|
970
|
+
|
|
783
971
|
};
|
|
784
972
|
}
|
|
785
973
|
export default UnderpostCluster;
|