@underpostnet/cyberia 3.3.0 → 3.3.76
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 +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +39 -14
- package/.github/workflows/cyberia-client.cd.yml +5 -0
- package/.github/workflows/cyberia-server.cd.yml +5 -0
- 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 +3 -0
- package/.github/workflows/engine-cyberia.ci.yml +4 -5
- package/.github/workflows/ghpkg.ci.yml +35 -20
- package/.github/workflows/hardhat.ci.yml +12 -11
- package/.github/workflows/npmpkg.ci.yml +17 -15
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
- package/.github/workflows/release.cd.yml +1 -0
- package/.prettierignore +1 -0
- package/AGENTS.md +96 -0
- package/CHANGELOG.md +653 -2513
- package/CLI-HELP.md +323 -157
- package/Dockerfile +25 -9
- package/Dockerfile.dev +25 -9
- package/Dockerfile.test +5 -5
- package/README.md +48 -28
- package/bin/build.js +86 -41
- package/bin/build.template.js +8 -3
- package/bin/cyberia.js +1494 -1403
- package/bin/deploy.js +18 -104
- package/bin/index.js +1494 -1403
- package/compose.env +17 -5
- package/conf.js +98 -7
- package/deploy/cyberia-client/deploy.sh +46 -0
- package/deploy/cyberia-client/package.sh +17 -0
- package/deploy/cyberia-client/state.sh +15 -0
- package/deploy/cyberia-server/deploy.sh +46 -0
- package/deploy/cyberia-server/package.sh +17 -0
- package/deploy/cyberia-server/state.sh +15 -0
- package/deploy/dd-cyberia/deploy.sh +9 -29
- package/deploy/dd-cyberia/init.sh +38 -45
- package/deploy/dd-cyberia/package.sh +16 -0
- package/deploy/dd-cyberia/state.sh +14 -0
- package/deploy/dd-cyberia/sync-deploy.sh +133 -147
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +610 -0
- package/deploy/lib/host.sh +194 -0
- package/deploy/lib/state.sh +65 -0
- package/deploy/pwa-microservices-template/deploy.sh +17 -42
- package/deploy/release/deploy.sh +9 -41
- package/deployment.yaml +7 -2
- package/docker-compose.yml +23 -17
- 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 +101 -40
- package/hardhat/package.json +2 -2
- package/manifests/alertmanager/deployment.yaml +81 -0
- package/manifests/blackbox-exporter/deployment.yaml +75 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- 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 +87 -12
- package/scripts/event-monitor.sh +56 -0
- package/scripts/k3s-node-setup.sh +27 -14
- package/scripts/kubeadm-node-setup.sh +31 -11
- package/scripts/maas-setup.sh +8 -8
- package/scripts/selinux-normalize.sh +907 -0
- package/scripts/shutdown-machine.sh +843 -0
- package/scripts/test-monitor.sh +5 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +15 -10
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +7 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +90 -186
- 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 +3 -3
- package/src/api/cyberia-action/cyberia-action.service.js +5 -6
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +2 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +13 -35
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +17 -34
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
- 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.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.service.js +3 -3
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +468 -5
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +502 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +29 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +35 -142
- package/src/api/cyberia-instance/cyberia-instance.controller.js +2 -5
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +5 -18
- package/src/api/cyberia-instance/cyberia-instance.service.js +5 -45
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +76 -3
- 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 +8 -4
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
- package/src/api/cyberia-quest/cyberia-quest.router.js +4 -4
- package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.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 +403 -214
- package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- 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.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- 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 +47 -12
- package/src/api/object-layer/object-layer.router.js +12 -5
- package/src/api/object-layer/object-layer.service.js +116 -123
- 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 +402 -0
- package/src/cli/baremetal.js +30 -35
- package/src/cli/client.js +306 -0
- package/src/cli/cloud-init.js +3 -3
- package/src/cli/cluster.js +191 -82
- package/src/cli/db.js +120 -68
- package/src/cli/deploy.js +157 -75
- 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-selection.js +115 -0
- package/src/cli/fs.js +159 -343
- package/src/cli/host.js +672 -0
- package/src/cli/image.js +4 -6
- package/src/cli/index.js +343 -151
- 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 +16 -12
- package/src/cli/repository.js +586 -363
- package/src/cli/run.js +398 -843
- package/src/cli/secrets.js +2158 -964
- package/src/cli/ssh.js +478 -279
- package/src/cli/state.js +394 -0
- package/src/cli/static.js +2 -2
- package/src/cli/system.js +1 -1
- package/src/cli/test.js +203 -125
- package/src/cli/vultr.js +61 -21
- package/src/cli/wireguard.js +1112 -306
- package/src/client/components/core/CalendarCore.js +53 -96
- package/src/client/components/core/Docs.js +327 -303
- package/src/client/components/core/FileExplorer.js +0 -21
- package/src/client/components/core/Modal.js +56 -1
- 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/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +2 -35
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- 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 +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +313 -30
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +48 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- 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/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- 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-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- 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 +92 -77
- package/src/client-builder/client-build-live.js +2 -2
- package/src/client-builder/client-build.js +105 -74
- package/src/client-builder/client-bundle.js +347 -0
- 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/MongoExpress.js +187 -0
- package/src/db/mongo/MongooseDB.js +7 -8
- package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
- package/src/grpc/cyberia/grpc-server.js +6 -8
- package/src/index.js +79 -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 +144 -147
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +8 -17
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +54 -10
- package/src/projects/cyberia/gemini-client.js +4 -4
- package/src/projects/cyberia/generate-saga.js +5 -12
- package/src/projects/cyberia/hot-reload-trigger.js +18 -29
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +178 -258
- package/src/projects/cyberia/ipfs-client.js +4 -4
- package/src/projects/cyberia/map-preview-generator.js +33 -69
- package/src/projects/cyberia/object-layer.js +153 -320
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +3 -19
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/projects/underpost/catalog-underpost.js +9 -1
- package/src/proxy.js +4 -7
- package/src/runtime/cyberia-client/Dockerfile +17 -8
- package/src/runtime/cyberia-client/Dockerfile.dev +13 -4
- package/src/runtime/cyberia-server/Dockerfile +18 -6
- package/src/runtime/cyberia-server/Dockerfile.dev +16 -3
- package/src/runtime/engine-cyberia/Dockerfile +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
- package/src/runtime/engine-cyberia/Dockerfile.test +5 -5
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- 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 +188 -0
- package/src/server/build/execution.js +402 -0
- package/src/server/build/package.js +508 -0
- package/src/server/build/testing.js +578 -0
- package/src/server/{dns.js → network/dns.js} +144 -11
- package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
- package/src/server/{middlewares.js → network/middlewares.js} +11 -5
- 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/{underpost-compression.js → network/underpost-compression.js} +1 -1
- package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
- package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
- package/src/server/{cri.js → ops/cri.js} +2 -2
- package/src/server/{cron.js → ops/cron.js} +349 -74
- package/src/server/ops/event-notification.js +284 -0
- package/src/server/{logger.js → ops/logger.js} +143 -23
- package/src/server/ops/monitoring.js +1724 -0
- package/src/server/{systemd.js → ops/systemd.js} +55 -1
- package/src/server/{conf.js → runtime/conf.js} +516 -423
- 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 -12
- 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/{selinux.js → security/selinux.js} +1 -1
- package/src/server/{backup.js → storage/backup.js} +17 -8
- package/src/server/{data-query.js → storage/data-query.js} +17 -1
- package/src/server/storage/downloader.js +121 -0
- package/src/server/storage/repository.js +68 -0
- package/src/server/storage/zip.js +153 -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/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +64 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +17 -23
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +466 -0
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +338 -0
- package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -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/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
- 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 +1187 -0
- package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +649 -55
- 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/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -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} +6 -6
- package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -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 +361 -0
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-loading.test.js +303 -0
- package/test/unit/conf-resolution.test.js +996 -0
- package/test/unit/coverage-artifact.test.js +221 -0
- package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-env-resolution.test.js +38 -0
- package/test/unit/deploy-log-table.test.js +518 -0
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/execution-profiles.test.js +219 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fleet-sync-source.test.js +149 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/logger-redaction.test.js +141 -0
- package/test/unit/package.test.js +457 -0
- package/test/unit/prepare-host.test.js +382 -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 +220 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +66 -0
- package/.nycrc +0 -9
- package/deploy/lib/logging.sh +0 -96
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
- package/manifests/mongodb/pv-pvc.yaml +0 -59
- package/scripts/coverall-test-cyberia.sh +0 -24
- package/scripts/coverall-test.sh +0 -24
- package/scripts/link-local-underpost-cli.sh +0 -6
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- 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/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
- 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/downloader.js +0 -73
- package/src/server/environment.js +0 -98
package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js}
RENAMED
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
loadConfInstances,
|
|
16
16
|
loadProjectInstanceEnvBuilder,
|
|
17
17
|
normalizeInstanceTopology,
|
|
18
|
-
} from '
|
|
19
|
-
import { statusPageAssetPathFactory } from '
|
|
20
|
-
import UnderpostDockerCompose from '
|
|
18
|
+
} from '../../../../src/server/runtime/conf.js';
|
|
19
|
+
import { statusPageAssetPathFactory } from '../../../../src/server/network/underpost-gateway.js';
|
|
20
|
+
import UnderpostDockerCompose from '../../../../src/cli/docker-compose.js';
|
|
21
21
|
|
|
22
22
|
// `clusterInstancesFactory` reads `./engine-private/conf/<deployId>/conf.instances.json`
|
|
23
23
|
// relative to the process cwd, mirroring every other conf loader. engine-private
|
|
@@ -61,7 +61,7 @@ const FIXTURES = {
|
|
|
61
61
|
describe('cluster custom instances', () => {
|
|
62
62
|
const created = [];
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
beforeAll(() => {
|
|
65
65
|
for (const [deployId, entries] of Object.entries(FIXTURES)) {
|
|
66
66
|
const dir = CONF_DIR(deployId);
|
|
67
67
|
if (fs.existsSync(dir)) throw new Error(`Refusing to write fixtures into an existing deploy: ${dir}`);
|
|
@@ -75,7 +75,7 @@ describe('cluster custom instances', () => {
|
|
|
75
75
|
fs.outputFileSync(`${customComposeDir}/project-router.conf`, 'project-owned\n');
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
afterAll(() => {
|
|
79
79
|
for (const dir of created) fs.removeSync(dir);
|
|
80
80
|
});
|
|
81
81
|
|
|
@@ -158,12 +158,8 @@ describe('cluster custom instances', () => {
|
|
|
158
158
|
const hostsPath = `${customComposeDir}/hosts`;
|
|
159
159
|
fs.writeFileSync(hostsPath, '127.0.0.1 localhost\n', 'utf8');
|
|
160
160
|
const options = { path: hostsPath, append: true, blockId: 'fixture-docker-compose' };
|
|
161
|
-
expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(
|
|
162
|
-
|
|
163
|
-
);
|
|
164
|
-
expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(
|
|
165
|
-
false,
|
|
166
|
-
);
|
|
161
|
+
expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(true);
|
|
162
|
+
expect(etcHostFactory(['fixture-client', 'fixture-server', 'fixture-engine'], options).changed).to.equal(false);
|
|
167
163
|
const hosts = fs.readFileSync(hostsPath, 'utf8');
|
|
168
164
|
expect(hosts.match(/underpost hosts fixture-docker-compose:begin/g)).to.have.length(1);
|
|
169
165
|
expect(hosts).to.include('127.0.0.1 localhost');
|
|
@@ -184,7 +180,7 @@ describe('cluster custom instances', () => {
|
|
|
184
180
|
it('resolves several instances across several deploys', () => {
|
|
185
181
|
const { byDeployId, unmatched } = clusterInstancesFactory(
|
|
186
182
|
['dd-fixture-a', 'dd-fixture-b'],
|
|
187
|
-
'mmo-server
|
|
183
|
+
'mmo-server,mmo-client,worker',
|
|
188
184
|
);
|
|
189
185
|
expect(byDeployId['dd-fixture-a'].ids).to.deep.equal(['mmo-server', 'mmo-client']);
|
|
190
186
|
expect(byDeployId['dd-fixture-a'].hosts).to.deep.equal(['server.fixture.test', 'client.fixture.test']);
|
|
@@ -207,13 +203,13 @@ describe('cluster custom instances', () => {
|
|
|
207
203
|
});
|
|
208
204
|
|
|
209
205
|
it('reports an id no deploy declares instead of silently skipping it', () => {
|
|
210
|
-
const { byDeployId, unmatched } = clusterInstancesFactory(['dd-fixture-a'], 'mmo-server
|
|
206
|
+
const { byDeployId, unmatched } = clusterInstancesFactory(['dd-fixture-a'], 'mmo-server,nope');
|
|
211
207
|
expect(byDeployId['dd-fixture-a'].ids).to.deep.equal(['mmo-server']);
|
|
212
208
|
expect(unmatched).to.deep.equal(['nope']);
|
|
213
209
|
});
|
|
214
210
|
|
|
215
211
|
it('is a no-op when no instance list is given', () => {
|
|
216
|
-
for (const list of ['', undefined, '
|
|
212
|
+
for (const list of ['', undefined, ','])
|
|
217
213
|
expect(clusterInstancesFactory(['dd-fixture-a'], list)).to.deep.equal({
|
|
218
214
|
byDeployId: { 'dd-fixture-a': { ids: [], hosts: [] } },
|
|
219
215
|
unmatched: [],
|
|
@@ -311,7 +307,7 @@ describe('cluster custom instances', () => {
|
|
|
311
307
|
});
|
|
312
308
|
|
|
313
309
|
describe('cluster gateway bootstrap order', () => {
|
|
314
|
-
const source = fs.readFileSync(new URL('
|
|
310
|
+
const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
315
311
|
const clusterRunner = source.slice(source.indexOf('cluster: async'), source.indexOf("'gateway-status': async"));
|
|
316
312
|
|
|
317
313
|
it('tests the ingress-only fallback before applying any workload Deployment', () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { expect } from 'chai';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
-
import Underpost from '
|
|
5
|
+
import Underpost from '../../../../src/index.js';
|
|
6
6
|
|
|
7
7
|
const deploymentManifest = (nodeName = '') =>
|
|
8
8
|
Underpost.deploy.deploymentYamlPartsFactory({
|
|
@@ -29,7 +29,7 @@ describe('deployment node placement', () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it('does not restart a controller after node-move patches its pod template', () => {
|
|
32
|
-
const source = fs.readFileSync(new URL('
|
|
32
|
+
const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
33
33
|
const start = source.indexOf(" 'node-move':");
|
|
34
34
|
const end = source.indexOf('\n /**', start);
|
|
35
35
|
const runner = source.slice(start, end);
|
|
@@ -38,7 +38,7 @@ describe('deployment node placement', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('forwards --node-name into live and generated custom-instance manifests', () => {
|
|
41
|
-
const source = fs.readFileSync(new URL('
|
|
41
|
+
const source = fs.readFileSync(new URL('../../../../src/cli/run.js', import.meta.url), 'utf8');
|
|
42
42
|
const placements = source.match(/nodeName: options\.nodeName\s*\? Underpost\.deploy\.resolveDeployNode/g) || [];
|
|
43
43
|
expect(placements).to.have.length(2);
|
|
44
44
|
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import UnderpostDockerCompose from '../../../../src/cli/docker-compose.js';
|
|
6
|
+
import { shellHarness } from '../../../support/shell-harness.js';
|
|
7
|
+
|
|
8
|
+
// Everything below writes generated artifacts under the engine tree and drives
|
|
9
|
+
// the docker CLI. Both are replaced: an in-memory file table for the artifacts,
|
|
10
|
+
// and the shell harness for the compose invocations.
|
|
11
|
+
const composeFixture = (files = {}) => {
|
|
12
|
+
const table = new Map(Object.entries(files));
|
|
13
|
+
const written = new Map();
|
|
14
|
+
const removed = [];
|
|
15
|
+
const keys = () => [...table.keys(), ...written.keys()];
|
|
16
|
+
|
|
17
|
+
vi.spyOn(fs, 'existsSync').mockImplementation((filePath) => {
|
|
18
|
+
const key = `${filePath}`;
|
|
19
|
+
if (removed.includes(key)) return false;
|
|
20
|
+
return table.has(key) || written.has(key) || keys().some((entry) => entry.startsWith(`${key}/`));
|
|
21
|
+
});
|
|
22
|
+
vi.spyOn(fs, 'readFileSync').mockImplementation(
|
|
23
|
+
(filePath) => table.get(`${filePath}`) ?? written.get(`${filePath}`) ?? '',
|
|
24
|
+
);
|
|
25
|
+
vi.spyOn(fs, 'writeFileSync').mockImplementation((filePath, value) => written.set(`${filePath}`, `${value}`));
|
|
26
|
+
vi.spyOn(fs, 'mkdirpSync').mockImplementation(() => undefined);
|
|
27
|
+
vi.spyOn(fs, 'copySync').mockImplementation((src, dest) =>
|
|
28
|
+
written.set(`${dest}`, written.get(`${src}`) ?? table.get(`${src}`) ?? ''),
|
|
29
|
+
);
|
|
30
|
+
vi.spyOn(fs, 'removeSync').mockImplementation((filePath) => {
|
|
31
|
+
written.delete(`${filePath}`);
|
|
32
|
+
removed.push(`${filePath}`);
|
|
33
|
+
});
|
|
34
|
+
return { written, removed };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const relative = (path) => `${path}`.replace(`${process.cwd()}/`, '');
|
|
38
|
+
|
|
39
|
+
describe('docker compose stack', () => {
|
|
40
|
+
let harness;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
harness = shellHarness();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
harness.restore();
|
|
48
|
+
vi.restoreAllMocks();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('generated artifacts', () => {
|
|
52
|
+
it('renders the nginx routes, mongo entrypoint, monitoring config and env example', () => {
|
|
53
|
+
const { written } = composeFixture();
|
|
54
|
+
UnderpostDockerCompose.generate({});
|
|
55
|
+
const paths = [...written.keys()].map(relative);
|
|
56
|
+
expect(paths).to.include('docker/nginx/default.conf');
|
|
57
|
+
expect(paths).to.include('docker/mongodb/entrypoint.sh');
|
|
58
|
+
expect(paths).to.include('docker/prometheus/prometheus.yml');
|
|
59
|
+
expect(paths).to.include('docker/grafana/provisioning/datasources/datasource.yml');
|
|
60
|
+
expect(paths).to.include('docker/compose.env.example');
|
|
61
|
+
expect(paths).to.include('docker/compose.app.yml');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('seeds the working env-file from the example only when it is absent', () => {
|
|
65
|
+
const { written } = composeFixture();
|
|
66
|
+
UnderpostDockerCompose.generate({});
|
|
67
|
+
const envPath = [...written.keys()].find((path) => relative(path) === 'docker/compose.env');
|
|
68
|
+
expect(written.get(envPath)).to.equal(written.get(`${envPath}.example`));
|
|
69
|
+
|
|
70
|
+
vi.restoreAllMocks();
|
|
71
|
+
const existing = composeFixture({ [envPath]: 'DB_PASSWORD=real\n' });
|
|
72
|
+
UnderpostDockerCompose.generate({});
|
|
73
|
+
expect(existing.written.has(envPath)).to.equal(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('bakes the deploy id and environment into the app command override', () => {
|
|
77
|
+
const { written } = composeFixture();
|
|
78
|
+
UnderpostDockerCompose.generate({ deployId: 'dd-core', env: 'production' });
|
|
79
|
+
const override = written.get([...written.keys()].find((path) => relative(path) === 'docker/compose.app.yml'));
|
|
80
|
+
expect(override).to.include('dd-core');
|
|
81
|
+
expect(override).to.include('production');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('self-bootstraps a fresh engine for the default deploy', () => {
|
|
85
|
+
expect(UnderpostDockerCompose.appCommand().join(' ')).to.include('underpost new engine');
|
|
86
|
+
expect(UnderpostDockerCompose.appCommand('dd-core', 'production').join(' ')).to.include(
|
|
87
|
+
'underpost start --build --run dd-core production',
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('honours every generated path override', () => {
|
|
92
|
+
const { written } = composeFixture();
|
|
93
|
+
UnderpostDockerCompose.generate({
|
|
94
|
+
nginxConf: 'custom/nginx.conf',
|
|
95
|
+
envFile: 'custom/env',
|
|
96
|
+
appOverride: 'custom/app.yml',
|
|
97
|
+
});
|
|
98
|
+
const paths = [...written.keys()].map(relative);
|
|
99
|
+
expect(paths).to.include('custom/nginx.conf');
|
|
100
|
+
expect(paths).to.include('custom/env');
|
|
101
|
+
expect(paths).to.include('custom/app.yml');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('scrapes the app service in the rendered prometheus config', () => {
|
|
105
|
+
expect(UnderpostDockerCompose.prometheusContent()).to.include('app:4001');
|
|
106
|
+
expect(UnderpostDockerCompose.grafanaDatasourceContent()).to.include('http://prometheus:9090');
|
|
107
|
+
expect(UnderpostDockerCompose.envExampleContent()).to.include('=');
|
|
108
|
+
expect(UnderpostDockerCompose.mongoEntrypointContent()).to.include('BOOTSTRAP_USER_CREATED');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// A named workflow is fully owned by its canonical directory: the generic
|
|
112
|
+
// CLI never writes application-specific content into it.
|
|
113
|
+
it('uses a custom workflow canonical files as-is', () => {
|
|
114
|
+
const base = 'engine-private/conf/dd-core/docker-compose/custom';
|
|
115
|
+
const { written } = composeFixture({
|
|
116
|
+
[UnderpostDockerCompose.resolve(`${base}/docker-compose.yml`)]: 'services: {}\n',
|
|
117
|
+
[UnderpostDockerCompose.resolve(`${base}/compose.env`)]: 'FIXTURE=true\n',
|
|
118
|
+
});
|
|
119
|
+
UnderpostDockerCompose.generate({ deployId: 'dd-core', dockerComposeId: 'custom' });
|
|
120
|
+
expect(written.size).to.equal(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('names the canonical files a custom workflow is missing', () => {
|
|
124
|
+
composeFixture();
|
|
125
|
+
expect(() => UnderpostDockerCompose.generate({ deployId: 'dd-core', dockerComposeId: 'custom' })).to.throw(
|
|
126
|
+
'missing',
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('host installation', () => {
|
|
132
|
+
it('skips a platform the installer does not target', () => {
|
|
133
|
+
const previous = Object.getOwnPropertyDescriptor(process, 'platform');
|
|
134
|
+
Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true });
|
|
135
|
+
try {
|
|
136
|
+
UnderpostDockerCompose.install({});
|
|
137
|
+
expect(harness.calls.length).to.equal(0);
|
|
138
|
+
} finally {
|
|
139
|
+
Object.defineProperty(process, 'platform', previous);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('installs nothing when compose already works', () => {
|
|
144
|
+
composeFixture({ '/etc/redhat-release': 'Rocky Linux release 9\n' });
|
|
145
|
+
harness.route({ match: 'docker compose version', code: 0, stdout: 'v2.29.0\n' });
|
|
146
|
+
UnderpostDockerCompose.install({});
|
|
147
|
+
expect(harness.ran('dnf -y install docker-ce')).to.equal(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('installs the engine and the compose plugin from the official repository', () => {
|
|
151
|
+
composeFixture({ '/etc/redhat-release': 'Rocky Linux release 9\n' });
|
|
152
|
+
harness.route({ match: 'docker compose version', code: 1, stdout: '' });
|
|
153
|
+
UnderpostDockerCompose.install({ user: 'operator' });
|
|
154
|
+
expect(harness.ran('download.docker.com/linux/rhel/docker-ce.repo')).to.equal(true);
|
|
155
|
+
expect(harness.ran('dnf -y install docker-ce docker-ce-cli containerd.io')).to.equal(true);
|
|
156
|
+
expect(harness.ran('systemctl enable --now docker')).to.equal(true);
|
|
157
|
+
expect(harness.ran('usermod -aG docker operator')).to.equal(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('reinstalls under --force and warns on a host that is not RHEL-compatible', () => {
|
|
161
|
+
composeFixture();
|
|
162
|
+
harness.route({ match: 'docker compose version', code: 0, stdout: 'v2.29.0\n' });
|
|
163
|
+
UnderpostDockerCompose.install({ force: true, user: 'root' });
|
|
164
|
+
expect(harness.ran('dnf -y install docker-ce')).to.equal(true);
|
|
165
|
+
// root is already privileged; adding it to the group buys nothing.
|
|
166
|
+
expect(harness.ran('usermod -aG docker')).to.equal(false);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('reset', () => {
|
|
171
|
+
it('tears down containers, volumes and images, then prunes the generated artifacts', () => {
|
|
172
|
+
const { removed } = composeFixture({
|
|
173
|
+
[UnderpostDockerCompose.resolve('docker/nginx/default.conf')]: 'server {}\n',
|
|
174
|
+
[UnderpostDockerCompose.resolve('docker/compose.env')]: 'DB_PASSWORD=real\n',
|
|
175
|
+
});
|
|
176
|
+
UnderpostDockerCompose.reset({});
|
|
177
|
+
expect(harness.ran('down --remove-orphans --volumes --rmi local')).to.equal(true);
|
|
178
|
+
expect(removed.map(relative)).to.include('docker/nginx/default.conf');
|
|
179
|
+
// The working env-file holds credentials, so it survives a plain reset.
|
|
180
|
+
expect(removed.map(relative)).not.to.include('docker/compose.env');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('drops the working env-file too under --force', () => {
|
|
184
|
+
const { removed } = composeFixture({ [UnderpostDockerCompose.resolve('docker/compose.env')]: 'DB=1\n' });
|
|
185
|
+
UnderpostDockerCompose.reset({ force: true });
|
|
186
|
+
expect(removed.map(relative)).to.include('docker/compose.env');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('never prunes the files a custom workflow owns', () => {
|
|
190
|
+
const base = 'engine-private/conf/dd-core/docker-compose/custom';
|
|
191
|
+
const { removed } = composeFixture({
|
|
192
|
+
[UnderpostDockerCompose.resolve(`${base}/docker-compose.yml`)]: 'services: {}\n',
|
|
193
|
+
[UnderpostDockerCompose.resolve(`${base}/compose.env`)]: 'FIXTURE=true\n',
|
|
194
|
+
});
|
|
195
|
+
UnderpostDockerCompose.reset({ deployId: 'dd-core', dockerComposeId: 'custom' });
|
|
196
|
+
expect(harness.ran('down --remove-orphans')).to.equal(true);
|
|
197
|
+
expect(removed).to.deep.equal([]);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('CLI dispatch', () => {
|
|
202
|
+
const run = async (target, options) => {
|
|
203
|
+
composeFixture();
|
|
204
|
+
await UnderpostDockerCompose.API.callback(target, options);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
it('brings the stack up when no action flag is given', async () => {
|
|
208
|
+
await run('', {});
|
|
209
|
+
expect(harness.ran('up -d')).to.equal(true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('rebuilds the images when asked to', async () => {
|
|
213
|
+
await run('', { up: true, build: true });
|
|
214
|
+
expect(harness.ran('up -d --build')).to.equal(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// baseCmd conditionally includes the app override, so it has to exist before
|
|
218
|
+
// the invocation is composed — notably after a reset prunes it.
|
|
219
|
+
it('regenerates the config before composing the invocation', async () => {
|
|
220
|
+
const { written } = composeFixture();
|
|
221
|
+
let generatedBeforeUp = false;
|
|
222
|
+
harness.route({
|
|
223
|
+
match: (command) => {
|
|
224
|
+
if (command.includes('up -d')) generatedBeforeUp = written.size > 0;
|
|
225
|
+
return false;
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
await UnderpostDockerCompose.API.callback('', { up: true });
|
|
229
|
+
expect(generatedBeforeUp).to.equal(true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('stops at the install when nothing else was requested', async () => {
|
|
233
|
+
composeFixture({ '/etc/redhat-release': 'Rocky\n' });
|
|
234
|
+
harness.route({ match: 'docker compose version', code: 0, stdout: 'v2\n' });
|
|
235
|
+
await UnderpostDockerCompose.API.callback('', { install: true });
|
|
236
|
+
expect(harness.ran('up -d')).to.equal(false);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('continues past the install when a lifecycle flag follows it', async () => {
|
|
240
|
+
composeFixture({ '/etc/redhat-release': 'Rocky\n' });
|
|
241
|
+
harness.route({ match: 'docker compose version', code: 0, stdout: 'v2\n' });
|
|
242
|
+
await UnderpostDockerCompose.API.callback('', { install: true, up: true });
|
|
243
|
+
expect(harness.ran('up -d')).to.equal(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('stops at the reset when nothing else was requested', async () => {
|
|
247
|
+
await run('', { reset: true });
|
|
248
|
+
expect(harness.ran('up -d')).to.equal(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('recreates the stack when a reset is followed by an up', async () => {
|
|
252
|
+
await run('', { reset: true, up: true });
|
|
253
|
+
expect(harness.ran('up -d')).to.equal(true);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('routes every remaining lifecycle flag to its compose subcommand', async () => {
|
|
257
|
+
for (const [options, expected] of [
|
|
258
|
+
[{ down: true }, 'down --remove-orphans'],
|
|
259
|
+
[{ down: true, volumes: true }, 'down --remove-orphans --volumes'],
|
|
260
|
+
[{ restart: true }, 'restart'],
|
|
261
|
+
[{ build: true }, 'build --no-cache'],
|
|
262
|
+
[{ pull: true }, 'pull'],
|
|
263
|
+
[{ logs: true }, 'logs -f --tail=200'],
|
|
264
|
+
[{ status: true }, 'ps --format'],
|
|
265
|
+
[{ exec: 'config' }, 'config'],
|
|
266
|
+
]) {
|
|
267
|
+
harness.restore();
|
|
268
|
+
harness = shellHarness();
|
|
269
|
+
await run('', options);
|
|
270
|
+
expect(harness.ran(expected), JSON.stringify(options)).to.equal(true);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('targets a single service where one is named', async () => {
|
|
275
|
+
await run('app', { logs: true });
|
|
276
|
+
expect(harness.ran('logs -f --tail=200 app')).to.equal(true);
|
|
277
|
+
harness.restore();
|
|
278
|
+
harness = shellHarness();
|
|
279
|
+
await run('mongodb', { restart: true });
|
|
280
|
+
expect(harness.ran('restart mongodb')).to.equal(true);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('opens bash in the app service and sh in every other', async () => {
|
|
284
|
+
await run('', { shell: true });
|
|
285
|
+
expect(harness.ran('exec app /bin/bash')).to.equal(true);
|
|
286
|
+
harness.restore();
|
|
287
|
+
harness = shellHarness();
|
|
288
|
+
await run('mongodb', { shell: true });
|
|
289
|
+
expect(harness.ran('exec mongodb /bin/sh')).to.equal(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('writes the config and runs nothing for a bare generate', async () => {
|
|
293
|
+
await run('', { generate: true });
|
|
294
|
+
expect(harness.calls.length).to.equal(0);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('fails the process rather than half-applying a broken invocation', async () => {
|
|
298
|
+
const previousExit = process.exit;
|
|
299
|
+
let exitCode;
|
|
300
|
+
process.exit = (code) => {
|
|
301
|
+
exitCode = code;
|
|
302
|
+
};
|
|
303
|
+
try {
|
|
304
|
+
composeFixture();
|
|
305
|
+
harness.route({ match: 'up -d', throws: new Error('docker daemon unreachable') });
|
|
306
|
+
await UnderpostDockerCompose.API.callback('', { up: true });
|
|
307
|
+
expect(exitCode).to.equal(1);
|
|
308
|
+
} finally {
|
|
309
|
+
process.exit = previousExit;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
// Named import: js-yaml's ESM build exports no default.
|
|
6
|
+
import { load } from 'js-yaml';
|
|
7
|
+
import {
|
|
8
|
+
MONGO_EXPRESS_BASE_URL,
|
|
9
|
+
MONGO_EXPRESS_IMAGE,
|
|
10
|
+
MONGO_EXPRESS_NAME,
|
|
11
|
+
MONGO_EXPRESS_NODE_PORT,
|
|
12
|
+
MONGO_EXPRESS_PORT,
|
|
13
|
+
MONGO_EXPRESS_SECRET_NAME,
|
|
14
|
+
MONGO_EXPRESS_SERVICE_NAME,
|
|
15
|
+
mongoAuthEnabledFactory,
|
|
16
|
+
mongoExpressManifestPathFactory,
|
|
17
|
+
mongoExpressNodePortManifestFactory,
|
|
18
|
+
parseMongodArgs,
|
|
19
|
+
} from '../../../../src/db/mongo/MongoExpress.js';
|
|
20
|
+
|
|
21
|
+
const MONGODB_AUTH_ARGS = load(fs.readFileSync('./manifests/mongodb/statefulset.yaml', 'utf8')).spec.template.spec
|
|
22
|
+
.containers[0].args;
|
|
23
|
+
const MONGODB_4_4_ARGS = load(fs.readFileSync('./manifests/mongodb-4.4/statefulset.yaml', 'utf8')).spec.template.spec
|
|
24
|
+
.containers[0].args;
|
|
25
|
+
|
|
26
|
+
const readYaml = (path) => load(fs.readFileSync(path, 'utf8'));
|
|
27
|
+
const manifestDir = (authEnabled) => mongoExpressManifestPathFactory({ authEnabled });
|
|
28
|
+
const container = (deployment) => deployment.spec.template.spec.containers[0];
|
|
29
|
+
const envEntry = (deployment, name) => container(deployment).env.find((entry) => entry.name === name);
|
|
30
|
+
|
|
31
|
+
describe('mongo-express auth mode selection', () => {
|
|
32
|
+
it('reads authentication from the deployed statefulset, not from the invocation flags', () => {
|
|
33
|
+
// The set that is running decides, whichever flag this invocation carries.
|
|
34
|
+
expect(mongoAuthEnabledFactory({ args: MONGODB_AUTH_ARGS, options: { mongodb4: true } })).to.equal(true);
|
|
35
|
+
expect(mongoAuthEnabledFactory({ args: MONGODB_4_4_ARGS, options: { mongodb: true } })).to.equal(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('falls back to the flags only when no statefulset is deployed', () => {
|
|
39
|
+
expect(mongoAuthEnabledFactory({ args: null, options: { mongodb4: true } })).to.equal(false);
|
|
40
|
+
expect(mongoAuthEnabledFactory({ args: null, options: { mongodb: true } })).to.equal(true);
|
|
41
|
+
expect(mongoAuthEnabledFactory({})).to.equal(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('separates an undeployed statefulset from one that declares no arguments', () => {
|
|
45
|
+
expect(parseMongodArgs('')).to.equal(null);
|
|
46
|
+
expect(parseMongodArgs(' ')).to.equal(null);
|
|
47
|
+
expect(parseMongodArgs('mongodb|--replSet|rs0|--auth|')).to.deep.equal(['--replSet', 'rs0', '--auth']);
|
|
48
|
+
// An empty vector is a deployed server running on its defaults: no --auth, so no credentials,
|
|
49
|
+
// and the flags of this invocation do not get to overrule it.
|
|
50
|
+
expect(mongoAuthEnabledFactory({ args: parseMongodArgs('mongodb|'), options: { mongodb: true } })).to.equal(false);
|
|
51
|
+
expect(mongoAuthEnabledFactory({ args: parseMongodArgs(''), options: { mongodb: true } })).to.equal(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('routes each auth mode to a kustomization that exists', () => {
|
|
55
|
+
for (const authEnabled of [true, false]) {
|
|
56
|
+
const path = manifestDir(authEnabled);
|
|
57
|
+
expect(fs.existsSync(`${path}/kustomization.yaml`), path).to.equal(true);
|
|
58
|
+
}
|
|
59
|
+
expect(manifestDir(true)).to.not.equal(manifestDir(false));
|
|
60
|
+
expect(mongoExpressManifestPathFactory({ underpostRoot: '/npm/underpost', authEnabled: true })).to.equal(
|
|
61
|
+
'/npm/underpost/manifests/deployment/mongo-express',
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('mongo-express manifests', () => {
|
|
67
|
+
const base = manifestDir(true);
|
|
68
|
+
const deployment = readYaml(`${base}/deployment.yaml`);
|
|
69
|
+
const service = readYaml(`${base}/service.yaml`);
|
|
70
|
+
const kustomization = readYaml(`${base}/kustomization.yaml`);
|
|
71
|
+
|
|
72
|
+
it('projects both credential pairs from the statefulset secret', () => {
|
|
73
|
+
// The client owns no credential of its own: the same secret the StatefulSet consumes gates
|
|
74
|
+
// the mongod connection and the UI session.
|
|
75
|
+
for (const name of [
|
|
76
|
+
'ME_CONFIG_MONGODB_ADMINUSERNAME',
|
|
77
|
+
'ME_CONFIG_MONGODB_ADMINPASSWORD',
|
|
78
|
+
'ME_CONFIG_BASICAUTH_USERNAME',
|
|
79
|
+
'ME_CONFIG_BASICAUTH_PASSWORD',
|
|
80
|
+
]) {
|
|
81
|
+
const entry = envEntry(deployment, name);
|
|
82
|
+
expect(entry, name).to.be.an('object');
|
|
83
|
+
expect(entry.value, name).to.equal(undefined);
|
|
84
|
+
expect(entry.valueFrom.secretKeyRef.name, name).to.equal(MONGO_EXPRESS_SECRET_NAME);
|
|
85
|
+
expect(entry.valueFrom.secretKeyRef.key, name).to.be.oneOf(['username', 'password']);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('targets the in-cluster statefulset identity, which is identical under kind, kubeadm and k3s', () => {
|
|
90
|
+
expect(envEntry(deployment, 'ME_CONFIG_MONGODB_SERVER').value).to.equal('mongodb-0.mongodb-service');
|
|
91
|
+
expect(envEntry(deployment, 'ME_CONFIG_MONGODB_PORT').value).to.equal('27017');
|
|
92
|
+
expect(envEntry(deployment, 'ME_CONFIG_MONGODB_ENABLE_ADMIN').value).to.equal('true');
|
|
93
|
+
// The deploy surface reports the URL operators open; the manifest decides what it is.
|
|
94
|
+
expect(envEntry(deployment, 'ME_CONFIG_SITE_BASEURL').value).to.equal(MONGO_EXPRESS_BASE_URL);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('pins the image the pull path preloads', () => {
|
|
98
|
+
expect(container(deployment).image).to.equal(MONGO_EXPRESS_IMAGE);
|
|
99
|
+
expect(MONGO_EXPRESS_IMAGE).to.match(/:.+$/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('keeps the service names and ports the deploy surface reports', () => {
|
|
103
|
+
expect(deployment.metadata.name).to.equal(MONGO_EXPRESS_NAME);
|
|
104
|
+
expect(service.metadata.name).to.equal(MONGO_EXPRESS_SERVICE_NAME);
|
|
105
|
+
expect(service.spec.type).to.equal('ClusterIP');
|
|
106
|
+
expect(service.spec.ports[0].port).to.equal(MONGO_EXPRESS_PORT);
|
|
107
|
+
expect(container(deployment).ports[0].containerPort).to.equal(MONGO_EXPRESS_PORT);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('leaves node exposure out of the kustomization', () => {
|
|
111
|
+
// An admin UI reaches the node network only through `--node-port`.
|
|
112
|
+
const nodePortManifest = mongoExpressNodePortManifestFactory({});
|
|
113
|
+
expect(kustomization.resources).to.deep.equal(['deployment.yaml', 'service.yaml']);
|
|
114
|
+
const nodePortService = readYaml(nodePortManifest);
|
|
115
|
+
expect(nodePortService.spec.type).to.equal('NodePort');
|
|
116
|
+
expect(nodePortService.spec.ports[0].nodePort).to.equal(MONGO_EXPRESS_NODE_PORT);
|
|
117
|
+
expect(nodePortService.spec.selector).to.deep.equal(deployment.spec.selector.matchLabels);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('drops only the credentials a no-auth mongod would reject', () => {
|
|
121
|
+
const overlay = manifestDir(false);
|
|
122
|
+
const overlayKustomization = readYaml(`${overlay}/kustomization.yaml`);
|
|
123
|
+
expect(overlayKustomization.resources).to.deep.equal(['../mongo-express']);
|
|
124
|
+
const patchPath = `${overlay}/${overlayKustomization.patches[0].path}`;
|
|
125
|
+
const deleted = readYaml(patchPath)
|
|
126
|
+
.spec.template.spec.containers[0].env.filter((entry) => entry.$patch === 'delete')
|
|
127
|
+
.map((entry) => entry.name);
|
|
128
|
+
expect(deleted).to.deep.equal([
|
|
129
|
+
'ME_CONFIG_MONGODB_ADMINUSERNAME',
|
|
130
|
+
'ME_CONFIG_MONGODB_ADMINPASSWORD',
|
|
131
|
+
'ME_CONFIG_MONGODB_AUTH_DATABASE',
|
|
132
|
+
]);
|
|
133
|
+
// The UI session stays gated: dropping the server credentials must not publish the client.
|
|
134
|
+
expect(deleted).to.not.include('ME_CONFIG_BASICAUTH_USERNAME');
|
|
135
|
+
expect(deleted).to.not.include('ME_CONFIG_BASICAUTH_PASSWORD');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('mongo-express cluster wiring', () => {
|
|
140
|
+
const clusterSource = fs.readFileSync('./src/cli/cluster.js', 'utf8');
|
|
141
|
+
|
|
142
|
+
it('registers the flag on the cluster command', () => {
|
|
143
|
+
expect(fs.readFileSync('./src/cli/index.js', 'utf8')).to.include("'--mongo-express'");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('deploys the client after the statefulset branches', () => {
|
|
147
|
+
expect(clusterSource).to.include('if (options.mongoExpress)');
|
|
148
|
+
expect(clusterSource.indexOf('options.mongoExpress')).to.be.greaterThan(clusterSource.indexOf('options.mongodb4'));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('keeps one deploy path shared with the service runner', () => {
|
|
152
|
+
const runSource = fs.readFileSync('./src/cli/run.js', 'utf8');
|
|
153
|
+
expect(runSource).to.include('MongoExpress.deploy(');
|
|
154
|
+
expect(runSource).to.not.include('manifests/deployment/mongo-express/deployment.yaml');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import {
|
|
5
|
+
buildCrudController,
|
|
6
|
+
serviceHandler,
|
|
7
|
+
setCrossOriginHeaders,
|
|
8
|
+
} from '../../../../src/server/network/middlewares.js';
|
|
9
|
+
|
|
10
|
+
// Minimal Express doubles: the middleware only sets headers and sends one JSON envelope.
|
|
11
|
+
const requestFrom = (origin) => ({ query: {}, params: {}, path: '/', headers: origin ? { origin } : {} });
|
|
12
|
+
const responseSpy = () => {
|
|
13
|
+
const headers = {};
|
|
14
|
+
const response = {
|
|
15
|
+
headers,
|
|
16
|
+
body: undefined,
|
|
17
|
+
statusCode: 0,
|
|
18
|
+
set: (key, value) => (headers[key] = value),
|
|
19
|
+
setHeader: (key, value) => (headers[key] = value),
|
|
20
|
+
status(code) {
|
|
21
|
+
this.statusCode = code;
|
|
22
|
+
return this;
|
|
23
|
+
},
|
|
24
|
+
json(body) {
|
|
25
|
+
this.body = body;
|
|
26
|
+
return this;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
return response;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const crudService = {
|
|
33
|
+
get: async () => ({ data: [], total: 0 }),
|
|
34
|
+
post: async () => ({}),
|
|
35
|
+
put: async () => ({}),
|
|
36
|
+
delete: async () => ({}),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe('public API cross-origin policy', () => {
|
|
40
|
+
it('answers a browser read with the origin it came from', async () => {
|
|
41
|
+
// The browser client is served from its own origin: a read without this header arrives with
|
|
42
|
+
// status 200 and is then discarded by the same-origin policy, which looks like a broken
|
|
43
|
+
// feature rather than a failed request.
|
|
44
|
+
const response = responseSpy();
|
|
45
|
+
await buildCrudController(crudService).get(requestFrom('http://localhost:8082'), response, {});
|
|
46
|
+
expect(response.headers['Access-Control-Allow-Origin']).to.equal('http://localhost:8082');
|
|
47
|
+
expect(response.headers['Cross-Origin-Resource-Policy']).to.equal('cross-origin');
|
|
48
|
+
expect(response.statusCode).to.equal(200);
|
|
49
|
+
expect(response.body.status).to.equal('success');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('falls back to any origin when the request declares none', async () => {
|
|
53
|
+
const response = responseSpy();
|
|
54
|
+
await buildCrudController(crudService).get(requestFrom(), response, {});
|
|
55
|
+
expect(response.headers['Access-Control-Allow-Origin']).to.equal('*');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('keeps writes same-origin: only reads are public', async () => {
|
|
59
|
+
const response = responseSpy();
|
|
60
|
+
await buildCrudController(crudService).post(requestFrom('http://localhost:8082'), response, {});
|
|
61
|
+
expect(response.headers).to.not.have.property('Access-Control-Allow-Origin');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('carries the same policy on a hand-written read handler', async () => {
|
|
65
|
+
// `setCrossOriginHeaders` is the one place the policy lives, so a custom route opts in with it
|
|
66
|
+
// rather than restating the headers.
|
|
67
|
+
const response = responseSpy();
|
|
68
|
+
await serviceHandler(async () => ({ ok: true }), { crossOrigin: true })(
|
|
69
|
+
requestFrom('https://www.cyberiaonline.com'),
|
|
70
|
+
response,
|
|
71
|
+
{},
|
|
72
|
+
);
|
|
73
|
+
expect(response.headers['Access-Control-Allow-Origin']).to.equal('https://www.cyberiaonline.com');
|
|
74
|
+
|
|
75
|
+
const direct = responseSpy();
|
|
76
|
+
setCrossOriginHeaders(requestFrom('https://www.cyberiaonline.com'), direct);
|
|
77
|
+
expect(direct.headers).to.deep.equal(response.headers);
|
|
78
|
+
});
|
|
79
|
+
});
|