feat(helm): add cloudhost-app Helm chart for all runtimes

- deployment.yaml with imagePullSecrets, health probes, WordPress volumes
- db-deployment.yaml for PostgreSQL/MySQL with readiness/liveness probes
- ingress.yaml with cert-manager TLS
- registry-pull-secret.yaml for insecure registries
- PVCs and secrets with helm.sh/resource-policy: keep
- _helpers.tpl with shared template functions
- values.yaml with comprehensive defaults
This commit is contained in:
keyhan
2026-04-22 16:43:29 +03:30
parent 2dd882bf34
commit 1a0b04feec
13 changed files with 526 additions and 0 deletions
@@ -0,0 +1,81 @@
{{/*
CloudHost App template helpers
*/}}
{{/*
Expand the name of the chart.
*/}}
{{- define "cloudhost-app.name" -}}
{{- .Values.app.name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Namespace for the release.
*/}}
{{- define "cloudhost-app.namespace" -}}
{{- .Values.app.namespace }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "cloudhost-app.labels" -}}
app: {{ include "cloudhost-app.name" . }}
app.kubernetes.io/managed-by: cloudhost-helm
runtime: {{ .Values.app.runtime }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "cloudhost-app.selectorLabels" -}}
app: {{ include "cloudhost-app.name" . }}
{{- end }}
{{/*
Database name (app name with dashes replaced by underscores)
*/}}
{{- define "cloudhost-app.dbName" -}}
{{- .Values.app.name | replace "-" "_" }}
{{- end }}
{{/*
Database deployment name
*/}}
{{- define "cloudhost-app.dbDeploymentName" -}}
{{- printf "%s-db" .Values.app.name }}
{{- end }}
{{/*
Database image auto-computed from type + version if not explicitly set
*/}}
{{- define "cloudhost-app.dbImage" -}}
{{- if .Values.database.image }}
{{- .Values.database.image }}
{{- else if eq .Values.database.type "postgresql" }}
{{- printf "postgres:%s-alpine" .Values.database.version }}
{{- else }}
{{- printf "mysql:%s" .Values.database.version }}
{{- end }}
{{- end }}
{{/*
Database port
*/}}
{{- define "cloudhost-app.dbPort" -}}
{{- if eq .Values.database.type "postgresql" }}5432{{- else }}3306{{- end }}
{{- end }}
{{/*
Database data mount path (volume mount target)
*/}}
{{- define "cloudhost-app.dbDataPath" -}}
{{- if eq .Values.database.type "postgresql" }}/var/lib/postgresql/data{{- else }}/var/lib/mysql{{- end }}
{{- end }}
{{/*
PostgreSQL PGDATA path must be a subdirectory of the mount to avoid "initdb: directory not empty" errors
*/}}
{{- define "cloudhost-app.pgDataDir" -}}
/var/lib/postgresql/data/pgdata
{{- end }}
@@ -0,0 +1,102 @@
{{- if .Values.database.enabled }}
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
{{- $dbName := include "cloudhost-app.dbDeploymentName" . -}}
{{- $dbPort := include "cloudhost-app.dbPort" . -}}
{{- $dbData := include "cloudhost-app.dbDataPath" . -}}
{{- $dbImg := include "cloudhost-app.dbImage" . -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $dbName }}
namespace: {{ $ns }}
labels:
app: {{ $dbName }}
{{- include "cloudhost-app.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app: {{ $dbName }}
template:
metadata:
labels:
app: {{ $dbName }}
spec:
containers:
- name: {{ $dbName }}
image: {{ $dbImg }}
ports:
- containerPort: {{ include "cloudhost-app.dbPort" . | int }}
env:
{{- if eq .Values.database.type "postgresql" }}
- name: PGDATA
value: {{ include "cloudhost-app.pgDataDir" . }}
- name: POSTGRES_DB
value: {{ include "cloudhost-app.dbName" . }}
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
{{- else }}
- name: MYSQL_DATABASE
value: {{ include "cloudhost-app.dbName" . }}
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
{{- end }}
volumeMounts:
- name: db-storage
mountPath: {{ $dbData }}
resources:
requests:
cpu: {{ .Values.database.resources.cpuRequest | quote }}
memory: {{ .Values.database.resources.memoryRequest | quote }}
limits:
cpu: {{ .Values.database.resources.cpuLimit | quote }}
memory: {{ .Values.database.resources.memoryLimit | quote }}
readinessProbe:
{{- if eq .Values.database.type "postgresql" }}
exec:
command: ["pg_isready", "-U", {{ .Values.database.username | quote }}]
{{- else }}
exec:
command: ["mysqladmin", "ping", "-h", "127.0.0.1"]
{{- end }}
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 6
livenessProbe:
{{- if eq .Values.database.type "postgresql" }}
exec:
command: ["pg_isready", "-U", {{ .Values.database.username | quote }}]
{{- else }}
exec:
command: ["mysqladmin", "ping", "-h", "127.0.0.1"]
{{- end }}
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 5
volumes:
- name: db-storage
persistentVolumeClaim:
claimName: {{ $dbName }}
{{- end }}
@@ -0,0 +1,20 @@
{{- if .Values.database.enabled }}
{{- $ns := include "cloudhost-app.namespace" . -}}
{{- $dbName := include "cloudhost-app.dbDeploymentName" . -}}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ $dbName }}
namespace: {{ $ns }}
labels:
app: {{ $dbName }}
{{- include "cloudhost-app.labels" . | nindent 4 }}
annotations:
"helm.sh/resource-policy": keep
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.database.storageSize | quote }}
{{- end }}
@@ -0,0 +1,17 @@
{{- if .Values.database.enabled }}
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $name }}-db-secret
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
annotations:
"helm.sh/resource-policy": keep
type: Opaque
data:
username: {{ .Values.database.username | b64enc | quote }}
password: {{ .Values.database.password | b64enc | quote }}
{{- end }}
@@ -0,0 +1,21 @@
{{- if .Values.database.enabled }}
{{- $ns := include "cloudhost-app.namespace" . -}}
{{- $dbName := include "cloudhost-app.dbDeploymentName" . -}}
{{- $dbPort := include "cloudhost-app.dbPort" . -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $dbName }}
namespace: {{ $ns }}
labels:
app: {{ $dbName }}
{{- include "cloudhost-app.labels" . | nindent 4 }}
spec:
type: ClusterIP
selector:
app: {{ $dbName }}
ports:
- port: {{ include "cloudhost-app.dbPort" . | int }}
targetPort: {{ include "cloudhost-app.dbPort" . | int }}
protocol: TCP
{{- end }}
@@ -0,0 +1,124 @@
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
annotations:
kubernetes.io/change-cause: {{ .Values.changeCause | default (printf "Deploy %s" .Values.app.image) | quote }}
spec:
revisionHistoryLimit: 10
replicas: {{ .Values.app.replicas }}
selector:
matchLabels:
{{- include "cloudhost-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "cloudhost-app.labels" . | nindent 8 }}
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: {{ $name }}
image: {{ .Values.app.image | quote }}
imagePullPolicy: Always
ports:
- containerPort: {{ .Values.app.port }}
{{- if and .Values.envVars (gt (len .Values.envVars) 0) }}
envFrom:
- secretRef:
name: {{ $name }}-env
{{- end }}
env:
{{- if and .Values.database.enabled (eq .Values.database.type "postgresql") }}
- name: DB_HOST
value: {{ include "cloudhost-app.dbDeploymentName" . }}
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: {{ include "cloudhost-app.dbName" . }}
- name: DB_USER
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
- name: DATABASE_URL
value: "postgresql://$(DB_USER):$(DB_PASSWORD)@{{ include "cloudhost-app.dbDeploymentName" . }}:5432/{{ include "cloudhost-app.dbName" . }}"
{{- end }}
{{- if and .Values.database.enabled (eq .Values.database.type "mysql") }}
- name: DB_HOST
value: {{ include "cloudhost-app.dbDeploymentName" . }}
- name: DB_PORT
value: "3306"
- name: DB_NAME
value: {{ include "cloudhost-app.dbName" . }}
- name: DB_USER
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
- name: DATABASE_URL
value: "mysql://$(DB_USER):$(DB_PASSWORD)@{{ include "cloudhost-app.dbDeploymentName" . }}:3306/{{ include "cloudhost-app.dbName" . }}"
{{- end }}
{{- /* WordPress-specific env vars (official image expects these) */}}
{{- if and .Values.wordpress.enabled .Values.database.enabled }}
- name: WORDPRESS_DB_HOST
value: "{{ include "cloudhost-app.dbDeploymentName" . }}:{{ include "cloudhost-app.dbPort" . }}"
- name: WORDPRESS_DB_NAME
value: {{ include "cloudhost-app.dbName" . }}
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: username
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ $name }}-db-secret
key: password
- name: WORDPRESS_TABLE_PREFIX
value: "wp_"
{{- end }}
resources:
requests:
cpu: {{ .Values.resources.cpuRequest | quote }}
memory: {{ .Values.resources.memoryRequest | quote }}
limits:
cpu: {{ .Values.resources.cpuLimit | quote }}
memory: {{ .Values.resources.memoryLimit | quote }}
readinessProbe:
tcpSocket:
port: {{ .Values.app.port }}
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
tcpSocket:
port: {{ .Values.app.port }}
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 5
{{- if .Values.wordpress.enabled }}
volumeMounts:
- name: wp-content
mountPath: /var/www/html/wp-content
{{- end }}
{{- if .Values.wordpress.enabled }}
volumes:
- name: wp-content
persistentVolumeClaim:
claimName: {{ $name }}-wp-content
{{- end }}
@@ -0,0 +1,31 @@
{{- if .Values.ingress.enabled }}
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
{{- $host := printf "%s.%s" (default $name .Values.ingress.subdomain) .Values.ingress.domain -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
annotations:
cert-manager.io/cluster-issuer: {{ .Values.ingress.clusterIssuer | quote }}
spec:
ingressClassName: nginx
rules:
- host: {{ $host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ $name }}
port:
number: 80
tls:
- hosts:
- {{ $host }}
secretName: {{ $name }}-tls
{{- end }}
@@ -0,0 +1,13 @@
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
{{- $registryUrl := .Values.registry.url | default "localhost:30500" -}}
apiVersion: v1
kind: Secret
metadata:
name: registry-pull-secret
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ printf `{"auths":{"%s":{"auth":""}}}` $registryUrl | b64enc | quote }}
@@ -0,0 +1,16 @@
{{- if and .Values.envVars (gt (len .Values.envVars) 0) }}
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $name }}-env
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
type: Opaque
data:
{{- range $key, $val := .Values.envVars }}
{{ $key }}: {{ $val | b64enc | quote }}
{{- end }}
{{- end }}
@@ -0,0 +1,17 @@
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "cloudhost-app.selectorLabels" . | nindent 4 }}
ports:
- port: 80
targetPort: {{ .Values.app.port }}
protocol: TCP
@@ -0,0 +1,19 @@
{{- if .Values.wordpress.enabled }}
{{- $name := include "cloudhost-app.name" . -}}
{{- $ns := include "cloudhost-app.namespace" . -}}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ $name }}-wp-content
namespace: {{ $ns }}
labels:
{{- include "cloudhost-app.labels" . | nindent 4 }}
annotations:
"helm.sh/resource-policy": keep
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.wordpress.wpContentStorageSize | quote }}
{{- end }}