underpost 2.8.885 → 2.81.0

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.
Files changed (72) hide show
  1. package/.env.production +3 -0
  2. package/.github/workflows/ghpkg.ci.yml +1 -1
  3. package/.github/workflows/npmpkg.ci.yml +1 -1
  4. package/.github/workflows/publish.ci.yml +5 -5
  5. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  6. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  7. package/.vscode/zed.keymap.json +17 -0
  8. package/.vscode/zed.settings.json +20 -0
  9. package/CHANGELOG.md +145 -1
  10. package/Dockerfile +20 -3
  11. package/README.md +6 -6
  12. package/bin/build.js +18 -9
  13. package/bin/deploy.js +130 -195
  14. package/bin/zed.js +20 -0
  15. package/cli.md +13 -7
  16. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  17. package/manifests/deployment/dd-test-development/deployment.yaml +50 -50
  18. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  19. package/manifests/lxd/underpost-setup.sh +5 -5
  20. package/package.json +3 -4
  21. package/{manifests/maas → scripts}/ssh-cluster-info.sh +1 -1
  22. package/scripts/ssl.sh +164 -0
  23. package/src/cli/baremetal.js +8 -8
  24. package/src/cli/cloud-init.js +1 -1
  25. package/src/cli/cluster.js +15 -4
  26. package/src/cli/cron.js +1 -1
  27. package/src/cli/db.js +2 -1
  28. package/src/cli/deploy.js +65 -14
  29. package/src/cli/fs.js +2 -2
  30. package/src/cli/image.js +19 -2
  31. package/src/cli/index.js +11 -4
  32. package/src/cli/monitor.js +34 -1
  33. package/src/cli/repository.js +42 -1
  34. package/src/cli/run.js +396 -86
  35. package/src/cli/script.js +32 -0
  36. package/src/cli/secrets.js +34 -0
  37. package/src/cli/test.js +42 -1
  38. package/src/client/components/core/Css.js +0 -8
  39. package/src/client/components/core/windowGetDimensions.js +229 -162
  40. package/src/index.js +2 -2
  41. package/src/mailer/MailerProvider.js +1 -0
  42. package/src/runtime/express/Dockerfile +41 -0
  43. package/src/runtime/express/Express.js +12 -4
  44. package/src/runtime/lampp/Dockerfile +1 -1
  45. package/src/server/backup.js +20 -0
  46. package/src/server/client-build-live.js +12 -10
  47. package/src/server/client-build.js +136 -91
  48. package/src/server/client-dev-server.js +16 -2
  49. package/src/server/client-icons.js +19 -0
  50. package/src/server/conf.js +495 -69
  51. package/src/server/dns.js +169 -46
  52. package/src/server/downloader.js +65 -24
  53. package/src/server/object-layer.js +260 -162
  54. package/src/server/peer.js +2 -8
  55. package/src/server/proxy.js +93 -76
  56. package/src/server/runtime.js +15 -16
  57. package/src/server/ssr.js +4 -4
  58. package/src/server/tls.js +251 -0
  59. package/src/server/valkey.js +11 -10
  60. package/src/ws/IoInterface.js +2 -1
  61. package/src/ws/IoServer.js +2 -1
  62. package/src/ws/core/core.ws.connection.js +1 -1
  63. package/src/ws/core/core.ws.emit.js +1 -1
  64. package/src/ws/core/core.ws.server.js +1 -1
  65. package/manifests/maas/lxd-preseed.yaml +0 -32
  66. package/src/server/ssl.js +0 -108
  67. /package/{manifests/maas → scripts}/device-scan.sh +0 -0
  68. /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
  69. /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
  70. /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
  71. /package/{manifests/maas → scripts}/nvim.sh +0 -0
  72. /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
@@ -13,7 +13,7 @@ spec:
13
13
  enableWebsockets: true
14
14
  services:
15
15
  - name: dd-test-development-blue-service
16
- port: 4021
16
+ port: 4041
17
17
  weight: 100
18
18
 
19
19
  - conditions:
@@ -21,7 +21,7 @@ spec:
21
21
  enableWebsockets: true
22
22
  services:
23
23
  - name: dd-test-development-blue-service
24
- port: 4022
24
+ port: 4042
25
25
  weight: 100
26
26
 
27
27
  ---
@@ -38,7 +38,7 @@ spec:
38
38
  enableWebsockets: true
39
39
  services:
40
40
  - name: dd-test-development-blue-service
41
- port: 4023
41
+ port: 4043
42
42
  weight: 100
43
43
 
44
44
  - conditions:
@@ -46,6 +46,6 @@ spec:
46
46
  enableWebsockets: true
47
47
  services:
48
48
  - name: dd-test-development-blue-service
