From 900a524f9b13778f721a140ceb4eb86b1d1af3cf Mon Sep 17 00:00:00 2001 From: zhx Date: Sat, 18 Jul 2026 15:32:58 +0800 Subject: [PATCH] feat: support Yumi activity runtime config --- .env | 86 --------------------------------- .gitignore | 1 + monitors/yumi/runtime_config.py | 80 ++++++++++++++++++++++++++++++ testbox/.env.example | 3 ++ testbox/docker-compose.apps.yml | 6 +++ tests/test_service_release.py | 75 ++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 86 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index edcf062..0000000 --- a/.env +++ /dev/null @@ -1,86 +0,0 @@ -APP_BIND=0.0.0.0 -APP_PORT=2026 -DEFAULT_REFRESH_INTERVAL_SECONDS=10 -REFRESH_INTERVAL_OPTIONS=5,10,15,30,60 -PROBE_TIMEOUT_SECONDS=3 -PROBE_CACHE_TTL_SECONDS=5 -PROBE_MAX_WORKERS=12 -HOST_METRIC_CACHE_TTL_SECONDS=25 -TAT_METRIC_TIMEOUT_SECONDS=30 -TAT_CPU_SAMPLE_SECONDS=1 -NACOS_BASE_URL=http://10.2.1.17:8848 -NACOS_NAMESPACE_ID=f7a8594e-090c-4830-a3ad-b5c0f9afcc07 -NACOS_NAMESPACE_NAME=RedCircleServiceProd -NACOS_DISCOVERY_GROUP=prod -NACOS_DISCOVERY_CLUSTER_NAME=DEFAULT -NACOS_CACHE_TTL_SECONDS=15 -MONGO_URI=mongodb://root:123456@10.2.21.9:27017/tarab_all?authSource=admin&readPreference=secondaryPreferred&retryWrites=false&maxPoolSize=50&minPoolSize=5&maxIdleTimeMS=6000 -MONGO_DB_NAME=tarab_all -MONGO_INSTANCE_ID=ins-o9mmob66 -MONGO_CACHE_TTL_SECONDS=20 -CPU_WARN_PERCENT=60 -CPU_BAD_PERCENT=85 -MEMORY_WARN_PERCENT=70 -MEMORY_BAD_PERCENT=90 -DISK_WARN_PERCENT=75 -DISK_BAD_PERCENT=90 -PROCESS_WARN_COUNT=180 -PROCESS_BAD_COUNT=260 -SERVICE_LATENCY_WARN_MS=1000 -SERVICE_LATENCY_BAD_MS=3000 -DEPLOY_REGION=me-saudi-arabia -TENCENT_SECRET_ID=IKIDMchhZEfrsiNo472DAtTpzzmLjttkOnyu -TENCENT_SECRET_KEY=nMkbLsGRO6ZqulSyJQJ0UjjU0KSKxOgl -GATEWAY_CLB_LOAD_BALANCER_ID=lb-cbiabr1q -GATEWAY_CLB_LISTENER_IDS=lbl-ktvz8m6k -GATEWAY_CLB_ALLOWED_DOMAINS=jvapi.haiyihy.com -GATEWAY_CLB_POLL_SECONDS=2 -GATEWAY_CLB_TASK_TIMEOUT_SECONDS=120 -GATEWAY_CLB_DRAIN_SECONDS=0 -CDN_ALLOWED_DOMAINS=h5.haiyihy.com -CDN_PURGE_TASK_LIMIT=20 - -# Admin frontend deployment for Yumi -UPDATE_DEFAULT_ADMIN_GIT_REF=main -ADMIN_FRONTEND_ENABLED=1 -ADMIN_FRONTEND_SERVICE_NAME=admin -ADMIN_FRONTEND_REPO_URL=git@gitea.haiyihy.com:hy/chatapp3-admin.git -ADMIN_FRONTEND_REPO_ROOT=/opt/deploy/sources/chatapp3-admin -ADMIN_FRONTEND_HOST=admin-platform -ADMIN_FRONTEND_IP=10.2.1.2 -ADMIN_FRONTEND_INSTANCE_ID=ins-p11begwe -ADMIN_FRONTEND_SSH_HOST=10.2.1.2 -ADMIN_FRONTEND_SSH_USER=root -ADMIN_FRONTEND_SSH_PORT=22 -ADMIN_FRONTEND_SSH_USE_SUDO=0 -ADMIN_FRONTEND_REMOTE_DIR=/opt/yumi-admin/frontend-admin -ADMIN_FRONTEND_SITE_DIR=/opt/yumi-admin/frontend-admin/site/yumiadmin-acc -ADMIN_FRONTEND_COMPOSE_FILE=/opt/yumi-admin/frontend-admin/docker-compose.yml -ADMIN_FRONTEND_COMPOSE_SERVICE=caddy -ADMIN_FRONTEND_RELEASE_META_PATH=/opt/yumi-admin/frontend-admin/.hy-app-monitor-admin-release.json -ADMIN_FRONTEND_BUILD_SCRIPT=build:antdv-next -ADMIN_FRONTEND_DIST_DIR=apps/dist -ADMIN_FRONTEND_PUBLIC_URL=https://yumiadmin-acc.global-interaction.com -CHATAPP_CRON_ENABLED=1 -CHATAPP_CRON_REPO_URL=git@gitea.haiyihy.com:hy/chatapp-cron.git -CHATAPP_CRON_REPO_ROOT=/opt/deploy/sources/chatapp-cron -CHATAPP_CRON_DEFAULT_GIT_REF=main -CHATAPP_CRON_SERVICE_NAME=chatapp-cron -CHATAPP_CRON_HOST=cron-sg-1 -CHATAPP_CRON_IP=10.3.1.14 -CHATAPP_CRON_INSTANCE_ID=ins-nt6bsay0 -CHATAPP_CRON_SSH_HOST=10.3.1.14 -CHATAPP_CRON_SSH_USER=root -CHATAPP_CRON_SSH_PORT=22 -CHATAPP_CRON_SSH_USE_SUDO=0 -CHATAPP_CRON_REMOTE_DIR=/opt/likei/cron-sg-1 -CHATAPP_CRON_COMPOSE_FILE=/opt/likei/cron-sg-1/docker-compose.yml -CHATAPP_CRON_COMPOSE_SERVICE=week-star-job -CHATAPP_CRON_BINARY_PATH=/opt/likei/cron-sg-1/week-star-job/service -CHATAPP_CRON_RELEASE_META_PATH=/opt/likei/cron-sg-1/.hy-app-monitor-chatapp-cron-release.json - -# Harbor credentials for hy-app-monitor service update -HARBOR_REGISTRY=10.2.1.3:18082 -HARBOR_PROJECT=likei -HARBOR_USERNAME=admin -HARBOR_PASSWORD=QkVXKcxCff_3x-pEkDCR_aAB diff --git a/.gitignore b/.gitignore index 77683c5..bb3e30e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ *.pyc .DS_Store .venv/ +.env diff --git a/monitors/yumi/runtime_config.py b/monitors/yumi/runtime_config.py index c8f4742..7085cfa 100644 --- a/monitors/yumi/runtime_config.py +++ b/monitors/yumi/runtime_config.py @@ -83,6 +83,14 @@ GO_ENV_SPECS: list[dict[str, Any]] = [ "aliases": ("GAME_JAVA_OTHER_BASE_URL", "INVITE_JAVA_APP_BASE_URL"), "kind": "string", }, + { + "section": "Java", + "primary": "CHATAPP_JAVA_ORDER_BASE_URL", + # LIKEI_GATEWAY_ROUTE_ORDER_URI 只是 Go 的最后兜底,同时被 Java 网关消费; + # 不能把它当等价 alias 回写,否则保存 Go 配置会意外改掉共享网关路由。 + "aliases": ("GAME_JAVA_ORDER_BASE_URL",), + "kind": "string", + }, { "section": "Java", "primary": "CHATAPP_JAVA_WALLET_BASE_URL", @@ -329,6 +337,74 @@ GO_ENV_SPECS: list[dict[str, Any]] = [ "aliases": ("WEEK_STAR_ROCKETMQ_TAG",), "kind": "string", }, + { + "section": "Yumi Gift Challenge", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_DEFAULT_SYS_ORIGIN", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_DELIVERY_LEASE_SECONDS", + "aliases": (), + "kind": "positive_int", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENABLED", + "aliases": (), + "kind": "bool", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENDPOINT", + # LIKEI / WeekStar MQ key 仅是运行时兜底,不能作为可回写 alias;这些共享 key + # 还被其他消费者使用,Yumi 页面保存时必须只修改自己的命名空间。 + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_NAMESPACE", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ACCESS_KEY", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ACCESS_SECRET", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_SECURITY_TOKEN", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_CONSUMER_GROUP", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_TOPIC", + "aliases": (), + "kind": "string", + }, + { + "section": "Yumi Gift Challenge MQ", + "primary": "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_TAG", + "aliases": (), + "kind": "string", + }, { "section": "Baishun", "primary": "CHATAPP_BAISHUN_PLATFORM_BASE_URL", @@ -557,6 +633,10 @@ def validate_scalar_kind(primary_key: str, value: str, kind: str) -> None: if kind == "int": int(value) return + if kind == "positive_int": + if int(value) <= 0: + raise ValueError(f"{primary_key} must be a positive integer") + return if kind == "bool": if value.strip().lower() not in {"1", "0", "true", "false", "yes", "no", "on", "off"}: raise ValueError(f"{primary_key} must be a boolean") diff --git a/testbox/.env.example b/testbox/.env.example index 75d4afa..be26397 100644 --- a/testbox/.env.example +++ b/testbox/.env.example @@ -231,6 +231,9 @@ CHATAPP_WEEK_STAR_TIMEZONE=Asia/Riyadh CHATAPP_ROOM_TURNOVER_REWARD_TIMEZONE=Asia/Riyadh CHATAPP_WEEK_STAR_SETTLE_INTERVAL_SECONDS=60 CHATAPP_ROOM_TURNOVER_REWARD_SETTLE_INTERVAL_SECONDS=60 +CHATAPP_GAME_KING_SETTLE_INTERVAL_SECONDS=60 +# Game King 每轮最多派送 4 组(单组硬超时 20 秒);120 秒为 DB 冻结和状态修复保留余量。 +CHATAPP_GAME_KING_SETTLE_TIMEOUT_SECONDS=120 CHATAPP_WEEK_STAR_ROCKETMQ_ENABLED=false CHATAPP_WEEK_STAR_ROCKETMQ_ENDPOINT=rocketmq-proxy:8081 CHATAPP_WEEK_STAR_ROCKETMQ_ACCESS_KEY=test diff --git a/testbox/docker-compose.apps.yml b/testbox/docker-compose.apps.yml index e49f3b3..fc66491 100644 --- a/testbox/docker-compose.apps.yml +++ b/testbox/docker-compose.apps.yml @@ -224,12 +224,18 @@ services: condition: service_healthy redis: condition: service_healthy + # Game King 的 cron 只触发 Go 内部结算接口;等待健康后启动可避免发布窗口内的无效首轮请求。 + golang: + condition: service_healthy other: condition: service_started wallet: condition: service_started env_file: - .env + environment: + # 容器内 127.0.0.1 指向 cron 自身,必须使用 Compose 服务名访问 Golang。 + CHATAPP_GO_BASE_URL: http://golang:2900 admin: image: chatapp-test/admin:testbox diff --git a/tests/test_service_release.py b/tests/test_service_release.py index 9ed8a44..b5b9a2f 100644 --- a/tests/test_service_release.py +++ b/tests/test_service_release.py @@ -4,11 +4,86 @@ import unittest from unittest.mock import patch from core.config import HARBOR_PROJECT, HARBOR_REGISTRY, load_hosts +from monitors.yumi.runtime_config import ( + GO_ENV_PRIMARY_BY_KEY, + GO_ENV_SPECS_BY_PRIMARY, + expand_host_updates, + validate_go_editor_content, +) from monitors.yumi.service_release import build_update_targets_payload, deploy_updated_services_payload from monitors.yumi.service_ops import service_records_by_name class ServiceReleaseTargetsTests(unittest.TestCase): + def test_yumi_activity_runtime_envs_are_editable_with_strict_types(self) -> None: + expected_keys = { + "CHATAPP_JAVA_ORDER_BASE_URL", + "CHATAPP_YUMI_GIFT_CHALLENGE_DEFAULT_SYS_ORIGIN", + "CHATAPP_YUMI_GIFT_CHALLENGE_DELIVERY_LEASE_SECONDS", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENABLED", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENDPOINT", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_NAMESPACE", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ACCESS_KEY", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ACCESS_SECRET", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_SECURITY_TOKEN", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_CONSUMER_GROUP", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_TOPIC", + "CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_TAG", + } + + self.assertTrue(expected_keys.issubset(GO_ENV_SPECS_BY_PRIMARY)) + self.assertEqual( + GO_ENV_PRIMARY_BY_KEY["GAME_INTERNAL_CALLBACK_SECRET"], + "CHATAPP_HTTP_INTERNAL_CALLBACK_SECRET", + ) + self.assertEqual( + GO_ENV_PRIMARY_BY_KEY["GAME_JAVA_ORDER_BASE_URL"], + "CHATAPP_JAVA_ORDER_BASE_URL", + ) + + valid = validate_go_editor_content( + "\n".join( + [ + 'CHATAPP_HTTP_INTERNAL_CALLBACK_SECRET="shared-secret"', + 'GAME_JAVA_ORDER_BASE_URL="http://10.2.1.15:2600"', + 'CHATAPP_YUMI_GIFT_CHALLENGE_DELIVERY_LEASE_SECONDS="300"', + 'CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENABLED="true"', + 'CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENDPOINT="rocketmq.example:8081"', + ] + ) + ) + self.assertTrue(valid["ok"]) + self.assertEqual(valid["values"]["CHATAPP_JAVA_ORDER_BASE_URL"], "http://10.2.1.15:2600") + + invalid_lease = validate_go_editor_content( + 'CHATAPP_YUMI_GIFT_CHALLENGE_DELIVERY_LEASE_SECONDS="0"' + ) + self.assertFalse(invalid_lease["ok"]) + self.assertIn("positive integer", invalid_lease["message"]) + + invalid_enabled = validate_go_editor_content( + 'CHATAPP_YUMI_GIFT_CHALLENGE_ROCKETMQ_ENABLED="enabled"' + ) + self.assertFalse(invalid_enabled["ok"]) + self.assertIn("must be a boolean", invalid_enabled["message"]) + + def test_game_king_legacy_env_shape_is_preserved_when_saved(self) -> None: + updates = expand_host_updates( + { + "GAME_INTERNAL_CALLBACK_SECRET": "old-secret", + "GAME_JAVA_ORDER_BASE_URL": "http://old-order:2600", + }, + { + "CHATAPP_HTTP_INTERNAL_CALLBACK_SECRET": "new-secret", + "CHATAPP_JAVA_ORDER_BASE_URL": "http://new-order:2600", + }, + ) + + self.assertEqual(updates["GAME_INTERNAL_CALLBACK_SECRET"], "new-secret") + self.assertEqual(updates["GAME_JAVA_ORDER_BASE_URL"], "http://new-order:2600") + self.assertNotIn("CHATAPP_HTTP_INTERNAL_CALLBACK_SECRET", updates) + self.assertNotIn("CHATAPP_JAVA_ORDER_BASE_URL", updates) + def test_runtime_topology_keeps_singapore_cron_out_of_golang_release(self) -> None: hosts = load_hosts() cron_hosts = [host for host in hosts if host["host"] == "cron-sg-1"]