Add self-contained API simulators lab with Compose and Helm.
Ship Proxmox, oVirt, VMware, and OpenStack behind one Postgres stack, Makefile helpers (up/clean/helm up|down/push), and a Helm chart with namespace, Let's Encrypt ingresses, migrate inits, and skip-if-seeded jobs.
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
COMPOSE ?= docker compose
|
||||||
|
COMPOSE_INIT := $(COMPOSE) --profile init
|
||||||
|
|
||||||
|
MIGRATE := proxmox-migrate ovirt-migrate vmware-migrate openstack-migrate
|
||||||
|
SEED := proxmox-seed ovirt-seed vmware-seed openstack-seed
|
||||||
|
APPS := postgres proxmox ovirt vmware openstack ovirt-gateway vmware-gateway openstack-gateway
|
||||||
|
|
||||||
|
CHART ?= charts/api-simulators-lab
|
||||||
|
HELM_RELEASE ?= simulators
|
||||||
|
# Prefer values.yaml namespace; override with: make helm-up HELM_NAMESPACE=other
|
||||||
|
HELM_NAMESPACE ?= $(shell awk '/^namespace:/{print $$2; exit}' $(CHART)/values.yaml)
|
||||||
|
|
||||||
|
# Second word for: make helm up | make helm down
|
||||||
|
ifeq (helm,$(firstword $(MAKECMDGOALS)))
|
||||||
|
HELM_CMD := $(word 2,$(MAKECMDGOALS))
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: up down clean restart logs shell pull ps urls help init-clean \
|
||||||
|
helm helm-up helm-down helm-template helm-lint helm-urls \
|
||||||
|
helm-install helm-uninstall push
|
||||||
|
|
||||||
|
help: ## Show targets
|
||||||
|
@echo "Compose:"
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} /^(up|down|clean|restart|logs|shell|pull|ps|urls|init-clean):.*?## / {printf " %-16s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
@echo ""
|
||||||
|
@echo "Helm (make helm up | make helm down):"
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} /^helm(-[a-zA-Z0-9]+)?:.*?## / {printf " %-16s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
@echo ""
|
||||||
|
@echo "Other:"
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} /^(help|push):.*?## / {printf " %-16s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
up: ## Compose: pull, migrate, start apps, seed
|
||||||
|
ifdef HELM_CMD
|
||||||
|
@:
|
||||||
|
else
|
||||||
|
$(COMPOSE) pull
|
||||||
|
$(COMPOSE) up -d postgres
|
||||||
|
$(COMPOSE) up -d --wait postgres
|
||||||
|
@echo ">> migrate"
|
||||||
|
@set -e; for s in $(MIGRATE); do \
|
||||||
|
echo " $$s"; \
|
||||||
|
$(COMPOSE_INIT) run --rm --no-deps $$s; \
|
||||||
|
done
|
||||||
|
@echo ">> apps + gateways"
|
||||||
|
$(COMPOSE) up -d --wait $(APPS)
|
||||||
|
@echo ">> seed"
|
||||||
|
@set -e; for s in $(SEED); do \
|
||||||
|
echo " $$s"; \
|
||||||
|
$(COMPOSE_INIT) run --rm --no-deps $$s; \
|
||||||
|
done
|
||||||
|
@$(MAKE) init-clean
|
||||||
|
@echo "Stack is up (migrate/seed containers removed)."
|
||||||
|
@$(MAKE) --no-print-directory urls
|
||||||
|
endif
|
||||||
|
|
||||||
|
urls: ## Compose: print simulator URLs
|
||||||
|
@printf '\nEndpoints:\n'
|
||||||
|
@printf ' %-22s %s\n' 'Proxmox' 'http://localhost:8006/'
|
||||||
|
@printf ' %-22s %s\n' 'oVirt Engine' 'https://localhost:7443/'
|
||||||
|
@printf ' %-22s %s\n' 'oVirt Web UI' 'http://localhost:7500/'
|
||||||
|
@printf ' %-22s %s\n' 'VMware (HTTPS)' 'https://localhost:8443/'
|
||||||
|
@printf ' %-22s %s\n' 'VMware (HTTP)' 'http://localhost:8081/'
|
||||||
|
@printf ' %-22s %s\n' 'OpenStack Keystone' 'http://localhost:9500/'
|
||||||
|
@printf ' %-22s %s\n' 'OpenStack Nova' 'http://localhost:8774/'
|
||||||
|
@printf ' %-22s %s\n' 'OpenStack HTTPS' 'https://localhost:9443/'
|
||||||
|
@printf ' %-22s %s\n' 'PostgreSQL' '127.0.0.1:5432 (lab / lab)'
|
||||||
|
@printf '\n'
|
||||||
|
|
||||||
|
init-clean: ## Compose: remove leftover migrate/seed containers
|
||||||
|
-$(COMPOSE_INIT) rm -f $(MIGRATE) $(SEED) 2>/dev/null
|
||||||
|
-@ids=$$(docker ps -aq --filter "label=com.docker.compose.project=api-simulators-lab" \
|
||||||
|
--filter "name=migrate" --filter "name=seed" 2>/dev/null); \
|
||||||
|
[ -z "$$ids" ] || docker rm -f $$ids 2>/dev/null || true
|
||||||
|
|
||||||
|
down: ## Compose: stop and remove containers (keep DB volume)
|
||||||
|
ifdef HELM_CMD
|
||||||
|
@:
|
||||||
|
else
|
||||||
|
$(COMPOSE_INIT) down --remove-orphans
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean: ## Compose: stop stack and remove volumes / leftovers
|
||||||
|
$(COMPOSE_INIT) down -v --remove-orphans
|
||||||
|
@$(MAKE) init-clean
|
||||||
|
-@nets=$$(docker network ls -q --filter "label=com.docker.compose.project=api-simulators-lab" 2>/dev/null); \
|
||||||
|
[ -z "$$nets" ] || docker network rm $$nets 2>/dev/null || true
|
||||||
|
@echo "Stack cleaned (containers, volumes, leftover init)."
|
||||||
|
|
||||||
|
restart: ## Compose: restart app services
|
||||||
|
$(COMPOSE) restart $(filter-out postgres,$(APPS))
|
||||||
|
|
||||||
|
logs: ## Compose: follow logs (SERVICE=proxmox optional)
|
||||||
|
$(COMPOSE) logs -f --tail=200 $(SERVICE)
|
||||||
|
|
||||||
|
shell: ## Compose: shell into a service (SERVICE=proxmox default)
|
||||||
|
$(COMPOSE) exec $(or $(SERVICE),proxmox) bash || \
|
||||||
|
$(COMPOSE) exec $(or $(SERVICE),proxmox) sh
|
||||||
|
|
||||||
|
pull: ## Compose: pull Hub images only
|
||||||
|
$(COMPOSE) pull
|
||||||
|
|
||||||
|
ps: ## Compose: show container status
|
||||||
|
$(COMPOSE) ps -a
|
||||||
|
|
||||||
|
# ── Helm ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
helm: ## Dispatcher: make helm up | make helm down
|
||||||
|
@case "$(HELM_CMD)" in \
|
||||||
|
up) $(MAKE) helm-up ;; \
|
||||||
|
down) $(MAKE) helm-down ;; \
|
||||||
|
"") echo "Usage: make helm up | make helm down"; exit 1 ;; \
|
||||||
|
*) echo "Unknown: make helm $(HELM_CMD)"; echo "Usage: make helm up | make helm down"; exit 1 ;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
helm-up: ## Helm: deploy chart (warns + asks confirmation)
|
||||||
|
@printf '\n'
|
||||||
|
@printf 'WARNING: Configure Helm values before deploying to the cluster.\n'
|
||||||
|
@printf ' File: %s/values.yaml\n' '$(CHART)'
|
||||||
|
@printf ' Review at least:\n'
|
||||||
|
@printf ' - namespace, ingresses, certManager\n'
|
||||||
|
@printf ' - imageTag, ticketSigningKey, imagePullSecrets\n'
|
||||||
|
@printf ' - postgres.persistence (size, storageClass)\n'
|
||||||
|
@printf ' Let'\''s Encrypt needs public DNS to the Ingress (not *.lab.local).\n'
|
||||||
|
@printf ' Release: %s\n' '$(HELM_RELEASE)'
|
||||||
|
@printf ' Namespace: %s (from values.namespace)\n' '$(HELM_NAMESPACE)'
|
||||||
|
@printf '\n'
|
||||||
|
ifeq ($(HELM_YES),1)
|
||||||
|
@echo "HELM_YES=1 — skipping confirmation."
|
||||||
|
else
|
||||||
|
@printf 'Values are configured and it is OK to deploy? [y/N] '
|
||||||
|
@read ans; \
|
||||||
|
case "$$ans" in \
|
||||||
|
y|Y|yes|YES) ;; \
|
||||||
|
*) echo "Aborted."; exit 1 ;; \
|
||||||
|
esac
|
||||||
|
endif
|
||||||
|
helm upgrade --install $(HELM_RELEASE) $(CHART) \
|
||||||
|
--namespace $(HELM_NAMESPACE) --create-namespace --wait \
|
||||||
|
--set namespace=$(HELM_NAMESPACE)
|
||||||
|
@$(MAKE) --no-print-directory helm-urls
|
||||||
|
|
||||||
|
helm-down: ## Helm: uninstall release
|
||||||
|
helm uninstall $(HELM_RELEASE) --namespace $(HELM_NAMESPACE) || true
|
||||||
|
@echo "Helm release $(HELM_RELEASE) removed from namespace $(HELM_NAMESPACE)."
|
||||||
|
|
||||||
|
helm-install: helm-up ## Alias of helm-up
|
||||||
|
|
||||||
|
helm-uninstall: helm-down ## Alias of helm-down
|
||||||
|
|
||||||
|
helm-template: ## Helm: render manifests
|
||||||
|
helm template $(HELM_RELEASE) $(CHART) --namespace $(HELM_NAMESPACE)
|
||||||
|
|
||||||
|
helm-lint: ## Helm: lint chart
|
||||||
|
helm lint $(CHART)
|
||||||
|
|
||||||
|
helm-urls: ## Helm: print Ingress hosts from values
|
||||||
|
@printf '\nIngress hosts (TLS / Let'\''s Encrypt):\n'
|
||||||
|
@awk '\
|
||||||
|
/^ingresses:/{p=1; next} \
|
||||||
|
p && /^[^ #\t]/{exit} \
|
||||||
|
p && /^ [a-zA-Z0-9_-]+:/{name=$$1; sub(/:/,"",name)} \
|
||||||
|
p && /^ host:/{host=$$2; printf " %-16s https://%s/\n", name, host} \
|
||||||
|
' $(CHART)/values.yaml
|
||||||
|
@printf '\n'
|
||||||
|
|
||||||
|
push: ## git add ., multiline commit (Ctrl-D), push origin
|
||||||
|
@set -e; \
|
||||||
|
git add .; \
|
||||||
|
echo "=== staged ==="; \
|
||||||
|
git status --short; \
|
||||||
|
echo; \
|
||||||
|
if git diff --cached --quiet; then \
|
||||||
|
echo "Nothing to commit — pushing current branch."; \
|
||||||
|
else \
|
||||||
|
echo "Enter commit message, then Ctrl-D:"; \
|
||||||
|
msg=$$(cat </dev/tty); \
|
||||||
|
if [ -z "$$msg" ]; then echo "Empty commit message, aborting." >&2; exit 1; fi; \
|
||||||
|
git commit -m "$$msg"; \
|
||||||
|
fi; \
|
||||||
|
echo "Pushing to both remotes:"; \
|
||||||
|
git remote get-url --push --all origin | sed 's/^/ - /'; \
|
||||||
|
git push origin HEAD
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
**Language / Язык:** [English](README.md) | [Русский](README.ru.md)
|
||||||
|
|
||||||
|
# API simulators lab (self-contained)
|
||||||
|
|
||||||
|
Four published simulators and one PostgreSQL. No source checkouts required.
|
||||||
|
Run locally with Docker Compose, or on Kubernetes with the Helm chart.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
simulators/
|
||||||
|
Makefile
|
||||||
|
docker-compose.yml
|
||||||
|
docker/
|
||||||
|
postgres-init/01-simulators.sql
|
||||||
|
tls/server.{crt,key} # shared lab TLS for Compose gateways
|
||||||
|
ovirt/ovirt-engine.conf
|
||||||
|
vmware/vmware-ports.conf
|
||||||
|
openstack/openstack-ports.conf
|
||||||
|
charts/api-simulators-lab/ # Helm: all four simulators + Ingress
|
||||||
|
```
|
||||||
|
|
||||||
|
Images: `inecs/proxmox-api-simulator`, `inecs/ovirt-api-simulator`,
|
||||||
|
`inecs/vmware-api-simulator`, `inecs/openstack-api-simulator`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
Postgres data lives in the Compose volume `postgres-data`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/inecs/Разработка/simulators
|
||||||
|
make help
|
||||||
|
make up # pull → migrate → apps → seed; prints URLs
|
||||||
|
make urls # print Compose endpoints again
|
||||||
|
make logs # or: make logs SERVICE=proxmox
|
||||||
|
make restart
|
||||||
|
make down # stop containers, keep DB volume
|
||||||
|
make clean # stop + remove volumes, leftovers, project networks
|
||||||
|
make shell # or: make shell SERVICE=ovirt
|
||||||
|
make push # git add . → multiline commit (Ctrl-D) → push origin
|
||||||
|
```
|
||||||
|
|
||||||
|
`make up` runs migrate/seed via `docker compose run --rm` (profile `init`), so those
|
||||||
|
containers do not stay in `Exited` state.
|
||||||
|
|
||||||
|
Equivalent Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
The first start creates databases via `docker/postgres-init` (only on an empty
|
||||||
|
`postgres-data` volume). Seed jobs run once after each simulator becomes healthy.
|
||||||
|
|
||||||
|
Pin an image tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
IMAGE_TAG=0.1.0 docker compose up -d
|
||||||
|
# or: IMAGE_TAG=0.1.0 make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compose endpoints
|
||||||
|
|
||||||
|
| Simulator | URL |
|
||||||
|
|---|---|
|
||||||
|
| Proxmox | http://localhost:8006/ |
|
||||||
|
| oVirt Engine | https://localhost:7443/ |
|
||||||
|
| oVirt Web UI | http://localhost:7500/ |
|
||||||
|
| VMware (HTTPS) | https://localhost:8443/ |
|
||||||
|
| VMware (HTTP) | http://localhost:8081/ |
|
||||||
|
| OpenStack Keystone | http://localhost:9500/ |
|
||||||
|
| OpenStack Nova | http://localhost:8774/ |
|
||||||
|
| OpenStack HTTPS | https://localhost:9443/ |
|
||||||
|
| PostgreSQL | `127.0.0.1:5432` (user `lab` / `lab`) |
|
||||||
|
|
||||||
|
### Reset the Compose database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
make up
|
||||||
|
# or: docker compose down -v && docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kubernetes (Helm)
|
||||||
|
|
||||||
|
Chart: `charts/api-simulators-lab`.
|
||||||
|
|
||||||
|
Configure before deploy: `charts/api-simulators-lab/values.yaml`
|
||||||
|
|
||||||
|
| Key | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `namespace` | Target namespace (`simulators` by default; `namespaceCreate: true`) |
|
||||||
|
| `ingresses` | Hosts, TLS secrets, annotations (one Ingress per entry) |
|
||||||
|
| `certManager` | Let's Encrypt via cert-manager (`letsencrypt-prod`) |
|
||||||
|
| `imageTag` / secrets / `postgres.persistence` | Images, keys, PVC |
|
||||||
|
|
||||||
|
Default Ingress hosts (override for real DNS before Let's Encrypt):
|
||||||
|
|
||||||
|
| Simulator | Host |
|
||||||
|
|---|---|
|
||||||
|
| Proxmox | https://proxmox.lab.local/ |
|
||||||
|
| oVirt | https://ovirt.lab.local/ |
|
||||||
|
| VMware | https://vmware.lab.local/ |
|
||||||
|
| OpenStack | https://openstack.lab.local/ |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make helm up # warns → confirm [y/N] → helm upgrade --install
|
||||||
|
make helm down # uninstall release
|
||||||
|
make helm-urls # print Ingress hosts from values
|
||||||
|
make helm-template
|
||||||
|
make helm-lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Skip the confirmation prompt (CI): `make helm-up HELM_YES=1`
|
||||||
|
|
||||||
|
Override namespace without editing values: `HELM_NAMESPACE=other make helm-up`
|
||||||
|
|
||||||
|
Equivalent Helm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade --install simulators charts/api-simulators-lab \
|
||||||
|
--namespace simulators --create-namespace --wait
|
||||||
|
```
|
||||||
|
|
||||||
|
### How the chart starts
|
||||||
|
|
||||||
|
1. **Namespace** — created from `values.namespace` when `namespaceCreate: true`.
|
||||||
|
2. **PostgreSQL** — StatefulSet + init SQL for four simulator databases.
|
||||||
|
3. **Simulators** — Deployments with init containers:
|
||||||
|
- `wait-postgres` → `migrate` (idempotent; no-op if schema is current).
|
||||||
|
4. **Seed** — post-install / post-upgrade Jobs per simulator.
|
||||||
|
- Each Job waits until the app is ready, then seeds.
|
||||||
|
- `seed.skipIfSql` skips when the DB already has lab data (no wipe/re-seed).
|
||||||
|
5. **Ingress** — TLS via cert-manager annotation `cert-manager.io/cluster-issuer`.
|
||||||
|
- Set `certManager.createClusterIssuer: true` and `certManager.email` to create
|
||||||
|
ClusterIssuers from the chart; otherwise an existing `letsencrypt-prod` is used.
|
||||||
|
- Let's Encrypt HTTP-01 needs **public DNS** pointing at the Ingress controller
|
||||||
|
(not `*.lab.local`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
`origin` pushes to both remotes (same pattern as other simulators):
|
||||||
|
|
||||||
|
- `git@github.com:sergeyantropoff/simulators.git`
|
||||||
|
- `ssh://git@git.antropoff.ru:30022/DevOpsTools/Simulators.git`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make push # stage all → multiline commit message (end with Ctrl-D) → push origin
|
||||||
|
```
|
||||||
|
|
||||||
|
Same UX as `proxmox_api_simulator`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Laboratory defaults only — do not expose this stack to the public Internet without rotating secrets (`ticketSigningKey`, DB passwords, Ingress hosts).
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
**Language / Язык:** [English](README.md) | [Русский](README.ru.md)
|
||||||
|
|
||||||
|
# Лаборатория API-симуляторов (автономная)
|
||||||
|
|
||||||
|
Четыре опубликованных симулятора и один PostgreSQL. Исходники репозиториев не нужны.
|
||||||
|
Локально — Docker Compose; в Kubernetes — Helm-чарт.
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
```
|
||||||
|
simulators/
|
||||||
|
Makefile
|
||||||
|
docker-compose.yml
|
||||||
|
docker/
|
||||||
|
postgres-init/01-simulators.sql
|
||||||
|
tls/server.{crt,key} # общие lab TLS для Compose gateway
|
||||||
|
ovirt/ovirt-engine.conf
|
||||||
|
vmware/vmware-ports.conf
|
||||||
|
openstack/openstack-ports.conf
|
||||||
|
charts/api-simulators-lab/ # Helm: все четыре симулятора + Ingress
|
||||||
|
```
|
||||||
|
|
||||||
|
Образы: `inecs/proxmox-api-simulator`, `inecs/ovirt-api-simulator`,
|
||||||
|
`inecs/vmware-api-simulator`, `inecs/openstack-api-simulator`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
Данные Postgres — в Compose volume `postgres-data`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/inecs/Разработка/simulators
|
||||||
|
make help
|
||||||
|
make up # pull → migrate → apps → seed; печатает URL
|
||||||
|
make urls # снова вывести Compose-эндпоинты
|
||||||
|
make logs # или: make logs SERVICE=proxmox
|
||||||
|
make restart
|
||||||
|
make down # остановить контейнеры, volume БД сохранить
|
||||||
|
make clean # остановить + удалить volumes, мусор и сети проекта
|
||||||
|
make shell # или: make shell SERVICE=ovirt
|
||||||
|
make push # git add . → многострочный commit (Ctrl-D) → push origin
|
||||||
|
```
|
||||||
|
|
||||||
|
`make up` гоняет migrate/seed через `docker compose run --rm` (profile `init`),
|
||||||
|
поэтому эти контейнеры не остаются в статусе `Exited`.
|
||||||
|
|
||||||
|
Эквивалент через Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
При первом старте БД создаются из `docker/postgres-init` (только на пустом
|
||||||
|
volume `postgres-data`). Seed выполняется один раз после готовности каждого симулятора.
|
||||||
|
|
||||||
|
Закрепить тег образа:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
IMAGE_TAG=0.1.0 docker compose up -d
|
||||||
|
# или: IMAGE_TAG=0.1.0 make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compose-эндпоинты
|
||||||
|
|
||||||
|
| Симулятор | URL |
|
||||||
|
|---|---|
|
||||||
|
| Proxmox | http://localhost:8006/ |
|
||||||
|
| oVirt Engine | https://localhost:7443/ |
|
||||||
|
| oVirt Web UI | http://localhost:7500/ |
|
||||||
|
| VMware (HTTPS) | https://localhost:8443/ |
|
||||||
|
| VMware (HTTP) | http://localhost:8081/ |
|
||||||
|
| OpenStack Keystone | http://localhost:9500/ |
|
||||||
|
| OpenStack Nova | http://localhost:8774/ |
|
||||||
|
| OpenStack HTTPS | https://localhost:9443/ |
|
||||||
|
| PostgreSQL | `127.0.0.1:5432` (пользователь `lab` / `lab`) |
|
||||||
|
|
||||||
|
### Сброс БД Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
make up
|
||||||
|
# или: docker compose down -v && docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kubernetes (Helm)
|
||||||
|
|
||||||
|
Чарт: `charts/api-simulators-lab`.
|
||||||
|
|
||||||
|
Перед деплоем настройте: `charts/api-simulators-lab/values.yaml`
|
||||||
|
|
||||||
|
| Ключ | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `namespace` | Целевой namespace (`simulators` по умолчанию; `namespaceCreate: true`) |
|
||||||
|
| `ingresses` | Хосты, TLS secrets, annotations (по одному Ingress на запись) |
|
||||||
|
| `certManager` | Let's Encrypt через cert-manager (`letsencrypt-prod`) |
|
||||||
|
| `imageTag` / секреты / `postgres.persistence` | Образы, ключи, PVC |
|
||||||
|
|
||||||
|
Хосты Ingress по умолчанию (для Let's Encrypt замените на публичный DNS):
|
||||||
|
|
||||||
|
| Симулятор | Host |
|
||||||
|
|---|---|
|
||||||
|
| Proxmox | https://proxmox.lab.local/ |
|
||||||
|
| oVirt | https://ovirt.lab.local/ |
|
||||||
|
| VMware | https://vmware.lab.local/ |
|
||||||
|
| OpenStack | https://openstack.lab.local/ |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make helm up # предупреждение → подтверждение [y/N] → helm upgrade --install
|
||||||
|
make helm down # uninstall релиза
|
||||||
|
make helm-urls # хосты Ingress из values
|
||||||
|
make helm-template
|
||||||
|
make helm-lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Без подтверждения (CI): `make helm-up HELM_YES=1`
|
||||||
|
|
||||||
|
Сменить namespace без правки файла: `HELM_NAMESPACE=other make helm-up`
|
||||||
|
|
||||||
|
Эквивалент Helm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade --install simulators charts/api-simulators-lab \
|
||||||
|
--namespace simulators --create-namespace --wait
|
||||||
|
```
|
||||||
|
|
||||||
|
### Как стартует чарт
|
||||||
|
|
||||||
|
1. **Namespace** — создаётся из `values.namespace`, если `namespaceCreate: true`.
|
||||||
|
2. **PostgreSQL** — StatefulSet + init SQL на четыре БД симуляторов.
|
||||||
|
3. **Симуляторы** — Deployments с init-контейнерами:
|
||||||
|
- `wait-postgres` → `migrate` (идемпотентно; ничего не делает, если схема уже актуальна).
|
||||||
|
4. **Seed** — Jobs post-install / post-upgrade на каждый симулятор.
|
||||||
|
- Job ждёт ready приложения, затем сидирует.
|
||||||
|
- `seed.skipIfSql` пропускает seed, если в БД уже есть лабораторные данные.
|
||||||
|
5. **Ingress** — TLS через аннотацию cert-manager `cert-manager.io/cluster-issuer`.
|
||||||
|
- `certManager.createClusterIssuer: true` и `certManager.email` — создать
|
||||||
|
ClusterIssuer из чарта; иначе ожидается уже существующий `letsencrypt-prod`.
|
||||||
|
- Let's Encrypt HTTP-01 требует **публичный DNS** на Ingress-контроллер
|
||||||
|
(не `*.lab.local`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
`origin` пушит в оба remote (как у остальных симуляторов):
|
||||||
|
|
||||||
|
- `git@github.com:sergeyantropoff/simulators.git`
|
||||||
|
- `ssh://git@git.antropoff.ru:30022/DevOpsTools/Simulators.git`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make push # stage all → многострочное сообщение (конец — Ctrl-D) → push origin
|
||||||
|
```
|
||||||
|
|
||||||
|
Тот же UX, что в `proxmox_api_simulator`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Только лабораторные defaults — не выставляйте стек в публичный Интернет без смены
|
||||||
|
секретов (`ticketSigningKey`, пароли БД, хосты Ingress).
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.DS_Store
|
||||||
|
*.tgz
|
||||||
|
charts/
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: api-simulators-lab
|
||||||
|
description: Laboratory stack — Proxmox, oVirt, VMware and OpenStack API simulators
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "latest"
|
||||||
|
keywords:
|
||||||
|
- simulators
|
||||||
|
- proxmox
|
||||||
|
- ovirt
|
||||||
|
- vmware
|
||||||
|
- openstack
|
||||||
|
maintainers:
|
||||||
|
- name: inecs
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
1. Namespace: {{ include "lab.namespace" . }}
|
||||||
|
|
||||||
|
2. Point DNS (or /etc/hosts for local-only) at the Ingress controller:
|
||||||
|
{{- range $name, $ing := .Values.ingresses }}
|
||||||
|
{{- if $ing.enabled }}
|
||||||
|
{{ $ing.host }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
3. Open simulators (TLS via cert-manager / Let's Encrypt when enabled):
|
||||||
|
{{- range $name, $ing := .Values.ingresses }}
|
||||||
|
{{- if $ing.enabled }}
|
||||||
|
{{- $https := or $ing.tls $.Values.certManager.enabled }}
|
||||||
|
http{{ if $https }}s{{ end }}://{{ $ing.host }}{{ $ing.path | default "/" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Values.certManager.enabled }}
|
||||||
|
|
||||||
|
cert-manager ClusterIssuer: {{ include "lab.clusterIssuer" . }}
|
||||||
|
{{- if .Values.certManager.createClusterIssuer }}
|
||||||
|
(created by this chart; email={{ .Values.certManager.email }})
|
||||||
|
{{- else }}
|
||||||
|
(expected to already exist in the cluster)
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
Configure namespace / hosts / TLS under `.Values.namespace`, `.Values.ingresses`, `.Values.certManager`.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "lab.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
*/}}
|
||||||
|
{{- define "lab.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "lab.chart" . }}
|
||||||
|
{{ include "lab.selectorLabels" . }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "lab.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.postgresHost" -}}
|
||||||
|
{{- printf "%s-postgres" (include "lab.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.namespace" -}}
|
||||||
|
{{- .Values.namespace | default .Release.Namespace }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.databaseUrl" -}}
|
||||||
|
{{- $sim := .sim -}}
|
||||||
|
{{- printf "postgresql://%s:%s@%s:5432/%s" $sim.db.user $sim.db.password (include "lab.postgresHost" .root) $sim.db.name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.commonEnv" -}}
|
||||||
|
- name: TICKET_SIGNING_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "lab.fullname" . }}
|
||||||
|
key: ticketSigningKey
|
||||||
|
- name: LOG_LEVEL
|
||||||
|
value: {{ .Values.logLevel | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "lab.clusterIssuer" -}}
|
||||||
|
{{- if .Values.certManager.useStaging }}
|
||||||
|
{{- .Values.certManager.stagingIssuerName }}
|
||||||
|
{{- else }}
|
||||||
|
{{- .Values.certManager.issuerName }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{{- if and .Values.certManager.enabled .Values.certManager.createClusterIssuer }}
|
||||||
|
{{- $solverClass := .Values.certManager.solverIngressClassName | default "nginx" }}
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: {{ .Values.certManager.issuerName }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
email: {{ required "certManager.email is required when createClusterIssuer=true" .Values.certManager.email | quote }}
|
||||||
|
server: {{ .Values.certManager.server | quote }}
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: {{ printf "%s-account-key" .Values.certManager.issuerName }}
|
||||||
|
solvers:
|
||||||
|
- http01:
|
||||||
|
ingress:
|
||||||
|
{{- if $solverClass }}
|
||||||
|
ingressClassName: {{ $solverClass }}
|
||||||
|
{{- end }}
|
||||||
|
---
|
||||||
|
{{- if .Values.certManager.createStagingIssuer }}
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: {{ .Values.certManager.stagingIssuerName }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
email: {{ .Values.certManager.email | quote }}
|
||||||
|
server: {{ .Values.certManager.stagingServer | quote }}
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: {{ printf "%s-account-key" .Values.certManager.stagingIssuerName }}
|
||||||
|
solvers:
|
||||||
|
- http01:
|
||||||
|
ingress:
|
||||||
|
{{- if $solverClass }}
|
||||||
|
ingressClassName: {{ $solverClass }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{{- range $name, $ing := .Values.ingresses }}
|
||||||
|
{{- if $ing.enabled }}
|
||||||
|
{{- $svcName := printf "%s-%s" (include "lab.fullname" $) $ing.service }}
|
||||||
|
{{- $className := $ing.className | default $.Values.ingressClassName }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.fullname" $ }}-{{ $name }}
|
||||||
|
namespace: {{ include "lab.namespace" $ }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" $ | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: ingress-{{ $name }}
|
||||||
|
annotations:
|
||||||
|
{{- if $.Values.certManager.enabled }}
|
||||||
|
cert-manager.io/cluster-issuer: {{ include "lab.clusterIssuer" $ | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with $ing.annotations }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- if $className }}
|
||||||
|
ingressClassName: {{ $className | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if $ing.tls }}
|
||||||
|
tls:
|
||||||
|
{{- range $ing.tls }}
|
||||||
|
- hosts:
|
||||||
|
{{- range .hosts }}
|
||||||
|
- {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
secretName: {{ .secretName }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else if $.Values.certManager.enabled }}
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- {{ $ing.host | quote }}
|
||||||
|
secretName: {{ printf "%s-%s-tls" (include "lab.fullname" $) $name }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
- host: {{ $ing.host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: {{ $ing.path | default "/" | quote }}
|
||||||
|
pathType: {{ $ing.pathType | default "Prefix" }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ $svcName }}
|
||||||
|
port:
|
||||||
|
number: {{ $ing.port }}
|
||||||
|
---
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{{- if .Values.namespaceCreate }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{{- if .Values.postgres.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.fullname" . }}-postgres-init
|
||||||
|
namespace: {{ include "lab.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
data:
|
||||||
|
01-simulators.sql: |
|
||||||
|
{{- range .Values.postgres.initDatabases }}
|
||||||
|
CREATE USER {{ .user }} WITH PASSWORD '{{ .password }}';
|
||||||
|
CREATE DATABASE {{ .database }} OWNER {{ .user }};
|
||||||
|
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
{{- if .Values.postgres.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.postgresHost" . }}
|
||||||
|
namespace: {{ include "lab.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
port: 5432
|
||||||
|
targetPort: postgres
|
||||||
|
protocol: TCP
|
||||||
|
selector:
|
||||||
|
{{- include "lab.selectorLabels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.postgresHost" . }}
|
||||||
|
namespace: {{ include "lab.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
spec:
|
||||||
|
serviceName: {{ include "lab.postgresHost" . }}
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "lab.selectorLabels" . | nindent 6 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "lab.selectorLabels" . | nindent 8 }}
|
||||||
|
app.kubernetes.io/component: postgres
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: "{{ .Values.postgres.image.repository }}:{{ .Values.postgres.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.postgres.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
containerPort: 5432
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
value: {{ .Values.postgres.auth.username | quote }}
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
value: {{ .Values.postgres.auth.database | quote }}
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "lab.fullname" . }}
|
||||||
|
key: postgres-password
|
||||||
|
- name: PGDATA
|
||||||
|
value: /var/lib/postgresql/data/pgdata
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /var/lib/postgresql/data
|
||||||
|
- name: init
|
||||||
|
mountPath: /docker-entrypoint-initdb.d
|
||||||
|
readOnly: true
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", {{ .Values.postgres.auth.username | quote }}, "-d", {{ .Values.postgres.auth.database | quote }}]
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 12
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command: ["pg_isready", "-U", {{ .Values.postgres.auth.username | quote }}, "-d", {{ .Values.postgres.auth.database | quote }}]
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
{{- with .Values.postgres.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
- name: init
|
||||||
|
configMap:
|
||||||
|
name: {{ include "lab.fullname" . }}-postgres-init
|
||||||
|
{{- if not .Values.postgres.persistence.enabled }}
|
||||||
|
- name: data
|
||||||
|
emptyDir: {}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.postgres.persistence.enabled }}
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
{{- if .Values.postgres.persistence.storageClass }}
|
||||||
|
storageClassName: {{ .Values.postgres.persistence.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.postgres.persistence.size }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "lab.fullname" . }}
|
||||||
|
namespace: {{ include "lab.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
ticketSigningKey: {{ .Values.ticketSigningKey | quote }}
|
||||||
|
postgres-password: {{ .Values.postgres.auth.password | quote }}
|
||||||
|
{{- range $name, $sim := .Values.simulators }}
|
||||||
|
{{- if and $sim.enabled $sim.db }}
|
||||||
|
{{ $name }}-db-password: {{ $sim.db.password | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{{- if .Values.seedJobs.enabled }}
|
||||||
|
{{- range $name, $sim := .Values.simulators }}
|
||||||
|
{{- if and $sim.enabled $sim.seed $sim.seed.enabled }}
|
||||||
|
{{- $fullname := printf "%s-%s" (include "lab.fullname" $) $name }}
|
||||||
|
{{- $tag := $sim.image.tag | default $.Values.imageTag }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullname }}-seed
|
||||||
|
namespace: {{ include "lab.namespace" $ }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" $ | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}-seed
|
||||||
|
annotations:
|
||||||
|
helm.sh/hook: post-install,post-upgrade
|
||||||
|
helm.sh/hook-weight: "10"
|
||||||
|
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||||
|
spec:
|
||||||
|
backoffLimit: {{ $.Values.seedJobs.backoffLimit }}
|
||||||
|
ttlSecondsAfterFinished: {{ $.Values.seedJobs.ttlSecondsAfterFinished }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "lab.selectorLabels" $ | nindent 8 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}-seed
|
||||||
|
spec:
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
{{- with $.Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
initContainers:
|
||||||
|
- name: wait-ready
|
||||||
|
image: curlimages/curl:8.12.1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ec
|
||||||
|
- |
|
||||||
|
url="http://{{ $fullname }}:{{ $sim.port }}{{ $sim.healthPath }}"
|
||||||
|
echo "waiting for $url"
|
||||||
|
until curl -fsS "$url" >/dev/null; do sleep 3; done
|
||||||
|
containers:
|
||||||
|
- name: seed
|
||||||
|
image: "{{ $sim.image.repository }}:{{ $tag }}"
|
||||||
|
imagePullPolicy: {{ $sim.image.pullPolicy }}
|
||||||
|
# Skip when skipIfSql returns a row (DB already seeded / has lab data).
|
||||||
|
command:
|
||||||
|
- python
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
import asyncio, os, subprocess, sys
|
||||||
|
import asyncpg
|
||||||
|
|
||||||
|
skip_sql = os.environ.get("SEED_SKIP_IF_SQL", "").strip()
|
||||||
|
seed_cmd = {{ $sim.seed.command | toJson }}
|
||||||
|
|
||||||
|
async def already_seeded() -> bool:
|
||||||
|
if not skip_sql:
|
||||||
|
return False
|
||||||
|
conn = await asyncpg.connect(os.environ["DATABASE_URL"])
|
||||||
|
try:
|
||||||
|
return await conn.fetchval(skip_sql) is not None
|
||||||
|
except Exception as exc: # noqa: BLE001 — empty/unmigrated DB
|
||||||
|
print(f"seed: skip-check deferred ({exc})")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
|
if asyncio.run(already_seeded()):
|
||||||
|
print("seed: database already has data — skipping")
|
||||||
|
raise SystemExit(0)
|
||||||
|
print("seed: applying", seed_cmd)
|
||||||
|
raise SystemExit(subprocess.call(seed_cmd))
|
||||||
|
env:
|
||||||
|
{{- include "lab.commonEnv" $ | nindent 12 }}
|
||||||
|
- name: DATABASE_URL
|
||||||
|
value: {{ include "lab.databaseUrl" (dict "root" $ "sim" $sim) | quote }}
|
||||||
|
- name: SEED_SKIP_IF_SQL
|
||||||
|
value: {{ ($sim.seed.skipIfSql | default "") | quote }}
|
||||||
|
{{- range $k, $v := $sim.env }}
|
||||||
|
- name: {{ $k }}
|
||||||
|
value: {{ $v | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- range $k, $v := ($sim.seed.env | default dict) }}
|
||||||
|
- name: {{ $k }}
|
||||||
|
value: {{ $v | quote }}
|
||||||
|
{{- end }}
|
||||||
|
---
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
{{- range $name, $sim := .Values.simulators }}
|
||||||
|
{{- if $sim.enabled }}
|
||||||
|
{{- $fullname := printf "%s-%s" (include "lab.fullname" $) $name }}
|
||||||
|
{{- $tag := $sim.image.tag | default $.Values.imageTag }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullname }}
|
||||||
|
namespace: {{ include "lab.namespace" $ }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" $ | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: {{ $sim.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
selector:
|
||||||
|
{{- include "lab.selectorLabels" $ | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullname }}
|
||||||
|
namespace: {{ include "lab.namespace" $ }}
|
||||||
|
labels:
|
||||||
|
{{- include "lab.labels" $ | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ $sim.replicas | default 1 }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "lab.selectorLabels" $ | nindent 6 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
{{- include "lab.selectorLabels" $ | nindent 8 }}
|
||||||
|
app.kubernetes.io/component: {{ $name }}
|
||||||
|
spec:
|
||||||
|
{{- with $.Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
initContainers:
|
||||||
|
- name: wait-postgres
|
||||||
|
image: "{{ $.Values.postgres.image.repository }}:{{ $.Values.postgres.image.tag }}"
|
||||||
|
imagePullPolicy: {{ $.Values.postgres.image.pullPolicy }}
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ec
|
||||||
|
- |
|
||||||
|
until pg_isready -h {{ include "lab.postgresHost" $ }} -p 5432 -U {{ $.Values.postgres.auth.username }}; do
|
||||||
|
echo "waiting for postgres..."; sleep 2;
|
||||||
|
done
|
||||||
|
{{- if and $sim.migrate $sim.migrate.enabled }}
|
||||||
|
- name: migrate
|
||||||
|
image: "{{ $sim.image.repository }}:{{ $tag }}"
|
||||||
|
imagePullPolicy: {{ $sim.image.pullPolicy }}
|
||||||
|
# Idempotent: migrate_cli applies 0 migrations when schema is already current.
|
||||||
|
command: {{- toYaml $sim.migrate.command | nindent 12 }}
|
||||||
|
env:
|
||||||
|
{{- include "lab.commonEnv" $ | nindent 12 }}
|
||||||
|
- name: DATABASE_URL
|
||||||
|
value: {{ include "lab.databaseUrl" (dict "root" $ "sim" $sim) | quote }}
|
||||||
|
{{- range $k, $v := $sim.env }}
|
||||||
|
- name: {{ $k }}
|
||||||
|
value: {{ $v | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
- name: simulator
|
||||||
|
image: "{{ $sim.image.repository }}:{{ $tag }}"
|
||||||
|
imagePullPolicy: {{ $sim.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: {{ $sim.port }}
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
{{- include "lab.commonEnv" $ | nindent 12 }}
|
||||||
|
- name: DATABASE_URL
|
||||||
|
value: {{ include "lab.databaseUrl" (dict "root" $ "sim" $sim) | quote }}
|
||||||
|
{{- range $k, $v := $sim.env }}
|
||||||
|
- name: {{ $k }}
|
||||||
|
value: {{ $v | quote }}
|
||||||
|
{{- end }}
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: {{ $sim.healthPath }}
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: {{ $sim.healthPath }}
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 3
|
||||||
|
{{- with $sim.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
---
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
# Laboratory defaults — rotate secrets before any non-lab use.
|
||||||
|
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: "simulators"
|
||||||
|
|
||||||
|
# Target Kubernetes namespace (also used by make helm up/down).
|
||||||
|
namespace: simulators
|
||||||
|
# Create the Namespace resource from this chart.
|
||||||
|
namespaceCreate: true
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
ticketSigningKey: development-only-signing-key-change-me
|
||||||
|
logLevel: INFO
|
||||||
|
imageTag: latest
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: postgres
|
||||||
|
tag: 17.5-bookworm
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
auth:
|
||||||
|
username: lab
|
||||||
|
password: lab
|
||||||
|
database: postgres
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 5Gi
|
||||||
|
storageClass: ""
|
||||||
|
resources: {}
|
||||||
|
# users/databases created on first start (empty volume only)
|
||||||
|
initDatabases:
|
||||||
|
- user: proxmox
|
||||||
|
password: proxmox
|
||||||
|
database: proxmox_simulator
|
||||||
|
- user: ovirt
|
||||||
|
password: ovirt
|
||||||
|
database: ovirt_simulator
|
||||||
|
- user: vmware
|
||||||
|
password: vmware
|
||||||
|
database: vmware_simulator
|
||||||
|
- user: openstack
|
||||||
|
password: openstack
|
||||||
|
database: openstack_simulator
|
||||||
|
|
||||||
|
simulators:
|
||||||
|
proxmox:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: inecs/proxmox-api-simulator
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
port: 8006
|
||||||
|
healthPath: /health/ready
|
||||||
|
replicas: 1
|
||||||
|
db:
|
||||||
|
user: proxmox
|
||||||
|
password: proxmox
|
||||||
|
name: proxmox_simulator
|
||||||
|
env:
|
||||||
|
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"
|
||||||
|
migrate:
|
||||||
|
enabled: true
|
||||||
|
command: ["python", "-m", "app.db.migrate_cli"]
|
||||||
|
seed:
|
||||||
|
enabled: true
|
||||||
|
# Skip seed Job when this query returns a row (already seeded).
|
||||||
|
skipIfSql: "SELECT 1 FROM nodes LIMIT 1"
|
||||||
|
command: ["python", "-m", "app.simulation.seed_cli"]
|
||||||
|
env:
|
||||||
|
SEED_PROFILE: small
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
ovirt:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: inecs/ovirt-api-simulator
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
port: 8080
|
||||||
|
healthPath: /health/ready
|
||||||
|
replicas: 1
|
||||||
|
db:
|
||||||
|
user: ovirt
|
||||||
|
password: ovirt
|
||||||
|
name: ovirt_simulator
|
||||||
|
env:
|
||||||
|
OVIRT_SERIES: "4.5"
|
||||||
|
APP_PORT: "8080"
|
||||||
|
migrate:
|
||||||
|
enabled: true
|
||||||
|
command: ["python", "-m", "app.db.migrate_cli"]
|
||||||
|
seed:
|
||||||
|
enabled: true
|
||||||
|
skipIfSql: "SELECT 1 FROM ov_datacenters LIMIT 1"
|
||||||
|
command: ["python", "-m", "app.ovirt.seed_cli", "--profile", "minimal"]
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
vmware:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: inecs/vmware-api-simulator
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
port: 8080
|
||||||
|
healthPath: /health/ready
|
||||||
|
replicas: 1
|
||||||
|
db:
|
||||||
|
user: vmware
|
||||||
|
password: vmware
|
||||||
|
name: vmware_simulator
|
||||||
|
env:
|
||||||
|
ENABLE_PVE_STUB: "false"
|
||||||
|
SEED_VSPHERE_PROFILE: small
|
||||||
|
APP_PORT: "8080"
|
||||||
|
TASK_WORKER_CONCURRENCY: "2"
|
||||||
|
SIMULATION_TIME_SCALE: "10"
|
||||||
|
migrate:
|
||||||
|
enabled: true
|
||||||
|
command: ["python", "-m", "app.db.migrate_cli"]
|
||||||
|
seed:
|
||||||
|
enabled: true
|
||||||
|
skipIfSql: "SELECT 1 FROM vsphere_objects LIMIT 1"
|
||||||
|
command: ["python", "-m", "app.simulation.seed_cli"]
|
||||||
|
env:
|
||||||
|
SEED_PROFILE: small
|
||||||
|
SEED_VSPHERE_PROFILE: small
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
openstack:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: inecs/openstack-api-simulator
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
port: 8080
|
||||||
|
healthPath: /health/ready
|
||||||
|
replicas: 1
|
||||||
|
db:
|
||||||
|
user: openstack
|
||||||
|
password: openstack
|
||||||
|
name: openstack_simulator
|
||||||
|
env:
|
||||||
|
APP_PORT: "8080"
|
||||||
|
TASK_WORKER_CONCURRENCY: "2"
|
||||||
|
SIMULATION_TIME_SCALE: "10"
|
||||||
|
migrate:
|
||||||
|
enabled: true
|
||||||
|
command: ["python", "-m", "app.db.migrate_cli"]
|
||||||
|
seed:
|
||||||
|
enabled: true
|
||||||
|
skipIfSql: "SELECT 1 FROM os_projects LIMIT 1"
|
||||||
|
command: ["python", "-m", "app.openstack.seed_cli", "--profile", "minimal"]
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
# Ingress hosts — edit here. Each enabled entry creates one Ingress.
|
||||||
|
# Default hosts: <name>.lab.local — for Let's Encrypt use a public DNS name.
|
||||||
|
ingressClassName: nginx
|
||||||
|
|
||||||
|
ingresses:
|
||||||
|
proxmox:
|
||||||
|
enabled: true
|
||||||
|
className: "" # falls back to ingressClassName
|
||||||
|
host: proxmox.lab.local
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
service: proxmox
|
||||||
|
port: 8006
|
||||||
|
tls:
|
||||||
|
- secretName: simulators-proxmox-tls
|
||||||
|
hosts: [proxmox.lab.local]
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
ovirt:
|
||||||
|
enabled: true
|
||||||
|
className: ""
|
||||||
|
host: ovirt.lab.local
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
service: ovirt
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
- secretName: simulators-ovirt-tls
|
||||||
|
hosts: [ovirt.lab.local]
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
vmware:
|
||||||
|
enabled: true
|
||||||
|
className: ""
|
||||||
|
host: vmware.lab.local
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
service: vmware
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
- secretName: simulators-vmware-tls
|
||||||
|
hosts: [vmware.lab.local]
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
proxy_set_header X-VMware-Service "vcenter";
|
||||||
|
proxy_set_header X-Forwarded-Port "443";
|
||||||
|
|
||||||
|
openstack:
|
||||||
|
enabled: true
|
||||||
|
className: ""
|
||||||
|
host: openstack.lab.local
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
service: openstack
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
- secretName: simulators-openstack-tls
|
||||||
|
hosts: [openstack.lab.local]
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
proxy_set_header X-OpenStack-Service "https";
|
||||||
|
proxy_set_header X-Forwarded-Port "443";
|
||||||
|
|
||||||
|
# Optional extras — same shape as above, e.g.:
|
||||||
|
# ovirt-ui:
|
||||||
|
# enabled: true
|
||||||
|
# host: ovirt-ui.lab.local
|
||||||
|
# service: ovirt
|
||||||
|
# port: 8080
|
||||||
|
# tls:
|
||||||
|
# - secretName: simulators-ovirt-ui-tls
|
||||||
|
# hosts: [ovirt-ui.lab.local]
|
||||||
|
# openstack-keystone:
|
||||||
|
# enabled: true
|
||||||
|
# host: keystone.lab.local
|
||||||
|
# service: openstack
|
||||||
|
# port: 8080
|
||||||
|
# tls:
|
||||||
|
# - secretName: simulators-keystone-tls
|
||||||
|
# hosts: [keystone.lab.local]
|
||||||
|
# annotations:
|
||||||
|
# nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
# proxy_set_header X-OpenStack-Service "keystone";
|
||||||
|
|
||||||
|
# TLS via cert-manager + Let's Encrypt (HTTP-01).
|
||||||
|
# Requires cert-manager in the cluster. Hosts must resolve publicly for ACME.
|
||||||
|
certManager:
|
||||||
|
enabled: true
|
||||||
|
# Create ClusterIssuer resources from this chart (false = use existing letsencrypt-prod).
|
||||||
|
createClusterIssuer: false
|
||||||
|
createStagingIssuer: true
|
||||||
|
email: "" # required when createClusterIssuer=true
|
||||||
|
issuerName: letsencrypt-prod
|
||||||
|
server: https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
stagingIssuerName: letsencrypt-staging
|
||||||
|
stagingServer: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
useStaging: false
|
||||||
|
solverIngressClassName: nginx
|
||||||
|
|
||||||
|
seedJobs:
|
||||||
|
# post-install / post-upgrade Jobs; skipIfSql avoids re-seeding when data exists
|
||||||
|
enabled: true
|
||||||
|
backoffLimit: 3
|
||||||
|
ttlSecondsAfterFinished: 600
|
||||||
@@ -0,0 +1,351 @@
|
|||||||
|
# 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"
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# Auto-generated from app.openstack.surface.SERVICES
|
||||||
|
upstream openstack_simulator {
|
||||||
|
server simulator:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
map $server_port $openstack_service {
|
||||||
|
default "simulator";
|
||||||
|
5000 "keystone";
|
||||||
|
8774 "nova";
|
||||||
|
9696 "neutron";
|
||||||
|
9292 "glance";
|
||||||
|
8776 "cinder";
|
||||||
|
8003 "placement";
|
||||||
|
8004 "heat";
|
||||||
|
8000 "heat-cfn";
|
||||||
|
8080 "swift";
|
||||||
|
6385 "ironic";
|
||||||
|
9876 "octavia";
|
||||||
|
9311 "barbican";
|
||||||
|
8786 "manila";
|
||||||
|
9001 "designate";
|
||||||
|
9511 "magnum";
|
||||||
|
9517 "zun";
|
||||||
|
8779 "trove";
|
||||||
|
8989 "mistral";
|
||||||
|
8042 "aodh";
|
||||||
|
8889 "cloudkitty";
|
||||||
|
9090 "freezer";
|
||||||
|
1234 "blazar";
|
||||||
|
8999 "vitrage";
|
||||||
|
15868 "masakari";
|
||||||
|
9890 "tacker";
|
||||||
|
5050 "adjutant";
|
||||||
|
9322 "watcher";
|
||||||
|
8888 "zaqar";
|
||||||
|
80 "horizon";
|
||||||
|
443 "https";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 5000; # keystone (identity)
|
||||||
|
listen 8774; # nova (compute)
|
||||||
|
listen 9696; # neutron (network)
|
||||||
|
listen 9292; # glance (image)
|
||||||
|
listen 8776; # cinder (volumev3)
|
||||||
|
listen 8003; # placement (placement)
|
||||||
|
listen 8004; # heat (orchestration)
|
||||||
|
listen 8000; # heat-cfn (cloudformation)
|
||||||
|
listen 8080; # swift (object-store)
|
||||||
|
listen 6385; # ironic (baremetal)
|
||||||
|
listen 9876; # octavia (load-balancer)
|
||||||
|
listen 9311; # barbican (key-manager)
|
||||||
|
listen 8786; # manila (sharev2)
|
||||||
|
listen 9001; # designate (dns)
|
||||||
|
listen 9511; # magnum (container-infra)
|
||||||
|
listen 9517; # zun (container)
|
||||||
|
listen 8779; # trove (database)
|
||||||
|
listen 8989; # mistral (workflowv2)
|
||||||
|
listen 8042; # aodh (alarming)
|
||||||
|
listen 8889; # cloudkitty (rating)
|
||||||
|
listen 9090; # freezer (backup)
|
||||||
|
listen 1234; # blazar (reservation)
|
||||||
|
listen 8999; # vitrage (rca)
|
||||||
|
listen 15868; # masakari (instance-ha)
|
||||||
|
listen 9890; # tacker (nfv-orchestration)
|
||||||
|
listen 5050; # adjutant (admin-logic)
|
||||||
|
listen 9322; # watcher (infra-optim)
|
||||||
|
listen 8888; # zaqar (messaging)
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://openstack_simulator;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_set_header X-OpenStack-Service $openstack_service;
|
||||||
|
# Console on :5000 may target another service via relative /v2.1|/v2.0|… paths.
|
||||||
|
proxy_set_header X-OpenStack-Route-Service $http_x_openstack_route_service;
|
||||||
|
proxy_set_header X-Request-ID $request_id;
|
||||||
|
proxy_set_header OpenStack-API-Version $http_openstack_api_version;
|
||||||
|
proxy_set_header X-OpenStack-Nova-API-Version $http_x_openstack_nova_api_version;
|
||||||
|
add_header X-OpenStack-Service $openstack_service always;
|
||||||
|
add_header Access-Control-Expose-Headers "X-Subject-Token,x-subject-token" always;
|
||||||
|
add_header X-Forwarded-Port $server_port always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name _;
|
||||||
|
ssl_certificate /etc/nginx/tls/server.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/tls/server.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://openstack_simulator;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
proxy_set_header X-Forwarded-Port 443;
|
||||||
|
proxy_set_header X-OpenStack-Service https;
|
||||||
|
proxy_set_header X-Request-ID $request_id;
|
||||||
|
add_header X-OpenStack-Service https always;
|
||||||
|
add_header X-Forwarded-Port 443 always;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# nginx gateway — only two public listeners:
|
||||||
|
# :443 Engine API + SSO (/ovirt-engine/…)
|
||||||
|
# :5000 Web UI console
|
||||||
|
# Upstream FastAPI listens on simulator:8080.
|
||||||
|
|
||||||
|
upstream ovirt_simulator {
|
||||||
|
server simulator:8080;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Engine HTTPS (API + SSO) ---
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/tls/server.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/tls/server.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
client_max_body_size 64m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
proxy_set_header X-Forwarded-Port 443;
|
||||||
|
proxy_pass http://ovirt_simulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Simulator Web UI console ---
|
||||||
|
server {
|
||||||
|
listen 5000;
|
||||||
|
listen [::]:5000;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Port 5000;
|
||||||
|
proxy_pass http://ovirt_simulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE USER proxmox WITH PASSWORD 'proxmox';
|
||||||
|
CREATE DATABASE proxmox_simulator OWNER proxmox;
|
||||||
|
|
||||||
|
CREATE USER ovirt WITH PASSWORD 'ovirt';
|
||||||
|
CREATE DATABASE ovirt_simulator OWNER ovirt;
|
||||||
|
|
||||||
|
CREATE USER vmware WITH PASSWORD 'vmware';
|
||||||
|
CREATE DATABASE vmware_simulator OWNER vmware;
|
||||||
|
|
||||||
|
CREATE USER openstack WITH PASSWORD 'openstack';
|
||||||
|
CREATE DATABASE openstack_simulator OWNER openstack;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICyTCCAbGgAwIBAgIJAIbJhnhVx8uWMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
|
||||||
|
BAMMCWxvY2FsaG9zdDAeFw0yNjA3MTIyMTQyNTFaFw0zNjA3MDkyMTQyNTFaMBQx
|
||||||
|
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||||
|
ggEBALscj7/1WDybjz8x01EvUFVemov6zkezOwfsOXKVyEOnOTxPjWruzDYnB8y6
|
||||||
|
NH/5PojUns7GB1kuRhZWUXGY0FG/sSgF0X9nwEHoby8ekju2F55NUzzpu9BfM2AU
|
||||||
|
S17S8h5Oxc4Qi6d9RoeRG25YmMywPCyp2SMnuu14w55KTAt7Ir7mbTAv8ZIMbVhq
|
||||||
|
34tH45ONQvGftN4JNvwZr7Uf+EuupWsnILfkz1Cw1cj88adDZHwxE7Hkx7TiQP6o
|
||||||
|
DPDeg+XYH0vB2HR25JSP9z0uyeeF6n6cExgfwVZy2una7jQp887N5xLgTUGlnmFM
|
||||||
|
y1z2AO2+Mw1Lh2UC/OQrp9T1ztMCAwEAAaMeMBwwGgYDVR0RBBMwEYIJbG9jYWxo
|
||||||
|
b3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCICPRCT+m+EKHkaWG2eY2AqQ7a
|
||||||
|
24Bd60ZsZxJNAloXAd1X8cedz5yq0rm9pqF5Fq883dysgCVSDylwqy4YzllhTWsy
|
||||||
|
+M3TE85ZyKKi6S7kR7Z0Exf0I4S7G9zTtrzEXn9kco1q5g/jE7aQi2E2z5poaIg+
|
||||||
|
TlUCq5IePsS6gZCvzXPgU1mJ5dQFlqsOW6Lk1mOCjmKT2SaF4eL2hleatqHv667c
|
||||||
|
fJWYLotjAJoVQKrjItGeHXPosZEW5g17gFD88XZRMlUx5xN5/ioaKmLiyI28aNF3
|
||||||
|
nNaukGC/N5fPsgghb0wkYmPHh+dFE/1uMIq0cOzNcyG6ZQkQzikiYhytWsOu
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7HI+/9Vg8m48/
|
||||||
|
MdNRL1BVXpqL+s5HszsH7DlylchDpzk8T41q7sw2JwfMujR/+T6I1J7OxgdZLkYW
|
||||||
|
VlFxmNBRv7EoBdF/Z8BB6G8vHpI7theeTVM86bvQXzNgFEte0vIeTsXOEIunfUaH
|
||||||
|
kRtuWJjMsDwsqdkjJ7rteMOeSkwLeyK+5m0wL/GSDG1Yat+LR+OTjULxn7TeCTb8
|
||||||
|
Ga+1H/hLrqVrJyC35M9QsNXI/PGnQ2R8MROx5Me04kD+qAzw3oPl2B9Lwdh0duSU
|
||||||
|
j/c9Lsnnhep+nBMYH8FWctrp2u40KfPOzecS4E1BpZ5hTMtc9gDtvjMNS4dlAvzk
|
||||||
|
K6fU9c7TAgMBAAECggEBAINmo2zjF3w4onh2vTgeSgQp087J62Ne8u21bwKRPXqF
|
||||||
|
TSSVmXKnELJW5ptXiNb2anwdFQmQ+EggvwegxsFH18QRIpBAxcb7TYD7gllM1tUo
|
||||||
|
I54AH5x/aG4E7Udj+So2aeHu3+q+o9STnZxGw0TS4zub6CZVgS+3DwcF8BqRgqXs
|
||||||
|
NuDIIJWosuchbb3DdlPygRajiN2teJtNfw9rcLfC4BY5i4y/H7RMpklM5VkTXiGc
|
||||||
|
NxyG4qkdHP0jlL9Z9wRa859uYeb7kVm+vhfgUXMbiRn9FcxrROOpPIwTAv76Y5nu
|
||||||
|
4EF/s0TPC+ei7hjCpN1WK2/n6dgiVYBVBUpWHalZBIECgYEA7+E2T5PlKuT7Kugs
|
||||||
|
qx+CHvZXm2hZ9NVDYS6gNAZt6kr5enbb9rzCF/U+jx14COPyGcoJeDOUW1yZGTgH
|
||||||
|
98JkEEHB6fgSPAU3pp2aMslMRNTZqfM0vL+BRpJT+fPbI9y8WpMzm/NpmnZZK8rh
|
||||||
|
xLbg+xAa7iMltscCcY2uD8NmhKkCgYEAx6+Ma5WQ0Enmju+XUANrOuKDN9aTXXc6
|
||||||
|
iqlqtXfadc/Lc6E+lzSxRm5t95t+6AX2mYsNOWsuWRCBqHMFDxg31moYEeBQvQW9
|
||||||
|
kwJQ5JsmOSCzMfDPUrQHihaq9xwxhoBxJXIRs3JlYm/nty8LO959R1V0IrHsPznH
|
||||||
|
BVs7pbAo6RsCgYEAyvK4t3UCI1tdoPyTpiffOADlN+d+jCTOf+8pvTpfTiUmk1Ty
|
||||||
|
XvtuH0TvK7gb8TGhh+4mOtswvmdGZE7CdvyxGgv4WtH142/qmH2okyU58NZAXYgV
|
||||||
|
a0d+wU1V3RhSpDHB7cOym1PCWdudL+7TOlIbYG5MyoNUCiKvT5E13cJM/xkCgYAC
|
||||||
|
WWNahKjuemAXAGSUUWX6jF2k04ZqTBPJO9MAjYdpaWdoVdZJqxoGzRfIGPE2Q5Oy
|
||||||
|
HLusGEG0VIhh9fByTAOkJx1fYHcyshWX3CgdeGHLvEG/bajSvUF1c2zReWhvv6UV
|
||||||
|
HrFsngTpUo20Tv5f1u88Xpn+Kn+wArr/qiIagecJTwKBgDPEa71fqt7WyjHCNuIm
|
||||||
|
hJeBCIjTZ8N1Jk0GUHyucbFPARWxYcn0zRTwHOXnXt+Z6GvAfuS7YElSXYVjw2Uy
|
||||||
|
wUVD+7zh0ydkWC1HJPnjalmHHVpv1RFNEJGAYQ8Vxd6G2EoY4ZFByZomWTxfXrvq
|
||||||
|
Dr9hsGtmZu1knNwfrOu2kyB5
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Lab gateway that exposes vCenter default HTTPS (443) and proxies to the
|
||||||
|
# single FastAPI simulator process.
|
||||||
|
#
|
||||||
|
# Upstream listens on an internal-only port (simulator:8080). Host clients
|
||||||
|
# should hit https://localhost/ — not the internal app port.
|
||||||
|
#
|
||||||
|
# Use a variable + Docker DNS resolver so nginx starts even if the simulator
|
||||||
|
# hostname is not yet resolvable (avoids crash-loop on recreate).
|
||||||
|
|
||||||
|
map $server_port $vmware_service {
|
||||||
|
default "simulator";
|
||||||
|
80 "http";
|
||||||
|
443 "vcenter";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||||
|
|
||||||
|
set $vmware_upstream simulator:8080;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://$vmware_upstream;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_set_header X-VMware-Service $vmware_service;
|
||||||
|
proxy_set_header X-Request-ID $request_id;
|
||||||
|
add_header X-VMware-Service $vmware_service always;
|
||||||
|
add_header X-Forwarded-Port $server_port always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/tls/server.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/tls/server.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||||
|
set $vmware_upstream simulator:8080;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://$vmware_upstream;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
proxy_set_header X-Forwarded-Port 443;
|
||||||
|
proxy_set_header X-VMware-Service vcenter;
|
||||||
|
proxy_set_header X-Request-ID $request_id;
|
||||||
|
add_header X-VMware-Service vcenter always;
|
||||||
|
add_header X-Forwarded-Port 443 always;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user