Port to AWS Go SDK v2 (#6588)

This commit is contained in:
Stephen Kitt 2025-07-03 10:19:21 +01:00 committed by GitHub
parent ae5e03a94d
commit 1449cb660e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 102 additions and 78 deletions

9
go.mod
View File

@ -9,8 +9,11 @@ require (
github.com/Azure/go-autorest/autorest v0.11.30
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
github.com/apparentlymart/go-cidr v1.1.0
github.com/aws/aws-sdk-go v1.55.7
github.com/aws/aws-sdk-go-v2 v1.36.5
github.com/aws/aws-sdk-go-v2/config v1.29.17
github.com/aws/aws-sdk-go-v2/credentials v1.17.70
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7
github.com/coredns/caddy v1.1.2-0.20241029205200-8de985351a98
github.com/dnstap/golang-dnstap v0.4.0
@ -75,9 +78,6 @@ require (
github.com/DataDog/sketches-go v1.4.7 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
@ -120,7 +120,6 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect

11
go.sum
View File

@ -72,8 +72,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0=
github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0=
github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0=
@ -92,6 +90,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2 h1:dXHWVVPx2W2fq2PTugj8QXpJ0YTRAGx0KLPKhMBmcsY=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.2/go.mod h1:wi1naoiPnCQG3cyjsivwPON1ZmQt/EJGxFqXzubBTAw=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7 h1:d+mnMa4JbJlooSbYQfrJpit/YINaB30JEVgrhtjZneA=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7/go.mod h1:1X1NotbcGHH7PCQJ98PsExSxsJj/VWzz8MfFz43+02M=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg=
@ -213,10 +213,6 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA=
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -533,10 +529,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -17,9 +17,9 @@ import (
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/coredns/coredns/request"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/route53/route53iface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/route53"
"github.com/aws/aws-sdk-go-v2/service/route53/types"
"github.com/miekg/dns"
)
@ -29,7 +29,7 @@ type Route53 struct {
Fall fall.F
zoneNames []string
client route53iface.Route53API
client route53Client
upstream *upstream.Upstream
refresh time.Duration
@ -50,12 +50,12 @@ type zones map[string][]*zone
// does exist, and returns a new *Route53. In addition to this, upstream is use
// for doing recursive queries against CNAMEs. Returns error if it cannot
// verify any given domain name/zone id pair.
func New(ctx context.Context, c route53iface.Route53API, keys map[string][]string, refresh time.Duration) (*Route53, error) {
func New(ctx context.Context, c route53Client, keys map[string][]string, refresh time.Duration) (*Route53, error) {
zones := make(map[string][]*zone, len(keys))
zoneNames := make([]string, 0, len(keys))
for dns, hostedZoneIDs := range keys {
for _, hostedZoneID := range hostedZoneIDs {
_, err := c.ListHostedZonesByNameWithContext(ctx, &route53.ListHostedZonesByNameInput{
_, err := c.ListHostedZonesByName(ctx, &route53.ListHostedZonesByNameInput{
DNSName: aws.String(dns),
HostedZoneId: aws.String(hostedZoneID),
})
@ -211,19 +211,19 @@ func maybeUnescape(s string) (string, error) {
}
}
func updateZoneFromRRS(rrs *route53.ResourceRecordSet, z *file.Zone) error {
func updateZoneFromRRS(rrs *types.ResourceRecordSet, z *file.Zone) error {
for _, rr := range rrs.ResourceRecords {
n, err := maybeUnescape(aws.StringValue(rrs.Name))
n, err := maybeUnescape(aws.ToString(rrs.Name))
if err != nil {
return fmt.Errorf("failed to unescape `%s' name: %v", aws.StringValue(rrs.Name), err)
return fmt.Errorf("failed to unescape `%s' name: %v", aws.ToString(rrs.Name), err)
}
v, err := maybeUnescape(aws.StringValue(rr.Value))
v, err := maybeUnescape(aws.ToString(rr.Value))
if err != nil {
return fmt.Errorf("failed to unescape `%s' value: %v", aws.StringValue(rr.Value), err)
return fmt.Errorf("failed to unescape `%s' value: %v", aws.ToString(rr.Value), err)
}
// Assemble RFC 1035 conforming record to pass into dns scanner.
rfc1035 := fmt.Sprintf("%s %d IN %s %s", n, aws.Int64Value(rrs.TTL), aws.StringValue(rrs.Type), v)
rfc1035 := fmt.Sprintf("%s %d IN %s %s", n, aws.ToInt64(rrs.TTL), rrs.Type, v)
r, err := dns.NewRR(rfc1035)
if err != nil {
return fmt.Errorf("failed to parse resource record: %v", err)
@ -253,21 +253,28 @@ func (h *Route53) updateZones(ctx context.Context) error {
newZ.Upstream = h.upstream
in := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String(hostedZone.id),
MaxItems: aws.String("1000"),
MaxItems: aws.Int32(1000),
}
err = h.client.ListResourceRecordSetsPagesWithContext(ctx, in,
func(out *route53.ListResourceRecordSetsOutput, last bool) bool {
for _, rrs := range out.ResourceRecordSets {
if err := updateZoneFromRRS(rrs, newZ); err != nil {
// Maybe unsupported record type. Log and carry on.
log.Warningf("Failed to process resource record set: %v", err)
}
complete := false
var out *route53.ListResourceRecordSetsOutput
for out, err = h.client.ListResourceRecordSets(ctx, in); !complete; out, err = h.client.ListResourceRecordSets(ctx, in) {
if err != nil {
err = fmt.Errorf("failed to list resource records for %v:%v from route53: %v", zName, hostedZone.id, err)
return
}
for _, rrs := range out.ResourceRecordSets {
if err := updateZoneFromRRS(&rrs, newZ); err != nil {
// Maybe unsupported record type. Log and carry on.
log.Warningf("Failed to process resource record set: %v", err)
}
return true
})
if err != nil {
err = fmt.Errorf("failed to list resource records for %v:%v from route53: %v", zName, hostedZone.id, err)
return
}
if out.IsTruncated {
in.StartRecordName = out.NextRecordName
in.StartRecordType = out.NextRecordType
in.StartRecordIdentifier = out.NextRecordIdentifier
} else {
complete = true
}
}
h.zMu.Lock()
(*z[i]).z = newZ

View File

@ -12,28 +12,28 @@ import (
"github.com/coredns/coredns/plugin/test"
crequest "github.com/coredns/coredns/request"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/route53/route53iface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/route53"
"github.com/aws/aws-sdk-go-v2/service/route53/types"
"github.com/miekg/dns"
)
type fakeRoute53 struct {
route53iface.Route53API
route53Client
}
func (fakeRoute53) ListHostedZonesByNameWithContext(_ aws.Context, input *route53.ListHostedZonesByNameInput, _ ...request.Option) (*route53.ListHostedZonesByNameOutput, error) {
func (fakeRoute53) ListHostedZonesByName(_ context.Context, input *route53.ListHostedZonesByNameInput, optFns ...func(*route53.Options)) (*route53.ListHostedZonesByNameOutput, error) {
return nil, nil
}
func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *route53.ListResourceRecordSetsInput, fn func(*route53.ListResourceRecordSetsOutput, bool) bool, _ ...request.Option) error {
if aws.StringValue(in.HostedZoneId) == "0987654321" {
return errors.New("bad. zone is bad")
func (fakeRoute53) ListResourceRecordSets(_ context.Context, in *route53.ListResourceRecordSetsInput, optFns ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
if aws.ToString(in.HostedZoneId) == "0987654321" {
return nil, errors.New("bad. zone is bad")
}
rrsResponse := map[string][]*route53.ResourceRecordSet{}
rrsResponse := map[string][]types.ResourceRecordSet{}
for _, r := range []struct {
rType, name, value, hostedZoneID string
rType types.RRType
name, value, hostedZoneID string
}{
{"A", "example.org.", "1.2.3.4", "1234567890"},
{"A", "www.example.org", "1.2.3.4", "1234567890"},
@ -54,11 +54,11 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
} {
rrs, ok := rrsResponse[r.hostedZoneID]
if !ok {
rrs = make([]*route53.ResourceRecordSet, 0)
rrs = make([]types.ResourceRecordSet, 0)
}
rrs = append(rrs, &route53.ResourceRecordSet{Type: aws.String(r.rType),
rrs = append(rrs, types.ResourceRecordSet{Type: r.rType,
Name: aws.String(r.name),
ResourceRecords: []*route53.ResourceRecord{
ResourceRecords: []types.ResourceRecord{
{
Value: aws.String(r.value),
},
@ -68,12 +68,10 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
rrsResponse[r.hostedZoneID] = rrs
}
if ok := fn(&route53.ListResourceRecordSetsOutput{
ResourceRecordSets: rrsResponse[aws.StringValue(in.HostedZoneId)],
}, true); !ok {
return errors.New("paging function return false")
}
return nil
return &route53.ListResourceRecordSetsOutput{
ResourceRecordSets: rrsResponse[aws.ToString(in.HostedZoneId)],
IsTruncated: false,
}, nil
}
func TestRoute53(t *testing.T) {

View File

@ -14,12 +14,11 @@ import (
"github.com/coredns/coredns/plugin/pkg/fall"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/route53/route53iface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/route53"
)
var log = clog.NewWithPlugin("route53")
@ -27,8 +26,27 @@ var log = clog.NewWithPlugin("route53")
func init() { plugin.Register("route53", setup) }
// exposed for testing
var f = func(opts session.Options) route53iface.Route53API {
return route53.New(session.Must(session.NewSessionWithOptions(opts)))
type route53Client interface {
ActivateKeySigningKey(ctx context.Context, params *route53.ActivateKeySigningKeyInput, optFns ...func(*route53.Options)) (*route53.ActivateKeySigningKeyOutput, error)
ListHostedZonesByName(ctx context.Context, params *route53.ListHostedZonesByNameInput, optFns ...func(*route53.Options)) (*route53.ListHostedZonesByNameOutput, error)
ListResourceRecordSets(ctx context.Context, params *route53.ListResourceRecordSetsInput, optFns ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
}
var f = func(ctx context.Context, cfgOpts []func(*config.LoadOptions) error, clientOpts []func(*route53.Options)) (route53Client, error) {
cfg, err := config.LoadDefaultConfig(ctx, cfgOpts...)
if err != nil {
return nil, err
}
// If no region is specified, retrieve one from IMDS (SDK v1 used the AWS global partition as a fallback, v2 doesn't)
if cfg.Region == "" {
imdsClient := imds.NewFromConfig(cfg)
region, err := imdsClient.GetRegion(ctx, &imds.GetRegionInput{})
if err != nil {
return nil, fmt.Errorf("failed to get region from IMDS: %w", err)
}
cfg.Region = region.Region
}
return route53.NewFromConfig(cfg, clientOpts...), nil
}
func setup(c *caddy.Controller) error {
@ -43,7 +61,8 @@ func setup(c *caddy.Controller) error {
// * Shared Credentials file
// * Shared Configuration file (if AWS_SDK_LOAD_CONFIG is set to truthy value)
// * EC2 Instance Metadata (credentials only)
opts := session.Options{}
cfgOpts := []func(*config.LoadOptions) error{}
clientOpts := []func(*route53.Options){}
var fall fall.F
refresh := time.Duration(1) * time.Minute // default update frequency to 1 minute
@ -74,11 +93,13 @@ func setup(c *caddy.Controller) error {
if len(v) < 2 {
return plugin.Error("route53", c.Errf("invalid access key: '%v'", v))
}
opts.Config.Credentials = credentials.NewStaticCredentials(v[0], v[1], "")
cfgOpts = append(cfgOpts, config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(v[0], v[1], "")))
log.Warningf("Save aws_access_key in Corefile has been deprecated, please use other authentication methods instead")
case "aws_endpoint":
if c.NextArg() {
opts.Config.Endpoint = aws.String(c.Val())
clientOpts = append(clientOpts, func(o *route53.Options) {
o.BaseEndpoint = aws.String(c.Val())
})
} else {
return plugin.Error("route53", c.ArgErr())
}
@ -86,17 +107,18 @@ func setup(c *caddy.Controller) error {
c.RemainingArgs() // eats args
case "credentials":
if c.NextArg() {
opts.Profile = c.Val()
cfgOpts = append(cfgOpts, config.WithSharedConfigProfile(c.Val()))
} else {
return c.ArgErr()
}
if c.NextArg() {
opts.SharedConfigFiles = []string{c.Val()}
sharedConfigFiles := []string{c.Val()}
// If AWS_SDK_LOAD_CONFIG is set also load ~/.aws/config to stay consistent
// with default SDK behavior.
if ok, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG")); ok {
opts.SharedConfigFiles = append(opts.SharedConfigFiles, defaults.SharedConfigFilename())
sharedConfigFiles = append(sharedConfigFiles, config.DefaultSharedConfigFilename())
}
cfgOpts = append(cfgOpts, config.WithSharedConfigFiles(sharedConfigFiles))
}
case "fallthrough":
fall.SetZonesFromArgs(c.RemainingArgs())
@ -122,8 +144,12 @@ func setup(c *caddy.Controller) error {
}
}
client := f(opts)
ctx, cancel := context.WithCancel(context.Background())
client, err := f(ctx, cfgOpts, clientOpts)
if err != nil {
cancel()
return plugin.Error("route53", c.Errf("failed to create route53 client: %v", err))
}
h, err := New(ctx, client, keys, refresh)
if err != nil {
cancel()

View File

@ -1,17 +1,18 @@
package route53
import (
"context"
"testing"
"github.com/coredns/caddy"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53/route53iface"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/route53"
)
func TestSetupRoute53(t *testing.T) {
f = func(opts session.Options) route53iface.Route53API {
return fakeRoute53{}
f = func(_ context.Context, _ []func(*config.LoadOptions) error, _ []func(*route53.Options)) (route53Client, error) {
return fakeRoute53{}, nil
}
tests := []struct {