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