{{/* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}} {{- if .Values.manifests.statefulset }} {{- $envAll := . }} {{- $serviceAccountName := "postgresql" }} {{ tuple $envAll "postgresql" $serviceAccountName | include "helm-toolkit.snippets.kubernetes_pod_rbac_serviceaccount" }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: {{ $serviceAccountName }} namespace: {{ $envAll.Release.Namespace }} rules: - apiGroups: - "" resources: - configmaps verbs: - create - get - list - patch - update - watch # delete and deletecollection are required only for 'patronictl remove' - delete - deletecollection - apiGroups: - "" resources: - endpoints verbs: - get - patch - update # the following three privileges are necessary only when using endpoints - create - list - watch # delete and deletecollection are required only for 'patronictl remove' - delete - deletecollection - apiGroups: - "" resources: - pods verbs: - get - list - patch - update - watch # The following privilege is only necessary for creation of headless service # for postgresql-config endpoint, in order to prevent cleaning it up by the # k8s master. - apiGroups: - "" resources: - services verbs: - create - get - list - patch - update - watch - delete --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: {{ $serviceAccountName }} namespace: {{ $envAll.Release.Namespace }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: {{ $serviceAccountName }} subjects: - kind: ServiceAccount name: {{ $serviceAccountName }} namespace: {{ $envAll.Release.Namespace }} --- apiVersion: apps/v1 kind: StatefulSet metadata: name: postgresql annotations: {{ tuple $envAll | include "helm-toolkit.snippets.release_uuid" }} labels: {{ tuple $envAll "postgresql" "server" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 4 }} cluster-name: {{ tuple "postgresql" "internal" . | include "helm-toolkit.endpoints.hostname_short_endpoint_lookup" }} spec: serviceName: {{ tuple "postgresql" "internal" . | include "helm-toolkit.endpoints.hostname_short_endpoint_lookup" }} podManagementPolicy: "Parallel" {{ tuple $envAll | include "helm-toolkit.snippets.kubernetes_upgrades_statefulset" | indent 2 }} replicas: {{ .Values.pod.replicas.server }} selector: matchLabels: {{ tuple $envAll "postgresql" "server" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 6 }} cluster-name: {{ tuple "postgresql" "internal" . | include "helm-toolkit.endpoints.hostname_short_endpoint_lookup" }} template: metadata: labels: {{ tuple $envAll "postgresql" "server" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 8 }} cluster-name: {{ tuple "postgresql" "internal" . | include "helm-toolkit.endpoints.hostname_short_endpoint_lookup" }} annotations: {{ tuple $envAll | include "helm-toolkit.snippets.release_uuid" | indent 8 }} configmap-bin-hash: {{ tuple "configmap-bin.yaml" . | include "helm-toolkit.utils.hash" }} configmap-etc-hash: {{ tuple "configmap-etc.yaml" . | include "helm-toolkit.utils.hash" }} {{ dict "envAll" $envAll "podName" "postgresql" "containerNames" (list "postgresql" "set-volume-perms" "init") | include "helm-toolkit.snippets.kubernetes_mandatory_access_control_annotation" | indent 8 }} configmap-admin-hash: {{ tuple "secret-admin.yaml" . | include "helm-toolkit.utils.hash" }} configmap-replica-hash: {{ tuple "secret-replica.yaml" . | include "helm-toolkit.utils.hash" }} configmap-secrets-etc-hash: {{ tuple "secrets-etc.yaml" . | include "helm-toolkit.utils.hash" }} spec: serviceAccountName: {{ $serviceAccountName }} {{ dict "envAll" $envAll "application" "server" | include "helm-toolkit.snippets.kubernetes_pod_security_context" | indent 6 }} affinity: {{ tuple $envAll "postgresql" "server" | include "helm-toolkit.snippets.kubernetes_pod_anti_affinity" | indent 8 }} nodeSelector: {{ .Values.labels.server.node_selector_key }}: {{ .Values.labels.server.node_selector_value }} terminationGracePeriodSeconds: {{ .Values.pod.lifecycle.termination_grace_period.server.timeout | default "180" }} initContainers: {{ tuple $envAll "postgresql" list | include "helm-toolkit.snippets.kubernetes_entrypoint_init_container" | indent 8 }} - name: set-volume-perms {{ tuple $envAll "postgresql" | include "helm-toolkit.snippets.image" | indent 10 }} {{ tuple $envAll $envAll.Values.pod.resources.server | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }} command: ["/bin/sh", "-c"] args: - set -xe; /bin/chown {{ .Values.pod.security_context.server.pod.runAsUser }} {{ .Values.storage.mount.path }}; /bin/chmod 700 {{ .Values.storage.mount.path }}; /bin/chmod 700 {{ .Values.storage.mount.path }}/*; /bin/cp {{ .Values.secrets.pki.client_cert_path }}_temp/* {{ .Values.secrets.pki.client_cert_path }}/.; /bin/cp {{ .Values.secrets.pki.server_cert_path }}_temp/* {{ .Values.secrets.pki.server_cert_path }}/.; /bin/chown {{ .Values.pod.security_context.server.pod.runAsUser }} {{ .Values.secrets.pki.client_cert_path }}; /bin/chown {{ .Values.pod.security_context.server.pod.runAsUser }} {{ .Values.secrets.pki.client_cert_path }}/*; /bin/chown {{ .Values.pod.security_context.server.pod.runAsUser }} {{ .Values.secrets.pki.server_cert_path }}; /bin/chown {{ .Values.pod.security_context.server.pod.runAsUser }} {{ .Values.secrets.pki.server_cert_path }}/*; /bin/chmod 700 {{ .Values.secrets.pki.client_cert_path }}; /bin/chmod 600 {{ .Values.secrets.pki.client_cert_path }}/*; /bin/chmod 700 {{ .Values.secrets.pki.server_cert_path }}; /bin/chmod 600 {{ .Values.secrets.pki.server_cert_path }}/*; {{ dict "envAll" $envAll "application" "server" "container" "set_volume_perms" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }} volumeMounts: - name: pod-tmp mountPath: /tmp - name: postgresql-data mountPath: {{ .Values.storage.mount.path }} - name: server-certs mountPath: {{ .Values.secrets.pki.server_cert_path }} # server-cert-temp mountpoint is temp storage for secrets. We copy the # secrets to server-certs folder and set owner and permissions. # This is needed because the secrets are always created readonly. - name: server-certs-temp mountPath: {{ .Values.secrets.pki.server_cert_path }}_temp - name: postgresql-pki subPath: crt mountPath: {{ .Values.secrets.pki.server_cert_path }}_temp/server.crt - name: postgresql-pki subPath: key mountPath: {{ .Values.secrets.pki.server_cert_path }}_temp/server.key - name: replication-pki subPath: ca mountPath: {{ .Values.secrets.pki.server_cert_path }}_temp/ca.crt - name: replication-pki subPath: caKey mountPath: {{ .Values.secrets.pki.server_cert_path }}_temp/ca.key # client-certs is the permanent folder for the client secrets - name: client-certs mountPath: {{ .Values.secrets.pki.client_cert_path }} # client-certs-temp is temporary folder for the client secrets, before they a copied to their permanent folder - name: client-certs-temp mountPath: {{ .Values.secrets.pki.client_cert_path }}_temp - name: replication-pki subPath: crt mountPath: {{ .Values.secrets.pki.client_cert_path }}_temp/client.crt - name: replication-pki subPath: key mountPath: {{ .Values.secrets.pki.client_cert_path }}_temp/client.key - name: postgresql-pki subPath: ca mountPath: {{ .Values.secrets.pki.client_cert_path }}_temp/ca.crt - name: postgresql-pki subPath: caKey mountPath: {{ .Values.secrets.pki.client_cert_path }}_temp/ca.key # This is for non-HA -> Patroni conversion and can be removed in the future - name: patroni-conversion {{ tuple $envAll "postgresql" | include "helm-toolkit.snippets.image" | indent 10 }} {{ tuple $envAll $envAll.Values.pod.resources.server | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }} env: - name: PGDATA value: "{{ .Values.storage.mount.path }}/pgdata" - name: PATRONI_KUBERNETES_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PATRONI_KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: PATRONI_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: PATRONI_KUBERNETES_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PATRONI_SUPERUSER_USERNAME valueFrom: secretKeyRef: name: {{ .Values.secrets.postgresql.admin }} key: 'POSTGRES_USER' - name: PATRONI_SUPERUSER_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.secrets.postgresql.admin }} key: 'POSTGRES_PASSWORD' - name: PATRONI_REPLICATION_USERNAME value: {{ index .Values.secrets.pki.replication.hosts.names 0 | quote }} - name: PATRONI_RESTAPI_CONNECT_ADDRESS value: $(PATRONI_KUBERNETES_POD_IP):{{ tuple "postgresql-restapi" "internal" "restapi" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_RESTAPI_LISTEN value: 0.0.0.0:{{ tuple "postgresql-restapi" "internal" "restapi" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_POSTGRESQL_CONNECT_ADDRESS value: $(PATRONI_KUBERNETES_POD_IP):{{ tuple "postgresql" "internal" "postgresql" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_POSTGRESQL_LISTEN value: 0.0.0.0:{{ tuple "postgresql" "internal" "postgresql" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_admin_PASSWORD value: $(PATRONI_SUPERUSER_PASSWORD) - name: PATRONI_admin_OPTIONS value: 'createrole,createdb' command: - /tmp/patroni_conversion.sh {{ dict "envAll" $envAll "application" "server" "container" "patroni_conversion" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }} volumeMounts: - name: pod-tmp mountPath: /tmp - name: patroni-conversion-tmp mountPath: /var/run/postgresql - name: postgresql-bin mountPath: /tmp/patroni_conversion.sh subPath: patroni_conversion.sh readOnly: true - name: postgresql-data mountPath: {{ .Values.storage.mount.path }} - name: postgresql-etc mountPath: /tmp/patroni-templated.yaml subPath: patroni.yaml readOnly: true containers: - name: postgresql {{ tuple $envAll "postgresql" | include "helm-toolkit.snippets.image" | indent 10 }} {{ tuple $envAll $envAll.Values.pod.resources.server | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }} {{ dict "envAll" $envAll "application" "server" "container" "postgresql" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }} ports: - containerPort: {{ tuple "postgresql-restapi" "internal" "restapi" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} protocol: TCP - containerPort: {{ tuple "postgresql" "internal" "postgresql" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} protocol: TCP env: - name: PGDATA value: "{{ .Values.storage.mount.path }}/pgdata" - name: PATRONI_KUBERNETES_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PATRONI_KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: PATRONI_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: PATRONI_KUBERNETES_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PATRONI_SUPERUSER_USERNAME valueFrom: secretKeyRef: name: {{ .Values.secrets.postgresql.admin }} key: 'POSTGRES_USER' - name: PATRONI_SUPERUSER_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.secrets.postgresql.admin }} key: 'POSTGRES_PASSWORD' - name: PATRONI_REPLICATION_USERNAME value: {{ index .Values.secrets.pki.replication.hosts.names 0 | quote }} - name: PATRONI_RESTAPI_CONNECT_ADDRESS value: $(PATRONI_KUBERNETES_POD_IP):{{ tuple "postgresql-restapi" "internal" "restapi" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_RESTAPI_LISTEN value: 0.0.0.0:{{ tuple "postgresql-restapi" "internal" "restapi" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_POSTGRESQL_CONNECT_ADDRESS value: $(PATRONI_KUBERNETES_POD_IP):{{ tuple "postgresql" "internal" "postgresql" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_POSTGRESQL_LISTEN value: 0.0.0.0:{{ tuple "postgresql" "internal" "postgresql" . | include "helm-toolkit.endpoints.endpoint_port_lookup" }} - name: PATRONI_{{ .Values.endpoints.postgresql.auth.admin.username }}_PASSWORD value: $(PATRONI_SUPERUSER_PASSWORD) - name: PATRONI_{{ .Values.endpoints.postgresql.auth.admin.username }}_OPTIONS value: 'createrole,createdb' {{- if .Values.manifests.secret_audit }} - name: AUDIT_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.secrets.postgresql.audit }} key: AUDIT_PASSWORD # Adding the audit user with no options just adds the user without # any GRANTs. This means the user gets to do only what default # PUBLIC permissions allow, which is only to SELECT from tables. - name: PATRONI_{{ .Values.endpoints.postgresql.auth.audit.username }}_PASSWORD value: $(AUDIT_PASSWORD) {{- end }} - name: PGSSLROOTCERT value: {{ .Values.secrets.pki.client_cert_path }}/ca.crt - name: PGSSLCERT value: "/home/postgres/.postgresql/postgresql.crt" - name: PGSSLKEY value: "/home/postgres/.postgresql/postgresql.key" command: - /tmp/start.sh livenessProbe: exec: command: - /tmp/readiness.sh initialDelaySeconds: 30 timeoutSeconds: 5 failureThreshold: 10 readinessProbe: exec: command: - /tmp/readiness.sh initialDelaySeconds: 30 timeoutSeconds: 5 failureThreshold: 10 volumeMounts: - name: pod-tmp mountPath: /tmp - name: pg-run mountPath: /var/run/postgresql - name: postgresql-bin mountPath: /tmp/set_password.sh subPath: set_password.sh readOnly: true - name: postgresql-bin mountPath: /tmp/start.sh subPath: start.sh readOnly: true - name: postgresql-bin mountPath: /tmp/readiness.sh subPath: readiness.sh readOnly: true - name: postgresql-etc mountPath: /tmp/patroni-templated.yaml subPath: patroni.yaml readOnly: true - name: postgresql-data mountPath: {{ .Values.storage.mount.path }} - name: server-certs mountPath: {{ .Values.secrets.pki.server_cert_path }} - name: client-certs mountPath: {{ .Values.secrets.pki.client_cert_path }} - name: postgres-home-config mountPath: "/home/postgres/.postgresql" - name: client-certs subPath: "client.crt" mountPath: "/home/postgres/.postgresql/postgresql.crt" readOnly: true - name: client-certs subPath: "client.key" mountPath: "/home/postgres/.postgresql/postgresql.key" readOnly: true volumes: - name: pod-tmp emptyDir: {} - name: postgres-home-config emptyDir: {} - name: pg-run emptyDir: medium: "Memory" # This is for non-HA -> Patroni conversion and can be removed in the future - name: patroni-conversion-tmp emptyDir: {} - name: postgresql-bin secret: secretName: postgresql-bin defaultMode: 0555 - name: client-certs-temp emptyDir: {} - name: server-certs-temp emptyDir: {} - name: client-certs emptyDir: {} - name: server-certs emptyDir: {} - name: replication-pki secret: secretName: {{ .Values.secrets.postgresql.replica }} defaultMode: 0640 - name: postgresql-pki secret: secretName: {{ .Values.secrets.postgresql.server }} defaultMode: 0640 - name: postgresql-etc secret: secretName: postgresql-etc defaultMode: 0444 {{- if not .Values.storage.pvc.enabled }} - name: postgresql-data hostPath: path: {{ .Values.storage.host.host_path }} {{- else }} volumeClaimTemplates: - metadata: name: postgresql-data annotations: {{ .Values.storage.pvc.class_path }}: {{ .Values.storage.pvc.class_name }} spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: {{ .Values.storage.pvc.size }} {{- end }} {{- end }}