diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 638ee59..24c2be9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: - name: Run chart-releaser uses: helm/chart-releaser-action@v1.6.0 with: - charts_dir: . + charts_dir: helm mark_as_latest: true packages_with_index: true pages_branch: gh-pages diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..37dd16a --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,29 @@ +apiVersion: v2 +name: maildev +description: MailDev is a simple way to test your emails during development with an easy to use web interface. +type: application +version: 1.0.3 +appVersion: "2.1.0" +home: https://github.com/alluen/maildev-helm +maintainers: + - name: chent1 + email: chent1@ohio.edu +sources: + - https://maildev.github.io/maildev + - https://github.com/maildev/maildev + - https://hub.docker.com/r/maildev/maildev +annotations: + artifacthub.io/changes: | + - kind: changed + description: image version to 2.1.0 + artifacthub.io/screenshots: | + - title: SMTP server & web interface for viewing and testing emails during development. + url: https://maildev.github.io/maildev/assets/img/original/maildev-light.png + - title: Resizable viewport to test your emails at different sizes. + url: https://maildev.github.io/maildev/assets/img/original/responsive.png + - title: View email HTML, plain text, headers or raw source. + url: https://maildev.github.io/maildev/assets/img/original/formats.png + - title: Convenient search feature to quickly find any email. + url: https://maildev.github.io/maildev/assets/img/original/search.png + - title: View your emails in an eye-friendly dark interface. + url: https://maildev.github.io/maildev/assets/img/original/maildev-dark.png diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt new file mode 100644 index 0000000..927be07 --- /dev/null +++ b/helm/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.services.web.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "maildev.fullname" . }}-http) + 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.services.web.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 "maildev.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "maildev.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.services.web.port }} +{{- else if contains "ClusterIP" .Values.services.web.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "maildev.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..eb217ce --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,127 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "maildev.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 "maildev.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 "maildev.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "maildev.labels" -}} +helm.sh/chart: {{ include "maildev.chart" . }} +{{ include "maildev.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "maildev.selectorLabels" -}} +app.kubernetes.io/name: {{ include "maildev.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "maildev.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "maildev.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Get the name of the persistent volume claim +*/}} +{{- define "maildev.pvcName" -}} + {{- if .Values.maildev.persistence.existingClaim -}} + {{- printf "%s" (tpl .Values.maildev.persistence.existingClaim $) -}} + {{- else -}} + {{- printf "%s" (include "maildev.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{/* +Get the name of the secret containing the web user password +*/}} +{{- define "maildev.web.secretName" -}} + {{- if .Values.maildev.config.web.existingSecret -}} + {{- printf "%s" .Values.maildev.config.web.existingSecret -}} + {{- else -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-web + {{- end -}} +{{- end -}} + +{{/* +Get the name of the secret containing the password for the incoming SMTP traffic +*/}} +{{- define "maildev.smtp.incoming.secretName" -}} + {{- if .Values.maildev.config.smtp.incoming.existingSecret -}} + {{- printf "%s" .Values.maildev.config.smtp.incoming.existingSecret -}} + {{- else -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-smtp-incoming + {{- end -}} +{{- end -}} + +{{/* +Get the name of the secret containing the password for the outgoing SMTP traffic +*/}} +{{- define "maildev.smtp.outgoing.secretName" -}} + {{- if .Values.maildev.config.smtp.outgoing.existingSecret -}} + {{- printf "%s" .Values.maildev.config.smtp.outgoing.existingSecret -}} + {{- else -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-smtp-outgoing + {{- end -}} +{{- end -}} + +{{/* +Get the name of the configmap containing the relay rules for the SMTP traffic +*/}} +{{- define "maildev.smtp.outgoing.autoRelay.configMapName" -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-smtp-relay-rules +{{- end -}} + +{{/* +Get the name of the service for the SMTP traffic +*/}} +{{- define "maildev.smtp.serviceName" -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-smtp +{{- end -}} + +{{/* +Get the name of the service for the Web GUI +*/}} +{{- define "maildev.web.serviceName" -}} + {{- printf "%s" (include "maildev.fullname" .) -}}-web +{{- end -}} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 0000000..0642e25 --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,279 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "maildev.fullname" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + selector: + matchLabels: + {{- include "maildev.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "maildev.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "maildev.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + {{- if .Values.maildev.config.smtp.outgoing.autoRelay.enabled }} + {{- if .Values.maildev.config.smtp.outgoing.autoRelay.mail }} + - {{- printf "%s %s" "--auto-relay" .Values.maildev.config.smtp.outgoing.autoRelay.mail }} + {{- else }} + - "--auto-relay" + {{- end }} + {{- end }} + {{- if .Values.maildev.config.web.disabled }} + - "--disable-web" + {{- end }} + {{- if .Values.maildev.config.https.enabled }} + - "--https" + {{- end }} + {{- if .Values.maildev.config.logMailContents }} + - "--log-mail-contents" + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.ssl }} + - "--outgoing-secure" + {{- end }} + {{- if .Values.maildev.config.modes.silent }} + - "--silent" + {{- end }} + {{- if .Values.maildev.config.modes.verbose }} + - "--verbose" + {{- end }} + env: + {{- if .Values.maildev.config.https.enabled }} + # HTTPS Settings + {{- if .Values.maildev.config.https.cert }} + - name: MAILDEV_HTTPS_CERT + value: {{ .Values.maildev.config.https.cert | quote }} + {{- end }} + {{- if .Values.maildev.config.https.key }} + - name: MAILDEV_HTTPS_KEY + value: {{ .Values.maildev.config.https.key | quote }} + {{- end }} + {{- end }} + # Maildev Settings + - name: MAILDEV_MAIL_DIRECTORY + value: {{ .Values.maildev.config.mailDirectory | quote }} + # SMTP Settings + - name: MAILDEV_SMTP_PORT + value: {{ .Values.services.smtp.port | quote }} + {{- if .Values.maildev.config.smtp.hideExtensions }} + - name: MAILDEV_HIDE_EXTENSIONS + value: {{ .Values.maildev.config.smtp.hideExtensions | quote }} + {{- end }} + {{- if .Values.maildev.config.smtp.incoming.username }} + - name: MAILDEV_INCOMING_USER + value: {{ .Values.maildev.config.smtp.incoming.username | quote }} + - name: MAILDEV_INCOMING_PASS + valueFrom: + secretKeyRef: + name: {{ include "maildev.smtp.incoming.secretName" . }} + key: smtp-incoming-password + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.host }} + - name: MAILDEV_OUTGOING_HOST + value: {{ .Values.maildev.config.smtp.outgoing.host | quote }} + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.host }} + - name: MAILDEV_OUTGOING_PORT + value: {{ .Values.maildev.config.smtp.outgoing.port | quote }} + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.host }} + - name: MAILDEV_OUTGOING_PASS + valueFrom: + secretKeyRef: + name: {{ include "maildev.smtp.outgoing.secretName" . }} + key: smtp-outgoing-password + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.username }} + - name: MAILDEV_OUTGOING_USER + value: {{ .Values.maildev.config.smtp.outgoing.username | quote }} + {{- end }} + # Web Settingss + {{- if .Values.maildev.config.web.username }} + - name: MAILDEV_WEB_USER + value: {{ .Values.maildev.config.web.username | quote }} + {{- end }} + {{- if or .Values.maildev.config.web.password .Values.maildev.config.web.existingSecret }} + - name: MAILDEV_WEB_PASS + valueFrom: + secretKeyRef: + name: {{ include "maildev.web.secretName" . }} + key: web-password + {{- end }} + - name: MAILDEV_WEB_PORT + value: {{ .Values.services.web.port | quote }} + {{- if .Values.extraEnv }} + ## Additional Setings + {{- range .Values.extraEnv }} + {{- if and (.name) (.value) }} + - name: {{ .name | quote }} + value: {{ .value | quote }} + {{- end }} + {{- end }} + {{- end }} + ports: + - name: web + containerPort: {{ .Values.services.web.port }} + protocol: TCP + - name: smtp + containerPort: {{ .Values.services.smtp.port }} + protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: web + readinessProbe: + httpGet: + path: /healthz + port: web + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.maildev.config.mailDirectory | quote }} + {{- if .Values.maildev.config.smtp.outgoing.autoRelay.enabled }} + - name: auto-relay-rules + mountPath: /etc/maildev + subPath: auto-relay-rules.json + {{- end }} + {{- if .Values.maildev.config.oauth2_proxy.enabled }} + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 4180 + env: + - name: OAUTH2_PROXY_UPSTREAMS + value: "http://localhost:1080/" + - name: OAUTH2_PROXY_UPSTREAM_TIMEOUT + value: "300s" + - name: OAUTH2_PROXY_SKIP_AUTH_ROUTES + value: "/reloadMailsFromDirectory" + - name: OAUTH2_PROXY_SKIP_PROVIDER_BUTTON + value: "true" + - name: OAUTH2_PROXY_SILENCE_PING_LOGGING + value: "true" + - name: OAUTH2_PROXY_REVERSE_PROXY + value: "true" + - name: OAUTH2_PROXY_HTTP_ADDRESS + value: "0.0.0.0:4180" + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: maildev-app-resource + key: oauth2_proxy_client_secret + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: maildev-app-resource + key: oauth2_proxy_client_id + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: maildev-app-resource + key: oauth2_proxy_cookie_secret + - name: OAUTH2_PROXY_EMAIL_DOMAINS + value: ohio.edu + - name: OAUTH2_PROXY_PROVIDER + value: oidc + - name: OAUTH2_PROXY_OIDC_ISSUER_URL + valueFrom: + secretKeyRef: + name: oauth2-proxy-oidc-issuer-url + key: oauth2_proxy_oidc_issuer_url + - name: OAUTH2_PROXY_SESSION_COOKIE_MINIMAL + value: "true" + volumeMounts: + - mountPath: /var/run/secrets/maildev-app-resource + name: secrets-maildev-app-resource + readOnly: true + + resources: + requests: + cpu: 5m + memory: 50Mi + limits: + cpu: 100m + memory: 250Mi + livenessProbe: + failureThreshold: 3 + httpGet: + path: /ping + port: 4180 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 2 + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ping + port: 4180 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 2 + {{- end }} + volumes: + {{- if .Values.maildev.config.oauth2_proxy.enabled }} + - name: secrets-maildev-app-resource + secret: + secretName: maildev-app-resource + {{- end }} + - name: data + {{- if .Values.maildev.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "maildev.pvcName" . | quote }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.maildev.config.smtp.outgoing.autoRelay.enabled }} + - name: auto-relay-rules + configMap: + name: {{ include "maildev.smtp.outgoing.autoRelay.configMapName" . }} + items: + - key: auto-relay-rules.json + path: auto-relay-rules.json + {{- 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 }} diff --git a/helm/templates/hpa.yaml b/helm/templates/hpa.yaml new file mode 100644 index 0000000..f283b0b --- /dev/null +++ b/helm/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "maildev.fullname" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "maildev.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100644 index 0000000..8126939 --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $svcName := include "maildev.web.serviceName" . -}} +{{- $svcPort := .Values.services.web.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ include "maildev.fullname" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $svcName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $svcName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/templates/persistence/cronjob.yaml b/helm/templates/persistence/cronjob.yaml new file mode 100644 index 0000000..aef372d --- /dev/null +++ b/helm/templates/persistence/cronjob.yaml @@ -0,0 +1,73 @@ +{{- if and .Values.maildev.persistence.enabled .Values.maildev.persistence.cron.enabled }} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "maildev.fullname" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +spec: + schedule: {{ .Values.maildev.persistence.cron.config.schedule }} + startingDeadlineSeconds: 100 + jobTemplate: + spec: + template: + spec: + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - {{ .Release.Name }} + topologyKey: "kubernetes.io/hostname" + containers: + - name: delete-email-files + image: "{{ .Values.maildev.persistence.cron.image.registry }}/{{ .Values.maildev.persistence.cron.image.repository }}:{{ .Values.maildev.persistence.cron.image.tag }}" + imagePullPolicy: {{ .Values.maildev.persistence.cron.image.pullPolicy }} + args: + - "find" + - "{{ .Values.maildev.config.mailDirectory }}" + - "-type" + - "f" + - "-mmin" + - "+{{ .Values.maildev.persistence.cron.config.minutesToKeep }}" + - "-exec" + - "rm" + - "-rf" + - "{}" + - ";" + volumeMounts: + - name: data + mountPath: {{ .Values.maildev.config.mailDirectory | quote }} + - name: delete-email-folders + image: "{{ .Values.maildev.persistence.cron.image.registry }}/{{ .Values.maildev.persistence.cron.image.repository }}:{{ .Values.maildev.persistence.cron.image.tag }}" + imagePullPolicy: {{ .Values.maildev.persistence.cron.image.pullPolicy }} + args: + - "find" + - "{{ .Values.maildev.config.mailDirectory }}" + - "-type" + - "d" + - "-mmin" + - "+{{ .Values.maildev.persistence.cron.config.minutesToKeep }}" + - "-mindepth" + - "1" + - "-exec" + - "rm" + - "-rf" + - "{}" + - ";" + volumeMounts: + - name: data + mountPath: {{ .Values.maildev.config.mailDirectory | quote }} + - name: reload-mail + image: "{{ .Values.maildev.persistence.cron.image.registry }}/{{ .Values.maildev.persistence.cron.image.repository }}:{{ .Values.maildev.persistence.cron.image.tag }}" + imagePullPolicy: {{ .Values.maildev.persistence.cron.image.pullPolicy }} + args: ["wget","http://{{ include "maildev.fullname" . }}-web:{{ .Values.services.web.port}}/reloadMailsFromDirectory"] + volumes: + - name: data + persistentVolumeClaim: + claimName: {{ include "maildev.pvcName" . | quote }} + restartPolicy: OnFailure + {{- end }} diff --git a/helm/templates/persistence/pvc.yaml b/helm/templates/persistence/pvc.yaml new file mode 100644 index 0000000..b8a5b77 --- /dev/null +++ b/helm/templates/persistence/pvc.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.maildev.persistence.enabled (not .Values.maildev.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- if .Values.maildev.persistence.annotations }} + annotations: + {{- toYaml .Values.maildev.persistence.annotations | nindent 4 }} + {{- end }} + name: {{ include "maildev.pvcName" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +spec: + accessModes: + {{- range .Values.maildev.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + {{- with .Values.maildev.persistence.resources }} + resources: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.maildev.persistence.storageClassName }} + storageClassName: {{ .Values.maildev.persistence.storageClassName | quote }} + {{- end }} +{{- end }} diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..07a28f1 --- /dev/null +++ b/helm/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "maildev.serviceAccountName" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/helm/templates/smtp/configmap.yaml b/helm/templates/smtp/configmap.yaml new file mode 100644 index 0000000..b411d6c --- /dev/null +++ b/helm/templates/smtp/configmap.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.maildev.config.smtp.outgoing.autoRelay.enabled .Values.maildev.config.smtp.outgoing.autoRelay.rules }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "maildev.smtp.outgoing.autoRelay.configMapName" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +data: + auto-relay-rules.json: | + {{- toPrettyJson .Values.maildev.config.smtp.outgoing.autoRelay.rules | nindent 4 }} +{{- end }} diff --git a/helm/templates/smtp/incoming-secret.yaml b/helm/templates/smtp/incoming-secret.yaml new file mode 100644 index 0000000..25d8bd6 --- /dev/null +++ b/helm/templates/smtp/incoming-secret.yaml @@ -0,0 +1,8 @@ +{{- if (not .Values.maildev.config.smtp.incoming.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "maildev.smtp.incoming.secretName" . }} +data: + smtp-incoming-password: {{ .Values.maildev.config.smtp.incoming.password | b64enc }} +{{- end }} diff --git a/helm/templates/smtp/outgoing-secret.yaml b/helm/templates/smtp/outgoing-secret.yaml new file mode 100644 index 0000000..f7a5405 --- /dev/null +++ b/helm/templates/smtp/outgoing-secret.yaml @@ -0,0 +1,8 @@ +{{- if (not .Values.maildev.config.smtp.outgoing.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "maildev.smtp.outgoing.secretName" . }} +data: + smtp-outgoing-password: {{ .Values.maildev.config.smtp.outgoing.password | b64enc }} +{{- end }} diff --git a/helm/templates/smtp/service.yaml b/helm/templates/smtp/service.yaml new file mode 100644 index 0000000..daf527c --- /dev/null +++ b/helm/templates/smtp/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "maildev.smtp.serviceName" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }}s +spec: + type: {{ .Values.services.smtp.type }} + ports: + - port: {{ .Values.services.smtp.port }} + targetPort: smtp + protocol: TCP + name: smtp + {{ if and (eq .Values.services.smtp.type "NodePort") .Values.services.smtp.nodePort }} + nodePort: {{ .Values.services.smtp.nodePort }} + {{ end }} + selector: + {{- include "maildev.selectorLabels" . | nindent 4 }} diff --git a/helm/templates/web/secret.yaml b/helm/templates/web/secret.yaml new file mode 100644 index 0000000..2c41f82 --- /dev/null +++ b/helm/templates/web/secret.yaml @@ -0,0 +1,8 @@ +{{- if (not .Values.maildev.config.web.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "maildev.web.secretName" . }} +data: + web-password: {{ .Values.maildev.config.web.password | b64enc }} +{{- end }} diff --git a/helm/templates/web/service.yaml b/helm/templates/web/service.yaml new file mode 100644 index 0000000..4f01ceb --- /dev/null +++ b/helm/templates/web/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "maildev.web.serviceName" . }} + labels: + {{- include "maildev.labels" . | nindent 4 }} +spec: + type: {{ .Values.services.web.type }} + ports: + - port: {{ .Values.services.web.port }} + {{- if .Values.maildev.config.oauth2_proxy.enabled }} + targetPort: 4180 + {{- else }} + targetPort: web + {{- end }} + protocol: TCP + name: web + selector: + {{- include "maildev.selectorLabels" . | nindent 4 }} diff --git a/helm/values.schema.json b/helm/values.schema.json new file mode 100644 index 0000000..6a4266f --- /dev/null +++ b/helm/values.schema.json @@ -0,0 +1,357 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "affinity": { + "type": "object" + }, + "autoscaling": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxReplicas": { + "type": "integer" + }, + "minReplicas": { + "type": "integer" + }, + "targetCPUUtilizationPercentage": { + "type": "integer" + }, + "targetMemoryUtilizationPercentage": { + "type": "integer" + } + } + }, + "extraEnv": { + "type": "array" + }, + "fullnameOverride": { + "type": "string" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "imagePullSecrets": { + "type": "array" + }, + "ingress": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "className": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "pathType": { + "type": "string" + } + } + } + } + } + } + }, + "tls": { + "type": "array" + } + } + }, + "maildev": { + "type": "object", + "properties": { + "config": { + "type": "object", + "properties": { + "https": { + "type": "object", + "properties": { + "cert": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "key": { + "type": "string" + } + } + }, + "logMailContents": { + "type": "boolean" + }, + "mailDirectory": { + "type": "string" + }, + "modes": { + "type": "object", + "properties": { + "silent": { + "type": "boolean" + }, + "verbose": { + "type": "boolean" + } + } + }, + "smtp": { + "type": "object", + "properties": { + "hideExtensions": { + "type": "string" + }, + "incoming": { + "type": "object", + "properties": { + "existingSecret": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "outgoing": { + "type": "object", + "properties": { + "autoRelay": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "mail": { + "type": "string" + }, + "rules": { + "type": "array" + } + } + }, + "existingSecret": { + "type": "string" + }, + "host": { + "type": "string" + }, + "password": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "ssl": { + "type": "boolean" + }, + "username": { + "type": "string" + } + } + } + } + }, + "web": { + "type": "object", + "properties": { + "disabled": { + "type": "boolean" + }, + "existingSecret": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + } + } + }, + "persistence": { + "type": "object", + "properties": { + "accessModes": { + "type": "array", + "items": { + "type": "string" + } + }, + "annotations": { + "type": "object" + }, + "cron": { + "type": "object", + "properties": { + "config": { + "type": "object", + "properties": { + "minutesToKeep": { + "type": "integer" + }, + "schedule": { + "type": "string" + } + } + }, + "enabled": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + }, + "enabled": { + "type": "boolean" + }, + "existingClaim": { + "type": "string" + }, + "resources": { + "type": "object", + "properties": { + "requests": { + "type": "object", + "properties": { + "storage": { + "type": "string" + } + } + } + } + }, + "storageClassName": { + "type": "string" + } + } + } + } + }, + "nameOverride": { + "type": "string" + }, + "nodeSelector": { + "type": "object" + }, + "podAnnotations": { + "type": "object" + }, + "podSecurityContext": { + "type": "object" + }, + "replicaCount": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "revisionHistoryLimit": { + "type": "integer" + }, + "securityContext": { + "type": "object" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "services": { + "type": "object", + "properties": { + "smtp": { + "type": "object", + "properties": { + "nodePort": { + "type": "integer" + }, + "port": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "web": { + "type": "object", + "properties": { + "port": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + } + } + }, + "tolerations": { + "type": "array" + } + } +} diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..c502853 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,224 @@ +# -- Provide a name in place of `maildev` +nameOverride: "" + +# -- String to fully override `"maildev.fullname"` +fullnameOverride: "" + +image: + # -- image registry + registry: docker.io + # -- image repository + repository: maildev/maildev + # -- image pull policy + pullPolicy: Always + # -- Overrides the image tag + tag: "2.1.0" + +# -- If defined, uses a Secret to pull an image from a private Docker registry or repository. +imagePullSecrets: [] + +# -- Annotations to be added to the pods +podAnnotations: {} + +# -- pod-level security context +podSecurityContext: {} +# fsGroup: 2000 + +# -- Number of replicas +replicaCount: 1 + +# -- The number of old ReplicaSets to retain +revisionHistoryLimit: 10 + +# -- Resource limits and requests for the controller pods. +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 + +# -- container-level security context +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true +# runAsNonRoot: true +# runAsUser: 1000 + +serviceAccount: + # -- Specifies whether a service account should be created + create: true + # -- 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: "" + +services: + web: + # -- Kubernetes service type for the web GUI + type: ClusterIP + # -- Kubernetes port to use for the web GUI + port: 1080 + smtp: + # -- Kubernetes service type for the SMTP server + type: ClusterIP + # -- Kubernetes port to use for the internal SMTP server + port: 1025 + # -- You can set the node port for the external SMTP server that should be used or leave it blank to get a random node port. Only active if `services.smtp.type == NodePort` + nodePort: + +ingress: + # -- Enable ingress record generation + enabled: false + # -- IngressClass that will be be used to implement the Ingress + className: "" + # -- Additional annotations for the Ingress resource + annotations: {} + # cert-manager.io/cluster-issuer: cluster-issuer-name + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # -- An array with hosts and paths + # @default -- see [values.yaml](./values.yaml) + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + # -- An array with the tls configuration + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +autoscaling: + # -- Enable Horizontal POD autoscaling + enabled: false + # -- Minimum number of replicas + minReplicas: 1 + # -- Maximum number of replicas + maxReplicas: 100 + # -- Target CPU utilization percentage + targetCPUUtilizationPercentage: 80 + # -- Target Memory utilization percentage + targetMemoryUtilizationPercentage: 80 + +# -- Node labels for pod assignment +nodeSelector: {} + +# -- Toleration labels for pod assignment +tolerations: [] + +# -- Affinity settings for pod assignment +affinity: {} + +# -- additional environment variables to be added to the pods +extraEnv: [] + +maildev: + config: + https: + # -- Switch from http to https protocol + enabled: false + # -- The ssl cert file + cert: "" + # -- The ssl private key + key: "" + # -- Log a JSON representation of each incoming mail + logMailContents: false + # -- Directory for persisting mail + mailDirectory: "/home/node/maildev-data" + modes: + # -- Run maildev in silent mode to disable the startup messages + silent: true + # -- Run maildev in verbose mode + verbose: false + smtp: + # -- Comma separated list of SMTP extensions to NOT advertise (SMTPUTF8, PIPELINING, 8BITMIME) + hideExtensions: "" + incoming: + # -- Name of an existing secret containing the password for the incoming SMTP mail. If set `maildev.config.smtp.incoming.password` will be ignored. Key needs to be `smtp-incoming-password`. + existingSecret: "" + # -- SMTP password for incoming mail + password: "" + # -- SMTP user for incoming mail + username: "" + outgoing: + autoRelay: + # -- Enabling the auto relay mode will automatically send each email to it's recipient without the need to click the "Relay" button. The outgoing email options are required to enable this feature. + enabled: false + # -- Optionally define a single email address which Maildev will forward all emails to instead of the original recipient + mail: "" + # -- The additional configuration for what email addresses you would like to allow or deny. + rules: [] + # [ + # { "allow": "*" }, + # { "deny": "*@test.com" }, + # { "allow": "ok@test.com" }, + # { "deny": "*@utah.com" }, + # { "allow": "johnny@utah.com" } + # ] + # -- Name of an existing secret containing the password for the outgoing SMTP mail. If set `maildev.config.smtp.outgoing.password` will be ignored. Key needs to be `smtp-outgoing-password`. + existingSecret: "" + # -- SMTP host for outgoing mail + host: "" + # -- SMTP password for outgoing mail + password: "" + # -- SMTP port for outgoing mail + port: 25 + # -- Use SMTP SSL for outgoing mail + ssl: false + # -- SMTP user for outgoing mail + username: "" + web: + # -- Disable the use of the web interface. Useful for unit testing + disabled: false + # -- Name of an existing secret containing the password for the GUI. If set `maildev.config.web.password` will be ignored. Key needs to be `web-password`. + existingSecret: "" + # -- Password for the GUI + password: "" + # -- Username for the GUI + username: "" + oauth2_proxy: + enabled: false + persistence: + # -- Use a PVC to persist data + enabled: false + # -- The desired access modes the volume should have. + accessModes: + - ReadWriteOnce + # -- Annotations to be added to the PersistentVolumeClaim + annotations: {} + # To clean some old mails when persistence is enabled + cron: + # -- Enable the creation of a cronjob to periodically delete old emails + enabled: false + config: + # -- How old emails may be before they get deleted + minutesToKeep: 1440 + # -- The schedule on which the cronjob should run + schedule: "0 1 * * *" + image: + # -- Image registry + registry: docker.io + # -- Image repository + repository: busybox + # -- Image pull policy + pullPolicy: Always + # -- Overrides the image tag + tag: latest + # -- Provide an existing PersistentVolumeClaim + existingClaim: "" + # -- Name of the StorageClass required by the claim. + storageClassName: "" + # -- Represents the minimum and maximum resources the volume should have. + resources: + requests: + storage: 2Gi \ No newline at end of file