From 11b4beff7ecf325baf6be560cb4e978c4d39efc9 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 9 May 2024 21:05:21 +0200 Subject: [PATCH] route53: adds option to not wait for changes (#2181) --- cmd/zz_gen_cmd_dnshelp.go | 1 + docs/content/dns/zz_gen_route53.md | 1 + providers/dns/route53/route53.go | 31 +++++++++++++++++---------- providers/dns/route53/route53.toml | 1 + providers/dns/route53/route53_test.go | 25 +++++++++++---------- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index fda2a845..0e12e9c8 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -2306,6 +2306,7 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln(` - "AWS_REGION": Managed by the AWS client ('AWS_REGION_FILE' is not supported)`) ew.writeln(` - "AWS_SDK_LOAD_CONFIG": Managed by the AWS client. Retrieve the region from the CLI config file ('AWS_SDK_LOAD_CONFIG_FILE' is not supported)`) ew.writeln(` - "AWS_SECRET_ACCESS_KEY": Managed by the AWS client. Secret access key ('AWS_SECRET_ACCESS_KEY_FILE' is not supported, use 'AWS_SHARED_CREDENTIALS_FILE' instead)`) + ew.writeln(` - "AWS_WAIT_FOR_RECORD_SETS_CHANGED": Wait for changes to be INSYNC (it can be unstable)`) ew.writeln() ew.writeln(`Additional Configuration:`) diff --git a/docs/content/dns/zz_gen_route53.md b/docs/content/dns/zz_gen_route53.md index 166fa18e..62ef172a 100644 --- a/docs/content/dns/zz_gen_route53.md +++ b/docs/content/dns/zz_gen_route53.md @@ -48,6 +48,7 @@ lego --domains example.com --email your_example@email.com --dns route53 --accept | `AWS_REGION` | Managed by the AWS client (`AWS_REGION_FILE` is not supported) | | `AWS_SDK_LOAD_CONFIG` | Managed by the AWS client. Retrieve the region from the CLI config file (`AWS_SDK_LOAD_CONFIG_FILE` is not supported) | | `AWS_SECRET_ACCESS_KEY` | Managed by the AWS client. Secret access key (`AWS_SECRET_ACCESS_KEY_FILE` is not supported, use `AWS_SHARED_CREDENTIALS_FILE` instead) | +| `AWS_WAIT_FOR_RECORD_SETS_CHANGED` | Wait for changes to be INSYNC (it can be unstable) | The environment variable names can be suffixed by `_FILE` to reference a file instead of a value. More information [here]({{< ref "dns#configuration-and-credentials" >}}). diff --git a/providers/dns/route53/route53.go b/providers/dns/route53/route53.go index dd8d4587..394aa506 100644 --- a/providers/dns/route53/route53.go +++ b/providers/dns/route53/route53.go @@ -34,6 +34,8 @@ const ( EnvAssumeRoleArn = envNamespace + "ASSUME_ROLE_ARN" EnvExternalID = envNamespace + "EXTERNAL_ID" + EnvWaitForRecordSetsChanged = envNamespace + "WAIT_FOR_RECORD_SETS_CHANGED" + EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" EnvPollingInterval = envNamespace + "POLLING_INTERVAL" @@ -53,6 +55,8 @@ type Config struct { AssumeRoleArn string ExternalID string + WaitForRecordSetsChanged bool + TTL int PropagationTimeout time.Duration PollingInterval time.Duration @@ -68,6 +72,8 @@ func NewDefaultConfig() *Config { AssumeRoleArn: env.GetOrDefaultString(EnvAssumeRoleArn, ""), ExternalID: env.GetOrDefaultString(EnvExternalID, ""), + WaitForRecordSetsChanged: env.GetOrDefaultBool(EnvWaitForRecordSetsChanged, true), + TTL: env.GetOrDefaultInt(EnvTTL, 10), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 4*time.Second), @@ -235,19 +241,22 @@ func (d *DNSProvider) changeRecord(ctx context.Context, action awstypes.ChangeAc changeID := resp.ChangeInfo.Id - return wait.For("route53", d.config.PropagationTimeout, d.config.PollingInterval, func() (bool, error) { - reqParams := &route53.GetChangeInput{Id: changeID} + if d.config.WaitForRecordSetsChanged { + return wait.For("route53", d.config.PropagationTimeout, d.config.PollingInterval, func() (bool, error) { + resp, err := d.client.GetChange(ctx, &route53.GetChangeInput{Id: changeID}) + if err != nil { + return false, fmt.Errorf("failed to query change status: %w", err) + } - resp, err := d.client.GetChange(ctx, reqParams) - if err != nil { - return false, fmt.Errorf("failed to query change status: %w", err) - } + if resp.ChangeInfo.Status == awstypes.ChangeStatusInsync { + return true, nil + } - if resp.ChangeInfo.Status == awstypes.ChangeStatusInsync { - return true, nil - } - return false, fmt.Errorf("unable to retrieve change: ID=%s", deref(changeID)) - }) + return false, fmt.Errorf("unable to retrieve change: ID=%s", deref(changeID)) + }) + } + + return nil } func (d *DNSProvider) getExistingRecordSets(ctx context.Context, hostedZoneID, fqdn string) ([]awstypes.ResourceRecord, error) { diff --git a/providers/dns/route53/route53.toml b/providers/dns/route53/route53.toml index f16541e3..da8b489a 100644 --- a/providers/dns/route53/route53.toml +++ b/providers/dns/route53/route53.toml @@ -131,6 +131,7 @@ Replace `Z11111112222222333333` with your hosted zone ID and `example.com` with AWS_SDK_LOAD_CONFIG = "Managed by the AWS client. Retrieve the region from the CLI config file (`AWS_SDK_LOAD_CONFIG_FILE` is not supported)" AWS_ASSUME_ROLE_ARN = "Managed by the AWS Role ARN (`AWS_ASSUME_ROLE_ARN_FILE` is not supported)" AWS_EXTERNAL_ID = "Managed by STS AssumeRole API operation (`AWS_EXTERNAL_ID_FILE` is not supported)" + AWS_WAIT_FOR_RECORD_SETS_CHANGED = "Wait for changes to be INSYNC (it can be unstable)" [Configuration.Additional] AWS_SHARED_CREDENTIALS_FILE = "Managed by the AWS client. Shared credentials file." AWS_MAX_RETRIES = "The number of maximum returns the service will use to make an individual API request" diff --git a/providers/dns/route53/route53_test.go b/providers/dns/route53/route53_test.go index 1c8e5f5f..4301ca10 100644 --- a/providers/dns/route53/route53_test.go +++ b/providers/dns/route53/route53_test.go @@ -25,7 +25,8 @@ var envTest = tester.NewEnvTest( EnvMaxRetries, EnvTTL, EnvPropagationTimeout, - EnvPollingInterval). + EnvPollingInterval, + EnvWaitForRecordSetsChanged). WithDomain(envDomain). WithLiveTestRequirements(EnvAccessKeyID, EnvSecretAccessKey, EnvRegion, envDomain) @@ -119,20 +120,22 @@ func TestNewDefaultConfig(t *testing.T) { { desc: "default configuration", expected: &Config{ - MaxRetries: 5, - TTL: 10, - PropagationTimeout: 2 * time.Minute, - PollingInterval: 4 * time.Second, + MaxRetries: 5, + TTL: 10, + PropagationTimeout: 2 * time.Minute, + PollingInterval: 4 * time.Second, + WaitForRecordSetsChanged: true, }, }, { - desc: "", + desc: "set values", envVars: map[string]string{ - EnvMaxRetries: "10", - EnvTTL: "99", - EnvPropagationTimeout: "60", - EnvPollingInterval: "60", - EnvHostedZoneID: "abc123", + EnvMaxRetries: "10", + EnvTTL: "99", + EnvPropagationTimeout: "60", + EnvPollingInterval: "60", + EnvHostedZoneID: "abc123", + EnvWaitForRecordSetsChanged: "false", }, expected: &Config{ MaxRetries: 10,