4.4 KiB
Kernel NATS consumer
Consumes JSON StartGameRequest messages. kernel.proto is used only as field schema reference; no protobuf runtime or encoding is used.
Expected message shape:
{
"team_count": 2,
"services": [],
"teams": [{"team_name": "red", "team_id": "red-1"}, {"team_name": "blue", "team_id": "blue-1"}],
"game_starts_at": 1730000000,
"rounds": 10,
"network_opens_at": 1729999900
}
Run from kernel/:
python -m pip install -e .
init-nats
NATS_URL=nats://127.0.0.1:4222 kernel-consumer
init-nats idempotently creates stream GAME and its game.start channel. It also adds configured NATS_SUBJECT to existing configured NATS_STREAM.
Environment variables: NATS_URL, NATS_STREAM (GAME), NATS_SUBJECT (game.start), NATS_CONSUMER (kernel-consumer).
Cloud.ru Evolution VPC
VPCNetworkManager is a synchronous client for documented Evolution VPC API operations. Pass a Cloud.ru Bearer JWT directly; do not commit it.
from cloud import CreateVPCRequest, VPCNetworkManager
manager = VPCNetworkManager(token="<jwt>")
operation = manager.create_vpc(
CreateVPCRequest(projectId="<project-id>", name="game-vpc")
)
vpcs = manager.list_vpcs(project_id="<project-id>")
It supports VPC CRUD, operation lookup, and static-route read/create/delete. Evolution VPC public API does not expose endpoints for listing or creating subnets.
Cloud.ru Virtual Machines
VMManager is a synchronous client for documented Evolution Virtual Machines API methods. Pass a Cloud.ru Bearer JWT directly; do not commit it.
from cloud import VMCreateRequest, VMManager
manager = VMManager(token="<jwt>")
vms = manager.list_vms(project_id="<project-id>")
vms = manager.create_vm(VMCreateRequest(
project_id="<project-id>",
name="game-vm",
disks=[{"disk_id": "<disk-id>"}],
))
manager.start_vm(vms[0].id)
It supports VM list/create/read/update/delete, batch creation, start/stop, power actions, rebuild, VNC and remote-console creation, password management, and guest-agent checks. Managers accept Pydantic request models and return validated Pydantic response models. VMCreateRequest requires project_id, name, and non-empty disks.
Network Pool
Copy environment template and set Cloud.ru credentials:
cp .env.example .env
Run database migrations before starting consumer:
DATABASE_URL="sqlite:///$PWD/kernel.db" PYTHONPATH=src alembic upgrade head
.env loads automatically for consumer and Alembic. Shell environment variables override .env. Set DATABASE_URL, CLOUD_PROJECT_ID, CLOUD_KEY_ID, and CLOUD_SECRET. Consumer requests fresh Cloud.ru JWT from IAM at startup; it uses CLOUD_TOKEN only when IAM credentials are absent. Set CLOUD_VPC_ID and CLOUD_AVAILABILITY_ZONE_ID (UUID) or CLOUD_AVAILABILITY_ZONE_NAME (resolved to an ID via GET /api/v1/availability-zones) when project defaults do not select VPC and availability zone. The subnet API expects the zone as a flat availability_zone_id UUID. Without DATABASE_URL, consumer uses absolute kernel/kernel.db, independent of launch folder. Alembic uses DATABASE_URL when set. For every valid game message, consumer creates UUID and allocates network through NetworkingManager. Free pool networks are verified using Cloud.ru subnet API before allocation. Missing cloud subnet records are removed and recreated as 10.N.0.0/20, where N ranges from 0 to 240 in steps of 16. NetworkPoolExhaustedError stops allocation when all CIDRs are used.
For each valid game event, the consumer creates team_count + 1 Ubuntu VMs. Every VM is attached to the allocated game subnet; the extra VM receives a new public IPv4 address. Configure CLOUD_VM_PASSWORD or CLOUD_VM_PUBLIC_KEY for Ubuntu authentication, plus optional CLOUD_VM_FLAVOR_NAME, CLOUD_VM_IMAGE_NAME, and CLOUD_VM_DISK_TYPE_NAME overrides.
Game Cleanup
Initialize both JetStream subjects with init-nats, then queue cleanup for a game:
init-nats
fire-game-cleanup 4bc5200f-09ba-4e18-8da4-64f3950d342e
The cleanup message is published to NATS_CLEANUP_SUBJECT (game.cleanup by default):
{"game_id": "4bc5200f-09ba-4e18-8da4-64f3950d342e"}
The consumer finds VMs named for that game, deletes them, waits until Cloud.ru removes them, and only then releases the game subnet back to the pool. Cleanup is idempotent if the VMs or network were already removed.