@underpostnet/cyberia 3.2.90 → 3.3.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.env.example +16 -0
- package/.github/workflows/coverall.cyberia.ci.yml +107 -0
- package/.github/workflows/cyberia-client.cd.yml +6 -18
- package/.github/workflows/cyberia-server.cd.yml +6 -18
- package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
- package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
- package/.github/workflows/engine-cyberia.cd.yml +6 -78
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +42 -21
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +2 -9
- package/.prettierignore +1 -0
- package/AGENTS.md +47 -0
- package/CHANGELOG.md +581 -2417
- package/CLI-HELP.md +416 -127
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +4 -4
- package/README.md +21 -1
- package/bin/build.js +70 -43
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +372 -135
- package/bin/deploy.js +18 -104
- package/bin/index.js +372 -135
- package/compose.env +16 -0
- package/conf.js +93 -4
- package/deploy/cyberia-client/deploy.sh +47 -0
- package/deploy/cyberia-client/package.sh +18 -0
- package/deploy/cyberia-client/state.sh +14 -0
- package/deploy/cyberia-server/deploy.sh +47 -0
- package/deploy/cyberia-server/package.sh +18 -0
- package/deploy/cyberia-server/state.sh +14 -0
- package/deploy/dd-cyberia/deploy.sh +22 -0
- package/deploy/dd-cyberia/init.sh +55 -0
- package/deploy/dd-cyberia/package.sh +17 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +116 -0
- package/deploy/lib/github-actions-logging.sh +594 -0
- package/deploy/lib/host.sh +185 -0
- package/deploy/lib/state.sh +62 -0
- package/deploy/pwa-microservices-template/deploy.sh +45 -0
- package/deploy/release/deploy.sh +32 -0
- package/deployment.yaml +7 -2
- package/docker-compose.yml +31 -37
- package/examples/static-page/README.md +19 -19
- package/examples/static-page/ssr-components/CustomPage.js +2 -2
- package/hardhat/hardhat.config.js +1 -6
- package/hardhat/package-lock.json +104 -43
- package/hardhat/package.json +3 -3
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/grafana/deployment.yaml +26 -7
- package/manifests/grafana/kustomization.yaml +1 -0
- package/manifests/grafana/service.yaml +6 -1
- package/manifests/mariadb/pv.yaml +1 -0
- package/manifests/mongodb/statefulset.yaml +9 -2
- package/manifests/mysql/pv-pvc.yaml +14 -1
- package/manifests/postgresql/pv.yaml +12 -1
- package/manifests/postgresql/pvc.yaml +1 -0
- package/manifests/prometheus/deployment.yaml +18 -22
- package/package.json +83 -10
- package/scripts/audit-selinux.sh +64 -0
- package/scripts/event-monitor.sh +56 -0
- package/scripts/gpu-diag.sh +0 -0
- package/scripts/ip-info.sh +0 -0
- package/scripts/k3s-node-setup.sh +30 -14
- package/scripts/kubeadm-node-setup.sh +40 -31
- package/scripts/lxd-vm-setup.sh +0 -0
- package/scripts/maas-nat-firewalld.sh +0 -0
- package/scripts/maas-setup.sh +8 -8
- package/scripts/nat-iptables.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +0 -0
- package/scripts/rocky-kickstart.sh +25 -9
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
- package/src/api/core/core.controller.js +1 -1
- package/src/api/core/core.router.js +2 -2
- package/src/api/core/core.service.js +2 -2
- package/src/api/crypto/crypto.controller.js +1 -1
- package/src/api/crypto/crypto.service.js +1 -1
- package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
- package/src/api/cyberia-action/cyberia-action.router.js +1 -1
- package/src/api/cyberia-action/cyberia-action.service.js +2 -2
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
- package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
- package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
- package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
- package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
- package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
- package/src/api/cyberia-map/cyberia-map.router.js +1 -1
- package/src/api/cyberia-map/cyberia-map.service.js +2 -2
- package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
- package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
- package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
- package/src/api/default/default.controller.js +1 -1
- package/src/api/default/default.router.js +1 -1
- package/src/api/default/default.service.js +2 -2
- package/src/api/document/document.controller.js +1 -1
- package/src/api/document/document.router.js +1 -1
- package/src/api/document/document.service.js +2 -2
- package/src/api/file/file.controller.js +1 -1
- package/src/api/file/file.router.js +1 -1
- package/src/api/file/file.service.js +2 -2
- package/src/api/instance/instance.controller.js +1 -1
- package/src/api/instance/instance.router.js +1 -1
- package/src/api/instance/instance.service.js +2 -2
- package/src/api/ipfs/ipfs.controller.js +1 -1
- package/src/api/ipfs/ipfs.router.js +2 -2
- package/src/api/ipfs/ipfs.service.js +2 -2
- package/src/api/object-layer/object-layer.controller.js +1 -1
- package/src/api/object-layer/object-layer.model.js +204 -3
- package/src/api/object-layer/object-layer.router.js +2 -2
- package/src/api/object-layer/object-layer.service.js +4 -17
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
- package/src/api/test/test.controller.js +1 -1
- package/src/api/test/test.service.js +1 -1
- package/src/api/user/guest.service.js +3 -3
- package/src/api/user/user.controller.js +1 -1
- package/src/api/user/user.router.js +2 -2
- package/src/api/user/user.service.js +4 -4
- package/src/api.js +5 -9
- package/src/cli/app.js +391 -0
- package/src/cli/baremetal.js +30 -36
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +242 -135
- package/src/cli/db.js +126 -67
- package/src/cli/deploy.js +158 -60
- package/src/cli/docker-compose.js +5 -5
- package/src/cli/domains.js +184 -0
- package/src/cli/dotenv-store.js +143 -0
- package/src/cli/event.js +2281 -0
- package/src/cli/fs.js +3 -3
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -7
- package/src/cli/index.js +426 -134
- package/src/cli/ipfs.js +34 -30
- package/src/cli/kickstart.js +1 -1
- package/src/cli/kubectl.js +218 -41
- package/src/cli/lxd.js +3 -3
- package/src/cli/monitor.js +1359 -26
- package/src/cli/package.js +90 -0
- package/src/cli/release.js +73 -34
- package/src/cli/repository.js +564 -367
- package/src/cli/run.js +362 -654
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +651 -331
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +26 -13
- package/src/cli/test.js +199 -125
- package/src/cli/vultr.js +623 -0
- package/src/cli/wireguard.js +2930 -0
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +258 -211
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/PanelForm.js +4 -4
- package/src/client/components/core/Repository.js +56 -0
- package/src/client/components/core/Translate.js +7 -0
- package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
- package/src/client/components/core/Wallet.js +0 -11
- package/src/client/components/core/Worker.js +7 -1
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppShellDefault.js +1 -1
- package/src/client/components/default/SettingsDefault.js +2 -2
- package/src/client/components/itemledger/SettingsItemledger.js +2 -2
- package/src/client/components/underpost/AppShellUnderpost.js +3 -2
- package/src/client/components/underpost/SettingsUnderpost.js +2 -2
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-384x384.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-512x512.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/default/assets/background/white.jpg +0 -0
- package/src/client/public/default/browserconfig.xml +1 -1
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon-48x48.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/manifest.webmanifest +5 -5
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +1 -1
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
- package/src/client/services/object-layer/object-layer.management.js +4 -4
- package/src/client/ssr/RootDocument.js +1 -1
- package/src/client/ssr/body/404.js +1 -1
- package/src/client/ssr/body/500.js +1 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
- package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
- package/src/client/ssr/head/CryptokoynScripts.js +1 -1
- package/src/client/ssr/head/Css.js +1 -1
- package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +1 -1
- package/src/client/ssr/head/ItemledgerScripts.js +1 -1
- package/src/client/ssr/head/Microdata.js +1 -1
- package/src/client/ssr/head/Production.js +1 -1
- package/src/client/ssr/head/Pwa.js +1 -1
- package/src/client/ssr/head/PwaItemledger.js +1 -1
- package/src/client/ssr/head/Seo.js +1 -1
- package/src/client/ssr/head/UnderpostScripts.js +1 -1
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
- package/src/client/ssr/views/Cyberia404.js +7 -5
- package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/views/Maintenance.js +1 -1
- package/src/client/ssr/views/NoNetworkConnection.js +1 -1
- package/src/client/ssr/views/Test.js +1 -1
- package/src/client/sw/core.sw.js +20 -14
- package/src/client-builder/client-build-docs.js +61 -46
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +58 -44
- package/src/client-builder/client-dev-server.js +3 -3
- package/src/client-builder/client-icons.js +2 -2
- package/src/client-builder/ssr.js +3 -3
- package/src/client.build.js +3 -6
- package/src/client.dev.js +3 -6
- package/src/db/DataBaseProvider.js +186 -3
- package/src/db/mariadb/MariaDB.js +3 -2
- package/src/db/mongo/MongoBootstrap.js +273 -101
- package/src/db/mongo/MongooseDB.js +11 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +1 -1
- package/src/index.js +103 -23
- package/src/mailer/EmailRender.js +2 -2
- package/src/mailer/MailerInterceptor.js +87 -0
- package/src/mailer/MailerProvider.js +50 -6
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
- package/src/projects/cyberia/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +54 -6
- package/src/projects/cyberia/gemini-client.js +1 -1
- package/src/projects/cyberia/generate-saga.js +15 -24
- package/src/projects/cyberia/hot-reload-trigger.js +1 -1
- package/src/projects/cyberia/instance-data.js +22 -3
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +1 -1
- package/src/projects/cyberia/object-layer.js +12 -22
- package/src/projects/cyberia/semantic-layer-generator.js +1 -1
- package/src/projects/underpost/catalog-underpost.js +13 -2
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +9 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -4
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
- package/src/runtime/engine-cyberia/compose.env +16 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
- package/src/runtime/express/Express.js +32 -27
- package/src/runtime/lampp/Lampp.js +22 -70
- package/src/runtime/nginx/Nginx.js +1 -1
- package/src/runtime/wp/Wp.js +16 -48
- package/src/server/{catalog.js → build/catalog.js} +15 -5
- package/src/server/build/coverage.js +117 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +465 -0
- package/src/server/build/testing.js +528 -0
- package/src/server/{dns.js → network/dns.js} +241 -14
- package/src/server/network/forward-proxy.js +541 -0
- package/src/server/{middlewares.js → network/middlewares.js} +59 -4
- package/src/server/network/node-capability.js +98 -0
- package/src/server/{peer.js → network/peer.js} +3 -3
- package/src/server/{proxy.js → network/proxy.js} +7 -13
- package/src/server/network/router.js +356 -0
- package/src/server/{tls.js → network/tls.js} +2 -2
- package/src/server/network/underpost-compression.js +186 -0
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/ops/cron.js +949 -0
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +108 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/ops/systemd.js +259 -0
- package/src/server/{conf.js → runtime/conf.js} +521 -520
- package/src/server/runtime/config-scope.js +215 -0
- package/src/server/runtime/environment.js +149 -0
- package/src/server/{process.js → runtime/process.js} +153 -13
- package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
- package/src/server/{runtime.js → runtime/runtime.js} +12 -8
- package/src/server/{start.js → runtime/start.js} +128 -39
- package/src/server/{auth.js → security/auth.js} +7 -8
- package/src/server/security/container-storage.js +382 -0
- package/src/server/{crypto.js → security/crypto.js} +1 -1
- package/src/server/security/selinux.js +185 -0
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +1 -1
- package/src/server/{downloader.js → storage/downloader.js} +2 -2
- package/src/server/storage/repository.js +68 -0
- package/src/server.js +4 -7
- package/src/ws/IoInterface.js +1 -1
- package/src/ws/IoServer.js +1 -1
- package/src/ws/core/core.ws.connection.js +1 -1
- package/src/ws/core/core.ws.emit.js +1 -1
- package/src/ws/core/core.ws.server.js +1 -1
- package/src/ws/default/default.ws.connection.js +1 -1
- package/src/ws/default/default.ws.emit.js +1 -1
- package/src/ws/default/default.ws.server.js +1 -1
- package/test/e2e/event-e2e-public-ingress-down.js +45 -0
- package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
- package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
- package/test/{api.test.js → integration/app/api.test.js} +33 -14
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
- package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
- package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
- package/test/integration/infra/1-security/config-scope.test.js +305 -0
- package/test/integration/infra/1-security/container-storage.test.js +356 -0
- package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
- package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
- package/test/integration/infra/1-security/selinux.test.js +69 -0
- package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
- package/test/integration/infra/1-security/systemd-service.test.js +70 -0
- package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
- package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
- package/test/integration/infra/2-network/node-capability.test.js +182 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
- package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
- package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
- package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
- package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
- package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
- package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
- package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
- package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
- package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
- package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
- package/test/integration/infra/5-observability/event-notification.test.js +197 -0
- package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
- package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
- package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
- package/test/support/shell-harness.js +73 -0
- package/test/unit/build-template.test.js +97 -0
- package/test/unit/build-workflow-offline.test.js +132 -0
- package/test/unit/catalog.test.js +71 -0
- package/test/unit/client-build-docs.test.js +334 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +979 -0
- package/test/unit/coverage-artifact.test.js +130 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/deploy-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +447 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/logger-redaction.test.js +112 -0
- package/test/unit/package.test.js +403 -0
- package/test/unit/prepare-host.test.js +299 -0
- package/test/unit/process-environment.test.js +18 -0
- package/test/unit/propagation-message.test.js +78 -0
- package/test/unit/redeploy-plan.test.js +24 -0
- package/test/unit/release-bump.test.js +474 -0
- package/test/unit/ssh-output-redaction.test.js +28 -0
- package/test/unit/start-options.test.js +46 -0
- package/test/unit/test-tiers.test.js +193 -0
- package/vitest.config.js +58 -0
- package/.nycrc +0 -9
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/user/user.build.js +0 -16
- package/src/cli/env.js +0 -177
- package/src/client/components/core/Blockchain.js +0 -41
- package/src/client/components/core/Blog.js +0 -9
- package/src/client/components/core/KeyboardAvoidance.js +0 -145
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/white0-min.jpg +0 -0
- package/src/client/public/default/assets/background/white0.jpg +0 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +0 -148
- package/src/server/cron.js +0 -476
package/src/cli/deploy.js
CHANGED
|
@@ -5,14 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
buildKindPorts,
|
|
9
|
-
buildPortProxyRouter,
|
|
10
|
-
buildProxyRouter,
|
|
11
8
|
clusterTypeFactory,
|
|
12
9
|
Config,
|
|
13
|
-
cronDeployIdResolve,
|
|
14
10
|
deployHostsFactory,
|
|
15
|
-
deployRangePortFactory,
|
|
16
11
|
gatewayApiEnabledFactory,
|
|
17
12
|
getDataDeploy,
|
|
18
13
|
instanceStatusPageEntriesFactory,
|
|
@@ -20,13 +15,22 @@ import {
|
|
|
20
15
|
loadConfServerJson,
|
|
21
16
|
loadReplicas,
|
|
22
17
|
nextTrafficFactory,
|
|
23
|
-
pathPortAssignmentFactory,
|
|
24
18
|
schedulableNodeFactory,
|
|
25
19
|
trafficFromRoutingInfoFactory,
|
|
26
|
-
} from '../server/conf.js';
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
} from '../server/runtime/conf.js';
|
|
21
|
+
import {
|
|
22
|
+
buildKindPorts,
|
|
23
|
+
buildPortProxyRouter,
|
|
24
|
+
buildProxyRouter,
|
|
25
|
+
deployRangePortFactory,
|
|
26
|
+
pathPortAssignmentFactory,
|
|
27
|
+
readDeployRoutes,
|
|
28
|
+
} from '../server/network/router.js';
|
|
29
|
+
import { loggerFactory } from '../server/ops/logger.js';
|
|
30
|
+
import { HOST_VOLUME_ROOT } from '../server/runtime/environment.js';
|
|
31
|
+
import { shellExec } from '../server/runtime/process.js';
|
|
32
|
+
import { ensureContainerStorage, ensureContainerStorageSubtree } from '../server/security/container-storage.js';
|
|
33
|
+
import { INTERNAL_READY_PATH, INTERNAL_HEALTH_PATH } from '../server/runtime/runtime-status.js';
|
|
30
34
|
import { staticContextRoutesFactory, statusPageRoutesFactory } from '../client-builder/client-build.js';
|
|
31
35
|
import {
|
|
32
36
|
UNDERPOST_GATEWAY,
|
|
@@ -39,13 +43,15 @@ import {
|
|
|
39
43
|
syncStaticAssetFromPod,
|
|
40
44
|
writeHostServerConf,
|
|
41
45
|
writeStaticAsset,
|
|
42
|
-
} from '../server/underpost-gateway.js';
|
|
46
|
+
} from '../server/network/underpost-gateway.js';
|
|
43
47
|
import { getCapVariableName } from '../client/components/core/CommonJs.js';
|
|
44
48
|
import fs from 'fs-extra';
|
|
45
49
|
import nodePath from 'node:path';
|
|
46
50
|
import dotenv from 'dotenv';
|
|
47
51
|
import os from 'node:os';
|
|
48
52
|
import crypto from 'node:crypto';
|
|
53
|
+
import { appSecretName } from './app.js';
|
|
54
|
+
import { domainContextFactory } from './domains.js';
|
|
49
55
|
import Underpost from '../index.js';
|
|
50
56
|
|
|
51
57
|
/**
|
|
@@ -77,7 +83,7 @@ const GATEWAY_CONTROLLER_NAME = `${GATEWAY_EXTENSION_GROUP}/gatewayclass-control
|
|
|
77
83
|
// Gateway references: one name, resolved through gatewayApiConfigFactory, so an
|
|
78
84
|
// override reaches the installer and the manifests together.
|
|
79
85
|
const GATEWAY_CLASS_DEFAULT = 'eg';
|
|
80
|
-
// Where `bin client <deployId> <env>` writes each host's bundle, including the
|
|
86
|
+
// Where `bin client <deployId> --env <env>` writes each host's bundle, including the
|
|
81
87
|
// SSR status views declared in conf.ssr.json (`<host><path>/<status>/index.html`).
|
|
82
88
|
// Engine root inside the workload container; the built PWA artifacts live under
|
|
83
89
|
// its `public/` tree, which is where the static edge documents are sourced from.
|
|
@@ -87,6 +93,17 @@ const UPSTREAM_FAILURE_STATUSES = [502, 503, 504];
|
|
|
87
93
|
|
|
88
94
|
const CONTAINER_ENGINE_ROOT = '/home/dd/engine';
|
|
89
95
|
|
|
96
|
+
// The manifest set `deploy --build-manifest` produces for one environment.
|
|
97
|
+
const DEFAULT_MANIFEST_FILES = [
|
|
98
|
+
'deployment.yaml',
|
|
99
|
+
'proxy.yaml',
|
|
100
|
+
'gateway.yaml',
|
|
101
|
+
'httproute.yaml',
|
|
102
|
+
'pv-pvc.yaml',
|
|
103
|
+
'traffic-service.yaml',
|
|
104
|
+
'grpc-service.yaml',
|
|
105
|
+
];
|
|
106
|
+
|
|
90
107
|
/**
|
|
91
108
|
* Maps a host/path's edge-served views onto the statuses the gateway intercepts
|
|
92
109
|
* for it, and the context directory each status is answered from.
|
|
@@ -152,6 +169,13 @@ const gatewayDurationFactory = (value) => {
|
|
|
152
169
|
|
|
153
170
|
const logger = loggerFactory(import.meta);
|
|
154
171
|
|
|
172
|
+
// hostPath volume trees are written under the operator's home directory, which carries a label no
|
|
173
|
+
// unprivileged container can read. This registers the persistent mapping and restores the tree,
|
|
174
|
+
// rather than only restoring it: a deploy must not depend on cluster bring-up having run on this
|
|
175
|
+
// host, and a plain `restorecon` against an unmapped path re-applies the very default that makes
|
|
176
|
+
// the volume unreadable. A no-op where SELinux or its userspace is absent.
|
|
177
|
+
const prepareContainerStorage = (path) => ensureContainerStorageSubtree(path, { execute: shellExec });
|
|
178
|
+
|
|
155
179
|
/**
|
|
156
180
|
* @class UnderpostDeploy
|
|
157
181
|
* @description Manages the deployment of applications and services.
|
|
@@ -440,20 +464,15 @@ EOF
|
|
|
440
464
|
}) {
|
|
441
465
|
if (!readinessProbe) throw new Error(`Refusing to build ${deployId}-${env}-${suffix} without a readiness probe`);
|
|
442
466
|
if (!cmd)
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
// `npm install -g npm@11.2.0`,
|
|
453
|
-
// `npm install -g underpost`,
|
|
454
|
-
`underpost secret underpost --create-from-env`,
|
|
455
|
-
`underpost start --build --run ${deployId} ${env}`,
|
|
456
|
-
];
|
|
467
|
+
// One command, and deliberately only `start`. This string is executed by whatever
|
|
468
|
+
// underpost the image happens to ship, which is as new as the last npm publish and no
|
|
469
|
+
// newer — so it may name only CLI surface that has always existed. Everything the
|
|
470
|
+
// deployment needs beyond that, host configuration included, is done inside the start
|
|
471
|
+
// lifecycle, where the code is the freshly pulled source rather than the image snapshot.
|
|
472
|
+
// `--pull-bundle` fetches the pre-built client bundle instead of building it in-pod.
|
|
473
|
+
cmd = [
|
|
474
|
+
`underpost start --build --run${pullBundle || skipFullBuild ? ' --pull-bundle --skip-full-build' : ''} ${deployId} ${env}`,
|
|
475
|
+
];
|
|
457
476
|
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
458
477
|
if (!volumes) volumes = [];
|
|
459
478
|
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
@@ -494,6 +513,12 @@ ${
|
|
|
494
513
|
envFrom:
|
|
495
514
|
- secretRef:
|
|
496
515
|
name: underpost-config
|
|
516
|
+
# The deployment's own environment, with its .env.<env>.oci overlay already
|
|
517
|
+
# applied by 'app apply'. Optional so a deployment predating that Secret still
|
|
518
|
+
# starts; the in-pod 'app load' resolves the same overlay as a second path.
|
|
519
|
+
- secretRef:
|
|
520
|
+
name: ${appSecretName(deployId, env)}
|
|
521
|
+
optional: true
|
|
497
522
|
${
|
|
498
523
|
internalStatusPort
|
|
499
524
|
? ` env:
|
|
@@ -724,7 +749,7 @@ ${Underpost.deploy
|
|
|
724
749
|
if (!volume.claimName) continue;
|
|
725
750
|
const pvcId = `${volume.claimName}-${deployId}-${env}-${deploymentVersion}`;
|
|
726
751
|
const pvId = pvcId.replace(/^pvc-/, 'pv-');
|
|
727
|
-
const hostPath =
|
|
752
|
+
const hostPath = `${HOST_VOLUME_ROOT}/${pvId}`;
|
|
728
753
|
volumeYaml += `---\n${Underpost.deploy.persistentVolumeFactory({
|
|
729
754
|
pvcId,
|
|
730
755
|
namespace: options.namespace,
|
|
@@ -1087,6 +1112,41 @@ spec:
|
|
|
1087
1112
|
kind: ClusterIssuer
|
|
1088
1113
|
secretName: ${host}`;
|
|
1089
1114
|
},
|
|
1115
|
+
/**
|
|
1116
|
+
* Mirrors each deploy's built development manifests into the project tree, and refreshes the
|
|
1117
|
+
* public default configuration they belong to.
|
|
1118
|
+
*
|
|
1119
|
+
* The project repository carries the development manifests of every routed deploy; the
|
|
1120
|
+
* private build directory is where they are produced. A manifest the build stopped producing
|
|
1121
|
+
* is removed here rather than left behind, so the two cannot disagree about what a deploy
|
|
1122
|
+
* ships.
|
|
1123
|
+
* @param {string[]} [deployIds] - Deploy ids to mirror; the route table plus `dd-cron` otherwise.
|
|
1124
|
+
* @returns {Array<{deployId: string, copied: string[], removed: string[]}>} What was mirrored.
|
|
1125
|
+
* @memberof UnderpostDeploy
|
|
1126
|
+
*/
|
|
1127
|
+
mirrorDefaultManifests(deployIds = readDeployRoutes().concat(['dd-cron'])) {
|
|
1128
|
+
const mirrored = [];
|
|
1129
|
+
for (const deployId of deployIds) {
|
|
1130
|
+
const copied = [];
|
|
1131
|
+
const removed = [];
|
|
1132
|
+
for (const file of DEFAULT_MANIFEST_FILES) {
|
|
1133
|
+
const source = `./engine-private/conf/${deployId}/build/development/${file}`;
|
|
1134
|
+
const target = `./manifests/deployment/${deployId}-development/${file}`;
|
|
1135
|
+
if (fs.existsSync(source)) {
|
|
1136
|
+
fs.copySync(source, target);
|
|
1137
|
+
copied.push(file);
|
|
1138
|
+
} else if (fs.existsSync(target)) {
|
|
1139
|
+
fs.removeSync(target);
|
|
1140
|
+
removed.push(file);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
shellExec(`node bin new --dev --default-conf --deploy-id ${deployId}`);
|
|
1144
|
+
mirrored.push({ deployId, copied, removed });
|
|
1145
|
+
}
|
|
1146
|
+
logger.info('Default deployment manifests mirrored', { deployIds: mirrored.map(({ deployId }) => deployId) });
|
|
1147
|
+
return mirrored;
|
|
1148
|
+
},
|
|
1149
|
+
|
|
1090
1150
|
/**
|
|
1091
1151
|
* Retrieves the current traffic status for a deployment.
|
|
1092
1152
|
* @param {string} deployId - Deployment ID for which the traffic status is being retrieved.
|
|
@@ -1697,13 +1757,13 @@ ${rules}`;
|
|
|
1697
1757
|
|
|
1698
1758
|
/**
|
|
1699
1759
|
* Node directory backing the static utility's volume, following the same
|
|
1700
|
-
*
|
|
1760
|
+
* `HOST_VOLUME_ROOT/<pv>` convention as every other hostPath volume.
|
|
1701
1761
|
* @param {object} [options] - Deploy/run options.
|
|
1702
1762
|
* @returns {string} Absolute host path.
|
|
1703
1763
|
* @memberof UnderpostDeploy
|
|
1704
1764
|
*/
|
|
1705
1765
|
underpostGatewayRootFactory(options = {}) {
|
|
1706
|
-
return options.underpostGatewayRoot ||
|
|
1766
|
+
return options.underpostGatewayRoot || `${HOST_VOLUME_ROOT}/${UNDERPOST_GATEWAY.volumeName}`;
|
|
1707
1767
|
},
|
|
1708
1768
|
|
|
1709
1769
|
/**
|
|
@@ -1726,6 +1786,45 @@ ${rules}`;
|
|
|
1726
1786
|
return `${ready}`.includes('true');
|
|
1727
1787
|
},
|
|
1728
1788
|
|
|
1789
|
+
/**
|
|
1790
|
+
* Reports whether a Deployment object exists at all.
|
|
1791
|
+
*
|
|
1792
|
+
* The inactive colour of a blue/green pair is routinely absent, so callers
|
|
1793
|
+
* that act on it must distinguish "not deployed yet" from a failed lookup.
|
|
1794
|
+
* @param {string} deployment - Deployment name.
|
|
1795
|
+
* @param {string} [namespace] - Kubernetes namespace.
|
|
1796
|
+
* @returns {boolean} True when the object is present.
|
|
1797
|
+
*/
|
|
1798
|
+
deploymentExists({ deployment, namespace = 'default' }) {
|
|
1799
|
+
return !!`${
|
|
1800
|
+
shellExec(`kubectl get deployment ${deployment} -n ${namespace} --ignore-not-found -o name`, {
|
|
1801
|
+
stdout: true,
|
|
1802
|
+
silent: true,
|
|
1803
|
+
silentOnError: true,
|
|
1804
|
+
}) || ''
|
|
1805
|
+
}`.trim();
|
|
1806
|
+
},
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* Node a live Deployment is pinned to, read from its `nodeSelector`.
|
|
1810
|
+
*
|
|
1811
|
+
* A colour created next to a live one must inherit that placement: its
|
|
1812
|
+
* hostPath volumes exist on that node only.
|
|
1813
|
+
* @param {string} deployment - Deployment name.
|
|
1814
|
+
* @param {string} [namespace] - Kubernetes namespace.
|
|
1815
|
+
* @returns {string} Node hostname, or empty when the workload is unpinned or absent.
|
|
1816
|
+
*/
|
|
1817
|
+
deploymentNode({ deployment, namespace = 'default' }) {
|
|
1818
|
+
if (!deployment) return '';
|
|
1819
|
+
return `${
|
|
1820
|
+
shellExec(
|
|
1821
|
+
`kubectl get deployment ${deployment} -n ${namespace} ` +
|
|
1822
|
+
`-o jsonpath='{.spec.template.spec.nodeSelector.kubernetes\.io/hostname}'`,
|
|
1823
|
+
{ stdout: true, silent: true, silentOnError: true },
|
|
1824
|
+
) || ''
|
|
1825
|
+
}`.trim();
|
|
1826
|
+
},
|
|
1827
|
+
|
|
1729
1828
|
/**
|
|
1730
1829
|
* Reports whether the Deployment controller has observed the current
|
|
1731
1830
|
* generation and every desired replica is updated, Ready, and Available.
|
|
@@ -1881,6 +1980,11 @@ ${rules}`;
|
|
|
1881
1980
|
? 'project'
|
|
1882
1981
|
: null,
|
|
1883
1982
|
});
|
|
1983
|
+
// The documents were just written under the operator's home tree, whose policy label
|
|
1984
|
+
// (user_home_t) the unprivileged gateway container cannot read — it would answer 403 for
|
|
1985
|
+
// every one of them. Register the mapping for the volume root and restore this subtree, so
|
|
1986
|
+
// the files this pass created carry the shared container label.
|
|
1987
|
+
prepareContainerStorage(hostRoot);
|
|
1884
1988
|
logger.info('Static edge documents placed', {
|
|
1885
1989
|
deployId,
|
|
1886
1990
|
podName: podName || '(no running workload; placed from this checkout)',
|
|
@@ -2142,8 +2246,7 @@ ${Underpost.deploy.buildCertManagerCertificate({ host, namespace })}
|
|
|
2142
2246
|
EOF`);
|
|
2143
2247
|
}
|
|
2144
2248
|
return;
|
|
2145
|
-
} else if (!deployList || deployList === 'dd')
|
|
2146
|
-
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
2249
|
+
} else if (!deployList || deployList === 'dd') deployList = readDeployRoutes().join(',');
|
|
2147
2250
|
const deployIds = deployList
|
|
2148
2251
|
.split(',')
|
|
2149
2252
|
.map((id) => id.trim())
|
|
@@ -2187,11 +2290,19 @@ EOF`);
|
|
|
2187
2290
|
Underpost.deploy.syncStaticAssets(deployId, env, options);
|
|
2188
2291
|
return;
|
|
2189
2292
|
}
|
|
2293
|
+
// Before the `--build-manifest` return: a manifest build is when the Secrets it mounts
|
|
2294
|
+
// must exist, and `run sync` reaches this command with `--build-manifest --info-router`.
|
|
2295
|
+
if (!options.disableUpdateUnderpostConfig) {
|
|
2296
|
+
Underpost.host.apply(domainContextFactory({ env, namespace }));
|
|
2297
|
+
// Each deployment's own env, OCI overlay applied. Without it the overlay reaches a pod
|
|
2298
|
+
// only through in-pod `app load`, so a stale image keeps the base file's 127.0.0.1.
|
|
2299
|
+
for (const _deployId of deployIds)
|
|
2300
|
+
Underpost.app.apply(domainContextFactory({ env, namespace, args: { 'deploy-id': _deployId } }));
|
|
2301
|
+
}
|
|
2190
2302
|
if (options.infoRouter === true || options.buildManifest === true) {
|
|
2191
2303
|
logger.info('router', await Underpost.deploy.routerFactory(deployList, env));
|
|
2192
2304
|
return;
|
|
2193
2305
|
}
|
|
2194
|
-
if (!options.disableUpdateUnderpostConfig) Underpost.deploy.configMap(env);
|
|
2195
2306
|
|
|
2196
2307
|
for (const _deployId of deployList.split(',')) {
|
|
2197
2308
|
const deployId = _deployId.trim();
|
|
@@ -2377,27 +2488,6 @@ EOF`);
|
|
|
2377
2488
|
}
|
|
2378
2489
|
}
|
|
2379
2490
|
},
|
|
2380
|
-
/**
|
|
2381
|
-
* Creates a Kubernetes Secret for a deployment (replaces configMap for secret data).
|
|
2382
|
-
* Secrets are mounted as tmpfs (never written to node disk) and support RBAC restrictions.
|
|
2383
|
-
* @param {string} env - Environment for which the secret is being created.
|
|
2384
|
-
* @param {string} [namespace='default'] - Kubernetes namespace for the secret.
|
|
2385
|
-
* @memberof UnderpostDeploy
|
|
2386
|
-
*/
|
|
2387
|
-
configMap(env, namespace = 'default') {
|
|
2388
|
-
const cronDeployId = cronDeployIdResolve() || 'dd-cron';
|
|
2389
|
-
const envFilePath = `/home/dd/engine/engine-private/conf/${cronDeployId}/.env.${env}`;
|
|
2390
|
-
// `--from-env-file` turns every KEY=VALUE into a secret key that the Deployment injects via
|
|
2391
|
-
// `envFrom`. Strip shell/runtime-critical keys (notably PATH) first — an injected PATH
|
|
2392
|
-
// overrides the image's own and breaks coreutils/sudo resolution inside the pod.
|
|
2393
|
-
const sanitizedEnvPath = `${envFilePath}.secret`;
|
|
2394
|
-
fs.writeFileSync(sanitizedEnvPath, Underpost.secret.sanitizeSecretEnvFile(fs.readFileSync(envFilePath, 'utf8')));
|
|
2395
|
-
shellExec(`kubectl delete secret underpost-config -n ${namespace} --ignore-not-found`);
|
|
2396
|
-
shellExec(
|
|
2397
|
-
`kubectl create secret generic underpost-config --from-env-file=${sanitizedEnvPath} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
|
|
2398
|
-
);
|
|
2399
|
-
fs.removeSync(sanitizedEnvPath);
|
|
2400
|
-
},
|
|
2401
2491
|
/**
|
|
2402
2492
|
* Switches the traffic for a deployment.
|
|
2403
2493
|
*
|
|
@@ -2537,7 +2627,7 @@ EOF`);
|
|
|
2537
2627
|
const grpcServicePath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
|
|
2538
2628
|
if (fs.existsSync(grpcServicePath)) shellExec(`kubectl apply -f ${grpcServicePath} -n ${namespace}`);
|
|
2539
2629
|
|
|
2540
|
-
Underpost.
|
|
2630
|
+
Underpost.host.store.set(`${deployId}-${env}-traffic`, targetTraffic);
|
|
2541
2631
|
},
|
|
2542
2632
|
|
|
2543
2633
|
/**
|
|
@@ -2547,9 +2637,8 @@ EOF`);
|
|
|
2547
2637
|
* behaves identically everywhere:
|
|
2548
2638
|
*
|
|
2549
2639
|
* 1. **Explicit node** — `node` (the resolved `--node` value). Upstream
|
|
2550
|
-
* runners derive it from
|
|
2551
|
-
*
|
|
2552
|
-
* `--node-name` directly (`run instance`).
|
|
2640
|
+
* runners derive it from `--node-name` (`run sync`: `--node-name` >
|
|
2641
|
+
* cluster-type default; `run instance`: `--node-name` directly).
|
|
2553
2642
|
* 2. **`UNDERPOST_DEPLOY_NODE` env** — for kubeadm / k3s, the configured
|
|
2554
2643
|
* target node name. This makes hostPath PV `nodeAffinity` deterministic
|
|
2555
2644
|
* regardless of where the manifest is *built*: building inside a
|
|
@@ -2632,7 +2721,7 @@ EOF`);
|
|
|
2632
2721
|
const clusterContext = options.clusterContext || 'kind';
|
|
2633
2722
|
const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
|
|
2634
2723
|
const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
|
|
2635
|
-
const rootVolumeHostPath =
|
|
2724
|
+
const rootVolumeHostPath = `${HOST_VOLUME_ROOT}/${pvId}`;
|
|
2636
2725
|
if (options.gitClean && volume.volumeMountPath) {
|
|
2637
2726
|
Underpost.repo.clean({ paths: [volume.volumeMountPath] });
|
|
2638
2727
|
}
|
|
@@ -2653,9 +2742,15 @@ EOF`);
|
|
|
2653
2742
|
const localHost = os.hostname();
|
|
2654
2743
|
dataNode = options.nodeName || localHost;
|
|
2655
2744
|
if (dataNode === localHost) {
|
|
2656
|
-
// Target node is the control plane / current host: write directly.
|
|
2657
|
-
|
|
2745
|
+
// Target node is the control plane / current host: write directly. The directory is
|
|
2746
|
+
// created owned by the invoking user — the root above it may be root-owned, and the copy
|
|
2747
|
+
// that follows is unprivileged — and labeled before as well as after the copy, so the
|
|
2748
|
+
// files land already carrying the shared container label instead of inheriting the
|
|
2749
|
+
// operator home tree's.
|
|
2750
|
+
const { uid, gid } = os.userInfo();
|
|
2751
|
+
ensureContainerStorage(rootVolumeHostPath, { execute: shellExec, owner: `${uid}:${gid}` });
|
|
2658
2752
|
fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
|
|
2753
|
+
prepareContainerStorage(rootVolumeHostPath);
|
|
2659
2754
|
} else {
|
|
2660
2755
|
// Target node is remote: fs.copySync would only write the control-plane
|
|
2661
2756
|
// filesystem, leaving the real node's hostPath empty. Ship the folder to
|
|
@@ -2675,6 +2770,9 @@ EOF`);
|
|
|
2675
2770
|
host: nodeHost,
|
|
2676
2771
|
localDir: volume.volumeMountPath,
|
|
2677
2772
|
remoteDir: rootVolumeHostPath,
|
|
2773
|
+
// The volume is mounted on that node, so the label has to exist there. Labeling the
|
|
2774
|
+
// control plane's copy of the path would leave the pod reading an unlabeled tree.
|
|
2775
|
+
containerStorage: true,
|
|
2678
2776
|
...(options.sshKeyPath ? { keyPath: options.sshKeyPath } : {}),
|
|
2679
2777
|
});
|
|
2680
2778
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import fs from 'fs-extra';
|
|
12
12
|
import nodePath from 'path';
|
|
13
|
-
import { getRootDirectory, shellExec } from '../server/process.js';
|
|
14
|
-
import { loggerFactory } from '../server/logger.js';
|
|
13
|
+
import { getRootDirectory, shellExec } from '../server/runtime/process.js';
|
|
14
|
+
import { loggerFactory } from '../server/ops/logger.js';
|
|
15
15
|
import Nginx from '../runtime/nginx/Nginx.js';
|
|
16
16
|
|
|
17
17
|
const logger = loggerFactory(import.meta);
|
|
@@ -143,7 +143,7 @@ class UnderpostDockerCompose {
|
|
|
143
143
|
// Point the template .env.example at the Docker service-discovery hosts
|
|
144
144
|
// before `new engine` copies it. The generated dd-engine/.env.development
|
|
145
145
|
// is seeded from this file and is applied over the process env by
|
|
146
|
-
// loadConf (src/server/conf.js), so without this the engine would fall
|
|
146
|
+
// loadConf (src/server/runtime/conf.js), so without this the engine would fall
|
|
147
147
|
// back to the .env.example localhost defaults for DB_HOST/VALKEY_HOST.
|
|
148
148
|
'sed -i "s#^DB_HOST=.*#DB_HOST=$${DB_HOST}#" .env.example',
|
|
149
149
|
'sed -i "s#^VALKEY_HOST=.*#VALKEY_HOST=$${VALKEY_HOST}#" .env.example',
|
|
@@ -151,7 +151,7 @@ class UnderpostDockerCompose {
|
|
|
151
151
|
'cd /home/dd',
|
|
152
152
|
'underpost new engine',
|
|
153
153
|
];
|
|
154
|
-
return [
|
|
154
|
+
return [`underpost start --build --run ${deployId} ${env}`];
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
@@ -192,7 +192,7 @@ services:
|
|
|
192
192
|
MONGO_IMAGE=mongo:latest
|
|
193
193
|
VALKEY_IMAGE=valkey/valkey:latest
|
|
194
194
|
APP_IMAGE=underpost/underpost-engine
|
|
195
|
-
APP_TAG=v3.
|
|
195
|
+
APP_TAG=v3.3.73
|
|
196
196
|
PROXY_IMAGE=nginx:stable-alpine
|
|
197
197
|
PROMETHEUS_IMAGE=prom/prometheus:latest
|
|
198
198
|
GRAFANA_IMAGE=grafana/grafana:latest
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared command surface for the four domains.
|
|
3
|
+
*
|
|
4
|
+
* `secret`, `host`, `app` and `state` are registered from one factory, so the action list and the
|
|
5
|
+
* option list exist exactly once. Symmetry is not a convention the four commands agree to follow
|
|
6
|
+
* — it is structural: a verb or a flag added here appears on all four, and one that is not here
|
|
7
|
+
* cannot appear on any of them.
|
|
8
|
+
*
|
|
9
|
+
* The three configuration domains carry a durable source; `state` is the runtime monitoring /
|
|
10
|
+
* telemetry layer and carries none, so its actions read and export a live observation instead.
|
|
11
|
+
* The verbs are the same either way — see {@link UnderpostState} for what each means there.
|
|
12
|
+
* @module src/cli/domains.js
|
|
13
|
+
* @namespace UnderpostDomains
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The canonical action set. Every domain implements all of these with the same meaning:
|
|
18
|
+
*
|
|
19
|
+
* | action | direction |
|
|
20
|
+
* |---------|-----------------------------------|
|
|
21
|
+
* | setup | onboard the domain, idempotently |
|
|
22
|
+
* | load | durable store -> local runtime |
|
|
23
|
+
* | publish | local runtime -> durable store |
|
|
24
|
+
* | apply | durable store -> live cluster |
|
|
25
|
+
* | status | read-only report |
|
|
26
|
+
* | rotate | replace the current projection |
|
|
27
|
+
* | clean | withdraw local traces |
|
|
28
|
+
* @constant {Array<{name: string, summary: string}>}
|
|
29
|
+
* @memberof UnderpostDomains
|
|
30
|
+
*/
|
|
31
|
+
const DOMAIN_ACTIONS = [
|
|
32
|
+
{ name: 'setup', summary: 'Onboards the domain: provisions whatever it needs, then converges it. Idempotent.' },
|
|
33
|
+
{ name: 'load', summary: 'Loads the durable source into the local runtime environment.' },
|
|
34
|
+
{ name: 'publish', summary: 'Writes the local runtime environment into the durable source.' },
|
|
35
|
+
{ name: 'apply', summary: 'Projects the durable source into the live cluster.' },
|
|
36
|
+
{ name: 'status', summary: 'Read-only report of the domain: sources, keys, and drift from the cluster.' },
|
|
37
|
+
{ name: 'rotate', summary: 'Replaces the current projection or encryption identity.' },
|
|
38
|
+
{ name: 'clean', summary: 'Withdraws the domain traces from the local filesystem.' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The canonical option set, identical on every action of every domain.
|
|
43
|
+
*
|
|
44
|
+
* Deliberately five. Everything a single domain used to carry its own flag for — an Age
|
|
45
|
+
* recipient, a secret name list, a sub-configuration — passes through `--args` instead, so the
|
|
46
|
+
* visible surface does not grow when one domain gains a parameter.
|
|
47
|
+
* @constant {Array<{flags: string, description: string}>}
|
|
48
|
+
* @memberof UnderpostDomains
|
|
49
|
+
*/
|
|
50
|
+
const DOMAIN_OPTIONS = [
|
|
51
|
+
{ flags: '--env <env>', description: 'Target environment: development | production | test (default: production).' },
|
|
52
|
+
{ flags: '--namespace <namespace>', description: 'Kubernetes namespace to act on (default: default).' },
|
|
53
|
+
{
|
|
54
|
+
flags: '--args <key=value-list>',
|
|
55
|
+
description:
|
|
56
|
+
'Comma-separated domain parameters, e.g. `names=postgres-secret`, `recipient=age1...`, `sub-conf=nexodev`.',
|
|
57
|
+
},
|
|
58
|
+
{ flags: '--dry-run', description: 'Reports what the action would change without changing anything.' },
|
|
59
|
+
{ flags: '--force', description: 'Confirms the irreversible variant of the action.' },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Key-level operators a domain may register alongside the canonical seven.
|
|
64
|
+
*
|
|
65
|
+
* They are not part of {@link DOMAIN_ACTIONS} on purpose: the canonical set is a bulk-lifecycle
|
|
66
|
+
* verb set, symmetric across every domain, with no place for per-key reads and writes. A domain
|
|
67
|
+
* that owns a key-value store opts into these instead of getting a command of its own — the host
|
|
68
|
+
* configuration store is the host domain's, so `underpost host get KEY` is where it is read.
|
|
69
|
+
* @constant {Array<string>}
|
|
70
|
+
* @memberof UnderpostDomains
|
|
71
|
+
*/
|
|
72
|
+
const DOMAIN_STORE_OPERATORS = ['get', 'set', 'delete', 'list'];
|
|
73
|
+
|
|
74
|
+
const DOMAIN_STORE_OPTIONS = [
|
|
75
|
+
{ flags: '--plain', description: 'Prints the value in plain text (get).' },
|
|
76
|
+
{ flags: '--filter <keyword>', description: 'Filters by matching key or value (list).' },
|
|
77
|
+
{ flags: '--copy', description: 'Copies the value to the clipboard (get).' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parses `--args key=value,key=value` into an object.
|
|
82
|
+
*
|
|
83
|
+
* Values may contain `=` (an Age recipient does not, a password can), so only the first
|
|
84
|
+
* separator splits. A bare `key` is the boolean shorthand for `key=true`.
|
|
85
|
+
* @param {string} [value] - Raw `--args` string.
|
|
86
|
+
* @returns {Object<string, string|boolean>} Parsed parameters.
|
|
87
|
+
* @memberof UnderpostDomains
|
|
88
|
+
*/
|
|
89
|
+
const parseDomainArgs = (value) => {
|
|
90
|
+
// Idempotent: an already-parsed object passes through, so normalizing a context twice — once
|
|
91
|
+
// at the CLI boundary, once inside the action a programmatic caller invoked directly — is safe.
|
|
92
|
+
if (value && typeof value === 'object') return { ...value };
|
|
93
|
+
return Object.fromEntries(
|
|
94
|
+
`${value ?? ''}`
|
|
95
|
+
.split(',')
|
|
96
|
+
.map((entry) => entry.trim())
|
|
97
|
+
.filter(Boolean)
|
|
98
|
+
.map((entry) => {
|
|
99
|
+
const index = entry.indexOf('=');
|
|
100
|
+
return index === -1 ? [entry, true] : [entry.slice(0, index).trim(), entry.slice(index + 1)];
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Normalizes the parsed options handed to a domain action, so every domain receives the same
|
|
107
|
+
* shape and none of them re-derives a default of its own.
|
|
108
|
+
* @param {object} [options] - Commander options for the invocation.
|
|
109
|
+
* @returns {{env: string, namespace: string, args: object, dryRun: boolean, force: boolean}} Normalized context.
|
|
110
|
+
* @memberof UnderpostDomains
|
|
111
|
+
*/
|
|
112
|
+
const domainContextFactory = (options = {}) => ({
|
|
113
|
+
env: `${options.env ?? ''}`.trim() || 'production',
|
|
114
|
+
namespace: `${options.namespace ?? ''}`.trim() || 'default',
|
|
115
|
+
args: parseDomainArgs(options.args),
|
|
116
|
+
dryRun: options.dryRun === true,
|
|
117
|
+
force: options.force === true,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Registers one domain as a top-level command carrying the full canonical surface.
|
|
122
|
+
*
|
|
123
|
+
* The action is a required argument rather than a flag so the three commands read as
|
|
124
|
+
* `underpost <domain> <action>`, and an unknown action fails against the shared list instead of
|
|
125
|
+
* silently doing nothing.
|
|
126
|
+
* @param {object} program - The commander program.
|
|
127
|
+
* @param {object} domain - Domain registration.
|
|
128
|
+
* @param {string} domain.name - Command name (`secret`, `host`, `app`, `state`).
|
|
129
|
+
* @param {boolean} [domain.store] - Whether this domain owns a key-value store, and so carries
|
|
130
|
+
* the key-level operators alongside the canonical actions.
|
|
131
|
+
* @param {string} domain.description - One-line command description.
|
|
132
|
+
* @param {Function} domain.api - Returns the domain API implementing every canonical action.
|
|
133
|
+
* @returns {object} The registered commander command.
|
|
134
|
+
* @memberof UnderpostDomains
|
|
135
|
+
*/
|
|
136
|
+
const registerDomainCommand = (program, { name, description, api, store = false }) => {
|
|
137
|
+
const actions = DOMAIN_ACTIONS.map((action) => action.name);
|
|
138
|
+
const operators = store ? DOMAIN_STORE_OPERATORS : [];
|
|
139
|
+
const command = program
|
|
140
|
+
.command(name)
|
|
141
|
+
.argument(
|
|
142
|
+
'<action>',
|
|
143
|
+
`Action to run. One of: ${[...actions, ...operators].join(', ')}.\n` +
|
|
144
|
+
DOMAIN_ACTIONS.map((action) => ` ${action.name.padEnd(8)} ${action.summary}`).join('\n') +
|
|
145
|
+
(store ? `\n ${'get|set|delete|list'.padEnd(8)} Key-level access to the store this domain owns.` : ''),
|
|
146
|
+
)
|
|
147
|
+
.description(description);
|
|
148
|
+
// Only a store-owning domain takes a key and a value; the others keep the bare action surface.
|
|
149
|
+
if (store) {
|
|
150
|
+
command.argument('[key]', 'Key to act on, for the key-level operators.');
|
|
151
|
+
command.argument('[value]', 'Value to write, for the `set` operator.');
|
|
152
|
+
}
|
|
153
|
+
// Commander hands the action `(...arguments, options, command)`, so the options object is
|
|
154
|
+
// second from the end no matter how many positionals this domain declared.
|
|
155
|
+
command.action((...received) => {
|
|
156
|
+
const options = received[received.length - 2];
|
|
157
|
+
const [action, key, value] = received;
|
|
158
|
+
if (operators.includes(action)) {
|
|
159
|
+
if (action !== 'list' && !key) throw new Error(`${name} ${action} requires a key`);
|
|
160
|
+
if (action === 'set' && value === undefined) throw new Error(`${name} set requires a value`);
|
|
161
|
+
return api()[action](key, value, options);
|
|
162
|
+
}
|
|
163
|
+
if (!actions.includes(action))
|
|
164
|
+
throw new Error(`Unknown ${name} action: ${action} (expected ${[...actions, ...operators].join(', ')})`);
|
|
165
|
+
const implementation = api()[action];
|
|
166
|
+
// Guards the contract at the boundary: a domain that has not implemented the full
|
|
167
|
+
// canonical set fails here by name, rather than as `undefined is not a function`.
|
|
168
|
+
if (typeof implementation !== 'function')
|
|
169
|
+
throw new Error(`Domain '${name}' does not implement the '${action}' action`);
|
|
170
|
+
return implementation(domainContextFactory(options));
|
|
171
|
+
});
|
|
172
|
+
for (const option of DOMAIN_OPTIONS) command.option(option.flags, option.description);
|
|
173
|
+
if (store) for (const option of DOMAIN_STORE_OPTIONS) command.option(option.flags, option.description);
|
|
174
|
+
return command;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export {
|
|
178
|
+
DOMAIN_ACTIONS,
|
|
179
|
+
DOMAIN_OPTIONS,
|
|
180
|
+
DOMAIN_STORE_OPERATORS,
|
|
181
|
+
domainContextFactory,
|
|
182
|
+
parseDomainArgs,
|
|
183
|
+
registerDomainCommand,
|
|
184
|
+
};
|