cancan-microstack 0.0.1__py3-none-any.whl
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.
- cancan_microstack/__init__.py +14 -0
- cancan_microstack/__version__.py +10 -0
- cancan_microstack/assets/__init__.py +6 -0
- cancan_microstack/assets/builds/caddy/Caddyfile +187 -0
- cancan_microstack/assets/builds/caddy/DEPLOYMENT.md +303 -0
- cancan_microstack/assets/builds/caddy/Dockerfile +46 -0
- cancan_microstack/assets/builds/caddy/README.md +343 -0
- cancan_microstack/assets/builds/caddy/geoip/README.md +5 -0
- cancan_microstack/assets/builds/caddy/start.sh +78 -0
- cancan_microstack/assets/builds/caddy/waf/coraza.conf +179 -0
- cancan_microstack/assets/builds/service/Dockerfile +59 -0
- cancan_microstack/assets/builds/service/README.md +13 -0
- cancan_microstack/assets/ddl/create_db.sql +22 -0
- cancan_microstack/assets/ddl/infra/execution_log_tbl.sql +46 -0
- cancan_microstack/assets/ddl/infra/node_instance_tbl.sql +56 -0
- cancan_microstack/assets/ddl/infra/service_action_log_tbl.sql +36 -0
- cancan_microstack/assets/ddl/infra/service_config_tbl.sql +26 -0
- cancan_microstack/assets/ddl/infra/service_info_tbl.sql +45 -0
- cancan_microstack/assets/ddl/infra/service_instance_tbl.sql +54 -0
- cancan_microstack/assets/ddl/infra/service_operation_tbl.sql +47 -0
- cancan_microstack/assets/ddl/infra/workflow_definition_tbl.sql +60 -0
- cancan_microstack/assets/ddl/infra/workflow_definition_version_tbl.sql +35 -0
- cancan_microstack/assets/ddl/infra/workflow_engine_alert_tbl.sql +34 -0
- cancan_microstack/assets/ddl/infra/workflow_run_tbl.sql +52 -0
- cancan_microstack/assets/ddl/ops/admin_user_tbl.sql +34 -0
- cancan_microstack/assets/ddl/ops/caddy_access_log_tbl.sql +91 -0
- cancan_microstack/assets/ddl/ops/caddy_certificate_tbl.sql +59 -0
- cancan_microstack/assets/ddl/ops/caddy_rate_limit_tbl.sql +64 -0
- cancan_microstack/assets/ddl/ops/caddy_route_tbl.sql +63 -0
- cancan_microstack/assets/ddl/ops/caddy_stats_tbl.sql +77 -0
- cancan_microstack/assets/ddl/trigger.sql +21 -0
- cancan_microstack/assets/docker/docker-compose.infra.yml +401 -0
- cancan_microstack/assets/scripts/README.md +195 -0
- cancan_microstack/assets/scripts/docker/build_images.sh +44 -0
- cancan_microstack/assets/scripts/docker/force_rebuild_images.sh +38 -0
- cancan_microstack/assets/scripts/docker/rebuild_all.sh +34 -0
- cancan_microstack/assets/scripts/docker/rebuild_compose.sh +61 -0
- cancan_microstack/assets/scripts/docker/restart.sh +35 -0
- cancan_microstack/assets/scripts/docker/restart_compose.sh +35 -0
- cancan_microstack/assets/scripts/docker/start.sh +78 -0
- cancan_microstack/assets/scripts/docker/start_all.sh +46 -0
- cancan_microstack/assets/scripts/docker/start_compose.sh +66 -0
- cancan_microstack/assets/scripts/docker/stop.sh +67 -0
- cancan_microstack/assets/scripts/docker/stop_all.sh +38 -0
- cancan_microstack/assets/scripts/docker/stop_compose.sh +38 -0
- cancan_microstack/assets/scripts/podman/build_images_podman.sh +59 -0
- cancan_microstack/assets/scripts/podman/cleanup_podman.sh +25 -0
- cancan_microstack/assets/scripts/podman/force_rebuild_images_podman.sh +56 -0
- cancan_microstack/assets/scripts/podman/rebuild_all_podman.sh +37 -0
- cancan_microstack/assets/scripts/podman/rebuild_compose_podman.sh +60 -0
- cancan_microstack/assets/scripts/podman/restart_compose_podman.sh +73 -0
- cancan_microstack/assets/scripts/podman/start_all_podman.sh +66 -0
- cancan_microstack/assets/scripts/podman/start_compose_podman.sh +80 -0
- cancan_microstack/assets/scripts/podman/start_podman.sh +91 -0
- cancan_microstack/assets/scripts/podman/stop.sh +73 -0
- cancan_microstack/assets/scripts/podman/stop_all_podman.sh +34 -0
- cancan_microstack/assets/scripts/podman/stop_compose_podman.sh +58 -0
- cancan_microstack/assets/scripts/start_controllersrv.sh +9 -0
- cancan_microstack/assets/scripts/utils/check_all_db_tables.sh +104 -0
- cancan_microstack/assets/scripts/utils/check_env.sh +177 -0
- cancan_microstack/assets/scripts/utils/check_service_management_deployment.sh +225 -0
- cancan_microstack/assets/scripts/utils/deploy_service_management.sh +176 -0
- cancan_microstack/assets/scripts/utils/force_reload_infrasrv.sh +52 -0
- cancan_microstack/assets/scripts/utils/monitor_service_management.sh +187 -0
- cancan_microstack/assets/scripts/utils/reset_postgres_volume.sh +68 -0
- cancan_microstack/assets/scripts/utils/test_async_operations.sh +141 -0
- cancan_microstack/assets/scripts/utils/verify_real_operations.sh +76 -0
- cancan_microstack/assets/service/Dockerfile +65 -0
- cancan_microstack/assets/www/adminops/assets/AppEmpty.vue_vue_type_script_setup_true_lang-BOKUurnM.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ConfigManage-DKV5YOUz.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ConfigManage-Y5bhy7wG.css +1 -0
- cancan_microstack/assets/www/adminops/assets/ConsoleManage-8ljYvCW2.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ConsoleManage-BWpyqbuQ.css +1 -0
- cancan_microstack/assets/www/adminops/assets/DashboardNew-B9Nf1OPl.js +1 -0
- cancan_microstack/assets/www/adminops/assets/DashboardNew-DYWZKQ1V.css +1 -0
- cancan_microstack/assets/www/adminops/assets/LogSearch-CA0Jhe78.js +1 -0
- cancan_microstack/assets/www/adminops/assets/LogSearch-CCZfTNPF.css +1 -0
- cancan_microstack/assets/www/adminops/assets/LoginView-BId3kP3M.css +1 -0
- cancan_microstack/assets/www/adminops/assets/LoginView-BQZTV_Qy.js +1 -0
- cancan_microstack/assets/www/adminops/assets/OperationProgressDialog-BdEYwqFq.js +1 -0
- cancan_microstack/assets/www/adminops/assets/OperationProgressDialog-D-pASR8G.css +1 -0
- cancan_microstack/assets/www/adminops/assets/PageContainer-Byss-yUC.js +1 -0
- cancan_microstack/assets/www/adminops/assets/PageContainer-C3nSZwM7.css +1 -0
- cancan_microstack/assets/www/adminops/assets/RateLimitManage-BDI8jLpC.css +1 -0
- cancan_microstack/assets/www/adminops/assets/RateLimitManage-DJY4NiF-.js +1 -0
- cancan_microstack/assets/www/adminops/assets/RouteManage-DaUQ4QLw.css +1 -0
- cancan_microstack/assets/www/adminops/assets/RouteManage-w9XCU0UA.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceCard-BFzHe6Tw.css +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceCard-BJUhWnA-.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceDetail-Cw24WuKp.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceDetail-Yum47zdB.css +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceList-C7ryvbhE.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceList-Cgd01fUx.css +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceLogs-COpG9H0h.js +1 -0
- cancan_microstack/assets/www/adminops/assets/ServiceLogs-H_Alq0cf.css +1 -0
- cancan_microstack/assets/www/adminops/assets/StatsOverview-D0TwMQkA.js +39 -0
- cancan_microstack/assets/www/adminops/assets/StatsOverview-lqAN6pqM.css +1 -0
- cancan_microstack/assets/www/adminops/assets/TotpBindView-CWlAmzFt.js +1 -0
- cancan_microstack/assets/www/adminops/assets/TotpBindView-HoQC1lhx.css +1 -0
- cancan_microstack/assets/www/adminops/assets/TotpVerifyView-BHN1VtX1.css +1 -0
- cancan_microstack/assets/www/adminops/assets/TotpVerifyView-D3w_lZk8.js +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowCenter-DU_mpIA0.css +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowCenter-i50rZyxN.js +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowDesigner-CnHokPL9.js +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowDesigner-DaZaZpLd.css +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowRuns-B09hK48c.js +1 -0
- cancan_microstack/assets/www/adminops/assets/WorkflowRuns-wGutKIIU.css +1 -0
- cancan_microstack/assets/www/adminops/assets/caddy-nnCKf8fG.js +1 -0
- cancan_microstack/assets/www/adminops/assets/format-Cuzxgna9.js +1 -0
- cancan_microstack/assets/www/adminops/assets/index-CiFlm8oc.js +64 -0
- cancan_microstack/assets/www/adminops/assets/index-UW0T1Dkc.css +1 -0
- cancan_microstack/assets/www/adminops/assets/service-BYlgGPs_.js +1 -0
- cancan_microstack/assets/www/adminops/assets/service-operation-6GzLw2Z1.js +1 -0
- cancan_microstack/assets/www/adminops/assets/style-CcIXnQ5y.css +1 -0
- cancan_microstack/assets/www/adminops/assets/style-lRnStdGu.js +39 -0
- cancan_microstack/assets/www/adminops/assets/useDebounce-BRlqfXqf.js +1 -0
- cancan_microstack/assets/www/adminops/assets/workflow-CUXs39Ac.js +1 -0
- cancan_microstack/assets/www/adminops/index.html +16 -0
- cancan_microstack/assets/www/adminops/vite.svg +1 -0
- cancan_microstack/cli/__init__.py +14 -0
- cancan_microstack/cli/__main__.py +9 -0
- cancan_microstack/cli/main.py +552 -0
- cancan_microstack/cmd/__init__.py +54 -0
- cancan_microstack/cmd/cancan/__init__.py +12 -0
- cancan_microstack/cmd/cancan/run.py +395 -0
- cancan_microstack/cmd/controllersrv/__init__.py +0 -0
- cancan_microstack/cmd/controllersrv/run.py +131 -0
- cancan_microstack/cmd/infrasrv/__init__.py +5 -0
- cancan_microstack/cmd/infrasrv/run.py +100 -0
- cancan_microstack/cmd/opsbffsrv/__init__.py +5 -0
- cancan_microstack/cmd/opsbffsrv/run.py +96 -0
- cancan_microstack/core/__init__.py +5 -0
- cancan_microstack/core/assets.py +123 -0
- cancan_microstack/core/compose_builder.py +102 -0
- cancan_microstack/core/doctor.py +152 -0
- cancan_microstack/core/microstack.py +71 -0
- cancan_microstack/core/runner.py +56 -0
- cancan_microstack/core/stack_manager.py +186 -0
- cancan_microstack/public/__init__.py +7 -0
- cancan_microstack/public/api/__init__.py +1 -0
- cancan_microstack/public/api/controllersrv_client.py +277 -0
- cancan_microstack/public/api/infrasrv_client.py +404 -0
- cancan_microstack/public/const/__init__.py +1 -0
- cancan_microstack/public/const/action_consts.py +18 -0
- cancan_microstack/public/const/app_consts.py +42 -0
- cancan_microstack/public/const/caddy_consts.py +22 -0
- cancan_microstack/public/const/controllersrv_consts.py +163 -0
- cancan_microstack/public/const/docker_consts.py +15 -0
- cancan_microstack/public/const/error.py +56 -0
- cancan_microstack/public/const/health_consts.py +52 -0
- cancan_microstack/public/const/hook_enums.py +56 -0
- cancan_microstack/public/const/logging_enums.py +13 -0
- cancan_microstack/public/const/metrics_enums.py +36 -0
- cancan_microstack/public/const/monitor_enums.py +26 -0
- cancan_microstack/public/const/operation_consts.py +53 -0
- cancan_microstack/public/const/opsbffsrv_error.py +92 -0
- cancan_microstack/public/const/overrides_consts.py +13 -0
- cancan_microstack/public/const/redis.py +17 -0
- cancan_microstack/public/const/service_consts.py +15 -0
- cancan_microstack/public/const/workflow_consts.py +65 -0
- cancan_microstack/public/error.py +41 -0
- cancan_microstack/public/logging/__init__.py +0 -0
- cancan_microstack/public/logging/initializer.py +109 -0
- cancan_microstack/public/logging/mq_handler.py +279 -0
- cancan_microstack/public/schemas/__init__.py +1 -0
- cancan_microstack/public/schemas/caddy/__init__.py +381 -0
- cancan_microstack/public/schemas/caddy/analysis.py +90 -0
- cancan_microstack/public/schemas/caddy/route.py +18 -0
- cancan_microstack/public/schemas/common.py +79 -0
- cancan_microstack/public/schemas/controllersrv/__init__.py +3 -0
- cancan_microstack/public/schemas/controllersrv/async_requests.py +30 -0
- cancan_microstack/public/schemas/controllersrv/compose_models.py +47 -0
- cancan_microstack/public/schemas/controllersrv/const.py +24 -0
- cancan_microstack/public/schemas/controllersrv/docker_models.py +45 -0
- cancan_microstack/public/schemas/controllersrv/docker_responses.py +104 -0
- cancan_microstack/public/schemas/controllersrv/requests.py +54 -0
- cancan_microstack/public/schemas/controllersrv/responses.py +124 -0
- cancan_microstack/public/schemas/controllersrv/task_models.py +102 -0
- cancan_microstack/public/schemas/controllersrv/validation.py +23 -0
- cancan_microstack/public/schemas/hook_metrics.py +124 -0
- cancan_microstack/public/schemas/hooks.py +39 -0
- cancan_microstack/public/schemas/infra/__init__.py +0 -0
- cancan_microstack/public/schemas/infra/cleanup.py +25 -0
- cancan_microstack/public/schemas/infra/container.py +74 -0
- cancan_microstack/public/schemas/infra/enums.py +135 -0
- cancan_microstack/public/schemas/infra/health_check.py +42 -0
- cancan_microstack/public/schemas/infra/hook_log.py +42 -0
- cancan_microstack/public/schemas/infra/operation.py +90 -0
- cancan_microstack/public/schemas/infra/overview.py +25 -0
- cancan_microstack/public/schemas/infra/push.py +33 -0
- cancan_microstack/public/schemas/infra/service_action_log.py +47 -0
- cancan_microstack/public/schemas/infra/service_config.py +10 -0
- cancan_microstack/public/schemas/infra/service_info.py +69 -0
- cancan_microstack/public/schemas/infra/service_instance.py +93 -0
- cancan_microstack/public/schemas/infra/service_management.py +152 -0
- cancan_microstack/public/schemas/infra/service_operation.py +79 -0
- cancan_microstack/public/schemas/infra/service_registry.py +158 -0
- cancan_microstack/public/schemas/infra/status_types.py +19 -0
- cancan_microstack/public/schemas/infra/workflow.py +566 -0
- cancan_microstack/public/schemas/logging/__init__.py +1 -0
- cancan_microstack/public/schemas/logging/log_event.py +121 -0
- cancan_microstack/public/schemas/opsbffsrv/__init__.py +1 -0
- cancan_microstack/public/schemas/opsbffsrv/async_ops.py +17 -0
- cancan_microstack/public/schemas/opsbffsrv/db_admin.py +147 -0
- cancan_microstack/public/schemas/opsbffsrv/db_init.py +48 -0
- cancan_microstack/public/schemas/opsbffsrv/service_config.py +89 -0
- cancan_microstack/public/schemas/opsbffsrv/service_logs.py +54 -0
- cancan_microstack/public/schemas/service_operation.py +24 -0
- cancan_microstack/public/schemas/service_registry.py +40 -0
- cancan_microstack/public/types/__init__.py +7 -0
- cancan_microstack/public/web/__init__.py +0 -0
- cancan_microstack/public/web/config_value.py +105 -0
- cancan_microstack/public/web/server.py +385 -0
- cancan_microstack/py.typed +0 -0
- cancan_microstack/runtime/__init__.py +0 -0
- cancan_microstack/runtime/compose_cmd.py +228 -0
- cancan_microstack/runtime/host_daemon.py +318 -0
- cancan_microstack/runtime/overrides.py +103 -0
- cancan_microstack/runtime/resources.py +25 -0
- cancan_microstack/runtime/workspace.py +94 -0
- cancan_microstack/services/__init__.py +0 -0
- cancan_microstack/services/controllersrv/__init__.py +8 -0
- cancan_microstack/services/controllersrv/application/__init__.py +0 -0
- cancan_microstack/services/controllersrv/application/docker_compose_app.py +427 -0
- cancan_microstack/services/controllersrv/conf/__init__.py +0 -0
- cancan_microstack/services/controllersrv/conf/config.py +76 -0
- cancan_microstack/services/controllersrv/conf/settings.py +54 -0
- cancan_microstack/services/controllersrv/domain/__init__.py +0 -0
- cancan_microstack/services/controllersrv/domain/docker_compose/__init__.py +0 -0
- cancan_microstack/services/controllersrv/domain/docker_compose/docker_compose_domain.py +278 -0
- cancan_microstack/services/controllersrv/domain/service_validator.py +327 -0
- cancan_microstack/services/controllersrv/domain/task/__init__.py +17 -0
- cancan_microstack/services/controllersrv/domain/task/task_queue.py +286 -0
- cancan_microstack/services/controllersrv/domain/task/task_worker.py +495 -0
- cancan_microstack/services/controllersrv/infrastructure/__init__.py +0 -0
- cancan_microstack/services/controllersrv/interface/__init__.py +0 -0
- cancan_microstack/services/controllersrv/interface/api/__init__.py +0 -0
- cancan_microstack/services/controllersrv/interface/api/docker_control_api.py +470 -0
- cancan_microstack/services/controllersrv/router.py +132 -0
- cancan_microstack/services/infrasrv/__init__.py +4 -0
- cancan_microstack/services/infrasrv/application/__init__.py +0 -0
- cancan_microstack/services/infrasrv/application/health_check_app.py +24 -0
- cancan_microstack/services/infrasrv/application/logging/__init__.py +1 -0
- cancan_microstack/services/infrasrv/application/logging/log_ingestion_service.py +183 -0
- cancan_microstack/services/infrasrv/application/service_config.py +22 -0
- cancan_microstack/services/infrasrv/application/service_logs_app.py +53 -0
- cancan_microstack/services/infrasrv/application/service_management_app.py +689 -0
- cancan_microstack/services/infrasrv/application/service_operation_tracker.py +251 -0
- cancan_microstack/services/infrasrv/application/service_registry.py +53 -0
- cancan_microstack/services/infrasrv/application/workflow/__init__.py +0 -0
- cancan_microstack/services/infrasrv/application/workflow/workflow_app.py +991 -0
- cancan_microstack/services/infrasrv/application/workflow/workflow_queue.py +302 -0
- cancan_microstack/services/infrasrv/application/workflow/workflow_tasks.py +46 -0
- cancan_microstack/services/infrasrv/application/workflow/workflow_worker_runtime.py +122 -0
- cancan_microstack/services/infrasrv/conf/__init__.py +0 -0
- cancan_microstack/services/infrasrv/conf/config.py +98 -0
- cancan_microstack/services/infrasrv/domain/__init__.py +0 -0
- cancan_microstack/services/infrasrv/domain/health_check/__init__.py +3 -0
- cancan_microstack/services/infrasrv/domain/health_check/health_check_domain.py +576 -0
- cancan_microstack/services/infrasrv/domain/hooks/__init__.py +19 -0
- cancan_microstack/services/infrasrv/domain/hooks/builtin_hooks.py +308 -0
- cancan_microstack/services/infrasrv/domain/hooks/hook_registry.py +43 -0
- cancan_microstack/services/infrasrv/domain/hooks/hooks_log_utils.py +275 -0
- cancan_microstack/services/infrasrv/domain/hooks/init.py +17 -0
- cancan_microstack/services/infrasrv/domain/hooks/metrics.py +205 -0
- cancan_microstack/services/infrasrv/domain/hooks/pre_registration_hooks.py +490 -0
- cancan_microstack/services/infrasrv/domain/registry/__init__.py +0 -0
- cancan_microstack/services/infrasrv/domain/registry/service_registry.py +509 -0
- cancan_microstack/services/infrasrv/domain/service_config/__init__.py +0 -0
- cancan_microstack/services/infrasrv/domain/service_config/service_config.py +50 -0
- cancan_microstack/services/infrasrv/domain/service_logs/__init__.py +0 -0
- cancan_microstack/services/infrasrv/domain/service_logs/service_logs_domain.py +51 -0
- cancan_microstack/services/infrasrv/domain/workflow/__init__.py +4 -0
- cancan_microstack/services/infrasrv/domain/workflow/engine.py +159 -0
- cancan_microstack/services/infrasrv/domain/workflow/node_handlers.py +509 -0
- cancan_microstack/services/infrasrv/domain/workflow/workflow_domain.py +164 -0
- cancan_microstack/services/infrasrv/infrastructure/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/api/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/api/controllersrv_api.py +165 -0
- cancan_microstack/services/infrasrv/infrastructure/cache/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/cache/service_registry_cache.py +174 -0
- cancan_microstack/services/infrasrv/infrastructure/db/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/execution_log_tbl.py +53 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/node_instance_tbl.py +55 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/service_action_log_tbl.py +44 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/service_config_tbl.py +30 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/service_info_tbl.py +59 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/service_instance_tbl.py +88 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/service_operation_tbl.py +73 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_definition_tbl.py +55 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_definition_version_tbl.py +43 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_engine_alert_tbl.py +57 -0
- cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_run_tbl.py +56 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/__init__.py +0 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_action_log_op.py +239 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_config.py +80 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_config_manager.py +198 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_info_op.py +297 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_instance_op.py +688 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_operation_op.py +387 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/service_registry.py +124 -0
- cancan_microstack/services/infrasrv/infrastructure/db/operate/workflow_op.py +804 -0
- cancan_microstack/services/infrasrv/infrastructure/ddl_manager.py +31 -0
- cancan_microstack/services/infrasrv/infrastructure/mongo/__init__.py +1 -0
- cancan_microstack/services/infrasrv/infrastructure/mongo/log_repository.py +129 -0
- cancan_microstack/services/infrasrv/interface/__init__.py +0 -0
- cancan_microstack/services/infrasrv/interface/api/__init__.py +0 -0
- cancan_microstack/services/infrasrv/interface/api/health_check_api.py +29 -0
- cancan_microstack/services/infrasrv/interface/api/hooks.py +284 -0
- cancan_microstack/services/infrasrv/interface/api/internal.py +49 -0
- cancan_microstack/services/infrasrv/interface/api/internal_instance_api.py +265 -0
- cancan_microstack/services/infrasrv/interface/api/internal_operation_api.py +206 -0
- cancan_microstack/services/infrasrv/interface/api/service_config.py +50 -0
- cancan_microstack/services/infrasrv/interface/api/service_logs_api.py +49 -0
- cancan_microstack/services/infrasrv/interface/api/service_management_api.py +113 -0
- cancan_microstack/services/infrasrv/interface/api/service_registry.py +117 -0
- cancan_microstack/services/infrasrv/interface/api/workflow_api.py +303 -0
- cancan_microstack/services/infrasrv/interface/schedule/__init__.py +0 -0
- cancan_microstack/services/infrasrv/interface/schedule/cleanup.py +13 -0
- cancan_microstack/services/infrasrv/interface/schedule/health_check.py +27 -0
- cancan_microstack/services/infrasrv/interface/schedule/log_cleanup.py +26 -0
- cancan_microstack/services/infrasrv/interface/schedule/operation_tracker.py +25 -0
- cancan_microstack/services/infrasrv/interface/schedule/scheduler.py +39 -0
- cancan_microstack/services/infrasrv/interface/schedule/workflow_scheduler.py +115 -0
- cancan_microstack/services/infrasrv/router.py +341 -0
- cancan_microstack/services/opsbffsrv/__init__.py +4 -0
- cancan_microstack/services/opsbffsrv/application/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/application/async_operation_app.py +150 -0
- cancan_microstack/services/opsbffsrv/application/auth_app.py +285 -0
- cancan_microstack/services/opsbffsrv/application/caddy/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/application/caddy/access_log_analysis_app.py +344 -0
- cancan_microstack/services/opsbffsrv/application/caddy/access_log_ingestion_service.py +169 -0
- cancan_microstack/services/opsbffsrv/application/caddy/certificate_management_app.py +355 -0
- cancan_microstack/services/opsbffsrv/application/caddy/rate_limit_management_app.py +496 -0
- cancan_microstack/services/opsbffsrv/application/caddy/route_management_app.py +401 -0
- cancan_microstack/services/opsbffsrv/application/caddy/stats_aggregation_app.py +364 -0
- cancan_microstack/services/opsbffsrv/application/db_admin_app.py +103 -0
- cancan_microstack/services/opsbffsrv/application/db_init_app.py +283 -0
- cancan_microstack/services/opsbffsrv/application/logging/__init__.py +1 -0
- cancan_microstack/services/opsbffsrv/application/logging/log_query_app.py +28 -0
- cancan_microstack/services/opsbffsrv/application/service_config.py +158 -0
- cancan_microstack/services/opsbffsrv/application/service_logs_app.py +74 -0
- cancan_microstack/services/opsbffsrv/application/service_registry.py +36 -0
- cancan_microstack/services/opsbffsrv/application/workflow_ops_app.py +730 -0
- cancan_microstack/services/opsbffsrv/conf/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/conf/config.py +224 -0
- cancan_microstack/services/opsbffsrv/domain/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/auth/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/auth/admin_init.py +38 -0
- cancan_microstack/services/opsbffsrv/domain/auth/auth_domain.py +108 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/access_log_analysis.py +358 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/certificate_management.py +325 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/default_routes.py +53 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/rate_limit_management.py +308 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/route_management.py +279 -0
- cancan_microstack/services/opsbffsrv/domain/caddy/stats_aggregation.py +654 -0
- cancan_microstack/services/opsbffsrv/domain/db_admin/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/db_admin/db_admin_domain.py +118 -0
- cancan_microstack/services/opsbffsrv/domain/db_init/__init__.py +3 -0
- cancan_microstack/services/opsbffsrv/domain/db_init/db_init_domain.py +358 -0
- cancan_microstack/services/opsbffsrv/domain/logging/__init__.py +1 -0
- cancan_microstack/services/opsbffsrv/domain/logging/log_query_domain.py +99 -0
- cancan_microstack/services/opsbffsrv/domain/service_config/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/service_config/service_config.py +81 -0
- cancan_microstack/services/opsbffsrv/domain/service_registry/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/domain/service_registry/service_registry.py +292 -0
- cancan_microstack/services/opsbffsrv/infrastructure/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/api/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/api/infrasrv_api.py +242 -0
- cancan_microstack/services/opsbffsrv/infrastructure/auth/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/auth/captcha_service.py +67 -0
- cancan_microstack/services/opsbffsrv/infrastructure/auth/password_service.py +12 -0
- cancan_microstack/services/opsbffsrv/infrastructure/auth/redis_store.py +131 -0
- cancan_microstack/services/opsbffsrv/infrastructure/auth/totp_service.py +59 -0
- cancan_microstack/services/opsbffsrv/infrastructure/caddy/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/caddy/access_log_parser.py +307 -0
- cancan_microstack/services/opsbffsrv/infrastructure/caddy/admin_api_client.py +678 -0
- cancan_microstack/services/opsbffsrv/infrastructure/caddy/ip_geo_locator.py +176 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/admin_user_tbl.py +33 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_access_log_tbl.py +90 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_certificate_tbl.py +65 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_rate_limit_tbl.py +69 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_route_tbl.py +66 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_stats_tbl.py +78 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_action_log_tbl.py +44 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_config_tbl.py +30 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_info_tbl.py +51 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_instance_tbl.py +68 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/admin_user_operate.py +59 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_access_log.py +531 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_certificate.py +451 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_rate_limit.py +360 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_route.py +271 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_stats.py +343 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_action_log_op.py +57 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_config.py +86 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_info_op.py +79 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_instance.py +58 -0
- cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_registry.py +138 -0
- cancan_microstack/services/opsbffsrv/infrastructure/ddl_manager.py +31 -0
- cancan_microstack/services/opsbffsrv/infrastructure/mongo/__init__.py +1 -0
- cancan_microstack/services/opsbffsrv/infrastructure/mongo/log_query_repository.py +87 -0
- cancan_microstack/services/opsbffsrv/interface/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/interface/api/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/interface/api/async_operation_api.py +137 -0
- cancan_microstack/services/opsbffsrv/interface/api/auth_api.py +113 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/__init__.py +3 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/access_log_api.py +174 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/certificate_api.py +235 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/rate_limit_api.py +302 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/route_api.py +250 -0
- cancan_microstack/services/opsbffsrv/interface/api/caddy/stats_api.py +243 -0
- cancan_microstack/services/opsbffsrv/interface/api/db_admin_api.py +62 -0
- cancan_microstack/services/opsbffsrv/interface/api/db_init_api.py +109 -0
- cancan_microstack/services/opsbffsrv/interface/api/instance_management_api.py +165 -0
- cancan_microstack/services/opsbffsrv/interface/api/log_query_api.py +41 -0
- cancan_microstack/services/opsbffsrv/interface/api/mongo_express_proxy_api.py +181 -0
- cancan_microstack/services/opsbffsrv/interface/api/pgweb_proxy_api.py +154 -0
- cancan_microstack/services/opsbffsrv/interface/api/rabbitmq_mgmt_proxy_api.py +518 -0
- cancan_microstack/services/opsbffsrv/interface/api/redis_commander_proxy_api.py +133 -0
- cancan_microstack/services/opsbffsrv/interface/api/service_config.py +146 -0
- cancan_microstack/services/opsbffsrv/interface/api/service_logs_api.py +81 -0
- cancan_microstack/services/opsbffsrv/interface/api/service_registry.py +66 -0
- cancan_microstack/services/opsbffsrv/interface/api/workflow_ops_api.py +413 -0
- cancan_microstack/services/opsbffsrv/interface/middleware/__init__.py +0 -0
- cancan_microstack/services/opsbffsrv/interface/middleware/auth_middleware.py +52 -0
- cancan_microstack/services/opsbffsrv/router.py +901 -0
- cancan_microstack/utils/__init__.py +1 -0
- cancan_microstack/utils/container_env.py +218 -0
- cancan_microstack-0.0.1.dist-info/METADATA +155 -0
- cancan_microstack-0.0.1.dist-info/RECORD +440 -0
- cancan_microstack-0.0.1.dist-info/WHEEL +5 -0
- cancan_microstack-0.0.1.dist-info/entry_points.txt +2 -0
- cancan_microstack-0.0.1.dist-info/licenses/LICENSE +21 -0
- cancan_microstack-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"""项目 AppServer,基于 Linglong 扩展服务注册、远程配置与内部管理路由。"""
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Awaitable,
|
|
7
|
+
Callable,
|
|
8
|
+
Dict,
|
|
9
|
+
Optional,
|
|
10
|
+
Sequence,
|
|
11
|
+
Tuple,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from fastapi import (
|
|
15
|
+
APIRouter,
|
|
16
|
+
Request,
|
|
17
|
+
)
|
|
18
|
+
from fastapi.responses import ORJSONResponse
|
|
19
|
+
|
|
20
|
+
from linglong_web import (
|
|
21
|
+
LinglongConfig,
|
|
22
|
+
LinglongConfigBase,
|
|
23
|
+
)
|
|
24
|
+
from linglong_web import (
|
|
25
|
+
HTTPClientConfig,
|
|
26
|
+
http_client,
|
|
27
|
+
)
|
|
28
|
+
from linglong_web import LinglongAppServer
|
|
29
|
+
from linglong_web import BaseServerExtension
|
|
30
|
+
from linglong_web import LinglongConst
|
|
31
|
+
from linglong_web.utils import logger
|
|
32
|
+
|
|
33
|
+
from cancan_microstack.public.const.app_consts import WebServerConst
|
|
34
|
+
from cancan_microstack.public.schemas.service_registry import (
|
|
35
|
+
InstanceMetadata,
|
|
36
|
+
ServiceMetadata,
|
|
37
|
+
ServiceRegistryPayload,
|
|
38
|
+
)
|
|
39
|
+
from cancan_microstack.public.schemas.infra.status_types import InstanceStatus
|
|
40
|
+
from cancan_microstack.public.web.config_value import ConfigValueResolver
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AppServer(LinglongAppServer):
|
|
44
|
+
"""项目 AppServer,实现服务注册、配置下发等行为 / Project specific AppServer."""
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
super().__init__()
|
|
48
|
+
self._registry_keepalive_task: Optional[asyncio.Task] = None
|
|
49
|
+
self._config_value_resolver = ConfigValueResolver()
|
|
50
|
+
|
|
51
|
+
async def initialize(
|
|
52
|
+
self,
|
|
53
|
+
service_name: str,
|
|
54
|
+
router: APIRouter,
|
|
55
|
+
config_dict: Dict[str, type[LinglongConfigBase]],
|
|
56
|
+
scheduler_group=None,
|
|
57
|
+
middleware: Optional[list[Callable[[Request, Callable[[Request], Awaitable[Any]]], Awaitable[Any]]]] = None,
|
|
58
|
+
on_startup: Optional[Sequence[Callable[[], Any]]] = None,
|
|
59
|
+
on_shutdown: Optional[Sequence[Callable[[], Any]]] = None,
|
|
60
|
+
extensions: Sequence[BaseServerExtension] | None = None,
|
|
61
|
+
) -> "AppServer":
|
|
62
|
+
await super().initialize(
|
|
63
|
+
service_name=service_name,
|
|
64
|
+
router=router,
|
|
65
|
+
config_dict=config_dict,
|
|
66
|
+
scheduler_group=scheduler_group,
|
|
67
|
+
middleware=middleware,
|
|
68
|
+
on_startup=on_startup,
|
|
69
|
+
on_shutdown=on_shutdown,
|
|
70
|
+
extensions=extensions,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self._initialize_config_value_resolver()
|
|
74
|
+
|
|
75
|
+
# controllersrv 等“宿主机控制服务”不参与服务注册/远程配置。
|
|
76
|
+
# Host controller services (e.g. controllersrv) do not participate in registry/remote config.
|
|
77
|
+
if not self._should_skip_registry():
|
|
78
|
+
self.register_shutdown_callback(self._deregister_from_infrasrv)
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def _initialize_config_value_resolver(self) -> None:
|
|
82
|
+
"""初始化配置解析器并展开 ConfigValue 默认值 / Initialize resolver and materialize ConfigValue defaults."""
|
|
83
|
+
snapshot = LinglongConfig.snapshot()
|
|
84
|
+
self._config_value_resolver = ConfigValueResolver.from_snapshot(snapshot)
|
|
85
|
+
defaults = self._config_value_resolver.materialize_defaults()
|
|
86
|
+
if defaults:
|
|
87
|
+
LinglongConfig.apply_updates(defaults)
|
|
88
|
+
logger.info("ConfigValue wrappers initialized: %s", list(sorted(defaults.keys())))
|
|
89
|
+
|
|
90
|
+
async def on_startup(self): # noqa: D401 - lifecycle override
|
|
91
|
+
await super().on_startup()
|
|
92
|
+
if not self._should_skip_registry():
|
|
93
|
+
self._registry_keepalive_task = asyncio.create_task(
|
|
94
|
+
self._registry_keepalive_loop(),
|
|
95
|
+
name=f"{self.service_name}-registry-keepalive",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def on_shutdown(self) -> None: # noqa: D401 - lifecycle override
|
|
99
|
+
if self._registry_keepalive_task:
|
|
100
|
+
self._registry_keepalive_task.cancel()
|
|
101
|
+
try:
|
|
102
|
+
await self._registry_keepalive_task
|
|
103
|
+
except asyncio.CancelledError:
|
|
104
|
+
pass
|
|
105
|
+
self._registry_keepalive_task = None
|
|
106
|
+
await super().on_shutdown()
|
|
107
|
+
|
|
108
|
+
async def _before_resources_initialized(self) -> None:
|
|
109
|
+
await self._fetch_service_config_from_remote(with_retry=True)
|
|
110
|
+
|
|
111
|
+
def _add_internal_routes(self) -> None:
|
|
112
|
+
if not self.app:
|
|
113
|
+
return
|
|
114
|
+
self.app.add_api_route("/internal/health", self._internal_health_check_handler, methods=["GET"])
|
|
115
|
+
self.app.add_api_route("/internal/config/update", self._internal_config_update_handler, methods=["POST"])
|
|
116
|
+
|
|
117
|
+
async def _internal_health_check_handler(self, request: Request): # noqa: D401 - FastAPI handler
|
|
118
|
+
return ORJSONResponse({
|
|
119
|
+
"status": InstanceStatus.UP,
|
|
120
|
+
"service": self.service_name,
|
|
121
|
+
"instance_id": self.instance_id,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
async def _internal_config_update_handler(self, request: Request):
|
|
125
|
+
try:
|
|
126
|
+
body = await request.json()
|
|
127
|
+
except Exception as exc: # pragma: no cover - FastAPI already validates JSON
|
|
128
|
+
logger.error("Failed to parse config update payload: %s", exc, exc_info=True)
|
|
129
|
+
return ORJSONResponse({"status": "error", "message": str(exc)}, status_code=400)
|
|
130
|
+
|
|
131
|
+
config_payload = body.get("config") if isinstance(body, dict) else None
|
|
132
|
+
if not config_payload:
|
|
133
|
+
return ORJSONResponse({"status": "error", "message": "No config provided"}, status_code=400)
|
|
134
|
+
|
|
135
|
+
self._update_config(config_payload)
|
|
136
|
+
return ORJSONResponse({"status": "success", "message": "Config updated"})
|
|
137
|
+
|
|
138
|
+
async def _fetch_service_config_from_remote(self, with_retry: bool = False) -> None:
|
|
139
|
+
if self._should_skip_registry():
|
|
140
|
+
logger.info("%s is a special service, skip remote config fetch", self.service_name)
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
url = f"{LinglongConfig.INFRASRV_HOST}/v1/infrasrv/service_config"
|
|
144
|
+
params = {"service_name": self.service_name}
|
|
145
|
+
retries = 3 if with_retry else 0
|
|
146
|
+
for attempt in range(retries + 1):
|
|
147
|
+
try:
|
|
148
|
+
resp = await http_client.get(url, params=params, timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT)
|
|
149
|
+
resp.raise_for_status()
|
|
150
|
+
data = await resp.json()
|
|
151
|
+
remote_config = data.get("data", {}) if isinstance(data, dict) else {}
|
|
152
|
+
if remote_config:
|
|
153
|
+
logger.info("Fetched remote config keys: %s", list(remote_config.keys()))
|
|
154
|
+
self._update_config(remote_config)
|
|
155
|
+
return
|
|
156
|
+
except Exception as exc:
|
|
157
|
+
is_dev = LinglongConfig.DEBUG
|
|
158
|
+
level = logger.warning if attempt < retries else logger.error
|
|
159
|
+
level(
|
|
160
|
+
"Fetch remote config failed (attempt %s/%s): %s",
|
|
161
|
+
attempt + 1,
|
|
162
|
+
retries + 1,
|
|
163
|
+
exc,
|
|
164
|
+
)
|
|
165
|
+
if attempt < retries:
|
|
166
|
+
await asyncio.sleep(3)
|
|
167
|
+
elif is_dev:
|
|
168
|
+
logger.info("Development mode: remote config fetch failure can be ignored")
|
|
169
|
+
|
|
170
|
+
def _update_config(self, remote_config: Dict[str, Any]) -> None:
|
|
171
|
+
if not remote_config:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
processed: Dict[str, Any] = {}
|
|
175
|
+
for key, value in remote_config.items():
|
|
176
|
+
config_key = key.upper()
|
|
177
|
+
if not hasattr(LinglongConfig, config_key):
|
|
178
|
+
logger.warning("Unknown remote config key '%s', skipping", config_key)
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
local_value = getattr(LinglongConfig, config_key)
|
|
182
|
+
try:
|
|
183
|
+
new_value = self._config_value_resolver.resolve_update_value(
|
|
184
|
+
config_key=config_key,
|
|
185
|
+
current_value=local_value,
|
|
186
|
+
raw_value=value,
|
|
187
|
+
)
|
|
188
|
+
processed[config_key] = new_value
|
|
189
|
+
except Exception as exc: # noqa: BLE001
|
|
190
|
+
logger.warning(
|
|
191
|
+
"Failed to apply remote config for '%s': %s",
|
|
192
|
+
config_key,
|
|
193
|
+
exc,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if processed:
|
|
197
|
+
LinglongConfig.apply_updates(processed)
|
|
198
|
+
logger.info("Applied remote config: %s", list(processed.keys()))
|
|
199
|
+
|
|
200
|
+
async def _register_to_infrasrv(self) -> None:
|
|
201
|
+
register_host, container_hostname, network_alias, host_source = self._resolve_registration_host()
|
|
202
|
+
instance_metadata = InstanceMetadata(
|
|
203
|
+
container_hostname=container_hostname,
|
|
204
|
+
network_alias=network_alias,
|
|
205
|
+
register_host_source=host_source,
|
|
206
|
+
app_host_binding=self.host,
|
|
207
|
+
)
|
|
208
|
+
service_metadata = ServiceMetadata(
|
|
209
|
+
version="1.0.0",
|
|
210
|
+
environment=(
|
|
211
|
+
LinglongConst.ENVIRONMENT.DEVELOPMENT if LinglongConfig.DEBUG else LinglongConst.ENVIRONMENT.PRODUCTION),
|
|
212
|
+
)
|
|
213
|
+
payload = ServiceRegistryPayload(
|
|
214
|
+
service_name=self.service_name or "",
|
|
215
|
+
instance_id=self.instance_id or "",
|
|
216
|
+
host=register_host,
|
|
217
|
+
port=self.port or 0,
|
|
218
|
+
internal_port=self.port or 0,
|
|
219
|
+
health_check_url="/internal/health",
|
|
220
|
+
container_name=container_hostname or network_alias,
|
|
221
|
+
compose_service_name=network_alias,
|
|
222
|
+
service_metadata=service_metadata,
|
|
223
|
+
instance_metadata=instance_metadata,
|
|
224
|
+
)
|
|
225
|
+
payload_dict = payload.model_dump()
|
|
226
|
+
payload_dict["instance_metadata"] = instance_metadata.model_dump(exclude_none=True)
|
|
227
|
+
|
|
228
|
+
url = f"{LinglongConfig.INFRASRV_HOST}/v1/infrasrv/registry/register"
|
|
229
|
+
for attempt in range(4):
|
|
230
|
+
try:
|
|
231
|
+
resp = await http_client.post(
|
|
232
|
+
url,
|
|
233
|
+
json=payload_dict,
|
|
234
|
+
timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT,
|
|
235
|
+
)
|
|
236
|
+
if resp and resp.status == 200:
|
|
237
|
+
logger.info("Service registered: %s (%s)", self.service_name, self.instance_id)
|
|
238
|
+
return
|
|
239
|
+
logger.warning("Register service failed, status=%s", resp.status if resp else "N/A")
|
|
240
|
+
except Exception as exc:
|
|
241
|
+
logger.warning("register attempt %s failed: %s", attempt + 1, exc)
|
|
242
|
+
await asyncio.sleep(3)
|
|
243
|
+
logger.error("All attempts to register %s failed", self.service_name)
|
|
244
|
+
|
|
245
|
+
async def _register_keepalive_once(self) -> bool:
|
|
246
|
+
"""执行一次保活注册 / Execute one keepalive registration tick."""
|
|
247
|
+
register_host, container_hostname, network_alias, host_source = self._resolve_registration_host()
|
|
248
|
+
instance_metadata = InstanceMetadata(
|
|
249
|
+
container_hostname=container_hostname,
|
|
250
|
+
network_alias=network_alias,
|
|
251
|
+
register_host_source=host_source,
|
|
252
|
+
app_host_binding=self.host,
|
|
253
|
+
)
|
|
254
|
+
service_metadata = ServiceMetadata(
|
|
255
|
+
version="1.0.0",
|
|
256
|
+
environment=(
|
|
257
|
+
LinglongConst.ENVIRONMENT.DEVELOPMENT if LinglongConfig.DEBUG else LinglongConst.ENVIRONMENT.PRODUCTION),
|
|
258
|
+
)
|
|
259
|
+
payload = ServiceRegistryPayload(
|
|
260
|
+
service_name=self.service_name or "",
|
|
261
|
+
instance_id=self.instance_id or "",
|
|
262
|
+
host=register_host,
|
|
263
|
+
port=self.port or 0,
|
|
264
|
+
internal_port=self.port or 0,
|
|
265
|
+
health_check_url="/internal/health",
|
|
266
|
+
container_name=container_hostname or network_alias,
|
|
267
|
+
compose_service_name=network_alias,
|
|
268
|
+
service_metadata=service_metadata,
|
|
269
|
+
instance_metadata=instance_metadata,
|
|
270
|
+
)
|
|
271
|
+
payload_dict = payload.model_dump()
|
|
272
|
+
payload_dict["instance_metadata"] = instance_metadata.model_dump(exclude_none=True)
|
|
273
|
+
|
|
274
|
+
url = f"{LinglongConfig.INFRASRV_HOST}/v1/infrasrv/registry/register"
|
|
275
|
+
try:
|
|
276
|
+
resp = await http_client.post(
|
|
277
|
+
url,
|
|
278
|
+
json=payload_dict,
|
|
279
|
+
timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT,
|
|
280
|
+
)
|
|
281
|
+
if resp and resp.status == 200:
|
|
282
|
+
return True
|
|
283
|
+
logger.warning(
|
|
284
|
+
"Registry keepalive failed, status=%s, service=%s, instance=%s",
|
|
285
|
+
resp.status if resp else "N/A",
|
|
286
|
+
self.service_name,
|
|
287
|
+
self.instance_id,
|
|
288
|
+
)
|
|
289
|
+
return False
|
|
290
|
+
except Exception as exc:
|
|
291
|
+
logger.warning(
|
|
292
|
+
"Registry keepalive request failed for %s (%s): %s",
|
|
293
|
+
self.service_name,
|
|
294
|
+
self.instance_id,
|
|
295
|
+
exc,
|
|
296
|
+
)
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
async def _registry_keepalive_loop(self) -> None:
|
|
300
|
+
"""持续执行服务注册保活 / Keep registry heartbeat alive via periodic upsert."""
|
|
301
|
+
interval_seconds = int(getattr(LinglongConfig, "SERVICE_REGISTRY_KEEPALIVE_SECONDS", 30) or 30)
|
|
302
|
+
interval_seconds = max(interval_seconds, 5)
|
|
303
|
+
|
|
304
|
+
# 启动即执行一次注册 / Register immediately on startup
|
|
305
|
+
first_ok = await self._register_keepalive_once()
|
|
306
|
+
if first_ok:
|
|
307
|
+
logger.info("Service registered: %s (%s)", self.service_name, self.instance_id)
|
|
308
|
+
else:
|
|
309
|
+
logger.warning("Initial registry keepalive failed: %s (%s)", self.service_name, self.instance_id)
|
|
310
|
+
|
|
311
|
+
while True:
|
|
312
|
+
try:
|
|
313
|
+
await asyncio.sleep(interval_seconds)
|
|
314
|
+
await self._register_keepalive_once()
|
|
315
|
+
except asyncio.CancelledError:
|
|
316
|
+
break
|
|
317
|
+
except Exception as exc:
|
|
318
|
+
logger.warning(
|
|
319
|
+
"Registry keepalive loop error for %s (%s): %s",
|
|
320
|
+
self.service_name,
|
|
321
|
+
self.instance_id,
|
|
322
|
+
exc,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
async def _deregister_from_infrasrv(self) -> None:
|
|
326
|
+
if self._should_skip_registry():
|
|
327
|
+
return
|
|
328
|
+
try:
|
|
329
|
+
url = f"{LinglongConfig.INFRASRV_HOST}/v1/infrasrv/registry/deregister"
|
|
330
|
+
params = {"service_name": self.service_name, "instance_id": self.instance_id}
|
|
331
|
+
resp = await http_client.delete(url, params=params, timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT)
|
|
332
|
+
if resp and resp.status == 200:
|
|
333
|
+
logger.info("Service deregistered: %s (%s)", self.service_name, self.instance_id)
|
|
334
|
+
else:
|
|
335
|
+
logger.warning("Service deregister failed, status=%s", resp.status if resp else "N/A")
|
|
336
|
+
except Exception as exc:
|
|
337
|
+
logger.error("Deregister error: %s", exc)
|
|
338
|
+
|
|
339
|
+
def _resolve_registration_host(self) -> Tuple[str, Optional[str], str, str]:
|
|
340
|
+
container_hostname = (os.environ.get("HOSTNAME") or "").strip() or None
|
|
341
|
+
network_alias = self._build_network_alias()
|
|
342
|
+
host_source = "container_hostname"
|
|
343
|
+
register_host = container_hostname or network_alias
|
|
344
|
+
|
|
345
|
+
override_host = (os.environ.get("SERVICE_REGISTRY_HOST") or "").strip()
|
|
346
|
+
if override_host:
|
|
347
|
+
register_host = override_host
|
|
348
|
+
host_source = "env:SERVICE_REGISTRY_HOST"
|
|
349
|
+
elif (not container_hostname or
|
|
350
|
+
self._looks_like_container_hostname(container_hostname) or
|
|
351
|
+
container_hostname.lower() in WebServerConst.UNSAFE_HOSTS):
|
|
352
|
+
register_host = network_alias
|
|
353
|
+
host_source = "network_alias"
|
|
354
|
+
return register_host, container_hostname, network_alias, host_source
|
|
355
|
+
|
|
356
|
+
def _build_network_alias(self) -> str:
|
|
357
|
+
explicit_alias = os.environ.get("SERVICE_NETWORK_ALIAS")
|
|
358
|
+
if explicit_alias and explicit_alias.strip():
|
|
359
|
+
return explicit_alias.strip()
|
|
360
|
+
suffix = os.environ.get("SERVICE_NETWORK_SUFFIX", ".service")
|
|
361
|
+
base_name = self.service_name or ""
|
|
362
|
+
if base_name.endswith(suffix):
|
|
363
|
+
return base_name
|
|
364
|
+
return f"{base_name}{suffix}" if base_name else suffix.lstrip('.')
|
|
365
|
+
|
|
366
|
+
@staticmethod
|
|
367
|
+
def _looks_like_container_hostname(hostname: Optional[str]) -> bool:
|
|
368
|
+
if not hostname:
|
|
369
|
+
return False
|
|
370
|
+
candidate = hostname.strip().lower()
|
|
371
|
+
return bool(WebServerConst.CONTAINER_ID_PATTERN.fullmatch(candidate))
|
|
372
|
+
|
|
373
|
+
def _should_skip_registry(self) -> bool:
|
|
374
|
+
# controllersrv 是宿主机控制服务:不注册、不拉取远端配置、不发心跳。
|
|
375
|
+
# controllersrv is a host controller service: skip registry/remote config/heartbeat.
|
|
376
|
+
if bool(getattr(LinglongConfig, "IS_CONTROLLER_SERVICE", False)):
|
|
377
|
+
logger.info("%s is a controller service, skip service registry", self.service_name)
|
|
378
|
+
return True
|
|
379
|
+
|
|
380
|
+
# 可通过配置显式禁用服务注册(例如某些 BFF/工具型服务)。
|
|
381
|
+
# Allow explicit opt-out via config for services that should not register.
|
|
382
|
+
if bool(getattr(LinglongConfig, "SKIP_SERVICE_REGISTRY", False)):
|
|
383
|
+
logger.info("%s is configured to skip service registry", self.service_name)
|
|
384
|
+
return True
|
|
385
|
+
return False
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Compose command detection & execution helpers.
|
|
2
|
+
|
|
3
|
+
提供对 Docker/Podman Compose 的自动探测与执行。
|
|
4
|
+
Provide auto-detection and execution helpers for Docker/Podman compose commands.
|
|
5
|
+
|
|
6
|
+
Notes:
|
|
7
|
+
- 用户不需要手动运行 `docker compose` / `podman compose`;cancan CLI 会在内部调用。
|
|
8
|
+
Users don't need to run `docker compose` / `podman compose` manually; cancan CLI will call it internally.
|
|
9
|
+
- 本模块只依赖标准库,便于未来抽离成独立包。
|
|
10
|
+
This module uses stdlib only, so it can be extracted into a standalone package later.
|
|
11
|
+
"""
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Callable
|
|
18
|
+
from typing import IO
|
|
19
|
+
from typing import Iterable
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class ComposeCommand:
|
|
24
|
+
"""Compose 命令描述 / Compose command descriptor."""
|
|
25
|
+
|
|
26
|
+
argv_prefix: list[str]
|
|
27
|
+
|
|
28
|
+
def with_file(self, compose_file: Path) -> list[str]:
|
|
29
|
+
return [*self.argv_prefix, "-f", str(compose_file)]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _can_run(
|
|
33
|
+
cmd: list[str],
|
|
34
|
+
runner: Callable[..., subprocess.CompletedProcess[str]],
|
|
35
|
+
*,
|
|
36
|
+
timeout_seconds: float = 5.0,
|
|
37
|
+
) -> bool:
|
|
38
|
+
"""轻量检测命令是否可运行 / Lightweight check whether a command can run."""
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
result = runner(
|
|
42
|
+
cmd,
|
|
43
|
+
stdout=subprocess.DEVNULL,
|
|
44
|
+
stderr=subprocess.DEVNULL,
|
|
45
|
+
text=True,
|
|
46
|
+
check=False,
|
|
47
|
+
timeout=timeout_seconds,
|
|
48
|
+
)
|
|
49
|
+
except Exception:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
return result.returncode == 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _first_existing(which: Callable[[str], str | None], names: Iterable[str]) -> str | None:
|
|
56
|
+
for name in names:
|
|
57
|
+
if which(name):
|
|
58
|
+
return name
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _daemon_ready_probe(prefix: list[str]) -> list[str] | None:
|
|
63
|
+
"""Return a probe command that validates the engine daemon is reachable.
|
|
64
|
+
|
|
65
|
+
返回用于验证引擎 daemon 可用的 probe 命令。
|
|
66
|
+
|
|
67
|
+
Notes:
|
|
68
|
+
- `docker compose version` 只验证 CLI 存在,不保证 Docker daemon 可用。
|
|
69
|
+
`docker compose version` only checks CLI presence, not Docker daemon readiness.
|
|
70
|
+
- `podman compose version` 同理。
|
|
71
|
+
Same for podman.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
if prefix[:2] == ["docker", "compose"] or prefix == ["docker-compose"]:
|
|
75
|
+
return ["docker", "info"]
|
|
76
|
+
if prefix[:2] == ["podman", "compose"] or prefix == ["podman-compose"]:
|
|
77
|
+
return ["podman", "info"]
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def detect_compose_command(
|
|
82
|
+
*,
|
|
83
|
+
engine: str | None = None,
|
|
84
|
+
which: Callable[[str], str | None] = shutil.which,
|
|
85
|
+
runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run,
|
|
86
|
+
) -> ComposeCommand:
|
|
87
|
+
"""自动探测 Compose 命令(Docker 或 Podman)。
|
|
88
|
+
|
|
89
|
+
Auto-detect compose command (Docker or Podman).
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
engine: "docker" | "podman" | "auto" | None.
|
|
93
|
+
- None/"auto": try docker first then podman
|
|
94
|
+
- "docker": docker only
|
|
95
|
+
- "podman": podman only
|
|
96
|
+
which: injectable for tests.
|
|
97
|
+
runner: injectable for tests.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
ComposeCommand: argv prefix, e.g. ["docker", "compose"] or ["podman-compose"].
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
RuntimeError: when no supported compose command is found.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
normalized = (engine or os.environ.get("CANCAN_CONTAINER_ENGINE") or "auto").strip().lower()
|
|
107
|
+
if normalized not in {"auto", "docker", "podman"}:
|
|
108
|
+
normalized = "auto"
|
|
109
|
+
|
|
110
|
+
candidates: list[list[str]] = []
|
|
111
|
+
|
|
112
|
+
def add_docker_candidates() -> None:
|
|
113
|
+
# Prefer `docker compose` when docker exists.
|
|
114
|
+
# 优先使用 `docker compose`。
|
|
115
|
+
if which("docker"):
|
|
116
|
+
candidates.append(["docker", "compose"])
|
|
117
|
+
if which("docker-compose"):
|
|
118
|
+
candidates.append(["docker-compose"])
|
|
119
|
+
|
|
120
|
+
def add_podman_candidates() -> None:
|
|
121
|
+
# Prefer `podman compose` (Podman v4+ plugin). It avoids podman-compose pod semantics.
|
|
122
|
+
# 优先使用 `podman compose`(Podman v4+ 插件),避免 podman-compose 的 pod 语义与噪声报错。
|
|
123
|
+
if which("podman"):
|
|
124
|
+
candidates.append(["podman", "compose"])
|
|
125
|
+
if which("podman-compose"):
|
|
126
|
+
candidates.append(["podman-compose"])
|
|
127
|
+
|
|
128
|
+
if normalized == "docker":
|
|
129
|
+
add_docker_candidates()
|
|
130
|
+
elif normalized == "podman":
|
|
131
|
+
add_podman_candidates()
|
|
132
|
+
else:
|
|
133
|
+
# Auto mode: try Docker first only if Docker daemon is reachable; otherwise prefer Podman.
|
|
134
|
+
# 自动模式:只有当 Docker daemon 可用时才优先 Docker;否则优先 Podman,避免卡住。
|
|
135
|
+
add_docker_candidates()
|
|
136
|
+
add_podman_candidates()
|
|
137
|
+
|
|
138
|
+
for prefix in candidates:
|
|
139
|
+
# 1) CLI exists?
|
|
140
|
+
# 1) CLI 是否存在?
|
|
141
|
+
if not _can_run([*prefix, "version"], runner, timeout_seconds=5.0):
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
# 2) Daemon reachable? (best-effort, fast timeout)
|
|
145
|
+
# 2) daemon 是否可用?(尽力而为,短超时)
|
|
146
|
+
probe = _daemon_ready_probe(prefix)
|
|
147
|
+
if probe is not None and _can_run(probe, runner, timeout_seconds=2.0):
|
|
148
|
+
return ComposeCommand(argv_prefix=prefix)
|
|
149
|
+
|
|
150
|
+
# If probe is not available, accept CLI presence.
|
|
151
|
+
# 若没有 probe,退化为“CLI 存在即接受”。
|
|
152
|
+
if probe is None:
|
|
153
|
+
return ComposeCommand(argv_prefix=prefix)
|
|
154
|
+
|
|
155
|
+
# Fallback: if some CLI exists but daemon probe failed (e.g. engine booting), return first CLI.
|
|
156
|
+
# 兜底:若 CLI 存在但 daemon probe 失败(例如引擎刚启动),返回第一个可用 CLI。
|
|
157
|
+
for prefix in candidates:
|
|
158
|
+
if _can_run([*prefix, "version"], runner, timeout_seconds=5.0):
|
|
159
|
+
return ComposeCommand(argv_prefix=prefix)
|
|
160
|
+
|
|
161
|
+
raise RuntimeError(
|
|
162
|
+
"No compose command found. Install Docker Desktop (docker compose) or Podman (podman compose / podman-compose)."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def run_compose(
|
|
167
|
+
*,
|
|
168
|
+
compose_file: Path,
|
|
169
|
+
args: list[str],
|
|
170
|
+
workspace: Path,
|
|
171
|
+
engine: str | None = None,
|
|
172
|
+
runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run,
|
|
173
|
+
) -> None:
|
|
174
|
+
"""执行 compose 子命令 / Execute compose subcommand."""
|
|
175
|
+
|
|
176
|
+
cmd = detect_compose_command(engine=engine).with_file(compose_file)
|
|
177
|
+
full_cmd = [*cmd, *args]
|
|
178
|
+
|
|
179
|
+
runner(
|
|
180
|
+
full_cmd,
|
|
181
|
+
cwd=str(workspace),
|
|
182
|
+
check=True,
|
|
183
|
+
text=True,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def run_compose_streaming(
|
|
188
|
+
*,
|
|
189
|
+
compose_file: Path,
|
|
190
|
+
args: list[str],
|
|
191
|
+
workspace: Path,
|
|
192
|
+
engine: str | None = None,
|
|
193
|
+
log_file: Path | None = None,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Run compose command and stream output to stdout while teeing to a log file.
|
|
196
|
+
|
|
197
|
+
执行 compose 命令,输出实时显示在终端,同时写入日志文件。
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
cmd = detect_compose_command(engine=engine).with_file(compose_file)
|
|
201
|
+
full_cmd = [*cmd, *args]
|
|
202
|
+
|
|
203
|
+
log_fp: IO[str] | None = None
|
|
204
|
+
if log_file is not None:
|
|
205
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
log_fp = open(log_file, "a", encoding="utf-8")
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
proc = subprocess.Popen(
|
|
210
|
+
full_cmd,
|
|
211
|
+
cwd=str(workspace),
|
|
212
|
+
stdout=subprocess.PIPE,
|
|
213
|
+
stderr=subprocess.STDOUT,
|
|
214
|
+
text=True,
|
|
215
|
+
bufsize=1,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert proc.stdout is not None
|
|
219
|
+
for line in proc.stdout:
|
|
220
|
+
print(line, end="")
|
|
221
|
+
if log_fp is not None:
|
|
222
|
+
log_fp.write(line)
|
|
223
|
+
rc = proc.wait()
|
|
224
|
+
if rc != 0:
|
|
225
|
+
raise subprocess.CalledProcessError(rc, full_cmd)
|
|
226
|
+
finally:
|
|
227
|
+
if log_fp is not None:
|
|
228
|
+
log_fp.close()
|