49
- port: 4024
49
+ port: 4044
50
50
  weight: 100
51
51
 
@@ -39,15 +39,15 @@ sudo dnf install -y tar bzip2 git epel-release
39
39
  sudo dnf -y update
40
40
 
41
41
  # --- NVM and Node.js Installation ---
42
- echo "Installing NVM and Node.js v23.8.0..."
42
+ echo "Installing NVM and Node.js v24.10.0..."
43
43
  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
44
44
 
45
45
  # Load nvm for the current session
46
46
  export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
47
47
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
48
48
 
49
- nvm install 23.8.0
50
- nvm use 23.8.0
49
+ nvm install 24.10.0
50
+ nvm use 24.10.0
51
51
 
52
52
  echo "
53
53
  ██╗░░░██╗███╗░░██╗██████╗░███████╗██████╗░██████╗░░█████╗░░██████╗████████╗
@@ -65,9 +65,9 @@ npm install -g underpost
65
65
 
66
66
  # Ensure underpost executable is in PATH and has execute permissions
67
67
  # Adjusting this for global npm install which usually handles permissions
68
- # If you still face issues, ensure /root/.nvm/versions/node/v23.8.0/bin is in your PATH
68
+ # If you still face issues, ensure /root/.nvm/versions/node/v24.10.0/bin is in your PATH
69
69
  # For global installs, it's usually handled automatically.
70
- # chmod +x /root/.nvm/versions/node/v23.8.0/bin/underpost # This might not be necessary for global npm installs
70
+ # chmod +x /root/.nvm/versions/node/v24.10.0/bin/underpost # This might not be necessary for global npm installs
71
71
 
72
72
  # --- Kernel Module for Bridge Filtering ---
73
73
  # This is crucial for Kubernetes networking (CNI)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.885",
5
+ "version": "2.81.0",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
@@ -80,7 +80,7 @@
80
80
  "http-proxy-middleware": "^2.0.6",
81
81
  "ignore-walk": "^6.0.4",
82
82
  "iovalkey": "^0.2.1",
83
- "jimp": "^0.22.12",
83
+ "jimp": "^1.6.0",
84
84
  "json-colorizer": "^2.2.2",
85
85
  "jsonwebtoken": "^9.0.2",
86
86
  "mariadb": "^3.2.2",
@@ -88,12 +88,11 @@
88
88
  "mocha": "^10.8.2",
89
89
  "mongoose": "^8.9.5",
90
90
  "morgan": "^1.10.0",
91
- "nodemailer": "^6.9.9",
91
+ "nodemailer": "^7.0.9",
92
92
  "nodemon": "^3.0.1",
93
93
  "peer": "^1.0.2",
94
94
  "peerjs": "^1.5.2",
95
95
  "prom-client": "^15.1.2",
96
- "public-ip": "^6.0.1",
97
96
  "read": "^2.1.0",
98
97
  "rrule": "^2.8.1",
99
98
  "sharp": "^0.32.5",
@@ -9,6 +9,6 @@ chmod 600 "$SSH_KEY"
9
9
 
10
10
  ssh -i "$SSH_KEY" -o BatchMode=yes "${REMOTE_USER}@${REMOTE_HOST}" sh <<EOF
11
11
  cd /home/dd/engine
12
- node bin deploy dd production --info-traffic
12
+ node bin deploy dd production --status
13
13
  kubectl get pods -A
14
14
  EOF
