Move backoff logic to Updater

This commit is contained in:
Ryan Harg 2024-11-27 10:39:33 +01:00
parent da402b0da9
commit 4b0f3cefb8
5 changed files with 58 additions and 27 deletions

View file

@ -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

View file

@ -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<String, LocalDateTime> 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());
}

View file

@ -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<String, LocalDateTime> 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);

View file

@ -2,7 +2,3 @@ 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

View file

@ -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<String> 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")
);
}
}