line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright (c) 2013 David Caldwell, All Rights Reserved. |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Net::DNS::Create::Route53; |
4
|
1
|
|
|
1
|
|
7
|
use feature ':5.10'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
143
|
|
5
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
6
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
38
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
6
|
use Net::DNS::Create qw(internal full_host email interval); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
9
|
1
|
|
|
1
|
|
1014
|
use Net::Amazon::Route53; |
|
1
|
|
|
|
|
141245
|
|
|
1
|
|
|
|
|
1944
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our %config; |
12
|
|
|
|
|
|
|
sub import { |
13
|
1
|
|
|
1
|
|
2
|
my $package = shift; |
14
|
1
|
|
|
|
|
6
|
my %c = @_; |
15
|
1
|
|
|
|
|
8
|
$config{$_} = $c{$_} for keys %c; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my $r53; |
19
|
|
|
|
|
|
|
sub r53() { |
20
|
0
|
|
0
|
0
|
0
|
0
|
$r53 //= Net::Amazon::Route53->new(id => $config{amazon_id}, |
21
|
|
|
|
|
|
|
key => $config{amazon_key}); |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $zones; |
25
|
|
|
|
|
|
|
sub hosted_zone($) { |
26
|
|
|
|
|
|
|
# The eval works around a bug in Net::Amazon::Route53 where it dies if there are no zones at all. |
27
|
0
|
0
|
0
|
0
|
0
|
0
|
$zones = eval { [r53->get_hosted_zones()] } || [] unless defined $zones; |
28
|
0
|
|
0
|
|
|
0
|
(grep { $_->name eq $_[0] } @$zones)[0] // undef; |
|
0
|
|
|
|
|
0
|
|
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub txt(@) { |
32
|
5
|
|
|
5
|
0
|
185
|
map { "\"$_\"" } @_; |
|
6
|
|
|
|
|
77
|
|
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub group_by_type_and_name($$) { |
36
|
1
|
|
|
1
|
0
|
2
|
my ($re, $entries) = @_; |
37
|
1
|
|
|
|
|
2
|
my %set; |
38
|
1
|
|
|
|
|
3
|
for my $r (grep { lc($_->type) =~ $re } @$entries) { |
|
26
|
|
|
|
|
293
|
|
39
|
10
|
|
|
|
|
274
|
push @{$set{$r->type .'_'. $r->name}}, $r; |
|
10
|
|
|
|
|
64
|
|
40
|
|
|
|
|
|
|
} |
41
|
1
|
|
|
|
|
50
|
map { $set{$_} } keys %set; |
|
7
|
|
|
|
|
25
|
|
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my @domain; |
45
|
1
|
|
|
1
|
|
48
|
sub _domain() { @domain } # Hook for testing |
46
|
|
|
|
|
|
|
sub domain($$) { |
47
|
1
|
|
|
1
|
0
|
4
|
my ($package, $domain, $entries) = @_; |
48
|
|
|
|
|
|
|
|
49
|
1
|
|
|
|
|
3
|
my @entries = map { ; |
50
|
26
|
|
|
|
|
795
|
my $rr = lc $_->type; |
51
|
|
|
|
|
|
|
|
52
|
26
|
50
|
33
|
|
|
786
|
$rr eq 'soa' ? () : # Amazon manages its own SOA stuff. Just ignore things we might have. |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
53
|
|
|
|
|
|
|
$rr eq 'rp' ? (warn("Amazon doesn't support RP records :-(") && ()) : |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$rr eq 'mx' || $rr eq 'ns' || $rr eq 'srv' || $rr eq 'txt' ? () : # Handled specially, below |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
+{ |
58
|
|
|
|
|
|
|
action => 'create', |
59
|
|
|
|
|
|
|
name => $_->name.'.', |
60
|
|
|
|
|
|
|
ttl => $_->ttl, |
61
|
|
|
|
|
|
|
type => uc $rr, |
62
|
|
|
|
|
|
|
$rr eq 'a' ? (value => $_->address) : |
63
|
|
|
|
|
|
|
$rr eq 'cname' ? (value => $_->cname.'.') : |
64
|
|
|
|
|
|
|
(err => warn "Don't know how to handle \"$rr\" RRs yet.") |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} @$entries; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Amazon wants all NS,MX,TXT and SRV entries for a particular name in one of their entries. We get them in as |
70
|
|
|
|
|
|
|
# separate entries so first we have to group them together. |
71
|
1
|
|
|
|
|
54
|
push @entries, map { my @set = @$_; |
|
7
|
|
|
|
|
225
|
|
72
|
7
|
|
|
|
|
31
|
my $rr = lc $set[0]->type; |
73
|
2
|
|
|
|
|
40
|
$rr eq 'ns' && $set[0]->name.'.' eq $domain ? () : # Amazon manages its own NS stuff. Just ignore things we might have. |
74
|
|
|
|
|
|
|
+{ |
75
|
|
|
|
|
|
|
action => 'create', |
76
|
|
|
|
|
|
|
name => $set[0]->name.'.', |
77
|
|
|
|
|
|
|
ttl => $set[0]->ttl, |
78
|
|
|
|
|
|
|
type => uc $rr, |
79
|
0
|
|
|
|
|
0
|
$rr eq 'mx' ? (records => [map { $_->preference." ".$_->exchange.'.' } @set]) : |
80
|
1
|
|
|
|
|
12679
|
$rr eq 'ns' ? (records => [map { $_->nsdname.'.' } @set] ) : |
81
|
5
|
|
|
|
|
102
|
$rr eq 'srv' ? (records => [map { $_->priority ." ".$_->weight ." ".$_->port ." ".$_->target.'.' } @set]) : |
82
|
7
|
50
|
66
|
|
|
141
|
$rr eq 'txt' ? (records => [map { join ' ', txt($_->char_str_list) } @set]) : |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
83
|
|
|
|
|
|
|
(err => die uc($rr)." can't happen here!") |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} group_by_type_and_name(qr/^(?:mx|ns|srv|txt)$/, $entries); |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
|
|
104
|
push @domain, { name => $domain, |
88
|
|
|
|
|
|
|
entries => \@entries }; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
my $counter = rand(1000); |
92
|
|
|
|
|
|
|
sub master() { |
93
|
0
|
|
|
0
|
0
|
|
my ($package) = @_; |
94
|
0
|
|
|
|
|
|
local $|=1; |
95
|
|
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
for my $domain (@domain) { |
97
|
0
|
|
|
|
|
|
my $zone = hosted_zone(full_host($domain->{name})); |
98
|
0
|
0
|
0
|
|
|
|
if (!$zone && scalar @{$domain->{entries}}) { |
|
0
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
|
my $hostedzone = Net::Amazon::Route53::HostedZone->new(route53 => r53, |
100
|
|
|
|
|
|
|
name => $domain->{name}, |
101
|
|
|
|
|
|
|
comment=>(getpwuid($<))[0].'/'.__PACKAGE__, |
102
|
|
|
|
|
|
|
callerreference=>__PACKAGE__."-".localtime."-".($counter++)); |
103
|
0
|
|
|
|
|
|
print "New Zone: $domain->{name}..."; |
104
|
0
|
|
|
|
|
|
$hostedzone->create(); |
105
|
0
|
|
|
|
|
|
$zone = $hostedzone; |
106
|
0
|
|
|
|
|
|
print "Created. Nameservers:\n".join('', map { " $_\n" } @{$zone->nameservers}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
0
|
0
|
|
|
|
|
if ($zone) { |
110
|
0
|
0
|
0
|
|
|
|
my $current = [ grep { $_->type ne 'SOA' && ($_->type ne 'NS' || $_->name ne $domain->{name}) } @{$zone->resource_record_sets} ]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
my $new = [ map { Net::Amazon::Route53::ResourceRecordSet->new(%{$_}, |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
112
|
0
|
|
0
|
|
|
|
values => [$_->{value} // @{$_->{records}}], |
113
|
|
|
|
|
|
|
route53 => r53, |
114
|
0
|
|
|
|
|
|
hostedzone => $zone) } @{$domain->{entries}} ]; |
115
|
0
|
|
|
|
|
|
printf "%s: %d -> %d\n", $domain->{name}, scalar @$current, scalar @$new; |
116
|
0
|
0
|
|
|
|
|
my $change = scalar @$current > 0 ? r53->atomic_update($current,$new) : |
|
|
0
|
|
|
|
|
|
117
|
|
|
|
|
|
|
scalar @$new > 0 ? r53->batch_create($new) : |
118
|
|
|
|
|
|
|
undef; |
119
|
|
|
|
|
|
|
|
120
|
0
|
0
|
|
|
|
|
unless (scalar @{$domain->{entries}}) { |
|
0
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
print "Deleting $domain->{name}\n"; |
122
|
0
|
|
|
|
|
|
$zone->delete; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub domain_list($@) { |
129
|
0
|
|
|
0
|
0
|
|
my $zone = hosted_zone(full_host($_[0])); |
130
|
0
|
0
|
|
|
|
|
printf "%-30s %-30s %s\n", $zone ? $zone->id : '', $_[0], !$zone ? '' : ' ['.join(" ",@{$zone->nameservers}).']'; |
|
0
|
0
|
|
|
|
|
|
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
0
|
|
|
0
|
0
|
|
sub master_list($$) { |
134
|
|
|
|
|
|
|
# This doesn't really make sense in the route53 context |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
1; |
138
|
|
|
|
|
|
|
__END__ |