Initial commit: VMware vSphere API simulator scaffold.
Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API contracts, docs, client examples, and the unit/integration/compatibility test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
*.tgz
|
||||
charts/*.tgz
|
||||
values-ingress-example.yaml
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v2
|
||||
name: vmware-api-simulator
|
||||
description: Stateful VMware API simulator (PostgreSQL-backed) for labs and CI
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
home: https://github.com/inecs/vmware-api-simulator
|
||||
keywords:
|
||||
- vmware
|
||||
- cloud
|
||||
- api
|
||||
- simulator
|
||||
maintainers:
|
||||
- name: inecs
|
||||
# Bundled PostgreSQL uses the official postgres image (see templates/postgresql-*.yaml).
|
||||
# No external chart dependency is required — run helm install directly.
|
||||
@@ -0,0 +1,42 @@
|
||||
**Language / Язык:** [English](README.md) | [Русский](README.ru.md)
|
||||
|
||||
# Helm chart: vmware-api-simulator
|
||||
|
||||
Deploys the published runtime image
|
||||
[`inecs/vmware-api-simulator`](https://hub.docker.com/r/inecs/vmware-api-simulator)
|
||||
with optional official PostgreSQL, migrations, seed Job, Ingress, and
|
||||
cert-manager Let's Encrypt `ClusterIssuer` resources.
|
||||
|
||||
Full guide: [docs/kubernetes.md](../../docs/kubernetes.md).
|
||||
|
||||
## Quick install
|
||||
|
||||
Prerequisites: Kubernetes, Helm 3, ingress-nginx (or compatible), cert-manager.
|
||||
|
||||
```bash
|
||||
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
|
||||
-n vmware-sim --create-namespace \
|
||||
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
|
||||
--set certManager.email=you@example.com \
|
||||
--set ingress.hosts[0].host=vmware-sim.example.com \
|
||||
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
|
||||
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
|
||||
--set postgresql.auth.password="$(openssl rand -hex 16)"
|
||||
```
|
||||
|
||||
Point DNS for your host at the Ingress controller, wait for the Certificate to
|
||||
become Ready, then open `https://vmware-sim.example.com/`.
|
||||
|
||||
## Values overview
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `image.repository` | `inecs/vmware-api-simulator` | Hub image |
|
||||
| `image.tag` | chart `appVersion` | Image tag |
|
||||
| `postgresql.enabled` | `true` | Bundle official PostgreSQL StatefulSet |
|
||||
| `secret.databaseUrl` / `externalDatabase.*` | | External DB when postgres disabled |
|
||||
| `ingress.enabled` | `false` | Expose via Ingress |
|
||||
| `certManager.enabled` | `false` | Annotate Ingress + optional ClusterIssuers |
|
||||
| `seed.enabled` | `false` | Post-install seed Job |
|
||||
|
||||
See [`values.yaml`](values.yaml) and [`values-ingress-example.yaml`](values-ingress-example.yaml).
|
||||
@@ -0,0 +1,42 @@
|
||||
**Language / Язык:** [English](README.md) | [Русский](README.ru.md)
|
||||
|
||||
# Helm-чарт: vmware-api-simulator
|
||||
|
||||
Развёртывает опубликованный runtime-образ
|
||||
[`inecs/vmware-api-simulator`](https://hub.docker.com/r/inecs/vmware-api-simulator)
|
||||
с опциональным официальным PostgreSQL, миграциями, seed Job, Ingress и
|
||||
ресурсами cert-manager Let's Encrypt `ClusterIssuer`.
|
||||
|
||||
Полный гайд: [docs/ru/kubernetes.md](../../docs/ru/kubernetes.md).
|
||||
|
||||
## Быстрая установка
|
||||
|
||||
Требования: Kubernetes, Helm 3, ingress-nginx (или совместимый), cert-manager.
|
||||
|
||||
```bash
|
||||
helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
|
||||
-n vmware-sim --create-namespace \
|
||||
-f ./helm/vmware-api-simulator/values-ingress-example.yaml \
|
||||
--set certManager.email=you@example.com \
|
||||
--set ingress.hosts[0].host=vmware-sim.example.com \
|
||||
--set ingress.tls[0].hosts[0]=vmware-sim.example.com \
|
||||
--set secret.ticketSigningKey="$(openssl rand -hex 32)" \
|
||||
--set postgresql.auth.password="$(openssl rand -hex 16)"
|
||||
```
|
||||
|
||||
Направьте DNS хоста на Ingress controller, дождитесь Ready у Certificate,
|
||||
затем откройте `https://vmware-sim.example.com/`.
|
||||
|
||||
## Обзор values
|
||||
|
||||
| Ключ | По умолчанию | Назначение |
|
||||
|---|---|---|
|
||||
| `image.repository` | `inecs/vmware-api-simulator` | Образ Hub |
|
||||
| `image.tag` | chart `appVersion` | Тег образа |
|
||||
| `postgresql.enabled` | `true` | Включить официальный PostgreSQL StatefulSet |
|
||||
| `secret.databaseUrl` / `externalDatabase.*` | | Внешняя БД, когда postgres выключен |
|
||||
| `ingress.enabled` | `false` | Открыть через Ingress |
|
||||
| `certManager.enabled` | `false` | Аннотировать Ingress + опциональные ClusterIssuers |
|
||||
| `seed.enabled` | `false` | Post-install seed Job |
|
||||
|
||||
См. [`values.yaml`](values.yaml) и [`values-ingress-example.yaml`](values-ingress-example.yaml).
|
||||
@@ -0,0 +1,35 @@
|
||||
vmware-api-simulator {{ .Chart.AppVersion }} installed as release "{{ .Release.Name }}".
|
||||
|
||||
Image: {{ include "vmware-api-simulator.image" . }}
|
||||
|
||||
1. Check readiness:
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}"
|
||||
|
||||
2. Access the API / Web UI:
|
||||
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
https://{{ .host }}/
|
||||
{{- end }}
|
||||
{{- if .Values.certManager.enabled }}
|
||||
TLS certificate is requested via cert-manager ClusterIssuer
|
||||
"{{ include "vmware-api-simulator.clusterIssuer" . }}".
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "vmware-api-simulator.fullname" . }} 8080:{{ .Values.service.port }}
|
||||
Then open http://127.0.0.1:8080/
|
||||
{{- end }}
|
||||
|
||||
3. Seed laboratory data (if seed.enabled was false):
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} exec deploy/{{ include "vmware-api-simulator.fullname" . }} -- \
|
||||
python -m app.simulation.seed_cli
|
||||
|
||||
Or upgrade with --set seed.enabled=true,seed.profile=small
|
||||
|
||||
Default lab login after seeding:
|
||||
administrator@vsphere.local / VMware1!
|
||||
|
||||
Change secret.ticketSigningKey (and postgresql.auth.password) before exposing
|
||||
the cluster publicly.
|
||||
@@ -0,0 +1,121 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.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 }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.labels" -}}
|
||||
helm.sh/chart: {{ include "vmware-api-simulator.chart" . }}
|
||||
{{ include "vmware-api-simulator.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "vmware-api-simulator.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Service account name
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "vmware-api-simulator.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Image reference
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.image" -}}
|
||||
{{- $tag := .Values.image.tag | default .Chart.AppVersion }}
|
||||
{{- printf "%s:%s" .Values.image.repository $tag }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Secret name holding DATABASE_URL and TICKET_SIGNING_KEY
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.secretName" -}}
|
||||
{{- if .Values.secret.existingSecret }}
|
||||
{{- .Values.secret.existingSecret }}
|
||||
{{- else }}
|
||||
{{- include "vmware-api-simulator.fullname" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
PostgreSQL hostname when bundled subchart is enabled
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.postgresqlHost" -}}
|
||||
{{- printf "%s-postgresql" .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Build DATABASE_URL when not supplied explicitly (bundled or external discrete fields).
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.databaseUrl" -}}
|
||||
{{- if .Values.secret.databaseUrl }}
|
||||
{{- .Values.secret.databaseUrl }}
|
||||
{{- else if .Values.postgresql.enabled }}
|
||||
{{- $user := .Values.postgresql.auth.username }}
|
||||
{{- $pass := .Values.postgresql.auth.password }}
|
||||
{{- $db := .Values.postgresql.auth.database }}
|
||||
{{- $host := include "vmware-api-simulator.postgresqlHost" . }}
|
||||
{{- printf "postgresql://%s:%s@%s:5432/%s" $user $pass $host $db }}
|
||||
{{- else if .Values.externalDatabase.host }}
|
||||
{{- $user := .Values.externalDatabase.user }}
|
||||
{{- $pass := .Values.externalDatabase.password }}
|
||||
{{- $db := .Values.externalDatabase.database }}
|
||||
{{- $host := .Values.externalDatabase.host }}
|
||||
{{- $port := int .Values.externalDatabase.port }}
|
||||
{{- printf "postgresql://%s:%s@%s:%d/%s" $user $pass $host $port $db }}
|
||||
{{- else }}
|
||||
{{- fail "Set postgresql.enabled=true, or secret.databaseUrl / secret.existingSecret, or externalDatabase.host" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
cert-manager ClusterIssuer name used by Ingress
|
||||
*/}}
|
||||
{{- define "vmware-api-simulator.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 .Values.ingress.className }}
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: {{ .Values.certManager.issuerName }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.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 "vmware-api-simulator.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,152 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.fullname" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "vmware-api-simulator.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 8 }}
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "vmware-api-simulator.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- if and .Values.migrate.enabled (not .Values.migrate.asJob) }}
|
||||
initContainers:
|
||||
- name: migrate
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: {{ include "vmware-api-simulator.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "vmware-api-simulator.secretName" . }}
|
||||
key: DATABASE_URL
|
||||
- name: LOG_LEVEL
|
||||
value: {{ .Values.config.logLevel | quote }}
|
||||
command:
|
||||
- python
|
||||
- -c
|
||||
- |
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import asyncpg
|
||||
|
||||
dsn = os.environ["DATABASE_URL"]
|
||||
deadline = time.time() + 300
|
||||
while True:
|
||||
try:
|
||||
async def ping() -> None:
|
||||
conn = await asyncpg.connect(dsn=dsn, timeout=5)
|
||||
await conn.close()
|
||||
|
||||
asyncio.run(ping())
|
||||
break
|
||||
except Exception as exc: # noqa: BLE001 - wait until Postgres accepts connections
|
||||
if time.time() >= deadline:
|
||||
print(f"database not ready: {exc}", file=sys.stderr)
|
||||
raise
|
||||
print(f"waiting for database: {exc}")
|
||||
time.sleep(3)
|
||||
|
||||
from app.db.migrate_cli import run
|
||||
|
||||
asyncio.run(run())
|
||||
resources:
|
||||
{{- toYaml .Values.migrate.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: simulator
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: {{ include "vmware-api-simulator.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: APP_HOST
|
||||
value: "0.0.0.0"
|
||||
- name: APP_PORT
|
||||
value: {{ .Values.service.port | quote }}
|
||||
- name: LOG_LEVEL
|
||||
value: {{ .Values.config.logLevel | quote }}
|
||||
- name: ENABLE_PVE_STUB
|
||||
value: {{ .Values.config.enablePveStub | quote }}
|
||||
{{- if .Values.config.contractSnapshot }}
|
||||
- name: CONTRACT_SNAPSHOT
|
||||
value: {{ .Values.config.contractSnapshot | quote }}
|
||||
{{- end }}
|
||||
- name: COMPATIBILITY_EVIDENCE
|
||||
value: {{ .Values.config.compatibilityEvidence | quote }}
|
||||
- name: CONTRACT_FALLBACK
|
||||
value: {{ .Values.config.contractFallback | quote }}
|
||||
- name: TASK_WORKER_CONCURRENCY
|
||||
value: {{ .Values.config.taskWorkerConcurrency | quote }}
|
||||
- name: TASK_LEASE_SECONDS
|
||||
value: {{ .Values.config.taskLeaseSeconds | quote }}
|
||||
- name: SIMULATION_TIME_SCALE
|
||||
value: {{ .Values.config.simulationTimeScale | quote }}
|
||||
- name: REQUEST_ID_HEADER
|
||||
value: {{ .Values.config.requestIdHeader | quote }}
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "vmware-api-simulator.secretName" . }}
|
||||
key: DATABASE_URL
|
||||
- name: TICKET_SIGNING_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "vmware-api-simulator.secretName" . }}
|
||||
key: TICKET_SIGNING_KEY
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,46 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "vmware-api-simulator.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- if .Values.certManager.enabled }}
|
||||
cert-manager.io/cluster-issuer: {{ include "vmware-api-simulator.clusterIssuer" . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.ingress.className }}
|
||||
ingressClassName: {{ . }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ .pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $svcPort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,50 @@
|
||||
{{- /* Kept for optional standalone migrate Job when migrate.asJob=true */ -}}
|
||||
{{- if and .Values.migrate.enabled .Values.migrate.asJob }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.fullname" . }}-migrate
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: migrate
|
||||
spec:
|
||||
backoffLimit: {{ .Values.migrate.backoffLimit }}
|
||||
activeDeadlineSeconds: {{ .Values.migrate.activeDeadlineSeconds }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: migrate
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "vmware-api-simulator.serviceAccountName" . }}
|
||||
restartPolicy: OnFailure
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: migrate
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: {{ include "vmware-api-simulator.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "vmware-api-simulator.secretName" . }}
|
||||
key: DATABASE_URL
|
||||
- name: LOG_LEVEL
|
||||
value: {{ .Values.config.logLevel | quote }}
|
||||
command: ["python", "-m", "app.db.migrate_cli"]
|
||||
resources:
|
||||
{{- toYaml .Values.migrate.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,19 @@
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.postgresqlHost" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: postgresql
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: postgresql
|
||||
protocol: TCP
|
||||
name: postgresql
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "vmware-api-simulator.name" . }}-postgresql
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,71 @@
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.postgresqlHost" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: postgresql
|
||||
spec:
|
||||
serviceName: {{ include "vmware-api-simulator.postgresqlHost" . }}
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "vmware-api-simulator.name" . }}-postgresql
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "vmware-api-simulator.name" . }}-postgresql
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: postgresql
|
||||
spec:
|
||||
containers:
|
||||
- name: postgresql
|
||||
image: {{ printf "%s:%s" .Values.postgresql.image.repository .Values.postgresql.image.tag | quote }}
|
||||
imagePullPolicy: {{ .Values.postgresql.image.pullPolicy }}
|
||||
ports:
|
||||
- name: postgresql
|
||||
containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: {{ .Values.postgresql.auth.database | quote }}
|
||||
- name: POSTGRES_USER
|
||||
value: {{ .Values.postgresql.auth.username | quote }}
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: {{ .Values.postgresql.auth.password | quote }}
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", {{ .Values.postgresql.auth.username | quote }}]
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", {{ .Values.postgresql.auth.username | quote }}]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.postgresql.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
{{- if .Values.postgresql.persistence.enabled }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
{{- with .Values.postgresql.persistence.storageClass }}
|
||||
storageClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.postgresql.persistence.size }}
|
||||
{{- else }}
|
||||
volumes:
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,12 @@
|
||||
{{- if and .Values.secret.create (not .Values.secret.existingSecret) }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.fullname" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
DATABASE_URL: {{ include "vmware-api-simulator.databaseUrl" . | quote }}
|
||||
TICKET_SIGNING_KEY: {{ required "secret.ticketSigningKey is required when secret.create=true" .Values.secret.ticketSigningKey | quote }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,56 @@
|
||||
{{- if .Values.seed.enabled }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.fullname" . }}-seed
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: seed
|
||||
annotations:
|
||||
"helm.sh/hook": post-install,post-upgrade
|
||||
"helm.sh/hook-weight": "5"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||||
spec:
|
||||
backoffLimit: {{ .Values.seed.backoffLimit }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: seed
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "vmware-api-simulator.serviceAccountName" . }}
|
||||
restartPolicy: OnFailure
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: seed
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: {{ include "vmware-api-simulator.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "vmware-api-simulator.secretName" . }}
|
||||
key: DATABASE_URL
|
||||
- name: SEED_VSPHERE_PROFILE
|
||||
value: {{ .Values.seed.profile | quote }}
|
||||
- name: ENABLE_PVE_STUB
|
||||
value: {{ .Values.config.enablePveStub | quote }}
|
||||
- name: LOG_LEVEL
|
||||
value: {{ .Values.config.logLevel | quote }}
|
||||
command: ["python", "-m", "app.simulation.seed_cli"]
|
||||
resources:
|
||||
{{- toYaml .Values.seed.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.fullname" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "vmware-api-simulator.selectorLabels" . | nindent 4 }}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "vmware-api-simulator.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "vmware-api-simulator.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: false
|
||||
{{- end }}
|
||||
@@ -0,0 +1,54 @@
|
||||
# Example: public Ingress + Let's Encrypt (cert-manager) + Hub image.
|
||||
#
|
||||
# helm upgrade --install vmware-sim ./helm/vmware-api-simulator \
|
||||
# -n vmware-sim --create-namespace \
|
||||
# -f ./helm/vmware-api-simulator/values-ingress-example.yaml \
|
||||
# --set certManager.email=you@example.com \
|
||||
# --set ingress.hosts[0].host=vmware-sim.example.com \
|
||||
# --set ingress.tls[0].hosts[0]=vmware-sim.example.com \
|
||||
# --set secret.ticketSigningKey="$(openssl rand -hex 32)" \
|
||||
# --set postgresql.auth.password="$(openssl rand -hex 16)"
|
||||
|
||||
image:
|
||||
repository: inecs/vmware-api-simulator
|
||||
tag: "0.1.0"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
secret:
|
||||
create: true
|
||||
ticketSigningKey: "replace-me"
|
||||
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
username: vmware
|
||||
password: "replace-me-db-password"
|
||||
database: vmware_simulator
|
||||
|
||||
seed:
|
||||
enabled: true
|
||||
profile: small
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: vmware-sim.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: vmware-api-simulator-tls
|
||||
hosts:
|
||||
- vmware-sim.example.com
|
||||
|
||||
certManager:
|
||||
enabled: true
|
||||
createClusterIssuer: true
|
||||
createStagingIssuer: true
|
||||
email: you@example.com
|
||||
issuerName: letsencrypt-prod
|
||||
stagingIssuerName: letsencrypt-staging
|
||||
# Set true first to validate HTTP-01 against Let's Encrypt staging.
|
||||
useStaging: false
|
||||
solverIngressClassName: nginx
|
||||
@@ -0,0 +1,180 @@
|
||||
## Default values for vmware-api-simulator.
|
||||
## Image: https://hub.docker.com/r/inecs/vmware-api-simulator
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: inecs/vmware-api-simulator
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
annotations: {}
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext:
|
||||
fsGroup: 10001
|
||||
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
runAsGroup: 10001
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
# Internal app port. Publish VMware service ports via Ingress/Service separately.
|
||||
port: 8080
|
||||
|
||||
## Application environment (non-secret).
|
||||
config:
|
||||
logLevel: INFO
|
||||
enablePveStub: "false"
|
||||
# Optional cold-start contract path (vSphere majors use evidence + UI hot-swap).
|
||||
contractSnapshot: ""
|
||||
contractFallback: error
|
||||
taskWorkerConcurrency: 2
|
||||
taskLeaseSeconds: "30"
|
||||
simulationTimeScale: "10"
|
||||
requestIdHeader: X-Request-ID
|
||||
compatibilityEvidence: /app/evidence/vsphere-8.0.2.json
|
||||
|
||||
## Secrets. Prefer existingSecret in production.
|
||||
secret:
|
||||
# Create a Secret from the values below when existingSecret is empty.
|
||||
create: true
|
||||
existingSecret: ""
|
||||
# Keys expected in an existing secret (when existingSecret is set):
|
||||
# DATABASE_URL, TICKET_SIGNING_KEY
|
||||
ticketSigningKey: "change-me-to-a-long-random-secret"
|
||||
# Used only when postgresql.enabled=true and databaseUrl is empty.
|
||||
# The chart builds postgresql://USER:PASSWORD@HOST:5432/DB
|
||||
databaseUrl: ""
|
||||
|
||||
## Bundled PostgreSQL (official image — same major as docker-compose.release.yml).
|
||||
postgresql:
|
||||
enabled: true
|
||||
image:
|
||||
repository: postgres
|
||||
tag: "17.5-bookworm"
|
||||
pullPolicy: IfNotPresent
|
||||
auth:
|
||||
username: vmware
|
||||
password: vmware
|
||||
database: vmware_simulator
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi
|
||||
storageClass: ""
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
|
||||
## External database when postgresql.enabled=false.
|
||||
## Provide either full URL (secret.databaseUrl / existingSecret) or discrete fields.
|
||||
externalDatabase:
|
||||
host: ""
|
||||
port: 5432
|
||||
user: vmware
|
||||
password: ""
|
||||
database: vmware_simulator
|
||||
# Optional existing secret with key database-password (or set password above).
|
||||
existingSecret: ""
|
||||
existingSecretPasswordKey: database-password
|
||||
|
||||
## Database migrations.
|
||||
## Default: idempotent initContainer on the Deployment (recommended).
|
||||
## Set asJob=true to run a standalone Job instead.
|
||||
migrate:
|
||||
enabled: true
|
||||
asJob: false
|
||||
backoffLimit: 20
|
||||
activeDeadlineSeconds: 600
|
||||
resources: {}
|
||||
|
||||
## Optional post-install seed Job (lab data).
|
||||
seed:
|
||||
enabled: false
|
||||
profile: small
|
||||
backoffLimit: 3
|
||||
resources: {}
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 1Gi
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health/live
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/ready
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 8
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
## Ingress + TLS via cert-manager (Let's Encrypt).
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations: {}
|
||||
# Extra annotations merged after cert-manager ones when certManager.enabled.
|
||||
hosts:
|
||||
- host: vmware-sim.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: vmware-api-simulator-tls
|
||||
hosts:
|
||||
- vmware-sim.example.com
|
||||
|
||||
## cert-manager ClusterIssuers for Let's Encrypt.
|
||||
## Requires cert-manager already installed in the cluster.
|
||||
certManager:
|
||||
enabled: false
|
||||
# Create ClusterIssuer resources from this chart.
|
||||
createClusterIssuer: true
|
||||
email: admin@example.com
|
||||
# Production issuer (used by Ingress by default).
|
||||
issuerName: letsencrypt-prod
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
# Staging issuer (optional; useful for dry-runs).
|
||||
createStagingIssuer: true
|
||||
stagingIssuerName: letsencrypt-staging
|
||||
stagingServer: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
# Which issuer the Ingress annotation should reference.
|
||||
useStaging: false
|
||||
# ACME HTTP-01 solver ingress class (usually same as ingress.className).
|
||||
solverIngressClassName: ""
|
||||
Reference in New Issue
Block a user