@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,652 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import UnderpostCron, {
|
|
7
|
+
cronCheckoutContextFactory,
|
|
8
|
+
cronDeployIdResolve,
|
|
9
|
+
cronJobYamlFactory,
|
|
10
|
+
loadCronDeployEnv,
|
|
11
|
+
parseList,
|
|
12
|
+
resolveDeployId,
|
|
13
|
+
resolveJobDeployList,
|
|
14
|
+
} from '../../../../src/server/ops/cron.js';
|
|
15
|
+
import Underpost from '../../../../src/index.js';
|
|
16
|
+
import UnderpostImage from '../../../../src/cli/image.js';
|
|
17
|
+
import UnderpostSecret from '../../../../src/cli/secrets.js';
|
|
18
|
+
import { Dns } from '../../../../src/server/network/dns.js';
|
|
19
|
+
import { SCOPE_ENTITLEMENTS } from '../../../../src/server/runtime/config-scope.js';
|
|
20
|
+
import { shellHarness } from '../../../support/shell-harness.js';
|
|
21
|
+
|
|
22
|
+
const CRON_ID_PATH = './engine-private/deploy/dd.cron';
|
|
23
|
+
const ROUTES_PATH = './engine-private/deploy/dd.routes';
|
|
24
|
+
|
|
25
|
+
// Every path this module reads lives in the private repository, and the apply
|
|
26
|
+
// path talks to a live cluster. The suite fixes both: an in-memory file table
|
|
27
|
+
// and the shell harness, so the assertions are over the manifests written and
|
|
28
|
+
// the kubectl vector issued.
|
|
29
|
+
const fileSystemFixture = (files = {}) => {
|
|
30
|
+
const table = new Map(Object.entries(files));
|
|
31
|
+
const written = new Map();
|
|
32
|
+
const directories = [];
|
|
33
|
+
vi.spyOn(fs, 'existsSync').mockImplementation((filePath) => table.has(`${filePath}`) || written.has(`${filePath}`));
|
|
34
|
+
vi.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
|
|
35
|
+
const key = `${filePath}`;
|
|
36
|
+
if (written.has(key)) return written.get(key);
|
|
37
|
+
if (table.has(key)) return table.get(key);
|
|
38
|
+
throw Object.assign(new Error(`ENOENT: ${key}`), { code: 'ENOENT' });
|
|
39
|
+
});
|
|
40
|
+
vi.spyOn(fs, 'writeFileSync').mockImplementation((filePath, value) => written.set(`${filePath}`, `${value}`));
|
|
41
|
+
vi.spyOn(fs, 'mkdirSync').mockImplementation((dir) => directories.push(`${dir}`));
|
|
42
|
+
return { table, written, directories };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const CONF_CRON = {
|
|
46
|
+
jobs: {
|
|
47
|
+
dns: { expression: '*/5 * * * *' },
|
|
48
|
+
backup: { expression: '0 3 * * *' },
|
|
49
|
+
disabled: { enabled: false },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
describe('cron deploy resolution', () => {
|
|
54
|
+
afterEach(() => vi.restoreAllMocks());
|
|
55
|
+
|
|
56
|
+
it('reads the cron deploy id off the private deploy marker', () => {
|
|
57
|
+
fileSystemFixture({ [CRON_ID_PATH]: ' dd-cron \n' });
|
|
58
|
+
expect(cronDeployIdResolve()).to.equal('dd-cron');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('reports no cron deploy when the marker is absent or blank', () => {
|
|
62
|
+
fileSystemFixture({});
|
|
63
|
+
expect(cronDeployIdResolve()).to.equal(null);
|
|
64
|
+
vi.restoreAllMocks();
|
|
65
|
+
fileSystemFixture({ [CRON_ID_PATH]: ' \n' });
|
|
66
|
+
expect(cronDeployIdResolve()).to.equal(null);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('prefers an explicit deploy id over the marker, but never the dd meta id', () => {
|
|
70
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
71
|
+
expect(resolveDeployId('dd-core')).to.equal('dd-core');
|
|
72
|
+
expect(resolveDeployId('dd')).to.equal('dd-cron');
|
|
73
|
+
expect(resolveDeployId('')).to.equal('dd-cron');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('parses a CLI list from either a string or an array', () => {
|
|
77
|
+
expect(parseList('dns, backup ,,')).to.deep.equal(['dns', 'backup']);
|
|
78
|
+
expect(parseList([' dns ', '', 'backup'])).to.deep.equal(['dns', 'backup']);
|
|
79
|
+
expect(parseList(undefined)).to.deep.equal([]);
|
|
80
|
+
expect(parseList(42)).to.deep.equal([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('fans a backup job out to the whole route table', () => {
|
|
84
|
+
fileSystemFixture({ [ROUTES_PATH]: 'dd-core,dd-cyberia', [CRON_ID_PATH]: 'dd-cron' });
|
|
85
|
+
expect(Underpost.cron.getRelatedDeployIdList('backup')).to.equal('dd-core,dd-cyberia');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('falls back to the cron deploy id when no route table is checked out', () => {
|
|
89
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
90
|
+
expect(Underpost.cron.getRelatedDeployIdList('backup')).to.equal('dd-cron');
|
|
91
|
+
expect(Underpost.cron.getRelatedDeployIdList('dns')).to.equal('dd-cron');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('falls back to the built-in cron id when nothing is configured', () => {
|
|
95
|
+
fileSystemFixture({});
|
|
96
|
+
expect(Underpost.cron.getRelatedDeployIdList('dns')).to.equal('dd-cron');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('normalizes an explicit job deploy list to the comma separated shape callbacks split on', () => {
|
|
100
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
101
|
+
expect(resolveJobDeployList('dns', ['dd-a', ' dd-b '])).to.equal('dd-a,dd-b');
|
|
102
|
+
expect(resolveJobDeployList('dns', 'dd')).to.equal('dd-cron');
|
|
103
|
+
expect(resolveJobDeployList('dns', '')).to.equal('dd-cron');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('exposes the registered job handlers', () => {
|
|
107
|
+
expect(Underpost.cron.getJobsIDs()).to.deep.equal(['dns', 'backup', 'vultr']);
|
|
108
|
+
expect(Underpost.cron.JOB.dns).to.equal(Underpost.dns);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('cron deploy environment', () => {
|
|
113
|
+
afterEach(() => vi.restoreAllMocks());
|
|
114
|
+
|
|
115
|
+
it('loads the cron deploy env first and lets no routed deploy overwrite it', () => {
|
|
116
|
+
const previous = { ...process.env };
|
|
117
|
+
fileSystemFixture({
|
|
118
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
119
|
+
[ROUTES_PATH]: 'dd-core',
|
|
120
|
+
'./engine-private/conf/dd-cron/.env.production': 'CRON_FIXTURE_KEY=cron\nSHARED_FIXTURE_KEY=cron\n',
|
|
121
|
+
'./engine-private/conf/dd-core/.env.production': 'SHARED_FIXTURE_KEY=core\nROUTED_FIXTURE_KEY=core\n',
|
|
122
|
+
});
|
|
123
|
+
const previousEnv = process.env.NODE_ENV;
|
|
124
|
+
process.env.NODE_ENV = 'production';
|
|
125
|
+
try {
|
|
126
|
+
loadCronDeployEnv();
|
|
127
|
+
expect(process.env.CRON_FIXTURE_KEY).to.equal('cron');
|
|
128
|
+
expect(process.env.SHARED_FIXTURE_KEY).to.equal('cron');
|
|
129
|
+
expect(process.env.ROUTED_FIXTURE_KEY).to.equal('core');
|
|
130
|
+
} finally {
|
|
131
|
+
process.env = previous;
|
|
132
|
+
if (previousEnv === undefined) delete process.env.NODE_ENV;
|
|
133
|
+
else process.env.NODE_ENV = previousEnv;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('loads nothing at all when neither marker nor route table exists', () => {
|
|
138
|
+
fileSystemFixture({});
|
|
139
|
+
expect(() => loadCronDeployEnv()).not.to.throw();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Regression: the job body is the checkout's own CLI, so a mount `container_t` may not read
|
|
144
|
+
// surfaced as `Cannot find module '/home/dd/engine/bin'` — naming neither the mount nor the policy
|
|
145
|
+
// that denied it, and pinning the pod to the node holding the checkout did not help.
|
|
146
|
+
describe('checkout readability', () => {
|
|
147
|
+
let harness;
|
|
148
|
+
|
|
149
|
+
beforeEach(() => (harness = shellHarness()));
|
|
150
|
+
afterEach(() => harness.restore());
|
|
151
|
+
|
|
152
|
+
const context = ({ mode, label }) => {
|
|
153
|
+
harness.route({ match: 'getenforce', code: 0, stdout: `${mode}\n` });
|
|
154
|
+
harness.route({ match: 'ls -Zd', code: 0, stdout: `${label} /home/dd/engine\n` });
|
|
155
|
+
return cronCheckoutContextFactory();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
it('names the type denying a checkout the pods cannot read', () => {
|
|
159
|
+
expect(context({ mode: 'Enforcing', label: 'unconfined_u:object_r:user_home_t:s0' })).to.deep.equal({
|
|
160
|
+
readable: false,
|
|
161
|
+
type: 'user_home_t',
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('reads the shared container label as usable', () => {
|
|
166
|
+
expect(context({ mode: 'Enforcing', label: 'system_u:object_r:container_file_t:s0' })).to.deep.equal({
|
|
167
|
+
readable: true,
|
|
168
|
+
type: 'container_file_t',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('claims nothing off an enforcing host, where no label denies', () => {
|
|
173
|
+
expect(context({ mode: 'Permissive', label: 'unconfined_u:object_r:user_home_t:s0' })).to.deep.equal({
|
|
174
|
+
readable: true,
|
|
175
|
+
type: '',
|
|
176
|
+
});
|
|
177
|
+
expect(harness.ran('ls -Zd')).to.equal(false);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('CronJob manifest', () => {
|
|
182
|
+
afterEach(() => vi.restoreAllMocks());
|
|
183
|
+
|
|
184
|
+
const render = (overrides = {}) => {
|
|
185
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
186
|
+
return cronJobYamlFactory({
|
|
187
|
+
name: 'dd-cron-dns',
|
|
188
|
+
expression: '*/5 * * * *',
|
|
189
|
+
deployList: 'dd-core',
|
|
190
|
+
jobList: 'dns',
|
|
191
|
+
...overrides,
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
it('sanitizes the name into a DNS subdomain and caps it', () => {
|
|
196
|
+
const yaml = render({ name: '--DD_Cron//DNS--' });
|
|
197
|
+
expect(yaml).to.include('name: dd-cron-dns');
|
|
198
|
+
expect(yaml).not.to.include('name: --DD_Cron');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('caps a long name at the 52 characters a CronJob name allows', () => {
|
|
202
|
+
const yaml = render({ name: 'a'.repeat(80) });
|
|
203
|
+
expect(yaml.match(/^ name: (.*)$/m)[1].length).to.equal(52);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('defaults to the engine image for the running version and the default namespace', () => {
|
|
207
|
+
const yaml = render();
|
|
208
|
+
expect(yaml).to.include(`image: underpost/underpost-engine:${Underpost.version}`);
|
|
209
|
+
expect(yaml).to.include('namespace: default');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('takes a custom image and namespace', () => {
|
|
213
|
+
const yaml = render({ image: 'fixture/image:1', namespace: 'ops' });
|
|
214
|
+
expect(yaml).to.include('image: fixture/image:1');
|
|
215
|
+
expect(yaml).to.include('namespace: ops');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('renders no nodeSelector when the pod is not pinned', () => {
|
|
219
|
+
expect(render()).not.to.include('nodeSelector');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('pins the pod to a node by hostname', () => {
|
|
223
|
+
expect(render({ nodeName: 'worker-1' })).to.include('kubernetes.io/hostname: worker-1');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// The pod takes its environment injected, so it materializes none: `app load` reads a
|
|
227
|
+
// deployment's env file and writes the working tree, and the mirror carries neither.
|
|
228
|
+
it('states the environment on the pod rather than materializing it from a file', () => {
|
|
229
|
+
expect(render()).to.not.include('app load');
|
|
230
|
+
expect(render()).to.include('- name: NODE_ENV\n value: production');
|
|
231
|
+
expect(render({ dev: true })).to.include('- name: NODE_ENV\n value: development');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('injects the cron credential Secret and takes the job straight from the CLI', () => {
|
|
235
|
+
const yaml = render();
|
|
236
|
+
expect(yaml).to.include('name: underpost-cron-env');
|
|
237
|
+
expect(yaml).to.match(/cd \/home\/dd\/engine &&\n\s+node bin cron dd-core dns/);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('forwards every cluster and execution flag to the containerised command', () => {
|
|
241
|
+
const yaml = render({ git: true, dev: true, dryRun: true, k3s: true, kind: true, kubeadm: true });
|
|
242
|
+
expect(yaml).to.include('node bin cron dd-core dns --git --dev --dry-run --k3s --kind --kubeadm');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('runs a pre-script before the cron command', () => {
|
|
246
|
+
expect(render({ cmd: 'npm ci' })).to.include('npm ci &&');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('publishes suspended when asked', () => {
|
|
250
|
+
expect(render({ suspend: true })).to.include('suspend: true');
|
|
251
|
+
expect(render()).to.include('suspend: false');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('host mounts', () => {
|
|
255
|
+
// Every mount is a host path an unprivileged `container_t` pod has to be able to use, so the
|
|
256
|
+
// set is asserted exactly rather than loosely: a mount added without a labeling story is a
|
|
257
|
+
// denial the next Enforcing transition discovers, not a review comment.
|
|
258
|
+
const hostPaths = (yaml) => [...yaml.matchAll(/^\s+path: (\S+)$/gm)].map((match) => match[1]);
|
|
259
|
+
|
|
260
|
+
it('mounts the engine mirror and the metrics textfile directory, and nothing else', () => {
|
|
261
|
+
expect(hostPaths(render())).to.deep.equal(['/opt/engine', '/var/lib/node_exporter/textfile']);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// `container_t` is denied every read under a home tree, so nothing the pods mount may live in
|
|
265
|
+
// one — the checkout included, which is why they mount a mirror of it instead.
|
|
266
|
+
it('mounts no host path out of a home directory', () => {
|
|
267
|
+
const yaml = render();
|
|
268
|
+
expect(yaml).to.not.include('node_modules/underpost');
|
|
269
|
+
expect(hostPaths(yaml).every((hostPath) => !/^\/(home|root)(\/|$)/.test(hostPath))).to.equal(true);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('lands the mirror on the path the image and the job body already resolve against', () => {
|
|
273
|
+
// The container's own layout does not change with the host source: the body still runs
|
|
274
|
+
// `cd /home/dd/engine`, and every relative path the CLI resolves hangs off it.
|
|
275
|
+
const yaml = render();
|
|
276
|
+
expect(yaml).to.include('mountPath: /home/dd/engine');
|
|
277
|
+
expect(yaml).to.include('cd /home/dd/engine');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('carries no hardcoded Node version, which a runtime upgrade would strand', () => {
|
|
281
|
+
expect(render()).to.not.match(/v\d+\.\d+\.\d+\/lib/);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('takes the job credentials from a Secret, optional so a pod without one still schedules', () => {
|
|
285
|
+
const yaml = render();
|
|
286
|
+
expect(yaml).to.include('envFrom:');
|
|
287
|
+
expect(yaml).to.include('secretRef:');
|
|
288
|
+
expect(yaml).to.include('name: underpost-cron-env');
|
|
289
|
+
expect(yaml).to.include('optional: true');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('mounts the mirror as a Directory that must already exist, never DirectoryOrCreate', () => {
|
|
293
|
+
// `DirectoryOrCreate` would let kubelet materialize an empty tree and run the job against
|
|
294
|
+
// no CLI at all; the mirror is a precondition, not something a pod may invent.
|
|
295
|
+
expect(render()).to.match(/path: \/opt\/engine\n\s+type: Directory\n/);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('cron CLI', () => {
|
|
301
|
+
let harness;
|
|
302
|
+
let previousEnv;
|
|
303
|
+
|
|
304
|
+
beforeEach(() => {
|
|
305
|
+
harness = shellHarness();
|
|
306
|
+
vi.spyOn(UnderpostImage.API, 'pullDockerHubImage').mockImplementation(() => undefined);
|
|
307
|
+
// `--apply` projects `underpost-cron-env` from whatever this host's own environment carries
|
|
308
|
+
// within the `cron` scope (see seedEnvValues / SCOPE_ENTITLEMENTS.cron), so any of those keys
|
|
309
|
+
// set for real in the runner's shell would otherwise get staged and show up in `written`
|
|
310
|
+
// alongside the manifests, breaking the assertions below in a way that depends on who is
|
|
311
|
+
// running the suite.
|
|
312
|
+
previousEnv = { ...process.env };
|
|
313
|
+
for (const key of SCOPE_ENTITLEMENTS.cron) delete process.env[key];
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
afterEach(() => {
|
|
317
|
+
harness.restore();
|
|
318
|
+
vi.restoreAllMocks();
|
|
319
|
+
process.env = previousEnv;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('direct execution', () => {
|
|
323
|
+
it('runs each selected job handler with its resolved deploy list', async () => {
|
|
324
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
325
|
+
const callback = vi.spyOn(Dns, 'callback').mockResolvedValue(undefined);
|
|
326
|
+
await UnderpostCron.API.callback('dd-core', 'dns', {});
|
|
327
|
+
expect(callback.mock.calls[0][0]).to.equal('dd-core');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('previews without running the handler under --dry-run', async () => {
|
|
331
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
332
|
+
const callback = vi.spyOn(Dns, 'callback').mockResolvedValue(undefined);
|
|
333
|
+
await UnderpostCron.API.callback('dd-core', 'dns', { dryRun: true });
|
|
334
|
+
expect(callback.mock.calls.length).to.equal(0);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('warns on an unknown job rather than failing the whole run', async () => {
|
|
338
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
339
|
+
const callback = vi.spyOn(Dns, 'callback').mockResolvedValue(undefined);
|
|
340
|
+
await UnderpostCron.API.callback('dd-core', 'not-a-job,dns', {});
|
|
341
|
+
expect(callback.mock.calls.length).to.equal(1);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Placement belongs to the manifest, so passing it to a direct run is a
|
|
345
|
+
// no-op the operator has to be told about rather than a silent nothing.
|
|
346
|
+
it('warns that node placement has no effect on a direct run', async () => {
|
|
347
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
348
|
+
await UnderpostCron.API.callback('dd-core', '', { nodeName: 'worker-1' });
|
|
349
|
+
expect(harness.calls.length).to.equal(0);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe('setup-start', () => {
|
|
354
|
+
const CONF_DIR = './engine-private/conf/dd-cron';
|
|
355
|
+
|
|
356
|
+
it('rewrites the start script to apply exactly the manifests it generates', async () => {
|
|
357
|
+
const { written } = fileSystemFixture({
|
|
358
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
359
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify(CONF_CRON),
|
|
360
|
+
[`${CONF_DIR}/package.json`]: JSON.stringify({ name: 'dd-cron' }),
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
await UnderpostCron.API.callback('dd-cron', '', { setupStart: true });
|
|
364
|
+
|
|
365
|
+
const packageJson = JSON.parse(written.get(`${CONF_DIR}/package.json`));
|
|
366
|
+
expect(packageJson.scripts.start).to.include('./manifests/cronjobs/dd-cron/dd-cron-dns.yaml');
|
|
367
|
+
expect(packageJson.scripts.start).to.include('./manifests/cronjobs/dd-cron/dd-cron-backup.yaml');
|
|
368
|
+
expect(packageJson.scripts.start).not.to.include('dd-cron-disabled.yaml');
|
|
369
|
+
expect(written.has('./manifests/cronjobs/dd-cron/dd-cron-dns.yaml')).to.equal(true);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('restricts the start script to the requested job list', async () => {
|
|
373
|
+
const { written } = fileSystemFixture({
|
|
374
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
375
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify(CONF_CRON),
|
|
376
|
+
[`${CONF_DIR}/package.json`]: JSON.stringify({}),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
await UnderpostCron.API.callback('dd-cron', 'dns', { setupStart: true });
|
|
380
|
+
|
|
381
|
+
expect(JSON.parse(written.get(`${CONF_DIR}/package.json`)).scripts.start).not.to.include('dd-cron-backup');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('stops before touching anything when no deploy id resolves', async () => {
|
|
385
|
+
const { written } = fileSystemFixture({});
|
|
386
|
+
await UnderpostCron.API.callback('', '', { setupStart: true });
|
|
387
|
+
expect(written.size).to.equal(0);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('stops when the deploy declares no cron conf', async () => {
|
|
391
|
+
const { written } = fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
392
|
+
await UnderpostCron.API.callback('dd-cron', '', { setupStart: true });
|
|
393
|
+
expect(written.size).to.equal(0);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('stops when the cron conf declares no jobs', async () => {
|
|
397
|
+
const { written } = fileSystemFixture({
|
|
398
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
399
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify({ jobs: {} }),
|
|
400
|
+
});
|
|
401
|
+
await UnderpostCron.API.callback('dd-cron', '', { setupStart: true });
|
|
402
|
+
expect(written.size).to.equal(0);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('stops when the job filter matches no enabled job', async () => {
|
|
406
|
+
const { written } = fileSystemFixture({
|
|
407
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
408
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify(CONF_CRON),
|
|
409
|
+
});
|
|
410
|
+
await UnderpostCron.API.callback('dd-cron', 'disabled', { setupStart: true });
|
|
411
|
+
expect(written.size).to.equal(0);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('still generates the manifests when the deploy carries no package.json', async () => {
|
|
415
|
+
const { written } = fileSystemFixture({
|
|
416
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
417
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify(CONF_CRON),
|
|
418
|
+
});
|
|
419
|
+
await UnderpostCron.API.callback('dd-cron', '', { setupStart: true });
|
|
420
|
+
expect(written.has('./manifests/cronjobs/dd-cron/dd-cron-dns.yaml')).to.equal(true);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
describe('manifest generation and apply', () => {
|
|
425
|
+
const CONF_DIR = './engine-private/conf/dd-cron';
|
|
426
|
+
|
|
427
|
+
const generate = async (options = {}) => {
|
|
428
|
+
const fixture = fileSystemFixture({
|
|
429
|
+
[CRON_ID_PATH]: 'dd-cron',
|
|
430
|
+
[`${CONF_DIR}/conf.cron.json`]: JSON.stringify(CONF_CRON),
|
|
431
|
+
});
|
|
432
|
+
await UnderpostCron.API.callback('dd-cron', options.jobList ?? '', {
|
|
433
|
+
generateK8sCronjobs: true,
|
|
434
|
+
...options,
|
|
435
|
+
});
|
|
436
|
+
return fixture;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
it('writes one manifest per enabled job and applies nothing without --apply', async () => {
|
|
440
|
+
const { written, directories } = await generate();
|
|
441
|
+
expect(directories).to.include('./manifests/cronjobs/dd-cron');
|
|
442
|
+
expect([...written.keys()]).to.deep.equal([
|
|
443
|
+
'./manifests/cronjobs/dd-cron/dd-cron-dns.yaml',
|
|
444
|
+
'./manifests/cronjobs/dd-cron/dd-cron-backup.yaml',
|
|
445
|
+
]);
|
|
446
|
+
expect(harness.calls.length).to.equal(0);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('stops when no deploy id resolves', async () => {
|
|
450
|
+
fileSystemFixture({});
|
|
451
|
+
await UnderpostCron.API.callback('', '', { generateK8sCronjobs: true });
|
|
452
|
+
expect(harness.calls.length).to.equal(0);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('stops when the cron conf is missing', async () => {
|
|
456
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron' });
|
|
457
|
+
await UnderpostCron.API.callback('dd-cron', '', { generateK8sCronjobs: true });
|
|
458
|
+
expect(harness.calls.length).to.equal(0);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('stops when the cron conf declares no jobs', async () => {
|
|
462
|
+
fileSystemFixture({ [CRON_ID_PATH]: 'dd-cron', [`${CONF_DIR}/conf.cron.json`]: JSON.stringify({ jobs: {} }) });
|
|
463
|
+
await UnderpostCron.API.callback('dd-cron', '', { generateK8sCronjobs: true });
|
|
464
|
+
expect(harness.calls.length).to.equal(0);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('stops when the job filter matches nothing declared', async () => {
|
|
468
|
+
const { written } = await generate({ jobList: 'not-a-job' });
|
|
469
|
+
expect(written.size).to.equal(0);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('replaces the published CronJobs and loads the default image before applying', async () => {
|
|
473
|
+
const pull = vi.spyOn(UnderpostImage.API, 'pullDockerHubImage');
|
|
474
|
+
await generate({ apply: true, kubeadm: true });
|
|
475
|
+
expect(harness.ran('kubectl delete cronjob dd-cron-dns --namespace=default --ignore-not-found')).to.equal(true);
|
|
476
|
+
expect(harness.ran('kubectl apply -f ./manifests/cronjobs/dd-cron/dd-cron-dns.yaml')).to.equal(true);
|
|
477
|
+
expect(pull.mock.calls[0][0]).to.include({ dockerhubImage: 'underpost', kubeadm: true });
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('leaves image loading alone when a custom image is pinned', async () => {
|
|
481
|
+
const pull = vi.spyOn(UnderpostImage.API, 'pullDockerHubImage');
|
|
482
|
+
await generate({ apply: true, image: 'fixture/image:1' });
|
|
483
|
+
expect(pull.mock.calls.length).to.equal(0);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('copies the engine mirror into the kind worker before applying on a kind cluster', async () => {
|
|
487
|
+
await generate({ apply: true, kind: true });
|
|
488
|
+
expect(harness.ran('docker cp /opt/engine kind-worker:/opt/engine')).to.equal(true);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// The pods execute the mirror, so a stale one is the same outage as an unlabeled one. `--delete`
|
|
492
|
+
// because it is an output: a file the checkout dropped must not survive in the tree they run.
|
|
493
|
+
it('refreshes the mirror from the checkout before labeling it', async () => {
|
|
494
|
+
await generate({ apply: true, kubeadm: true });
|
|
495
|
+
const rsync = harness.calls.find((command) => command.includes('rsync'));
|
|
496
|
+
expect(rsync).to.include('--delete');
|
|
497
|
+
expect(rsync).to.include(`'${process.cwd()}/'`);
|
|
498
|
+
expect(rsync).to.include("'/opt/engine/'");
|
|
499
|
+
expect(harness.calls.indexOf(rsync)).to.be.lessThan(
|
|
500
|
+
harness.calls.findIndex((command) => command.includes('restorecon')),
|
|
501
|
+
);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// The mirror carries the shared container label, so every `container_t` process on the node
|
|
505
|
+
// can read it. An allowlist is what keeps a repository that gains a key from leaking it.
|
|
506
|
+
it('mirrors an allowlist, closed by a trailing exclude of everything else', async () => {
|
|
507
|
+
await generate({ apply: true, kubeadm: true });
|
|
508
|
+
const rules = [...harness.calls.find((command) => command.includes('rsync')).matchAll(/'(--\S+?)'/g)].map(
|
|
509
|
+
([, rule]) => rule,
|
|
510
|
+
);
|
|
511
|
+
expect(rules.at(-1)).to.equal('--exclude=*');
|
|
512
|
+
// rsync takes the first matching rule, so a subtraction placed after the include that
|
|
513
|
+
// covers its parent never fires.
|
|
514
|
+
for (const asset of ['/src/client/public/**', '/src/runtime/engine-cyberia/**'])
|
|
515
|
+
expect(rules.indexOf(`--exclude=${asset}`), asset).to.be.lessThan(rules.indexOf('--include=/src/**'));
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('narrows engine-private to the documents a job body resolves, and no environment', async () => {
|
|
519
|
+
await generate({ apply: true, kubeadm: true });
|
|
520
|
+
const rsync = harness.calls.find((command) => command.includes('rsync'));
|
|
521
|
+
// `cron` reads deploy/dd.cron, deploy/dd.routes and the conf documents; every value those
|
|
522
|
+
// documents reference arrives injected, so no env file is mirrored at any environment.
|
|
523
|
+
expect(rsync).to.include("'--include=/engine-private/conf/*/conf.*.json'");
|
|
524
|
+
expect(rsync).to.include("'--include=/engine-private/deploy/dd.cron'");
|
|
525
|
+
expect(rsync).to.include("'--include=/engine-private/deploy/dd.routes'");
|
|
526
|
+
expect(rsync).to.not.include('.env');
|
|
527
|
+
// `conf/<id>` also holds per-deploy key pairs, so neither half may be globbed.
|
|
528
|
+
for (const glob of ['/engine-private/**', '/engine-private/conf/**', '/engine-private/deploy/**'])
|
|
529
|
+
expect(rsync, glob).to.not.include(glob);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// The mirror is readable by every `container_t` process on the node, so no key may reach it.
|
|
533
|
+
// Every deploy's env file is that deploy's whole credential set, and the mirror is readable by
|
|
534
|
+
// every container on the node, so none of them is mirrored at any environment.
|
|
535
|
+
it('mirrors no deployment environment, whichever environment the pods run', async () => {
|
|
536
|
+
for (const dev of [false, true]) {
|
|
537
|
+
harness.calls.length = 0;
|
|
538
|
+
const { written } = await generate({ apply: true, kubeadm: true, dev });
|
|
539
|
+
expect(
|
|
540
|
+
harness.calls.find((command) => command.includes('rsync')),
|
|
541
|
+
`dev=${dev}`,
|
|
542
|
+
).to.not.include('.env');
|
|
543
|
+
for (const manifest of written.values())
|
|
544
|
+
expect(manifest, `dev=${dev}`).to.include(`value: ${dev ? 'development' : 'production'}`);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('mirrors no key material, and mounts the connection key as a Secret instead', async () => {
|
|
549
|
+
const { written } = await generate({ apply: true, kubeadm: true });
|
|
550
|
+
const rsync = harness.calls.find((command) => command.includes('rsync'));
|
|
551
|
+
for (const material of ['id_rsa', 'conf.users.json', 'ipfs-cluster-secret', 'mariadb-password'])
|
|
552
|
+
expect(rsync, material).to.not.include(material);
|
|
553
|
+
|
|
554
|
+
for (const manifest of written.values()) {
|
|
555
|
+
expect(manifest).to.include('secretName: underpost-ssh-key');
|
|
556
|
+
expect(manifest).to.include('defaultMode: 0400');
|
|
557
|
+
expect(manifest).to.include('mountPath: /etc/underpost/secrets/ssh');
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('projects the key Secret alongside the credential Secret', async () => {
|
|
562
|
+
const applied = [];
|
|
563
|
+
vi.spyOn(UnderpostSecret.API, 'applyIfPresent').mockImplementation((name) => (applied.push(name), true));
|
|
564
|
+
await generate({ apply: true, kubeadm: true });
|
|
565
|
+
expect(applied).to.deep.equal(['underpost-cron-env', 'underpost-ssh-key']);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// A nodeSelector naming an unregistered node leaves every Job Pending at its
|
|
569
|
+
// next fire, with nothing in the manifest to say why.
|
|
570
|
+
it('warns about an unregistered target node but still applies', async () => {
|
|
571
|
+
harness.route({ match: 'kubectl get node', code: 0, stdout: '' });
|
|
572
|
+
await generate({ apply: true, nodeName: 'worker-9' });
|
|
573
|
+
expect(harness.ran('kubectl get node worker-9 -o name')).to.equal(true);
|
|
574
|
+
expect(harness.ran('kubectl apply -f')).to.equal(true);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('accepts a registered target node', async () => {
|
|
578
|
+
harness.route({ match: 'kubectl get node', code: 0, stdout: 'node/worker-1\n' });
|
|
579
|
+
await generate({ apply: true, nodeName: 'worker-1' });
|
|
580
|
+
expect(harness.ran('kubectl apply -f')).to.equal(true);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// Regression: an unpinned pod landed on a node whose /home/dd/engine was not the checkout,
|
|
584
|
+
// and the job body died on `Cannot find module '/home/dd/engine/bin'` before it could report
|
|
585
|
+
// anything of its own. A hostPath mount is node-local, so the placement has to be too.
|
|
586
|
+
it('pins the pods it publishes to the node whose checkout they mount', async () => {
|
|
587
|
+
const local = os.hostname();
|
|
588
|
+
harness.route({ match: `kubectl get node ${local}`, code: 0, stdout: `node/${local}\n` });
|
|
589
|
+
const { written } = await generate({ apply: true, kubeadm: true });
|
|
590
|
+
for (const manifest of written.values()) expect(manifest).to.include(`kubernetes.io/hostname: ${local}`);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('leaves a manifest it only writes unpinned, and reads no cluster to write it', async () => {
|
|
594
|
+
const { written } = await generate();
|
|
595
|
+
for (const manifest of written.values()) expect(manifest).to.not.include('nodeSelector');
|
|
596
|
+
expect(harness.calls.length).to.equal(0);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('leaves the pods unpinned when the cluster does not know this machine as a node', async () => {
|
|
600
|
+
harness.route({ match: 'kubectl get node', code: 0, stdout: '' });
|
|
601
|
+
const { written } = await generate({ apply: true, kubeadm: true });
|
|
602
|
+
for (const manifest of written.values()) expect(manifest).to.not.include('nodeSelector');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// The context is rendered into the pod's own command, so a run that names none used to
|
|
606
|
+
// publish a CronJob whose body addressed no cluster at all.
|
|
607
|
+
it('reads the cluster context off the cluster when the run names none', async () => {
|
|
608
|
+
harness.route({
|
|
609
|
+
match: 'kubectl get nodes',
|
|
610
|
+
code: 0,
|
|
611
|
+
stdout: 'NAME STATUS ROLES AGE VERSION\nnode-a Ready control-plane 1d v1.30.5+k3s1\n',
|
|
612
|
+
});
|
|
613
|
+
const { written } = await generate({ apply: true });
|
|
614
|
+
for (const manifest of written.values()) expect(manifest).to.include('--k3s');
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('never overrides a context the run named', async () => {
|
|
618
|
+
harness.route({
|
|
619
|
+
match: 'kubectl get nodes',
|
|
620
|
+
code: 0,
|
|
621
|
+
stdout: 'NAME STATUS ROLES AGE VERSION\nnode-a Ready control-plane 1d v1.30.5+k3s1\n',
|
|
622
|
+
});
|
|
623
|
+
const { written } = await generate({ apply: true, kubeadm: true });
|
|
624
|
+
for (const manifest of written.values()) {
|
|
625
|
+
expect(manifest).to.include('--kubeadm');
|
|
626
|
+
expect(manifest).to.not.include('--k3s');
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// Reported, not repaired: relabeling the operator's live working tree is not this command's
|
|
631
|
+
// call, so a denied checkout still publishes its manifests.
|
|
632
|
+
it('publishes even when its pods cannot read the checkout they mount', async () => {
|
|
633
|
+
harness.route({ match: 'getenforce', code: 0, stdout: 'Enforcing\n' });
|
|
634
|
+
harness.route({ match: 'ls -Zd', code: 0, stdout: 'unconfined_u:object_r:user_home_t:s0 /home/dd/engine\n' });
|
|
635
|
+
await generate({ apply: true, kubeadm: true });
|
|
636
|
+
expect(harness.ran('kubectl apply -f')).to.equal(true);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('creates one immediate Job per published CronJob', async () => {
|
|
640
|
+
harness.route({ match: 'kubectl get cronjob', code: 0, stdout: 'cronjob.batch/dd-cron-dns\n' });
|
|
641
|
+
await generate({ apply: true, createJobNow: true });
|
|
642
|
+
expect(harness.count('kubectl create job')).to.equal(2);
|
|
643
|
+
expect(harness.ran('--from=cronjob/dd-cron-dns -n default')).to.equal(true);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('skips the immediate Job for a CronJob that was never published', async () => {
|
|
647
|
+
harness.route({ match: 'kubectl get cronjob', code: 0, stdout: '' });
|
|
648
|
+
await generate({ createJobNow: true });
|
|
649
|
+
expect(harness.ran('kubectl create job')).to.equal(false);
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
});
|