|
1 | | -using Gandi; |
| 1 | +using Gandi; |
2 | 2 | using Gandi.Dns; |
3 | 3 | using Microsoft.Extensions.Options; |
4 | 4 | using System.Diagnostics; |
5 | 5 | using System.Net; |
6 | | -using ThrottleDebounce; |
| 6 | +using ThrottleDebounce.Retry; |
7 | 7 | using Unfucked; |
8 | 8 | using Unfucked.HTTP.Exceptions; |
9 | 9 | using Unfucked.STUN; |
@@ -32,21 +32,25 @@ public class DynamicDnsServiceImpl(ILiveDns liveDns, ISelfWanAddressClient stun, |
32 | 32 | null; |
33 | 33 | #endif |
34 | 34 |
|
| 35 | + /// <exception cref="GandiException"></exception> |
35 | 36 | protected override async Task ExecuteAsync(CancellationToken ct) { |
36 | 37 | try { |
37 | 38 | foreach (string subdomain in configuration.Value.subdomains) { |
38 | 39 | // this retry is to handle the case where the service starts before the computer connects to the network on bootup, not where Gandi's API servers are down |
39 | 40 | await Retrier.Attempt(async _ => { |
40 | | - IPAddress? initialRecordValue = null; |
41 | | - if ((await liveDns.Get(RecordType.A, subdomain, ct))?.Values.First() is { } existingIpAddress) { |
42 | | - try { |
43 | | - initialRecordValue = IPAddress.Parse(existingIpAddress); |
44 | | - } catch (FormatException) { } |
45 | | - } |
46 | | - initialRecordValues[subdomain] = initialRecordValue; |
47 | | - }, maxAttempts: null, delay: Retrier.Delays.Constant(TimeSpan.FromSeconds(3)), ex => ex is not (OutOfMemoryException or GandiException { InnerException: ClientErrorException }), |
48 | | - beforeRetry: async (i, e) => |
49 | | - logger.LogWarning("Failed to fetch existing DNS record from Gandi HTTP API server, retrying (attempt {attempt}): {message}", i + 2, e.MessageChain()), ct); |
| 41 | + IPAddress? initialRecordValue = null; |
| 42 | + if ((await liveDns.Get(RecordType.A, subdomain, ct))?.Values.First() is {} existingIpAddress) { |
| 43 | + try { |
| 44 | + initialRecordValue = IPAddress.Parse(existingIpAddress); |
| 45 | + } catch (FormatException) {} |
| 46 | + } |
| 47 | + initialRecordValues[subdomain] = initialRecordValue; |
| 48 | + }, new RetryOptions { |
| 49 | + Delay = Delays.Constant(TimeSpan.FromSeconds(3)), |
| 50 | + IsRetryAllowed = (ex, _) => ex is not (OutOfMemoryException or GandiException { InnerException: ClientErrorException }), |
| 51 | + BeforeRetry = (e, i) => logger.LogWarning("Failed to fetch existing DNS record from Gandi HTTP API server, retrying (attempt {attempt}): {message}", i + 2, e.MessageChain()), |
| 52 | + CancellationToken = ct |
| 53 | + }); |
50 | 54 |
|
51 | 55 | logger.LogInformation("On startup, the {subdomain}.{domain} DNS A record is pointing to {address}", subdomain, configuration.Value.domain, |
52 | 56 | initialRecordValues[subdomain]?.ToString() ?? "(nothing)"); |
@@ -79,6 +83,7 @@ await Retrier.Attempt(async _ => { |
79 | 83 | } |
80 | 84 | } |
81 | 85 |
|
| 86 | + /// <exception cref="GandiException"></exception> |
82 | 87 | private async Task updateDnsRecordIfNecessary(CancellationToken ct = default) { |
83 | 88 | SelfWanAddressResponse originalResponse = await stun.GetSelfWanAddress(ct); |
84 | 89 | if (originalResponse.SelfWanAddress != null) { |
@@ -126,6 +131,7 @@ private async Task<bool> getUnanimousAgreement(SelfWanAddressResponse originalRe |
126 | 131 | return extraResponses.All(extra => originalResponse.SelfWanAddress!.Equals(extra.SelfWanAddress)); |
127 | 132 | } |
128 | 133 |
|
| 134 | + /// <exception cref="GandiException"></exception> |
129 | 135 | private async Task updateDnsRecords(IPAddress currentIPAddress, CancellationToken ct = default) { |
130 | 136 | foreach (string subdomain in configuration.Value.subdomains) { |
131 | 137 | if (!configuration.Value.dryRun) { |
|
0 commit comments