package/scripts/ssl.sh ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bash
2
+ # RHEL / Rocky Linux — Generate local TLS cert + fullchain (cert + root CA) using mkcert
3
+ # Usage:
4
+ # ./generate-local-ssl-fullchain.sh /path/to/target "localhost 127.0.0.1 ::1"
5
+
6
+ set -euo pipefail
7
+ IFS=$'\n\t'
8
+
9
+ err() { echo "[ERROR] $*" >&2; }
10
+ info() { echo "[INFO] $*"; }
11
+
12
+ print_usage() {
13
+ cat <<'EOF'
14
+ Usage: $0 TARGET_DIR "domain1 domain2 ..."
15
+ Example: $0 /etc/ssl/local "localhost 127.0.0.1 ::1"
16
+
17
+ Outputs created in TARGET_DIR:
18
+ - <name>.pem : leaf/server certificate
19
+ - <name>-key.pem : private key
20
+ - <name>-fullchain.pem: certificate followed by root CA (use for servers needing full chain)
21
+ - rootCA.pem : local root CA (if OpenSSL fallback used or copied from mkcert CAROOT)
22
+ EOF
23
+ }
24
+
25
+ if [[ ${1-} == "-h" || ${1-} == "--help" ]]; then
26
+ print_usage; exit 0; fi
27
+
28
+ TARGET_DIR="${1-}"
29
+ DOMAINS="${2-}"
30
+
31
+ if [[ -z "$TARGET_DIR" ]]; then read -rp "Target directory to store certificates (absolute path): " TARGET_DIR; fi
32
+ if [[ -z "$DOMAINS" ]]; then read -rp "Space-separated domains/IPs to include (e.g. 'localhost 127.0.0.1 api.myapp.test'): " DOMAINS; fi
33
+
34
+ TARGET_DIR="$(realpath -m "$TARGET_DIR")"
35
+ mkdir -p "$TARGET_DIR"
36
+ IFS=' ' read -r -a DOMAIN_ARR <<< "$DOMAINS"
37
+ if [[ ${#DOMAIN_ARR[@]} -eq 0 ]]; then err "No domains provided. Exiting."; exit 1; fi
38
+
39
+ NAME_SAFE="${DOMAIN_ARR[0]//[^a-zA-Z0-9_.-]/_}"
40
+ CERT_FILE="$TARGET_DIR/${NAME_SAFE}.pem"
41
+ KEY_FILE="$TARGET_DIR/${NAME_SAFE}-key.pem"
42
+ FULLCHAIN_FILE="$TARGET_DIR/${NAME_SAFE}-fullchain.pem"
43
+ ROOT_PEM="$TARGET_DIR/rootCA.pem"
44
+
45
+ info "Target dir: $TARGET_DIR"
46
+ info "Domains: ${DOMAIN_ARR[*]}"
47
+ info "Outputs: $CERT_FILE, $KEY_FILE, $FULLCHAIN_FILE, $ROOT_PEM"
48
+
49
+ # Install prerequisites
50
+ if ! command -v dnf >/dev/null 2>&1; then err "dnf not found. This script expects RHEL/Rocky with dnf."; exit 1; fi
51
+ sudo dnf install -y curl nss-tools ca-certificates || true
52
+
53
+ # Download and install mkcert binary (no 'go install')
54
+ download_mkcert_binary() {
55
+ UNAME_M=$(uname -m)
56
+ case "$UNAME_M" in
57
+ x86_64|amd64) ARCH_STR='linux-amd64' ;;
58
+ aarch64|arm64) ARCH_STR='linux-arm64' ;;
59
+ *) ARCH_STR='linux-amd64' ;;
60
+ esac
61
+ info "Searching mkcert release for $ARCH_STR"
62
+ ASSET_URL=$(curl -sS "https://api.github.com/repos/FiloSottile/mkcert/releases/latest" | \
63
+ grep -E '"browser_download_url":' | grep -i "$ARCH_STR" | head -n1 | sed -E 's/.*"(https:[^"]+)".*/\1/' || true)
64
+ if [[ -z "$ASSET_URL" ]]; then
65
+ ASSET_URL=$(curl -sS "https://api.github.com/repos/FiloSottile/mkcert/releases/latest" | \
66
+ grep -E '"browser_download_url":' | grep -i 'linux' | grep -i 'amd64' | head -n1 | sed -E 's/.*"(https:[^"]+)".*/\1/' || true)
67
+ fi
68
+ if [[ -z "$ASSET_URL" ]]; then err "Could not find mkcert asset for your arch"; return 1; fi
69
+ TMP_BIN="$(mktemp -u /tmp/mkcert.XXXXXX)"
70
+ if curl -fSL -o "$TMP_BIN" "$ASSET_URL"; then
71
+ sudo mv "$TMP_BIN" /usr/local/bin/mkcert
72
+ sudo chmod +x /usr/local/bin/mkcert
73
+ info "mkcert installed to /usr/local/bin/mkcert"
74
+ return 0
75
+ fi
76
+ return 1
77
+ }
78
+
79
+ use_mkcert() {
80
+ if ! command -v mkcert >/dev/null 2>&1; then
81
+ info "mkcert not found — attempting to download/install binary"
82
+ download_mkcert_binary || return 1
83
+ fi
84
+ MKCERT_BIN="$(command -v mkcert || echo /usr/local/bin/mkcert)"
85
+ info "Running mkcert -install as sudo (if necessary)"
86
+ if ! sudo "$MKCERT_BIN" -install >/dev/null 2>&1; then
87
+ if ! "$MKCERT_BIN" -install >/dev/null 2>&1; then
88
+ err "mkcert -install failed"; return 1
89
+ fi
90
+ fi
91
+ MK_ARGS=()
92
+ for d in "${DOMAIN_ARR[@]}"; do MK_ARGS+=("$d"); done
93
+ info "Generating cert+key with mkcert"
94
+ if ! "$MKCERT_BIN" -cert-file "$CERT_FILE" -key-file "$KEY_FILE" "${MK_ARGS[@]}"; then err "mkcert failed to generate"; return 1; fi
95
+ # copy root CA from mkcert CAROOT into TARGET_DIR
96
+ if ROOT_FROM_MKCERT="$($MKCERT_BIN -CAROOT 2>/dev/null || true)"; then
97
+ if [[ -f "$ROOT_FROM_MKCERT/rootCA.pem" ]]; then
98
+ cp "$ROOT_FROM_MKCERT/rootCA.pem" "$ROOT_PEM"
99
+ info "Copied mkcert root CA to $ROOT_PEM"
100
+ fi
101
+ fi
102
+ # create fullchain (cert followed by root CA)
103
+ if [[ -f "$ROOT_PEM" ]]; then
104
+ cat "$CERT_FILE" "$ROOT_PEM" > "$FULLCHAIN_FILE"
105
+ info "Created fullchain: $FULLCHAIN_FILE"
106
+ else
107
+ cp "$CERT_FILE" "$FULLCHAIN_FILE"
108
+ info "No root CA found to append; fullchain contains only leaf cert: $FULLCHAIN_FILE"
109
+ fi
110
+ return 0
111
+ }
112
+
113
+ use_openssl() {
114
+ command -v openssl >/dev/null 2>&1 || { err "openssl required"; return 1; }
115
+ ROOT_KEY="$TARGET_DIR/rootCA.key"
116
+ if [[ ! -f "$ROOT_KEY" || ! -f "$ROOT_PEM" ]]; then
117
+ openssl genrsa -out "$ROOT_KEY" 4096
118
+ openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_PEM" -subj "/CN=Local Development Root CA"
119
+ fi
120
+ CSR_KEY="$TARGET_DIR/${NAME_SAFE}.key"
121
+ CSR_PEM="$TARGET_DIR/${NAME_SAFE}.csr"
122
+ openssl genrsa -out "$CSR_KEY" 2048
123
+ openssl req -new -key "$CSR_KEY" -out "$CSR_PEM" -subj "/CN=${DOMAIN_ARR[0]}"
124
+ V3EXT="$TARGET_DIR/v3.ext"
125
+ printf 'authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nkeyUsage = digitalSignature, keyEncipherment\nsubjectAltName = @alt_names\n\n[alt_names]\n' > "$V3EXT"
126
+ DNS=1; IP=1
127
+ for d in "${DOMAIN_ARR[@]}"; do
128
+ if [[ "$d" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "$d" == *":"* ]]; then
129
+ printf 'IP.%d = %s\n' "$IP" "$d" >> "$V3EXT"; IP=$((IP+1))
130
+ else
131
+ printf 'DNS.%d = %s\n' "$DNS" "$d" >> "$V3EXT"; DNS=$((DNS+1))
132
+ fi
133
+ done
134
+ if openssl x509 -req -in "$CSR_PEM" -CA "$ROOT_PEM" -CAkey "$ROOT_KEY" -CAcreateserial -out "$CERT_FILE" -days 825 -sha256 -extfile "$V3EXT"; then
135
+ mv -f "$CSR_KEY" "$KEY_FILE"
136
+ # create fullchain: leaf + root
137
+ cat "$CERT_FILE" "$ROOT_PEM" > "$FULLCHAIN_FILE"
138
+ sudo cp "$ROOT_PEM" /etc/pki/ca-trust/source/anchors/ || true
139
+ sudo update-ca-trust extract || true
140
+ if command -v certutil >/dev/null 2>&1; then
141
+ mkdir -p "$HOME/.pki/nssdb"
142
+ certutil -d sql:$HOME/.pki/nssdb -A -t "CT,C,C" -n "Local Dev Root CA" -i "$ROOT_PEM" || true
143
+ fi
144
+ info "OpenSSL created cert, key and fullchain"
145
+ return 0
146
+ fi
147
+ err "OpenSSL failed to create certificate"
148
+ return 1
149
+ }
150
+
151
+ # main
152
+ if use_mkcert; then
153
+ info "Done: created cert, key, fullchain and root CA in $TARGET_DIR"
154
+ exit 0
155
+ else
156
+ info "mkcert flow failed — trying OpenSSL fallback"
157
+ if use_openssl; then
158
+ info "Done: created cert, key, fullchain and root CA in $TARGET_DIR"
159
+ exit 0
160
+ fi
161
+ fi
162
+
163
+ err "Failed to create certificates with mkcert or OpenSSL"
164
+ exit 2
@@ -11,7 +11,7 @@ import dotenv from 'dotenv';
11
11
  import { loggerFactory } from '../server/logger.js';
