# Self-contained lab stack: Proxmox + oVirt + VMware + OpenStack # One PostgreSQL, four databases. No sibling source trees required. # # cd /Users/inecs/Разработка/simulators # docker compose pull # docker compose up -d # # Override tags: IMAGE_TAG=0.1.0 docker compose up -d # # Laboratory only — default passwords / TICKET_SIGNING_KEY are intentional. name: api-simulators-lab x-ticket: &ticket TICKET_SIGNING_KEY: ${TICKET_SIGNING_KEY:-development-only-signing-key-change-me} LOG_LEVEL: ${LOG_LEVEL:-INFO} networks: lab: proxmox_net: ovirt_net: vmware_net: openstack_net: volumes: postgres-data: services: postgres: image: postgres:17.5-bookworm restart: unless-stopped networks: [lab, proxmox_net, ovirt_net, vmware_net, openstack_net] environment: POSTGRES_USER: lab POSTGRES_PASSWORD: lab POSTGRES_DB: postgres healthcheck: test: ["CMD-SHELL", "pg_isready -U lab -d postgres"] interval: 5s timeout: 3s retries: 12 start_period: 5s volumes: - postgres-data:/var/lib/postgresql/data - ./docker/postgres-init:/docker-entrypoint-initdb.d:ro ports: - "127.0.0.1:5432:5432" # ── Proxmox VE API ────────────────────────────────────── proxmox-migrate: profiles: [init] image: inecs/proxmox-api-simulator:${IMAGE_TAG:-latest} networks: [proxmox_net] environment: <<: *ticket DATABASE_URL: postgresql://proxmox:proxmox@postgres:5432/proxmox_simulator CONTRACT_SNAPSHOT: /app/contracts/pve-9.2.3.json COMPATIBILITY_EVIDENCE: /app/evidence/pve-9.2.3.json depends_on: postgres: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.db.migrate_cli"] restart: "no" proxmox: image: inecs/proxmox-api-simulator:${IMAGE_TAG:-latest} restart: unless-stopped networks: [proxmox_net] environment: <<: *ticket DATABASE_URL: postgresql://proxmox:proxmox@postgres:5432/proxmox_simulator CONTRACT_SNAPSHOT: /app/contracts/pve-9.2.3.json COMPATIBILITY_EVIDENCE: /app/evidence/pve-9.2.3.json TASK_WORKER_CONCURRENCY: "2" SIMULATION_TIME_SCALE: "10" depends_on: postgres: { condition: service_healthy } healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8006/health/ready', timeout=2)", ] interval: 10s timeout: 3s retries: 10 start_period: 25s ports: - "8006:8006" proxmox-seed: profiles: [init] image: inecs/proxmox-api-simulator:${IMAGE_TAG:-latest} networks: [proxmox_net] environment: <<: *ticket DATABASE_URL: postgresql://proxmox:proxmox@postgres:5432/proxmox_simulator SEED_PROFILE: ${PROXMOX_SEED_PROFILE:-small} depends_on: proxmox: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.simulation.seed_cli"] restart: "no" # ── oVirt Engine API ──────────────────────────────────── ovirt-migrate: profiles: [init] image: inecs/ovirt-api-simulator:${IMAGE_TAG:-latest} networks: [ovirt_net] environment: <<: *ticket DATABASE_URL: postgresql://ovirt:ovirt@postgres:5432/ovirt_simulator OVIRT_SERIES: ${OVIRT_SERIES:-4.5} depends_on: postgres: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.db.migrate_cli"] restart: "no" ovirt: image: inecs/ovirt-api-simulator:${IMAGE_TAG:-latest} restart: unless-stopped networks: ovirt_net: aliases: [simulator] environment: <<: *ticket DATABASE_URL: postgresql://ovirt:ovirt@postgres:5432/ovirt_simulator OVIRT_SERIES: ${OVIRT_SERIES:-4.5} APP_PORT: "8080" depends_on: postgres: { condition: service_healthy } healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health/ready', timeout=2)", ] interval: 10s timeout: 3s retries: 10 start_period: 30s expose: - "8080" ovirt-gateway: image: nginx:1.28.0-alpine restart: unless-stopped networks: [ovirt_net] depends_on: ovirt: { condition: service_healthy } ports: - "7443:443" - "7500:5000" volumes: - ./docker/ovirt/ovirt-engine.conf:/etc/nginx/conf.d/default.conf:ro - ./docker/tls/server.crt:/etc/nginx/tls/server.crt:ro - ./docker/tls/server.key:/etc/nginx/tls/server.key:ro read_only: true tmpfs: [/var/cache/nginx, /var/run, /tmp] security_opt: [no-new-privileges:true] ovirt-seed: profiles: [init] image: inecs/ovirt-api-simulator:${IMAGE_TAG:-latest} networks: [ovirt_net] environment: <<: *ticket DATABASE_URL: postgresql://ovirt:ovirt@postgres:5432/ovirt_simulator OVIRT_SERIES: ${OVIRT_SERIES:-4.5} depends_on: ovirt: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.ovirt.seed_cli", "--profile", "${OVIRT_SEED_PROFILE:-minimal}"] restart: "no" # ── VMware vSphere API ────────────────────────────────── vmware-migrate: profiles: [init] image: inecs/vmware-api-simulator:${IMAGE_TAG:-latest} networks: [vmware_net] environment: <<: *ticket DATABASE_URL: postgresql://vmware:vmware@postgres:5432/vmware_simulator ENABLE_PVE_STUB: "false" APP_PORT: "8080" depends_on: postgres: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.db.migrate_cli"] restart: "no" vmware: image: inecs/vmware-api-simulator:${IMAGE_TAG:-latest} restart: unless-stopped networks: vmware_net: aliases: [simulator] environment: <<: *ticket DATABASE_URL: postgresql://vmware:vmware@postgres:5432/vmware_simulator ENABLE_PVE_STUB: "false" SEED_VSPHERE_PROFILE: ${VMWARE_SEED_PROFILE:-small} APP_PORT: "8080" TASK_WORKER_CONCURRENCY: "2" SIMULATION_TIME_SCALE: "10" depends_on: postgres: { condition: service_healthy } healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health/ready', timeout=2)", ] interval: 10s timeout: 3s retries: 10 start_period: 25s expose: - "8080" vmware-gateway: image: nginx:1.28.0-alpine restart: unless-stopped networks: [vmware_net] depends_on: vmware: { condition: service_healthy } ports: - "8081:80" - "8443:443" volumes: - ./docker/vmware/vmware-ports.conf:/etc/nginx/conf.d/default.conf:ro - ./docker/tls/server.crt:/etc/nginx/tls/server.crt:ro - ./docker/tls/server.key:/etc/nginx/tls/server.key:ro read_only: true tmpfs: [/var/cache/nginx, /var/run, /tmp] security_opt: [no-new-privileges:true] vmware-seed: profiles: [init] image: inecs/vmware-api-simulator:${IMAGE_TAG:-latest} networks: [vmware_net] environment: <<: *ticket DATABASE_URL: postgresql://vmware:vmware@postgres:5432/vmware_simulator ENABLE_PVE_STUB: "false" SEED_PROFILE: ${VMWARE_SEED_PROFILE:-small} SEED_VSPHERE_PROFILE: ${VMWARE_SEED_PROFILE:-small} depends_on: vmware: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.simulation.seed_cli"] restart: "no" # ── OpenStack API ─────────────────────────────────────── openstack-migrate: profiles: [init] image: inecs/openstack-api-simulator:${IMAGE_TAG:-latest} networks: [openstack_net] environment: <<: *ticket DATABASE_URL: postgresql://openstack:openstack@postgres:5432/openstack_simulator APP_PORT: "8080" depends_on: postgres: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.db.migrate_cli"] restart: "no" openstack: image: inecs/openstack-api-simulator:${IMAGE_TAG:-latest} restart: unless-stopped networks: openstack_net: aliases: [simulator] environment: <<: *ticket DATABASE_URL: postgresql://openstack:openstack@postgres:5432/openstack_simulator APP_PORT: "8080" TASK_WORKER_CONCURRENCY: "2" SIMULATION_TIME_SCALE: "10" depends_on: postgres: { condition: service_healthy } healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health/ready', timeout=2)", ] interval: 10s timeout: 3s retries: 10 start_period: 25s expose: - "8080" openstack-gateway: image: nginx:1.28.0-alpine restart: unless-stopped networks: [openstack_net] depends_on: openstack: { condition: service_healthy } ports: - "9080:80" - "9443:443" - "9500:5000" - "5050:5050" - "6385:6385" - "8000:8000" - "8003:8003" - "8004:8004" - "8042:8042" - "8080:8080" - "8774:8774" - "8776:8776" - "8779:8779" - "8786:8786" - "8989:8989" - "9001:9001" - "9090:9090" - "9292:9292" - "9696:9696" - "9876:9876" volumes: - ./docker/openstack/openstack-ports.conf:/etc/nginx/conf.d/default.conf:ro - ./docker/tls/server.crt:/etc/nginx/tls/server.crt:ro - ./docker/tls/server.key:/etc/nginx/tls/server.key:ro read_only: true tmpfs: [/var/cache/nginx, /var/run, /tmp] security_opt: [no-new-privileges:true] openstack-seed: profiles: [init] image: inecs/openstack-api-simulator:${IMAGE_TAG:-latest} networks: [openstack_net] environment: <<: *ticket DATABASE_URL: postgresql://openstack:openstack@postgres:5432/openstack_simulator depends_on: openstack: { condition: service_healthy } entrypoint: ["python"] command: ["-m", "app.openstack.seed_cli", "--profile", "${OPENSTACK_SEED_PROFILE:-minimal}"] restart: "no"