From 4b0f3cefb88d61407110ec8f98a44462f0cb0623 Mon Sep 17 00:00:00 2001 From: Ryan Harg Date: Wed, 27 Nov 2024 10:39:33 +0100 Subject: [PATCH] Move backoff logic to Updater --- README.md | 10 +++--- src/main/java/de/rpr/ddnsclient/Updater.java | 23 ++++++++++++- .../java/de/rpr/ddnsclient/dyndns/Ddnss.java | 12 ------- src/main/resources/application.properties | 6 +--- .../java/de/rpr/ddnsclient/UpdaterTest.java | 34 ++++++++++++++++--- 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 72e81c0..3046f9f 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ The configuration can contain a list of update targets: ddnssclient can also be configured using some properties or environment variables -| Property | Environment variable | Value | Default | -| -------- |-----------------------|-----------------------------------|---------| -| ddnsclient.interval | DDNSCLIENT_INTERVAL | e.g. "3600s", "60m", "1h" | "5m" | -| ddnsclient.ip-provider | DDNSCLIENT_IP_PROVIDER | an ip lookup provider (see below) | null | -| ddnsclient.dyndns.backup-duration | DDNSCLIENT_DYNDNS_BACKUP_DURATION | e.g. "60s", "1m" | "60s" | +| Property | Environment variable | Value | Default | +|-----------------------------|------------------------------|-----------------------------------|---------| +| ddnsclient.interval | DDNSCLIENT_INTERVAL | e.g. "3600s", "60m", "1h" | "5m" | +| ddnsclient.ip-provider | DDNSCLIENT_IP_PROVIDER | an ip lookup provider (see below) | null | +| ddnsclient.backoff-duration | DDNS_CLIENT_BACKOFF_DURATION | e.g. "60s", "1m" | "60s" | ## IP lookup diff --git a/src/main/java/de/rpr/ddnsclient/Updater.java b/src/main/java/de/rpr/ddnsclient/Updater.java index 3bd003d..b2b4c20 100644 --- a/src/main/java/de/rpr/ddnsclient/Updater.java +++ b/src/main/java/de/rpr/ddnsclient/Updater.java @@ -9,8 +9,14 @@ import de.rpr.ddnsclient.model.DyndnsAuth; import de.rpr.ddnsclient.model.IPs; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + @ApplicationScoped public class Updater { @@ -19,15 +25,23 @@ public class Updater { private final DynDnsRouter dynDnsRouter; private final IpProvider ipProvider; private final DnsResolver dnsResolver; + private final Duration backoff; private final Config config; + private final Map updateMap = new HashMap<>(); + @Inject - public Updater(DynDnsRouter dynDnsRouter, IpProvider ipProvider, DnsResolver dnsResolver, Config config) { + public Updater(DynDnsRouter dynDnsRouter, + IpProvider ipProvider, + DnsResolver dnsResolver, + Config config, + @ConfigProperty(name = "ddnsclient.backoff-duration:300s") Duration backoff) { this.dynDnsRouter = dynDnsRouter; this.ipProvider = ipProvider; this.dnsResolver = dnsResolver; this.config = config; + this.backoff = backoff; } void run() { @@ -42,6 +56,12 @@ public class Updater { config.forEach(cfg -> { + if (updateMap.containsKey(cfg.hostname()) + && Duration.between(updateMap.get(cfg.hostname()), LocalDateTime.now()).toSeconds() < backoff.toSeconds()) { + log.debug("Back-off period, skipping update."); + return; + } + log.infof("Handling %s.", cfg.hostname()); IPs registeredIps = dnsResolver.resolve(cfg.hostname()); @@ -51,6 +71,7 @@ public class Updater { log.tracef("IPs changed, updating..."); DynDns dynDns = dynDnsRouter.get(cfg.provider()); dynDns.update(cfg.hostname(), publicIps, new DyndnsAuth(null, null, cfg.token())); + updateMap.put(cfg.hostname(), LocalDateTime.now()); } else { log.infof("Hostname is up-to-date.", cfg.hostname()); } diff --git a/src/main/java/de/rpr/ddnsclient/dyndns/Ddnss.java b/src/main/java/de/rpr/ddnsclient/dyndns/Ddnss.java index 627225a..bf12efa 100644 --- a/src/main/java/de/rpr/ddnsclient/dyndns/Ddnss.java +++ b/src/main/java/de/rpr/ddnsclient/dyndns/Ddnss.java @@ -22,11 +22,6 @@ public class Ddnss implements DynDns { private static final Logger log = Logger.getLogger(Ddnss.class); - @ConfigProperty(name = "ddnsclient.dyndns.backoff-duration") - Duration backoff; - - private final Map updateMap = new HashMap<>(); - @Override public String name() { return "ddnss"; @@ -34,12 +29,6 @@ public class Ddnss implements DynDns { public void update(String hostname, IPs currentIps, DyndnsAuth auth) { - if (updateMap.containsKey(hostname) - && Duration.between(updateMap.get(hostname), LocalDateTime.now()).toSeconds() < backoff.toSeconds()) { - log.debug("Back-off period, skipping update."); - return; - } - String updateUrl = MessageFormat.format("https://ddnss.de/upd.php?key={0}&host={1}&ip={2}&ip6={3}", auth.token(), hostname, currentIps.v4(), currentIps.v6()); @@ -55,7 +44,6 @@ public class Ddnss implements DynDns { if (response.statusCode() != 200) { log.errorf("Couldn't update hostname %s." + hostname); } - updateMap.put(hostname, LocalDateTime.now()); log.info("Hostname updated."); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c17eaeb..ed17802 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,8 +1,4 @@ quarkus.naming.enable-jndi=true quarkus.log.level=INFO quarkus.log.category."de.rpr.ddnsclient".min-level=TRACE -quarkus.log.category."de.rpr.ddnsclient".level=INFO - -ddnsclient.interval=15s -#ddnsclient.ip-provider= -ddnsclient.dyndns.backoff-duration=60s \ No newline at end of file +quarkus.log.category."de.rpr.ddnsclient".level=INFO \ No newline at end of file diff --git a/src/test/java/de/rpr/ddnsclient/UpdaterTest.java b/src/test/java/de/rpr/ddnsclient/UpdaterTest.java index 7ac4e57..87d9983 100644 --- a/src/test/java/de/rpr/ddnsclient/UpdaterTest.java +++ b/src/test/java/de/rpr/ddnsclient/UpdaterTest.java @@ -15,6 +15,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.Duration; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -34,10 +35,12 @@ class UpdaterTest { add(new Value("ddnss", "example.org", null, null, "token")); }}; + Duration backoff = Duration.ofMinutes(5); + @Test void should_throw_exception_if_config_is_empty() { Config config = new Config(); - Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, config); + Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, config, backoff); Assertions.assertThrows(IllegalStateException.class, updater::run); } @@ -53,7 +56,7 @@ class UpdaterTest { add(new Value("ddnss", "host2.example.org", null, null, "token")); }}; - Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, multipleConfigEntries); + Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, multipleConfigEntries, backoff); updater.run(); ArgumentCaptor hostnameCaptor = ArgumentCaptor.forClass(String.class); @@ -71,7 +74,7 @@ class UpdaterTest { when(ipProvider.getPublicIps()).thenReturn(ips); when(dnsResolver.resolve("example.org")).thenReturn(ips); - Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig); + Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff); updater.run(); verify(dynDnsRouter, never()).get(any()); @@ -89,7 +92,7 @@ class UpdaterTest { when(ipProvider.getPublicIps()).thenReturn(publicIps); when(dnsResolver.resolve("example.org")).thenReturn(new IPs("registered_ipv4", "registered_ipv6")); - Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig); + Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff); updater.run(); verify(ddnss).update( @@ -99,4 +102,27 @@ class UpdaterTest { ); } + @Test + void should_not_try_to_update_hostname_within_configured_backoff_duration() { + + DynDns ddnss = mock(Ddnss.class); + + when(dynDnsRouter.get("ddnss")).thenReturn(ddnss); + + IPs publicIps = new IPs("ipv4", "ipv6"); + when(ipProvider.getPublicIps()).thenReturn(publicIps); + when(dnsResolver.resolve("example.org")).thenReturn(new IPs("registered_ipv4", "registered_ipv6")); + + Updater updater = new Updater(dynDnsRouter, ipProvider, dnsResolver, ddnssConfig, backoff); + updater.run(); + + updater.run(); + + verify(ddnss, times(1)).update( + "example.org", + publicIps, + new DyndnsAuth(null, null, "token") + ); + } + } \ No newline at end of file