cyberia 3.2.9 → 3.2.22

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 (184) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +7 -0
  2. package/.github/workflows/engine-cyberia.ci.yml +14 -2
  3. package/.github/workflows/ghpkg.ci.yml +1 -0
  4. package/.github/workflows/npmpkg.ci.yml +10 -5
  5. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  6. package/.github/workflows/release.cd.yml +1 -0
  7. package/.vscode/extensions.json +9 -9
  8. package/.vscode/settings.json +20 -4
  9. package/CHANGELOG.md +363 -1
  10. package/CLI-HELP.md +975 -1061
  11. package/README.md +190 -348
  12. package/bin/build.js +102 -125
  13. package/bin/build.template.js +33 -0
  14. package/bin/cyberia.js +238 -56
  15. package/bin/deploy.js +16 -3
  16. package/bin/index.js +238 -56
  17. package/bump.config.js +26 -0
  18. package/conf.js +131 -24
  19. package/deployment.yaml +76 -2
  20. package/hardhat/package-lock.json +113 -144
  21. package/hardhat/package.json +4 -3
  22. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  23. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  24. package/manifests/deployment/dd-cyberia-development/deployment.yaml +76 -2
  25. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  26. package/manifests/kind-config-dev.yaml +8 -0
  27. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  28. package/manifests/mongodb/pv-pvc.yaml +44 -8
  29. package/manifests/mongodb/statefulset.yaml +55 -68
  30. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  31. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  32. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  33. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  34. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  35. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  36. package/manifests/valkey/statefulset.yaml +1 -1
  37. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  38. package/package.json +31 -19
  39. package/scripts/ipxe-setup.sh +52 -49
  40. package/scripts/k3s-node-setup.sh +81 -46
  41. package/scripts/link-local-underpost-cli.sh +6 -0
  42. package/scripts/lxd-vm-setup.sh +193 -8
  43. package/scripts/maas-nat-firewalld.sh +145 -0
  44. package/scripts/test-monitor.sh +250 -0
  45. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  46. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  47. package/src/api/core/core.router.js +19 -14
  48. package/src/api/core/core.service.js +5 -5
  49. package/src/api/crypto/crypto.router.js +18 -12
  50. package/src/api/crypto/crypto.service.js +3 -3
  51. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  52. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  53. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  55. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  56. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  57. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  58. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  59. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  60. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  61. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  62. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  63. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  64. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  65. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  66. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  67. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  68. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  69. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  70. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  71. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  72. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  73. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  74. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  75. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  76. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +458 -0
  77. package/src/api/default/default.router.js +22 -18
  78. package/src/api/default/default.service.js +5 -5
  79. package/src/api/document/document.router.js +28 -23
  80. package/src/api/document/document.service.js +100 -23
  81. package/src/api/file/file.router.js +19 -13
  82. package/src/api/file/file.service.js +9 -7
  83. package/src/api/instance/instance.router.js +29 -24
  84. package/src/api/instance/instance.service.js +6 -6
  85. package/src/api/ipfs/ipfs.router.js +21 -16
  86. package/src/api/ipfs/ipfs.service.js +8 -8
  87. package/src/api/object-layer/object-layer.router.js +512 -507
  88. package/src/api/object-layer/object-layer.service.js +17 -14
  89. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  90. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  91. package/src/api/test/test.router.js +17 -12
  92. package/src/api/types.js +24 -0
  93. package/src/api/user/guest.service.js +5 -4
  94. package/src/api/user/user.router.js +297 -288
  95. package/src/api/user/user.service.js +100 -35
  96. package/src/cli/baremetal.js +132 -101
  97. package/src/cli/cluster.js +700 -232
  98. package/src/cli/db.js +59 -60
  99. package/src/cli/deploy.js +291 -294
  100. package/src/cli/env.js +1 -4
  101. package/src/cli/fs.js +13 -3
  102. package/src/cli/image.js +58 -4
  103. package/src/cli/index.js +127 -15
  104. package/src/cli/ipfs.js +4 -6
  105. package/src/cli/kubectl.js +4 -1
  106. package/src/cli/lxd.js +1099 -223
  107. package/src/cli/monitor.js +396 -9
  108. package/src/cli/release.js +355 -146
  109. package/src/cli/repository.js +169 -30
  110. package/src/cli/run.js +347 -117
  111. package/src/cli/secrets.js +11 -2
  112. package/src/cli/test.js +9 -3
  113. package/src/client/Default.index.js +9 -3
  114. package/src/client/components/core/Auth.js +5 -0
  115. package/src/client/components/core/ClientEvents.js +76 -0
  116. package/src/client/components/core/EventBus.js +4 -0
  117. package/src/client/components/core/Modal.js +82 -41
  118. package/src/client/components/core/PanelForm.js +14 -10
  119. package/src/client/components/core/Worker.js +162 -363
  120. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  121. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  122. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +55 -1
  123. package/src/client/public/cyberia-docs/ARCHITECTURE.md +223 -361
  124. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  125. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  126. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +212 -185
  127. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  128. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  129. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +23 -1
  130. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  131. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  132. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  133. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  134. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  135. package/src/client/sw/core.sw.js +174 -112
  136. package/src/db/DataBaseProvider.js +115 -15
  137. package/src/db/mariadb/MariaDB.js +2 -1
  138. package/src/db/mongo/MongoBootstrap.js +657 -0
  139. package/src/db/mongo/MongooseDB.js +130 -21
  140. package/src/grpc/cyberia/grpc-server.js +25 -57
  141. package/src/index.js +1 -1
  142. package/src/runtime/cyberia-client/Dockerfile +10 -7
  143. package/src/runtime/cyberia-client/Dockerfile.dev +67 -0
  144. package/src/runtime/cyberia-server/Dockerfile +11 -6
  145. package/src/runtime/cyberia-server/Dockerfile.dev +47 -0
  146. package/src/runtime/express/Express.js +2 -2
  147. package/src/runtime/wp/Dockerfile +3 -3
  148. package/src/runtime/wp/Wp.js +8 -5
  149. package/src/server/auth.js +2 -2
  150. package/src/server/catalog-underpost.js +61 -0
  151. package/src/server/catalog.js +77 -0
  152. package/src/server/client-build-docs.js +1 -1
  153. package/src/server/client-build.js +94 -129
  154. package/src/server/conf.js +496 -135
  155. package/src/server/ipfs-client.js +5 -3
  156. package/src/server/process.js +180 -19
  157. package/src/server/proxy.js +9 -2
  158. package/src/server/runtime-status.js +235 -0
  159. package/src/server/runtime.js +1 -1
  160. package/src/server/start.js +44 -11
  161. package/src/server/valkey.js +2 -0
  162. package/src/ws/IoInterface.js +16 -16
  163. package/src/ws/core/channels/core.ws.chat.js +11 -11
  164. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  165. package/src/ws/core/channels/core.ws.stream.js +19 -19
  166. package/src/ws/core/core.ws.connection.js +8 -8
  167. package/src/ws/core/core.ws.server.js +6 -5
  168. package/src/ws/default/channels/default.ws.main.js +10 -10
  169. package/src/ws/default/default.ws.connection.js +4 -4
  170. package/src/ws/default/default.ws.server.js +4 -3
  171. package/test/deploy-monitor.test.js +251 -0
  172. package/bin/file.js +0 -202
  173. package/bin/vs.js +0 -74
  174. package/bin/zed.js +0 -84
  175. package/manifests/deployment/dd-test-development/deployment.yaml +0 -254
  176. package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
  177. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  178. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  179. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  180. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  181. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  182. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  183. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  184. /package/src/client/ssr/{pages → views}/Test.js +0 -0
