1
0
mirror of https://github.com/axllent/mailpit.git synced 2025-04-11 11:41:58 +02:00

Feature: Add readyz subcommand for Docker healthcheck (#270)

This commit is contained in:
Ralph Slooten 2024-03-31 00:06:25 +13:00
parent 83c70aa7c1
commit a805567810
5 changed files with 86 additions and 2 deletions

View File

@ -25,4 +25,6 @@ RUN apk add --no-cache tzdata
EXPOSE 1025/tcp 1110/tcp 8025/tcp
HEALTHCHECK --interval=15s CMD /mailpit readyz
ENTRYPOINT ["/mailpit"]

75
cmd/readyz.go Normal file
View File

@ -0,0 +1,75 @@
package cmd
import (
"crypto/tls"
"fmt"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/axllent/mailpit/config"
"github.com/spf13/cobra"
)
var (
useHTTPS bool
)
// readyzCmd represents the healthcheck command
var readyzCmd = &cobra.Command{
Use: "readyz",
Short: "Run a healthcheck to test if Mailpit is running",
Long: `This command connects to the /readyz endpoint of a running Mailpit server
and exits with a status of 0 if the connection is successful, else with a
status 1 if unhealthy.
If running within Docker, it should automatically detect environment
settings to determine the HTTP bind interface & port.
`,
Run: func(cmd *cobra.Command, args []string) {
webroot := strings.TrimRight(path.Join("/", config.Webroot, "/"), "/") + "/"
proto := "http"
if useHTTPS {
proto = "https"
}
uri := fmt.Sprintf("%s://%s%sreadyz", proto, config.HTTPListen, webroot)
conf := &http.Transport{
IdleConnTimeout: time.Second * 5,
ExpectContinueTimeout: time.Second * 5,
TLSHandshakeTimeout: time.Second * 5,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: conf}
res, err := client.Get(uri)
if err != nil || res.StatusCode != 200 {
os.Exit(1)
}
},
}
func init() {
rootCmd.AddCommand(readyzCmd)
if len(os.Getenv("MP_UI_BIND_ADDR")) > 0 {
config.HTTPListen = os.Getenv("MP_UI_BIND_ADDR")
}
if len(os.Getenv("MP_WEBROOT")) > 0 {
config.Webroot = os.Getenv("MP_WEBROOT")
}
config.UITLSCert = os.Getenv("MP_UI_TLS_CERT")
if config.UITLSCert != "" {
useHTTPS = true
}
readyzCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "Set the HTTP bind interface & port")
readyzCmd.Flags().StringVar(&config.Webroot, "webroot", config.Webroot, "Set the webroot for web UI & API")
readyzCmd.Flags().BoolVar(&useHTTPS, "https", useHTTPS, "Connect via HTTPS (ignores HTTPS validation)")
}

View File

@ -89,7 +89,7 @@ func init() {
rootCmd.Flags().BoolVarP(&logger.VerboseLogging, "verbose", "v", logger.VerboseLogging, "Verbose logging")
// Web UI / API
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI")
rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface & port for UI")
rootCmd.Flags().StringVar(&config.Webroot, "webroot", config.Webroot, "Set the webroot for web UI & API")
rootCmd.Flags().StringVar(&config.UIAuthFile, "ui-auth-file", config.UIAuthFile, "A password file for web UI & API authentication")
rootCmd.Flags().StringVar(&config.UITLSCert, "ui-tls-cert", config.UITLSCert, "TLS certificate for web UI (HTTPS) - requires ui-tls-key")

View File

@ -115,6 +115,11 @@ func Close() {
}
}
// Ping the database connection and return an error if unsuccessful
func Ping() error {
return db.Ping()
}
// StatsGet returns the total/unread statistics for a mailbox
func StatsGet() MailboxStats {
var (

View File

@ -3,12 +3,14 @@ package handlers
import (
"net/http"
"sync/atomic"
"github.com/axllent/mailpit/internal/storage"
)
// ReadyzHandler is a ready probe that signals k8s to be able to retrieve traffic
func ReadyzHandler(isReady *atomic.Value) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
if isReady == nil || !isReady.Load().(bool) {
if isReady == nil || !isReady.Load().(bool) || storage.Ping() != nil {
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
return
}