Add a stateful Proxmox API console and broad handler coverage beyond the

initial QEMU slice, backed by imported contracts for majors 6–9.
- Implement durable handlers for access/auth, cluster, LXC, storage, HA,
  firewall, Ceph, SDN, ACME, notifications, pools, mapping, and node ops
- Serve an interactive Web UI with catalog browsing, demo seed controls,
  and OpenAPI/help surfaces
- Bundle PVE 6.4-15, 7.4-16, and 8.4.5 contract revisions alongside 9.2.3
- Support in-memory runtime contract Apply (POST /ui/api/contract/apply)
  so /version and /api2 routes follow the selected major until restart
- Expand seed profiles (including demo-cluster), migrations 007–008, TLS
  gateway config, Compose/Makefile tooling, and compatibility evidence
- Tighten .gitignore for macOS, hidden directories (.*/), and local secrets
This commit is contained in:
Sergey Antropoff
2026-07-16 01:08:01 +03:00
parent 003ee5d634
commit 777926487b
189 changed files with 241501 additions and 944 deletions
+7
View File
@@ -0,0 +1,7 @@
.DS_Store
.git
.gitignore
*.md
*.tgz
charts/*.tgz
values-ingress-example.yaml
+16
View File
@@ -0,0 +1,16 @@
apiVersion: v2
name: proxmox-api-simulator
description: Stateful Proxmox VE API simulator (PostgreSQL-backed) for labs and CI
type: application
version: 0.1.0
appVersion: "0.1.0"
home: https://github.com/inecs/proxmox-api-simulator
keywords:
- proxmox
- pve
- 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.
+40
View File
@@ -0,0 +1,40 @@
# Helm chart: proxmox-api-simulator
Deploys the published runtime image
[`inecs/proxmox-api-simulator`](https://hub.docker.com/r/inecs/proxmox-api-simulator)
with optional Bitnami 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 pve-sim ./helm/proxmox-api-simulator \
-n proxmox-sim --create-namespace \
-f ./helm/proxmox-api-simulator/values-ingress-example.yaml \
--set certManager.email=you@example.com \
--set ingress.hosts[0].host=pve-sim.example.com \
--set ingress.tls[0].hosts[0]=pve-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://pve-sim.example.com/`.
## Values overview
| Key | Default | Meaning |
|---|---|---|
| `image.repository` | `inecs/proxmox-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,32 @@
proxmox-api-simulator {{ .Chart.AppVersion }} installed as release "{{ .Release.Name }}".
Image: {{ include "proxmox-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 "proxmox-api-simulator.clusterIssuer" . }}".
{{- end }}
{{- else }}
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "proxmox-api-simulator.fullname" . }} 8006:{{ .Values.service.port }}
Then open http://127.0.0.1:8006/
{{- end }}
3. Seed laboratory data (if seed.enabled was false):
kubectl -n {{ .Release.Namespace }} exec deploy/{{ include "proxmox-api-simulator.fullname" . }} -- \
python -m app.simulation.seed_cli
Or upgrade with --set seed.enabled=true,seed.profile=small
Default lab login after seeding: root@pam / secret
Change secret.ticketSigningKey before exposing the cluster publicly.
@@ -0,0 +1,121 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "proxmox-api-simulator.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "proxmox-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 "proxmox-api-simulator.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "proxmox-api-simulator.labels" -}}
helm.sh/chart: {{ include "proxmox-api-simulator.chart" . }}
{{ include "proxmox-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 "proxmox-api-simulator.selectorLabels" -}}
app.kubernetes.io/name: {{ include "proxmox-api-simulator.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Service account name
*/}}
{{- define "proxmox-api-simulator.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "proxmox-api-simulator.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Image reference
*/}}
{{- define "proxmox-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 "proxmox-api-simulator.secretName" -}}
{{- if .Values.secret.existingSecret }}
{{- .Values.secret.existingSecret }}
{{- else }}
{{- include "proxmox-api-simulator.fullname" . }}
{{- end }}
{{- end }}
{{/*
PostgreSQL hostname when bundled subchart is enabled
*/}}
{{- define "proxmox-api-simulator.postgresqlHost" -}}
{{- printf "%s-postgresql" .Release.Name }}
{{- end }}
{{/*
Build DATABASE_URL when not supplied explicitly (bundled or external discrete fields).
*/}}
{{- define "proxmox-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 "proxmox-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 "proxmox-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 "proxmox-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 "proxmox-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,148 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "proxmox-api-simulator.fullname" . }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "proxmox-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 "proxmox-api-simulator.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "proxmox-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 "proxmox-api-simulator.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "proxmox-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 "proxmox-api-simulator.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8006
protocol: TCP
env:
- name: APP_HOST
value: "0.0.0.0"
- name: APP_PORT
value: "8006"
- name: LOG_LEVEL
value: {{ .Values.config.logLevel | quote }}
- name: CONTRACT_SNAPSHOT
value: {{ .Values.config.contractSnapshot | quote }}
- 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 "proxmox-api-simulator.secretName" . }}
key: DATABASE_URL
- name: TICKET_SIGNING_KEY
valueFrom:
secretKeyRef:
name: {{ include "proxmox-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 "proxmox-api-simulator.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
annotations:
{{- if .Values.certManager.enabled }}
cert-manager.io/cluster-issuer: {{ include "proxmox-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 "proxmox-api-simulator.fullname" . }}-migrate
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
app.kubernetes.io/component: migrate
spec:
backoffLimit: {{ .Values.migrate.backoffLimit }}
activeDeadlineSeconds: {{ .Values.migrate.activeDeadlineSeconds }}
template:
metadata:
labels:
{{- include "proxmox-api-simulator.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: migrate
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "proxmox-api-simulator.serviceAccountName" . }}
restartPolicy: OnFailure
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: migrate
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "proxmox-api-simulator.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "proxmox-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 "proxmox-api-simulator.postgresqlHost" . }}
labels:
{{- include "proxmox-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 "proxmox-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 "proxmox-api-simulator.postgresqlHost" . }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
app.kubernetes.io/component: postgresql
spec:
serviceName: {{ include "proxmox-api-simulator.postgresqlHost" . }}
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "proxmox-api-simulator.name" . }}-postgresql
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "proxmox-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 "proxmox-api-simulator.fullname" . }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
type: Opaque
stringData:
DATABASE_URL: {{ include "proxmox-api-simulator.databaseUrl" . | quote }}
TICKET_SIGNING_KEY: {{ required "secret.ticketSigningKey is required when secret.create=true" .Values.secret.ticketSigningKey | quote }}
{{- end }}
@@ -0,0 +1,54 @@
{{- if .Values.seed.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "proxmox-api-simulator.fullname" . }}-seed
labels:
{{- include "proxmox-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 "proxmox-api-simulator.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: seed
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "proxmox-api-simulator.serviceAccountName" . }}
restartPolicy: OnFailure
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: seed
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "proxmox-api-simulator.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "proxmox-api-simulator.secretName" . }}
key: DATABASE_URL
- name: SEED_PROFILE
value: {{ .Values.seed.profile | 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 "proxmox-api-simulator.fullname" . }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "proxmox-api-simulator.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "proxmox-api-simulator.serviceAccountName" . }}
labels:
{{- include "proxmox-api-simulator.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: false
{{- end }}
@@ -0,0 +1,53 @@
# Example: public Ingress + Let's Encrypt (cert-manager) + Hub image.
#
# helm upgrade --install pve-sim ./helm/proxmox-api-simulator \
# -n proxmox-sim --create-namespace \
# -f helm/proxmox-api-simulator/values-ingress-example.yaml \
# --set certManager.email=you@example.com \
# --set ingress.hosts[0].host=pve-sim.example.com \
# --set ingress.tls[0].hosts[0]=pve-sim.example.com \
# --set secret.ticketSigningKey="$(openssl rand -hex 32)"
image:
repository: inecs/proxmox-api-simulator
tag: "0.1.0"
pullPolicy: IfNotPresent
secret:
create: true
ticketSigningKey: "replace-me"
postgresql:
enabled: true
auth:
username: proxmox
password: "replace-me-db-password"
database: proxmox_simulator
seed:
enabled: true
profile: small
ingress:
enabled: true
className: nginx
hosts:
- host: pve-sim.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: proxmox-api-simulator-tls
hosts:
- pve-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
+177
View File
@@ -0,0 +1,177 @@
## Default values for proxmox-api-simulator.
## Image: https://hub.docker.com/r/inecs/proxmox-api-simulator
replicaCount: 1
image:
repository: inecs/proxmox-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
port: 8006
## Application environment (non-secret).
config:
logLevel: INFO
contractSnapshot: /app/contracts/pve-9.2.3.json
compatibilityEvidence: /app/evidence/pve-9.2.3.json
contractFallback: error
taskWorkerConcurrency: 2
taskLeaseSeconds: "30"
simulationTimeScale: "10"
requestIdHeader: X-Request-ID
## 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: proxmox
password: proxmox
database: proxmox_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: proxmox
password: ""
database: proxmox_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: pve-sim.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: proxmox-api-simulator-tls
hosts:
- pve-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: ""