|
@@ -32,11 +32,13 @@ import (
|
|
|
"gopkg.in/urfave/cli.v1"
|
|
"gopkg.in/urfave/cli.v1"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-// Route53 limits change sets to 32k of 'RDATA size'. DNS changes need to be split up into
|
|
|
|
|
-// multiple batches to work around the limit. Unfortunately I cannot find any
|
|
|
|
|
-// documentation explaining how the RDATA size of a change set is computed and the best we
|
|
|
|
|
-// can do is estimate it. For this reason, our internal limit is much lower than 32k.
|
|
|
|
|
-const route53ChangeLimit = 20000
|
|
|
|
|
|
|
+const (
|
|
|
|
|
+ // Route53 limits change sets to 32k of 'RDATA size'. Change sets are also limited to
|
|
|
|
|
+ // 1000 items. UPSERTs count double.
|
|
|
|
|
+ // https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-changeresourcerecordsets
|
|
|
|
|
+ route53ChangeSizeLimit = 32000
|
|
|
|
|
+ route53ChangeCountLimit = 1000
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
|
route53AccessKeyFlag = cli.StringFlag{
|
|
route53AccessKeyFlag = cli.StringFlag{
|
|
@@ -104,7 +106,7 @@ func (c *route53Client) deploy(name string, t *dnsdisc.Tree) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Submit change batches.
|
|
// Submit change batches.
|
|
|
- batches := splitChanges(changes, route53ChangeLimit)
|
|
|
|
|
|
|
+ batches := splitChanges(changes, route53ChangeSizeLimit, route53ChangeCountLimit)
|
|
|
for i, changes := range batches {
|
|
for i, changes := range batches {
|
|
|
log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes)))
|
|
log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes)))
|
|
|
batch := new(route53.ChangeBatch)
|
|
batch := new(route53.ChangeBatch)
|
|
@@ -178,7 +180,7 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e
|
|
|
// Entry is unknown, push a new one
|
|
// Entry is unknown, push a new one
|
|
|
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
|
|
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
|
|
|
changes = append(changes, newTXTChange("CREATE", path, ttl, splitTXT(val)))
|
|
changes = append(changes, newTXTChange("CREATE", path, ttl, splitTXT(val)))
|
|
|
- } else if prevValue != val {
|
|
|
|
|
|
|
+ } else if prevValue != val || prevRecords.ttl != ttl {
|
|
|
// Entry already exists, only change its content.
|
|
// Entry already exists, only change its content.
|
|
|
log.Info(fmt.Sprintf("Updating %s from %q to %q", path, prevValue, val))
|
|
log.Info(fmt.Sprintf("Updating %s from %q to %q", path, prevValue, val))
|
|
|
changes = append(changes, newTXTChange("UPSERT", path, ttl, splitTXT(val)))
|
|
changes = append(changes, newTXTChange("UPSERT", path, ttl, splitTXT(val)))
|
|
@@ -214,18 +216,26 @@ func sortChanges(changes []*route53.Change) {
|
|
|
|
|
|
|
|
// splitChanges splits up DNS changes such that each change batch
|
|
// splitChanges splits up DNS changes such that each change batch
|
|
|
// is smaller than the given RDATA limit.
|
|
// is smaller than the given RDATA limit.
|
|
|
-func splitChanges(changes []*route53.Change, limit int) [][]*route53.Change {
|
|
|
|
|
- var batches [][]*route53.Change
|
|
|
|
|
- var batchSize int
|
|
|
|
|
|
|
+func splitChanges(changes []*route53.Change, sizeLimit, countLimit int) [][]*route53.Change {
|
|
|
|
|
+ var (
|
|
|
|
|
+ batches [][]*route53.Change
|
|
|
|
|
+ batchSize int
|
|
|
|
|
+ batchCount int
|
|
|
|
|
+ )
|
|
|
for _, ch := range changes {
|
|
for _, ch := range changes {
|
|
|
// Start new batch if this change pushes the current one over the limit.
|
|
// Start new batch if this change pushes the current one over the limit.
|
|
|
- size := changeSize(ch)
|
|
|
|
|
- if len(batches) == 0 || batchSize+size > limit {
|
|
|
|
|
|
|
+ count := changeCount(ch)
|
|
|
|
|
+ size := changeSize(ch) * count
|
|
|
|
|
+ overSize := batchSize+size > sizeLimit
|
|
|
|
|
+ overCount := batchCount+count > countLimit
|
|
|
|
|
+ if len(batches) == 0 || overSize || overCount {
|
|
|
batches = append(batches, nil)
|
|
batches = append(batches, nil)
|
|
|
batchSize = 0
|
|
batchSize = 0
|
|
|
|
|
+ batchCount = 0
|
|
|
}
|
|
}
|
|
|
batches[len(batches)-1] = append(batches[len(batches)-1], ch)
|
|
batches[len(batches)-1] = append(batches[len(batches)-1], ch)
|
|
|
batchSize += size
|
|
batchSize += size
|
|
|
|
|
+ batchCount += count
|
|
|
}
|
|
}
|
|
|
return batches
|
|
return batches
|
|
|
}
|
|
}
|
|
@@ -241,6 +251,13 @@ func changeSize(ch *route53.Change) int {
|
|
|
return size
|
|
return size
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func changeCount(ch *route53.Change) int {
|
|
|
|
|
+ if *ch.Action == "UPSERT" {
|
|
|
|
|
+ return 2
|
|
|
|
|
+ }
|
|
|
|
|
+ return 1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// collectRecords collects all TXT records below the given name.
|
|
// collectRecords collects all TXT records below the given name.
|
|
|
func (c *route53Client) collectRecords(name string) (map[string]recordSet, error) {
|
|
func (c *route53Client) collectRecords(name string) (map[string]recordSet, error) {
|
|
|
log.Info(fmt.Sprintf("Retrieving existing TXT records on %s (%s)", name, c.zoneID))
|
|
log.Info(fmt.Sprintf("Retrieving existing TXT records on %s (%s)", name, c.zoneID))
|