From b6b1b9a467c4859f4edbe1f5f3fc2beaa28b0475 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=B6ran=20P=C3=B6hner?=
 <10630407+groundhog2k@users.noreply.github.com>
Date: Sat, 7 May 2022 09:20:19 +0200
Subject: [PATCH] Implement MySQL helm chart (#980)

Fixes #979
---
 charts/mysql/.helmignore                   |  23 ++
 charts/mysql/Chart.yaml                    |  12 +
 charts/mysql/README.md                     | 123 ++++++++++
 charts/mysql/RELEASENOTES.md               |   6 +
 charts/mysql/templates/_helpers.tpl        |  72 ++++++
 charts/mysql/templates/customconfig.yaml   |  11 +
 charts/mysql/templates/scripts.yaml        |  23 ++
 charts/mysql/templates/secureconfig.yaml   |  22 ++
 charts/mysql/templates/service.yaml        |  28 +++
 charts/mysql/templates/serviceaccount.yaml |  12 +
 charts/mysql/templates/statefulset.yaml    | 264 +++++++++++++++++++++
 charts/mysql/values.yaml                   | 192 +++++++++++++++
 12 files changed, 788 insertions(+)
 create mode 100644 charts/mysql/.helmignore
 create mode 100644 charts/mysql/Chart.yaml
 create mode 100644 charts/mysql/README.md
 create mode 100644 charts/mysql/RELEASENOTES.md
 create mode 100644 charts/mysql/templates/_helpers.tpl
 create mode 100644 charts/mysql/templates/customconfig.yaml
 create mode 100644 charts/mysql/templates/scripts.yaml
 create mode 100644 charts/mysql/templates/secureconfig.yaml
 create mode 100644 charts/mysql/templates/service.yaml
 create mode 100644 charts/mysql/templates/serviceaccount.yaml
 create mode 100644 charts/mysql/templates/statefulset.yaml
 create mode 100644 charts/mysql/values.yaml

diff --git a/charts/mysql/.helmignore b/charts/mysql/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/charts/mysql/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/mysql/Chart.yaml b/charts/mysql/Chart.yaml
new file mode 100644
index 00000000..3a5bedcf
--- /dev/null
+++ b/charts/mysql/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: mysql
+description: A Helm chart for MySQL on Kubernetes
+
+type: application
+
+maintainers:
+  - name: groundhog2k
+
+version: 0.1.0
+
+appVersion: "8.0.29-oracle"
diff --git a/charts/mysql/README.md b/charts/mysql/README.md
new file mode 100644
index 00000000..60e91cfd
--- /dev/null
+++ b/charts/mysql/README.md
@@ -0,0 +1,123 @@
+# MySQL
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 8.0.29](https://img.shields.io/badge/AppVersion-8.0.29--oracle-informational?style=flat-square)
+
+## Changelog
+
+see [RELEASENOTES.md](RELEASENOTES.md)
+
+A Helm chart for MySQL on Kubernetes
+
+## TL;DR
+
+```bash
+helm repo add groundhog2k https://groundhog2k.github.io/helm-charts/
+helm install my-release groundhog2k/mysql
+```
+
+## Introduction
+
+This chart uses the original [MySQL image from Docker Hub](https://hub.docker.com/_/mysql) to deploy a stateful MySQL instance in a Kubernetes cluster.
+
+It fully supports deployment of the multi-architecture docker image.
+
+## Prerequisites
+
+- Kubernetes 1.12+
+- Helm 3.x
+- PV provisioner support in the underlying infrastructure
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```bash
+helm install my-release groundhog2k/mysql
+```
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` deployment:
+
+```bash
+helm uninstall my-release
+```
+
+## Common parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| fullnameOverride | string | `""` | Fully override the deployment name |
+| nameOverride | string | `""` | Partially override the deployment name |
+
+## Deployment parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
+| image.repository | string | `"mysql"` | Image name |
+| image.tag | string | `""` | Image tag |
+| imagePullSecrets | list | `[]` | Image pull secrets |
+| extraInitContainers | list | `[]` | Extra init containers |
+| extaContainers | list | `[]` | Extra containers for usage as sidecars |
+| startupProbe | object | `see values.yaml` | Startup probe configuration |
+| livenessProbe | object | `see values.yaml` | Liveness probe configuration |
+| readinessProbe | object | `see values.yaml` | Readiness probe configuration |
+| customStartupProbe | object | `{}` | Custom startup probe (overwrites default startup probe configuration) |
+| customLivenessProbe | object | `{}` | Custom liveness probe (overwrites default liveness probe configuration) |
+| customReadinessProbe | object | `{}` | Custom readiness probe (overwrites default readiness probe configuration) |
+| resources | object | `{}` | Resource limits and requests |
+| nodeSelector | object | `{}` | Deployment node selector |
+| podAnnotations | object | `{}` | Additional pod annotations |
+| podSecurityContext | object | `see values.yaml` | Pod security context |
+| securityContext | object | `see values.yaml` | Container security context |
+| env | list | `[]` | Additional container environmment variables |
+| serviceAccount.annotations | object | `{}` | Additional service account annotations |
+| serviceAccount.create | bool | `false` | Enable service account creation |
+| serviceAccount.name | string | `""` | Name of the service account |
+| affinity | object | `{}` | Affinity for pod assignment |
+| tolerations | list | `[]` | Tolerations for pod assignment |
+| podManagementPolicy | string | `"OrderedReady"` | Pod management policy |
+| updateStrategyType | string | `"RollingUpdate"` | Pod update strategy |
+| revisionHistoryLimit | int | `nil` | Maximum number of revisions maintained in revision history
+
+## Service paramters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| service.type | string | `"ClusterIP"` | Service type |
+| service.port | int | `3306` | MySQL service port |
+| service.nodePort | int | `nil` | The node port (only relevant for type LoadBalancer or NodePort) |
+| service.clusterIP | string | `nil` | The cluster ip address (only relevant for type LoadBalancer or NodePort) |
+| service.loadBalancerIP | string | `nil` | The load balancer ip address (only relevant for type LoadBalancer) |
+| service.annotations | object | `{}` | Additional service annotations |
+
+## Storage parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| storage.accessModes[0] | string | `"ReadWriteOnce"` | Storage access mode |
+| storage.persistentVolumeClaimName | string | `nil` | PVC name when existing storage volume should be used |
+| storage.volumeName | string | `"db-volume"` | Internal volume name and prefix of a created PVC |
+| storage.requestedSize | string | `nil` | Size for new PVC, when no existing PVC is used |
+| storage.className | string | `nil` | Storage class name |
+
+## MySQL parameters
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| userDatabase | object | `{}` | Optional MySQL user database |
+| userDatabase.name | string | `""` | Name of the user database |
+| userDatabase.user | string | `""` | User name with full access to user database|
+| userDatabase.password | string | `""` | Password of created user |
+| settings.rootPassword | string | `nil` | MySQL root password |
+| settings.allowEmptyRootPassword | bool | `false` | Set true to allow an empty root password |
+| settings.skipTZInfo | bool | `false` | Set true to skip loading timezone data during init |
+| settings.arguments | list | `[]` | Additional arguments for mysqld (entrypoint process) |
+| customConfig | string | `nil` | Additional MySQL custom configuration mounted as `/etc/mysql/custom.cnf` |
+| extraEnvSecrets | list | `[]` | A list of existing secrets that will be mounted into the container as environment variables |
+| extraSecretConfigs | string | `nil` | An existing secret with files that will be mounted into the container as custom MySQL configuration files (`*.cnf`) in `/etc/mysql/conf.d` |
+| extraScripts | string | `nil` | An existing configMap with files that will be mounted into the container as script files (`*.sql`, `*.sh`) in `/docker-entrypoint-initdb.d` |
+| extraSecrets | list | `[]` | A list of additional existing secrets that will be mounted into the container |
+| extraSecrets[].name | string | `nil` | Name of the existing K8s secret |
+| extraSecrets[].mountPath | string | `nil` | Mount path where the secret should be mounted into the container (f.e. /mysecretfolder) |
diff --git a/charts/mysql/RELEASENOTES.md b/charts/mysql/RELEASENOTES.md
new file mode 100644
index 00000000..c3002ea8
--- /dev/null
+++ b/charts/mysql/RELEASENOTES.md
@@ -0,0 +1,6 @@
+# Changelog
+
+| Chart version | App version | Change description |
+| :------------ | :---------- | :----------------- |
+| 0.1.0 | 8.0.29 | Initial version of chart |
+| | | |
diff --git a/charts/mysql/templates/_helpers.tpl b/charts/mysql/templates/_helpers.tpl
new file mode 100644
index 00000000..76d4b207
--- /dev/null
+++ b/charts/mysql/templates/_helpers.tpl
@@ -0,0 +1,72 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "mysql.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "mysql.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 "mysql.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "mysql.labels" -}}
+helm.sh/chart: {{ include "mysql.chart" . }}
+{{ include "mysql.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "mysql.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "mysql.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "mysql.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "mysql.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
+
+{{/*
+Generate secret with configuration
+*/}}
+{{- define "mysql.createSecureConfig" -}}
+  {{- if or (.Values.settings.rootPassword) (.Values.userDatabase) }}
+true
+  {{- end }}
+{{- end }}
diff --git a/charts/mysql/templates/customconfig.yaml b/charts/mysql/templates/customconfig.yaml
new file mode 100644
index 00000000..a36dc03f
--- /dev/null
+++ b/charts/mysql/templates/customconfig.yaml
@@ -0,0 +1,11 @@
+{{- if .Values.customConfig }}
+kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: {{ include "mysql.fullname" . }}
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+data:
+  custom.cnf: |
+    {{- .Values.customConfig | nindent 4 }}
+{{- end }}
diff --git a/charts/mysql/templates/scripts.yaml b/charts/mysql/templates/scripts.yaml
new file mode 100644
index 00000000..989333e6
--- /dev/null
+++ b/charts/mysql/templates/scripts.yaml
@@ -0,0 +1,23 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "mysql.fullname" . }}-scripts
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+data:
+  init.sh: |
+    #!/bin/sh
+    echo "Start initialization"
+    if [ -d /extrascripts ]; then
+      echo "Copy extra scripts"
+      cp /extrascripts/* /scripts
+    fi
+    if [ -d /extraconfigs ]; then
+      echo "Copy extra configs"
+      cp /extraconfigs/* /configs
+    fi
+    if [ -d /customconfig ]; then
+      echo "Copy custom config"
+      cp /customconfig/* /configs
+    fi
+    echo "Initialization done."
diff --git a/charts/mysql/templates/secureconfig.yaml b/charts/mysql/templates/secureconfig.yaml
new file mode 100644
index 00000000..5da6f91e
--- /dev/null
+++ b/charts/mysql/templates/secureconfig.yaml
@@ -0,0 +1,22 @@
+{{- if include "mysql.createSecureConfig" . }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "mysql.fullname" . }}
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+type: Opaque
+data:
+  {{- with .Values.settings }}
+  {{- if .rootPassword }}
+  MYSQL_ROOT_PASSWORD: {{ .rootPassword | b64enc }}
+  {{- end }}
+  {{- end }}
+  {{- with .Values.userDatabase }}
+  MYSQL_DATABASE: {{ required "Values: userDatabase.name is mandatory if userDatabase is specified." .name | b64enc }}
+  {{- if .user }}
+  MYSQL_USER: {{ .user | b64enc }}
+  MYSQL_PASSWORD: {{ required "Values: userDatabase.password is mandatory if userDatabase.user is specified." .password | b64enc }}
+  {{- end }}
+  {{- end }}
+{{- end }}
diff --git a/charts/mysql/templates/service.yaml b/charts/mysql/templates/service.yaml
new file mode 100644
index 00000000..779655c6
--- /dev/null
+++ b/charts/mysql/templates/service.yaml
@@ -0,0 +1,28 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "mysql.fullname" . }}
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+  {{- with .Values.service.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: {{ .Values.service.port }}
+      targetPort: sql
+      protocol: TCP
+      name: sql
+      {{- if and ( or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") ) (.Values.service.nodePort) }}
+      nodePort: {{ .Values.service.nodePort }}
+      {{- end }}
+  {{- if and (eq .Values.service.type "LoadBalancer") (.Values.service.loadBalancerIP) }}
+  loadBalancerIP: {{ .Values.service.loadBalancerIP }}
+  {{- end }}
+  {{- if .Values.service.clusterIP }}
+  clusterIP: {{ .Values.service.clusterIP }}
+  {{- end }}
+  selector:
+    {{- include "mysql.selectorLabels" . | nindent 4 }}
diff --git a/charts/mysql/templates/serviceaccount.yaml b/charts/mysql/templates/serviceaccount.yaml
new file mode 100644
index 00000000..3a6a3ff9
--- /dev/null
+++ b/charts/mysql/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "mysql.serviceAccountName" . }}
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/mysql/templates/statefulset.yaml b/charts/mysql/templates/statefulset.yaml
new file mode 100644
index 00000000..abd876cf
--- /dev/null
+++ b/charts/mysql/templates/statefulset.yaml
@@ -0,0 +1,264 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: {{ include "mysql.fullname" . }}
+  labels:
+    {{- include "mysql.labels" . | nindent 4 }}
+spec:
+  replicas: 1
+  {{- if .Values.revisionHistoryLimit }}
+  revisionHistoryLimit: {{ .Values.revisionHistoryLimit }}
+  {{- end }}
+  serviceName: {{ include "mysql.fullname" . }}
+  podManagementPolicy: {{ .Values.podManagementPolicy }}
+  updateStrategy: 
+    type: {{ .Values.updateStrategyType }}
+  selector:
+    matchLabels:
+      {{- include "mysql.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      annotations:
+        checksum/customconfig: {{ include (print $.Template.BasePath "/customconfig.yaml") . | sha256sum }}
+        checksum/secureconfig: {{ include (print $.Template.BasePath "/secureconfig.yaml") . | sha256sum }}
+        checksum/scripts: {{ include (print $.Template.BasePath "/scripts.yaml") . | sha256sum }}
+      {{- with .Values.podAnnotations }}
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "mysql.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "mysql.serviceAccountName" . }}
+      {{- with .Values.podSecurityContext }}
+      securityContext:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      initContainers:
+        - name: {{ .Chart.Name }}-init
+          {{- with .Values.securityContext  }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          volumeMounts:
+            {{- if .Values.extraScripts }}
+            - mountPath: /extrascripts
+              name: extrascripts-volume
+            {{- end }}
+            {{- if .Values.extraSecretConfigs }}
+            - mountPath: /extraconfigs
+              name: extraconfigs-volume
+            {{- end }}
+            {{- if .Values.customConfig }}
+            - mountPath: /customconfig
+              name: customconfig-volume
+            {{- end }}
+            - mountPath: /initscripts
+              name: initscripts
+            - mountPath: /scripts
+              name: scripts
+            - mountPath: /configs
+              name: configs
+          command: [ "/initscripts/init.sh" ]
+      {{- with .Values.extraInitContainers }}
+      {{- toYaml . | nindent 8 }}
+      {{- end }}
+      containers:
+        - name: {{ .Chart.Name }}
+          {{- with .Values.securityContext  }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          ports:
+            - name: sql
+              containerPort: 3306
+              protocol: TCP
+          {{- if .Values.customStartupProbe }}
+          startupProbe:
+            {{- toYaml .Values.customStartupProbe | nindent 12 }}
+          {{- else }}
+          {{- if .Values.startupProbe.enabled }}
+          startupProbe:
+            exec:
+              command:
+                - bash
+                - -c
+                - mysqladmin status -uroot -p$MYSQL_ROOT_PASSWORD
+          {{- with .Values.startupProbe }}
+            initialDelaySeconds: {{ .initialDelaySeconds }}
+            timeoutSeconds: {{ .timeoutSeconds }}
+            failureThreshold: {{ .failureThreshold }}
+            successThreshold: {{ .successThreshold }}
+            periodSeconds: {{ .periodSeconds }}                
+          {{- end }}
+          {{- end }}
+          {{- end }}
+          {{- if .Values.customLivenessProbe }}
+          livenessProbe:
+            {{- toYaml .Values.customLivenessProbe | nindent 12 }}
+          {{- else }}
+          {{- if .Values.livenessProbe.enabled }}
+          livenessProbe:
+            exec:
+              command:
+                - bash
+                - -c
+                - mysqladmin status -uroot -p$MYSQL_ROOT_PASSWORD
+          {{- with .Values.livenessProbe }}
+            initialDelaySeconds: {{ .initialDelaySeconds }}
+            timeoutSeconds: {{ .timeoutSeconds }}
+            failureThreshold: {{ .failureThreshold }}
+            successThreshold: {{ .successThreshold }}
+            periodSeconds: {{ .periodSeconds }}                
+          {{- end }}
+          {{- end }}
+          {{- end }}
+          {{- if .Values.customReadinessProbe }}
+          readinessProbe:
+            {{- toYaml .Values.customReadinessProbe | nindent 12 }}
+          {{- else }}
+          {{- if .Values.readinessProbe.enabled }}
+          readinessProbe:
+            exec:
+              command:
+                - bash
+                - -c
+                - mysqladmin status -uroot -p$MYSQL_ROOT_PASSWORD
+          {{- with .Values.readinessProbe }}
+            initialDelaySeconds: {{ .initialDelaySeconds }}
+            timeoutSeconds: {{ .timeoutSeconds }}
+            failureThreshold: {{ .failureThreshold }}
+            successThreshold: {{ .successThreshold }}
+            periodSeconds: {{ .periodSeconds }}                
+          {{- end }}
+          {{- end }}
+          {{- end }}
+          {{- with .Values.resources }}
+          resources:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          volumeMounts:
+            - mountPath: /var/lib/mysql
+              name: {{ .Values.storage.volumeName }}
+            - mountPath: /tmp
+              name: tmp-volume
+            - mountPath: /var/run/mysqld
+              name: tmp-volume         
+            - mountPath: /etc/mysql/conf.d
+              name: configs
+            - mountPath: /docker-entrypoint-initdb.d
+              name: scripts
+            {{- range $secret := .Values.extraSecrets }}
+            - name: {{ $secret.name }}
+              mountPath: {{ $secret.mountPath }}
+            {{- end }}
+          {{- if .Values.settings.arguments }}
+          args:
+            {{- range .Values.settings.arguments }}
+            - {{ . }}
+            {{- end }}
+          {{- end }}
+          env:
+          {{- if .Values.settings.skipTZInfo }}
+            - name: MYSQL_INITDB_SKIP_TZINFO
+              value: "yes"
+          {{- end }}
+          {{- if .Values.settings.allowEmptyRootPassword }}
+            - name: MYSQL_ALLOW_EMPTY_ROOT_PASSWORD
+              value: "yes"
+          {{- end }}
+          {{- with .Values.env }}
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
+          envFrom:
+          {{- if include "mysql.createSecureConfig" . }}
+            - secretRef:
+                name: {{ include "mysql.fullname" . }}
+          {{- end }}
+          {{- range .Values.extraEnvSecrets }}
+            - secretRef:
+                name: {{ . }}
+          {{- end }}
+      {{- with .Values.extraContainers }}
+      {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      volumes:
+        - name: tmp-volume
+          emptyDir: {}
+        - name: configs
+          emptyDir: {}
+        - name: scripts
+          emptyDir: {}
+        - name: initscripts
+          configMap:
+            name: {{ include "mysql.fullname" . }}-scripts
+            defaultMode: 0555
+      {{- if .Values.extraSecretConfigs }}
+        - name: extraconfigs-volume
+          secret:
+            secretName: {{ .Values.extraSecretConfigs }}
+      {{- end }}
+      {{- if .Values.customConfig }}
+        - name: customconfig-volume
+          configMap:
+            name: {{ include "mysql.fullname" . }}
+      {{- end }}
+      {{- if .Values.extraScripts }}
+        - name: extrascripts-volume
+          configMap:
+            name: {{ .Values.extraScripts }}
+            defaultMode: 0555
+      {{- end }}
+        {{- range $secret := .Values.extraSecrets }}
+        - name: {{ $secret.name }}
+          secret:
+            secretName: {{ $secret.name }}
+            defaultMode: 0440
+        {{- end }}
+{{- $fullname := include "mysql.fullname" . }}
+{{- with .Values.storage }}
+  {{- $createPvc := and (empty .persistentVolumeClaimName) .requestedSize }}
+  {{- if not $createPvc }}
+        - name: {{ .volumeName }}
+        {{- if .persistentVolumeClaimName }}
+          persistentVolumeClaim:
+            claimName: {{ .persistentVolumeClaimName }}
+        {{- else }}
+          emptyDir: {}
+        {{- end }}
+  {{- else }}
+  volumeClaimTemplates:
+    - metadata:
+        name: {{ .volumeName }}
+      spec:
+        {{- with .accessModes }}
+        accessModes:
+          {{- toYaml . | nindent 10 }}
+        {{- end }}
+        {{- if .className }}
+        storageClassName: {{ .className }}
+        {{- end }}
+        resources:
+          requests:
+            storage: {{ .requestedSize }}
+  {{- end }}
+{{- end }}
diff --git a/charts/mysql/values.yaml b/charts/mysql/values.yaml
new file mode 100644
index 00000000..cdbd7e3f
--- /dev/null
+++ b/charts/mysql/values.yaml
@@ -0,0 +1,192 @@
+## Default values for MySQL deployment
+
+## MySQL docker image
+image:
+  repository: mysql
+  pullPolicy: IfNotPresent
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+## Pull secrets and name override options
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+## Optional service account
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: false
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+## Additional node selector
+nodeSelector: {}
+
+## Additional pod annotations
+podAnnotations: {}
+
+## Pod management policy
+podManagementPolicy: OrderedReady
+
+## Pod update strategy
+updateStrategyType: RollingUpdate
+
+## Pod security options
+podSecurityContext:
+  fsGroup: 999
+
+## Default security options to run MySQL as non-root, read only container without privilege escalation
+securityContext:
+  allowPrivilegeEscalation: false
+  privileged: false
+  readOnlyRootFilesystem: true
+  runAsNonRoot: true
+  runAsGroup: 999
+  runAsUser: 999
+
+## Default database service port (default MySQL port)
+service:
+  type: ClusterIP
+  port: 3306
+  ## The node port (only relevant for type LoadBalancer or NodePort)
+  nodePort:
+  ## The cluster ip address (only relevant for type LoadBalancer or NodePort)
+  clusterIP:
+  ## The loadbalancer ip address (only relevant for type LoadBalancer)
+  loadBalancerIP:
+  # Annotations to add to the service
+  annotations: {}
+
+# Resource limits and requests
+resources: {}
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+tolerations: []
+
+affinity: {}
+
+## Maximum number of revisions maintained in revision history
+revisionHistoryLimit:
+
+## Custom startup probe (overwrites default startup probe)
+customStartupProbe: {}
+
+## Default startup probe
+startupProbe:
+  enabled: true
+  initialDelaySeconds: 10
+  timeoutSeconds: 5
+  failureThreshold: 30
+  successThreshold: 1
+  periodSeconds: 10
+
+## Custom liveness probe (overwrites default liveness probe)
+customLivenessProbe: {}
+
+## Default liveness probe
+livenessProbe:
+  enabled: true
+  initialDelaySeconds: 120
+  timeoutSeconds: 5
+  failureThreshold: 3
+  successThreshold: 1
+  periodSeconds: 10
+
+## Custom readiness probe (overwrites default readiness probe)
+customReadinessProbe: {}
+
+## Default readiness probe
+readinessProbe:
+  enabled: true
+  initialDelaySeconds: 30
+  timeoutSeconds: 5
+  failureThreshold: 3
+  successThreshold: 1
+  periodSeconds: 10
+
+## Extra init containers
+extraInitContainers: []
+
+## Extra containers for usage as sidecars
+extraContainers: []
+
+## Additional environment variables
+env: []
+
+## Database configuration
+settings:
+  ## Arguments for the container entrypoint process
+  arguments: []
+  #  - --character-set-server=utf8mb4
+  #  - --collation-server=utf8mb4_unicode_ci
+
+  ## The root user password
+  rootPassword:
+
+  ## Set true to allow an empty root password
+  allowEmptyRootPassword: false
+
+  ## Set true to skip loading timezone data during init
+  skipTZInfo: false
+
+## Optional user database which is created during first startup with user and password
+userDatabase: {}
+  ## Name of the user database
+  # name:
+
+  ## Database user with full access rights
+  # user:
+
+  ## Password of the database user
+  # password:
+
+## Optional custom configuration block that will be mounted as file in /etc/mysql/conf.d/custom.cnf
+## For more flexible options see extraSecretConfigs: section
+customConfig: |
+
+## A list of existing secrets that will be mounted into the container as environment variables
+## As an alternative these secrets can set the database root password or optional user database when userDatabase: and rootPassword: were not specified
+## For example: Setting MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD will allow creating a user database and grant access for the given user
+extraEnvSecrets: []
+
+## An existing secret with files that will be mounted into the container as custom MySQL configuration files (*.cnf) in /etc/mysql/conf.d
+## For more details see https://hub.docker.com/_/mysql
+extraSecretConfigs:
+
+## An existing configMap with files that will be mounted into the container as script files (*.sql, *.sh) in /docker-entrypoint-initdb.d
+## For more details see https://hub.docker.com/_/mysql
+extraScripts:
+
+## A list of additional existing secrets that will be mounted into the container
+## The mounted files of the secrets can be used for advanced configuration - f.e. TLS
+extraSecrets: []
+    ## Name of the existing K8s secret
+#  - name:
+    ## Mount path where the secret should be mounted into the container (f.e. /mysecretfolder)
+#    mountPath:
+
+## Storage parameters
+storage:
+  ##  Set persistentVolumenClaimName to reference an existing PVC
+  persistentVolumeClaimName:
+
+  ## Internal volume name and prefix of a created PVC
+  volumeName: "db-volume"
+
+  ## Alternative set requestedSize to define a size for a dynamically created PVC
+  requestedSize:
+
+  ## the storage class name
+  className:
+
+  ## Default access mode (ReadWriteOnce)
+  accessModes:
+    - ReadWriteOnce
-- 
GitLab