Skip to content

Commit

Permalink
chore(all): migrate to service architecture with github.com/qdm12/gos…
Browse files Browse the repository at this point in the history
…ervices (#743)
  • Loading branch information
qdm12 authored Jun 13, 2024
1 parent 20792e9 commit 130ab00
Show file tree
Hide file tree
Showing 12 changed files with 294 additions and 268 deletions.
113 changes: 45 additions & 68 deletions cmd/updater/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import (
"github.com/qdm12/ddns-updater/internal/shoutrrr"
"github.com/qdm12/ddns-updater/internal/update"
"github.com/qdm12/ddns-updater/pkg/publicip"
"github.com/qdm12/goservices"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/goshutdown"
"github.com/qdm12/gosplash"
"github.com/qdm12/log"
)
Expand Down Expand Up @@ -173,12 +173,6 @@ func _main(ctx context.Context, reader *reader.Reader, args []string, logger log
}

db := data.NewDatabase(records, persistentDB)
defer func() {
err := db.Close()
if err != nil {
logger.Error(err.Error())
}
}()

httpSettings := publicip.HTTPSettings{
Enabled: *config.PubIP.HTTPEnabled,
Expand Down Expand Up @@ -208,45 +202,65 @@ func _main(ctx context.Context, reader *reader.Reader, args []string, logger log
*config.Health.HealthchecksioUUID)

updater := update.NewUpdater(db, client, shoutrrrClient, logger, timeNow)
runner := update.NewRunner(db, updater, ipGetter, config.Update.Period,
updaterService := update.NewService(db, updater, ipGetter, config.Update.Period,
config.Update.Cooldown, logger, resolver, timeNow, hioClient)

runnerHandler, runnerCtx, runnerDone := goshutdown.NewGoRoutineHandler("runner")
go runner.Run(runnerCtx, runnerDone)

// note: errors are logged within the goroutine,
// no need to collect the resulting errors.
go runner.ForceUpdate(ctx)

isHealthy := health.MakeIsHealthy(db, resolver)
healthLogger := logger.New(log.SetComponent("healthcheck server"))
healthServer := health.NewServer(*config.Health.ServerAddress,
healthServer, err := health.NewServer(*config.Health.ServerAddress,
healthLogger, isHealthy)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler("health server")
go healthServer.Run(healthServerCtx, healthServerDone)
if err != nil {
return fmt.Errorf("creating health server: %w", err)
}

serverLogger := logger.New(log.SetComponent("http server"))
server := server.New(ctx, config.Server.ListeningAddress, config.Server.RootURL,
db, serverLogger, runner)
serverHandler, serverCtx, serverDone := goshutdown.NewGoRoutineHandler("server")
go server.Run(serverCtx, serverDone)
shoutrrrClient.Notify("Launched with " + strconv.Itoa(len(records)) + " records to watch")
server, err := server.New(ctx, config.Server.ListeningAddress, config.Server.RootURL,
db, serverLogger, updaterService)
if err != nil {
return fmt.Errorf("creating server: %w", err)
}

backupHandler, backupCtx, backupDone := goshutdown.NewGoRoutineHandler("backup")
var backupService goservices.Service
backupLogger := logger.New(log.SetComponent("backup"))
go backupRunLoop(backupCtx, backupDone, *config.Backup.Period, *config.Paths.DataDir,
*config.Backup.Directory, backupLogger, timeNow)
backupService = backup.New(*config.Backup.Period, *config.Paths.DataDir,
*config.Backup.Directory, backupLogger)
backupService, err = goservices.NewRestarter(goservices.RestarterSettings{Service: backupService})
if err != nil {
return fmt.Errorf("creating backup restarter: %w", err)
}

servicesSequence, err := goservices.NewSequence(goservices.SequenceSettings{
ServicesStart: []goservices.Service{db, updaterService, healthServer, server, backupService},
ServicesStop: []goservices.Service{server, healthServer, updaterService, backupService, db},
})
if err != nil {
return fmt.Errorf("creating services sequence: %w", err)
}

runError, startErr := servicesSequence.Start(ctx)
if startErr != nil {
return fmt.Errorf("starting services: %w", startErr)
}

shutdownGroup := goshutdown.NewGroupHandler("")
shutdownGroup.Add(runnerHandler, healthServerHandler, serverHandler, backupHandler)
// note: errors are logged within the goroutine,
// no need to collect the resulting errors.
go updaterService.ForceUpdate(ctx)

shoutrrrClient.Notify("Launched with " + strconv.Itoa(len(records)) + " records to watch")

<-ctx.Done()
select {
case <-ctx.Done():
case err = <-runError:
exitHealthchecksio(hioClient, logger, healthchecksio.Exit1)
shoutrrrClient.Notify(err.Error())
return fmt.Errorf("exiting due to critical error: %w", err)
}

err = shutdownGroup.Shutdown(context.Background())
err = servicesSequence.Stop()
if err != nil {
exitHealthchecksio(hioClient, logger, healthchecksio.Exit1)
shoutrrrClient.Notify(err.Error())
return err
return fmt.Errorf("stopping failed: %w", err)
}

exitHealthchecksio(hioClient, logger, healthchecksio.Exit0)
Expand Down Expand Up @@ -324,43 +338,6 @@ func readRecords(providers []provider.Provider, persistentDB *persistence.Databa
return records, nil
}

type InfoErroer interface {
Info(s string)
Error(s string)
}

func backupRunLoop(ctx context.Context, done chan<- struct{}, backupPeriod time.Duration,
dataDir, outputDir string, logger InfoErroer, timeNow func() time.Time) {
defer close(done)
if backupPeriod == 0 {
logger.Info("disabled")
return
}
logger.Info("each " + backupPeriod.String() +
"; writing zip files to directory " + outputDir)
ziper := backup.NewZiper()
timer := time.NewTimer(backupPeriod)
for {
fileName := "ddns-updater-backup-" + strconv.Itoa(int(timeNow().UnixNano())) + ".zip"
zipFilepath := filepath.Join(outputDir, fileName)
err := ziper.ZipFiles(
zipFilepath,
filepath.Join(dataDir, "updates.json"),
filepath.Join(dataDir, "config.json"),
)
if err != nil {
logger.Error(err.Error())
}
select {
case <-timer.C:
timer.Reset(backupPeriod)
case <-ctx.Done():
timer.Stop()
return
}
}
}

func exitHealthchecksio(hioClient *healthchecksio.Client,
logger log.LoggerInterface, state healthchecksio.State) {
err := hioClient.Ping(context.Background(), state)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ require (
github.com/go-chi/chi/v5 v5.0.11
github.com/golang/mock v1.6.0
github.com/miekg/dns v1.1.58
github.com/qdm12/goservices v0.1.0
github.com/qdm12/gosettings v0.4.1
github.com/qdm12/goshutdown v0.3.0
github.com/qdm12/gosplash v0.1.0
github.com/qdm12/gotree v0.2.0
github.com/qdm12/log v0.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+q
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/qdm12/goservices v0.1.0 h1:9sODefm/yuIGS7ynCkEnNlMTAYn9GzPhtcK4F69JWvc=
github.com/qdm12/goservices v0.1.0/go.mod h1:/JOFsAnHFiSjyoXxa5FlfX903h20K5u/3rLzCjYVMck=
github.com/qdm12/gosettings v0.4.1 h1:c7+14jO1Y2kFXBCUfS2+QE2NgwTKfzcdJzGEFRItCI8=
github.com/qdm12/gosettings v0.4.1/go.mod h1:uItKwGXibJp2pQ0am6MBKilpjfvYTGiH+zXHd10jFj8=
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
github.com/qdm12/gotree v0.2.0 h1:+58ltxkNLUyHtATFereAcOjBVfY6ETqRex8XK90Fb/c=
Expand Down
5 changes: 5 additions & 0 deletions internal/backup/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package backup

type Logger interface {
Info(message string)
}
97 changes: 97 additions & 0 deletions internal/backup/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package backup

import (
"context"
"path/filepath"
"strconv"
"time"
)

type Service struct {
// Injected fields
backupPeriod time.Duration
dataDir string
outputDir string
logger Logger

// Internal fields
stopCh chan<- struct{}
done <-chan struct{}
}

func New(backupPeriod time.Duration,
dataDir, outputDir string, logger Logger) *Service {
return &Service{
logger: logger,
backupPeriod: backupPeriod,
dataDir: dataDir,
outputDir: outputDir,
}
}

func (s *Service) String() string {
return "backup"
}

func makeZipFileName() string {
return "ddns-updater-backup-" + strconv.Itoa(int(time.Now().UnixNano())) + ".zip"
}

func (s *Service) Start(ctx context.Context) (runError <-chan error, startErr error) {
ready := make(chan struct{})
runErrorCh := make(chan error)
stopCh := make(chan struct{})
s.stopCh = stopCh
done := make(chan struct{})
s.done = done
go run(ready, runErrorCh, stopCh, done,
s.outputDir, s.dataDir, s.backupPeriod, s.logger)
select {
case <-ready:
case <-ctx.Done():
return nil, s.Stop()
}
return runErrorCh, nil
}

func run(ready chan<- struct{}, runError chan<- error, stopCh <-chan struct{},
done chan<- struct{}, outputDir, dataDir string, backupPeriod time.Duration,
logger Logger) {
defer close(done)

if backupPeriod == 0 {
close(ready)
logger.Info("disabled")
return
}

logger.Info("each " + backupPeriod.String() +
"; writing zip files to directory " + outputDir)
timer := time.NewTimer(backupPeriod)
close(ready)

for {
select {
case <-timer.C:
case <-stopCh:
_ = timer.Stop()
return
}
err := zipFiles(
filepath.Join(outputDir, makeZipFileName()),
filepath.Join(dataDir, "config.json"),
filepath.Join(dataDir, "updates.json"),
)
if err != nil {
runError <- err
return
}
timer.Reset(backupPeriod)
}
}

func (s *Service) Stop() (err error) {
close(s.stopCh)
<-s.done
return nil
}
32 changes: 6 additions & 26 deletions internal/backup/zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,25 @@ import (
"os"
)

var _ FileZiper = (*Ziper)(nil)

type FileZiper interface {
ZipFiles(outputFilepath string, inputFilepaths ...string) error
}

type Ziper struct {
createFile func(name string) (*os.File, error)
openFile func(name string) (*os.File, error)
ioCopy func(dst io.Writer, src io.Reader) (written int64, err error)
}

func NewZiper() *Ziper {
return &Ziper{
createFile: os.Create,
openFile: os.Open,
ioCopy: io.Copy,
}
}

func (z *Ziper) ZipFiles(outputFilepath string, inputFilepaths ...string) error {
f, err := z.createFile(outputFilepath)
func zipFiles(outputFilepath string, inputFilepaths ...string) error {
f, err := os.Create(outputFilepath)
if err != nil {
return err
}
defer f.Close()
w := zip.NewWriter(f)
defer w.Close()
for _, filepath := range inputFilepaths {
err = z.addFile(w, filepath)
err = addFile(w, filepath)
if err != nil {
return err
}
}
return nil
}

func (z *Ziper) addFile(w *zip.Writer, filepath string) error {
f, err := z.openFile(filepath)
func addFile(w *zip.Writer, filepath string) error {
f, err := os.Open(filepath)
if err != nil {
return err
}
Expand All @@ -65,6 +45,6 @@ func (z *Ziper) addFile(w *zip.Writer, filepath string) error {
if err != nil {
return err
}
_, err = z.ioCopy(ioWriter, f)
_, err = io.Copy(ioWriter, f)
return err
}
15 changes: 15 additions & 0 deletions internal/data/data.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package data

import (
"context"
"sync"

"github.com/qdm12/ddns-updater/internal/records"
Expand All @@ -19,3 +20,17 @@ func NewDatabase(data []records.Record, persistentDB PersistentDatabase) *Databa
persistentDB: persistentDB,
}
}

func (db *Database) String() string {
return "database"
}

func (db *Database) Start(_ context.Context) (_ <-chan error, err error) {
return nil, nil //nolint:nilnil
}

func (db *Database) Stop() (err error) {
db.Lock() // ensure write operation finishes
defer db.Unlock()
return db.persistentDB.Close()
}
6 changes: 0 additions & 6 deletions internal/data/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,3 @@ func (db *Database) Update(id uint, record records.Record) (err error) {
}
return nil
}

func (db *Database) Close() (err error) {
db.Lock() // ensure write operation finishes
defer db.Unlock()
return db.persistentDB.Close()
}
Loading

0 comments on commit 130ab00

Please sign in to comment.