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 | .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 ./... | 	go fmt ./... | ||||||
| .PHONY:fmt |  | ||||||
| 
 | 
 | ||||||
| lint: fmt | lint: | ||||||
| 	golint ./... | 	golangci-lint run --fix | ||||||
| .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 |  | ||||||
| 
 | 
 | ||||||
| test: | test: | ||||||
| 	go test ./... | 	go test ./... | ||||||
| .PHONY:test |  | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| module mailsrv | module mailsrv | ||||||
| 
 | 
 | ||||||
| go 1.17 | go 1.20 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	github.com/alecthomas/kingpin/v2 v2.3.2 | 	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/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.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 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/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 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= | ||||||
| github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= | 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 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= | ||||||
| github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= | 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.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.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 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 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= | ||||||
| github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= | 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= | 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 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |  | ||||||
|  | |||||||
| @ -3,11 +3,12 @@ package mail | |||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Email struct { | type Email struct { | ||||||
|  | 	Path      string | ||||||
| 	Sender    string   `json:"sender"` | 	Sender    string   `json:"sender"` | ||||||
| 	Receivers []string `json:"receivers"` | 	Receivers []string `json:"receivers"` | ||||||
| 	Subject   string   `json:"subject"` | 	Subject   string   `json:"subject"` | ||||||
| @ -26,9 +27,9 @@ func NewEmail(sender string, receivers []string, subject, content string) Email | |||||||
| func FromJSON(path string) (Email, error) { | func FromJSON(path string) (Email, error) { | ||||||
| 	var mail Email | 	var mail Email | ||||||
| 
 | 
 | ||||||
| 	content, err := ioutil.ReadFile(path) | 	content, err := os.ReadFile(path) | ||||||
| 	if err != nil { | 	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 { | 	if err := json.Unmarshal(content, &mail); err != nil { | ||||||
|  | |||||||
| @ -51,14 +51,14 @@ func (s Sender) SendMail(email mail.Email) error { | |||||||
| 	return nil | 	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() { | func (s Sender) watchOutbox() { | ||||||
| 	log.Info().Str("outbox", s.outboxPath).Msg("start watching outbox directory") | 	log.Info().Str("outbox", s.outboxPath).Msg("start watching outbox directory") | ||||||
| 
 | 
 | ||||||
| 	ticker := time.NewTicker(TickerInterval) | 	ticker := time.NewTicker(TickerInterval) | ||||||
| 
 | 
 | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for _ = range ticker.C { | 		for range ticker.C { | ||||||
| 			log.Debug().Str("action", "retrieving json e-mail format...").Str("path", s.outboxPath) | 			log.Debug().Str("action", "retrieving json e-mail format...").Str("path", s.outboxPath) | ||||||
| 
 | 
 | ||||||
| 			files, err := os.ReadDir(s.outboxPath) | 			files, err := os.ReadDir(s.outboxPath) | ||||||
| @ -69,18 +69,34 @@ func (s Sender) watchOutbox() { | |||||||
| 
 | 
 | ||||||
| 			for _, file := range files { | 			for _, file := range files { | ||||||
| 				filename := file.Name() | 				filename := file.Name() | ||||||
| 				if strings.HasSuffix(filename, JSONSuffix) { | 				if !strings.HasSuffix(filename, JSONSuffix) { | ||||||
| 					s.queue.Add(path.Join(s.outboxPath, filename)) | 					log.Debug().Str("filename", filename).Msg("incorrect suffix") | ||||||
| 					continue | 					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 { | func (s Sender) processNextEmail() bool { | ||||||
| 	item, quit := s.queue.Get() | 	item, quit := s.queue.Get() | ||||||
| 	if quit { | 	if quit { | ||||||
| @ -88,28 +104,17 @@ func (s Sender) processNextEmail() bool { | |||||||
| 	} | 	} | ||||||
| 	defer s.queue.Done(item) | 	defer s.queue.Done(item) | ||||||
| 
 | 
 | ||||||
| 	path, ok := item.(string) | 	email, ok := item.(mail.Email) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		log.Error().Any("item", item).Msg("unable to cast queue item into mail.Email") | 		log.Error().Any("item", item).Msg("unable to cast queue item into mail.Email") | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	email, err := mail.FromJSON(path) | 	if err := s.SendMail(email); err != nil { | ||||||
| 	if err != nil { | 		log.Err(err).Msg("unable to send the email") | ||||||
| 		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 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// whatever the return, the email will be not enqueued again | 	if path := email.Path; path != "" { | ||||||
| 	s.SendMail(email) |  | ||||||
| 
 |  | ||||||
| 		if err := os.Remove(path); err != nil { | 		if err := os.Remove(path); err != nil { | ||||||
| 			// this is a fatal error, can't send same e-mail indefinitely | 			// this is a fatal error, can't send same e-mail indefinitely | ||||||
| 			if !os.IsExist(err) { | 			if !os.IsExist(err) { | ||||||
| @ -117,37 +122,38 @@ func (s Sender) processNextEmail() bool { | |||||||
| 				s.queue.Shutdown() | 				s.queue.Shutdown() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // run starts processing the queue | // run starts processing the queue. | ||||||
| func (s Sender) run() <-chan struct{} { | func (s Sender) run() <-chan struct{} { | ||||||
| 	queueCh := make(chan struct{}) | 	chQueue := make(chan struct{}) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for s.processNextEmail() { | 		for s.processNextEmail() { | ||||||
| 		} | 		} | ||||||
| 		queueCh <- struct{}{} | 		chQueue <- struct{}{} | ||||||
| 	}() | 	}() | ||||||
| 	return queueCh | 	return chQueue | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Run launches the queue processing and the outbox watcher | // Run launches the queue processing and the outbox watcher. | ||||||
| // catches `SIGINT` and `SIGTERM` to properly stopped the queue | // It catches `SIGINT` and `SIGTERM` to properly stopped the queue. | ||||||
| func (s Sender) Run() { | func (s Sender) Run() { | ||||||
| 	log.Info().Msg("sender service is running") | 	log.Info().Msg("sender service is running") | ||||||
| 
 | 
 | ||||||
| 	sigCh := make(chan os.Signal, 1) | 	chSignal := make(chan os.Signal, 1) | ||||||
| 	signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) | 	signal.Notify(chSignal, os.Interrupt, syscall.SIGTERM) | ||||||
| 
 | 
 | ||||||
| 	s.watchOutbox() | 	s.watchOutbox() | ||||||
| 	queueCh := s.run() | 	chQueue := s.run() | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case <-sigCh: | 	case <-chSignal: | ||||||
| 		log.Warn().Msg("stop signal received, stopping e-mail queue...") | 		log.Warn().Msg("stop signal received, stopping e-mail queue...") | ||||||
| 		s.queue.Shutdown() | 		s.queue.Shutdown() | ||||||
| 	case <-queueCh: | 	case <-chQueue: | ||||||
| 		log.Info().Msg("e-mail queue stopped successfully") | 		log.Info().Msg("e-mail queue stopped successfully") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user