@@ -1,23 +1,208 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
1
3
 
2
- echo "Expanding /dev/sda2 and resizing filesystem..."
4
+ # ---------------------------------------------------------------------------
5
+ # LXD VM OS base setup. Runs inside the VM via `lxc exec`. Idempotent.
6
+ # ---------------------------------------------------------------------------
3
7
 
4
- if ! command -v parted &>/dev/null; then
5
- sudo dnf install -y parted
8
+ # lxdbr0 is a plain L2 bridge: LXD runs no DHCP/DNS on it (MAAS owns
9
+ # provisioning), so VMs configure their NIC statically and deterministically.
10
+ # The host pins the gateway (10.250.250.1) on the bridge and provides NAT; no
11
+ # resolver listens there, so DNS must use public/MAAS resolvers, not the gateway.
12
+ # LXD_NET_MODE=dhcp opts back into DHCP-first for VMs on a MAAS-served segment.
13
+ LXD_NET_MODE="${LXD_NET_MODE:-static}"
14
+ LXD_FALLBACK_IPV4_CIDR="${LXD_FALLBACK_IPV4_CIDR:-10.250.250.100/24}"
15
+ LXD_FALLBACK_GATEWAY="${LXD_FALLBACK_GATEWAY:-10.250.250.1}"
16
+ LXD_FALLBACK_DNS="${LXD_FALLBACK_DNS:-1.1.1.1 8.8.8.8}"
17
+ ROCKY_MIRROR_HOST="${ROCKY_MIRROR_HOST:-mirrors.rockylinux.org}"
18
+
19
+ current_ipv4() {
20
+ ip -4 addr show "$IFACE" | awk '/inet /{print $2; exit}'
21
+ }
22
+
23
+ wait_for_ipv4() {
24
+ local current_ip=""
25
+
26
+ for _ in $(seq 1 10); do
27
+ current_ip="$(current_ipv4)"
28
+ if [ -n "$current_ip" ]; then
29
+ echo "$current_ip"
30
+ return 0
31
+ fi
32
+ sleep 1
33
+ done
34
+
35
+ return 1
36
+ }
37
+
38
+ refresh_resolver_state() {
39
+ if command -v resolvectl >/dev/null 2>&1; then
40
+ sudo resolvectl flush-caches || true
41
+ fi
42
+
43
+ if [ -f /run/NetworkManager/resolv.conf ]; then
44
+ sudo ln -sf /run/NetworkManager/resolv.conf /etc/resolv.conf || true
45
+ fi
46
+ }
47
+
48
+ verify_name_resolution() {
49
+ getent ahostsv4 "$ROCKY_MIRROR_HOST" >/dev/null 2>&1
50
+ }
51
+
52
+ retry_dnf() {
53
+ local attempt=1
54
+ local max_attempts=3
55
+
56
+ until "$@"; do
57
+ if [ "$attempt" -ge "$max_attempts" ]; then
58
+ return 1
59
+ fi
60
+
61
+ echo "DNF command failed (attempt $attempt/$max_attempts): $*"
62
+ echo "Refreshing resolver state before retry..."
63
+ refresh_resolver_state
64
+ verify_name_resolution || true
65
+ attempt=$((attempt + 1))
66
+ done
67
+ }
68
+
69
+ # k3s derives the node name from the system hostname. The Rocky image keeps
70
+ # "localhost.localdomain" for every VM, so without this every node would try to
71
+ # register under the same name — the second one is rejected ("Node password
72
+ # rejected, duplicate hostname"). Pin the hostname to the LXD instance name so
73
+ # node names are unique and deterministic (control plane labeling relies on it).
74
+ if [ -n "${LXD_NODE_NAME:-}" ] && [ "$(hostname)" != "$LXD_NODE_NAME" ]; then
75
+ echo "--- Hostname ---"
76
+ echo "Setting hostname to ${LXD_NODE_NAME} (k3s node name)..."
77
+ sudo hostnamectl set-hostname "$LXD_NODE_NAME" 2>/dev/null || sudo hostname "$LXD_NODE_NAME"
78
+ fi
79
+
80
+ echo "--- Network Configuration ---"
81
+
82
+ # 1. Detect primary non-loopback interface
83
+ IFACE=$(ip -o link show up | awk -F': ' '$2!="lo"{print $2; exit}')
84
+ echo "Using network interface: ${IFACE:-none}"
85
+
86
+ if [ -z "$IFACE" ]; then
87
+ echo "CRITICAL ERROR: No network interface detected."
88
+ exit 1
89
+ fi
90
+
91
+ # 2. Force NetworkManager initialization if interface lacks an IP address
92
+ CURRENT_IP="$(current_ipv4 || true)"
93
+
94
+ if command -v nmcli >/dev/null 2>&1 && [ -z "$CURRENT_IP" ]; then
95
+ echo "Inspecting NetworkManager profiles..."
96
+
97
+ # Check if a profile is tracking this device
98
+ NM_CON=$(nmcli -t -f NAME,DEVICE connection show | awk -F: -v iface="$IFACE" '$2==iface{print $1; exit}')
99
+
100
+ if [ -z "$NM_CON" ]; then
101
+ echo "No connection profile matches $IFACE. Forcing generation of profile 'k3s-net'..."
102
+ nmcli connection add type ethernet con-name k3s-net ifname "$IFACE"
103
+ NM_CON="k3s-net"
104
+ fi
105
+
106
+ apply_static_ipv4() {
107
+ echo "Applying static LXD bridge address ${LXD_FALLBACK_IPV4_CIDR} (gw ${LXD_FALLBACK_GATEWAY}, dns ${LXD_FALLBACK_DNS})..."
108
+ nmcli connection modify "$NM_CON" \
109
+ connection.autoconnect yes \
110
+ connection.interface-name "$IFACE" \
111
+ ipv4.method manual \
112
+ ipv4.addresses "$LXD_FALLBACK_IPV4_CIDR" \
113
+ ipv4.gateway "$LXD_FALLBACK_GATEWAY" \
114
+ ipv4.dns "$LXD_FALLBACK_DNS" \
115
+ ipv4.ignore-auto-dns yes \
116
+ ipv6.method ignore
117
+ nmcli connection up "$NM_CON"
118
+ CURRENT_IP="$(wait_for_ipv4 || true)"
119
+ }
120
+
121
+ # Static-first: lxdbr0 has no DHCP, so a DHCP attempt only adds a ~45s
122
+ # activation timeout per boot. DHCP is opt-in for VMs on a MAAS-served
123
+ # segment (LXD_NET_MODE=dhcp), with a static fallback if no lease appears.
124
+ if [ "$LXD_NET_MODE" = "dhcp" ]; then
125
+ echo "Configuring DHCP-first NetworkManager profile '$NM_CON'..."
126
+ nmcli connection modify "$NM_CON" \
127
+ connection.autoconnect yes \
128
+ connection.interface-name "$IFACE" \
129
+ ipv4.method auto \
130
+ ipv4.dhcp-client-id mac \
131
+ ipv4.ignore-auto-dns no \
132
+ ipv6.method ignore
133
+ echo "Bringing network interface up with DHCP..."
134
+ nmcli connection up "$NM_CON" || echo "DHCP activation failed; static fallback will be applied."
135
+ CURRENT_IP="$(wait_for_ipv4 || true)"
136
+ if [ -z "$CURRENT_IP" ]; then
137
+ echo "No DHCP lease on $IFACE."
138
+ apply_static_ipv4
139
+ fi
140
+ else
141
+ echo "Configuring static NetworkManager profile '$NM_CON' for the plain LXD bridge..."
142
+ apply_static_ipv4
143
+ fi
144
+ fi
145
+
146
+ # 3. Give the network interface a short window to settle and lease an IP address
147
+ echo "Waiting for IP address allocation..."
148
+ CURRENT_IP="${CURRENT_IP:-$(wait_for_ipv4 || true)}"
149
+ if [ -n "$CURRENT_IP" ]; then
150
+ echo "Interface $IFACE successfully initialized with IP: $CURRENT_IP"
151
+ fi
152
+
153
+ # 4. Verify DNS and outbound connectivity before proceeding (Fail Closed)
154
+ echo "Verifying internet and DNS connectivity..."
155
+ refresh_resolver_state
156
+ if ! verify_name_resolution; then
157
+ echo "CRITICAL ERROR: DNS lookup failed for $ROCKY_MIRROR_HOST. Diagnostic snapshot:"
158
+ echo "=== /etc/resolv.conf ==="
159
+ cat /etc/resolv.conf || true
160
+ echo "=== Interface State ==="
161
+ ip -4 addr show "$IFACE" || true
162
+ echo "=== Routing Table ==="
163
+ ip route show || true
164
+ echo "=== NetworkManager Status ==="
165
+ nmcli device status || true
166
+ exit 1
167
+ fi
168
+
169
+ if ! timeout 15 curl -fsSL --max-time 10 "https://${ROCKY_MIRROR_HOST}" -o /dev/null; then
170
+ echo "CRITICAL ERROR: Network/DNS remains unreachable. Diagnostic snapshot:"
171
+ echo "=== /etc/resolv.conf ==="
172
+ cat /etc/resolv.conf || true
173
+ echo "=== Interface State ==="
174
+ ip -4 addr show "$IFACE" || true
175
+ echo "=== Routing Table ==="
176
+ ip route show || true
177
+ echo "=== NetworkManager Status ==="
178
+ nmcli device status || true
179
+ exit 1
6
180
  fi
