@underpostnet/cyberia 3.2.80
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 +14 -0
- package/.env.example +144 -0
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
- package/.github/workflows/engine-cyberia.cd.yml +157 -0
- package/.github/workflows/engine-cyberia.ci.yml +125 -0
- package/.github/workflows/ghpkg.ci.yml +160 -0
- package/.github/workflows/gitlab.ci.yml +20 -0
- package/.github/workflows/hardhat.ci.yml +82 -0
- package/.github/workflows/npmpkg.ci.yml +126 -0
- package/.github/workflows/publish.ci.yml +87 -0
- package/.github/workflows/publish.cyberia.ci.yml +79 -0
- package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
- package/.github/workflows/release.cd.yml +46 -0
- package/.nycrc +9 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +26 -0
- package/CHANGELOG.md +2516 -0
- package/CLI-HELP.md +1099 -0
- package/Dockerfile +161 -0
- package/Dockerfile.dev +161 -0
- package/Dockerfile.test +165 -0
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/baremetal/commission-workflows.json +211 -0
- package/baremetal/packer-workflows.json +24 -0
- package/bin/build.js +261 -0
- package/bin/build.template.js +33 -0
- package/bin/cyberia.js +5627 -0
- package/bin/deploy.js +1547 -0
- package/bin/index.js +5627 -0
- package/bump.config.js +27 -0
- package/compose.env +144 -0
- package/conf.js +952 -0
- package/deployment.yaml +418 -0
- package/docker-compose.yml +434 -0
- package/examples/static-page/README.md +150 -0
- package/examples/static-page/ssr-components/CustomPage.js +554 -0
- package/examples/static-page/static-config-example.json +57 -0
- package/hardhat/.env.example +31 -0
- package/hardhat/contracts/ObjectLayerToken.sol +391 -0
- package/hardhat/deployments/.gitkeep +0 -0
- package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
- package/hardhat/hardhat.config.js +136 -0
- package/hardhat/ignition/modules/.gitkeep +0 -0
- package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
- package/hardhat/networks/besu-object-layer.network.json +138 -0
- package/hardhat/package-lock.json +2871 -0
- package/hardhat/package.json +43 -0
- package/hardhat/scripts/deployObjectLayerToken.js +98 -0
- package/hardhat/test/ObjectLayerToken.js +696 -0
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/ipfs/configure-ipfs.sh +13 -0
- package/jsconfig.json +9 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
- package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/fastapi/initial_data.sh +8 -0
- package/manifests/deployment/kafka/deployment.yaml +67 -0
- package/manifests/deployment/mongo-express/deployment.yaml +62 -0
- package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
- package/manifests/deployment/playwright/deployment.yaml +52 -0
- package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
- package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
- package/manifests/envoy-service-nodeport.yaml +22 -0
- package/manifests/grafana/deployment.yaml +64 -0
- package/manifests/grafana/kustomization.yaml +7 -0
- package/manifests/grafana/pvc.yaml +12 -0
- package/manifests/grafana/service.yaml +14 -0
- package/manifests/grafana/storage-class.yaml +9 -0
- package/manifests/ipfs/configmap.yaml +64 -0
- package/manifests/ipfs/headless-service.yaml +35 -0
- package/manifests/ipfs/kustomization.yaml +8 -0
- package/manifests/ipfs/statefulset.yaml +149 -0
- package/manifests/ipfs/storage-class.yaml +9 -0
- package/manifests/kind-config-dev.yaml +20 -0
- package/manifests/kind-config.yaml +12 -0
- package/manifests/kubeadm-calico-config.yaml +14 -0
- package/manifests/letsencrypt-prod.yaml +14 -0
- package/manifests/lxd/lxd-admin-profile.yaml +26 -0
- package/manifests/lxd/lxd-preseed.yaml +30 -0
- package/manifests/mariadb/kustomization.yaml +9 -0
- package/manifests/mariadb/pv.yaml +12 -0
- package/manifests/mariadb/pvc.yaml +10 -0
- package/manifests/mariadb/service.yaml +10 -0
- package/manifests/mariadb/statefulset.yaml +56 -0
- package/manifests/mariadb/storage-class.yaml +10 -0
- package/manifests/mongodb/headless-service.yaml +10 -0
- package/manifests/mongodb/kustomization.yaml +11 -0
- package/manifests/mongodb/pv-pvc.yaml +59 -0
- package/manifests/mongodb/statefulset.yaml +113 -0
- package/manifests/mongodb/storage-class.yaml +9 -0
- package/manifests/mongodb-4.4/headless-service.yaml +10 -0
- package/manifests/mongodb-4.4/kustomization.yaml +9 -0
- package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
- package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
- package/manifests/mongodb-4.4/statefulset.yaml +79 -0
- package/manifests/mongodb-4.4/storage-class.yaml +9 -0
- package/manifests/mysql/kustomization.yaml +7 -0
- package/manifests/mysql/pv-pvc.yaml +27 -0
- package/manifests/mysql/statefulset.yaml +55 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/manifests/pv-pvc-dd.yaml +34 -0
- package/manifests/valkey/kustomization.yaml +7 -0
- package/manifests/valkey/service.yaml +10 -0
- package/manifests/valkey/statefulset.yaml +37 -0
- package/manifests/valkey/valkey-nodeport.yaml +17 -0
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +128 -0
- package/nodemon.json +6 -0
- package/package.json +101 -0
- package/packer/images/Rocky9Amd64/Makefile +62 -0
- package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
- package/packer/images/Rocky9Amd64/README.md +122 -0
- package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
- package/packer/images/Rocky9Arm64/Makefile +69 -0
- package/packer/images/Rocky9Arm64/README.md +122 -0
- package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
- package/packer/scripts/fuse-nbd +64 -0
- package/packer/scripts/fuse-tar-root +63 -0
- package/proxy.yaml +189 -0
- package/pv-pvc.yaml +164 -0
- package/scripts/device-scan.sh +65 -0
- package/scripts/disk-clean.sh +182 -0
- package/scripts/disk-devices.sh +13 -0
- package/scripts/gen-fqdns.sh +14 -0
- package/scripts/gpu-diag.sh +19 -0
- package/scripts/ip-info.sh +118 -0
- package/scripts/ipxe-setup.sh +200 -0
- package/scripts/k3s-node-setup.sh +124 -0
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/lxd-vm-setup.sh +208 -0
- package/scripts/maas-nat-firewalld.sh +145 -0
- package/scripts/maas-setup.sh +135 -0
- package/scripts/maas-upload-boot-resource.sh +183 -0
- package/scripts/nat-iptables.sh +111 -0
- package/scripts/nvim.sh +91 -0
- package/scripts/packer-init-vars-file.sh +40 -0
- package/scripts/packer-setup.sh +289 -0
- package/scripts/ports-ls.sh +33 -0
- package/scripts/quick-tftp.sh +19 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/scripts/rocky-kickstart.sh +986 -0
- package/scripts/rocky-pwa.sh +200 -0
- package/scripts/rocky-setup.sh +106 -0
- package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
- package/scripts/ssl.sh +170 -0
- package/scripts/test-monitor.sh +248 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
- package/src/api/core/core.controller.js +8 -0
- package/src/api/core/core.model.js +11 -0
- package/src/api/core/core.router.js +23 -0
- package/src/api/core/core.service.js +30 -0
- package/src/api/crypto/crypto.controller.js +10 -0
- package/src/api/crypto/crypto.model.js +23 -0
- package/src/api/crypto/crypto.router.js +23 -0
- package/src/api/crypto/crypto.service.js +64 -0
- package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
- package/src/api/cyberia-action/cyberia-action.model.js +79 -0
- package/src/api/cyberia-action/cyberia-action.router.js +21 -0
- package/src/api/cyberia-action/cyberia-action.service.js +58 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
- package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
- package/src/api/cyberia-map/cyberia-map.model.js +33 -0
- package/src/api/cyberia-map/cyberia-map.router.js +19 -0
- package/src/api/cyberia-map/cyberia-map.service.js +76 -0
- package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
- package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
- package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +21 -0
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
- package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
- package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/default/default.controller.js +6 -0
- package/src/api/default/default.model.js +20 -0
- package/src/api/default/default.router.js +21 -0
- package/src/api/default/default.service.js +42 -0
- package/src/api/document/document.controller.js +9 -0
- package/src/api/document/document.model.js +95 -0
- package/src/api/document/document.router.js +32 -0
- package/src/api/document/document.service.js +633 -0
- package/src/api/file/file.controller.js +15 -0
- package/src/api/file/file.model.js +127 -0
- package/src/api/file/file.ref.json +39 -0
- package/src/api/file/file.router.js +36 -0
- package/src/api/file/file.service.js +499 -0
- package/src/api/instance/instance.controller.js +8 -0
- package/src/api/instance/instance.model.js +34 -0
- package/src/api/instance/instance.router.js +27 -0
- package/src/api/instance/instance.service.js +75 -0
- package/src/api/ipfs/ipfs.controller.js +8 -0
- package/src/api/ipfs/ipfs.model.js +77 -0
- package/src/api/ipfs/ipfs.router.js +26 -0
- package/src/api/ipfs/ipfs.service.js +145 -0
- package/src/api/object-layer/object-layer.controller.js +18 -0
- package/src/api/object-layer/object-layer.model.js +200 -0
- package/src/api/object-layer/object-layer.router.js +729 -0
- package/src/api/object-layer/object-layer.service.js +894 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
- package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
- package/src/api/test/test.controller.js +14 -0
- package/src/api/test/test.model.js +14 -0
- package/src/api/test/test.router.js +23 -0
- package/src/api/test/test.service.js +35 -0
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +100 -0
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.build.js +16 -0
- package/src/api/user/user.controller.js +35 -0
- package/src/api/user/user.model.js +142 -0
- package/src/api/user/user.router.js +367 -0
- package/src/api/user/user.service.js +582 -0
- package/src/api.js +23 -0
- package/src/cli/baremetal.js +3947 -0
- package/src/cli/cloud-init.js +673 -0
- package/src/cli/cluster.js +1395 -0
- package/src/cli/db.js +1533 -0
- package/src/cli/deploy.js +1542 -0
- package/src/cli/docker-compose.js +802 -0
- package/src/cli/env.js +177 -0
- package/src/cli/fs.js +380 -0
- package/src/cli/image.js +389 -0
- package/src/cli/index.js +1059 -0
- package/src/cli/ipfs.js +182 -0
- package/src/cli/kickstart.js +271 -0
- package/src/cli/kubectl.js +214 -0
- package/src/cli/lxd.js +1232 -0
- package/src/cli/monitor.js +659 -0
- package/src/cli/release.js +595 -0
- package/src/cli/repository.js +1969 -0
- package/src/cli/run.js +3326 -0
- package/src/cli/secrets.js +151 -0
- package/src/cli/ssh.js +975 -0
- package/src/cli/static.js +653 -0
- package/src/cli/system.js +332 -0
- package/src/cli/test.js +176 -0
- package/src/client/Cryptokoyn.index.js +30 -0
- package/src/client/CyberiaPortal.index.js +31 -0
- package/src/client/Default.index.js +77 -0
- package/src/client/Itemledger.index.js +35 -0
- package/src/client/Underpost.index.js +31 -0
- package/src/client/components/core/404.js +20 -0
- package/src/client/components/core/500.js +20 -0
- package/src/client/components/core/Account.js +630 -0
- package/src/client/components/core/AgGrid.js +238 -0
- package/src/client/components/core/Alert.js +76 -0
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/Auth.js +353 -0
- package/src/client/components/core/Badge.js +26 -0
- package/src/client/components/core/Blockchain.js +41 -0
- package/src/client/components/core/Blog.js +9 -0
- package/src/client/components/core/BtnIcon.js +109 -0
- package/src/client/components/core/CalendarCore.js +437 -0
- package/src/client/components/core/Chat.js +62 -0
- package/src/client/components/core/ClientEvents.js +163 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/CommonJs.js +1014 -0
- package/src/client/components/core/Content.js +349 -0
- package/src/client/components/core/Css.js +1134 -0
- package/src/client/components/core/CssCore.js +881 -0
- package/src/client/components/core/D3Chart.js +44 -0
- package/src/client/components/core/Docs.js +478 -0
- package/src/client/components/core/DropDown.js +321 -0
- package/src/client/components/core/EventBus.js +96 -0
- package/src/client/components/core/EventsUI.js +130 -0
- package/src/client/components/core/FileExplorer.js +1406 -0
- package/src/client/components/core/FullScreen.js +201 -0
- package/src/client/components/core/Input.js +537 -0
- package/src/client/components/core/Keyboard.js +80 -0
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +152 -0
- package/src/client/components/core/LogIn.js +212 -0
- package/src/client/components/core/LogOut.js +72 -0
- package/src/client/components/core/Logger.js +22 -0
- package/src/client/components/core/Modal.js +2956 -0
- package/src/client/components/core/NotificationManager.js +80 -0
- package/src/client/components/core/Pagination.js +252 -0
- package/src/client/components/core/Panel.js +1308 -0
- package/src/client/components/core/PanelForm.js +915 -0
- package/src/client/components/core/Polyhedron.js +947 -0
- package/src/client/components/core/PublicProfile.js +895 -0
- package/src/client/components/core/Recover.js +211 -0
- package/src/client/components/core/Responsive.js +146 -0
- package/src/client/components/core/RichText.js +33 -0
- package/src/client/components/core/Router.js +523 -0
- package/src/client/components/core/Scroll.js +76 -0
- package/src/client/components/core/SearchBox.js +1007 -0
- package/src/client/components/core/SignUp.js +153 -0
- package/src/client/components/core/SocketIo.js +163 -0
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +161 -0
- package/src/client/components/core/ToggleSwitch.js +89 -0
- package/src/client/components/core/ToolTip.js +78 -0
- package/src/client/components/core/Translate.js +694 -0
- package/src/client/components/core/Validator.js +140 -0
- package/src/client/components/core/VanillaJs.js +485 -0
- package/src/client/components/core/Wallet.js +95 -0
- package/src/client/components/core/WebComponent.js +44 -0
- package/src/client/components/core/Webhook.js +58 -0
- package/src/client/components/core/Worker.js +313 -0
- package/src/client/components/core/windowGetDimensions.js +269 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
- package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
- package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
- package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
- package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
- package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
- package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
- package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
- package/src/client/components/default/AppShellDefault.js +765 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/CommonDefault.js +29 -0
- package/src/client/components/default/CssDefault.js +27 -0
- package/src/client/components/default/LogInDefault.js +16 -0
- package/src/client/components/default/LogOutDefault.js +12 -0
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +16 -0
- package/src/client/components/default/SignUpDefault.js +11 -0
- package/src/client/components/default/SocketIoDefault.js +6 -0
- package/src/client/components/default/TranslateDefault.js +7 -0
- package/src/client/components/itemledger/AppShellItemledger.js +353 -0
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/CssItemledger.js +212 -0
- package/src/client/components/itemledger/LogInItemledger.js +17 -0
- package/src/client/components/itemledger/LogOutItemledger.js +12 -0
- package/src/client/components/itemledger/RouterItemledger.js +38 -0
- package/src/client/components/itemledger/SettingsItemledger.js +16 -0
- package/src/client/components/itemledger/SignUpItemledger.js +11 -0
- package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
- package/src/client/components/itemledger/TranslateItemledger.js +7 -0
- package/src/client/components/underpost/AppShellUnderpost.js +691 -0
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/CssUnderpost.js +281 -0
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
- package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
- package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
- package/src/client/components/underpost/LogInUnderpost.js +25 -0
- package/src/client/components/underpost/LogOutUnderpost.js +17 -0
- package/src/client/components/underpost/RouterUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +16 -0
- package/src/client/components/underpost/SignUpUnderpost.js +11 -0
- package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
- package/src/client/components/underpost/TranslateUnderpost.js +10 -0
- package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
- package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
- package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
- package/src/client/public/cryptokoyn/favicon.ico +0 -0
- package/src/client/public/cryptokoyn/microdata.json +85 -0
- package/src/client/public/cryptokoyn/site.webmanifest +57 -0
- package/src/client/public/cryptokoyn/sitemap +148 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
- package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
- package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
- 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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152-precomposed.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-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76-precomposed.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/dark.jpg +0 -0
- package/src/client/public/default/assets/background/dark.svg +557 -0
- package/src/client/public/default/assets/background/white.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/default/assets/logo/base-icon.png +0 -0
- package/src/client/public/default/assets/logo/underpost.gif +0 -0
- package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
- package/src/client/public/default/browserconfig.xml +12 -0
- 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 +69 -0
- 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/safari-pinned-tab.svg +24 -0
- package/src/client/public/default/site.webmanifest +69 -0
- package/src/client/public/default/sitemap +148 -0
- package/src/client/public/default/yandex-browser-50x50.png +0 -0
- package/src/client/public/default/yandex-browser-manifest.json +9 -0
- package/src/client/public/doc/favicon.ico +0 -0
- package/src/client/public/doc/sitemap +148 -0
- package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
- package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
- package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
- package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
- package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
- package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
- package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
- package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
- package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/itemledger/apple-touch-icon.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
- package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
- package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
- package/src/client/public/itemledger/browserconfig.xml +12 -0
- package/src/client/public/itemledger/favicon-16x16.png +0 -0
- package/src/client/public/itemledger/favicon-32x32.png +0 -0
- package/src/client/public/itemledger/favicon-48x48.png +0 -0
- package/src/client/public/itemledger/favicon.ico +0 -0
- package/src/client/public/itemledger/manifest.webmanifest +69 -0
- package/src/client/public/itemledger/microdata.json +71 -0
- package/src/client/public/itemledger/mstile-144x144.png +0 -0
- package/src/client/public/itemledger/mstile-150x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x150.png +0 -0
- package/src/client/public/itemledger/mstile-310x310.png +0 -0
- package/src/client/public/itemledger/mstile-70x70.png +0 -0
- package/src/client/public/itemledger/sitemap +148 -0
- package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
- package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
- package/src/client/public/test/favicon.ico +0 -0
- package/src/client/public/test/sitemap +148 -0
- package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
- package/src/client/services/core/core.service.js +309 -0
- package/src/client/services/crypto/crypto.service.js +68 -0
- package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
- package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +137 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
- package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
- package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/default/default.management.js +1015 -0
- package/src/client/services/default/default.service.js +99 -0
- package/src/client/services/document/document.service.js +134 -0
- package/src/client/services/file/file.service.js +94 -0
- package/src/client/services/instance/instance.management.js +78 -0
- package/src/client/services/instance/instance.service.js +107 -0
- package/src/client/services/ipfs/ipfs.service.js +118 -0
- package/src/client/services/object-layer/object-layer.management.js +414 -0
- package/src/client/services/object-layer/object-layer.service.js +208 -0
- package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
- package/src/client/services/test/test.service.js +68 -0
- package/src/client/services/user/guest.service.js +86 -0
- package/src/client/services/user/user.management.js +51 -0
- package/src/client/services/user/user.service.js +106 -0
- package/src/client/ssr/RootDocument.js +237 -0
- package/src/client/ssr/body/404.js +77 -0
- package/src/client/ssr/body/500.js +76 -0
- package/src/client/ssr/body/CacheControl.js +117 -0
- package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
- package/src/client/ssr/head/CryptokoynScripts.js +4 -0
- package/src/client/ssr/head/Css.js +241 -0
- package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
- package/src/client/ssr/head/DefaultScripts.js +6 -0
- package/src/client/ssr/head/ItemledgerScripts.js +4 -0
- package/src/client/ssr/head/Microdata.js +11 -0
- package/src/client/ssr/head/Production.js +1 -0
- package/src/client/ssr/head/Pwa.js +146 -0
- package/src/client/ssr/head/PwaItemledger.js +197 -0
- package/src/client/ssr/head/Seo.js +15 -0
- package/src/client/ssr/head/UnderpostScripts.js +6 -0
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
- package/src/client/ssr/views/Cyberia404.js +328 -0
- package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
- package/src/client/ssr/views/Maintenance.js +64 -0
- package/src/client/ssr/views/NoNetworkConnection.js +68 -0
- package/src/client/ssr/views/Test.js +199 -0
- package/src/client/sw/core.sw.js +274 -0
- package/src/client-builder/client-build-docs.js +493 -0
- package/src/client-builder/client-build-live.js +100 -0
- package/src/client-builder/client-build.js +1092 -0
- package/src/client-builder/client-dev-server.js +88 -0
- package/src/client-builder/client-formatted.js +170 -0
- package/src/client-builder/client-icons.js +108 -0
- package/src/client-builder/ssr.js +124 -0
- package/src/client.build.js +17 -0
- package/src/client.dev.js +21 -0
- package/src/db/DataBaseProvider.js +213 -0
- package/src/db/mariadb/MariaDB.js +72 -0
- package/src/db/mongo/MongoBootstrap.js +657 -0
- package/src/db/mongo/MongooseDB.js +199 -0
- package/src/grpc/cyberia/grpc-server.js +162 -0
- package/src/index.js +357 -0
- package/src/mailer/EmailRender.js +116 -0
- package/src/mailer/MailerProvider.js +213 -0
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
- package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
- package/src/projects/cyberia/catalog-cyberia.js +90 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2137 -0
- package/src/projects/cyberia/hot-reload-trigger.js +163 -0
- package/src/projects/cyberia/instance-data.js +719 -0
- package/src/projects/cyberia/ipfs-client.js +599 -0
- package/src/projects/cyberia/map-preview-generator.js +304 -0
- package/src/projects/cyberia/object-layer.js +978 -0
- package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
- package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
- package/src/projects/cyberia/semantic-layer-generator.js +753 -0
- package/src/projects/cyberia/shape-generator.js +1060 -0
- package/src/projects/underpost/catalog-underpost.js +60 -0
- package/src/proxy.js +19 -0
- package/src/runtime/cyberia-client/Dockerfile +49 -0
- package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
- package/src/runtime/cyberia-server/Dockerfile +43 -0
- package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
- package/src/runtime/engine-cyberia/Dockerfile +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +144 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +128 -0
- package/src/runtime/express/Dockerfile +45 -0
- package/src/runtime/express/Express.js +285 -0
- package/src/runtime/lampp/Dockerfile +62 -0
- package/src/runtime/lampp/Lampp.js +359 -0
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/runtime/wp/Dockerfile +62 -0
- package/src/runtime/wp/Wp.js +642 -0
- package/src/server/auth.js +713 -0
- package/src/server/backup.js +102 -0
- package/src/server/catalog.js +72 -0
- package/src/server/conf.js +2316 -0
- package/src/server/cron.js +476 -0
- package/src/server/crypto.js +210 -0
- package/src/server/data-query.js +461 -0
- package/src/server/dns.js +553 -0
- package/src/server/downloader.js +73 -0
- package/src/server/logger.js +229 -0
- package/src/server/middlewares.js +152 -0
- package/src/server/peer.js +75 -0
- package/src/server/process.js +299 -0
- package/src/server/proxy.js +139 -0
- package/src/server/runtime-status.js +252 -0
- package/src/server/runtime.js +183 -0
- package/src/server/start.js +272 -0
- package/src/server/tls.js +254 -0
- package/src/server/valkey.js +201 -0
- package/src/server.js +25 -0
- package/src/ws/IoInterface.js +129 -0
- package/src/ws/IoServer.js +68 -0
- package/src/ws/core/channels/core.ws.chat.js +68 -0
- package/src/ws/core/channels/core.ws.mailer.js +116 -0
- package/src/ws/core/channels/core.ws.stream.js +90 -0
- package/src/ws/core/core.ws.connection.js +41 -0
- package/src/ws/core/core.ws.emit.js +37 -0
- package/src/ws/core/core.ws.server.js +44 -0
- package/src/ws/default/channels/default.ws.main.js +57 -0
- package/src/ws/default/default.ws.connection.js +35 -0
- package/src/ws/default/default.ws.emit.js +14 -0
- package/src/ws/default/default.ws.server.js +39 -0
- package/test/api.test.js +53 -0
- package/test/crypto.test.js +117 -0
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +267 -0
- package/test/shape-generator.test.js +463 -0
- package/tsconfig.docs.json +15 -0
- package/typedoc.dd-cyberia.json +31 -0
- package/typedoc.json +31 -0
|
@@ -0,0 +1,1542 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy module for managing the deployment of applications and services.
|
|
3
|
+
* @module src/cli/deploy.js
|
|
4
|
+
* @namespace UnderpostDeploy
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildKindPorts,
|
|
9
|
+
buildPortProxyRouter,
|
|
10
|
+
buildProxyRouter,
|
|
11
|
+
Config,
|
|
12
|
+
cronDeployIdResolve,
|
|
13
|
+
deployRangePortFactory,
|
|
14
|
+
getDataDeploy,
|
|
15
|
+
loadConfInstances,
|
|
16
|
+
loadConfServerJson,
|
|
17
|
+
loadReplicas,
|
|
18
|
+
pathPortAssignmentFactory,
|
|
19
|
+
} from '../server/conf.js';
|
|
20
|
+
import { loggerFactory } from '../server/logger.js';
|
|
21
|
+
import { shellExec } from '../server/process.js';
|
|
22
|
+
import { INTERNAL_READY_PATH, INTERNAL_HEALTH_PATH } from '../server/runtime-status.js';
|
|
23
|
+
import fs from 'fs-extra';
|
|
24
|
+
import dotenv from 'dotenv';
|
|
25
|
+
import os from 'node:os';
|
|
26
|
+
import crypto from 'node:crypto';
|
|
27
|
+
import Underpost from '../index.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Clamps an identifier to the Kubernetes DNS-1123 label limit (63 chars),
|
|
31
|
+
* used for pod-local `volumes[].name` / `volumeMounts[].name`. Names within the
|
|
32
|
+
* limit are returned verbatim so existing short names are stable; longer ones
|
|
33
|
+
* are truncated and suffixed with an 8-char content hash to stay unique and
|
|
34
|
+
* deterministic (e.g. the per-variant instance volume names, which append the
|
|
35
|
+
* full `<deployId>-<env>-<traffic>` and can exceed 63).
|
|
36
|
+
* @param {string} name - Candidate name.
|
|
37
|
+
* @returns {string} A name no longer than 63 characters.
|
|
38
|
+
*/
|
|
39
|
+
const k8sVolumeName = (name) => {
|
|
40
|
+
if (typeof name !== 'string' || name.length <= 63) return name;
|
|
41
|
+
const hash = crypto.createHash('sha1').update(name).digest('hex').slice(0, 8);
|
|
42
|
+
return `${name.slice(0, 54)}-${hash}`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const logger = loggerFactory(import.meta);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @class UnderpostDeploy
|
|
49
|
+
* @description Manages the deployment of applications and services.
|
|
50
|
+
* This class provides a set of static methods to handle the deployment process,
|
|
51
|
+
* including resource allocation, configuration management, and Kubernetes deployment.
|
|
52
|
+
* @memberof UnderpostDeploy
|
|
53
|
+
*/
|
|
54
|
+
class UnderpostDeploy {
|
|
55
|
+
static API = {
|
|
56
|
+
/**
|
|
57
|
+
* Creates a router configuration for a list of deployments.
|
|
58
|
+
* @param {string} deployList - List of deployment IDs to include in the router.
|
|
59
|
+
* @param {string} env - Environment for which the router is being created.
|
|
60
|
+
* @returns {object} - Router configuration for the specified deployments.
|
|
61
|
+
* @memberof UnderpostDeploy
|
|
62
|
+
*/
|
|
63
|
+
async routerFactory(deployList, env) {
|
|
64
|
+
const initEnvPath = `./engine-private/conf/${deployList.split(',')[0]}/.env.${env}`;
|
|
65
|
+
const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
|
|
66
|
+
process.env.PORT = initEnvObj.PORT;
|
|
67
|
+
process.env.NODE_ENV = env;
|
|
68
|
+
await Config.build('proxy', deployList);
|
|
69
|
+
return buildPortProxyRouter({ port: env === 'development' ? 80 : 443, proxyRouter: buildProxyRouter() });
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Creates a YAML service configuration for a deployment.
|
|
73
|
+
* @param {string} deployId - Deployment ID for which the service is being created.
|
|
74
|
+
* @param {string} path - Path for which the service is being created.
|
|
75
|
+
* @param {string} env - Environment for which the service is being created.
|
|
76
|
+
* @param {number} port - Port number for the service.
|
|
77
|
+
* @param {Array<string>} deploymentVersions - List of deployment versions.
|
|
78
|
+
* @param {string} serviceId - Custom service name (optional).
|
|
79
|
+
* @param {Array} pathRewritePolicy - Path rewrite policy (optional).
|
|
80
|
+
* @param {object} timeoutPolicy - Timeout policy (optional).
|
|
81
|
+
* @param {object} retryPolicy - Retry policy (optional).
|
|
82
|
+
* @returns {string} - YAML service configuration for the specified deployment.
|
|
83
|
+
* @memberof UnderpostDeploy
|
|
84
|
+
*/
|
|
85
|
+
deploymentYamlServiceFactory({
|
|
86
|
+
deployId,
|
|
87
|
+
path,
|
|
88
|
+
env,
|
|
89
|
+
port,
|
|
90
|
+
deploymentVersions,
|
|
91
|
+
serviceId,
|
|
92
|
+
pathRewritePolicy,
|
|
93
|
+
timeoutPolicy,
|
|
94
|
+
retryPolicy,
|
|
95
|
+
}) {
|
|
96
|
+
return `
|
|
97
|
+
- conditions:
|
|
98
|
+
- prefix: ${path}
|
|
99
|
+
${
|
|
100
|
+
pathRewritePolicy
|
|
101
|
+
? `pathRewritePolicy:
|
|
102
|
+
replacePrefix:
|
|
103
|
+
${pathRewritePolicy.map(
|
|
104
|
+
(rd) => `- prefix: ${rd.prefix}
|
|
105
|
+
replacement: ${rd.replacement}
|
|
106
|
+
`,
|
|
107
|
+
).join(`
|
|
108
|
+
`)}`
|
|
109
|
+
: ''
|
|
110
|
+
}${
|
|
111
|
+
timeoutPolicy
|
|
112
|
+
? `\n timeoutPolicy:\n${timeoutPolicy.response ? ` response: ${timeoutPolicy.response}\n` : ''}${
|
|
113
|
+
timeoutPolicy.idle ? ` idle: ${timeoutPolicy.idle}\n` : ''
|
|
114
|
+
}`
|
|
115
|
+
: ''
|
|
116
|
+
}${
|
|
117
|
+
retryPolicy
|
|
118
|
+
? `\n retryPolicy:\n${retryPolicy.count !== undefined ? ` count: ${retryPolicy.count}\n` : ''}${
|
|
119
|
+
retryPolicy.perTryTimeout ? ` perTryTimeout: ${retryPolicy.perTryTimeout}\n` : ''
|
|
120
|
+
}`
|
|
121
|
+
: ''
|
|
122
|
+
}
|
|
123
|
+
enableWebsockets: true
|
|
124
|
+
services:
|
|
125
|
+
${deploymentVersions
|
|
126
|
+
.map(
|
|
127
|
+
(version, i) =>
|
|
128
|
+
` - name: ${serviceId ? serviceId : `${deployId}-${env}-${version}-service`}
|
|
129
|
+
port: ${port}
|
|
130
|
+
weight: ${i === 0 ? 100 : 0}
|
|
131
|
+
`,
|
|
132
|
+
)
|
|
133
|
+
.join('')}`;
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Builds Kubernetes probes that gate on the in-pod internal status endpoint.
|
|
137
|
+
*
|
|
138
|
+
* HTTP mode (default) aligns Kubernetes pod readiness with actual Underpost
|
|
139
|
+
* runtime readiness:
|
|
140
|
+
* - readinessProbe → GET /_internal/ready (200 only when running-deployment)
|
|
141
|
+
* - livenessProbe → GET /_internal/health (deadlock / hung-process detection)
|
|
142
|
+
* - startupProbe → GET /_internal/ready (long window for hot-built/slow boots)
|
|
143
|
+
*
|
|
144
|
+
* Migration: pass `useHttp: false` to emit the legacy TCP socket probes
|
|
145
|
+
* (port-bound only) for deployments not yet serving the internal endpoint.
|
|
146
|
+
*
|
|
147
|
+
* @param {object} opts
|
|
148
|
+
* @param {number} opts.port - In-pod internal status port (deployment base PORT).
|
|
149
|
+
* @param {boolean} [opts.useHttp=true] - Emit HTTP probes; false → legacy TCP.
|
|
150
|
+
* @param {boolean} [opts.liveness=true] - Include a livenessProbe.
|
|
151
|
+
* @param {boolean} [opts.startup=true] - Include a startupProbe.
|
|
152
|
+
* @returns {{readinessProbe: object, livenessProbe?: object, startupProbe?: object}}
|
|
153
|
+
* @memberof UnderpostDeploy
|
|
154
|
+
*/
|
|
155
|
+
runtimeProbesFactory({ port, useHttp = true, liveness = true, startup = true } = {}) {
|
|
156
|
+
if (!port) return {};
|
|
157
|
+
if (!useHttp) {
|
|
158
|
+
const tcp = { tcpSocket: { port }, initialDelaySeconds: 5, periodSeconds: 10, failureThreshold: 6 };
|
|
159
|
+
const probes = { readinessProbe: tcp };
|
|
160
|
+
if (liveness) probes.livenessProbe = { ...tcp, initialDelaySeconds: 30 };
|
|
161
|
+
return probes;
|
|
162
|
+
}
|
|
163
|
+
const probes = {
|
|
164
|
+
readinessProbe: {
|
|
165
|
+
httpGet: { path: INTERNAL_READY_PATH, port },
|
|
166
|
+
initialDelaySeconds: 5,
|
|
167
|
+
periodSeconds: 5,
|
|
168
|
+
timeoutSeconds: 3,
|
|
169
|
+
failureThreshold: 3,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
if (liveness)
|
|
173
|
+
probes.livenessProbe = {
|
|
174
|
+
httpGet: { path: INTERNAL_HEALTH_PATH, port },
|
|
175
|
+
initialDelaySeconds: 30,
|
|
176
|
+
periodSeconds: 15,
|
|
177
|
+
timeoutSeconds: 3,
|
|
178
|
+
failureThreshold: 3,
|
|
179
|
+
};
|
|
180
|
+
if (startup)
|
|
181
|
+
// A startupProbe suspends readiness/liveness until it first succeeds, so
|
|
182
|
+
// its window bounds in-container hot builds and slow boots. 180 × 10s =
|
|
183
|
+
// 30 min before the pod is considered failed to start.
|
|
184
|
+
probes.startupProbe = {
|
|
185
|
+
httpGet: { path: INTERNAL_READY_PATH, port },
|
|
186
|
+
initialDelaySeconds: 10,
|
|
187
|
+
periodSeconds: 10,
|
|
188
|
+
timeoutSeconds: 3,
|
|
189
|
+
failureThreshold: 180,
|
|
190
|
+
};
|
|
191
|
+
return probes;
|
|
192
|
+
},
|
|
193
|
+
/**
|
|
194
|
+
* Creates a YAML deployment configuration for a deployment.
|
|
195
|
+
* @param {string} deployId - Deployment ID for which the deployment is being created.
|
|
196
|
+
* @param {string} env - Environment for which the deployment is being created.
|
|
197
|
+
* @param {string} suffix - Suffix for the deployment.
|
|
198
|
+
* @param {object} resources - Resource configuration for the deployment.
|
|
199
|
+
* @param {number} replicas - Number of replicas for the deployment.
|
|
200
|
+
* @param {string} image - Docker image for the deployment.
|
|
201
|
+
* @param {string} namespace - Kubernetes namespace for the deployment.
|
|
202
|
+
* @param {Array<object>} volumes - Volume configurations for the deployment.
|
|
203
|
+
* @param {Array<string>} cmd - Command to run in the deployment container.
|
|
204
|
+
* @param {boolean} skipFullBuild - Whether to skip the full client bundle build during deployment.
|
|
205
|
+
* @param {boolean} pullBundle - Whether to pull the pre-built client bundle from Cloudinary before starting. Use together with skipFullBuild to skip the local build entirely.
|
|
206
|
+
* @param {string} [imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`). When omitted, defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
207
|
+
* @param {object} lifecycle - Kubernetes lifecycle hooks configuration for the deployment container.
|
|
208
|
+
* @param {object} readinessProbe - Kubernetes readiness probe configuration for the deployment container.
|
|
209
|
+
* @param {object} livenessProbe - Kubernetes liveness probe configuration for the deployment container.
|
|
210
|
+
* @param {object} startupProbe - Kubernetes startup probe configuration for the deployment container.
|
|
211
|
+
* @param {number} containerPort - Container port to expose for the deployment.
|
|
212
|
+
* @returns {string} - YAML deployment configuration for the specified deployment.
|
|
213
|
+
* @memberof UnderpostDeploy
|
|
214
|
+
*/
|
|
215
|
+
deploymentYamlPartsFactory({
|
|
216
|
+
deployId,
|
|
217
|
+
env,
|
|
218
|
+
suffix,
|
|
219
|
+
resources,
|
|
220
|
+
replicas,
|
|
221
|
+
image,
|
|
222
|
+
namespace,
|
|
223
|
+
volumes,
|
|
224
|
+
cmd,
|
|
225
|
+
skipFullBuild,
|
|
226
|
+
pullBundle,
|
|
227
|
+
imagePullPolicy,
|
|
228
|
+
// K8S lifecycle + probe wiring. Pass-through structures shaped like the
|
|
229
|
+
// upstream Kubernetes API, spliced verbatim into the container spec.
|
|
230
|
+
// lifecycle: { postStart: { exec: { command: [...] } }, preStop: { exec: { command: [...] } } }
|
|
231
|
+
// readinessProbe: { tcpSocket: { port: 8081 }, ... }
|
|
232
|
+
// livenessProbe: { tcpSocket: { port: 8081 }, ... }
|
|
233
|
+
// containerPort: integer; rendered as ports[0].containerPort. Optional.
|
|
234
|
+
lifecycle,
|
|
235
|
+
readinessProbe,
|
|
236
|
+
livenessProbe,
|
|
237
|
+
startupProbe,
|
|
238
|
+
containerPort,
|
|
239
|
+
// Explicit, secret-free internal status port injected as an env var so the
|
|
240
|
+
// in-pod endpoint binds exactly what the probes and the monitor target,
|
|
241
|
+
// independent of the ambient `PORT` baked into the image/secret.
|
|
242
|
+
internalStatusPort,
|
|
243
|
+
}) {
|
|
244
|
+
if (!cmd)
|
|
245
|
+
cmd =
|
|
246
|
+
pullBundle || skipFullBuild
|
|
247
|
+
? [
|
|
248
|
+
// When pullBundle (or skipFullBuild) is set the container pulls the pre-built client
|
|
249
|
+
// bundle from Cloudinary (push-bundle must have been run on the dev machine beforehand).
|
|
250
|
+
`underpost secret underpost --create-from-env`,
|
|
251
|
+
`underpost start --build --run --pull-bundle --skip-full-build ${deployId} ${env}`,
|
|
252
|
+
]
|
|
253
|
+
: [
|
|
254
|
+
// `npm install -g npm@11.2.0`,
|
|
255
|
+
// `npm install -g underpost`,
|
|
256
|
+
`underpost secret underpost --create-from-env`,
|
|
257
|
+
`underpost start --build --run ${deployId} ${env}`,
|
|
258
|
+
];
|
|
259
|
+
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
260
|
+
if (!volumes) volumes = [];
|
|
261
|
+
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
262
|
+
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
|
|
263
|
+
: [];
|
|
264
|
+
volumes = volumes.concat(confVolume);
|
|
265
|
+
// const containerImage = image ? image : `localhost/rockylinux9-underpost:v${packageJson.version}`;
|
|
266
|
+
const containerImage = image ? image : `underpost/underpost-engine:v${packageJson.version}`;
|
|
267
|
+
return `apiVersion: apps/v1
|
|
268
|
+
kind: Deployment
|
|
269
|
+
metadata:
|
|
270
|
+
name: ${deployId}-${env}-${suffix}
|
|
271
|
+
namespace: ${namespace ? namespace : 'default'}
|
|
272
|
+
labels:
|
|
273
|
+
app: ${deployId}-${env}-${suffix}
|
|
274
|
+
deploy-id: ${deployId}-${env}
|
|
275
|
+
spec:
|
|
276
|
+
replicas: ${replicas}
|
|
277
|
+
selector:
|
|
278
|
+
matchLabels:
|
|
279
|
+
app: ${deployId}-${env}-${suffix}
|
|
280
|
+
template:
|
|
281
|
+
metadata:
|
|
282
|
+
labels:
|
|
283
|
+
app: ${deployId}-${env}-${suffix}
|
|
284
|
+
deploy-id: ${deployId}-${env}
|
|
285
|
+
spec:
|
|
286
|
+
containers:
|
|
287
|
+
- name: ${deployId}-${env}-${suffix}
|
|
288
|
+
image: ${containerImage}
|
|
289
|
+
imagePullPolicy: ${imagePullPolicy ? imagePullPolicy : containerImage.startsWith('localhost/') ? 'Never' : 'IfNotPresent'}
|
|
290
|
+
envFrom:
|
|
291
|
+
- secretRef:
|
|
292
|
+
name: underpost-config
|
|
293
|
+
${
|
|
294
|
+
internalStatusPort
|
|
295
|
+
? ` env:
|
|
296
|
+
- name: UNDERPOST_INTERNAL_PORT
|
|
297
|
+
value: "${internalStatusPort}"
|
|
298
|
+
`
|
|
299
|
+
: ''
|
|
300
|
+
}${
|
|
301
|
+
containerPort
|
|
302
|
+
? ` ports:
|
|
303
|
+
- containerPort: ${containerPort}
|
|
304
|
+
`
|
|
305
|
+
: ''
|
|
306
|
+
}${
|
|
307
|
+
resources
|
|
308
|
+
? ` resources:
|
|
309
|
+
requests:
|
|
310
|
+
memory: "${resources.requests.memory}"
|
|
311
|
+
cpu: "${resources.requests.cpu}"
|
|
312
|
+
limits:
|
|
313
|
+
memory: "${resources.limits.memory}"
|
|
314
|
+
cpu: "${resources.limits.cpu}"`
|
|
315
|
+
: ''
|
|
316
|
+
}
|
|
317
|
+
command:
|
|
318
|
+
- /bin/sh
|
|
319
|
+
- -c
|
|
320
|
+
- >
|
|
321
|
+
${cmd.join(' &&\n ')}
|
|
322
|
+
${
|
|
323
|
+
readinessProbe
|
|
324
|
+
? ` readinessProbe:
|
|
325
|
+
${JSON.stringify(readinessProbe, null, 2)
|
|
326
|
+
.split('\n')
|
|
327
|
+
.map((l) => ' ' + l)
|
|
328
|
+
.join('\n')}
|
|
329
|
+
`
|
|
330
|
+
: ''
|
|
331
|
+
}${
|
|
332
|
+
livenessProbe
|
|
333
|
+
? ` livenessProbe:
|
|
334
|
+
${JSON.stringify(livenessProbe, null, 2)
|
|
335
|
+
.split('\n')
|
|
336
|
+
.map((l) => ' ' + l)
|
|
337
|
+
.join('\n')}
|
|
338
|
+
`
|
|
339
|
+
: ''
|
|
340
|
+
}${
|
|
341
|
+
startupProbe
|
|
342
|
+
? ` startupProbe:
|
|
343
|
+
${JSON.stringify(startupProbe, null, 2)
|
|
344
|
+
.split('\n')
|
|
345
|
+
.map((l) => ' ' + l)
|
|
346
|
+
.join('\n')}
|
|
347
|
+
`
|
|
348
|
+
: ''
|
|
349
|
+
}${
|
|
350
|
+
lifecycle
|
|
351
|
+
? ` lifecycle:
|
|
352
|
+
${JSON.stringify(lifecycle, null, 2)
|
|
353
|
+
.split('\n')
|
|
354
|
+
.map((l) => ' ' + l)
|
|
355
|
+
.join('\n')}
|
|
356
|
+
`
|
|
357
|
+
: ''
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
${
|
|
361
|
+
volumes.length > 0
|
|
362
|
+
? Underpost.deploy
|
|
363
|
+
.volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
|
|
364
|
+
.render.split(`\n`)
|
|
365
|
+
.map((l) => ' ' + l)
|
|
366
|
+
.join(`\n`)
|
|
367
|
+
: ''
|
|
368
|
+
}
|
|
369
|
+
---
|
|
370
|
+
apiVersion: v1
|
|
371
|
+
kind: Service
|
|
372
|
+
metadata:
|
|
373
|
+
name: ${deployId}-${env}-${suffix}-service
|
|
374
|
+
namespace: ${namespace}
|
|
375
|
+
spec:
|
|
376
|
+
selector:
|
|
377
|
+
app: ${deployId}-${env}-${suffix}
|
|
378
|
+
ports:
|
|
379
|
+
{{ports}} type: LoadBalancer`;
|
|
380
|
+
},
|
|
381
|
+
/**
|
|
382
|
+
* Builds a manifest for a list of deployments.
|
|
383
|
+
* @param {string} deployList - List of deployment IDs to include in the manifest.
|
|
384
|
+
* @param {string} env - Environment for which the manifest is being built.
|
|
385
|
+
* @param {object} options - Options for the manifest build process.
|
|
386
|
+
* @param {string} options.replicas - Number of replicas for each deployment.
|
|
387
|
+
* @param {string} options.image - Docker image for the deployment.
|
|
388
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment (defaults to "default").
|
|
389
|
+
* @param {string} [options.versions] - Comma-separated list of versions to deploy.
|
|
390
|
+
* @param {string} [options.cmd] - Custom initialization command for deploymentYamlPartsFactory (comma-separated commands).
|
|
391
|
+
* @param {string} [options.timeoutResponse] - HTTPProxy per-route response timeout (e.g. "300000ms", "infinity").
|
|
392
|
+
* @param {string} [options.timeoutIdle] - HTTPProxy per-route idle timeout (e.g. "10s", "infinity").
|
|
393
|
+
* @param {string} [options.retryCount] - HTTPProxy per-route retry count (e.g. 3).
|
|
394
|
+
* @param {string} [options.retryPerTryTimeout] - HTTPProxy per-route per-try timeout (e.g. "150ms").
|
|
395
|
+
* @param {boolean} [options.disableDeploymentProxy] - Whether to disable deployment proxy route generation.
|
|
396
|
+
* @param {string} [options.traffic] - Comma-separated active traffic colour(s) used to select which versions receive traffic (e.g. "blue", "green").
|
|
397
|
+
* @param {boolean} [options.cert] - Whether to include cert-manager Certificate resources in secret.yaml (production only).
|
|
398
|
+
* @param {boolean} [options.selfSigned] - Whether to include TLS block in HTTPProxy using a pre-created self-signed secret. Enables HTTPS for development without cert-manager.
|
|
399
|
+
* @param {boolean} [options.skipFullBuild] - Whether to skip the full client bundle build; forwarded to deploymentYamlPartsFactory.
|
|
400
|
+
* @param {boolean} [options.pullBundle] - Whether to pull the pre-built client bundle from Cloudinary; forwarded to deploymentYamlPartsFactory. Use together with skipFullBuild.
|
|
401
|
+
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); forwarded to deploymentYamlPartsFactory. Defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
402
|
+
* @param {boolean} [options.disableRuntimeProbes] - Omit internal-status HTTP probes from generated manifests. When true no readiness/liveness/startup probes are emitted.
|
|
403
|
+
* @param {boolean} [options.tcpProbes] - Emit legacy TCP socket probes instead of HTTP internal-status probes (migration path).
|
|
404
|
+
* @param {string} [options.node] - Explicit target node for hostPath PV nodeAffinity pinning; resolved through {@link UnderpostDeploy.resolveDeployNode} together with the cluster flags.
|
|
405
|
+
* @param {boolean} [options.kind] - Kind cluster context; affects the cluster-type node default when no explicit node is set.
|
|
406
|
+
* @param {boolean} [options.kubeadm] - Kubeadm cluster context; affects the cluster-type node default when no explicit node is set.
|
|
407
|
+
* @param {boolean} [options.k3s] - K3s cluster context; affects the cluster-type node default when no explicit node is set.
|
|
408
|
+
* @returns {Promise<void>} - Promise that resolves when the manifest is built.
|
|
409
|
+
* @memberof UnderpostDeploy
|
|
410
|
+
*/
|
|
411
|
+
async buildManifest(deployList, env, options) {
|
|
412
|
+
const replicas = options.replicas;
|
|
413
|
+
const image = options.image;
|
|
414
|
+
if (!options.namespace) options.namespace = 'default';
|
|
415
|
+
|
|
416
|
+
for (const _deployId of deployList.split(',')) {
|
|
417
|
+
const deployId = _deployId.trim();
|
|
418
|
+
if (!deployId) continue;
|
|
419
|
+
const confServer = loadReplicas(
|
|
420
|
+
deployId,
|
|
421
|
+
loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
422
|
+
);
|
|
423
|
+
const router = await Underpost.deploy.routerFactory(deployId, env);
|
|
424
|
+
const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
|
|
425
|
+
const { fromPort, toPort } = deployRangePortFactory(router);
|
|
426
|
+
const deploymentVersions = options.versions.split(',');
|
|
427
|
+
fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
|
|
428
|
+
if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
|
|
429
|
+
|
|
430
|
+
logger.info('port range', { deployId, fromPort, toPort });
|
|
431
|
+
|
|
432
|
+
// The internal status endpoint binds `fromPort - 1`: app instances bind
|
|
433
|
+
// the router range starting at `fromPort`, so this slot is always free
|
|
434
|
+
// inside the pod. It is injected into the pod env (UNDERPOST_INTERNAL_PORT)
|
|
435
|
+
// and used for both the probes and the monitor's port-forward target so
|
|
436
|
+
// all three agree regardless of the image's ambient PORT.
|
|
437
|
+
// Opt out with `--disable-runtime-probes` to keep legacy probe-less pods.
|
|
438
|
+
const internalPort = fromPort - 1;
|
|
439
|
+
const probes = options.disableRuntimeProbes
|
|
440
|
+
? {}
|
|
441
|
+
: Underpost.deploy.runtimeProbesFactory({ port: internalPort, useHttp: !options.tcpProbes });
|
|
442
|
+
|
|
443
|
+
let deploymentYamlParts = '';
|
|
444
|
+
for (const deploymentVersion of deploymentVersions) {
|
|
445
|
+
deploymentYamlParts += `---
|
|
446
|
+
${Underpost.deploy
|
|
447
|
+
.deploymentYamlPartsFactory({
|
|
448
|
+
deployId,
|
|
449
|
+
env,
|
|
450
|
+
suffix: deploymentVersion,
|
|
451
|
+
replicas,
|
|
452
|
+
image,
|
|
453
|
+
namespace: options.namespace,
|
|
454
|
+
cmd: options.cmd ? options.cmd.split(',').map((c) => c.trim()) : undefined,
|
|
455
|
+
skipFullBuild: options.skipFullBuild,
|
|
456
|
+
pullBundle: options.pullBundle,
|
|
457
|
+
imagePullPolicy: options.imagePullPolicy,
|
|
458
|
+
internalStatusPort: options.disableRuntimeProbes ? undefined : internalPort,
|
|
459
|
+
readinessProbe: probes.readinessProbe,
|
|
460
|
+
livenessProbe: probes.livenessProbe,
|
|
461
|
+
startupProbe: probes.startupProbe,
|
|
462
|
+
})
|
|
463
|
+
.replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
464
|
+
`;
|
|
465
|
+
}
|
|
466
|
+
fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
|
|
467
|
+
|
|
468
|
+
Underpost.deploy.buildGrpcServiceManifest({
|
|
469
|
+
deployId,
|
|
470
|
+
env,
|
|
471
|
+
confServer,
|
|
472
|
+
namespace: options.namespace,
|
|
473
|
+
traffic: options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
477
|
+
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
|
|
478
|
+
: [];
|
|
479
|
+
if (confVolume.length > 0) {
|
|
480
|
+
// Mirror deployVolume's data-node resolution so the generated manifest
|
|
481
|
+
// pins the PV to the same node that physically receives the volume data.
|
|
482
|
+
const pvDataNode = Underpost.deploy.resolveDeployNode({
|
|
483
|
+
node: options.node,
|
|
484
|
+
kind: options.kind,
|
|
485
|
+
kubeadm: options.kubeadm,
|
|
486
|
+
k3s: options.k3s,
|
|
487
|
+
env,
|
|
488
|
+
});
|
|
489
|
+
let volumeYaml = '';
|
|
490
|
+
for (const deploymentVersion of deploymentVersions) {
|
|
491
|
+
for (const volume of confVolume) {
|
|
492
|
+
if (!volume.claimName) continue;
|
|
493
|
+
const pvcId = `${volume.claimName}-${deployId}-${env}-${deploymentVersion}`;
|
|
494
|
+
const pvId = pvcId.replace(/^pvc-/, 'pv-');
|
|
495
|
+
const hostPath = `/home/dd/engine/volume/${pvId}`;
|
|
496
|
+
volumeYaml += `---\n${Underpost.deploy.persistentVolumeFactory({
|
|
497
|
+
pvcId,
|
|
498
|
+
namespace: options.namespace,
|
|
499
|
+
hostPath,
|
|
500
|
+
nodeName: pvDataNode,
|
|
501
|
+
})}\n`;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/pv-pvc.yaml`, volumeYaml, 'utf8');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
let proxyYaml = '';
|
|
508
|
+
let secretYaml = '';
|
|
509
|
+
const customServices = fs.existsSync(`./engine-private/conf/${deployId}/conf.services.json`)
|
|
510
|
+
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.services.json`))
|
|
511
|
+
: [];
|
|
512
|
+
|
|
513
|
+
for (const host of Object.keys(confServer)) {
|
|
514
|
+
if (env === 'production' && options.cert === true)
|
|
515
|
+
secretYaml += Underpost.deploy.buildCertManagerCertificate({ host, namespace: options.namespace });
|
|
516
|
+
|
|
517
|
+
const pathPortAssignment = pathPortAssignmentData[host];
|
|
518
|
+
// logger.info('', { host, pathPortAssignment });
|
|
519
|
+
let _proxyYaml = Underpost.deploy.baseProxyYamlFactory({ host, env, options });
|
|
520
|
+
const deploymentVersions =
|
|
521
|
+
options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'];
|
|
522
|
+
let proxyRoutes = '';
|
|
523
|
+
const globalTimeoutPolicy =
|
|
524
|
+
(options.timeoutResponse && options.timeoutResponse !== '') ||
|
|
525
|
+
(options.timeoutIdle && options.timeoutIdle !== '')
|
|
526
|
+
? {
|
|
527
|
+
response: options.timeoutResponse,
|
|
528
|
+
idle: options.timeoutIdle,
|
|
529
|
+
}
|
|
530
|
+
: undefined;
|
|
531
|
+
const globalRetryPolicy =
|
|
532
|
+
options.retryCount ||
|
|
533
|
+
options.retryCount === 0 ||
|
|
534
|
+
(options.retryPerTryTimeout && options.retryPerTryTimeout !== '')
|
|
535
|
+
? {
|
|
536
|
+
count: options.retryCount,
|
|
537
|
+
perTryTimeout: options.retryPerTryTimeout,
|
|
538
|
+
}
|
|
539
|
+
: undefined;
|
|
540
|
+
if (!options.disableDeploymentProxy)
|
|
541
|
+
for (const conditionObj of pathPortAssignment) {
|
|
542
|
+
const { path, port } = conditionObj;
|
|
543
|
+
proxyRoutes += Underpost.deploy.deploymentYamlServiceFactory({
|
|
544
|
+
path,
|
|
545
|
+
deployId,
|
|
546
|
+
env,
|
|
547
|
+
port,
|
|
548
|
+
deploymentVersions,
|
|
549
|
+
timeoutPolicy: globalTimeoutPolicy,
|
|
550
|
+
retryPolicy: globalRetryPolicy,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
for (const customService of customServices) {
|
|
554
|
+
const {
|
|
555
|
+
path: _path,
|
|
556
|
+
port,
|
|
557
|
+
serviceId,
|
|
558
|
+
host: _host,
|
|
559
|
+
pathRewritePolicy,
|
|
560
|
+
timeoutPolicy: _timeoutPolicy,
|
|
561
|
+
retryPolicy: _retryPolicy,
|
|
562
|
+
} = customService;
|
|
563
|
+
if (host === _host) {
|
|
564
|
+
proxyRoutes += Underpost.deploy.deploymentYamlServiceFactory({
|
|
565
|
+
path: _path,
|
|
566
|
+
port,
|
|
567
|
+
serviceId,
|
|
568
|
+
deploymentVersions,
|
|
569
|
+
pathRewritePolicy,
|
|
570
|
+
timeoutPolicy: _timeoutPolicy ? _timeoutPolicy : globalTimeoutPolicy,
|
|
571
|
+
retryPolicy: _retryPolicy ? _retryPolicy : globalRetryPolicy,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (proxyRoutes) proxyYaml += _proxyYaml + proxyRoutes;
|
|
576
|
+
}
|
|
577
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
|
|
578
|
+
fs.writeFileSync(yamlPath, proxyYaml, 'utf8');
|
|
579
|
+
if (env === 'production') {
|
|
580
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
|
|
581
|
+
fs.writeFileSync(yamlPath, secretYaml, 'utf8');
|
|
582
|
+
} else {
|
|
583
|
+
const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml', 'pv-pvc.yaml', 'grpc-service.yaml'];
|
|
584
|
+
for (const file of deploymentsFiles) {
|
|
585
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
|
|
586
|
+
fs.copyFileSync(
|
|
587
|
+
`./engine-private/conf/${deployId}/build/${env}/${file}`,
|
|
588
|
+
`./manifests/deployment/${deployId}-${env}/${file}`,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
/**
|
|
596
|
+
* Builds and writes a gRPC ClusterIP service YAML for a deployment.
|
|
597
|
+
* Scans conf.server.json for gRPC ports and emits grpc-service.yaml under
|
|
598
|
+
* `engine-private/conf/<deployId>/build/<env>/`. The selector always uses the
|
|
599
|
+
* explicit `app: <deployId>-<env>-<traffic>` label to target only the active
|
|
600
|
+
* colour (blue or green).
|
|
601
|
+
* @param {string} deployId - Deployment ID.
|
|
602
|
+
* @param {string} env - Environment ('development' or 'production').
|
|
603
|
+
* @param {object} confServer - Parsed conf.server.json content.
|
|
604
|
+
* @param {string} [namespace='default'] - Kubernetes namespace.
|
|
605
|
+
* @param {string[]} [traffic=['blue']] - Active traffic colour(s) ('blue', 'green', or both).
|
|
606
|
+
* @param {string|null} [host=null] - Specific host to scan for gRPC ports. If null, all hosts are scanned.
|
|
607
|
+
* @returns {string|null} - Path to the written YAML file, or null if no gRPC ports found.
|
|
608
|
+
* @memberof UnderpostDeploy
|
|
609
|
+
*/
|
|
610
|
+
buildGrpcServiceManifest({ deployId, env, confServer, namespace = 'default', traffic = ['blue'], host = null }) {
|
|
611
|
+
const grpcPorts = new Set();
|
|
612
|
+
const hostsToScan = host ? [host] : Object.keys(confServer);
|
|
613
|
+
for (const h of hostsToScan) {
|
|
614
|
+
if (!confServer[h]) continue;
|
|
615
|
+
for (const path of Object.keys(confServer[h])) {
|
|
616
|
+
const grpc = confServer[h][path].grpc;
|
|
617
|
+
if (grpc && grpc.port) grpcPorts.add(parseInt(grpc.port));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (grpcPorts.size === 0) return null;
|
|
621
|
+
const grpcPortsList = [...grpcPorts]
|
|
622
|
+
.map(
|
|
623
|
+
(port) => ` - name: grpc-${port}
|
|
624
|
+
protocol: TCP
|
|
625
|
+
port: ${port}
|
|
626
|
+
targetPort: ${port}`,
|
|
627
|
+
)
|
|
628
|
+
.join('\n');
|
|
629
|
+
let grpcServiceYaml = '';
|
|
630
|
+
for (const color of traffic) {
|
|
631
|
+
const grpcServiceName = `${deployId}-grpc-service-${env}-${color}`;
|
|
632
|
+
const selectorYaml = `app: ${deployId}-${env}-${color}`;
|
|
633
|
+
grpcServiceYaml += `---
|
|
634
|
+
apiVersion: v1
|
|
635
|
+
kind: Service
|
|
636
|
+
metadata:
|
|
637
|
+
name: ${grpcServiceName}
|
|
638
|
+
namespace: ${namespace}
|
|
639
|
+
labels:
|
|
640
|
+
app: ${grpcServiceName}
|
|
641
|
+
spec:
|
|
642
|
+
type: ClusterIP
|
|
643
|
+
selector:
|
|
644
|
+
${selectorYaml}
|
|
645
|
+
ports:
|
|
646
|
+
${grpcPortsList}
|
|
647
|
+
`;
|
|
648
|
+
logger.info(
|
|
649
|
+
`gRPC ClusterIP service YAML written: ${grpcServiceName} (selector: ${selectorYaml}, ports: ${[...grpcPorts].join(', ')})`,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
const yamlPath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
|
|
653
|
+
fs.writeFileSync(yamlPath, grpcServiceYaml, 'utf8');
|
|
654
|
+
return yamlPath;
|
|
655
|
+
},
|
|
656
|
+
/**
|
|
657
|
+
* Builds a Certificate resource for a host using cert-manager.
|
|
658
|
+
* @param {string} host - Hostname for which the certificate is being built.
|
|
659
|
+
* @param {string} namespace - Kubernetes namespace for the certificate.
|
|
660
|
+
* @returns {string} - Certificate resource YAML for the specified host.
|
|
661
|
+
* @memberof UnderpostDeploy
|
|
662
|
+
*/
|
|
663
|
+
buildCertManagerCertificate({ host, namespace }) {
|
|
664
|
+
return `
|
|
665
|
+
---
|
|
666
|
+
apiVersion: cert-manager.io/v1
|
|
667
|
+
kind: Certificate
|
|
668
|
+
metadata:
|
|
669
|
+
name: ${host}
|
|
670
|
+
namespace: ${namespace}
|
|
671
|
+
spec:
|
|
672
|
+
commonName: ${host}
|
|
673
|
+
dnsNames:
|
|
674
|
+
- ${host}
|
|
675
|
+
issuerRef:
|
|
676
|
+
name: letsencrypt-prod
|
|
677
|
+
kind: ClusterIssuer
|
|
678
|
+
secretName: ${host}`;
|
|
679
|
+
},
|
|
680
|
+
/**
|
|
681
|
+
* Retrieves the current traffic status for a deployment.
|
|
682
|
+
* @param {string} deployId - Deployment ID for which the traffic status is being retrieved.
|
|
683
|
+
* @param {object} options - Options for the traffic retrieval.
|
|
684
|
+
* @param {string} options.hostTest - Hostname to test for traffic status.
|
|
685
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
686
|
+
* @returns {string|null} - Current traffic status ('blue' or 'green') or null if not found.
|
|
687
|
+
* @memberof UnderpostDeploy
|
|
688
|
+
*/
|
|
689
|
+
getCurrentTraffic(deployId, options = { hostTest: '', namespace: '', env: '' }) {
|
|
690
|
+
if (!options.namespace) options.namespace = 'default';
|
|
691
|
+
// kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
|
|
692
|
+
const hostTest = options?.hostTest
|
|
693
|
+
? options.hostTest
|
|
694
|
+
: Object.keys(loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`))[0];
|
|
695
|
+
// Missing HTTPProxy is the canonical "no traffic colour set yet" state
|
|
696
|
+
// for blue/green rollouts. silentOnError swallows kubectl's NotFound
|
|
697
|
+
// exit so the function can return null cleanly.
|
|
698
|
+
const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -n ${options.namespace} -o yaml`, {
|
|
699
|
+
silent: true,
|
|
700
|
+
stdout: true,
|
|
701
|
+
silentOnError: true,
|
|
702
|
+
});
|
|
703
|
+
if (!info) return null;
|
|
704
|
+
// Env-scoped resolution: read THIS deploy's colour from its own service
|
|
705
|
+
// name (`<deployId>-<env>-<colour>-service`). Essential for shared
|
|
706
|
+
// multi-instance hosts, where one HTTPProxy holds several variants' routes
|
|
707
|
+
// on possibly different colours — a whole-document `.match('blue')` would
|
|
708
|
+
// return whichever colour appears first, i.e. a sibling's, not this one's.
|
|
709
|
+
// The regex is anchored on the full `<deployId>-<env>-` prefix so
|
|
710
|
+
// `dd-cyberia-mmo-server` never matches `dd-cyberia-mmo-server-forest`.
|
|
711
|
+
if (options.env) {
|
|
712
|
+
const escaped = deployId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
713
|
+
const match = info.match(new RegExp(`${escaped}-${options.env}-(blue|green)-service`));
|
|
714
|
+
return match ? match[1] : null;
|
|
715
|
+
}
|
|
716
|
+
return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
|
|
717
|
+
},
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Creates a base YAML configuration for an HTTPProxy resource.
|
|
721
|
+
* @param {string} host - Hostname for which the HTTPProxy is being created.
|
|
722
|
+
* @param {string} env - Environment for which the HTTPProxy is being created.
|
|
723
|
+
* @param {object} options - Options for the HTTPProxy creation.
|
|
724
|
+
* @param {string} options.namespace - Kubernetes namespace for the HTTPProxy.
|
|
725
|
+
* @returns {string} - Base YAML configuration for the HTTPProxy resource.
|
|
726
|
+
* @memberof UnderpostDeploy
|
|
727
|
+
*/
|
|
728
|
+
baseProxyYamlFactory({ host, env, options }) {
|
|
729
|
+
const includeTls = env !== 'development' || options.selfSigned === true;
|
|
730
|
+
return `
|
|
731
|
+
---
|
|
732
|
+
apiVersion: projectcontour.io/v1
|
|
733
|
+
kind: HTTPProxy
|
|
734
|
+
metadata:
|
|
735
|
+
name: ${host}
|
|
736
|
+
namespace: ${options.namespace}
|
|
737
|
+
spec:
|
|
738
|
+
virtualhost:
|
|
739
|
+
fqdn: ${host}${
|
|
740
|
+
includeTls
|
|
741
|
+
? `
|
|
742
|
+
tls:
|
|
743
|
+
secretName: ${host}`
|
|
744
|
+
: ''
|
|
745
|
+
}
|
|
746
|
+
routes:`;
|
|
747
|
+
},
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Callback function for handling deployment options.
|
|
751
|
+
* @param {string} deployList - List of deployment IDs to process.
|
|
752
|
+
* @param {string} env - Environment for which the deployment is being processed.
|
|
753
|
+
* @param {object} options - Options for the deployment process.
|
|
754
|
+
* @param {boolean} options.remove - Whether to remove the deployment.
|
|
755
|
+
* @param {boolean} options.infoRouter - Whether to display router information.
|
|
756
|
+
* @param {boolean} options.sync - Whether to synchronize deployment configurations.
|
|
757
|
+
* @param {boolean} options.buildManifest - Whether to build the deployment manifest.
|
|
758
|
+
* @param {boolean} options.infoUtil - Whether to display utility information.
|
|
759
|
+
* @param {boolean} options.expose - Whether to expose the deployment.
|
|
760
|
+
* @param {boolean} options.cert - Whether to create cert-manager Certificate resources for the deployment.
|
|
761
|
+
* @param {string} options.certHosts - Comma-separated list of hosts for which to create cert-manager certificates.
|
|
762
|
+
* @param {boolean} options.selfSigned - Use a pre-created self-signed TLS secret instead of cert-manager. The secret must already exist in the namespace with the same name as the host. Enables TLS in the Contour HTTPProxy virtualhost without requiring a production ClusterIssuer.
|
|
763
|
+
* @param {string} options.versions - Comma-separated list of versions to deploy.
|
|
764
|
+
* @param {string} options.image - Docker image for the deployment.
|
|
765
|
+
* @param {string} options.traffic - Traffic status for the deployment.
|
|
766
|
+
* @param {string} options.replicas - Number of replicas for the deployment.
|
|
767
|
+
* @param {string} options.node - Explicit target node (highest precedence in the node chain). When empty, {@link UnderpostDeploy.resolveDeployNode} falls back to the cluster-type default (`kind-worker` for kind, host for kubeadm/k3s). Used for both volume placement and hostPath PV nodeAffinity.
|
|
768
|
+
* @param {string} [options.sshKeyPath] - Private key path for node SSH operations, forwarded to deployVolume when shipping a hostPath volume to a remote target node over SSH. Defaults to engine-private/deploy/id_rsa.
|
|
769
|
+
* @param {boolean} options.disableUpdateDeployment - Whether to disable deployment updates.
|
|
770
|
+
* @param {boolean} options.disableUpdateProxy - Whether to disable proxy updates.
|
|
771
|
+
* @param {boolean} options.disableDeploymentProxy - Whether to disable deployment proxy.
|
|
772
|
+
* @param {boolean} options.disableUpdateVolume - Whether to disable volume updates.
|
|
773
|
+
* @param {boolean} options.status - Whether to display deployment status.
|
|
774
|
+
* @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
|
|
775
|
+
* @param {string} [options.namespace] - Kubernetes namespace for the deployment (defaults to "default").
|
|
776
|
+
* @param {string} [options.timeoutResponse] - HTTPProxy per-route response timeout (e.g. "300000ms", "infinity").
|
|
777
|
+
* @param {string} [options.timeoutIdle] - HTTPProxy per-route idle timeout (e.g. "10s", "infinity").
|
|
778
|
+
* @param {string} [options.retryCount] - HTTPProxy per-route retry count (e.g. 3).
|
|
779
|
+
* @param {string} [options.retryPerTryTimeout] - HTTPProxy per-route per-try timeout (e.g. "150ms").
|
|
780
|
+
* @param {string} [options.kindType] - Kubernetes resource kind to target when using --expose (defaults to "svc").
|
|
781
|
+
* @param {number} [options.port] - Port number override for exposing the deployment.
|
|
782
|
+
* @param {string} [options.cmd] - Custom initialization command (comma-separated) for deploymentYamlPartsFactory.
|
|
783
|
+
* @param {number} [options.exposePort] - Remote port override when --expose is active (overrides auto-detected service port). Used as both local and remote port unless exposeLocalPort is also set.
|
|
784
|
+
* @param {number} [options.exposeLocalPort] - Local port override for --expose (e.g. 80); remote port is still auto-detected. Enables /etc/hosts access without a port in the browser URL.
|
|
785
|
+
* @param {boolean} [options.localProxy] - When true (with --expose), forward all service TCP ports locally and start the Node.js path-routing proxy for full path-based routing (e.g. /wp alongside /).
|
|
786
|
+
* @param {boolean} [options.tls] - When true (with --expose --local-proxy), start the proxy on port 443 with TLS using self-signed certificates resolved from the local SSL store.
|
|
787
|
+
* @param {boolean} [options.k3s] - Whether to use k3s cluster context.
|
|
788
|
+
* @param {boolean} [options.kubeadm] - Whether to use kubeadm cluster context.
|
|
789
|
+
* @param {boolean} [options.kind] - Whether to use kind cluster context.
|
|
790
|
+
* @param {boolean} [options.gitClean] - Whether to run git clean on volume mount paths before copying.
|
|
791
|
+
* @param {boolean} [options.skipFullBuild] - Whether to skip the full client bundle build; passed through to buildManifest/deploymentYamlPartsFactory.
|
|
792
|
+
* @param {boolean} [options.pullBundle] - Whether to pull the pre-built client bundle from Cloudinary; passed through to buildManifest/deploymentYamlPartsFactory. Use together with skipFullBuild.
|
|
793
|
+
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override (`Always`, `IfNotPresent`, `Never`); passed through to buildManifest/deploymentYamlPartsFactory. Defaults to `Never` for `localhost/` images and `IfNotPresent` otherwise.
|
|
794
|
+
* @param {boolean} [options.disableRuntimeProbes] - Omit internal-status HTTP probes from generated manifests. When true no readiness/liveness/startup probes are emitted.
|
|
795
|
+
* @param {boolean} [options.tcpProbes] - Emit legacy TCP socket probes instead of HTTP internal-status probes.
|
|
796
|
+
* @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
|
|
797
|
+
* @memberof UnderpostDeploy
|
|
798
|
+
*/
|
|
799
|
+
async callback(
|
|
800
|
+
deployList = '',
|
|
801
|
+
env = 'development',
|
|
802
|
+
options = {
|
|
803
|
+
remove: false,
|
|
804
|
+
infoRouter: false,
|
|
805
|
+
sync: false,
|
|
806
|
+
buildManifest: false,
|
|
807
|
+
infoUtil: false,
|
|
808
|
+
expose: false,
|
|
809
|
+
cert: false,
|
|
810
|
+
certHosts: '',
|
|
811
|
+
versions: '',
|
|
812
|
+
image: '',
|
|
813
|
+
traffic: '',
|
|
814
|
+
replicas: '',
|
|
815
|
+
node: '',
|
|
816
|
+
disableUpdateDeployment: false,
|
|
817
|
+
disableUpdateProxy: false,
|
|
818
|
+
disableDeploymentProxy: false,
|
|
819
|
+
disableUpdateVolume: false,
|
|
820
|
+
status: false,
|
|
821
|
+
disableUpdateUnderpostConfig: false,
|
|
822
|
+
namespace: '',
|
|
823
|
+
timeoutResponse: '',
|
|
824
|
+
timeoutIdle: '',
|
|
825
|
+
retryCount: '',
|
|
826
|
+
retryPerTryTimeout: '',
|
|
827
|
+
kindType: '',
|
|
828
|
+
port: 0,
|
|
829
|
+
exposePort: 0,
|
|
830
|
+
exposeLocalPort: 0,
|
|
831
|
+
localProxy: false,
|
|
832
|
+
tls: false,
|
|
833
|
+
selfSigned: false,
|
|
834
|
+
cmd: '',
|
|
835
|
+
k3s: false,
|
|
836
|
+
kubeadm: false,
|
|
837
|
+
kind: false,
|
|
838
|
+
gitClean: false,
|
|
839
|
+
imagePullPolicy: '',
|
|
840
|
+
},
|
|
841
|
+
) {
|
|
842
|
+
const namespace = options.namespace ? options.namespace : 'default';
|
|
843
|
+
if (!deployList && options.certHosts) {
|
|
844
|
+
for (const host of options.certHosts.split(',')) {
|
|
845
|
+
shellExec(`sudo kubectl apply -f - -n ${namespace} <<EOF
|
|
846
|
+
${Underpost.deploy.buildCertManagerCertificate({ host, namespace })}
|
|
847
|
+
EOF`);
|
|
848
|
+
}
|
|
849
|
+
return;
|
|
850
|
+
} else if (!deployList) deployList = 'dd-default';
|
|
851
|
+
if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
852
|
+
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
853
|
+
if (options.status === true) {
|
|
854
|
+
for (const _deployId of deployList.split(',')) {
|
|
855
|
+
const deployId = _deployId.trim();
|
|
856
|
+
const instances = [];
|
|
857
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}/conf.instances.json`)) {
|
|
858
|
+
// Expands multiInstance variants so status lists every deployed
|
|
859
|
+
// instance (mmo-server, mmo-server-forest, …), not just the templates.
|
|
860
|
+
const confInstances = loadConfInstances(deployId);
|
|
861
|
+
for (const instance of confInstances) {
|
|
862
|
+
const _deployId = `${deployId}-${instance.id}`;
|
|
863
|
+
instances.push({
|
|
864
|
+
id: instance.id,
|
|
865
|
+
host: instance.host,
|
|
866
|
+
path: instance.path,
|
|
867
|
+
fromPort: instance.fromPort,
|
|
868
|
+
toPort: instance.toPort,
|
|
869
|
+
fromDebugPort: instance.fromDebugPort,
|
|
870
|
+
toDebugPort: instance.toDebugPort,
|
|
871
|
+
traffic: Underpost.deploy.getCurrentTraffic(_deployId, { namespace, hostTest: instance.host, env }),
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
logger.info('', {
|
|
876
|
+
deployId,
|
|
877
|
+
env,
|
|
878
|
+
traffic: Underpost.deploy.getCurrentTraffic(deployId, { namespace }),
|
|
879
|
+
router: await Underpost.deploy.routerFactory(deployId, env),
|
|
880
|
+
pods: await Underpost.kubectl.get(deployId),
|
|
881
|
+
instances,
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
const interfaceName = Underpost.dns.getDefaultNetworkInterface();
|
|
885
|
+
logger.info('Machine', {
|
|
886
|
+
hostname: os.hostname(),
|
|
887
|
+
arch: Underpost.baremetal.getHostArch(),
|
|
888
|
+
ipv4Public: await Underpost.dns.getPublicIp(),
|
|
889
|
+
ipv4Local: Underpost.dns.getLocalIPv4Address(),
|
|
890
|
+
resources: Underpost.cluster.getResourcesCapacity(options.node),
|
|
891
|
+
defaultInterfaceName: interfaceName,
|
|
892
|
+
defaultInterfaceInfo: os.networkInterfaces()[interfaceName],
|
|
893
|
+
});
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
|
|
897
|
+
if (!options.replicas) options.replicas = 1;
|
|
898
|
+
if (options.sync)
|
|
899
|
+
await getDataDeploy({
|
|
900
|
+
buildSingleReplica: true,
|
|
901
|
+
});
|
|
902
|
+
if (options.buildManifest === true) await Underpost.deploy.buildManifest(deployList, env, options);
|
|
903
|
+
if (options.infoRouter === true || options.buildManifest === true) {
|
|
904
|
+
logger.info('router', await Underpost.deploy.routerFactory(deployList, env));
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (!options.disableUpdateUnderpostConfig) Underpost.deploy.configMap(env);
|
|
908
|
+
|
|
909
|
+
for (const _deployId of deployList.split(',')) {
|
|
910
|
+
const deployId = _deployId.trim();
|
|
911
|
+
if (!deployId) continue;
|
|
912
|
+
if (options.expose === true) {
|
|
913
|
+
const kindType = options.kindType ? options.kindType : 'svc';
|
|
914
|
+
const svc = Underpost.kubectl.get(deployId, kindType)[0];
|
|
915
|
+
if (!svc) {
|
|
916
|
+
logger.error(`No ${kindType} found matching '${deployId}', skipping expose`);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (options.localProxy) {
|
|
920
|
+
const svcPorts = [
|
|
921
|
+
...new Set(
|
|
922
|
+
svc['PORT(S)']
|
|
923
|
+
.split(',')
|
|
924
|
+
.filter((p) => p.includes('/TCP'))
|
|
925
|
+
.map((p) => parseInt(p.split(':')[0])),
|
|
926
|
+
),
|
|
927
|
+
];
|
|
928
|
+
for (const svcPort of svcPorts) {
|
|
929
|
+
shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${svcPort}:${svcPort}`, {
|
|
930
|
+
async: true,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
const envFile = `./engine-private/conf/${deployId}/.env.${env}`;
|
|
934
|
+
let basePort = svcPorts[0] - 1;
|
|
935
|
+
if (fs.existsSync(envFile)) {
|
|
936
|
+
const portMatch = fs.readFileSync(envFile, 'utf8').match(/^PORT=(\d+)/m);
|
|
937
|
+
if (portMatch) basePort = parseInt(portMatch[1]);
|
|
938
|
+
}
|
|
939
|
+
logger.info(deployId, { svc, svcPorts, basePort });
|
|
940
|
+
const tlsFlag = options.tls ? ' tls' : '';
|
|
941
|
+
shellExec(
|
|
942
|
+
`NODE_ENV=${env} PORT=${basePort} DEV_PROXY_PORT_OFFSET=0 node src/proxy proxy ${deployId} ${env}${tlsFlag}`,
|
|
943
|
+
{ async: true },
|
|
944
|
+
);
|
|
945
|
+
} else {
|
|
946
|
+
const remotePort = options.exposePort
|
|
947
|
+
? parseInt(options.exposePort)
|
|
948
|
+
: options.port
|
|
949
|
+
? parseInt(options.port)
|
|
950
|
+
: kindType !== 'svc'
|
|
951
|
+
? 80
|
|
952
|
+
: parseInt(svc[`PORT(S)`].split('/TCP')[0]);
|
|
953
|
+
const localPort = options.exposeLocalPort ? parseInt(options.exposeLocalPort) : remotePort;
|
|
954
|
+
logger.info(deployId, {
|
|
955
|
+
svc,
|
|
956
|
+
localPort,
|
|
957
|
+
remotePort,
|
|
958
|
+
});
|
|
959
|
+
shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${localPort}:${remotePort}`, {
|
|
960
|
+
async: true,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
|
|
967
|
+
const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
|
|
968
|
+
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
|
|
969
|
+
: [];
|
|
970
|
+
|
|
971
|
+
if (!options.disableUpdateDeployment)
|
|
972
|
+
for (const version of options.versions.split(',')) {
|
|
973
|
+
shellExec(
|
|
974
|
+
`sudo kubectl delete svc ${deployId}-${env}-${version}-service -n ${namespace} --ignore-not-found`,
|
|
975
|
+
);
|
|
976
|
+
shellExec(
|
|
977
|
+
`sudo kubectl delete deployment ${deployId}-${env}-${version} -n ${namespace} --ignore-not-found`,
|
|
978
|
+
);
|
|
979
|
+
if (!options.disableUpdateVolume)
|
|
980
|
+
for (const volume of confVolume)
|
|
981
|
+
Underpost.deploy.deployVolume(volume, {
|
|
982
|
+
deployId,
|
|
983
|
+
env,
|
|
984
|
+
version,
|
|
985
|
+
namespace,
|
|
986
|
+
nodeName: Underpost.deploy.resolveDeployNode({
|
|
987
|
+
node: options.node,
|
|
988
|
+
kind: options.kind,
|
|
989
|
+
kubeadm: options.kubeadm,
|
|
990
|
+
k3s: options.k3s,
|
|
991
|
+
env,
|
|
992
|
+
}),
|
|
993
|
+
clusterContext: options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind',
|
|
994
|
+
gitClean: options.gitClean || false,
|
|
995
|
+
sshKeyPath: options.sshKeyPath || '',
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
for (const host of Object.keys(confServer)) {
|
|
1000
|
+
if (!options.disableUpdateProxy) {
|
|
1001
|
+
shellExec(`sudo kubectl delete HTTPProxy ${host} -n ${namespace} --ignore-not-found`);
|
|
1002
|
+
if (Underpost.deploy.isValidTLSContext({ host, env, options }))
|
|
1003
|
+
shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const manifestsPath =
|
|
1008
|
+
env === 'production'
|
|
1009
|
+
? `engine-private/conf/${deployId}/build/production`
|
|
1010
|
+
: `manifests/deployment/${deployId}-${env}`;
|
|
1011
|
+
|
|
1012
|
+
if (!options.remove) {
|
|
1013
|
+
if (!options.disableUpdateDeployment) {
|
|
1014
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml -n ${namespace}`);
|
|
1015
|
+
const grpcServicePath = `./${manifestsPath}/grpc-service.yaml`;
|
|
1016
|
+
if (fs.existsSync(grpcServicePath)) shellExec(`sudo kubectl apply -f ${grpcServicePath} -n ${namespace}`);
|
|
1017
|
+
}
|
|
1018
|
+
if (!options.disableUpdateProxy)
|
|
1019
|
+
shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
|
|
1020
|
+
|
|
1021
|
+
if (
|
|
1022
|
+
Underpost.deploy.isValidTLSContext({ host: Object.keys(confServer)[0], env, options }) &&
|
|
1023
|
+
!options.selfSigned
|
|
1024
|
+
) {
|
|
1025
|
+
const secretPath = `./${manifestsPath}/secret.yaml`;
|
|
1026
|
+
if (fs.existsSync(secretPath) && fs.readFileSync(secretPath, 'utf8').trim()) {
|
|
1027
|
+
shellExec(`sudo kubectl apply -f ${secretPath} -n ${namespace}`);
|
|
1028
|
+
} else logger.info('Skipping secret.yaml apply (no objects yet; applied by the --cert step)');
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
/**
|
|
1034
|
+
* Creates a Kubernetes Secret for a deployment (replaces configMap for secret data).
|
|
1035
|
+
* Secrets are mounted as tmpfs (never written to node disk) and support RBAC restrictions.
|
|
1036
|
+
* @param {string} env - Environment for which the secret is being created.
|
|
1037
|
+
* @param {string} [namespace='default'] - Kubernetes namespace for the secret.
|
|
1038
|
+
* @memberof UnderpostDeploy
|
|
1039
|
+
*/
|
|
1040
|
+
configMap(env, namespace = 'default') {
|
|
1041
|
+
const cronDeployId = cronDeployIdResolve() || 'dd-cron';
|
|
1042
|
+
const envFilePath = `/home/dd/engine/engine-private/conf/${cronDeployId}/.env.${env}`;
|
|
1043
|
+
// `--from-env-file` turns every KEY=VALUE into a secret key that the Deployment injects via
|
|
1044
|
+
// `envFrom`. Strip shell/runtime-critical keys (notably PATH) first — an injected PATH
|
|
1045
|
+
// overrides the image's own and breaks coreutils/sudo resolution inside the pod.
|
|
1046
|
+
const sanitizedEnvPath = `${envFilePath}.secret`;
|
|
1047
|
+
fs.writeFileSync(sanitizedEnvPath, Underpost.secret.sanitizeSecretEnvFile(fs.readFileSync(envFilePath, 'utf8')));
|
|
1048
|
+
shellExec(`kubectl delete secret underpost-config -n ${namespace} --ignore-not-found`);
|
|
1049
|
+
shellExec(
|
|
1050
|
+
`kubectl create secret generic underpost-config --from-env-file=${sanitizedEnvPath} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
|
|
1051
|
+
);
|
|
1052
|
+
fs.removeSync(sanitizedEnvPath);
|
|
1053
|
+
},
|
|
1054
|
+
/**
|
|
1055
|
+
* Switches the traffic for a deployment.
|
|
1056
|
+
* @param {string} deployId - Deployment ID for which the traffic is being switched.
|
|
1057
|
+
* @param {string} env - Environment for which the traffic is being switched.
|
|
1058
|
+
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
1059
|
+
* @param {number} replicas - Number of replicas for the deployment.
|
|
1060
|
+
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
1061
|
+
* @param {object} options - Options for the traffic switch.
|
|
1062
|
+
* @param {string} options.timeoutResponse - Timeout response setting for the deployment.
|
|
1063
|
+
* @param {string} options.timeoutIdle - Timeout idle setting for the deployment.
|
|
1064
|
+
* @param {string} options.retryCount - Retry count setting for the deployment.
|
|
1065
|
+
* @param {string} options.retryPerTryTimeout - Retry per-try timeout setting for the deployment.
|
|
1066
|
+
* @param {string} [options.imagePullPolicy] - Container imagePullPolicy override; forwarded to the manifest rebuild triggered here.
|
|
1067
|
+
* @memberof UnderpostDeploy
|
|
1068
|
+
*/
|
|
1069
|
+
switchTraffic(
|
|
1070
|
+
deployId,
|
|
1071
|
+
env,
|
|
1072
|
+
targetTraffic,
|
|
1073
|
+
replicas = 1,
|
|
1074
|
+
namespace = 'default',
|
|
1075
|
+
options = {
|
|
1076
|
+
timeoutResponse: '',
|
|
1077
|
+
timeoutIdle: '',
|
|
1078
|
+
retryCount: '',
|
|
1079
|
+
retryPerTryTimeout: '',
|
|
1080
|
+
imagePullPolicy: '',
|
|
1081
|
+
},
|
|
1082
|
+
) {
|
|
1083
|
+
const timeoutFlags = Underpost.deploy.timeoutFlagsFactory(options);
|
|
1084
|
+
const imagePullPolicyFlag = options.imagePullPolicy ? ` --image-pull-policy ${options.imagePullPolicy}` : '';
|
|
1085
|
+
|
|
1086
|
+
shellExec(
|
|
1087
|
+
`node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} --namespace ${namespace}${timeoutFlags}${imagePullPolicyFlag} ${deployId} ${env}`,
|
|
1088
|
+
);
|
|
1089
|
+
|
|
1090
|
+
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
|
|
1091
|
+
|
|
1092
|
+
const grpcServicePath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
|
|
1093
|
+
if (fs.existsSync(grpcServicePath)) shellExec(`kubectl apply -f ${grpcServicePath} -n ${namespace}`);
|
|
1094
|
+
|
|
1095
|
+
Underpost.env.set(`${deployId}-${env}-traffic`, targetTraffic);
|
|
1096
|
+
},
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Resolves the effective target node for a deployment, applying a single
|
|
1100
|
+
* precedence chain shared by every deploy workflow — the default `deploy`
|
|
1101
|
+
* callback, `run sync`, and custom `run instance` — so node customization
|
|
1102
|
+
* behaves identically everywhere:
|
|
1103
|
+
*
|
|
1104
|
+
* 1. **Explicit node** — `node` (the resolved `--node` value). Upstream
|
|
1105
|
+
* runners derive it from the comma-path field or `--node-name`
|
|
1106
|
+
* (`run sync`: `path.split(',')[4]` > `--node-name` > default) and from
|
|
1107
|
+
* `--node-name` directly (`run instance`).
|
|
1108
|
+
* 2. **`UNDERPOST_DEPLOY_NODE` env** — for kubeadm / k3s, the configured
|
|
1109
|
+
* target node name. This makes hostPath PV `nodeAffinity` deterministic
|
|
1110
|
+
* regardless of where the manifest is *built*: building inside a
|
|
1111
|
+
* container or CI runner would otherwise leak that box's `os.hostname()`
|
|
1112
|
+
* (e.g. a random container id) into `nodeSelector`, pinning the PV to a
|
|
1113
|
+
* node that does not exist in the cluster.
|
|
1114
|
+
* 3. **Cluster-type default** — when nothing above is set: `kind-worker`
|
|
1115
|
+
* for a kind cluster (the node that hosts kind hostPath volumes),
|
|
1116
|
+
* otherwise the control-plane / current host (`os.hostname()`) for
|
|
1117
|
+
* kubeadm / k3s. With no explicit cluster flag, `development` is treated
|
|
1118
|
+
* as kind and `production` as the host, preserving legacy behaviour.
|
|
1119
|
+
*
|
|
1120
|
+
* @param {object} params
|
|
1121
|
+
* @param {string} [params.node=''] - Explicit node (`--node`); highest precedence.
|
|
1122
|
+
* @param {boolean} [params.kind=false] - Kind cluster context.
|
|
1123
|
+
* @param {boolean} [params.kubeadm=false] - Kubeadm cluster context.
|
|
1124
|
+
* @param {boolean} [params.k3s=false] - K3s cluster context.
|
|
1125
|
+
* @param {string} [params.env=''] - Deployment environment; tie-breaker when no cluster flag is set.
|
|
1126
|
+
* @returns {string} The effective node name.
|
|
1127
|
+
* @memberof UnderpostDeploy
|
|
1128
|
+
*/
|
|
1129
|
+
resolveDeployNode({ node = '', kind = false, kubeadm = false, k3s = false, env = '' } = {}) {
|
|
1130
|
+
if (node) return node;
|
|
1131
|
+
const isKind = kind || (!kubeadm && !k3s && env !== 'production');
|
|
1132
|
+
if (isKind) return 'kind-worker';
|
|
1133
|
+
return process.env.UNDERPOST_DEPLOY_NODE || os.hostname();
|
|
1134
|
+
},
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Deploys a volume for a deployment.
|
|
1138
|
+
* @param {object} volume - Volume configuration.
|
|
1139
|
+
* @param {string} volume.claimName - Name of the persistent volume claim.
|
|
1140
|
+
* @param {string} volume.volumeMountPath - Mount path of the volume in the container.
|
|
1141
|
+
* @param {string} volume.volumeName - Name of the volume.
|
|
1142
|
+
* @param {object} options - Options for the volume deployment.
|
|
1143
|
+
* @param {string} options.deployId - Deployment ID.
|
|
1144
|
+
* @param {string} options.env - Environment for the deployment.
|
|
1145
|
+
* @param {string} options.version - Version of the deployment.
|
|
1146
|
+
* @param {string} options.namespace - Kubernetes namespace for the deployment.
|
|
1147
|
+
* @param {string} options.nodeName - Effective target node (already resolved via {@link UnderpostDeploy.resolveDeployNode}). The volume data is written/shipped here and the PV is pinned to it; an empty value falls back to the cluster-type default inside this method.
|
|
1148
|
+
* @param {string} [options.clusterContext='kind'] - Cluster context type ('kind', 'kubeadm', or 'k3s').
|
|
1149
|
+
* @param {boolean} [options.gitClean=false] - Whether to run git clean on volumeMountPath before copying.
|
|
1150
|
+
* @param {string} [options.sshKeyPath=''] - Private key path used when the target node is remote and the volume is shipped over SSH. Empty falls back to copyDirToNode's default (engine-private/deploy/id_rsa).
|
|
1151
|
+
* @memberof UnderpostDeploy
|
|
1152
|
+
*/
|
|
1153
|
+
deployVolume(
|
|
1154
|
+
volume = { claimName: '', volumeMountPath: '', volumeName: '' },
|
|
1155
|
+
options = {
|
|
1156
|
+
deployId: '',
|
|
1157
|
+
env: '',
|
|
1158
|
+
version: '',
|
|
1159
|
+
namespace: '',
|
|
1160
|
+
nodeName: '',
|
|
1161
|
+
clusterContext: 'kind',
|
|
1162
|
+
gitClean: false,
|
|
1163
|
+
sshKeyPath: '',
|
|
1164
|
+
},
|
|
1165
|
+
) {
|
|
1166
|
+
if (!volume.claimName) {
|
|
1167
|
+
logger.warn('Volume claimName is required to deploy volume', volume);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
const { deployId, env, version, namespace } = options;
|
|
1171
|
+
const clusterContext = options.clusterContext || 'kind';
|
|
1172
|
+
const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
|
|
1173
|
+
const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
|
|
1174
|
+
const rootVolumeHostPath = `/home/dd/engine/volume/${pvId}`;
|
|
1175
|
+
if (options.gitClean && volume.volumeMountPath) {
|
|
1176
|
+
Underpost.repo.clean({ paths: [volume.volumeMountPath] });
|
|
1177
|
+
}
|
|
1178
|
+
// The node that physically receives the volume data. hostPath volumes are
|
|
1179
|
+
// node-local, so the data must land on the node where the pod will run, and
|
|
1180
|
+
// the PV is pinned there (nodeAffinity) so the scheduler co-locates the pod
|
|
1181
|
+
// with its volume — never mounting an empty DirectoryOrCreate on another node.
|
|
1182
|
+
let dataNode;
|
|
1183
|
+
if (clusterContext === 'kind') {
|
|
1184
|
+
const kindNode = options.nodeName || 'kind-worker';
|
|
1185
|
+
dataNode = kindNode;
|
|
1186
|
+
shellExec(`docker exec -i ${kindNode} bash -c "mkdir -p ${rootVolumeHostPath}"`);
|
|
1187
|
+
shellExec(`tar -C ${volume.volumeMountPath} -c . | docker cp - ${kindNode}:${rootVolumeHostPath}`);
|
|
1188
|
+
shellExec(
|
|
1189
|
+
`docker exec -i ${kindNode} bash -c "chown -R 1000:1000 ${rootVolumeHostPath}; chmod -R 755 ${rootVolumeHostPath}"`,
|
|
1190
|
+
);
|
|
1191
|
+
} else {
|
|
1192
|
+
const localHost = os.hostname();
|
|
1193
|
+
dataNode = options.nodeName || localHost;
|
|
1194
|
+
if (dataNode === localHost) {
|
|
1195
|
+
// Target node is the control plane / current host: write directly.
|
|
1196
|
+
if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
|
|
1197
|
+
fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
|
|
1198
|
+
} else {
|
|
1199
|
+
// Target node is remote: fs.copySync would only write the control-plane
|
|
1200
|
+
// filesystem, leaving the real node's hostPath empty. Ship the folder to
|
|
1201
|
+
// the node over SSH so the data exists where the pod is pinned.
|
|
1202
|
+
const nodeHost =
|
|
1203
|
+
shellExec(
|
|
1204
|
+
`kubectl get node ${dataNode} -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}'`,
|
|
1205
|
+
{ stdout: true, silent: true, silentOnError: true },
|
|
1206
|
+
).trim() || dataNode;
|
|
1207
|
+
logger.info('Shipping volume to remote node over SSH', {
|
|
1208
|
+
node: dataNode,
|
|
1209
|
+
host: nodeHost,
|
|
1210
|
+
src: volume.volumeMountPath,
|
|
1211
|
+
dest: rootVolumeHostPath,
|
|
1212
|
+
});
|
|
1213
|
+
Underpost.ssh.copyDirToNode({
|
|
1214
|
+
host: nodeHost,
|
|
1215
|
+
localDir: volume.volumeMountPath,
|
|
1216
|
+
remoteDir: rootVolumeHostPath,
|
|
1217
|
+
...(options.sshKeyPath ? { keyPath: options.sshKeyPath } : {}),
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
|
|
1222
|
+
shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
|
|
1223
|
+
shellExec(`kubectl apply -f - -n ${namespace} <<EOF
|
|
1224
|
+
${Underpost.deploy.persistentVolumeFactory({
|
|
1225
|
+
hostPath: rootVolumeHostPath,
|
|
1226
|
+
pvcId,
|
|
1227
|
+
namespace,
|
|
1228
|
+
nodeName: dataNode,
|
|
1229
|
+
})}
|
|
1230
|
+
EOF
|
|
1231
|
+
`);
|
|
1232
|
+
},
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Creates volume mounts and volumes for a deployment.
|
|
1236
|
+
* @param {Array<volume>} volumes - List of volume configurations.
|
|
1237
|
+
* @param {string} volume.volumeName - Name of the volume.
|
|
1238
|
+
* @param {string} volume.volumeMountPath - Mount path of the volume in the container.
|
|
1239
|
+
* @param {string} volume.volumeHostPath - Host path of the volume.
|
|
1240
|
+
* @param {string} volume.volumeType - Type of the volume (e.g. 'Directory').
|
|
1241
|
+
* @param {string|null} volume.claimName - Name of the persistent volume claim (if applicable).
|
|
1242
|
+
* @param {string|null} volume.configMap - Name of the config map (if applicable).
|
|
1243
|
+
* @param {string|null} volume.secret - Name of the Kubernetes Secret (if applicable). Mounts as readOnly.
|
|
1244
|
+
* @param {boolean} [volume.emptyDir=false] - If true, uses an emptyDir volume (writable tmpfs).
|
|
1245
|
+
* @returns {object} - Object containing the rendered volume mounts and volumes.
|
|
1246
|
+
* @memberof UnderpostDeploy
|
|
1247
|
+
*/
|
|
1248
|
+
volumeFactory(
|
|
1249
|
+
volumes = [
|
|
1250
|
+
{
|
|
1251
|
+
volumeName: 'volume-name',
|
|
1252
|
+
volumeMountPath: '/path/in/container',
|
|
1253
|
+
volumeHostPath: '/path/on/host',
|
|
1254
|
+
volumeType: 'Directory',
|
|
1255
|
+
claimName: null,
|
|
1256
|
+
configMap: null,
|
|
1257
|
+
version: null,
|
|
1258
|
+
},
|
|
1259
|
+
],
|
|
1260
|
+
) {
|
|
1261
|
+
let _volumeMounts = `
|
|
1262
|
+
volumeMounts:`;
|
|
1263
|
+
let _volumes = `
|
|
1264
|
+
volumes:`;
|
|
1265
|
+
volumes.map((volumeData) => {
|
|
1266
|
+
let {
|
|
1267
|
+
volumeName,
|
|
1268
|
+
volumeMountPath,
|
|
1269
|
+
volumeHostPath,
|
|
1270
|
+
volumeType,
|
|
1271
|
+
claimName,
|
|
1272
|
+
configMap,
|
|
1273
|
+
secret,
|
|
1274
|
+
emptyDir,
|
|
1275
|
+
version,
|
|
1276
|
+
} = volumeData;
|
|
1277
|
+
if (version) {
|
|
1278
|
+
volumeName = `${volumeName}-${version}`;
|
|
1279
|
+
claimName = claimName ? `${claimName}-${version}` : null;
|
|
1280
|
+
}
|
|
1281
|
+
// The pod-local volume name is a DNS-1123 label (max 63 chars); the PVC
|
|
1282
|
+
// `claimName` it references is a subdomain (max 253) and stays verbatim.
|
|
1283
|
+
// Per-variant instance names append <deployId>-<env>-<traffic> and can
|
|
1284
|
+
// exceed 63, so clamp only the pod-local name (mount name must match it).
|
|
1285
|
+
const podVolumeName = k8sVolumeName(volumeName);
|
|
1286
|
+
_volumeMounts += `
|
|
1287
|
+
- name: ${podVolumeName}
|
|
1288
|
+
mountPath: ${volumeMountPath}
|
|
1289
|
+
${secret ? ` readOnly: true\n` : ''}`;
|
|
1290
|
+
|
|
1291
|
+
_volumes += `
|
|
1292
|
+
- name: ${podVolumeName}
|
|
1293
|
+
${
|
|
1294
|
+
emptyDir
|
|
1295
|
+
? ` emptyDir: {}`
|
|
1296
|
+
: secret
|
|
1297
|
+
? ` secret:
|
|
1298
|
+
secretName: ${secret}`
|
|
1299
|
+
: configMap
|
|
1300
|
+
? ` configMap:
|
|
1301
|
+
name: ${configMap}`
|
|
1302
|
+
: claimName
|
|
1303
|
+
? ` persistentVolumeClaim:
|
|
1304
|
+
claimName: ${claimName}`
|
|
1305
|
+
: ` hostPath:
|
|
1306
|
+
path: ${volumeHostPath}
|
|
1307
|
+
type: ${volumeType}
|
|
1308
|
+
`
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
`;
|
|
1312
|
+
});
|
|
1313
|
+
return { render: _volumeMounts + _volumes };
|
|
1314
|
+
},
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* Creates a persistent volume and persistent volume claim for a deployment.
|
|
1318
|
+
* @param {object} options - Options for the persistent volume and claim creation.
|
|
1319
|
+
* @param {string} options.hostPath - Host path for the persistent volume.
|
|
1320
|
+
* @param {string} options.pvcId - Persistent volume claim ID.
|
|
1321
|
+
* @param {string} [options.namespace='default'] - Kubernetes namespace for the PVC claimRef.
|
|
1322
|
+
* @param {string} [options.nodeName=''] - Node name to which the persistent volume is pinned (optional).
|
|
1323
|
+
* @returns {string} - YAML configuration for the persistent volume and claim.
|
|
1324
|
+
* @memberof UnderpostDeploy
|
|
1325
|
+
*/
|
|
1326
|
+
persistentVolumeFactory({ hostPath, pvcId, namespace = 'default', nodeName = '' }) {
|
|
1327
|
+
const pvId = pvcId.replace(/^pvc-/, 'pv-');
|
|
1328
|
+
// hostPath volumes are node-local: deployVolume writes the content to the
|
|
1329
|
+
// filesystem of a single node. Without nodeAffinity the scheduler can place
|
|
1330
|
+
// the pod on a different node and mount an empty DirectoryOrCreate hostPath
|
|
1331
|
+
// (missing the materialized assets). Pin the PV to the node that holds the
|
|
1332
|
+
// data so the pod is always co-located with its volume.
|
|
1333
|
+
const nodeAffinity = nodeName
|
|
1334
|
+
? `
|
|
1335
|
+
nodeAffinity:
|
|
1336
|
+
required:
|
|
1337
|
+
nodeSelectorTerms:
|
|
1338
|
+
- matchExpressions:
|
|
1339
|
+
- key: kubernetes.io/hostname
|
|
1340
|
+
operator: In
|
|
1341
|
+
values:
|
|
1342
|
+
- ${nodeName}`
|
|
1343
|
+
: '';
|
|
1344
|
+
return `apiVersion: v1
|
|
1345
|
+
kind: PersistentVolume
|
|
1346
|
+
metadata:
|
|
1347
|
+
name: ${pvId}
|
|
1348
|
+
spec:
|
|
1349
|
+
capacity:
|
|
1350
|
+
storage: 5Gi
|
|
1351
|
+
accessModes:
|
|
1352
|
+
- ReadWriteOnce
|
|
1353
|
+
persistentVolumeReclaimPolicy: Retain
|
|
1354
|
+
storageClassName: manual${nodeAffinity}
|
|
1355
|
+
claimRef:
|
|
1356
|
+
apiVersion: v1
|
|
1357
|
+
kind: PersistentVolumeClaim
|
|
1358
|
+
name: ${pvcId}
|
|
1359
|
+
namespace: ${namespace}
|
|
1360
|
+
hostPath:
|
|
1361
|
+
path: ${hostPath}
|
|
1362
|
+
type: DirectoryOrCreate
|
|
1363
|
+
---
|
|
1364
|
+
apiVersion: v1
|
|
1365
|
+
kind: PersistentVolumeClaim
|
|
1366
|
+
metadata:
|
|
1367
|
+
name: ${pvcId}
|
|
1368
|
+
spec:
|
|
1369
|
+
accessModes:
|
|
1370
|
+
- ReadWriteOnce
|
|
1371
|
+
storageClassName: manual
|
|
1372
|
+
volumeName: ${pvId}
|
|
1373
|
+
resources:
|
|
1374
|
+
requests:
|
|
1375
|
+
storage: 5Gi`;
|
|
1376
|
+
},
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* Checks if a TLS context is valid.
|
|
1380
|
+
* @param {object} options - Options for the check.
|
|
1381
|
+
* @param {string} options.host - Host for which the TLS context is being checked.
|
|
1382
|
+
* @param {string} options.env - Environment for which the TLS context is being checked.
|
|
1383
|
+
* @param {object} options.options - Options for the TLS context check.
|
|
1384
|
+
* @returns {boolean} - True if the TLS context is valid, false otherwise.
|
|
1385
|
+
* @memberof UnderpostDeploy
|
|
1386
|
+
*/
|
|
1387
|
+
isValidTLSContext: ({ host, env, options }) =>
|
|
1388
|
+
(env === 'production' &&
|
|
1389
|
+
options.cert === true &&
|
|
1390
|
+
(!options.certHosts || options.certHosts.split(',').includes(host))) ||
|
|
1391
|
+
options.selfSigned === true,
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Predefined resource templates for Kubernetes deployments.
|
|
1395
|
+
* @memberof UnderpostDeploy
|
|
1396
|
+
*/
|
|
1397
|
+
resourcesTemplate: {
|
|
1398
|
+
dev_small: {
|
|
1399
|
+
id: 'dev_small',
|
|
1400
|
+
useCase: 'microservice_development',
|
|
1401
|
+
resources: {
|
|
1402
|
+
requests: {
|
|
1403
|
+
memory: '128Mi',
|
|
1404
|
+
cpu: '250m',
|
|
1405
|
+
},
|
|
1406
|
+
limits: {
|
|
1407
|
+
memory: '512Mi',
|
|
1408
|
+
cpu: '1',
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
},
|
|
1412
|
+
prod_moderate: {
|
|
1413
|
+
id: 'prod_moderate',
|
|
1414
|
+
useCase: 'production_moderate',
|
|
1415
|
+
resources: {
|
|
1416
|
+
requests: {
|
|
1417
|
+
memory: '256Mi',
|
|
1418
|
+
cpu: '500m',
|
|
1419
|
+
},
|
|
1420
|
+
limits: {
|
|
1421
|
+
memory: '512Mi',
|
|
1422
|
+
cpu: '1',
|
|
1423
|
+
},
|
|
1424
|
+
},
|
|
1425
|
+
},
|
|
1426
|
+
memory_heavy: {
|
|
1427
|
+
id: 'memory_heavy',
|
|
1428
|
+
useCase: 'memory_intensive_app',
|
|
1429
|
+
resources: {
|
|
1430
|
+
requests: {
|
|
1431
|
+
memory: '512Mi',
|
|
1432
|
+
cpu: '500m',
|
|
1433
|
+
},
|
|
1434
|
+
limits: {
|
|
1435
|
+
memory: '1Gi',
|
|
1436
|
+
cpu: '1',
|
|
1437
|
+
},
|
|
1438
|
+
},
|
|
1439
|
+
},
|
|
1440
|
+
cpu_bound: {
|
|
1441
|
+
id: 'cpu_bound',
|
|
1442
|
+
useCase: 'cpu_intensive_job',
|
|
1443
|
+
resources: {
|
|
1444
|
+
requests: {
|
|
1445
|
+
memory: '256Mi',
|
|
1446
|
+
cpu: '1000m',
|
|
1447
|
+
},
|
|
1448
|
+
limits: {
|
|
1449
|
+
memory: '512Mi',
|
|
1450
|
+
cpu: '2000m',
|
|
1451
|
+
},
|
|
1452
|
+
},
|
|
1453
|
+
},
|
|
1454
|
+
},
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Creates a resource object for Kubernetes deployments.
|
|
1458
|
+
* @param {object} resources - Resource specifications.
|
|
1459
|
+
* @param {string} resources.requestsMemory - Memory request for the container.
|
|
1460
|
+
* @param {string} resources.requestsCpu - CPU request for the container.
|
|
1461
|
+
* @param {string} resources.limitsMemory - Memory limit for the container.
|
|
1462
|
+
* @param {string} resources.limitsCpu - CPU limit for the container.
|
|
1463
|
+
* @returns {object|undefined} - Resource object for Kubernetes deployments or undefined if any resource is missing.
|
|
1464
|
+
* @memberof UnderpostDeploy
|
|
1465
|
+
*/
|
|
1466
|
+
resourcesFactory: (
|
|
1467
|
+
resources = {
|
|
1468
|
+
resourceTemplateId: '',
|
|
1469
|
+
requestsMemory: '',
|
|
1470
|
+
requestsCpu: '',
|
|
1471
|
+
limitsMemory: '',
|
|
1472
|
+
limitsCpu: '',
|
|
1473
|
+
},
|
|
1474
|
+
) => {
|
|
1475
|
+
if (resources) {
|
|
1476
|
+
if (resources.resourceTemplateId)
|
|
1477
|
+
return Underpost.deploy.resourcesTemplate[resources.resourceTemplateId].resources;
|
|
1478
|
+
if (resources.requestsMemory && resources.requestsCpu && resources.limitsMemory && resources.limitsCpu)
|
|
1479
|
+
return {
|
|
1480
|
+
requests: {
|
|
1481
|
+
memory: resources.requestsMemory,
|
|
1482
|
+
cpu: resources.requestsCpu,
|
|
1483
|
+
},
|
|
1484
|
+
limits: {
|
|
1485
|
+
memory: resources.limitsMemory,
|
|
1486
|
+
cpu: resources.limitsCpu,
|
|
1487
|
+
},
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
return undefined;
|
|
1491
|
+
},
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Extracts a non-standard `imagePullPolicy` key from an env-resolved
|
|
1495
|
+
* instance lifecycle block (the convention used in `conf.instances.json`,
|
|
1496
|
+
* where `imagePullPolicy` sits alongside `postStart`/`preStop` for
|
|
1497
|
+
* per-instance ergonomics) and returns a clean lifecycle hash that is
|
|
1498
|
+
* safe to splice into the K8S container spec.
|
|
1499
|
+
*
|
|
1500
|
+
* Returns `{ lifecycle, imagePullPolicy }`:
|
|
1501
|
+
* - `lifecycle` — the input minus `imagePullPolicy`, or `undefined` when
|
|
1502
|
+
* the resulting block is empty.
|
|
1503
|
+
* - `imagePullPolicy` — the extracted value, or `undefined` if absent.
|
|
1504
|
+
*
|
|
1505
|
+
* @param {object|undefined} lifecycle - Env-resolved lifecycle block
|
|
1506
|
+
* (already passed through pickEnv). May be `undefined`.
|
|
1507
|
+
* @returns {{ lifecycle: (object|undefined), imagePullPolicy: (string|undefined) }}
|
|
1508
|
+
* @memberof UnderpostDeploy
|
|
1509
|
+
*/
|
|
1510
|
+
extractInstanceImagePullPolicy(lifecycle) {
|
|
1511
|
+
if (!lifecycle || typeof lifecycle !== 'object' || !('imagePullPolicy' in lifecycle)) {
|
|
1512
|
+
return { lifecycle, imagePullPolicy: undefined };
|
|
1513
|
+
}
|
|
1514
|
+
const { imagePullPolicy, ...rest } = lifecycle;
|
|
1515
|
+
return {
|
|
1516
|
+
lifecycle: Object.keys(rest).length > 0 ? rest : undefined,
|
|
1517
|
+
imagePullPolicy,
|
|
1518
|
+
};
|
|
1519
|
+
},
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* Generates timeout flags string for deployment commands.
|
|
1523
|
+
* @param {object} options - Options containing timeout settings.
|
|
1524
|
+
* @param {string|number} [options.timeoutResponse] - Timeout response value.
|
|
1525
|
+
* @param {string|number} [options.timeoutIdle] - Timeout idle value.
|
|
1526
|
+
* @param {string|number} [options.retryCount] - Retry count value.
|
|
1527
|
+
* @param {string|number} [options.retryPerTryTimeout] - Retry per try timeout value.
|
|
1528
|
+
* @returns {string} The timeout flags string.
|
|
1529
|
+
* @memberof UnderpostDeploy
|
|
1530
|
+
*/
|
|
1531
|
+
timeoutFlagsFactory: (options = {}) => {
|
|
1532
|
+
return (
|
|
1533
|
+
`${options.timeoutResponse ? ` --timeout-response ${options.timeoutResponse}` : ''}` +
|
|
1534
|
+
`${options.timeoutIdle ? ` --timeout-idle ${options.timeoutIdle}` : ''}` +
|
|
1535
|
+
`${options.retryCount || options.retryCount === 0 ? ` --retry-count ${options.retryCount}` : ''}` +
|
|
1536
|
+
`${options.retryPerTryTimeout ? ` --retry-per-try-timeout ${options.retryPerTryTimeout}` : ''}`
|
|
1537
|
+
);
|
|
1538
|
+
},
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
export default UnderpostDeploy;
|