added healthz endpoint, misc improvements and docs review

update

use default templates where there are not user-specific settings

improved Dockerfile

renamed healthz endoint

delete deprecated file

fixed healthz endpoint

improved build and deployment

updated docs
This commit is contained in:
Rodrigue Chakode 2020-05-23 23:25:12 +02:00
parent fa73d0a6f8
commit 250b9aeb03
12 changed files with 91 additions and 130 deletions

View File

@ -1,4 +1,4 @@
FROM alpine:3.9.6
FROM alpine:3.11.6
ARG RUNTIME_USER="mxgateway"
ARG RUNTIME_USER_UID=4583
@ -7,11 +7,8 @@ RUN addgroup -g $RUNTIME_USER_UID $RUNTIME_USER && \
adduser --disabled-password --no-create-home --gecos "" \
--home /app --ingroup $RUNTIME_USER --uid $RUNTIME_USER_UID $RUNTIME_USER
COPY entrypoint.sh \
bin/hugo-mx-gateway \
templates \
LICENSE \
/app/
COPY entrypoint.sh bin/hugo-mx-gateway LICENSE /app/
COPY templates /app/templates
RUN chown -R $RUNTIME_USER:$RUNTIME_USER /app

View File

@ -3,6 +3,7 @@ DOCKER_IMAGE_REPO=rchakode/hugo-mx-gateway
ARCH=$$(uname -m)
GOCMD=GO111MODULE=on go
GOBUILD=$(GOCMD) build
GOBUILD_FLAGS=-a -tags netgo -ldflags '-w -extldflags "-static"'
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOVENDOR=$(GOCMD) mod vendor
@ -16,7 +17,7 @@ vendor:
$(GOVENDOR)
build:
$(GOBUILD) -o $(PACKAGE_NAME) -v
$(GOBUILD) $(GOBUILD_FLAGS) -o $(PACKAGE_NAME) -v
build-ci:
docker run --rm -it \

View File

