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,318 @@
|
|
|
1
|
+
"""Host-side daemon helpers for services like controllersrv.
|
|
2
|
+
|
|
3
|
+
用于在宿主机后台启动/停止 controllersrv,并使用 pidfile 进行管理。
|
|
4
|
+
Start/stop host-side services (e.g. controllersrv) in background using a pidfile.
|
|
5
|
+
|
|
6
|
+
Design goals / 设计目标:
|
|
7
|
+
- 标准库实现,避免引入额外依赖
|
|
8
|
+
Stdio-only to avoid extra dependencies.
|
|
9
|
+
- 默认不覆盖用户已有进程/日志
|
|
10
|
+
Do not overwrite user's existing processes/logs by default.
|
|
11
|
+
"""
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
import signal
|
|
16
|
+
import subprocess
|
|
17
|
+
import time
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Callable
|
|
21
|
+
|
|
22
|
+
log = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class DaemonPaths:
|
|
27
|
+
"""pid/log 路径约定 / pid/log path convention."""
|
|
28
|
+
|
|
29
|
+
pid_file: Path
|
|
30
|
+
log_file: Path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _proc_identity(pid: int) -> dict | None:
|
|
34
|
+
"""读取进程身份信息(启动时间 / 命令行),用于防止 PID 复用误杀。
|
|
35
|
+
|
|
36
|
+
Read process identity (start time / cmdline) to guard against PID-reuse mis-kill.
|
|
37
|
+
|
|
38
|
+
仅依赖标准库,尽力而为;任何一项拿不到就置 None(向后兼容、降级)。
|
|
39
|
+
Stdlib-only and best-effort; any missing field is set to None (backward-compatible degradation).
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
身份字典;若进程不存在则返回 None / identity dict, or None if the process does not exist.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# ps 在 macOS / Linux 上都可用,输出进程启动时间与命令行。
|
|
47
|
+
# ps is available on both macOS and Linux; emits process start time and command line.
|
|
48
|
+
out = subprocess.run(
|
|
49
|
+
["ps", "-o", "lstart=", "-o", "command=", "-p", str(pid)],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
timeout=2.0,
|
|
53
|
+
)
|
|
54
|
+
except Exception as exc: # noqa: BLE001
|
|
55
|
+
log.warning("Failed to read process identity for pid=%s: %s", pid, exc)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
if out.returncode != 0:
|
|
59
|
+
# 进程不存在 / process not found.
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
line = out.stdout.strip()
|
|
63
|
+
if not line:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
# lstart 是固定 5 字段格式(如 "Mon Jun 14 10:00:00 2026"),其后是 command。
|
|
67
|
+
# lstart is a fixed 5-field format (e.g. "Mon Jun 14 10:00:00 2026"), followed by command.
|
|
68
|
+
parts = line.split(maxsplit=5)
|
|
69
|
+
if len(parts) >= 5:
|
|
70
|
+
lstart = " ".join(parts[:5])
|
|
71
|
+
cmdline = parts[5] if len(parts) == 6 else ""
|
|
72
|
+
return {"lstart": lstart, "cmdline": cmdline}
|
|
73
|
+
|
|
74
|
+
return {"lstart": line, "cmdline": ""}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def default_daemon_paths(workspace: Path, *, name: str) -> DaemonPaths:
|
|
78
|
+
"""计算默认 pid/log 路径 / Compute default pid/log paths."""
|
|
79
|
+
|
|
80
|
+
pid_dir = workspace / "server_log_data" / "pids"
|
|
81
|
+
pid_file = pid_dir / f"{name}.pid"
|
|
82
|
+
log_file = workspace / "server_log_data" / f"{name}.out.log"
|
|
83
|
+
return DaemonPaths(pid_file=pid_file, log_file=log_file)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _pid_alive(pid: int) -> bool:
|
|
87
|
+
try:
|
|
88
|
+
os.kill(pid, 0)
|
|
89
|
+
except OSError:
|
|
90
|
+
return False
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _pid_alive_as_recorded(pid: int, recorded_identity: dict | None) -> bool:
|
|
95
|
+
"""判断 pid 是否仍是当初记录的那个进程,避免 PID 复用误杀。
|
|
96
|
+
|
|
97
|
+
Decide whether pid is still the originally-recorded process, to avoid PID-reuse mis-kill.
|
|
98
|
+
|
|
99
|
+
向后兼容:旧 pidfile 无身份信息(recorded_identity is None)时降级为仅 os.kill(pid, 0)。
|
|
100
|
+
Backward-compatible: when the old pidfile carries no identity info (recorded_identity is None),
|
|
101
|
+
degrade to the legacy os.kill(pid, 0) behavior.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
if not _pid_alive(pid):
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
# 旧 pidfile:无身份信息,降级为旧行为。
|
|
108
|
+
# Old pidfile: no identity info, degrade to legacy behavior.
|
|
109
|
+
if not recorded_identity:
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
actual = _proc_identity(pid)
|
|
113
|
+
if actual is None:
|
|
114
|
+
# 拿不到当前身份(进程刚退出或 ps 不可用):保守按"非同一进程"处理,避免误杀。
|
|
115
|
+
# Cannot read current identity (process just exited or ps unavailable):
|
|
116
|
+
# conservatively treat as "not the same process" to avoid mis-kill.
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
same = (
|
|
120
|
+
actual.get("lstart") == recorded_identity.get("lstart")
|
|
121
|
+
and actual.get("cmdline") == recorded_identity.get("cmdline")
|
|
122
|
+
)
|
|
123
|
+
if not same:
|
|
124
|
+
log.warning(
|
|
125
|
+
"PID %s reused by a different process (recorded=%s, actual=%s); not treating as alive",
|
|
126
|
+
pid,
|
|
127
|
+
recorded_identity,
|
|
128
|
+
actual,
|
|
129
|
+
)
|
|
130
|
+
return same
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def read_pid_record(pid_file: Path) -> tuple[int, dict | None] | None:
|
|
134
|
+
"""读取 pidfile 的完整记录(pid + 身份信息)。
|
|
135
|
+
|
|
136
|
+
Read the full pidfile record (pid + identity info).
|
|
137
|
+
|
|
138
|
+
支持两种格式 / Supports two formats:
|
|
139
|
+
- 新格式 / new: JSON {"pid": int, "lstart": str, "cmdline": str}
|
|
140
|
+
- 旧格式 / legacy: 纯整数文本(身份信息返回 None,降级为旧行为)
|
|
141
|
+
plain integer text (identity returned as None, degrades to legacy behavior)
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
(pid, identity_or_None);无法读取时返回 None / (pid, identity_or_None), or None if unreadable.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
raw = pid_file.read_text(encoding="utf-8").strip()
|
|
149
|
+
except FileNotFoundError:
|
|
150
|
+
return None
|
|
151
|
+
except OSError:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
if not raw:
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
# 先尝试新格式(JSON)。/ Try the new (JSON) format first.
|
|
158
|
+
try:
|
|
159
|
+
data = json.loads(raw)
|
|
160
|
+
if isinstance(data, dict) and "pid" in data:
|
|
161
|
+
pid = int(data["pid"])
|
|
162
|
+
identity = {
|
|
163
|
+
"lstart": data.get("lstart"),
|
|
164
|
+
"cmdline": data.get("cmdline"),
|
|
165
|
+
}
|
|
166
|
+
# 身份字段都缺失则视为无身份信息(降级)。
|
|
167
|
+
# If all identity fields are missing, treat as no identity info (degrade).
|
|
168
|
+
if identity["lstart"] is None and identity["cmdline"] is None:
|
|
169
|
+
identity = None
|
|
170
|
+
return pid, identity
|
|
171
|
+
except (ValueError, TypeError):
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
# 回退到旧格式:纯整数。/ Fall back to legacy format: plain integer.
|
|
175
|
+
try:
|
|
176
|
+
return int(raw), None
|
|
177
|
+
except ValueError:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def read_pid(pid_file: Path) -> int | None:
|
|
182
|
+
"""读取 pidfile 中的 pid(兼容新旧格式)/ Read the pid from the pidfile (both formats)."""
|
|
183
|
+
|
|
184
|
+
record = read_pid_record(pid_file)
|
|
185
|
+
if record is None:
|
|
186
|
+
return None
|
|
187
|
+
return record[0]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def start_daemon(
|
|
191
|
+
*,
|
|
192
|
+
argv: list[str],
|
|
193
|
+
workspace: Path,
|
|
194
|
+
paths: DaemonPaths,
|
|
195
|
+
env: dict[str, str] | None = None,
|
|
196
|
+
popen: Callable[..., subprocess.Popen] = subprocess.Popen,
|
|
197
|
+
) -> int:
|
|
198
|
+
"""后台启动进程并写 pidfile。
|
|
199
|
+
|
|
200
|
+
Start process in background and write pidfile.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
pid
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
# 用身份校验判断已有进程是否真的还活着,避免 PID 复用把无关进程当作存活而拒绝启动。
|
|
207
|
+
# Use identity-aware liveness so a reused PID (an unrelated process) is not mistaken as alive.
|
|
208
|
+
existing_record = read_pid_record(paths.pid_file)
|
|
209
|
+
if existing_record is not None:
|
|
210
|
+
existing_pid, existing_identity = existing_record
|
|
211
|
+
if existing_pid and _pid_alive_as_recorded(existing_pid, existing_identity):
|
|
212
|
+
return existing_pid
|
|
213
|
+
|
|
214
|
+
paths.pid_file.parent.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
paths.log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
216
|
+
|
|
217
|
+
merged_env = os.environ.copy()
|
|
218
|
+
if env:
|
|
219
|
+
merged_env.update(env)
|
|
220
|
+
|
|
221
|
+
# Append to log file, do not truncate.
|
|
222
|
+
# 追加日志,避免覆盖。
|
|
223
|
+
log_fp = open(paths.log_file, "a", encoding="utf-8")
|
|
224
|
+
try:
|
|
225
|
+
proc = popen(
|
|
226
|
+
argv,
|
|
227
|
+
cwd=str(workspace),
|
|
228
|
+
stdout=log_fp,
|
|
229
|
+
stderr=log_fp,
|
|
230
|
+
start_new_session=True,
|
|
231
|
+
env=merged_env,
|
|
232
|
+
text=True,
|
|
233
|
+
)
|
|
234
|
+
finally:
|
|
235
|
+
# child keeps the fd; safe to close in parent.
|
|
236
|
+
# 子进程会继承 fd;父进程关闭即可。
|
|
237
|
+
log_fp.close()
|
|
238
|
+
|
|
239
|
+
# Give it a brief moment to fail fast.
|
|
240
|
+
# 短暂等待以便“快速失败”。
|
|
241
|
+
time.sleep(0.2)
|
|
242
|
+
if proc.poll() is not None:
|
|
243
|
+
raise RuntimeError(f"Failed to start daemon: exited with code {proc.returncode}")
|
|
244
|
+
|
|
245
|
+
# 不仅靠"没退出"判成功:再确认该 pid 当前确实是一个存活进程,并抓取其身份信息写入 pidfile。
|
|
246
|
+
# Don't rely only on "didn't exit": confirm the pid is a live process now and capture its
|
|
247
|
+
# identity to persist into the pidfile.
|
|
248
|
+
if not _pid_alive(proc.pid):
|
|
249
|
+
raise RuntimeError(f"Failed to start daemon: pid {proc.pid} not alive after launch")
|
|
250
|
+
|
|
251
|
+
identity = _proc_identity(proc.pid)
|
|
252
|
+
record: dict = {"pid": proc.pid}
|
|
253
|
+
if identity:
|
|
254
|
+
record.update(identity)
|
|
255
|
+
else:
|
|
256
|
+
# 拿不到身份信息(如 ps 不可用):写入纯 pid,仍然向后兼容(判活时降级为旧行为)。
|
|
257
|
+
# No identity available (e.g. ps unavailable): write plain pid, still backward-compatible
|
|
258
|
+
# (liveness check degrades to legacy behavior).
|
|
259
|
+
log.warning("Could not capture process identity for pid=%s; pidfile will use legacy format", proc.pid)
|
|
260
|
+
|
|
261
|
+
paths.pid_file.write_text(json.dumps(record), encoding="utf-8")
|
|
262
|
+
return proc.pid
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def stop_daemon(
|
|
266
|
+
*,
|
|
267
|
+
paths: DaemonPaths,
|
|
268
|
+
timeout_seconds: float = 5.0,
|
|
269
|
+
remover: Callable[[Path], None] | None = None,
|
|
270
|
+
) -> bool:
|
|
271
|
+
"""停止后台进程(SIGTERM -> SIGKILL)。
|
|
272
|
+
|
|
273
|
+
Stop daemon process (SIGTERM then SIGKILL).
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
True if a process was stopped, False if pidfile missing/dead.
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
record = read_pid_record(paths.pid_file)
|
|
280
|
+
if record is None:
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
pid, identity = record
|
|
284
|
+
if not pid:
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
# 身份校验:若该 pid 已被复用为其它进程(或已死),不要向它发信号,避免误杀无关进程。
|
|
288
|
+
# Identity check: if the pid has been reused by another process (or is dead), do not signal
|
|
289
|
+
# it, to avoid killing an unrelated process.
|
|
290
|
+
if not _pid_alive_as_recorded(pid, identity):
|
|
291
|
+
try:
|
|
292
|
+
paths.pid_file.unlink(missing_ok=True)
|
|
293
|
+
except Exception as exc: # noqa: BLE001
|
|
294
|
+
log.warning("Failed to remove stale pidfile %s: %s", paths.pid_file, exc)
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
os.kill(pid, signal.SIGTERM)
|
|
298
|
+
deadline = time.time() + timeout_seconds
|
|
299
|
+
while time.time() < deadline:
|
|
300
|
+
if not _pid_alive(pid):
|
|
301
|
+
break
|
|
302
|
+
time.sleep(0.1)
|
|
303
|
+
|
|
304
|
+
if _pid_alive(pid):
|
|
305
|
+
os.kill(pid, signal.SIGKILL)
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
paths.pid_file.unlink(missing_ok=True)
|
|
309
|
+
except Exception as exc: # noqa: BLE001
|
|
310
|
+
log.warning("Failed to remove pidfile %s after stop: %s", paths.pid_file, exc)
|
|
311
|
+
|
|
312
|
+
if remover:
|
|
313
|
+
try:
|
|
314
|
+
remover(paths.pid_file)
|
|
315
|
+
except Exception as exc: # noqa: BLE001
|
|
316
|
+
log.warning("pidfile remover callback failed for %s: %s", paths.pid_file, exc)
|
|
317
|
+
|
|
318
|
+
return True
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Optional override support for Cancan-managed services.
|
|
2
|
+
|
|
3
|
+
允许业务仓库通过 ``cancan_overrides`` 目录注入 controllersrv / infrasrv /
|
|
4
|
+
opsbffsrv 的自定义配置,而无需 fork 主库。
|
|
5
|
+
Optional overrides so downstream workspaces can inject custom files without forking.
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import pkgutil
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import (
|
|
11
|
+
Iterable,
|
|
12
|
+
List,
|
|
13
|
+
Optional,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from cancan_microstack.public.const.overrides_consts import (
|
|
17
|
+
OVERRIDE_DIR_NAME,
|
|
18
|
+
OVERRIDE_ENV,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from .workspace import configure_workspace, get_workspace_root
|
|
22
|
+
|
|
23
|
+
_override_root: Optional[Path] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def configure_overrides(root: Path | str | None = None) -> Optional[Path]:
|
|
27
|
+
"""显式设置 overrides 根目录(None 时自动探测)。
|
|
28
|
+
|
|
29
|
+
Explicitly set override root; auto-discover when None.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
global _override_root
|
|
33
|
+
if isinstance(root, str):
|
|
34
|
+
override_path = Path(root)
|
|
35
|
+
elif root is None:
|
|
36
|
+
override_path = discover_override_root()
|
|
37
|
+
else:
|
|
38
|
+
override_path = root
|
|
39
|
+
|
|
40
|
+
if not override_path:
|
|
41
|
+
_override_root = None
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
resolved = override_path.resolve()
|
|
45
|
+
if not resolved.exists():
|
|
46
|
+
return None
|
|
47
|
+
_override_root = resolved
|
|
48
|
+
os.environ[OVERRIDE_ENV] = str(resolved)
|
|
49
|
+
return resolved
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def discover_override_root(start: Path | None = None) -> Optional[Path]:
|
|
53
|
+
"""从 workspace 起向上寻找 ``cancan_overrides`` 目录 / Search upward for overrides folder."""
|
|
54
|
+
|
|
55
|
+
env_value = os.environ.get(OVERRIDE_ENV)
|
|
56
|
+
if env_value:
|
|
57
|
+
candidate = Path(env_value).expanduser().resolve()
|
|
58
|
+
if candidate.exists():
|
|
59
|
+
return candidate
|
|
60
|
+
|
|
61
|
+
workspace = start or get_workspace_root()
|
|
62
|
+
for candidate in [workspace, *workspace.parents]:
|
|
63
|
+
override_dir = candidate / OVERRIDE_DIR_NAME
|
|
64
|
+
if override_dir.exists():
|
|
65
|
+
return override_dir
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_override_root() -> Optional[Path]:
|
|
70
|
+
"""返回当前 overrides 根目录(若存在)/ Return current override root when configured."""
|
|
71
|
+
|
|
72
|
+
if _override_root is not None:
|
|
73
|
+
return _override_root
|
|
74
|
+
return configure_overrides(os.environ.get(OVERRIDE_ENV))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def extend_service_package(service_name: str, package_name: str, package_path: Iterable[str]) -> List[str]:
|
|
78
|
+
"""扩展 cancan_microstack.services 的搜索路径以支持 overrides。
|
|
79
|
+
|
|
80
|
+
If ``cancan_overrides/<service_name>`` exists, prepend it to module search path
|
|
81
|
+
so workspace files override packaged ones.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
override_root = get_override_root()
|
|
85
|
+
if not override_root:
|
|
86
|
+
return list(package_path)
|
|
87
|
+
|
|
88
|
+
service_override = override_root / service_name
|
|
89
|
+
if not service_override.exists():
|
|
90
|
+
return list(package_path)
|
|
91
|
+
|
|
92
|
+
combined_paths = [str(service_override), *package_path]
|
|
93
|
+
return list(pkgutil.extend_path(combined_paths, package_name))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def bootstrap_from_workspace(workspace: Path | None) -> None:
|
|
97
|
+
"""在启动前同时配置 workspace 与 overrides。
|
|
98
|
+
|
|
99
|
+
Ensure workspace and overrides are configured together before startup.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
effective_root = configure_workspace(workspace)
|
|
103
|
+
configure_overrides(discover_override_root(effective_root))
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Helpers for resolving workspace overrides vs packaged assets.
|
|
2
|
+
|
|
3
|
+
工作区文件优先,包内资产兜底。
|
|
4
|
+
Prefer workspace files; fall back to packaged assets when missing.
|
|
5
|
+
"""
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from cancan_microstack.core.assets import AssetManager
|
|
9
|
+
|
|
10
|
+
from .workspace import get_workspace_root
|
|
11
|
+
|
|
12
|
+
_asset_manager = AssetManager()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_workspace_or_asset(relative_path: str, asset_logical: str) -> Path:
|
|
16
|
+
"""工作区路径优先,缺失时使用包资产。
|
|
17
|
+
|
|
18
|
+
Prefer workspace-relative path; fall back to packaged asset when missing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
workspace_root = get_workspace_root()
|
|
22
|
+
candidate = workspace_root / relative_path
|
|
23
|
+
if candidate.exists():
|
|
24
|
+
return candidate
|
|
25
|
+
return _asset_manager.resolve_path(asset_logical)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Workspace detection helpers for Cancan Microstack.
|
|
2
|
+
|
|
3
|
+
用于在宿主项目中自动定位工作目录,并提供 server_log_data 等通用路径。
|
|
4
|
+
Detect workspace roots in host projects and provision common paths like server_log_data.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
_WORKSPACE_ENV = "CANCAN_WORKSPACE_ROOT"
|
|
11
|
+
_LOG_DIR_NAME = "server_log_data"
|
|
12
|
+
_MARKERS: tuple[str, ...] = (
|
|
13
|
+
"server_log_data",
|
|
14
|
+
"cmd",
|
|
15
|
+
"docker-compose.infra.yml",
|
|
16
|
+
"docker-compose.services.yml",
|
|
17
|
+
"pyproject.toml",
|
|
18
|
+
"requirements.txt",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_workspace_root: Optional[Path] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def configure_workspace(root: Path | str | None = None) -> Path:
|
|
25
|
+
"""配置或重新配置当前 workspace 根。
|
|
26
|
+
|
|
27
|
+
设置当前 Cancan 工作目录,可显式传入路径,也可依赖自动探测。
|
|
28
|
+
Configure the current Cancan workspace root (explicit or auto-detected).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
global _workspace_root
|
|
32
|
+
if isinstance(root, str):
|
|
33
|
+
root_path = Path(root)
|
|
34
|
+
elif root is None:
|
|
35
|
+
root_path = detect_workspace_root()
|
|
36
|
+
else:
|
|
37
|
+
root_path = root
|
|
38
|
+
|
|
39
|
+
_workspace_root = root_path.resolve()
|
|
40
|
+
os.environ[_WORKSPACE_ENV] = str(_workspace_root)
|
|
41
|
+
return _workspace_root
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_workspace_root() -> Path:
|
|
45
|
+
"""返回已配置的 workspace 根;若未配置则懒加载探测。
|
|
46
|
+
|
|
47
|
+
Return configured workspace root, lazily detecting when absent.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
if _workspace_root is not None:
|
|
51
|
+
return _workspace_root
|
|
52
|
+
return configure_workspace(os.environ.get(_WORKSPACE_ENV))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def detect_workspace_root(start: Path | str | None = None) -> Path:
|
|
56
|
+
"""探测 workspace 根(环境变量优先,其次向上遍历寻找标记)。
|
|
57
|
+
|
|
58
|
+
Detect workspace root using env var or upward marker walk.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
env_value = os.environ.get(_WORKSPACE_ENV)
|
|
62
|
+
if env_value:
|
|
63
|
+
return Path(env_value).expanduser().resolve()
|
|
64
|
+
|
|
65
|
+
start_path = Path(start or Path.cwd()).resolve()
|
|
66
|
+
for candidate in [start_path, *start_path.parents]:
|
|
67
|
+
if _has_markers(candidate):
|
|
68
|
+
return candidate
|
|
69
|
+
return start_path
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def ensure_server_log_dir() -> Path:
|
|
73
|
+
"""确保标准日志目录存在 / Ensure canonical ``server_log_data`` exists."""
|
|
74
|
+
|
|
75
|
+
root = get_workspace_root()
|
|
76
|
+
log_dir = root / _LOG_DIR_NAME
|
|
77
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
return log_dir
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def ensure_subdir(relative_path: str) -> Path:
|
|
82
|
+
"""确保 workspace 下子目录存在并返回路径 / Ensure subdir exists under workspace."""
|
|
83
|
+
|
|
84
|
+
root = get_workspace_root()
|
|
85
|
+
target = root / relative_path
|
|
86
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
return target
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _has_markers(candidate: Path) -> bool:
|
|
91
|
+
for marker in _MARKERS:
|
|
92
|
+
if (candidate / marker).exists():
|
|
93
|
+
return True
|
|
94
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""controllersrv - Docker Compose 控制器服务。
|
|
2
|
+
|
|
3
|
+
这是一个特殊的服务,运行在宿主机上,负责管理 Docker Compose 集群,
|
|
4
|
+
不需要进行服务注册、配置拉取等常规微服务操作。
|
|
5
|
+
"""
|
|
6
|
+
from cancan_microstack.runtime.overrides import extend_service_package
|
|
7
|
+
|
|
8
|
+
__path__ = extend_service_package("controllersrv", __name__, __path__) # type: ignore[name-defined]
|
|
File without changes
|