feat(multi-vm): config-driven N-VM topology - #9
Conversation
|
Reviewed this properly — it's good work. Backward compatible by construction ( 1. Blocker — a single VM being down takes the whole stack offline
f" proxy_pass http://{container}:{t['port']};\n"nginx resolves literal upstream hostnames at config-parse time, not request time. In single-VM this was harmless — if Tested it on a real docker network with one VM container running and only the other absent: The running VM's route dies along with the missing one.
This repo already hit exactly this in v4.9.1 with the f" location {p} {{\n"
f" resolver {DOCKER_EMBEDDED_DNS} valid=10s ipv6=off;\n"
f" set $vm_upstream http://{container}:{t['port']};\n"
f" rewrite ^{p}(.*)$ /$1 break;\n"
f" proxy_pass $vm_upstream;\n"Deferring the lookup to request time means nginx starts regardless, and only the routes belonging to the down VM return 502. Side effect worth noting: 2.
|
- nginx proxy_pass uses resolver + variable to defer DNS lookup to request time, so one down VM doesn't prevent nginx from starting (same pattern as the v4.9.1 mcpunifier fix). - vms.yaml renamed to vms.yaml.example with sanitized sample values; .gitignore added for vms.yaml. - docs/multi-vm-setup.md and run.sh updated for the rename. - config_helper.py falls back to vms.yaml.example when the real vms.yaml is absent.
|
Addressed all three points: 1. Nginx resolver for terminal routes - switched from literal 2. vms.yaml.example - renamed from 3. pip install error messages - wrapped the auto-install blocks in try/except to catch |
|
Fixes verified — all three land, and I ran the branch rather than just reading it.
One thing has to come out before this can land.
|
Add vms.yaml to declare VM resource allocation (cpuset, RAM, CPU, disk, hot-tier binds). Each terminal in config.yaml gets an optional vm field referencing a VM name. Absent = default (routes to mt5). config_helper.py reads vm field for nginx routing, adds new commands: vms, vm_group, vm_info, port_list --vm, generate_compose. run.sh loops over all VMs for per-VM DNAT + generates vm-group files from config instead of requiring manual files. docker-compose.yml.j2 renders compose from vms.yaml via Jinja2. docs/multi-vm-setup.md covers the full walkthrough. Backward compatible: no vms.yaml = single-VM mode, all routes to mt5. No vm field = routes to default container.
- nginx proxy_pass uses resolver + variable to defer DNS lookup to request time, so one down VM doesn't prevent nginx from starting (same pattern as the v4.9.1 mcpunifier fix). - vms.yaml renamed to vms.yaml.example with sanitized sample values; .gitignore added for vms.yaml. - docs/multi-vm-setup.md and run.sh updated for the rename. - config_helper.py falls back to vms.yaml.example when the real vms.yaml is absent.
…them On ImportError, pip install runs with both stdout and stderr sent to DEVNULL, so a failure (offline, no perms, broken pip) surfaces only as a cryptic ImportError. Wrapping in try/except with a clear message tells the user what to do.
97a90c4 to
e9a8621
Compare
|
All three points addressed. Chartctl contamination rebased out of PR #9 - the [StartUp] stanza, the from mt5api.chartctl import webrequest block, and the chartctl_enabled command are removed. PR #9 now carries only multi-VM changes (3 commits). The chartctl bootstrap lands in PR #10 alongside the module and EA. |
psyb0t
left a comment
There was a problem hiding this comment.
Contamination's gone and the fixes survived the rebase — re-verified on e9a8621 rather than taking the diff's word for it:
nginx -ton the generated config with zero upstream containers present: passes. The literal-proxy_passcontrol still fails with[emerg] host not found in upstreamunder identical conditions, so that's the actual fix working, not a vacuous pass.write_inifor a live terminal emits zero[StartUp]/MT5ChartLoaderlines now.generate_composerenders both VMs anddocker compose configvalidates clean.- No
chartctl/webrequestadditions anywhere in the diff, and dropping thechartctl_enabledcommand is correct — master has no chartctl at all, so it came in with the same contamination.
Good work on this one. The docker-compose.yml.j2 approach was the right call, the backward compat is sound by construction, and docs/multi-vm-setup.md is genuinely useful.
Approving.
…them Ships the config-driven N-VM topology merged in #9 alongside the test and CI work that was missing underneath it. vms.yaml declares each Windows VM's resources and every terminal binds to one via a new vm: field. config_helper.py generates nginx routes aimed at the owning VM's container, run.sh loops each VM for DNAT and per-VM group files, and docker-compose.yml renders from docker-compose.yml.j2. No vms.yaml means single-VM and a terminal with no vm: field routes to mt5, so existing deployments are unaffected. Terminal routes now resolve their upstream per request rather than at config-parse time. With a literal proxy_pass, one absent VM container stopped nginx starting at all, taking every healthy VM's routes, the REST API and /mcp/ down with it. Adds contract tests for the handlers that move money. They drive the real Flask routes with the MT5 SDK faked at the m() seam and assert the exact request that would reach order_send: a market BUY priced at ask and a SELL at bid, closing a BUY sending a SELL at bid, a partial close sending only the requested volume, an sl-only modify preserving the existing tp. Every failure path also asserts order_send was never called, because a handler that errors after sending has already traded. Adds tests for the files config_helper.py generates, and make test-integration, a container-backed suite driven by pytest and testcontainers that boots real nginx against the generated config with one VM deliberately absent and stands the MCP unifier up beside a stub terminal. The host-side suite pins pytest 9.0.3, which is outside the affected CVE-2025-71176 range, and testcontainers 4.14.2, the newest stable release outside the seven-day supply-chain window. Its fixtures use the 4.14 API and transfer generated config through container environment variables, so they also work with remote Docker daemons where bind-mounting a caller-local temp path would fail. CI now runs all of it. pipeline.yml previously triggered only on v* tags and did nothing but publish badges and ClawHub skills, so the suite under tests/ had never run in CI. It now runs test, integration and lint on pushes to master and on every non-draft pull request, including when a draft becomes ready for review, with the ClawHub publish gated on all three. Reusable workflow calls are pinned to an immutable commit. scripts/test-mcpunifier.sh is retired: its seven assertions moved unchanged into tests/integration/test_mcpunifier.py, so the project has one integration harness in one language instead of a shell script beside it. scripts/lint.sh now ignores tracked deletions in the working tree, allowing the removal itself to pass pre-release lint before staging.
Declarative N-VM topology via
vms.yaml— each terminal gets an optionalvmfield inconfig.yaml, nginx routes to the correct container, per-VM concurrency caps viaMT5_HTTPAPI_MAX_IN_FLIGHT_<NAME>.Backward compatible: no
vms.yaml= single-VM mode, novmfield = routes tomt5. Seedocs/multi-vm-setup.mdfor the full walkthrough.