@ -46,14 +46,16 @@ This screenshot show an example of a form successfully submitted and handled by
# Configuration variables
According to your deployment approach (Google App Engine, Kubernetes or Docker), you must provide the following configuration parameters as environment variables:
* `SMTP_SERVER_ADDR`: set the IP or the hostname of the SMTP server. Currently, it's required that the SMTP server being supporting TLS.
* `SMTP_SERVER_ADDR`: Set the address of the SMTP server in the form of `host:port`. It's required that the SMTP server being supporting TLS.
* `SMTP_VERITY_CERT`: Tell if the `hugo-mx-gateway` App should validate the SMTP certificate against valid authorities. If you're using a self-signed certificate on the SMTP server, this value must be set to `false`.
* `SMTP_CLIENT_USERNAME`: Set the username to connect to the SMTP server.
* `SMTP_CLIENT_PASSWORD`: Set the password to connect to the SMTP server.
* `CONTACT_REPLY_EMAIL`: Set an email address for the reply email. It's not necessary a valid email address, for example if don't want the user to reply you can use something like `noreply@example.com`.
* `CONTACT_REPLY_BCC_EMAIL`: Set an email address for bcc copy of the email sent to the user. This is useful for tracking and follow up.
* `CONTACT_REPLY_BCC_EMAIL`: Sets an email address for bcc copy of the email sent to the user. This is useful for tracking and follow up.
* `DEMO_URL`: Specific for demo forms, it can be used to set the URL of the demo site that will be included to the user reply email (e.g. `https://demo.example.com/`).
* `ALLOWED_ORIGINS`: Set a list of comma-separated domains that the `hugo-mx-gateway` App shoudl trust. This is for security reason to filter requests. Only requests with an `Origin` header belonging to the defined origins will be accepted, through it's only required that the request has a valid `Referer` header. It's expected in the future to these request filtering and admission rules.
* `TEMPLATE_DEMO_REQUEST_REPLY`: Specify the path of the email template to reply to demo requests. The default templare used in described in the file `templates/template_reply_demo_request.html`
* `TEMPLATE_CONTACT_REQUEST_REPLY`: Specify the path of the email template to reply to contact requests. The default templare used in described in the file `templates/template_reply_contact_request.html`.
# Deployment options
@ -95,7 +97,7 @@ Either way, check the [values.yaml](./helm/values.yaml) file to set the [configu
> **Security Context:**
> `hugo-mx-gateway`'s pod is deployed with a unprivileged security context by default. However, if needed, it's possible to launch the pod in privileged mode by setting the Helm configuration value `securityContext.enabled` to `false`.
In the next deployment commands, it's assumed that the target namespace `hugo-mx-gateway` do exist. If not create it first, or, alternatively, adapt the commands to use any other namespace of your choice.
In the next deployment commands, it's assumed that the target namespace `hugo-mx-gateway` does exist. Otherwise create it first, or, alternatively, adapt the commands to use any other namespace of your choice.
### Installation using Helm 3 (i.e. without tiller)
<a name="installation-using-helm-3-ie-without-tiller"></a>
@ -104,11 +106,8 @@ Helm 3 does not longer require to have [`tiller`](https://v2.helm.sh/docs/instal
As a consequence the below command shall work with a fresh installation of `hugo-mx-gateway` or a former version installed with Helm 3. There is a [known issue](https://github.com/helm/helm/issues/6850) when there is already a version **not** installed with Helm 3.
```bash
helm upgrade \
--namespace hugo-mx-gateway \
--install hugo-mx-gateway \
helm/hugo-mx-gateway/
```
helm upgrade --namespace hugo-mx-gateway --install hugo-mx-gateway helm/
```
### Installation using Kubectl
@ -116,10 +115,7 @@ helm upgrade \
This approach requires to have the Helm client (version 2 or 3) installed to generate a raw template for kubectl.
```
$ helm template \
--namespace hugo-mx-gateway \
--name hugo-mx-gateway \
helm/hugo-mx-gateway/ | kubectl apply -f -
$ helm template hugo-mx-gateway --namespace hugo-mx-gateway helm/ | kubectl apply -f -
```
## Deployment on Docker
@ -129,7 +125,7 @@ $ helm template \
$ docker run -d \
--publish 8080:8080 \
--name 'hugo-mx-gateway' \
-e SMTP_SERVER_ADDR="smtp.mailgun.org:587" \
-e SMTP_SERVER_ADDR="smtp.example.com:465" \
-e SMTP_VERITY_CERT=true \
-e SMTP_CLIENT_USERNAME="postmaster@example.com" \
-e SMTP_CLIENT_PASSWORD="postmasterSecretPassWord" \

View File

@ -16,4 +16,4 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the #
# specific language governing permissions and limitations under the License. #
/app/hugo-mx-gateway
cd /app && ./hugo-mx-gateway

28
healthz.go Normal file
View File

@ -0,0 +1,28 @@
/*
Copyright 2020 Rodrigue Chakode and contributors.
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.
*/
package main
import (
"net/http"
)
// Healthz performs health check
func Healthz(httpResp http.ResponseWriter, httpReq *http.Request) {
httpResp.WriteHeader(http.StatusOK)
httpResp.Header().Set("Content-Type", "application/json; charset=UTF-8")
httpResp.Write([]byte(`{"status": "ok"}`))
}

View File

@ -2,5 +2,5 @@ apiVersion: v2
name: hugo-mx-gateway
description: Helm chart for hugo-mx-gateway
type: application
version: 0.1.0
appVersion: 1.16.0
version: 1.0.0
appVersion: 1590272682

View File

@ -36,15 +36,17 @@ spec:
{{- end }}
ports:
- name: http
containerPort: 80
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /
path: /healthz
port: http
initialDelaySeconds: 2
periodSeconds: 60
readinessProbe:
httpGet:
path: /
path: /healthz
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}

View File

@ -8,7 +8,7 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
targetPort: 8080
protocol: TCP
name: http
selector:

View File

@ -8,7 +8,7 @@ envs:
ALLOWED_ORIGINS: "127.0.0.1,example.com"
CONTACT_REPLY_EMAIL: "noreply@example.com"
CONTACT_REPLY_CC_EMAIL: "contact@example.com"
DEMO_URL: "https://demo.example.com/"
DEMO_URL: "https://demo.example.com/"
image:
repository: rchakode/hugo-mx-gateway
@ -28,15 +28,15 @@ serviceAccount:
name:
podSecurityContext: {}
# fsGroup: 2000
# fsGroup: 4583
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 4583
service:
type: ClusterIP

39
main.go
View File

@ -4,7 +4,7 @@ Copyright 2020 Rodrigue Chakode and contributors.
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
@ -18,12 +18,12 @@ package main
import (
"net/http"
"time"
"os"
"time"
"github.com/spf13/viper"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
type Route struct {
@ -36,29 +36,34 @@ type Route struct {
type Routes []Route
var routes = Routes{
Route {
Route{
"SendMail",
"POST",
"/sendmail",
SendMail,
},
Route{
"Healthz",
"GET",
"/healthz",
Healthz,
},
}
func MuxLoggerHandler(inner http.Handler, name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner.ServeHTTP(w, r)
log.Printf(
"%s %s %s %s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
})
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner.ServeHTTP(w, r)
log.Printf(
"%s %s %s %s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
})
}
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
@ -99,5 +104,3 @@ func main() {
log.Fatal(http.ListenAndServe(":"+port, router))
}

View File

@ -224,12 +224,19 @@ func SendMail(httpResp http.ResponseWriter, httpReq *http.Request) {
contactRequest.Subject,
)
err := error(nil)
replyTplFile := ""
if contactRequest.RequestTarget == "demo" {
err = sendMailReq.ParseTemplate(viper.GetString("TEMPLATE_DEMO_REQUEST_REPLY"), templateData)
replyTplFile = viper.GetString("TEMPLATE_DEMO_REQUEST_REPLY");
if replyTplFile == "" {
replyTplFile = "./templates/template_reply_demo_request.html"
}
} else {
err = sendMailReq.ParseTemplate(viper.GetString("TEMPLATE_CONTACT_REQUEST_REPLY"), templateData)
replyTplFile = viper.GetString("TEMPLATE_CONTACT_REQUEST_REPLY");
if replyTplFile == "" {
replyTplFile = "./templates/template_reply_contact_request.html"
}
}
err := sendMailReq.ParseTemplate(replyTplFile, templateData)
if err == nil {
err := sendMailReq.Execute()

View File

@ -1,73 +0,0 @@
swagger: "2.0"
info:
description: "Emailer for Hugo contact form"
version: "1.0.0"
title: "Emailer for Hugo contact form"
contact:
email: "rodrigue.chakode at gmail.com"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "mail.example.com"
basePath: "/v1"
tags:
- name: "email"
description: "API to handle email"
schemes:
- "https"
- "http"
paths:
/send:
post:
tags:
- "email"
summary: "Send an email using details provided in the request body"
description: ""
operationId: "sendMail"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Email object containing, the recipient, the subject and the content"
required: true
schema:
$ref: "#/definitions/EmailRequest"
responses:
405:
description: "Invalid input"
500:
description: "Internal server error"
security:
- petstore_auth:
- "send:emails"
securityDefinitions:
petstore_auth:
type: "oauth2"
authorizationUrl: "http://auth.example.com/oauth/dialog"
flow: "implicit"
scopes:
send:emails: "send email"
api_key:
type: "apiKey"
name: "api_key"
in: "header"
definitions:
EmailRequest:
type: "object"
properties:
recipient:
type: "string"
format: "email"
subject:
type: "string"
body:
type: "string"
xml:
name: "Order"
externalDocs:
description: "Learn more"
url: "https://github.com/rchakode/hugo-mx-gateway"