7
181
 
182
+ echo "--- Disk Resizing ---"
183
+ if ! command -v parted >/dev/null 2>&1; then
184
+ sudo dnf install -y parted
185
+ fi
186
+
187
+ set +e
8
188
  sudo parted /dev/sda ---pretend-input-tty <<EOF
9
189
  unit s
10
190
  resizepart 2 100%
11
191
  Yes
12
192
  quit
13
193
  EOF
194
+ set -e
14
195
 
15
196
  sudo resize2fs /dev/sda2
16
- echo "Disk resized."
197
+ echo "Disk partition and filesystem resized successfully."
17
198
 
18
- echo "Installing essential packages..."
19
- sudo dnf install -y tar bzip2 git curl jq epel-release
20
- sudo dnf -y update
199
+ echo "--- Package Installation ---"
200
+ retry_dnf sudo dnf install -y epel-release
201
+ retry_dnf sudo dnf install -y tar bzip2 git curl jq
202
+ retry_dnf sudo dnf -y update
21
203
 
22
- echo "Loading br_netfilter module..."
204
+ echo "--- Kernel Modules for K3s ---"
23
205
  sudo modprobe br_netfilter
206
+ echo "br_netfilter" | sudo tee /etc/modules-load.d/k3s-br_netfilter.conf > /dev/null
207
+
208
+ echo "Setup complete. System is ready for k3s-node-setup.sh"
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # MAAS NAT + firewall helper
5
+ # - Enables IPv4 forwarding and masquerading for the MAAS/provisioning subnet
6
+ # - Opens the MAAS API on the uplink zone when requested
7
+ # - Opens provisioning and optional NFS services on the zone bound to MAAS_LAN_CIDR
8
+ #
9
+ # Usage examples:
10
+ # sudo MAAS_LAN_CIDR="192.168.1.0/24" PUBLIC_ZONE="public" ./maas-nat-firewalld.sh
11
+ # sudo NFS_MODE="v3" CONFIGURE_NFS_V3_PORTS="true" ./maas-nat-firewalld.sh
12
+
13
+ MAAS_LAN_CIDR="${MAAS_LAN_CIDR:-192.168.1.0/24}"
14
+ PUBLIC_ZONE="${PUBLIC_ZONE:-public}"
15
+ TRUSTED_ZONE="${TRUSTED_ZONE:-trusted}"
16
+ NFS_MODE="${NFS_MODE:-v4}" # v4 or v3
17
+ CONFIGURE_NFS_V3_PORTS="${CONFIGURE_NFS_V3_PORTS:-false}"
18
+ EXPOSE_MAAS_API_PUBLIC="${EXPOSE_MAAS_API_PUBLIC:-true}"
19
+
20
+ # Fixed NFSv3 ports (only used when CONFIGURE_NFS_V3_PORTS=true)
21
+ NFS_MOUNTD_PORT="${NFS_MOUNTD_PORT:-20048}"
22
+ NFS_STATD_PORT="${NFS_STATD_PORT:-32765}"
23
+ # Outgoing port must differ from the listen port; rpc.statd exits 255 if they match.
24
+ NFS_STATD_OUTGOING_PORT="${NFS_STATD_OUTGOING_PORT:-32766}"
25
+ NFS_LOCKD_PORT="${NFS_LOCKD_PORT:-32803}"
26
+ NFS_LOCKD_UDP_PORT="${NFS_LOCKD_UDP_PORT:-${NFS_LOCKD_PORT}}"
27
+ NFS_CONF_DROPIN="/etc/nfs.conf.d/99-maas-nfs-v3-ports.conf"
28
+
29
+ case "${NFS_MODE}" in
30
+ v3|v4) ;;
31
+ *)
32
+ echo "[ERROR] NFS_MODE must be 'v3' or 'v4'." >&2
33
+ exit 1
34
+ ;;
35
+ esac
36
+
37
+ # Ensure the kernel NFS server is enabled and actually running. `exportfs -rav` only populates the
38
+ # export table; it does not start nfsd, and `systemctl try-restart` is a no-op when the unit is
39
+ # stopped. Without this, clients hang on mount because nothing listens on 2049/mountd.
40
+ ensure_nfs_server_running() {
41
+ if [[ "${NFS_MODE}" == "v3" ]]; then
42
+ # NFSv3 depends on the portmapper and the status monitor.
43
+ sudo systemctl enable --now rpcbind 2>/dev/null || true
44
+ sudo systemctl enable --now rpc-statd 2>/dev/null || true
45
+ fi
46
+
47
+ local unit=""
48
+ for candidate in nfs-server nfs-kernel-server; do
49
+ if systemctl cat "${candidate}.service" >/dev/null 2>&1; then
50
+ unit="${candidate}"
51
+ break
52
+ fi
53
+ done
54
+ if [[ -z "${unit}" ]]; then
55
+ echo "[ERROR] No NFS server unit found (nfs-server/nfs-kernel-server). Install nfs-utils or nfs-kernel-server." >&2
56
+ return 1
57
+ fi
58
+
59
+ echo "[INFO] Enabling and (re)starting ${unit} to apply exports and fixed ports..."
60
+ sudo systemctl enable "${unit}" 2>/dev/null || true
61
+ sudo systemctl restart "${unit}"
62
+ }
63
+
64
+ echo "[INFO] Enabling firewalld..."
65
+ sudo systemctl enable --now firewalld
66
+
67
+ echo "[INFO] Enabling kernel forwarding..."
68
+ sudo tee /etc/sysctl.d/99-maas-nat.conf >/dev/null <<'EOF'
69
+ net.ipv4.ip_forward = 1
70
+ EOF
71
+
72
+ sudo sysctl --system >/dev/null
73
+
74
+ echo "[INFO] Allowing NAT for the MAAS LAN..."
75
+ sudo firewall-cmd --permanent --zone="${PUBLIC_ZONE}" --add-masquerade
76
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-source="${MAAS_LAN_CIDR}"
77
+
78
+ echo "[INFO] Opening MAAS services on the MAAS LAN zone..."
79
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port=5240/tcp
80
+ for service in dns dhcp tftp; do
81
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-service="${service}"
82
+ done
83
+
84
+ if [[ "${EXPOSE_MAAS_API_PUBLIC}" == "true" ]]; then
85
+ echo "[INFO] Opening MAAS API on the uplink zone..."
86
+ sudo firewall-cmd --permanent --zone="${PUBLIC_ZONE}" --add-port=5240/tcp
87
+ fi
88
+
89
+ echo "[INFO] Opening NFS ports on the MAAS LAN zone..."
90
+
91
+ if [[ "${NFS_MODE}" == "v4" ]]; then
92
+ # NFSv4 normally needs only TCP 2049.
93
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port=2049/tcp
94
+ else
95
+ # NFSv3 side-band daemons need fixed ports for deterministic firewall rules.
96
+ if [[ "${CONFIGURE_NFS_V3_PORTS}" != "true" ]]; then
97
+ echo "[ERROR] NFSv3 requires CONFIGURE_NFS_V3_PORTS=true for consistent firewalld rules." >&2
98
+ echo "[ERROR] Re-run with CONFIGURE_NFS_V3_PORTS=true or switch to NFS_MODE=v4." >&2
99
+ exit 1
100
+ fi
101
+
102
+ echo "[INFO] Writing fixed NFSv3 port configuration..."
103
+ sudo install -d -m 0755 /etc/nfs.conf.d
104
+ sudo tee "${NFS_CONF_DROPIN}" >/dev/null <<EOF
105
+ [nfsd]
106
+ vers3=y
107
+
108
+ [mountd]
109
+ port=${NFS_MOUNTD_PORT}
110
+
111
+ [statd]
112
+ port=${NFS_STATD_PORT}
113
+ outgoing-port=${NFS_STATD_OUTGOING_PORT}
114
+
115
+ [lockd]
116
+ port=${NFS_LOCKD_PORT}
117
+ udp-port=${NFS_LOCKD_UDP_PORT}
118
+ EOF
119
+
120
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-service=rpc-bind
121
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port=2049/tcp
122
+ for proto in tcp udp; do
123
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port="${NFS_MOUNTD_PORT}/${proto}"
124
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port="${NFS_STATD_PORT}/${proto}"
125
+ # Outgoing port is the fixed source for SM_NOTIFY; peers need to reach it.
126
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port="${NFS_STATD_OUTGOING_PORT}/${proto}"
127
+ done
128
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port="${NFS_LOCKD_PORT}/tcp"
129
+ sudo firewall-cmd --permanent --zone="${TRUSTED_ZONE}" --add-port="${NFS_LOCKD_UDP_PORT}/udp"
130
+ fi
131
+
132
+ ensure_nfs_server_running
133
+
134
+ echo "[INFO] Reloading firewall..."
135
+ sudo firewall-cmd --reload
136
+
137
+ echo
138
+ echo "[INFO] Firewall status:"
139
+ sudo firewall-cmd --zone="${PUBLIC_ZONE}" --list-all
140
+ echo
141
+ echo "[INFO] MAAS LAN zone (${TRUSTED_ZONE}):"
142
+ sudo firewall-cmd --zone="${TRUSTED_ZONE}" --list-all
143
+
144
+ echo
145
+ echo "[INFO] MAAS NAT + firewall configuration completed."
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # test-monitor.sh — end-to-end deploy + two-phase monitor smoke test.
4
+ #
5
+ # Two deployment shapes are supported (see
6
+ # src/client/public/nexodev/docs/references/Deploy-Monitor-PRD.md and
7
+ # 'Deploy custom instance to K8S.md'):
8
+ #
9
+ # --mode runtime `underpost start` deploy (e.g. dd-test). Monitored with the
10
+ # HTTP gate: /_internal/ready probes + port-forward status.
11
+ # --mode instance Custom instances from conf.instances.json (e.g. cyberia
12
+ # mmo-server / mmo-client). Monitored with the kubernetes gate
13
+ # (TCP readinessProbe) + exec status transport.
14
+ #
15
+ # Every variable in the DEFAULTS block below is overridable with a flag of the
16
+ # same name (--env, --deploy-id, …). Run with --help for the full list.
17
+ #
18
+ set -euo pipefail
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ ENGINE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
22
+ cd "$ENGINE_DIR"
23
+
24
+ # ─────────────────────────── DEFAULTS (all overridable) ───────────────────────────
25
+ MODE=runtime # runtime | instance
26
+ ENV=development # development | production
27
+ DEPLOY_ID=dd-test # deploy id (instance mode: parent of conf.instances.json)
28
+ INSTANCE_IDS= # instance mode: csv of ids (default: all in conf.instances.json)
29
+ IMAGE=underpost/wp:v3.2.14 # runtime mode image (instance mode reads image from conf)
30
+ VERSIONS=green # csv of blue/green versions
31
+ REPLICAS=1 # replicas per deployment
32
+ NAMESPACE=default # k8s namespace
33
+ CLUSTER= # kind | kubeadm | k3s | "" (auto/none)
34
+ TIMEOUT_RESPONSE=300000ms # HTTPProxy per-route response timeout
35
+ TEMPLATE_REPO=underpostnet/pwa-microservices-template-private # runtime mode link repo
36
+ ENVOY_NAMESPACE=projectcontour # ingress namespace (instance TLS exposure)
37
+ ENVOY_SERVICE=envoy # ingress service (instance TLS exposure)
38
+ HTTPS_PORT=443 # local https port for instance TLS exposure
39
+
40
+ USE_CERT=false # runtime: pass --cert to the proxy step
41
+ USE_PULL_BUNDLE=false # runtime: include --pull-bundle in the start cmd
42
+ USE_TLS=false # generate self-signed certs + expose over HTTPS
43
+ USE_TEST_REPO=false # runtime: publish src to engine-test-<id> + pod clones it (--private-test-repo)
44
+ DO_BUILD_TEMPLATE=true # run `node bin/build.template --update-private`
45
+ DO_CLUSTER_MANIFESTS=true # run `node bin run build-cluster-deployment-manifests`
46
+ DO_EXPOSE=true # expose locally after deploy + monitor
47
+
48
+ usage() {
49
+ cat <<'EOF'
50
+ Usage: scripts/test-monitor.sh [flags]
51
+
52
+ Modes:
53
+ --mode <runtime|instance> Deployment shape to test (default: runtime)
54
+
55
+ Common (every default is overridable):
56
+ --env <development|production>
57
+ --deploy-id <id>
58
+ --instance-ids <csv> instance mode only; default = all ids in conf.instances.json
59
+ --image <image> runtime mode only
60
+ --versions <csv> e.g. green or blue,green
61
+ --replicas <n>
62
+ --namespace <ns>
63
+ --cluster <kind|kubeadm|k3s|none>
64
+ --timeout-response <dur> e.g. 300000ms
65
+ --template-repo <owner/repo> runtime mode link repo
66
+ --envoy-namespace <ns> instance TLS exposure ingress namespace
67
+ --envoy-service <name> instance TLS exposure ingress service
68
+ --https-port <port> local https port for instance TLS exposure
69
+
70
+ Toggles (use --x / --no-x):
71
+ --tls | --no-tls self-signed certs + HTTPS exposure (default: off)
72
+ --cert | --no-cert runtime proxy --cert (default: off)
73
+ --pull-bundle | --no-pull-bundle runtime start --pull-bundle (default: off)
74
+ --test-repo | --no-test-repo runtime: publish src to engine-test-<id> and have the
75
+ pod clone it via --private-test-repo (default: off)
76
+ --expose | --no-expose local exposure after monitor (default: on)
77
+ --build-template | --no-build-template sync private template (default: on)
78
+ --cluster-manifests | --no-cluster-manifests rebuild cluster manifests (default: on)
79
+
80
+ -h, --help
81
+
82
+ Examples:
83
+ # Runtime deploy (default), no TLS
84
+ scripts/test-monitor.sh
85
+
86
+ # Runtime deploy over HTTPS
87
+ scripts/test-monitor.sh --tls
88
+
89
+ # Runtime deploy from a work-in-progress test source repo (engine-test-<id>)
90
+ scripts/test-monitor.sh --test-repo
91
+
92
+ # Cyberia instances (both), dev on kind, no TLS
93
+ scripts/test-monitor.sh --mode instance --deploy-id dd-cyberia --cluster kind
94
+
95
+ # Only the mmo-server instance, production, over HTTPS
96
+ scripts/test-monitor.sh --mode instance --deploy-id dd-cyberia \
97
+ --instance-ids mmo-server --env production --cluster kubeadm --tls
98
+ EOF
99
+ }
100
+
101
+ # ─────────────────────────── flag parsing ───────────────────────────
102
+ # Supports `--key value` and `--key=value` for value flags, plus --x/--no-x toggles.
103
+ needval() { [[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 1; }; }
104
+ while [[ $# -gt 0 ]]; do
105
+ case "$1" in
106
+ --mode) needval "$@"; MODE="$2"; shift 2;; --mode=*) MODE="${1#*=}"; shift;;
107
+ --env) needval "$@"; ENV="$2"; shift 2;; --env=*) ENV="${1#*=}"; shift;;
108
+ --deploy-id) needval "$@"; DEPLOY_ID="$2"; shift 2;; --deploy-id=*) DEPLOY_ID="${1#*=}"; shift;;
109
+ --instance-ids) needval "$@"; INSTANCE_IDS="$2"; shift 2;; --instance-ids=*) INSTANCE_IDS="${1#*=}"; shift;;
110
+ --image) needval "$@"; IMAGE="$2"; shift 2;; --image=*) IMAGE="${1#*=}"; shift;;
111
+ --versions) needval "$@"; VERSIONS="$2"; shift 2;; --versions=*) VERSIONS="${1#*=}"; shift;;
112
+ --replicas) needval "$@"; REPLICAS="$2"; shift 2;; --replicas=*) REPLICAS="${1#*=}"; shift;;
113
+ --namespace) needval "$@"; NAMESPACE="$2"; shift 2;; --namespace=*) NAMESPACE="${1#*=}"; shift;;
114
+ --cluster) needval "$@"; CLUSTER="$2"; shift 2;; --cluster=*) CLUSTER="${1#*=}"; shift;;
115
+ --timeout-response) needval "$@"; TIMEOUT_RESPONSE="$2"; shift 2;; --timeout-response=*) TIMEOUT_RESPONSE="${1#*=}"; shift;;
116
+ --template-repo) needval "$@"; TEMPLATE_REPO="$2"; shift 2;; --template-repo=*) TEMPLATE_REPO="${1#*=}"; shift;;
117
+ --envoy-namespace) needval "$@"; ENVOY_NAMESPACE="$2"; shift 2;; --envoy-namespace=*) ENVOY_NAMESPACE="${1#*=}"; shift;;
118
+ --envoy-service) needval "$@"; ENVOY_SERVICE="$2"; shift 2;; --envoy-service=*) ENVOY_SERVICE="${1#*=}"; shift;;
119
+ --https-port) needval "$@"; HTTPS_PORT="$2"; shift 2;; --https-port=*) HTTPS_PORT="${1#*=}"; shift;;
120
+ --tls) USE_TLS=true; shift;; --no-tls) USE_TLS=false; shift;;
121
+ --cert) USE_CERT=true; shift;; --no-cert) USE_CERT=false; shift;;
122
+ --pull-bundle) USE_PULL_BUNDLE=true; shift;; --no-pull-bundle) USE_PULL_BUNDLE=false; shift;;
123
+ --test-repo) USE_TEST_REPO=true; shift;; --no-test-repo) USE_TEST_REPO=false; shift;;
124
+ --expose) DO_EXPOSE=true; shift;; --no-expose) DO_EXPOSE=false; shift;;
125
+ --build-template) DO_BUILD_TEMPLATE=true; shift;; --no-build-template) DO_BUILD_TEMPLATE=false; shift;;
126
+ --cluster-manifests) DO_CLUSTER_MANIFESTS=true; shift;; --no-cluster-manifests) DO_CLUSTER_MANIFESTS=false; shift;;
127
+ -h|--help) usage; exit 0;;
128
+ *) echo "Unknown flag: $1" >&2; usage; exit 1;;
129
+ esac
130
+ done
131
+
132
+ # ─────────────────────────── derived flags ───────────────────────────
133
+ CLUSTER_FLAG=""
134
+ case "$CLUSTER" in
135
+ kind) CLUSTER_FLAG="--kind";;
136
+ kubeadm) CLUSTER_FLAG="--kubeadm";;
137
+ k3s) CLUSTER_FLAG="--k3s";;
138
+ ""|none) CLUSTER_FLAG="";;
139
+ *) echo "Unknown --cluster: $CLUSTER (expected kind|kubeadm|k3s|none)" >&2; exit 1;;
140
+ esac
141
+ DEV_FLAG=""; [ "$ENV" = development ] && DEV_FLAG="--dev"
142
+ INSTANCES_CONF="./engine-private/conf/${DEPLOY_ID}/conf.instances.json"
143
+ SERVER_CONF="./engine-private/conf/${DEPLOY_ID}/conf.server.json"
144
+
145
+ echo "[test-monitor] mode=$MODE env=$ENV deploy=$DEPLOY_ID cluster=${CLUSTER:-none} tls=$USE_TLS test-repo=$USE_TEST_REPO expose=$DO_EXPOSE"
146
+
147
+ # ─────────────────────────── shared helpers ───────────────────────────
148
+
149
+ # setup_tls_secrets <host> [<host> …] — regenerate self-signed certs and create
150
+ # the per-host k8s TLS secret the HTTPProxy references (name == host).
151
+ setup_tls_secrets() {
152
+ local ssl_base="${ENGINE_DIR}/engine-private/ssl"
153
+ for host in "$@"; do
154
+ local cert_dir="${ssl_base}/${host}"
155
+ mkdir -p "$cert_dir"
156
+ bash "${SCRIPT_DIR}/ssl.sh" "$cert_dir" "$host"
157
+ local name_safe="${host//[^a-zA-Z0-9_.-]/_}"
158
+ kubectl delete secret "$host" -n "$NAMESPACE" --ignore-not-found
159
+ kubectl create secret tls "$host" \
160
+ --cert="${cert_dir}/${name_safe}.pem" \
161
+ --key="${cert_dir}/${name_safe}-key.pem" \
162
+ -n "$NAMESPACE"
163
+ done
164
+ }
165
+
166
+ # hosts_from <conf.json> — unique hosts declared in a conf.server.json or conf.instances.json.
167
+ hosts_from() {
168
+ node -e "
169
+ const c = require('$1');
170
+ const hosts = Array.isArray(c) ? c.map(i => i.host) : Object.keys(c);
171
+ console.log([...new Set(hosts)].join(' '));
172
+ "
173
+ }
174
+
175
+ # ─────────────────────────── pre-steps ───────────────────────────
176
+ [ "$DO_CLUSTER_MANIFESTS" = true ] && node bin run build-cluster-deployment-manifests
177
+ [ "$DO_BUILD_TEMPLATE" = true ] && node bin/build.template --update-private
178
+
179
+ # ─────────────────────────── runtime mode ───────────────────────────
180
+ run_runtime_mode() {
181
+ local link_cmd="cd /home/dd,underpost clone ${TEMPLATE_REPO},cd /home/dd/${TEMPLATE_REPO##*/},npm install,npm link"
182
+ local deploy_cmd proxy_flag="" expose_flags="" start_flags=""
183
+
184
+ # Publish the local engine src to underpostnet/engine-test-<id> so the pod
185
+ # (started with --private-test-repo) clones this work-in-progress source.
186
+ [ "$USE_TEST_REPO" = true ] && node bin/build "$DEPLOY_ID" --update-private
187
+
188
+ [ "$USE_PULL_BUNDLE" = true ] && start_flags="${start_flags} --pull-bundle"
189
+ [ "$USE_TEST_REPO" = true ] && start_flags="${start_flags} --private-test-repo"
190
+ deploy_cmd="${link_cmd},underpost start --build --run${start_flags} ${DEPLOY_ID} ${ENV}"
191
+
192
+ [ "$USE_CERT" = true ] && proxy_flag="--cert"
193
+ [ "$USE_TLS" = true ] && proxy_flag="--self-signed"
194
+
195
+ node bin deploy "$DEPLOY_ID" "$ENV" $CLUSTER_FLAG --sync --build-manifest \
196
+ --image "$IMAGE" --timeout-response "$TIMEOUT_RESPONSE" --versions "$VERSIONS" --replicas "$REPLICAS" --cmd "$deploy_cmd"
197
+ node bin deploy "$DEPLOY_ID" "$ENV" $CLUSTER_FLAG --disable-update-proxy $proxy_flag
198
+ node bin monitor "$DEPLOY_ID" "$ENV" --ready-deployment --promote \
199
+ --timeout-response "$TIMEOUT_RESPONSE" --versions "$VERSIONS" --replicas "$REPLICAS"
200
+
201
+ node bin run etc-hosts --deploy-id "$DEPLOY_ID"
202
+
203
+ if [ "$USE_TLS" = true ]; then
204
+ setup_tls_secrets $(hosts_from "$SERVER_CONF")
205
+ expose_flags="--tls"
206
+ fi
207
+ [ "$DO_EXPOSE" = true ] && node bin deploy --expose --local-proxy "$DEPLOY_ID" "$ENV" $expose_flags
208
+ }
209
+
210
+ # ─────────────────────────── instance mode ───────────────────────────
211
+ run_instance_mode() {
212
+ [ -f "$INSTANCES_CONF" ] || { echo "Missing $INSTANCES_CONF" >&2; exit 1; }
213
+ if [ -z "$INSTANCE_IDS" ]; then
214
+ INSTANCE_IDS=$(node -e "console.log(require('$INSTANCES_CONF').map(i=>i.id).join(','))")
215
+ fi
216
+ local tls_flag=""; [ "$USE_TLS" = true ] && tls_flag="--tls --test"
217
+
218
+ # `run instance` builds/pulls the image, applies the manifest, monitors with the
219
+ # kubernetes gate (TCP readinessProbe) + exec status, then promotes traffic.
220
+ IFS=',' read -ra ids <<< "$INSTANCE_IDS"
221
+ for id in "${ids[@]}"; do
222
+ [ -n "$id" ] || continue
223
+ echo "[test-monitor] deploying instance ${DEPLOY_ID},${id}"
224
+ node bin run instance "${DEPLOY_ID},${id},${REPLICAS}" \
225
+ $DEV_FLAG $CLUSTER_FLAG --namespace "$NAMESPACE" --etc-hosts $tls_flag \
226
+ ${IMAGE:+--image-name "$IMAGE"}
227
+ done
228
+
229
+ [ "$DO_EXPOSE" = true ] || return 0
230
+ if [ "$USE_TLS" = true ]; then
231
+ # Instances are routed by the Contour ingress; port-forward Envoy so the
232
+ # HTTPProxy host routing (server/client.cyberiaonline.com) works locally.
233
+ echo "[test-monitor] exposing HTTPS via ${ENVOY_NAMESPACE}/${ENVOY_SERVICE} on :${HTTPS_PORT}"
234
+ sudo kubectl port-forward -n "$ENVOY_NAMESPACE" "svc/${ENVOY_SERVICE}" "${HTTPS_PORT}:443" &
235
+ else
236
+ # Plain HTTP: port-forward each instance service port directly.
237
+ for id in "${ids[@]}"; do
238
+ [ -n "$id" ] || continue
239
+ node bin deploy --expose "${DEPLOY_ID}-${id}" "$ENV" --namespace "$NAMESPACE" || true
240
+ done
241
+ fi
242
+ }
243
+
244
+ case "$MODE" in
245
+ runtime) run_runtime_mode;;
246
+ instance) run_instance_mode;;
247
+ *) echo "Unknown --mode: $MODE (expected runtime|instance)" >&2; exit 1;;
248
+ esac
249
+
250
+ echo "[test-monitor] done (mode=$MODE tls=$USE_TLS)"