12
12
  import { getLocalIPv4Address } from '../server/dns.js';
13
13
  import fs from 'fs-extra';
14
- import { Downloader } from '../server/downloader.js';
14
+ import Downloader from '../server/downloader.js';
15
15
  import UnderpostCloudInit from './cloud-init.js';
16
16
  import { s4, timer } from '../client/components/core/CommonJs.js';
17
17
 
@@ -153,10 +153,10 @@ class UnderpostBaremetal {
153
153
  // Handle control server installation.
154
154
  if (options.controlServerInstall === true) {
155
155
  // Ensure scripts are executable and then run them.
156
- shellExec(`chmod +x ${underpostRoot}/manifests/maas/maas-setup.sh`);
157
- shellExec(`chmod +x ${underpostRoot}/manifests/maas/nat-iptables.sh`);
158
- shellExec(`${underpostRoot}/manifests/maas/maas-setup.sh`);
159
- shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`);
156
+ shellExec(`chmod +x ${underpostRoot}/scripts/maas-setup.sh`);
157
+ shellExec(`chmod +x ${underpostRoot}/scripts/nat-iptables.sh`);
158
+ shellExec(`${underpostRoot}/scripts/maas-setup.sh`);
159
+ shellExec(`${underpostRoot}/scripts/nat-iptables.sh`);
160
160
  return;
161
161
  }
162
162
 
@@ -349,7 +349,7 @@ class UnderpostBaremetal {
349
349
  const name = url.split('/').pop().replace('.zip', '');
350
350
  const path = `../${name}`;
351
351
  if (!fs.existsSync(path)) {
352
- await Downloader(url, `../${name}.zip`); // Download firmware if not exists.
352
+ await Downloader.downloadFile(url, `../${name}.zip`); // Download firmware if not exists.
353
353
  shellExec(`cd .. && mkdir ${name} && cd ${name} && unzip ../${name}.zip`); // Unzip firmware.
354
354
  }
355
355
  shellExec(`sudo cp -a ${path}/* ${tftpRootPath}`); // Copy firmware files to TFTP root.
@@ -530,7 +530,7 @@ menuentry '${menuentryStr}' {
530
530
  if (options.cloudInitUpdate === true) return;
531
531
 
532
532
  // Apply NAT iptables rules.
533
- shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
533
+ shellExec(`${underpostRoot}/scripts/nat-iptables.sh`, { silent: true });
534
534
 
535
535
  // Wait for MAC address assignment.
536
536
  logger.info('Waiting for MAC assignment...');
@@ -952,7 +952,7 @@ EOF`);
952
952
  */
953
953
  getHostArch() {
954
954
  // `uname -m` returns e.g. 'x86_64' or 'aarch64'
955
- const machine = shellExec('uname -m', { stdout: true }).trim();
955
+ const machine = shellExec('uname -m', { stdout: true, silent: true }).trim();
956
956
  if (machine === 'x86_64') return { alias: 'amd64', name: 'x86_64' };
957
957
  if (machine === 'aarch64') return { alias: 'arm64', name: 'aarch64' };
958
958
  throw new Error(`Unsupported host architecture: ${machine}`);
@@ -182,7 +182,7 @@ cut -d: -f1 /etc/passwd`,
182
182
 
183
183
  // Copy the device scan script from manifests.
184
184
  logger.info('Build', `${nfsHostToolsPath}/device_scan.sh`);
185
- fs.copySync(`${underpostRoot}/manifests/maas/device-scan.sh`, `${nfsHostToolsPath}/device_scan.sh`);
185
+ fs.copySync(`${underpostRoot}/scripts/device-scan.sh`, `${nfsHostToolsPath}/device_scan.sh`);
186
186
 
187
187
  // Build and write the config path script.
188
188
  logger.info('Build', `${nfsHostToolsPath}/config-path.sh`);
@@ -15,6 +15,13 @@ import fs from 'fs-extra';
15
15
 
16
16
  const logger = loggerFactory(import.meta);
17
17
 
18
+ /**
19
+ * @class UnderpostCluster
20
+ * @description Manages Kubernetes cluster initialization, configuration, and component deployment.
21
+ * This class provides a set of static methods to handle cluster initialization, configuration,
22
+ * and optional component deployments.
23
+ * @memberof UnderpostCluster
24
+ */
18
25
  class UnderpostCluster {
19
26
  static API = {
20
27
  /**
@@ -520,7 +527,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
520
527
 
521
528
  // shellExec(`sudo sysctl --system`); // Apply sysctl changes immediately
522
529
  // Apply NAT iptables rules.
523
- shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
530
+ shellExec(`${underpostRoot}/scripts/nat-iptables.sh`, { silent: true });
524
531
 
525
532
  // Disable firewalld (common cause of network issues in Kubernetes)
526
533
  shellExec(`sudo systemctl stop firewalld || true`); // Stop if running
@@ -663,8 +670,8 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
663
670
  shellExec('sudo iptables -F || true');
664
671
  shellExec('sudo iptables -t nat -F || true');
665
672
  // Restore iptables rules
666
- shellExec(`chmod +x ${options.underpostRoot}/manifests/maas/nat-iptables.sh`);
667
- shellExec(`${options.underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
673
+ shellExec(`chmod +x ${options.underpostRoot}/scripts/nat-iptables.sh`);
674
+ shellExec(`${options.underpostRoot}/scripts/nat-iptables.sh`, { silent: true });
668
675
  shellExec('sudo ip link del cni0 || true');
669
676
  shellExec('sudo ip link del flannel.1 || true');
670
677
 
@@ -691,7 +698,11 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
691
698
  */
692
699
  getResourcesCapacity(node) {
693
700
  const resources = {};
694
- const nodeName = node ?? os.hostname();
701
+ const nodeName = node
702
+ ? node
703
+ : UnderpostDeploy.API.get('kind-control-plane', 'node').length > 0
704
+ ? 'kind-control-plane'
705
+ : os.hostname();
695
706
  const info = shellExec(`kubectl describe node ${nodeName} | grep -E '(Allocatable:|Capacity:)' -A 6`, {
696
707
  stdout: true,
697
708
  silent: true,
package/src/cli/cron.js CHANGED
@@ -17,7 +17,7 @@ const logger = loggerFactory(import.meta);
17
17
 
18
18
  /**
19
19
  * UnderpostCron main module methods
20
- * @class
20
+ * @class UnderpostCron
21
21
  * @memberof UnderpostCron
22
22
  */
23
23
  class UnderpostCron {
package/src/cli/db.js CHANGED
@@ -271,10 +271,11 @@ class UnderpostDB {
271
271
  const deployId = _deployId.trim();
272
272
  if (!deployId) continue;
273
273
  const confServer = loadReplicas(
274
+ deployId,
274
275
  JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
275
276
  );
276
277
  const router = await UnderpostDeploy.API.routerFactory(deployId, env);
277
- const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
278
+ const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
278
279
 
279
280
  for (const host of Object.keys(confServer)) {
280
281
  for (const { path, port } of pathPortAssignmentData[host]) {
package/src/cli/deploy.js CHANGED
@@ -20,6 +20,10 @@ import fs from 'fs-extra';
20
20
  import dotenv from 'dotenv';
21
21
  import UnderpostRootEnv from './env.js';
22
22
  import UnderpostCluster from './cluster.js';
23
+ import { timer } from '../client/components/core/CommonJs.js';
24
+ import os from 'node:os';
25
+ import Dns, { getLocalIPv4Address } from '../server/dns.js';
26
+ import UnderpostBaremetal from './baremetal.js';
23
27
 
24
28
  const logger = loggerFactory(import.meta);
25
29
 
@@ -183,10 +187,11 @@ spec:
183
187
  const deployId = _deployId.trim();
184
188
  if (!deployId) continue;
185
189
  const confServer = loadReplicas(
190
+ deployId,
186
191
  JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
187
192
  );
188
193
  const router = await UnderpostDeploy.API.routerFactory(deployId, env);
189
- const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
194
+ const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
190
195
  const { fromPort, toPort } = deployRangePortFactory(router);
191
196
  const deploymentVersions = options.versions.split(',');
192
197
  fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
@@ -226,12 +231,12 @@ metadata:
226
231
  spec:
227
232
  virtualhost:
228
233
  fqdn: ${host}${
229
- env === 'development'
230
- ? ''
231
- : `
234
+ env === 'development'
235
+ ? ''
236
+ : `
232
237
  tls:
233
238
  secretName: ${host}`
234
- }
239
+ }
235
240
  routes:`;
236
241
  for (const conditionObj of pathPortAssignment) {
237
242
  const { path, port } = conditionObj;
@@ -324,7 +329,8 @@ spec:
324
329
  * @param {string} options.node - Node name for resource allocation.
325
330
  * @param {string} options.restoreHosts - Whether to restore hosts.
326
331
  * @param {string} options.disableUpdateDeployment - Whether to disable updating the deployment.
327
- * @param {string} options.infoTraffic - Whether to display traffic information.
332
+ * @param {string} options.disableUpdateProxy - Whether to disable updating the proxy.
333
+ * @param {string} options.status - Whether to display status host machine server and traffic information.
328
334
  * @param {string} options.etcHosts - Whether to update /etc/hosts.
329
335
  * @returns {Promise<void>} - Promise that resolves when the callback is complete.
330
336
  * @memberof UnderpostDeploy
@@ -348,7 +354,8 @@ spec:
348
354
  node: '',
349
355
  restoreHosts: false,
350
356
  disableUpdateDeployment: false,
351
- infoTraffic: false,
357
+ disableUpdateProxy: false,
358
+ status: false,
352
359
  etcHosts: false,
353
360
  },
354
361
  ) {
@@ -414,7 +421,7 @@ EOF`);
414
421
  } else if (!deployList) deployList = 'dd-default';
415
422
  if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
416
423
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
417
- if (options.infoTraffic === true) {
424
+ if (options.status === true) {
418
425
  for (const _deployId of deployList.split(',')) {
419
426
  const deployId = _deployId.trim();
420
427
  logger.info('', {
@@ -425,6 +432,16 @@ EOF`);
425
432
  pods: await UnderpostDeploy.API.get(deployId),
426
433
  });
427
434
  }
435
+ const interfaceName = Dns.getDefaultNetworkInterface();
436
+ logger.info('Machine', {
437
+ node: os.hostname(),
438
+ arch: UnderpostBaremetal.API.getHostArch(),
439
+ ipv4Public: await Dns.getPublicIp(),
440
+ ipv4Local: getLocalIPv4Address(),
441
+ resources: UnderpostCluster.API.getResourcesCapacity(),
442
+ defaultInterfaceName: interfaceName,
443
+ defaultInterfaceInfo: os.networkInterfaces()[interfaceName],
444
+ });
428
445
  return;
429
446
  }
430
447
  if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
@@ -466,11 +483,14 @@ EOF`);
466
483
  }
467
484
 
468
485
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
486
+
469
487
  for (const host of Object.keys(confServer)) {
470
- shellExec(`sudo kubectl delete HTTPProxy ${host}`);
471
- if (UnderpostDeploy.API.isValidTLSContext({ host, env, options }))
472
- shellExec(`sudo kubectl delete Certificate ${host}`);
473
- if (!options.remove === true && env === 'development') etcHosts.push(host);
488
+ if (!options.disableUpdateProxy) {
489
+ shellExec(`sudo kubectl delete HTTPProxy ${host}`);
490
+ if (UnderpostDeploy.API.isValidTLSContext({ host, env, options }))
491
+ shellExec(`sudo kubectl delete Certificate ${host}`);
492
+ }
493
+ if (!options.remove) etcHosts.push(host);
474
494
  }
475
495
 
476
496
  const manifestsPath =
@@ -478,9 +498,9 @@ EOF`);
478
498
  ? `engine-private/conf/${deployId}/build/production`
479
499
  : `manifests/deployment/${deployId}-${env}`;
480
500
 
481
- if (!options.remove === true) {
501
+ if (!options.remove) {
482
502
  if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
483
- shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
503
+ if (!options.disableUpdateProxy) shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
484
504
 
485
505
  if (UnderpostDeploy.API.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }))
486
506
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
@@ -655,6 +675,37 @@ EOF`);
655
675
  env === 'production' &&
656
676
  options.cert === true &&
657
677
  (!options.certHosts || options.certHosts.split(',').includes(host)),
678
+
679
+ /**
680
+ * Monitors the ready status of a deployment.
681
+ * @param {string} deployId - Deployment ID for which the ready status is being monitored.
682
+ * @param {string} env - Environment for which the ready status is being monitored.
683
+ * @param {string} targetTraffic - Target traffic status for the deployment.
684
+ * @param {Array<string>} ignorePods - List of pod names to ignore.
685
+ * @memberof UnderpostDeploy
686
+ */
687
+ async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = []) {
688
+ let checkStatusIteration = 0;
689
+ const checkStatusIterationMsDelay = 1000;
690
+ const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
691
+ logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
692
+ const minReadyOk = 3;
693
+ let readyOk = 0;
694
+
695
+ while (readyOk < minReadyOk) {
696
+ const ready = UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready;
697
+ if (ready === true) {
698
+ readyOk++;
699
+ logger.info(`${iteratorTag} | Deployment ready. Verification number: ${readyOk}`);
700
+ }
701
+ await timer(checkStatusIterationMsDelay);
702
+ checkStatusIteration++;
703
+ logger.info(
704
+ `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
705
+ );
706
+ }
707
+ logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
708
+ },
658
709
  };
659
710
  }
660
711
 
package/src/cli/fs.js CHANGED
@@ -10,7 +10,7 @@ import dotenv from 'dotenv';
10
10
  import AdmZip from 'adm-zip';
11
11
  import * as dir from 'path';
12
12
  import fs from 'fs-extra';
13
- import { Downloader } from '../server/downloader.js';
13
+ import Downloader from '../server/downloader.js';
14
14
  import UnderpostRepository from './repository.js';
15
15
  import { shellExec } from '../server/process.js';
16
16
  dotenv.config();
@@ -204,7 +204,7 @@ class UnderpostFileStorage {
204
204
  resource_type: 'raw',
205
205
  });
