1717package main
1818
1919import (
20+ "context"
2021 "errors"
2122 "fmt"
2223 "sort"
2324 "strconv"
2425 "strings"
26+ "time"
2527
26- "github.com/aws/aws-sdk-go/aws"
27- "github.com/aws/aws-sdk-go/aws/credentials"
28- "github.com/aws/aws-sdk-go/aws/session"
29- "github.com/aws/aws-sdk-go/service/route53"
28+ "github.com/aws/aws-sdk-go-v2/aws"
29+ "github.com/aws/aws-sdk-go-v2/config"
30+ "github.com/aws/aws-sdk-go-v2/credentials"
31+ "github.com/aws/aws-sdk-go-v2/service/route53"
32+ "github.com/aws/aws-sdk-go-v2/service/route53/types"
3033 "github.com/ethereum/go-ethereum/log"
3134 "github.com/ethereum/go-ethereum/p2p/dnsdisc"
3235 "gopkg.in/urfave/cli.v1"
@@ -38,6 +41,7 @@ const (
3841 // https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-changeresourcerecordsets
3942 route53ChangeSizeLimit = 32000
4043 route53ChangeCountLimit = 1000
44+ maxRetryLimit = 60
4145)
4246
4347var (
5862)
5963
6064type route53Client struct {
61- api * route53.Route53
65+ api * route53.Client
6266 zoneID string
6367}
6468
@@ -74,13 +78,13 @@ func newRoute53Client(ctx *cli.Context) *route53Client {
7478 if akey == "" || asec == "" {
7579 exit (fmt .Errorf ("need Route53 Access Key ID and secret proceed" ))
7680 }
77- config := & aws.Config { Credentials : credentials .NewStaticCredentials (akey , asec , "" )}
78- session , err := session . NewSession ( config )
81+ creds := aws .NewCredentialsCache ( credentials .NewStaticCredentialsProvider (akey , asec , "" ))
82+ cfg , err := config . LoadDefaultConfig ( context . Background (), config . WithCredentialsProvider ( creds ) )
7983 if err != nil {
80- exit (fmt .Errorf ("can't create AWS session : %v" , err ))
84+ exit (fmt .Errorf ("can't initialize AWS configuration : %v" , err ))
8185 }
8286 return & route53Client {
83- api : route53 .New ( session ),
87+ api : route53 .NewFromConfig ( cfg ),
8488 zoneID : ctx .String (route53ZoneIDFlag .Name ),
8589 }
8690}
@@ -105,25 +109,43 @@ func (c *route53Client) deploy(name string, t *dnsdisc.Tree) error {
105109 return nil
106110 }
107111
108- // Submit change batches.
112+ // Submit all change batches.
109113 batches := splitChanges (changes , route53ChangeSizeLimit , route53ChangeCountLimit )
114+ changesToCheck := make ([]* route53.ChangeResourceRecordSetsOutput , len (batches ))
110115 for i , changes := range batches {
111116 log .Info (fmt .Sprintf ("Submitting %d changes to Route53" , len (changes )))
112- batch := new (route53.ChangeBatch )
113- batch .SetChanges (changes )
114- batch .SetComment (fmt .Sprintf ("enrtree update %d/%d of %s at seq %d" , i + 1 , len (batches ), name , t .Seq ()))
117+ batch := & types.ChangeBatch {
118+ Changes : changes ,
119+ Comment : aws .String (fmt .Sprintf ("enrtree update %d/%d of %s at seq %d" , i + 1 , len (batches ), name , t .Seq ())),
120+ }
115121 req := & route53.ChangeResourceRecordSetsInput {HostedZoneId : & c .zoneID , ChangeBatch : batch }
116- resp , err : = c .api .ChangeResourceRecordSets (req )
122+ changesToCheck [ i ] , err = c .api .ChangeResourceRecordSets (context . TODO (), req )
117123 if err != nil {
118124 return err
119125 }
126+ }
120127
121- log .Info (fmt .Sprintf ("Waiting for change request %s" , * resp .ChangeInfo .Id ))
122- wreq := & route53.GetChangeInput {Id : resp .ChangeInfo .Id }
123- if err := c .api .WaitUntilResourceRecordSetsChanged (wreq ); err != nil {
124- return err
128+ // wait for all change batches to propagate
129+ for _ , change := range changesToCheck {
130+ log .Info (fmt .Sprintf ("Waiting for change request %s" , * change .ChangeInfo .Id ))
131+ wreq := & route53.GetChangeInput {Id : change .ChangeInfo .Id }
132+ var count int
133+ for {
134+ wresp , err := c .api .GetChange (context .TODO (), wreq )
135+ if err != nil {
136+ return err
137+ }
138+
139+ count ++
140+
141+ if wresp .ChangeInfo .Status == types .ChangeStatusInsync || count >= maxRetryLimit {
142+ break
143+ }
144+
145+ time .Sleep (30 * time .Second )
125146 }
126147 }
148+
127149 return nil
128150}
129151
@@ -140,7 +162,7 @@ func (c *route53Client) findZoneID(name string) (string, error) {
140162 log .Info (fmt .Sprintf ("Finding Route53 Zone ID for %s" , name ))
141163 var req route53.ListHostedZonesByNameInput
142164 for {
143- resp , err := c .api .ListHostedZonesByName (& req )
165+ resp , err := c .api .ListHostedZonesByName (context . TODO (), & req )
144166 if err != nil {
145167 return "" , err
146168 }
@@ -149,7 +171,7 @@ func (c *route53Client) findZoneID(name string) (string, error) {
149171 return * zone .Id , nil
150172 }
151173 }
152- if ! * resp .IsTruncated {
174+ if ! resp .IsTruncated {
153175 break
154176 }
155177 req .DNSName = resp .NextDNSName
@@ -159,15 +181,15 @@ func (c *route53Client) findZoneID(name string) (string, error) {
159181}
160182
161183// computeChanges creates DNS changes for the given record.
162- func (c * route53Client ) computeChanges (name string , records map [string ]string , existing map [string ]recordSet ) []* route53 .Change {
184+ func (c * route53Client ) computeChanges (name string , records map [string ]string , existing map [string ]recordSet ) []types .Change {
163185 // Convert all names to lowercase.
164186 lrecords := make (map [string ]string , len (records ))
165187 for name , r := range records {
166188 lrecords [strings .ToLower (name )] = r
167189 }
168190 records = lrecords
169191
170- var changes []* route53 .Change
192+ var changes []types .Change
171193 for path , val := range records {
172194 ttl := int64 (rootTTL )
173195 if path != name {
@@ -204,21 +226,21 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e
204226}
205227
206228// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
207- func sortChanges (changes []* route53 .Change ) {
229+ func sortChanges (changes []types .Change ) {
208230 score := map [string ]int {"CREATE" : 1 , "UPSERT" : 2 , "DELETE" : 3 }
209231 sort .Slice (changes , func (i , j int ) bool {
210- if * changes [i ].Action == * changes [j ].Action {
232+ if changes [i ].Action == changes [j ].Action {
211233 return * changes [i ].ResourceRecordSet .Name < * changes [j ].ResourceRecordSet .Name
212234 }
213- return score [* changes [i ].Action ] < score [* changes [j ].Action ]
235+ return score [string ( changes [i ].Action ) ] < score [string ( changes [j ].Action ) ]
214236 })
215237}
216238
217239// splitChanges splits up DNS changes such that each change batch
218240// is smaller than the given RDATA limit.
219- func splitChanges (changes []* route53 .Change , sizeLimit , countLimit int ) [][]* route53 .Change {
241+ func splitChanges (changes []types .Change , sizeLimit , countLimit int ) [][]types .Change {
220242 var (
221- batches [][]* route53 .Change
243+ batches [][]types .Change
222244 batchSize int
223245 batchCount int
224246 )
@@ -241,7 +263,7 @@ func splitChanges(changes []*route53.Change, sizeLimit, countLimit int) [][]*rou
241263}
242264
243265// changeSize returns the RDATA size of a DNS change.
244- func changeSize (ch * route53 .Change ) int {
266+ func changeSize (ch types .Change ) int {
245267 size := 0
246268 for _ , rr := range ch .ResourceRecordSet .ResourceRecords {
247269 if rr .Value != nil {
@@ -251,8 +273,8 @@ func changeSize(ch *route53.Change) int {
251273 return size
252274}
253275
254- func changeCount (ch * route53 .Change ) int {
255- if * ch .Action == "UPSERT" {
276+ func changeCount (ch types .Change ) int {
277+ if ch .Action == types . ChangeActionUpsert {
256278 return 2
257279 }
258280 return 1
@@ -262,42 +284,58 @@ func changeCount(ch *route53.Change) int {
262284func (c * route53Client ) collectRecords (name string ) (map [string ]recordSet , error ) {
263285 log .Info (fmt .Sprintf ("Retrieving existing TXT records on %s (%s)" , name , c .zoneID ))
264286 var req route53.ListResourceRecordSetsInput
265- req .SetHostedZoneId ( c .zoneID )
287+ req .HostedZoneId = & c .zoneID
266288 existing := make (map [string ]recordSet )
267- err := c .api .ListResourceRecordSetsPages (& req , func (resp * route53.ListResourceRecordSetsOutput , last bool ) bool {
289+ for {
290+ resp , err := c .api .ListResourceRecordSets (context .TODO (), & req )
291+ if err != nil {
292+ return existing , err
293+ }
294+
268295 for _ , set := range resp .ResourceRecordSets {
269- if ! isSubdomain (* set .Name , name ) || * set .Type != "TXT" {
296+ if ! isSubdomain (* set .Name , name ) || set .Type != types . RRTypeTxt {
270297 continue
271298 }
299+
272300 s := recordSet {ttl : * set .TTL }
273301 for _ , rec := range set .ResourceRecords {
274302 s .values = append (s .values , * rec .Value )
275303 }
276304 name := strings .TrimSuffix (* set .Name , "." )
277305 existing [name ] = s
278306 }
279- return true
280- })
281- return existing , err
307+
308+ if ! resp .IsTruncated {
309+ break
310+ }
311+
312+ // sets the cursor to the next batch
313+ req .StartRecordIdentifier = resp .NextRecordIdentifier
314+ }
315+
316+ return existing , nil
282317}
283318
284319// newTXTChange creates a change to a TXT record.
285- func newTXTChange (action , name string , ttl int64 , values ... string ) * route53.Change {
286- var c route53.Change
287- var r route53.ResourceRecordSet
288- var rrs []* route53.ResourceRecord
320+ func newTXTChange (action , name string , ttl int64 , values ... string ) types.Change {
321+ r := types.ResourceRecordSet {
322+ Type : types .RRTypeTxt ,
323+ Name : & name ,
324+ TTL : & ttl ,
325+ }
326+ var rrs []types.ResourceRecord
289327 for _ , val := range values {
290- rr := new (route53 .ResourceRecord )
291- rr .SetValue (val )
328+ var rr types .ResourceRecord
329+ rr .Value = aws . String (val )
292330 rrs = append (rrs , rr )
293331 }
294- r . SetType ( "TXT" )
295- r .SetName ( name )
296- r . SetTTL ( ttl )
297- r . SetResourceRecords ( rrs )
298- c . SetAction (action )
299- c . SetResourceRecordSet ( & r )
300- return & c
332+
333+ r .ResourceRecords = rrs
334+
335+ return types. Change {
336+ Action : types . ChangeAction (action ),
337+ ResourceRecordSet : & r ,
338+ }
301339}
302340
303341// isSubdomain returns true if name is a subdomain of domain.
0 commit comments