From 7fbed47e82f5ce5ea50ce1cd02613e280dcc7c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bojan=20=C4=8Cekrli=C4=87?= Date: Sat, 24 Oct 2020 21:20:37 +0200 Subject: [PATCH] Initial commit of the helm chart This is the initial commit of the helm chart. TO-DO: * write usage documentation * add job which will automatically add the chart to `gh-pages` on creating a new release --- .gitignore | 1 + helm/postfix/.helmignore | 23 +++++ helm/postfix/Chart.yaml | 23 +++++ helm/postfix/charts/.gitkeep | 0 helm/postfix/templates/NOTES.txt | 16 +++ helm/postfix/templates/_helpers.tpl | 77 +++++++++++++++ helm/postfix/templates/configmap.yaml | 18 ++++ helm/postfix/templates/hpa.yaml | 36 +++++++ helm/postfix/templates/service.yaml | 22 +++++ helm/postfix/templates/serviceaccount.yaml | 14 +++ helm/postfix/templates/statefulset.yaml | 107 +++++++++++++++++++++ helm/postfix/values.yaml | 102 ++++++++++++++++++++ helm/test.sh | 9 ++ helm/test_1_default.yml | 1 + helm/test_2_no_persistence.yml | 2 + helm/test_3_no_service_account.yml | 2 + helm/test_4_common_config.yml | 36 +++++++ 17 files changed, 489 insertions(+) create mode 100644 helm/postfix/.helmignore create mode 100644 helm/postfix/Chart.yaml create mode 100644 helm/postfix/charts/.gitkeep create mode 100644 helm/postfix/templates/NOTES.txt create mode 100644 helm/postfix/templates/_helpers.tpl create mode 100644 helm/postfix/templates/configmap.yaml create mode 100644 helm/postfix/templates/hpa.yaml create mode 100644 helm/postfix/templates/service.yaml create mode 100644 helm/postfix/templates/serviceaccount.yaml create mode 100644 helm/postfix/templates/statefulset.yaml create mode 100644 helm/postfix/values.yaml create mode 100755 helm/test.sh create mode 100644 helm/test_1_default.yml create mode 100644 helm/test_2_no_persistence.yml create mode 100644 helm/test_3_no_service_account.yml create mode 100644 helm/test_4_common_config.yml diff --git a/.gitignore b/.gitignore index 485dee6..17390e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea +helm/fixtures \ No newline at end of file diff --git a/helm/postfix/.helmignore b/helm/postfix/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/postfix/.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/helm/postfix/Chart.yaml b/helm/postfix/Chart.yaml new file mode 100644 index 0000000..e8aadcb --- /dev/null +++ b/helm/postfix/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: postfix +description: Helm chart for Postfix mail server + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 1.0.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +appVersion: 1.0.0 diff --git a/helm/postfix/charts/.gitkeep b/helm/postfix/charts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/helm/postfix/templates/NOTES.txt b/helm/postfix/templates/NOTES.txt new file mode 100644 index 0000000..4863717 --- /dev/null +++ b/helm/postfix/templates/NOTES.txt @@ -0,0 +1,16 @@ +Postfix service installed. Send email by using the following address and port: + +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "postfix.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "postfix.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "postfix.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "postfix.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/helm/postfix/templates/_helpers.tpl b/helm/postfix/templates/_helpers.tpl new file mode 100644 index 0000000..7f61032 --- /dev/null +++ b/helm/postfix/templates/_helpers.tpl @@ -0,0 +1,77 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "postfix.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 "postfix.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 "postfix.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "postfix.labels" -}} +helm.sh/chart: {{ include "postfix.chart" . }} +{{ include "postfix.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "postfix.selectorLabels" -}} +app.kubernetes.io/name: {{ include "postfix.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "postfix.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "postfix.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Define checksum annotations +*/}} +{{- define "postfix.checksums" -}} +# Reload for Statefulset when configmap changes on deployment +checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} +{{- end -}} + +{{- define "postfix.reloader" -}} +# Auto-reload postfix if somebody changes config map directly in Kuberentes. +# Uses: https://github.com/stakater/Reloader +configmap.reloader.stakater.com/reload: "{{ include "postfix.fullname" . }}" +{{- end -}} diff --git a/helm/postfix/templates/configmap.yaml b/helm/postfix/templates/configmap.yaml new file mode 100644 index 0000000..820dd20 --- /dev/null +++ b/helm/postfix/templates/configmap.yaml @@ -0,0 +1,18 @@ +{{- $chart := "postfix" -}} +{{- $fullName := include (print $chart ".fullname") . -}} +{{- $labels := include (print $chart ".labels") . -}} +{{- $files := .Files -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ $fullName }} + labels: + {{- $labels | nindent 4 }} +data: + {{- with .Values.config.general }}{{ toYaml . | nindent 2 }}{{ end }} + {{- range $key, $value := .Values.config.postfix }} + POSTFIX_{{ $key }}: {{ $value | quote }} + {{- end }} + {{- range $key, $value := .Values.config.opendkim }} + OPENDKIM_{{ $key }}: {{ $value | quote }} + {{- end }} diff --git a/helm/postfix/templates/hpa.yaml b/helm/postfix/templates/hpa.yaml new file mode 100644 index 0000000..11142af --- /dev/null +++ b/helm/postfix/templates/hpa.yaml @@ -0,0 +1,36 @@ +{{- if .Values.autoscaling.enabled }} +{{- $chart := "postfix" -}} +{{- $fullName := include (print $chart ".fullname") . -}} +{{- $labels := include (print $chart ".labels") . -}} +{{- $kind := "StatefulSet" -}} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $fullName | quote }} + namespace: {{ .Release.Namespace | quote }} + labels: + {{- $labels | nindent 4 }} + {{- with .Values.autoscaling.labels }}{{ toYaml . | nindent 4 }}{{ end }} + annotations: + {{- with .Values.autoscaling.annotations }}{{ toYaml . | nindent 4 }}{{ end }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: {{ $kind | quote }} + name: {{ $fullName | quote }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/postfix/templates/service.yaml b/helm/postfix/templates/service.yaml new file mode 100644 index 0000000..58f6337 --- /dev/null +++ b/helm/postfix/templates/service.yaml @@ -0,0 +1,22 @@ +{{- $chart := "postfix" -}} +{{- $fullName := include (print $chart ".fullname") . -}} +{{- $labels := include (print $chart ".labels") . -}} +{{- $selectorLabels := include (print $chart ".selectorLabels") . -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ $fullName | quote }} + labels: + {{- $labels | nindent 4 }} + {{- with .Values.service.labels }}{{ toYaml . | nindent 4 }}{{ end }} + annotations: + {{- with .Values.service.annotations }}{{ toYaml . | nindent 4 }}{{ end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: smtp + protocol: TCP + name: smtp + selector: + {{- $selectorLabels | nindent 4 }} diff --git a/helm/postfix/templates/serviceaccount.yaml b/helm/postfix/templates/serviceaccount.yaml new file mode 100644 index 0000000..25f5084 --- /dev/null +++ b/helm/postfix/templates/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.serviceAccount.create -}} +{{- $chart := "postfix" -}} +{{- $labels := include (print $chart ".labels") . -}} +{{- $serviceAccountName := include (print $chart ".serviceAccountName") . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceAccountName }} + labels: + {{- $labels | nindent 4 }} + {{- with .Values.serviceAccount.labels }}{{ toYaml . | nindent 4 }}{{ end }} + annotations: + {{- with .Values.serviceAccount.annotations }}{{ toYaml . | nindent 4 }}{{ end }} +{{- end }} diff --git a/helm/postfix/templates/statefulset.yaml b/helm/postfix/templates/statefulset.yaml new file mode 100644 index 0000000..60a2a2f --- /dev/null +++ b/helm/postfix/templates/statefulset.yaml @@ -0,0 +1,107 @@ +{{- $chart := "postfix" -}} +{{- $fullName := include (print $chart ".fullname") . -}} +{{- $labels := include (print $chart ".labels") . -}} +{{- $reloaderAnnotations := include (print $chart ".reloader") . -}} +{{- $selectorLabels := include (print $chart ".selectorLabels") . -}} +{{- $checksums := include (print $chart ".checksums") . -}} +{{- $serviceAccountName := include (print $chart ".serviceAccountName") . -}} +{{- $kind := "StatefulSet" -}} +apiVersion: apps/v1 +kind: {{ $kind | quote }} +metadata: + name: {{ $fullName | quote }} + namespace: {{ .Release.Namespace | quote }} + labels: + {{- $labels | nindent 4 }} + {{- with .Values.deployment.labels }}{{ toYaml . | nindent 4 }}{{ end }} + annotations: + {{- $reloaderAnnotations | nindent 4 }} + {{- with .Values.deployment.annotations }}{{ toYaml . | nindent 4 }}{{ end }} +spec: + {{ if eq $kind "StatefulSet" }}serviceName: {{ $fullName }}{{ end }} + {{ if eq $kind "Deployment" }}{{ with .Values.strategy }}strategy: {{- toYaml . | nindent 4 }}{{ end }}{{ end }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- $selectorLabels | nindent 6 }} + template: + metadata: + labels: + {{- $selectorLabels | nindent 8 }} + {{- with .Values.pod.labels }}{{ toYaml . | nindent 8 }}{{- end }} + annotations: + # https://keel.sh/docs/#helm-same-tag-force-updates + # Current consensus on a best way to "force" update Helm releases is by modifying your pod spec template by adding: + date/deploy-date: {{ now | quote }} + {{- $checksums | nindent 8 }} + {{- with .Values.pod.annotations }}{{ toYaml . | nindent 8 }}{{ end }} + spec: + serviceAccountName: {{ $serviceAccountName | quote }} + {{ with .Values.imagePullSecrets }}imagePullSecrets: {{- toYaml . | nindent 8 }}{{- end }} + {{ if .Values.schedulerName }}schedulerName: {{ .Values.schedulerName | quote }}{{ end }} + {{ if .Values.priorityClassName }}priorityClassName: {{ .Values.priorityClassName | quote }}{{ end }} + {{- with .Values.pod.securityContext }}securityContext: {{- 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 }} + + {{- if .Values.extraInitContainers }} + # + # Init containers + # + initContainers: + {{- tpl .Values.extraInitContainers . | nindent 6 }} + {{- end }} + + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: {{- toYaml .Values.container.postfix.securityContext | indent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + ports: + - name: smtp + containerPort: 587 + protocol: TCP + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 60 + tcpSocket: + port: 587 + livenessProbe: + initialDelaySeconds: 10 + periodSeconds: 120 + tcpSocket: + port: 587 + envFrom: + - configMapRef: + name: {{ $fullName | quote }} + {{ with .Values.extraEnv }}env: {{- toYaml . | nindent 12 }}{{ end }} + volumeMounts: + - mountPath: /var/spool/postfix + name: {{ $fullName }} + {{- with .Values.extraVolumeMounts }}{{- toYaml . | nindent 12 }}{{ end }} + resources: {{ toYaml .Values.resources | nindent 12 }} + + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: {{ $fullName }} + namespace: infrastructure + spec: + accessModes: {{- toYaml .Values.persistence.accessModes | nindent 10 }} + {{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ with .Values.extraVolumes }}volumes: {{- toYaml . | nindent 4 }}{{ end }} + {{- else }} + volumes: + - name: {{ $fullName }} + emptyDir: {} + {{- with .Values.extraVolumes }}{{- toYaml . | nindent 4 }}{{ end }} + {{- end }} diff --git a/helm/postfix/values.yaml b/helm/postfix/values.yaml new file mode 100644 index 0000000..6c6a110 --- /dev/null +++ b/helm/postfix/values.yaml @@ -0,0 +1,102 @@ +replicaCount: 1 + +image: + repository: boky/postfix + pullPolicy: IfNotPresent + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true # Specifies whether a service account should be created + annotations: {} # Annotations to add to the service account + name: "" # The name of the service account to use. If not set and create is true, a name is generated using the fullname template + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 587 + labels: {} + annotations: {} + +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 + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + labels: {} + annotations: {} + +resources: {} +nodeSelector: {} +tolerations: [] +affinity: {} +extraVolumes: [] +extraVolumeMounts: [] +extraInitContainers: [] +extraEnv: [] + +deployment: + labels: {} + annotations: {} + +pod: + securityContext: {} + labels: {} + annotations: {} + +container: + postfix: + securityContext: {} + +config: + general: {} + # e.g. + # TZ: + # FORCE_COLOR: + # INBOUND_DEBUGGING: + # ALLOWED_SENDER_DOMAINS: + # ALLOW_EMPTY_SENDER_DOMAINS: + # LOG_FORMAT: + # RELAYHOST: + # RELAYHOST_USERNAME: + # RELAYHOST_PASSWORD: + # RELAYHOST_TLS_LEVEL: + # MASQUERADED_DOMAINS: + # SMTP_HEADER_CHECKS: + # DKIM_SELECTOR: + # DKIM_AUTOGENERATE: + postfix: {} + # e.g. + # myhostname: "postfix" + opendkim: {} + # e.g. + # RequireSafeKeys: "yes" + +persistence: + enabled: true + accessModes: + - ReadWriteOnce + size: 1Gi + storageClass: "" diff --git a/helm/test.sh b/helm/test.sh new file mode 100755 index 0000000..78f98d5 --- /dev/null +++ b/helm/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e +mkdir -p fixtures + +for i in `find -maxdepth 1 -type f -name test\*yml | sort`; do + echo "☆☆☆☆☆☆☆☆☆☆ $i ☆☆☆☆☆☆☆☆☆☆" + helm template -f $i --dry-run postfix > fixtures/demo.yaml + docker run -it -v `pwd`/fixtures:/fixtures garethr/kubeval fixtures/demo.yaml +done diff --git a/helm/test_1_default.yml b/helm/test_1_default.yml new file mode 100644 index 0000000..2100873 --- /dev/null +++ b/helm/test_1_default.yml @@ -0,0 +1 @@ +# This file has ben purposefuly left bank diff --git a/helm/test_2_no_persistence.yml b/helm/test_2_no_persistence.yml new file mode 100644 index 0000000..4d6af8b --- /dev/null +++ b/helm/test_2_no_persistence.yml @@ -0,0 +1,2 @@ +persistence: + enabled: false diff --git a/helm/test_3_no_service_account.yml b/helm/test_3_no_service_account.yml new file mode 100644 index 0000000..6648213 --- /dev/null +++ b/helm/test_3_no_service_account.yml @@ -0,0 +1,2 @@ +serviceAccount: + enabled: false diff --git a/helm/test_4_common_config.yml b/helm/test_4_common_config.yml new file mode 100644 index 0000000..1c81bec --- /dev/null +++ b/helm/test_4_common_config.yml @@ -0,0 +1,36 @@ +autoscaling: + enabled: true + maxReplicas: 4 + +resources: + limits: + cpu: 50Mi + memory: 100Mi + requests: + cpu: 10m + memory: 50Mi + +config: + general: + TZ: Europe/London + ALLOWED_SENDER_DOMAINS: example.org + postfix: + myhostname: localhost + opendkim: + RequireSafeKeys: yes + +extraVolumeMounts: + - mountPath: /etc/opendkim/keys + name: opendkim-keys + +extraVolumes: + - name: opendkim-keys + persistentVolumeClaim: + claimName: opendkim-keys-claim + +persistence: + enabled: true + accessModes: + - ReadWriteOnce + size: 128Mi + storageClass: "demo-demo"