提交 cb026d9b 编写于 作者: T TommyLike

Support local development

上级 540ed7f5
......@@ -3,7 +3,9 @@ FROM ubuntu:14.04
LABEL version="1.0.0"
LABEL maintainer="tommylikehu@gmail.com"
ENV TINI_VERSION v0.14.0
COPY docker-entrypoint.sh /usr/local/bin/
ENV TINI_VERSION v0.18.0
RUN set -x \
&& apt-get update \
&& apt-get install -y ca-certificates curl --no-install-recommends \
......@@ -24,4 +26,7 @@ ADD update-exim4.conf.conf /etc/exim4/
RUN sudo update-exim4.conf
EXPOSE 25
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["tini", "--", "exim", "-bd", "-v"]
\ No newline at end of file
#! /bin/bash
set -e
#Update exim4 service first
sudo update-exim4.conf
exec $@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014-2019 by the Free Software Foundation, Inc.
#
# This file is part of HyperKitty.
#
# HyperKitty is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# HyperKitty is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# HyperKitty. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Aurelien Bompard <abompard@fedoraproject.org>
#
import json
from email import message_from_binary_file
from email.message import EmailMessage
from email.policy import default
from functools import wraps
from django.conf import settings
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
from django.urls import reverse
from django.http import HttpResponse
from django.utils.http import urlunquote
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django_mailman3.models import MailDomain
from urllib.parse import urljoin
from hyperkitty.lib.incoming import add_to_list, DuplicateMessage
from hyperkitty.lib.utils import get_message_id_hash
import logging
logger = logging.getLogger(__name__)
def key_and_ip_auth(func):
@wraps(func)
def _decorator(request, *args, **kwargs):
for attr in ('MAILMAN_ARCHIVER_KEY', 'MAILMAN_ARCHIVER_FROM'):
if not hasattr(settings, attr):
msg = "Missing setting: %s" % attr
logger.error(msg)
raise ImproperlyConfigured(msg)
# if (request.META.get("REMOTE_ADDR") not in
# settings.MAILMAN_ARCHIVER_FROM):
# logger.error(
# "Access to the archiving API endpoint was forbidden from "
# "IP {}, your MAILMAN_ARCHIVER_FROM setting may be "
# "misconfigured".format(request.META["REMOTE_ADDR"]))
# return HttpResponse(
# """<html><title>Forbidden</title><body>
# <h1>Access is forbidden</h1></body></html>""",
# content_type="text/html", status=403)
if request.GET.get("key") != settings.MAILMAN_ARCHIVER_KEY:
return HttpResponse(
"""<html><title>Auth required</title><body>
<h1>Authorization Required</h1></body></html>""",
content_type="text/html", status=401)
return func(request, *args, **kwargs)
return _decorator
def _get_url(mlist_fqdn, msg_id=None):
# We can't use HttpRequest.build_absolute_uri() because the mailman API may
# be accessed via localhost.
# https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.build_absolute_uri
# https://docs.djangoproject.com/en/dev/ref/contrib/sites/#getting-the-current-domain-for-full-urls
# result = urljoin(public_url, urlunquote(
# reverse('hk_list_overview', args=[mlist_fqdn])))
# We use the MailDomain association from django_mailman3 to find out the
# proper domain.
if msg_id is None:
url = reverse('hk_list_overview', args=[mlist_fqdn])
else:
msg_hash = get_message_id_hash(msg_id.strip().strip("<>"))
url = reverse('hk_message_index', kwargs={
"mlist_fqdn": mlist_fqdn, "message_id_hash": msg_hash})
relative_url = urlunquote(url)
mail_domain = mlist_fqdn.split("@")[1]
try:
domain = MailDomain.objects.get(
mail_domain=mail_domain).site.domain
except MailDomain.DoesNotExist:
domain = mail_domain
return urljoin("https://%s" % domain, relative_url)
@key_and_ip_auth
def urls(request):
result = _get_url(request.GET["mlist"], request.GET.get("msgid"))
return HttpResponse(json.dumps({"url": result}),
content_type='application/javascript')
@require_POST
@key_and_ip_auth
@csrf_exempt
def archive(request):
mlist_fqdn = request.POST["mlist"]
if "message" not in request.FILES:
raise SuspiciousOperation
msg = message_from_binary_file(
request.FILES['message'], _class=EmailMessage, policy=default)
try:
add_to_list(mlist_fqdn, msg)
except DuplicateMessage as e:
logger.info("Duplicate email with message-id '%s'", e.args[0])
except ValueError as e:
logger.warning("Could not archive the email with message-id '%s': %s",
msg.get("Message-Id", None), e)
return HttpResponse(json.dumps({"error": str(e)}),
content_type='application/javascript')
url = _get_url(mlist_fqdn, msg['Message-Id'])
logger.info("Archived message %s to %s", msg['Message-Id'], url)
return HttpResponse(json.dumps({"url": url}),
content_type='application/javascript')
\ No newline at end of file
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
# 1 control plane node and 3 workers
nodes:
# the control plane node config
- role: control-plane
# the three workers
- role: worker
# The node mapping used to export mailman website and exim4 service
extraPortMappings:
- containerPort: 30000
hostPort: 8000
- containerPort: 30080
hostPort: 8080
- containerPort: 30025
hostPort: 25
- role: worker
- role: worker
#!/bin/bash
export CURRENT_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
if [[ "${CLUSTER_NAME}xxx" == "xxx" ]];then
CLUSTER_NAME="integration"
fi
export CLUSTER_CONTEXT="--name ${CLUSTER_NAME}"
# clean up
function cleanup {
echo "Uninstall mailman services"
kubectl delete -f ${CURRENT_ROOT}/mailman-with-postgres.yaml
echo "Deleting helm services"
helm delete mailman-nfs
# echo "Uninstall nfs common utils into kind nodes"
# NODES=$(kind get nodes --name ${CLUSTER_NAME})
# NODES_ARY=($NODES)
# for key in "${!NODES_ARY[@]}"
# do
# echo "starting to patch node ${NODES_ARY[$key]} and uninstall nfs-common utils"
# docker exec -it ${NODES_ARY[$key]} bin/bash -c "apt remove nfs-common -y"
# done
echo "Running kind: [kind delete cluster ${CLUSTER_CONTEXT}]"
kind delete cluster ${CLUSTER_CONTEXT}
}
export KUBECONFIG="$(kind get kubeconfig-path ${CLUSTER_CONTEXT})"
cleanup
#!/bin/bash
export CURRENT_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
export LOG_LEVEL=3
export CLEANUP_CLUSTER=${CLEANUP_CLUSTER:-0}
if [[ "${CLUSTER_NAME}xxx" == "xxx" ]];then
CLUSTER_NAME="integration"
fi
export CLUSTER_CONTEXT="--name ${CLUSTER_NAME}"
export KIND_OPT=${KIND_OPT:=" --config ${CURRENT_ROOT}/kind-config.yaml"}
# spin up cluster with kind command
function kind-up-cluster {
check-prerequisites
check-kind
echo "Running kind: [kind create cluster ${CLUSTER_CONTEXT} ${KIND_OPT}]"
kind create cluster ${CLUSTER_CONTEXT} ${KIND_OPT}
}
# install helm if not installed
function install-helm {
echo "checking helm"
which helm >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "Install helm via script"
HELM_TEMP_DIR=`mktemp -d`
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get > ${HELM_TEMP_DIR}/get_helm.sh
#TODO: There are some issue with helm's latest version, remove '--version' when it get fixed.
chmod 700 ${HELM_TEMP_DIR}/get_helm.sh && ${HELM_TEMP_DIR}/get_helm.sh --version v2.13.0
else
echo -n "found helm, version: " && helm version
fi
}
# check if kubectl installed
function check-prerequisites {
echo "checking prerequisites"
which kubectl >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "kubectl not installed, exiting."
exit 1
else
echo -n "found kubectl, " && kubectl version --short --client
fi
}
# check if kind installed
function check-kind {
echo "checking kind"
which kind >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "installing kind ."
GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0
else
echo -n "found kind, version: " && kind version
fi
}
function install-mailman-service {
echo "installing helm service"
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
install-helm
helm init --service-account tiller --kubeconfig ${KUBECONFIG} --wait
echo "Install nfs common utils into kind nodes"
NODES=$(kind get nodes --name ${CLUSTER_NAME})
NODES_ARY=($NODES)
for key in "${!NODES_ARY[@]}"
do
echo "starting to patch node ${NODES_ARY[$key]} and install nfs-common utils"
docker exec -it ${NODES_ARY[$key]} bin/bash -c "apt update && apt install nfs-common -y"
done
echo "Install nfs provisioner"
kubectl create clusterrolebinding default-cluster-rule --clusterrole=cluster-admin --serviceaccount=default:default
helm repo add cloudposse-incubator https://charts.cloudposse.com/incubator
helm install --name mailman-nfs cloudposse-incubator/nfs-provisioner --set persistence.storageClass=standard --set persistence.size=1Gi --wait
echo "Install mailman services"
kubectl apply -f ${CURRENT_ROOT}/mailman-with-postgres.yaml
}
# clean up
function cleanup {
echo "Running kind: [kind delete cluster ${CLUSTER_CONTEXT}]"
kind delete cluster ${CLUSTER_CONTEXT}
}
echo $* | grep -E -q "\-\-help|\-h"
if [[ $? -eq 0 ]]; then
echo "Customize the kind-cluster name:
export CLUSTER_NAME=<custom cluster name> # default: integration
Customize kind options other than --name:
export KIND_OPT=<kind options>
"
exit 0
fi
if [[ $CLEANUP_CLUSTER -eq 1 ]]; then
trap cleanup EXIT
fi
kind-up-cluster
export KUBECONFIG="$(kind get kubeconfig-path ${CLUSTER_CONTEXT})"
install-mailman-service
#Deployment for mailman suit services
# PVC (nfs share) used for mailman-core and mailman MTA service
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: core-and-mta-volume
annotations:
volume.beta.kubernetes.io/storage-class: "local-nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: local-nfs
# Headless Service for mailman suit service
---
apiVersion: v1
kind: Service
metadata:
name: mail-suit-service
labels:
app: mail-suit-service
spec:
selector:
app: mail-suit-service
clusterIP: None
# StatefulSet for mail core service
---
kind: StatefulSet
apiVersion: apps/v1beta1
metadata:
name: mailman-core
namespace: default
labels:
app: mail-suit-service
spec:
serviceName: mail-suit-service
replicas: 1
selector:
matchLabels:
app: mail-suit-service
template:
metadata:
labels:
app: mail-suit-service
spec:
containers:
- name: mailman-core
image: maxking/mailman-core:0.2.3
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /opt/mailman/
name: mailman-core-volume
env:
- name: DATABASE_URL
value: postgres://mailman:mailmanpass@mailman-database-0.mail-suit-service.default.svc.cluster.local/mailmandb
- name: DATABASE_TYPE
value: postgres
- name: DATABASE_CLASS
value: mailman.database.postgresql.PostgreSQLDatabase
# NOTE: Please update the HYPERKITTY_API_KEY
- name: HYPERKITTY_API_KEY
value: someapikey
# NOTE: Please update the HYPERKITTY_URL
- name: HYPERKITTY_URL
value: http://mail-web-service.default.svc.cluster.local:8000/hyperkitty
# NOTE: We must use the cluster service here, since NodePort or NodeBalance will SNAT client address
- name: SMTP_HOST
value: mailman-exim4-0.mail-suit-service.default.svc.cluster.local
volumes:
- name: mailman-core-volume
persistentVolumeClaim:
claimName: core-and-mta-volume
# StatefulSet for postgres database service
---
kind: StatefulSet
apiVersion: apps/v1beta1
metadata:
name: mailman-database
namespace: default
labels:
app: mail-suit-service
spec:
serviceName: mail-suit-service
replicas: 1
selector:
matchLabels:
app: mail-suit-service
template:
metadata:
labels:
app: mail-suit-service
spec:
containers:
- name: mailman-database
image: postgres:9.6-alpine
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: mailman-database-volume
env:
- name: POSTGRES_DB
value: mailmandb
- name: POSTGRES_USER
value: mailman
- name: POSTGRES_PASSWORD
value: mailmanpass
#NOTE: Empty dir can't be used in a production dir. Please upgrade it before using.
volumes:
- name: mailman-database-volume
emptyDir: {}
# configmap for mail exim4 service, these three files are directly read from exim config folder
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mailman-exim4-configmap
namespace: default
data:
25_mm3_macros: |
# Place this file at
# /etc/exim4/conf.d/main/25_mm3_macros
domainlist mm3_domains=tommylike.me
MM3_LMTP_HOST=mailman-core-0.mail-suit-service.default.svc.cluster.local
MM3_LMTP_PORT=8024
# According to the configuration of: https://mailman.readthedocs.io/en/release-3.0/src/mailman/docs/MTA.html
# We need updating this, for the purpose of delivering emails to the mailman
MM3_HOME=/opt/mailman/var
################################################################
# The configuration below is boilerplate:
# you should not need to change it.
# The path to the list receipt (used as the required file when
# matching list addresses)
MM3_LISTCHK=MM3_HOME/lists/${local_part}.${domain}
55_mm3_transport: |
# Place this file at
# /etc/exim4/conf.d/transport/55_mm3_transport
mailman3_transport:
debug_print = "Email for mailman"
driver = smtp
protocol = lmtp
allow_localhost
hosts = MM3_LMTP_HOST
port = MM3_LMTP_PORT
rcpt_include_affixes = true
455_mm3_router: |
# Place this file at
# /etc/exim4/conf.d/router/455_mm3_router
mailman3_router:
driver = accept
domains = +mm3_domains
require_files = MM3_LISTCHK
local_part_suffix_optional
local_part_suffix = -admin : \
-bounces : -bounces+* : \
-confirm : -confirm+* : \
-join : -leave : \
-owner : -request : \
-subscribe : -unsubscribe
transport = mailman3_transport
update-exim4-conf.conf: |
dc_eximconfig_configtype='internet'
dc_other_hostnames='tommylike.me;'
dc_local_interfaces=''
dc_readhost=''
# NOTE: wildchart is used here, but it's not safe at all.
dc_relay_domains='*'
dc_minimaldns='false'
# NOTE: wildchart is used here, but it's not safe at all.
dc_relay_nets='*'
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'
#Service for exim4 pods to export Port 25 via NodePort
---
apiVersion: v1
kind: Service
metadata:
name: mail-exim4-service
labels:
app: mail-suit-service
spec:
type: NodePort
ports:
- port: 25
name: exim4-port
nodePort: 30025
selector:
component: mail-exim4-service
# StatefulSet for exim4 services
---
kind: StatefulSet
apiVersion: apps/v1beta1
metadata:
name: mailman-exim4
namespace: default
labels:
app: mail-suit-service
component: mail-exim4-service
spec:
serviceName: mail-suit-service
replicas: 1
selector:
matchLabels:
app: mail-suit-service
component: mail-exim4-service
template:
metadata:
labels:
app: mail-suit-service
component: mail-exim4-service
spec:
containers:
- name: mailman-exim4
#NOTE: This image is directly built from our dockerfile located in exim4 folder
image: tommylike/mailman-exim4:0.0.1
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /etc/exim4/conf.d/main/25_mm3_macros
name: mailman-exim4-configmap-volume
subPath: 25_mm3_macros
- mountPath: /etc/exim4/conf.d/transport/55_mm3_transport
name: mailman-exim4-configmap-volume
subPath: 55_mm3_transport
- mountPath: /etc/exim4/conf.d/router/455_mm3_router
name: mailman-exim4-configmap-volume
subPath: 455_mm3_router
- mountPath: /etc/exim4/update-exim4.conf.conf
name: mailman-exim4-configmap-volume
subPath: update-exim4-conf.conf
- mountPath: /opt/mailman/
name: mailman-exim4-volume
#NOTE: Empty dir can't be used in a production dir. Please upgrade it before using.
volumes:
- name: mailman-exim4-configmap-volume
configMap:
name: mailman-exim4-configmap
- name: mailman-exim4-volume
persistentVolumeClaim:
claimName: core-and-mta-volume
# Service for mail web service to export NodePort
---
apiVersion: v1
kind: Service
metadata:
name: mail-web-service
labels:
app: mail-suit-service
spec:
type: NodePort
ports:
- port: 8080
name: website-port-uwsgi
nodePort: 30080
- port: 8000
name: website-port-http
nodePort: 30000
selector:
component: mail-web-service
# configmap for mail web service
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mailman-web-configmap
namespace: default
data:
settings_local.py: |
import os
import socket
DEBUG = True
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
#NOTE: this is the MTA host, we need to update it.
EMAIL_HOST = 'mailman-exim4-0.mail-suit-service.default.svc.cluster.local'
EMAIL_PORT = 25
MAILMAN_ARCHIVER_FROM = socket.gethostbyname(os.environ.get('MAILMAN_HOST_IP'))
ALLOWED_HOSTS = [
"localhost", # Archiving API from Mailman, keep it.
# Add here all production URLs you may have.
"mailman-database-0.mail-suit-service.default.svc.cluster.local",
"mailman-core-0.mail-suit-service.default.svc.cluster.local",
"mailman-web-0.mail-suit-service.default.svc.cluster.local",
"mail-web-service.default.svc.cluster.local",
#NOTE: This is the public ip address of the served host
"159.138.26.163",
"tommylike.me",
os.environ.get('SERVE_FROM_DOMAIN'),
os.environ.get('DJANGO_ALLOWED_HOSTS'),
]
# Deployment for mail web service
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: mailman-web
namespace: default
labels:
component: mail-web-service
app: mail-suit-service
spec:
replicas: 2
selector:
matchLabels:
component: mail-web-service
app: mail-suit-service
template:
metadata:
labels:
component: mail-web-service
app: mail-suit-service
spec:
hostname: mailman-web
containers:
- name: mailman-web
# We modified the mail-web image to add static folder.
image: tommylike/mailman-web:0.2.3
imagePullPolicy: "IfNotPresent"
volumeMounts:
- mountPath: /opt/mailman-web-config
name: mailman-web-configmap-volume
- mountPath: /opt/mailman-web-data
name: mailman-web-volume
env:
- name: DATABASE_TYPE
value: postgres
- name: DATABASE_URL
value: postgres://mailman:mailmanpass@mailman-database-0.mail-suit-service.default.svc.cluster.local/mailmandb
- name: HYPERKITTY_API_KEY
# NOTE: Please update the HYPERKITTY_API_KEY
value: someapikey
- name: SECRET_KEY
# NOTE: Please update the SECRET_KEY
value: community_key
- name: UWSGI_STATIC_MAP
# NOTE: This static folder has been added into docker image located at /opt/mailman-web/static
value: /static=/opt/mailman-web-data/static
- name: MAILMAN_REST_URL
value: http://mailman-core-0.mail-suit-service.default.svc.cluster.local:8001
- name: MAILMAN_HOST_IP
value: mailman-core-0.mail-suit-service.default.svc.cluster.local
- name: MAILMAN_ADMIN_USER
value: tommylike
- name: MAILMAN_ADMIN_EMAIL
value: tommylikehu@gmail.com
#NOTE: this is the domain name that mailman web will serve
- name: SERVE_FROM_DOMAIN
value: tommylike.me
#NOTE: Command is overwritten for the purpose of copy config file into dest folder
command:
- /bin/sh
- -c
- |
cp /opt/mailman-web-config/settings_local.py /opt/mailman-web-data;
docker-entrypoint.sh uwsgi --ini /opt/mailman-web/uwsgi.ini;
#NOTE: Empty dir can't be used in a production dir. Please upgrade it before using.
volumes:
- name: mailman-web-volume
emptyDir: {}
- name: mailman-web-configmap-volume
configMap:
name: mailman-web-configmap
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册