@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
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import Underpost from '../../../../src/index.js';
|
|
6
|
+
import {
|
|
7
|
+
UNDERPOST_GATEWAY,
|
|
8
|
+
assertStaticAssets,
|
|
9
|
+
gatewayFallbackProbeRunner,
|
|
10
|
+
gatewayStaticAssetExists,
|
|
11
|
+
installGatewayConf,
|
|
12
|
+
instanceFallbackChecksFactory,
|
|
13
|
+
placeInstanceStaticAssets,
|
|
14
|
+
pwaFallbackChecksFactory,
|
|
15
|
+
readHostInstanceRegistry,
|
|
16
|
+
seedDefaultStatusPage,
|
|
17
|
+
syncStaticAssetFromPod,
|
|
18
|
+
writeHostInstanceRegistry,
|
|
19
|
+
writeHostServerConf,
|
|
20
|
+
writeStaticAsset,
|
|
21
|
+
} from '../../../../src/server/network/underpost-gateway.js';
|
|
22
|
+
import { shellHarness } from '../../../support/shell-harness.js';
|
|
23
|
+
|
|
24
|
+
const HOST_ROOT = '/underpost/gateway';
|
|
25
|
+
const CONF_DIR = '/tmp/gateway-conf-fixture';
|
|
26
|
+
|
|
27
|
+
// The gateway root is a root-owned node directory and every install talks to a
|
|
28
|
+
// running Nginx in the cluster. Both are replaced so the assertions are over the
|
|
29
|
+
// command vector and the files a run would place.
|
|
30
|
+
const gatewayFixture = (files = {}) => {
|
|
31
|
+
const table = new Map(Object.entries(files));
|
|
32
|
+
const written = new Map();
|
|
33
|
+
const removed = [];
|
|
34
|
+
const keys = () => [...table.keys(), ...written.keys()];
|
|
35
|
+
|
|
36
|
+
vi.spyOn(fs, 'existsSync').mockImplementation((filePath) => {
|
|
37
|
+
const key = `${filePath}`;
|
|
38
|
+
if (removed.includes(key)) return false;
|
|
39
|
+
return table.has(key) || written.has(key) || keys().some((entry) => entry.startsWith(`${key}/`));
|
|
40
|
+
});
|
|
41
|
+
vi.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
|
|
42
|
+
const key = `${filePath}`;
|
|
43
|
+
if (written.has(key)) return written.get(key);
|
|
44
|
+
if (table.has(key)) return table.get(key);
|
|
45
|
+
throw Object.assign(new Error(`ENOENT: ${key}`), { code: 'ENOENT' });
|
|
46
|
+
});
|
|
47
|
+
const write = (filePath, value) => {
|
|
48
|
+
const key = `${filePath}`;
|
|
49
|
+
const index = removed.indexOf(key);
|
|
50
|
+
if (index !== -1) removed.splice(index, 1);
|
|
51
|
+
written.set(key, `${value}`);
|
|
52
|
+
};
|
|
53
|
+
vi.spyOn(fs, 'writeFileSync').mockImplementation(write);
|
|
54
|
+
vi.spyOn(fs, 'mkdirpSync').mockImplementation(() => undefined);
|
|
55
|
+
vi.spyOn(fs, 'removeSync').mockImplementation((filePath) => {
|
|
56
|
+
written.delete(`${filePath}`);
|
|
57
|
+
removed.push(`${filePath}`);
|
|
58
|
+
});
|
|
59
|
+
vi.spyOn(fs, 'statSync').mockImplementation((filePath) => ({
|
|
60
|
+
size: `${written.get(`${filePath}`) ?? table.get(`${filePath}`) ?? ''}`.length,
|
|
61
|
+
}));
|
|
62
|
+
vi.spyOn(fs, 'readdirSync').mockImplementation((dir) =>
|
|
63
|
+
keys()
|
|
64
|
+
.filter((filePath) => filePath.startsWith(`${dir}/`))
|
|
65
|
+
.map((filePath) => filePath.slice(`${dir}/`.length))
|
|
66
|
+
.filter((name) => !name.includes('/')),
|
|
67
|
+
);
|
|
68
|
+
return { table, written, removed, write };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe('gateway static assets', () => {
|
|
72
|
+
let harness;
|
|
73
|
+
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
harness = shellHarness();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
harness.restore();
|
|
80
|
+
vi.restoreAllMocks();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('placement', () => {
|
|
84
|
+
it('copies a document into the root-owned gateway tree', () => {
|
|
85
|
+
gatewayFixture({ './project/public/404/index.html': '<!doctype html>404\n' });
|
|
86
|
+
expect(
|
|
87
|
+
writeStaticAsset({
|
|
88
|
+
hostRoot: HOST_ROOT,
|
|
89
|
+
assetPath: 'app.test/root/status-pages/404/index.html',
|
|
90
|
+
sourcePath: './project/public/404/index.html',
|
|
91
|
+
}),
|
|
92
|
+
).to.equal(true);
|
|
93
|
+
expect(harness.ran(`sudo cp -f ./project/public/404/index.html ${HOST_ROOT}/app.test`)).to.equal(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// A half-finished copy leaves an empty file behind, and serving it would
|
|
97
|
+
// answer a status page with a blank body.
|
|
98
|
+
it('places nothing for a missing or empty source', () => {
|
|
99
|
+
gatewayFixture({ './project/public/empty.html': '' });
|
|
100
|
+
expect(
|
|
101
|
+
writeStaticAsset({ hostRoot: HOST_ROOT, assetPath: 'a', sourcePath: './project/public/empty.html' }),
|
|
102
|
+
).to.equal(false);
|
|
103
|
+
expect(writeStaticAsset({ hostRoot: HOST_ROOT, assetPath: 'a', sourcePath: './absent.html' })).to.equal(false);
|
|
104
|
+
expect(writeStaticAsset({ hostRoot: HOST_ROOT, assetPath: 'a' })).to.equal(false);
|
|
105
|
+
expect(harness.ran('sudo cp')).to.equal(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('treats an empty document under the gateway root as absent', () => {
|
|
109
|
+
gatewayFixture({ [`${HOST_ROOT}/a/index.html`]: 'body', [`${HOST_ROOT}/b/index.html`]: '' });
|
|
110
|
+
expect(gatewayStaticAssetExists({ hostRoot: HOST_ROOT, assetPath: 'a/index.html' })).to.equal(true);
|
|
111
|
+
expect(gatewayStaticAssetExists({ hostRoot: HOST_ROOT, assetPath: 'b/index.html' })).to.equal(false);
|
|
112
|
+
expect(gatewayStaticAssetExists({ hostRoot: HOST_ROOT, assetPath: 'c/index.html' })).to.equal(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// The pod is the authority: several clients are built from sources that only
|
|
116
|
+
// exist inside the container.
|
|
117
|
+
it('pulls a document out of the running workload through a keyed staging path', () => {
|
|
118
|
+
const { write } = gatewayFixture();
|
|
119
|
+
harness.route({
|
|
120
|
+
match: (command) => {
|
|
121
|
+
const staged = /kubectl cp \S+ (\/tmp\/underpost-gateway-\S+)/.exec(command)?.[1];
|
|
122
|
+
if (staged) write(staged, '<!doctype html>from pod\n');
|
|
123
|
+
return false;
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
expect(
|
|
127
|
+
syncStaticAssetFromPod({
|
|
128
|
+
podName: 'app-pod',
|
|
129
|
+
namespace: 'ops',
|
|
130
|
+
container: 'app',
|
|
131
|
+
sourcePath: '/app/public/404/index.html',
|
|
132
|
+
hostRoot: HOST_ROOT,
|
|
133
|
+
assetPath: 'app.test/root/status-pages/404/index.html',
|
|
134
|
+
}),
|
|
135
|
+
).to.equal(true);
|
|
136
|
+
expect(harness.ran('kubectl cp ops/app-pod:/app/public/404/index.html')).to.equal(true);
|
|
137
|
+
expect(harness.ran('-c app')).to.equal(true);
|
|
138
|
+
expect(harness.ran(`sudo cp -f /tmp/underpost-gateway-`)).to.equal(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('reports nothing placed when the workload has no such document', () => {
|
|
142
|
+
gatewayFixture();
|
|
143
|
+
expect(
|
|
144
|
+
syncStaticAssetFromPod({
|
|
145
|
+
podName: 'app-pod',
|
|
146
|
+
sourcePath: '/app/absent.html',
|
|
147
|
+
hostRoot: HOST_ROOT,
|
|
148
|
+
assetPath: 'a',
|
|
149
|
+
}),
|
|
150
|
+
).to.equal(false);
|
|
151
|
+
expect(harness.ran('sudo cp -f')).to.equal(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// A missing document is invisible at deploy time and only surfaces later as
|
|
155
|
+
// a shared default page in place of the host's own.
|
|
156
|
+
it('fails the deploy when a configured document reached neither pass', () => {
|
|
157
|
+
gatewayFixture();
|
|
158
|
+
expect(() =>
|
|
159
|
+
assertStaticAssets({
|
|
160
|
+
records: [{ source: null, assetPath: 'app.test/root/status-pages/404/index.html' }],
|
|
161
|
+
hostRoot: HOST_ROOT,
|
|
162
|
+
label: 'run instance',
|
|
163
|
+
}),
|
|
164
|
+
).to.throw('missing configured assets');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('accepts a document that either pass supplied', () => {
|
|
168
|
+
gatewayFixture({ [`${HOST_ROOT}/b`]: 'body' });
|
|
169
|
+
const records = [
|
|
170
|
+
{ source: 'project', assetPath: 'a' },
|
|
171
|
+
{ source: null, assetPath: 'b' },
|
|
172
|
+
];
|
|
173
|
+
expect(assertStaticAssets({ records, hostRoot: HOST_ROOT, label: 'run instance' })).to.equal(records);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('places every declared status page a set of instances carries', () => {
|
|
177
|
+
gatewayFixture({ 'fixture-server/public/404/index.html': '<!doctype html>404\n' });
|
|
178
|
+
vi.spyOn(Underpost.deploy, 'underpostGatewayRootFactory').mockReturnValue(HOST_ROOT);
|
|
179
|
+
const records = placeInstanceStaticAssets({
|
|
180
|
+
instances: [
|
|
181
|
+
{
|
|
182
|
+
id: 'mmo-server',
|
|
183
|
+
host: 'server.fixture.test',
|
|
184
|
+
path: '/',
|
|
185
|
+
metadata: { repository: 'underpostnet/fixture-server' },
|
|
186
|
+
customStatusPages: [{ status: '404', hostPath: './public/404/index.html' }],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
options: {},
|
|
190
|
+
label: 'run instance',
|
|
191
|
+
});
|
|
192
|
+
expect(records[0].source).to.equal('project');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('host server blocks', () => {
|
|
197
|
+
it('writes a block and reports the change', () => {
|
|
198
|
+
const { written } = gatewayFixture();
|
|
199
|
+
expect(writeHostServerConf({ confDir: CONF_DIR, host: 'app.test', conf: 'server {}\n' })).to.equal(true);
|
|
200
|
+
expect(written.get(`${CONF_DIR}/app.test.conf`)).to.equal('server {}\n');
|
|
201
|
+
expect(writeHostServerConf({ confDir: CONF_DIR, host: 'app.test', conf: 'server {}\n' })).to.equal(false);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('removes the block when there is nothing to serve', () => {
|
|
205
|
+
const { removed } = gatewayFixture({ [`${CONF_DIR}/app.test.conf`]: 'server {}\n' });
|
|
206
|
+
expect(writeHostServerConf({ confDir: CONF_DIR, host: 'app.test', conf: '' })).to.equal(true);
|
|
207
|
+
expect(removed).to.include(`${CONF_DIR}/app.test.conf`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// The registry records what the host was last rendered with, which is the
|
|
211
|
+
// only place a variant's descriptor survives leaving the conf.
|
|
212
|
+
it('records and reads back the descriptors a host was rendered with', () => {
|
|
213
|
+
const { written } = gatewayFixture();
|
|
214
|
+
const instances = [{ id: 'mmo-server', host: 'app.test', path: '/' }];
|
|
215
|
+
expect(writeHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test', instances })).to.equal(true);
|
|
216
|
+
expect(writeHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test', instances })).to.equal(false);
|
|
217
|
+
expect(readHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test' })).to.deep.equal(instances);
|
|
218
|
+
// Given a non-.conf suffix so the install pass never feeds it to Nginx.
|
|
219
|
+
expect([...written.keys()][0].endsWith('.instances.json')).to.equal(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('reads no descriptors from an absent or malformed registry', () => {
|
|
223
|
+
gatewayFixture();
|
|
224
|
+
expect(readHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test' })).to.deep.equal([]);
|
|
225
|
+
vi.restoreAllMocks();
|
|
226
|
+
gatewayFixture({ [`${CONF_DIR}/app.test.instances.json`]: '{ not json' });
|
|
227
|
+
expect(readHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test' })).to.deep.equal([]);
|
|
228
|
+
vi.restoreAllMocks();
|
|
229
|
+
gatewayFixture({ [`${CONF_DIR}/app.test.instances.json`]: JSON.stringify([{ id: 'a' }, {}]) });
|
|
230
|
+
expect(readHostInstanceRegistry({ confDir: CONF_DIR, host: 'app.test' })).to.deep.equal([{ id: 'a' }]);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('gateway config install', () => {
|
|
235
|
+
const installFixture = (files = {}) =>
|
|
236
|
+
gatewayFixture({ [`${CONF_DIR}/app.test.conf`]: 'server { listen 80; }\n', ...files });
|
|
237
|
+
|
|
238
|
+
it('validates the candidate config before signalling the running master', () => {
|
|
239
|
+
installFixture();
|
|
240
|
+
harness.route({ match: 'nginx -t', code: 0, stdout: 'configuration file test is successful\n' });
|
|
241
|
+
expect(installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.equal(true);
|
|
242
|
+
const testIndex = harness.calls.findIndex((command) => command.includes('nginx -t'));
|
|
243
|
+
const reloadIndex = harness.calls.findIndex((command) => command.includes('nginx -s reload'));
|
|
244
|
+
expect(reloadIndex).to.be.above(testIndex);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// A block that does not parse would take the whole edge down on reload.
|
|
248
|
+
it('restores the previous blocks and fails when Nginx rejects the candidate', () => {
|
|
249
|
+
const { written } = installFixture({
|
|
250
|
+
[`${HOST_ROOT}/${UNDERPOST_GATEWAY.confDir}/app.test.conf`]: 'server { listen 8080; }\n',
|
|
251
|
+
});
|
|
252
|
+
harness.route({ match: 'nginx -t', code: 1, stdout: 'nginx: [emerg] unknown directive\n' });
|
|
253
|
+
expect(() => installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.throw(
|
|
254
|
+
'Gateway config rejected',
|
|
255
|
+
);
|
|
256
|
+
expect(harness.ran('underpost-gateway-restore-app.test.conf')).to.equal(true);
|
|
257
|
+
expect(harness.ran('nginx -s reload')).to.equal(false);
|
|
258
|
+
expect([...written.keys()].every((path) => !path.includes('restore'))).to.equal(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('removes a block that was not there before the failed candidate', () => {
|
|
262
|
+
installFixture();
|
|
263
|
+
harness.route({ match: 'nginx -t', code: 1, stdout: 'nginx: [emerg]\n' });
|
|
264
|
+
expect(() => installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.throw();
|
|
265
|
+
expect(harness.ran(`sudo rm -f ${HOST_ROOT}/${UNDERPOST_GATEWAY.confDir}/app.test.conf`)).to.equal(true);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// The running master normally retains its old config when a reload fails, so
|
|
269
|
+
// the restore is re-signalled to converge it back.
|
|
270
|
+
it('restores and re-signals when the reload itself fails', () => {
|
|
271
|
+
installFixture();
|
|
272
|
+
harness.route({ match: 'nginx -t', code: 0, stdout: 'successful\n' });
|
|
273
|
+
let firstReload = true;
|
|
274
|
+
harness.route({
|
|
275
|
+
match: (command) => {
|
|
276
|
+
if (!command.includes('nginx -s reload')) return false;
|
|
277
|
+
if (!firstReload) return false;
|
|
278
|
+
firstReload = false;
|
|
279
|
+
return true;
|
|
280
|
+
},
|
|
281
|
+
throws: new Error('reload signal refused'),
|
|
282
|
+
});
|
|
283
|
+
expect(() => installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.throw(
|
|
284
|
+
'reload signal refused',
|
|
285
|
+
);
|
|
286
|
+
expect(harness.count('nginx -s reload')).to.equal(2);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('installs nothing when the build produced no blocks', () => {
|
|
290
|
+
gatewayFixture();
|
|
291
|
+
expect(installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.equal(false);
|
|
292
|
+
vi.restoreAllMocks();
|
|
293
|
+
gatewayFixture({ [`${CONF_DIR}/app.test.instances.json`]: '[]' });
|
|
294
|
+
expect(installGatewayConf({ hostRoot: HOST_ROOT, confSourceDir: CONF_DIR })).to.equal(false);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('default status page', () => {
|
|
299
|
+
it('creates the include directory and writes the shared fallback', () => {
|
|
300
|
+
gatewayFixture();
|
|
301
|
+
expect(seedDefaultStatusPage(HOST_ROOT)).to.equal(true);
|
|
302
|
+
expect(harness.ran(`sudo mkdir -p ${HOST_ROOT}/${UNDERPOST_GATEWAY.confDir}`)).to.equal(true);
|
|
303
|
+
expect(harness.ran('404 Not Found')).to.equal(true);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// An operator may have replaced it.
|
|
307
|
+
it('never overwrites an existing document', () => {
|
|
308
|
+
gatewayFixture({ [`${HOST_ROOT}/${UNDERPOST_GATEWAY.defaultHostDir}/status-pages/404/index.html`]: 'custom' });
|
|
309
|
+
expect(seedDefaultStatusPage(HOST_ROOT)).to.equal(false);
|
|
310
|
+
expect(harness.ran('sudo tee')).to.equal(false);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('fallback probe selection', () => {
|
|
315
|
+
it('probes one route per sub-path that declares a maintenance view', () => {
|
|
316
|
+
gatewayFixture({
|
|
317
|
+
'./engine-private/conf/dd-core/conf.server.json': JSON.stringify({
|
|
318
|
+
'app.fixture.test': {
|
|
319
|
+
'/': { client: 'App', ssr: 'App' },
|
|
320
|
+
'/single': { client: 'S', singleReplica: true, replicas: ['/blue'] },
|
|
321
|
+
},
|
|
322
|
+
}),
|
|
323
|
+
'./engine-private/conf/dd-core/conf.ssr.json': JSON.stringify({
|
|
324
|
+
App: { head: [], body: [], mailer: {}, views: [{ path: '/', client: 'App' }] },
|
|
325
|
+
}),
|
|
326
|
+
});
|
|
327
|
+
expect(() => pwaFallbackChecksFactory('dd-core')).not.to.throw();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// A variant declaring several status pages is still one route to probe.
|
|
331
|
+
it('probes each instance sub-path once, however many pages it declares', () => {
|
|
332
|
+
const checks = instanceFallbackChecksFactory([
|
|
333
|
+
{
|
|
334
|
+
id: 'mmo-server',
|
|
335
|
+
host: 'server.fixture.test',
|
|
336
|
+
path: '/',
|
|
337
|
+
customStatusPages: [
|
|
338
|
+
{ status: '404', hostPath: './public/404/index.html' },
|
|
339
|
+
{ status: '500', hostPath: './public/500/index.html' },
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: 'mmo-server-forest',
|
|
344
|
+
host: 'server.fixture.test',
|
|
345
|
+
path: '/FOREST',
|
|
346
|
+
customStatusPages: [{ status: '404', hostPath: './public/404/index.html' }],
|
|
347
|
+
},
|
|
348
|
+
]);
|
|
349
|
+
expect(checks.map(({ path, kind }) => `${path}:${kind}`)).to.deep.equal(['/:status:404', '/FOREST:status:404']);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('probes nothing for instances declaring no status page', () => {
|
|
353
|
+
expect(instanceFallbackChecksFactory([{ id: 'a', host: 'a.test', path: '/' }])).to.deep.equal([]);
|
|
354
|
+
expect(instanceFallbackChecksFactory()).to.deep.equal([]);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
describe('pre-runtime fallback probes', () => {
|
|
359
|
+
const CHECK = {
|
|
360
|
+
host: 'app.fixture.test',
|
|
361
|
+
path: '/',
|
|
362
|
+
assetPath: 'app.fixture.test/root/status-pages/404/index.html',
|
|
363
|
+
kind: 'status:404',
|
|
364
|
+
};
|
|
365
|
+
const DOCUMENT = '<!doctype html>404\n';
|
|
366
|
+
|
|
367
|
+
const probeFixture = () => {
|
|
368
|
+
vi.spyOn(Underpost.deploy, 'underpostGatewayRootFactory').mockReturnValue(HOST_ROOT);
|
|
369
|
+
return gatewayFixture({ [`${HOST_ROOT}/${CHECK.assetPath}`]: DOCUMENT });
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
it('runs nothing when the Gateway API stack is not in use', async () => {
|
|
373
|
+
probeFixture();
|
|
374
|
+
expect(await gatewayFallbackProbeRunner({ checks: [CHECK], options: {}, label: 'deploy' })).to.deep.equal([]);
|
|
375
|
+
expect(harness.calls.length).to.equal(0);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('runs nothing when no route declares a fallback', async () => {
|
|
379
|
+
probeFixture();
|
|
380
|
+
expect(
|
|
381
|
+
await gatewayFallbackProbeRunner({ checks: [], options: { gatewayApi: true }, label: 'deploy' }),
|
|
382
|
+
).to.deep.equal([]);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('refuses to continue when the gateway is not operational', async () => {
|
|
386
|
+
probeFixture();
|
|
387
|
+
await expect(
|
|
388
|
+
gatewayFallbackProbeRunner({
|
|
389
|
+
checks: [CHECK],
|
|
390
|
+
options: { gatewayApi: true },
|
|
391
|
+
label: 'deploy',
|
|
392
|
+
gatewayStatusRunner: async () => ({ programmed: false, servesHttps: false }),
|
|
393
|
+
}),
|
|
394
|
+
).rejects.toThrow('not operational before application deployment');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('refuses a dev gateway that does not terminate TLS', async () => {
|
|
398
|
+
probeFixture();
|
|
399
|
+
await expect(
|
|
400
|
+
gatewayFallbackProbeRunner({
|
|
401
|
+
checks: [CHECK],
|
|
402
|
+
options: { gatewayApi: true, dev: true },
|
|
403
|
+
label: 'deploy',
|
|
404
|
+
gatewayStatusRunner: async () => ({ programmed: true, servesHttps: false }),
|
|
405
|
+
}),
|
|
406
|
+
).rejects.toThrow('not operational');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// The assertion is on the response body, not on object status: an accepted
|
|
410
|
+
// route says nothing about which document a client receives.
|
|
411
|
+
it('passes only when the edge answers an upstream failure with the configured document', async () => {
|
|
412
|
+
probeFixture();
|
|
413
|
+
harness.route({ match: 'wget -q -O -', code: 0, stdout: DOCUMENT });
|
|
414
|
+
harness.route({ match: 'wget -S -O /dev/null', code: 0, stdout: 'HTTP/1.1 502 Bad Gateway\n' });
|
|
415
|
+
const [result] = await gatewayFallbackProbeRunner({
|
|
416
|
+
checks: [CHECK],
|
|
417
|
+
options: { gatewayApi: true },
|
|
418
|
+
label: 'deploy',
|
|
419
|
+
gatewayStatusRunner: async () => ({ programmed: true, servesHttps: true }),
|
|
420
|
+
});
|
|
421
|
+
expect(result).to.include({ passed: true, status: '502', bodyMatchesConfiguredAsset: true, attempts: 1 });
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('probes over TLS through the local resolve override in dev', async () => {
|
|
425
|
+
probeFixture();
|
|
426
|
+
harness.route({ match: 'curl -sSk --noproxy', code: 0, stdout: DOCUMENT });
|
|
427
|
+
harness.route({ match: "-w '%{http_code}'", code: 0, stdout: '503\n' });
|
|
428
|
+
const [result] = await gatewayFallbackProbeRunner({
|
|
429
|
+
checks: [CHECK],
|
|
430
|
+
options: { gatewayApi: true, dev: true },
|
|
431
|
+
label: 'deploy',
|
|
432
|
+
gatewayStatusRunner: async () => ({ programmed: true, servesHttps: true }),
|
|
433
|
+
});
|
|
434
|
+
expect(result.passed).to.equal(true);
|
|
435
|
+
expect(harness.ran(`--resolve ${CHECK.host}:443:127.0.0.1`)).to.equal(true);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// A probe must not pass against a shared default page.
|
|
439
|
+
it('fails a probe answered with a body that is not the configured document', async () => {
|
|
440
|
+
probeFixture();
|
|
441
|
+
harness.route({ match: 'wget -q -O -', code: 0, stdout: '<!doctype html>shared default\n' });
|
|
442
|
+
harness.route({ match: 'wget -S -O /dev/null', code: 0, stdout: 'HTTP/1.1 502 Bad Gateway\n' });
|
|
443
|
+
await expect(
|
|
444
|
+
gatewayFallbackProbeRunner({
|
|
445
|
+
checks: [{ ...CHECK, host: 'app.fixture.test' }],
|
|
446
|
+
options: { gatewayApi: true },
|
|
447
|
+
label: 'deploy',
|
|
448
|
+
gatewayStatusRunner: async () => ({ programmed: true, servesHttps: true }),
|
|
449
|
+
}),
|
|
450
|
+
).rejects.toThrow('fallback probes failed');
|
|
451
|
+
}, 90000);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
trafficFromRoutingInfoFactory,
|
|
15
15
|
trafficProbePathsFactory,
|
|
16
16
|
trafficTableRowsFactory,
|
|
17
|
-
} from '
|
|
17
|
+
} from '../../../../src/server/runtime/conf.js';
|
|
18
18
|
|
|
19
19
|
// Pure resolution over the conf: the cluster lookups are injected, so every case
|
|
20
20
|
// here fixes the routed colour and the endpoint readiness explicitly.
|
|
@@ -648,7 +648,7 @@ describe('blue/green traffic plan', () => {
|
|
|
648
648
|
});
|
|
649
649
|
|
|
650
650
|
it('keeps configured report rows when no Kubernetes deployments exist', () => {
|
|
651
|
-
const runSource = fs.readFileSync(new URL('
|
|
651
|
+
const runSource = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
652
652
|
const start = runSource.indexOf(" 'get-traffic': async");
|
|
653
653
|
const end = runSource.indexOf(" 'instance-promote': async", start);
|
|
654
654
|
const getTraffic = runSource.slice(start, end);
|
|
@@ -670,7 +670,7 @@ describe('blue/green traffic plan', () => {
|
|
|
670
670
|
});
|
|
671
671
|
|
|
672
672
|
it('feeds the report from stable Service selectors before legacy route text', () => {
|
|
673
|
-
const runSource = fs.readFileSync(new URL('
|
|
673
|
+
const runSource = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
674
674
|
const start = runSource.indexOf(" 'get-traffic': async");
|
|
675
675
|
const end = runSource.indexOf(" 'instance-promote': async", start);
|
|
676
676
|
const getTraffic = runSource.slice(start, end);
|
|
@@ -683,11 +683,15 @@ describe('blue/green traffic plan', () => {
|
|
|
683
683
|
});
|
|
684
684
|
|
|
685
685
|
it('uses real followed curl probes and reports current and opposite deployment status without ENV', () => {
|
|
686
|
-
const runSource = fs.readFileSync(new URL('
|
|
686
|
+
const runSource = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
687
687
|
const start = runSource.indexOf(" 'get-traffic': async");
|
|
688
688
|
const end = runSource.indexOf(" 'instance-promote': async", start);
|
|
689
689
|
const getTraffic = runSource.slice(start, end);
|
|
690
|
-
|
|
690
|
+
// The probe itself moved to one implementation the ingress event shares;
|
|
691
|
+
// what matters here is that the report still goes through it.
|
|
692
|
+
expect(getTraffic).to.include('publicIngressProbeFactory(url)');
|
|
693
|
+
const confSource = fs.readFileSync(new URL('../../../../src/server/runtime/conf.js', import.meta.url), 'utf8');
|
|
694
|
+
expect(confSource).to.include('curl -L -v -i -s');
|
|
691
695
|
expect(getTraffic).to.include("'OPPOSITE'");
|
|
692
696
|
expect(getTraffic).not.to.include('showOpposite');
|
|
693
697
|
expect(getTraffic).to.include("'CURRENT'");
|
|
@@ -699,7 +703,7 @@ describe('blue/green traffic plan', () => {
|
|
|
699
703
|
});
|
|
700
704
|
|
|
701
705
|
it('caches stable Service readiness across hosts sharing the same deployment', () => {
|
|
702
|
-
const runSource = fs.readFileSync(new URL('
|
|
706
|
+
const runSource = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
703
707
|
const start = runSource.indexOf(" 'get-traffic': async");
|
|
704
708
|
const end = runSource.indexOf(" 'instance-promote': async", start);
|
|
705
709
|
const getTraffic = runSource.slice(start, end);
|
package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js}
RENAMED
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
staticPathSegmentFactory,
|
|
14
14
|
statusPageAssetPathFactory,
|
|
15
15
|
statusPageBuildSegment,
|
|
16
|
-
} from '
|
|
17
|
-
import { staticContextRoutesFactory, statusPageRoutesFactory } from '
|
|
16
|
+
} from '../../../../src/server/network/underpost-gateway.js';
|
|
17
|
+
import { staticContextRoutesFactory, statusPageRoutesFactory } from '../../../../src/client-builder/client-build.js';
|
|
18
18
|
|
|
19
19
|
// A `conf.ssr.json` client entry: two intercepted contexts, one status page,
|
|
20
20
|
// and one ordinary view that must stay with the workload.
|
|
@@ -119,6 +119,47 @@ describe('underpost gateway edge tier', () => {
|
|
|
119
119
|
expect(conf).to.include('map $http_upgrade $connection_upgrade');
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
// For every host whose errors are intercepted, this workload is the hop the
|
|
123
|
+
// site path already passes through — and the last one holding the body in
|
|
124
|
+
// the clear. Compressing anywhere further out is compressing ciphertext.
|
|
125
|
+
describe('response compression', () => {
|
|
126
|
+
it('compresses what it serves and what it proxies', () => {
|
|
127
|
+
expect(conf).to.include('gzip on;');
|
|
128
|
+
expect(conf).to.include('gzip_vary on;');
|
|
129
|
+
// Nginx's default is `off`, which skips the proxied responses that carry
|
|
130
|
+
// most of the bytes this workload forwards.
|
|
131
|
+
expect(conf).to.include('gzip_proxied any;');
|
|
132
|
+
expect(conf).to.match(/gzip_types [^;]*application\/javascript[^;]*;/);
|
|
133
|
+
expect(conf).to.match(/gzip_types [^;]*text\/css[^;]*;/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Documents are served from a volume, so an operator who places a `.gz`
|
|
137
|
+
// beside one gets it sent with no compression pass at all.
|
|
138
|
+
it('prefers a pre-compressed sibling from the static root', () => {
|
|
139
|
+
expect(conf).to.include('gzip_static on;');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('renders brotli and its modules only when the image is declared to carry them', () => {
|
|
143
|
+
expect(conf).to.not.include('brotli');
|
|
144
|
+
expect(conf).to.not.include('load_module');
|
|
145
|
+
const declared = nginxConfFactory({ compression: { brotliModuleDir: '/usr/lib/nginx/modules' } });
|
|
146
|
+
expect(declared).to.include('load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so;');
|
|
147
|
+
expect(declared).to.include('brotli on;');
|
|
148
|
+
expect(declared).to.include('brotli_static on;');
|
|
149
|
+
// Main context: nginx rejects `load_module` anywhere else.
|
|
150
|
+
expect(declared.indexOf('load_module')).to.be.lessThan(declared.indexOf('events {'));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// The config is mounted with `subPath`, which Kubernetes never refreshes,
|
|
154
|
+
// so only a changed pod template rolls the workload onto a new policy.
|
|
155
|
+
it('rolls the deployment when the policy changes', () => {
|
|
156
|
+
const hash = (docs) => kind(docs, 'Deployment').match(/nginx-conf-hash: '([0-9a-f]+)'/)[1];
|
|
157
|
+
expect(hash(manifests({ compression: { brotliModuleDir: '/usr/lib/nginx/modules' } }))).to.not.equal(
|
|
158
|
+
hash(manifests()),
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
122
163
|
// A 200 would let the PWA service worker store the shared page as the
|
|
123
164
|
// host's own and keep serving it after the real document lands.
|
|
124
165
|
it('serves the shared fallback as a 404 that is never stored', () => {
|
|
@@ -339,7 +380,7 @@ describe('underpost gateway edge tier', () => {
|
|
|
339
380
|
// on a running gateway to validate against, and cannot mutate the host either.
|
|
340
381
|
// Installing and reloading is the apply path's job.
|
|
341
382
|
describe('build/apply separation', () => {
|
|
342
|
-
const source = fs.readFileSync(new URL('
|
|
383
|
+
const source = fs.readFileSync(new URL('../../../../src/server/network/underpost-gateway.js', import.meta.url), 'utf8');
|
|
343
384
|
const bodyOf = (name) => {
|
|
344
385
|
const start = source.indexOf(`const ${name} = `);
|
|
345
386
|
const next = source.slice(start + 1).search(/\nconst \w+ = |\nexport \{/);
|
|
@@ -389,7 +430,7 @@ describe('underpost gateway edge tier', () => {
|
|
|
389
430
|
const applySites = () =>
|
|
390
431
|
['src/cli/cluster.js', 'src/cli/deploy.js', 'src/cli/run.js'].flatMap((file) =>
|
|
391
432
|
fs
|
|
392
|
-
.readFileSync(new URL(
|
|
433
|
+
.readFileSync(new URL(`../../../../${file}`, import.meta.url), 'utf8')
|
|
393
434
|
.split('\n')
|
|
394
435
|
.map((line, index) => ({ file, line: index + 1, text: line }))
|
|
395
436
|
.filter((entry) => entry.text.includes('kubectl apply') && entry.text.includes('<<')),
|
|
@@ -407,7 +448,7 @@ describe('underpost gateway edge tier', () => {
|
|
|
407
448
|
});
|
|
408
449
|
|
|
409
450
|
describe('traffic switch publication order', () => {
|
|
410
|
-
const deploySource = fs.readFileSync(new URL('
|
|
451
|
+
const deploySource = fs.readFileSync(new URL('../../../../src/cli/deploy.js', import.meta.url), 'utf8');
|
|
411
452
|
const switchTraffic = deploySource.slice(
|
|
412
453
|
deploySource.indexOf(' switchTraffic('),
|
|
413
454
|
deploySource.indexOf(' resolveDeployNode(', deploySource.indexOf(' switchTraffic(')),
|
|
@@ -442,7 +483,7 @@ describe('underpost gateway edge tier', () => {
|
|
|
442
483
|
});
|
|
443
484
|
|
|
444
485
|
describe('merged Gateway listener isolation', () => {
|
|
445
|
-
const deploySource = fs.readFileSync(new URL('
|
|
486
|
+
const deploySource = fs.readFileSync(new URL('../../../../src/cli/deploy.js', import.meta.url), 'utf8');
|
|
446
487
|
const gatewayFactory = deploySource.slice(
|
|
447
488
|
deploySource.indexOf(' gatewayYamlFactory('),
|
|
448
489
|
deploySource.indexOf(' gatewayNameFactory(', deploySource.indexOf(' gatewayYamlFactory(')),
|