bump to go 1.20 + fix some code issues
This commit is contained in:
parent
c1551be07f
commit
2bc22b637d
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,4 @@
|
||||
mailsrv
|
||||
builds
|
||||
outbox
|
||||
|
||||
*.ini
|
||||
|
||||
32
Makefile
32
Makefile
@ -1,26 +1,20 @@
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
fmt:
|
||||
.DEFAULT_GOAL := run
|
||||
|
||||
BIN_NAME := mailsrv
|
||||
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
BUILD_DIR := $(ROOT_DIR)builds
|
||||
|
||||
build: format lint
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@go build -o $(BUILD_DIR)/$(BIN_NAME) main.go
|
||||
|
||||
format:
|
||||
go fmt ./...
|
||||
.PHONY:fmt
|
||||
|
||||
lint: fmt
|
||||
golint ./...
|
||||
.PHONY:lint
|
||||
|
||||
vet: fmt
|
||||
go vet ./...
|
||||
shadow ./...
|
||||
.PHONY:vet
|
||||
|
||||
build:
|
||||
go build -o mailsrv
|
||||
.PHONY:build
|
||||
|
||||
build-check: vet
|
||||
go build -o mailsrv
|
||||
.PHONY:build
|
||||
lint:
|
||||
golangci-lint run --fix
|
||||
|
||||
test:
|
||||
go test ./...
|
||||
.PHONY:test
|
||||
|
||||
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module mailsrv
|
||||
|
||||
go 1.17
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.3.2
|
||||
|
||||
8
go.sum
8
go.sum
@ -5,7 +5,6 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8V
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
@ -18,13 +17,8 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -34,6 +28,4 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -3,11 +3,12 @@ package mail
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Email struct {
|
||||
Path string
|
||||
Sender string `json:"sender"`
|
||||
Receivers []string `json:"receivers"`
|
||||
Subject string `json:"subject"`
|
||||
@ -26,9 +27,9 @@ func NewEmail(sender string, receivers []string, subject, content string) Email
|
||||
func FromJSON(path string) (Email, error) {
|
||||
var mail Email
|
||||
|
||||
content, err := ioutil.ReadFile(path)
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return mail, err
|
||||
return mail, fmt.Errorf("%w, unable to read the file: %s", err, path)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(content, &mail); err != nil {
|
||||
|
||||
@ -51,14 +51,14 @@ func (s Sender) SendMail(email mail.Email) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// watchOutbox reads the `outbox` directory every `TickInterval` and put JSON format e-mail in the queue
|
||||
// watchOutbox reads the `outbox` directory every `TickInterval` and put JSON format e-mail in the queue.
|
||||
func (s Sender) watchOutbox() {
|
||||
log.Info().Str("outbox", s.outboxPath).Msg("start watching outbox directory")
|
||||
|
||||
ticker := time.NewTicker(TickerInterval)
|
||||
|
||||
go func() {
|
||||
for _ = range ticker.C {
|
||||
for range ticker.C {
|
||||
log.Debug().Str("action", "retrieving json e-mail format...").Str("path", s.outboxPath)
|
||||
|
||||
files, err := os.ReadDir(s.outboxPath)
|
||||
@ -69,18 +69,34 @@ func (s Sender) watchOutbox() {
|
||||
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
if strings.HasSuffix(filename, JSONSuffix) {
|
||||
s.queue.Add(path.Join(s.outboxPath, filename))
|
||||
if !strings.HasSuffix(filename, JSONSuffix) {
|
||||
log.Debug().Str("filename", filename).Msg("incorrect suffix")
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug().Str("filename", filename).Msg("incorrect suffix")
|
||||
path := path.Join(s.outboxPath, filename)
|
||||
email, err := mail.FromJSON(path)
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Str("path", path).Msg("unable to parse JSON email")
|
||||
|
||||
// if JSON parsing failed the `path` is renamed with an error suffix to not watch it again
|
||||
newPath := fmt.Sprintf("%s%s", path, ErrorSuffix)
|
||||
if err := os.Rename(path, newPath); err != nil {
|
||||
log.Err(err).Str("path", path).Str("new path", newPath).Msg("unable to rename bad JSON email path")
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
email.Path = path
|
||||
s.queue.Add(email)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// processNextEmail loops over the queue and send email
|
||||
// processNextEmail iterates over the queue and send email.
|
||||
func (s Sender) processNextEmail() bool {
|
||||
item, quit := s.queue.Get()
|
||||
if quit {
|
||||
@ -88,66 +104,56 @@ func (s Sender) processNextEmail() bool {
|
||||
}
|
||||
defer s.queue.Done(item)
|
||||
|
||||
path, ok := item.(string)
|
||||
email, ok := item.(mail.Email)
|
||||
if !ok {
|
||||
log.Error().Any("item", item).Msg("unable to cast queue item into mail.Email")
|
||||
return true
|
||||
}
|
||||
|
||||
email, err := mail.FromJSON(path)
|
||||
if err != nil {
|
||||
log.Err(err).Str("path", path).Msg("unable to parse JSON email")
|
||||
|
||||
// if JSON parsing failed the `path` is renamed with an error suffix to avoid enqueued it again
|
||||
newPath := fmt.Sprintf("%s%s", path, ErrorSuffix)
|
||||
if err := os.Rename(path, newPath); err != nil {
|
||||
log.Err(err).Str("path", path).Str("new path", newPath).Msg("unable to rename bad JSON email path")
|
||||
s.queue.Shutdown()
|
||||
}
|
||||
return true
|
||||
if err := s.SendMail(email); err != nil {
|
||||
log.Err(err).Msg("unable to send the email")
|
||||
}
|
||||
|
||||
// whatever the return, the email will be not enqueued again
|
||||
s.SendMail(email)
|
||||
|
||||
if err := os.Remove(path); err != nil {
|
||||
// this is a fatal error, can't send same e-mail indefinitely
|
||||
if !os.IsExist(err) {
|
||||
log.Err(err).Str("path", path).Msg("unable to remove the JSON email")
|
||||
s.queue.Shutdown()
|
||||
if path := email.Path; path != "" {
|
||||
if err := os.Remove(path); err != nil {
|
||||
// this is a fatal error, can't send same e-mail indefinitely
|
||||
if !os.IsExist(err) {
|
||||
log.Err(err).Str("path", path).Msg("unable to remove the JSON email")
|
||||
s.queue.Shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// run starts processing the queue
|
||||
// run starts processing the queue.
|
||||
func (s Sender) run() <-chan struct{} {
|
||||
queueCh := make(chan struct{})
|
||||
chQueue := make(chan struct{})
|
||||
go func() {
|
||||
for s.processNextEmail() {
|
||||
}
|
||||
queueCh <- struct{}{}
|
||||
chQueue <- struct{}{}
|
||||
}()
|
||||
return queueCh
|
||||
return chQueue
|
||||
}
|
||||
|
||||
// Run launches the queue processing and the outbox watcher
|
||||
// catches `SIGINT` and `SIGTERM` to properly stopped the queue
|
||||
// Run launches the queue processing and the outbox watcher.
|
||||
// It catches `SIGINT` and `SIGTERM` to properly stopped the queue.
|
||||
func (s Sender) Run() {
|
||||
log.Info().Msg("sender service is running")
|
||||
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
chSignal := make(chan os.Signal, 1)
|
||||
signal.Notify(chSignal, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
s.watchOutbox()
|
||||
queueCh := s.run()
|
||||
chQueue := s.run()
|
||||
|
||||
select {
|
||||
case <-sigCh:
|
||||
case <-chSignal:
|
||||
log.Warn().Msg("stop signal received, stopping e-mail queue...")
|
||||
s.queue.Shutdown()
|
||||
case <-queueCh:
|
||||
case <-chQueue:
|
||||
log.Info().Msg("e-mail queue stopped successfully")
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user