@@ -3,6 +3,8 @@ package main
33import (
44 "context"
55 "net"
6+ "os/signal"
7+ "syscall"
68
79 "fmt"
810 "net/http"
@@ -16,10 +18,8 @@ import (
1618 "github.com/qdm12/golibs/network/connectivity"
1719 libparams "github.com/qdm12/golibs/params"
1820 "github.com/qdm12/golibs/server"
19- "github.com/qdm12/golibs/signals"
2021
2122 "github.com/qdm12/ddns-updater/internal/data"
22- "github.com/qdm12/ddns-updater/internal/env"
2323 "github.com/qdm12/ddns-updater/internal/handlers"
2424 "github.com/qdm12/ddns-updater/internal/healthcheck"
2525 "github.com/qdm12/ddns-updater/internal/models"
@@ -31,70 +31,97 @@ import (
3131)
3232
3333func main () {
34- logger , err := logging .NewLogger (logging .ConsoleEncoding , logging .InfoLevel , - 1 )
35- if err != nil {
36- panic (err )
37- }
38- paramsReader := params .NewReader (logger )
39- encoding , level , nodeID , err := paramsReader .GetLoggerConfig ()
40- if err != nil {
41- logger .Error (err )
42- } else {
43- logger , err = logging .NewLogger (encoding , level , nodeID )
44- if err != nil {
45- panic (err )
46- }
47- }
34+ os .Exit (_main (context .Background ()))
35+ // returns 1 on error
36+ // returns 2 on os signal
37+ }
38+
39+ func _main (ctx context.Context ) int {
4840 if libhealthcheck .Mode (os .Args ) {
4941 // Running the program in a separate instance through the Docker
5042 // built-in healthcheck, in an ephemeral fashion to query the
5143 // long running instance of the program about its status
5244 if err := libhealthcheck .Query (); err != nil {
53- logger . Error (err )
54- os . Exit ( 1 )
45+ fmt . Println (err )
46+ return 1
5547 }
56- os . Exit ( 0 )
48+ return 0
5749 }
58- fmt .Println (splash .Splash (paramsReader ))
59- e := env .NewEnv (logger )
60- gotifyURL , err := paramsReader .GetGotifyURL ()
61- e .FatalOnError (err )
62- if gotifyURL != nil {
63- gotifyToken , err := paramsReader .GetGotifyToken ()
64- e .FatalOnError (err )
65- e .SetGotify (admin .NewGotify (* gotifyURL , gotifyToken , & http.Client {Timeout : time .Second }))
50+ logger , err := setupLogger ()
51+ if err != nil {
52+ fmt .Println (err )
53+ return 1
54+ }
55+ paramsReader := params .NewReader (logger )
56+
57+ fmt .Println (splash .Splash (
58+ paramsReader .GetVersion (),
59+ paramsReader .GetVcsRef (),
60+ paramsReader .GetBuildDate ()))
61+
62+ notify , err := setupGotify (paramsReader , logger )
63+ if err != nil {
64+ logger .Error (err )
65+ return 1
6666 }
67+
6768 listeningPort , warning , err := paramsReader .GetListeningPort ()
68- e .FatalOnError (err )
6969 if len (warning ) > 0 {
7070 logger .Warn (warning )
7171 }
72+ if err != nil {
73+ logger .Error (err )
74+ notify (4 , err )
75+ return 1
76+ }
7277 rootURL , err := paramsReader .GetRootURL ()
73- e .FatalOnError (err )
78+ if err != nil {
79+ logger .Error (err )
80+ notify (4 , err )
81+ return 1
82+ }
7483 defaultPeriod , err := paramsReader .GetDelay (libparams .Default ("10m" ))
75- e .FatalOnError (err )
84+ if err != nil {
85+ logger .Error (err )
86+ notify (4 , err )
87+ return 1
88+ }
7689 dir , err := paramsReader .GetExeDir ()
77- e .FatalOnError (err )
90+ if err != nil {
91+ logger .Error (err )
92+ notify (4 , err )
93+ return 1
94+ }
7895 dataDir , err := paramsReader .GetDataDir (dir )
79- e .FatalOnError (err )
80- var persistentDB persistence.Database
81- persistentDB , err = persistence .NewJSON (dataDir )
82- e .FatalOnError (err )
83- go signals .WaitForExit (e .ShutdownFromSignal )
96+ if err != nil {
97+ logger .Error (err )
98+ notify (4 , err )
99+ return 1
100+ }
101+
102+ persistentDB , err := persistence .NewJSON (dataDir )
103+ if err != nil {
104+ logger .Error (err )
105+ notify (4 , err )
106+ return 1
107+ }
84108 settings , warnings , err := paramsReader .GetSettings (dataDir + "/config.json" )
85109 for _ , w := range warnings {
86- e .Warn (w )
110+ logger .Warn (w )
111+ notify (2 , w )
87112 }
88113 if err != nil {
89- e .Fatal (err )
114+ logger .Error (err )
115+ notify (4 , err )
116+ return 1
90117 }
91118 if len (settings ) > 1 {
92119 logger .Info ("Found %d settings to update records" , len (settings ))
93120 } else if len (settings ) == 1 {
94121 logger .Info ("Found single setting to update records" )
95122 }
96123 for _ , err := range connectivity .NewConnectivity (5 * time .Second ).Checks ("google.com" ) {
97- e .Warn (err )
124+ logger .Warn (err )
98125 }
99126 records := make ([]models.Record , len (settings ))
100127 idToPeriod := make (map [int ]time.Duration )
@@ -103,7 +130,9 @@ func main() {
103130 logger .Info ("Reading history from database: domain %s host %s" , setting .Domain , setting .Host )
104131 events , err := persistentDB .GetEvents (setting .Domain , setting .Host )
105132 if err != nil {
106- e .FatalOnError (err )
133+ logger .Error (err )
134+ notify (4 , err )
135+ return 1
107136 }
108137 records [i ] = models .NewRecord (setting , events )
109138 idToPeriod [id ] = defaultPeriod
@@ -113,27 +142,91 @@ func main() {
113142 i ++
114143 }
115144 HTTPTimeout , err := paramsReader .GetHTTPTimeout ()
116- e .FatalOnError (err )
145+ if err != nil {
146+ logger .Error (err )
147+ notify (4 , err )
148+ return 1
149+ }
117150 client := network .NewClient (HTTPTimeout )
151+ defer client .Close ()
118152 db := data .NewDatabase (records , persistentDB )
119- e .SetDB (db )
120- updater := update .NewUpdater (db , logger , client , e .Notify )
121- ctx , cancel := context .WithCancel (context .Background ())
153+ defer func () {
154+ if err := db .Close (); err != nil {
155+ logger .Error (err )
156+ }
157+ }()
158+ updater := update .NewUpdater (db , logger , client , notify )
159+ ctx , cancel := context .WithCancel (ctx )
122160 defer cancel ()
123- forceUpdate := trigger .StartUpdates (ctx , updater , idToPeriod , e .CheckError )
161+ checkError := func (err error ) {
162+ if err != nil {
163+ logger .Error (err )
164+ }
165+ }
166+ forceUpdate := trigger .StartUpdates (ctx , updater , idToPeriod , checkError )
124167 forceUpdate ()
125- productionHandlerFunc := handlers .NewHandler (rootURL , dir , db , logger , forceUpdate , e . CheckError ).GetHandlerFunc ()
168+ productionHandlerFunc := handlers .NewHandler (rootURL , dir , db , logger , forceUpdate , checkError ).GetHandlerFunc ()
126169 healthcheckHandlerFunc := libhealthcheck .GetHandler (func () error {
127170 return healthcheck .IsHealthy (db , net .LookupIP , logger )
128171 })
129172 logger .Info ("Web UI listening at address 0.0.0.0:%s with root URL %s" , listeningPort , rootURL )
130- e .Notify (1 , fmt .Sprintf ("Just launched\n It has %d records to watch" , len (records )))
131- serverErrs := server .RunServers (
132- ctx ,
133- server.Settings {Name : "production" , Addr : "0.0.0.0:" + listeningPort , Handler : productionHandlerFunc },
134- server.Settings {Name : "healthcheck" , Addr : "127.0.0.1:9999" , Handler : healthcheckHandlerFunc },
173+ notify (1 , fmt .Sprintf ("Launched with %d records to watch" , len (records )))
174+ serverErrors := make (chan []error )
175+ go func () {
176+ serverErrors <- server .RunServers (ctx ,
177+ server.Settings {Name : "production" , Addr : "0.0.0.0:" + listeningPort , Handler : productionHandlerFunc },
178+ server.Settings {Name : "healthcheck" , Addr : "127.0.0.1:9999" , Handler : healthcheckHandlerFunc },
179+ )
180+ }()
181+
182+ osSignals := make (chan os.Signal , 1 )
183+ signal .Notify (osSignals ,
184+ syscall .SIGINT ,
185+ syscall .SIGTERM ,
186+ os .Interrupt ,
135187 )
136- if len (serverErrs ) > 0 {
137- e .Fatal (serverErrs )
188+ select {
189+ case errors := <- serverErrors :
190+ for _ , err := range errors {
191+ logger .Error (err )
192+ }
193+ return 1
194+ case signal := <- osSignals :
195+ message := fmt .Sprintf ("Stopping program: caught OS signal %q" , signal )
196+ logger .Warn (message )
197+ notify (2 , message )
198+ return 2
199+ case <- ctx .Done ():
200+ message := fmt .Sprintf ("Stopping program: %s" , ctx .Err ())
201+ logger .Warn (message )
202+ return 1
138203 }
139204}
205+
206+ func setupLogger () (logging.Logger , error ) {
207+ paramsReader := params .NewReader (nil )
208+ encoding , level , nodeID , err := paramsReader .GetLoggerConfig ()
209+ if err != nil {
210+ return nil , err
211+ }
212+ return logging .NewLogger (encoding , level , nodeID )
213+ }
214+
215+ func setupGotify (paramsReader params.Reader , logger logging.Logger ) (notify func (priority int , messageArgs ... interface {}), err error ) {
216+ gotifyURL , err := paramsReader .GetGotifyURL ()
217+ if err != nil {
218+ return nil , err
219+ } else if gotifyURL == nil {
220+ return func (priority int , messageArgs ... interface {}) {}, nil
221+ }
222+ gotifyToken , err := paramsReader .GetGotifyToken ()
223+ if err != nil {
224+ return nil , err
225+ }
226+ gotify := admin .NewGotify (* gotifyURL , gotifyToken , & http.Client {Timeout : time .Second })
227+ return func (priority int , messageArgs ... interface {}) {
228+ if err := gotify .Notify ("DDNS Updater" , priority , messageArgs ... ); err != nil {
229+ logger .Error (err )
230+ }
231+ }, nil
232+ }
0 commit comments