88 "github.com/juju/errors"
99 "github.com/mattolenik/cloudflare-ddns-client/conf"
1010 "github.com/mattolenik/cloudflare-ddns-client/ip"
11- "github.com/rs/zerolog/log "
11+ "github.com/mattolenik/cloudflare-ddns-client/task "
1212)
1313
1414type DDNSProvider interface {
@@ -49,9 +49,9 @@ func NewDefaultConfigProvider() *DefaultConfigProvider {
4949}
5050
5151type Daemon interface {
52- Update (provider DDNSProvider ) error
53- Start (provider DDNSProvider , updatePeriod , failureRetryDelay time.Duration ) error
54- Stop () error
52+ Update () error
53+ Start (updatePeriod , retryDelay time.Duration ) chan task. Status
54+ Stop ()
5555}
5656
5757type DDNSDaemon struct {
@@ -62,8 +62,8 @@ type DDNSDaemon struct {
6262 configProvider ConfigProvider
6363}
6464
65- // NewDDNSDaemon creates a new DDNSDaemon
66- func NewDDNSDaemon (ddnsProvider DDNSProvider , ipProvider IPProvider , configProvider ConfigProvider ) * DDNSDaemon {
65+ // NewDefaultDaemon creates a new DDNSDaemon
66+ func NewDefaultDaemon (ddnsProvider DDNSProvider , ipProvider IPProvider , configProvider ConfigProvider ) * DDNSDaemon {
6767 if ddnsProvider == nil {
6868 panic ("ddnsProvider must not be nil" )
6969 }
@@ -87,7 +87,6 @@ func (d *DDNSDaemon) Update() error {
8787 if err != nil {
8888 return errors .Annotate (err , "unable to retrieve public IP" )
8989 }
90- log .Info ().Msgf ("Found public IP '%s'" , ip )
9190 domain , record , err := d .configProvider .Get ()
9291 if err != nil {
9392 return errors .Annotate (err , "unable to find domain or record in configuration" )
@@ -99,70 +98,82 @@ func (d *DDNSDaemon) Update() error {
9998// Start continually keeps DDNS up to date.
10099// updatePeriod - how often to check for updates
101100// failureRetryDelay - how long to wait until retry after a failure
102- func (d * DDNSDaemon ) Start (updatePeriod , failureRetryDelay time.Duration ) error {
101+ func (d * DDNSDaemon ) Start (updatePeriod , retryDelay time.Duration ) ( status chan task. Status ) {
103102 var lastIP string
104103 var lastIPUpdate time.Time
105104
106- log .Info ().Msgf ("Daemon running, will now monitor for IP updates every %d seconds" , int (updatePeriod .Seconds ()))
107-
108- for d .shouldRun {
109- domain , record , err := d .configProvider .Get ()
110- if err != nil {
111- return errors .Annotate (err , "unable to find domain or record in configuration" )
112- }
113- dnsRecordIP , err := d .ddnsProvider .Get (domain , record )
114- if err != nil {
115- log .Error ().Msgf ("Unable to look up current DNS record, will retry in %d seconds. Error was:\n %v" , int (updatePeriod .Seconds ()), err )
116- time .Sleep (failureRetryDelay )
117- continue
118- }
119- newIP , err := d .ipProvider .Get ()
120- if err != nil {
121- log .Error ().Msgf ("Unable to retrieve public IP, will retry in %d seconds. Error was:\n %v" , int (updatePeriod .Seconds ()), err )
122- time .Sleep (failureRetryDelay )
123- continue
124- }
125- if newIP == lastIP && newIP == dnsRecordIP {
126- log .Info ().Msgf (
127- "No IP change detected since %s (%d seconds ago)" ,
128- lastIPUpdate .Format (time .RFC1123Z ),
129- int (time .Since (lastIPUpdate ).Seconds ()))
105+ status <- task .InfoStatusf ("Daemon running, will now monitor for IP updates every %d seconds" , int (updatePeriod .Seconds ()))
106+
107+ func () {
108+ defer close (status )
109+ for d .shouldRun {
110+ domain , record , err := d .configProvider .Get ()
111+ if err != nil {
112+ status <- task .FatalStatusWrap (err , "unable to find domain or record in configuration" )
113+ return
114+ }
115+ dnsRecordIP , err := d .ddnsProvider .Get (domain , record )
116+ if err != nil {
117+ status <- task .ErrorStatusf ("Unable to look up current DNS record, will retry in %d seconds. Error was:\n %v" , int (updatePeriod .Seconds ()), err )
118+ time .Sleep (retryDelay )
119+ continue
120+ }
121+ newIP , err := d .ipProvider .Get ()
122+ if err != nil {
123+ status <- task .ErrorStatusf ("Unable to retrieve public IP, will retry in %d seconds. Error was:\n %v" , int (updatePeriod .Seconds ()), err )
124+ time .Sleep (retryDelay )
125+ continue
126+ }
127+
128+ // Nothing has changed, log and move on
129+ if newIP == lastIP && newIP == dnsRecordIP {
130+ status <- task .InfoStatusf (
131+ "No IP change detected since %s (%d seconds ago)" ,
132+ lastIPUpdate .Format (time .RFC1123Z ),
133+ int (time .Since (lastIPUpdate ).Seconds ()))
134+ time .Sleep (updatePeriod )
135+ continue
136+ }
137+
138+ // IP has changed, log depending on how it has changed
139+ if lastIP == "" {
140+ // Log line for first time
141+ status <- task .InfoStatusf ("Found public IP '%s'" , newIP )
142+ } else if newIP != lastIP {
143+ // Log line for IP change
144+ status <- task .InfoStatusf ("Detected new public IP address, it changed from '%s' to '%s'" , lastIP , newIP )
145+ } else if dnsRecordIP != newIP {
146+ // Log line for no new IP, but mismatch with DNS record
147+ status <- task .InfoStatusf ("Public IP address did not change, but DNS record did match, is '%s' but expected '%s', correcting" , dnsRecordIP , newIP )
148+ }
149+
150+ lastIP = newIP
151+ lastIPUpdate = time .Now ()
152+
153+ // Reach out to the actual DDNS provider and make the update
154+ err = d .ddnsProvider .Update (domain , record , lastIP )
155+ if err != nil {
156+ status <- task .ErrorStatusf ("Unable to update DNS, will retry in %d seconds. Erorr was:\n %v" , updatePeriod / time .Second , err )
157+ time .Sleep (retryDelay )
158+ continue
159+ }
160+ // Do another run check before the sleep occurs so as to not draw out the stop operation
161+ if ! d .shouldRun {
162+ break
163+ }
130164 time .Sleep (updatePeriod )
131- continue
132165 }
133- if lastIP == "" {
134- // Log line for first time
135- log .Info ().Msgf ("Found public IP '%s'" , newIP )
136- } else if newIP != lastIP {
137- // Log line for IP change
138- log .Info ().Msgf ("Detected new public IP address, it changed from '%s' to '%s'" , lastIP , newIP )
139- } else if dnsRecordIP != newIP {
140- // Log line for no new IP, but mismatch with DNS record
141- log .Info ().Msgf ("Public IP address did not change, but DNS record did match, is '%s' but expected '%s', correcting" , dnsRecordIP , newIP )
142- }
143- lastIP = newIP
144- lastIPUpdate = time .Now ()
145-
146- err = d .ddnsProvider .Update (domain , record , lastIP )
147- if err != nil {
148- log .Error ().Msgf ("Unable to update DNS, will retry in %d seconds. Erorr was:\n %v" , updatePeriod / time .Second , err )
149- time .Sleep (failureRetryDelay )
150- continue
151- }
152- if ! d .shouldRun {
153- break
154- }
155- time .Sleep (updatePeriod )
156- }
157- return nil
166+ }()
167+ return status
158168}
159169
160170// StartWithDefaults calls Start but with default values
161- func (d * DDNSDaemon ) StartWithDefaults () error {
171+ func (d * DDNSDaemon ) StartWithDefaults () chan task. Status {
162172 t := 10 * time .Second
163- return errors . Trace ( d .Start (t , t ) )
173+ return d .Start (t , t )
164174}
165175
176+ // Stop instructs the daemon to stop as soon as the current (if any) operation is finished
166177func (d * DDNSDaemon ) Stop () {
167178 d .shouldRun = true
168179}
0 commit comments