From 352e65e5138fe703abca0ad692ba5c8b9bb613de Mon Sep 17 00:00:00 2001 From: WrenIX Date: Tue, 26 Sep 2023 00:12:53 +0200 Subject: [PATCH] fix(postgresql): init --- postgresql/.helmignore | 23 +++++ postgresql/Chart.yaml | 16 ++++ postgresql/files/10-init-user.sh | 10 ++ postgresql/files/20-init-db.sh | 10 ++ postgresql/files/_run | 17 ++++ postgresql/templates/_helpers.tpl | 62 ++++++++++++ postgresql/templates/job/job.yaml | 74 +++++++++++++++ postgresql/templates/job/secret.yaml | 16 ++++ postgresql/templates/service.yaml | 15 +++ postgresql/templates/serviceaccount.yaml | 12 +++ postgresql/templates/statefulset.yaml | 116 +++++++++++++++++++++++ postgresql/values.yaml | 115 ++++++++++++++++++++++ 12 files changed, 486 insertions(+) create mode 100644 postgresql/.helmignore create mode 100644 postgresql/Chart.yaml create mode 100644 postgresql/files/10-init-user.sh create mode 100644 postgresql/files/20-init-db.sh create mode 100644 postgresql/files/_run create mode 100644 postgresql/templates/_helpers.tpl create mode 100644 postgresql/templates/job/job.yaml create mode 100644 postgresql/templates/job/secret.yaml create mode 100644 postgresql/templates/service.yaml create mode 100644 postgresql/templates/serviceaccount.yaml create mode 100644 postgresql/templates/statefulset.yaml create mode 100644 postgresql/values.yaml diff --git a/postgresql/.helmignore b/postgresql/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/postgresql/.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/postgresql/Chart.yaml b/postgresql/Chart.yaml new file mode 100644 index 0000000..2409860 --- /dev/null +++ b/postgresql/Chart.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: "v2" +name: "postgresql" +description: "A Helm chart for running PostgreSQL (Postgres) database" +type: "application" +version: "0.1.0" +appVersion: "16.0-alpine3.18" + +keywords: + - "postgresql" + - "postgres" + - "database" + - "sql" + +annotations: + category: "Database" diff --git a/postgresql/files/10-init-user.sh b/postgresql/files/10-init-user.sh new file mode 100644 index 0000000..3a87be2 --- /dev/null +++ b/postgresql/files/10-init-user.sh @@ -0,0 +1,10 @@ +{{- range $username, $password := .Values.job.users }} +echo 'user "{{ $username }}":' +psql -tc "SELECT 1 FROM pg_user WHERE usename = '{{ $username }}'" | grep -q 1; +if [ $? -ne 0 ]; then + psql -c "CREATE USER {{ $username }} WITH ENCRYPTED PASSWORD '{{ $password }}'"; +else + psql -c "ALTER USER {{ $username }} WITH ENCRYPTED PASSWORD '{{ $password }}'"; +fi +echo "" +{{- end }} diff --git a/postgresql/files/20-init-db.sh b/postgresql/files/20-init-db.sh new file mode 100644 index 0000000..5dec00e --- /dev/null +++ b/postgresql/files/20-init-db.sh @@ -0,0 +1,10 @@ +{{- range $name, $config := .Values.job.databases }} +echo 'database "{{ $name }}":' +psql -tc "SELECT 1 FROM pg_database WHERE datname = '{{ $name }}'" | grep -q 1 +if [ $? -ne 0 ]; then + psql -c "CREATE DATABASE {{ $name }} {{ with $config.additionalParams }}{{ . }} {{ end }}"; +fi +psql -c "ALTER DATABASE {{ $name }} OWNER TO {{ $config.owner }}"; +psql -c "GRANT ALL PRIVILEGES ON DATABASE {{ $name }} TO {{ $config.owner }}"; +echo "" +{{- end }} diff --git a/postgresql/files/_run b/postgresql/files/_run new file mode 100644 index 0000000..4619bdd --- /dev/null +++ b/postgresql/files/_run @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +RETRIES={{ .Values.job.retries }} +until psql -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq "0" ]; do + echo "Waiting {{ .Values.job.wait }}s for postgres server, $((RETRIES--)) remaining attempts..." + sleep {{ .Values.job.wait }} +done +if [ ${RETRIES} -eq "0" ]; then + echo "Maximum wait attempts exceeded, aborting". + exit 1; +fi + +set -e +for script in *.sh; do + echo "**** execute script ${script}" + ./${script} +done + diff --git a/postgresql/templates/_helpers.tpl b/postgresql/templates/_helpers.tpl new file mode 100644 index 0000000..30c6288 --- /dev/null +++ b/postgresql/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.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 "postgresql.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 "postgresql.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "postgresql.labels" -}} +helm.sh/chart: {{ include "postgresql.chart" . }} +{{ include "postgresql.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "postgresql.selectorLabels" -}} +app.kubernetes.io/name: {{ include "postgresql.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "postgresql.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "postgresql.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/postgresql/templates/job/job.yaml b/postgresql/templates/job/job.yaml new file mode 100644 index 0000000..028f030 --- /dev/null +++ b/postgresql/templates/job/job.yaml @@ -0,0 +1,74 @@ +{{- if .Values.job.enabled }} +--- +apiVersion: "batch/v1" +kind: "Job" +metadata: + name: "{{ include "postgresql.fullname" . }}-bootstrap" + labels: + {{- include "postgresql.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": "post-install,post-upgrade" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" +spec: + backoffLimit: 4 + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "postgresql.selectorLabels" . | nindent 8 }} + spec: + restartPolicy: Never + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "postgresql.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: "psql" + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: "PGHOST" + value: {{ include "postgresql.fullname" . | quote }} + - name: "PGPORT" + value: {{ .Values.service.port | quote }} + - name: "PGUSER" + value: {{ .Values.postgres.user | quote }} + - name: "PGPASSWORD" + value: {{ .Values.postgres.password | quote }} + command: + - "/bin/sh" + - "-c" + - "cd /bootstrap/ && ./_run" + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - mountPath: "/bootstrap/" + name: "bootstrap" + readOnly: true + {{- 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: "bootstrap" + secret: + secretName: {{ include "postgresql.fullname" . }}-job + defaultMode: 0550 +{{- end }} + diff --git a/postgresql/templates/job/secret.yaml b/postgresql/templates/job/secret.yaml new file mode 100644 index 0000000..2acf048 --- /dev/null +++ b/postgresql/templates/job/secret.yaml @@ -0,0 +1,16 @@ +{{- if .Values.job.enabled }} +--- +apiVersion: "v1" +kind: "Secret" +metadata: + name: {{ include "postgresql.fullname" . }}-job + labels: + {{- include "postgresql.labels" . | nindent 4 }} +stringData: + _run: |- + {{- tpl (.Files.Get "files/_run") . | nindent 4 }} + 10-init-user.sh: |- + {{- tpl (.Files.Get "files/10-init-user.sh") . | nindent 4 }} + 20-init-db.sh: |- + {{- tpl (.Files.Get "files/20-init-db.sh") . | nindent 4 }} +{{- end }} diff --git a/postgresql/templates/service.yaml b/postgresql/templates/service.yaml new file mode 100644 index 0000000..cb3296e --- /dev/null +++ b/postgresql/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: postgresql + protocol: TCP + name: postgresql + selector: + {{- include "postgresql.selectorLabels" . | nindent 4 }} diff --git a/postgresql/templates/serviceaccount.yaml b/postgresql/templates/serviceaccount.yaml new file mode 100644 index 0000000..6839539 --- /dev/null +++ b/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "postgresql.serviceAccountName" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/postgresql/templates/statefulset.yaml b/postgresql/templates/statefulset.yaml new file mode 100644 index 0000000..99d60ba --- /dev/null +++ b/postgresql/templates/statefulset.yaml @@ -0,0 +1,116 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + replicas: 1 + serviceName: {{ include "postgresql.fullname" . }}-headless + selector: + matchLabels: + {{- include "postgresql.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "postgresql.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "postgresql.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: "POSTGRES_PASSWORD" + value: {{ .Values.postgres.password | quote }} + - name: "POSTGRES_USER" + value: {{ .Values.postgres.user | quote }} + - name: "PGPORT" + value: {{ .Values.service.port | quote }} + - name: "PGDATA" + value: "/var/lib/postgresql/data" + ports: + - name: postgresql + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + exec: + command: + - /bin/sh + - -c + - exec pg_isready -U {{ .Values.postgres.user | quote }} -h 127.0.0.1 -p {{ .Values.service.port }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - exec pg_isready -U {{ .Values.postgres.user | quote }} -h 127.0.0.1 -p {{ .Values.service.port }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: "data" + mountPath: "/var/lib/postgresql/data" + {{- 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: "data" + {{- if .Values.persistence.hostPath }} + hostPath: + type: Directory + path: {{ .Values.persistence.hostPath | quote }} + {{- else if .Values.persistence.enabled }} + persistentVolumeClaim: + {{ if .Values.persistence.existingClaim }} + claimName: {{ .Values.persistence.existingClaim }} + {{- else }} + claimName: {{ include "postgresql.fullname" . }}-data + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if and .Values.persistence.enabled (not .Values.persistence.hostPath) (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + labels: + {{- include "postgresql.labels" . | nindent 8 }} + {{- with .Values.persistence.annotations }} + annotations: + {{ toYaml . | indent 8 }} + {{- end }} + spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- with .Values.persistence.storageClass }} + {{- if (eq "-" .) }} + storageClassName: "" + {{- else }} + storageClassName: {{ . | quote }} + {{- end }} + {{- end }} + {{- end }} diff --git a/postgresql/values.yaml b/postgresql/values.yaml new file mode 100644 index 0000000..3df135d --- /dev/null +++ b/postgresql/values.yaml @@ -0,0 +1,115 @@ +# Default values for postgresql. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +image: + registry: docker.io + repository: postgres + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +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: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 5432 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + enabled: true + annotations: {} + ## Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + + ## Do not create an PVC, direct use hostPath in Pod + # hostPath: + accessMode: ReadWriteOnce + size: 1Gi + +# PostgreSQL configuration. +postgres: + # -- Database user. + user: "postgres" + # -- Database password. + password: "SUPERSTRONGPW" + + +# This job bootstrap the database server with databases and users. +job: + # -- Enable database bootstrapping. + enabled: true + # -- Amount of retries while waiting for postgresql server is available. + retries: 60 + # -- Time to wait in each wait in each iteration until postgresql server is available. + wait: 5 + # -- Bootstrap users into postgresql server. When users already exists, they will stay untouched. + # + # users: + # username: RandomPassword0#" + users: {} + + # -- Bootstrap databases into postgresql server. When databases already exists, they will stay untouched. + # + # databases: + # "name_of_database": + # owner: "existing_user_which_will_get_grant" + # additionalParams: "" # Optional + databases: {} + +