206
206
  logger.info('download result', downloadResult);
207
- await Downloader(downloadResult, path + '.zip');
207
+ await Downloader.downloadFile(downloadResult, path + '.zip');
208
208
  path = UnderpostFileStorage.API.zip2File(path + '.zip');
209
209
  fs.removeSync(path + '.zip');
210
210
  },
package/src/cli/image.js CHANGED
@@ -15,6 +15,13 @@ dotenv.config();
15
15
 
16
16
  const logger = loggerFactory(import.meta);
17
17
 
18
+ /**
19
+ * @class UnderpostImage
20
+ * @description Manages Docker image operations, including pulling, building, and loading images into Kubernetes clusters.
21
+ * This class provides a set of static methods to handle image operations, including pulling base images,
22
+ * building custom images, and loading them into specified Kubernetes clusters (Kind, Kubeadm, or K3s).
23
+ * @memberof UnderpostImage
24
+ */
18
25
  class UnderpostImage {
19
26
  static API = {
20
27
  dockerfile: {
@@ -27,6 +34,7 @@ class UnderpostImage {
27
34
  * @param {boolean} [options.kubeadmLoad=false] - If true, load image into Kubeadm cluster.
28
35
  * @param {boolean} [options.k3sLoad=false] - If true, load image into K3s cluster.
29
36
  * @param {string} [options.path=false] - Path to the Dockerfile context.
37
+ * @param {boolean} [options.dev=false] - If true, use development mode.
30
38
  * @param {string} [options.version=''] - Version tag for the image.
31
39
  * @memberof UnderpostImage
32
40
  */
@@ -36,11 +44,14 @@ class UnderpostImage {
36
44
  kubeadmLoad: false,
37
45
  k3sLoad: false,
38
46
  path: false,
47
+ dev: false,
39
48
  version: '',
40
49
  },
41
50
  ) {
42
51
  // shellExec(`sudo podman pull docker.io/library/debian:buster`);
43
52
  shellExec(`sudo podman pull docker.io/library/rockylinux:9`);
53
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
54
+ const baseCommandOption = options.dev ? ' --dev' : '';
44
55
  const IMAGE_NAME = `rockylinux9-underpost`;
45
56
  const IMAGE_NAME_FULL = `${IMAGE_NAME}:${options.version ?? Underpost.version}`;
46
57
  let LOAD_TYPE = '';
@@ -54,7 +65,7 @@ class UnderpostImage {
54
65
  }
55
66
 
56
67
  shellExec(
57
- `underpost dockerfile-image-build --podman-save --reset --image-path=. --path ${
68
+ `${baseCommand} dockerfile-image-build${baseCommandOption} --podman-save --reset --image-path=. --path ${
58
69
  options.path ?? getUnderpostRootPath()
59
70
  } --image-name=${IMAGE_NAME_FULL} ${LOAD_TYPE}`,
60
71
  );
@@ -75,6 +86,7 @@ class UnderpostImage {
75
86
  * @param {boolean} [options.secrets=false] - If true, load secrets from the .env file for the build.
76
87
  * @param {string} [options.secretsPath=''] - Custom path to the .env file for secrets.
77
88
  * @param {boolean} [options.reset=false] - If true, perform a no-cache build.
89
+ * @param {boolean} [options.dev=false] - If true, use development mode.
78
90
  * @memberof UnderpostImage
79
91
  */
80
92
  build(
@@ -90,6 +102,7 @@ class UnderpostImage {
90
102
  secrets: false,
91
103
  secretsPath: '',
92
104
  reset: false,
105
+ dev: false,
93
106
  },
94
107
  ) {
95
108
  const {
@@ -104,6 +117,7 @@ class UnderpostImage {
104
117
  kubeadmLoad,
105
118
  k3sLoad,
106
119
  reset,
120
+ dev,
107
121
  } = options;
108
122
  const podManImg = `localhost/${imageName}`;
109
123
  if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
@@ -132,7 +146,10 @@ class UnderpostImage {
132
146
  } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput} --network host`,
133
147
  );
134
148
 
135
- if (podmanSave === true) shellExec(`podman save -o ${tarFile} ${podManImg}`);
149
+ if (podmanSave === true) {
150
+ if (fs.existsSync(tarFile)) fs.removeSync(tarFile);
151
+ shellExec(`podman save -o ${tarFile} ${podManImg}`);
152
+ }
136
153
  if (kindLoad === true) shellExec(`sudo kind load image-archive ${tarFile}`);
137
154
  if (kubeadmLoad === true) {
138
155
  // Use 'ctr' for Kubeadm