sprinkle_dns 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +76 -0
- data/LICENSE +21 -0
- data/README.md +181 -0
- data/Rakefile +1 -0
- data/examples/example01.rb +47 -0
- data/examples/example02.rb +61 -0
- data/examples/example03.rb +27 -0
- data/examples/example04.rb +22 -0
- data/lib/sprinkle_dns/cli/hosted_zone_diff.rb +206 -0
- data/lib/sprinkle_dns/cli/interactive_change_request_printer.rb +32 -0
- data/lib/sprinkle_dns/cli/propagated_change_request_printer.rb +33 -0
- data/lib/sprinkle_dns/client.rb +169 -0
- data/lib/sprinkle_dns/config.rb +43 -0
- data/lib/sprinkle_dns/core_ext/array_wrap.rb +11 -0
- data/lib/sprinkle_dns/core_ext/zonify.rb +4 -0
- data/lib/sprinkle_dns/entry_policy_service.rb +100 -0
- data/lib/sprinkle_dns/exceptions.rb +9 -0
- data/lib/sprinkle_dns/hosted_zone.rb +36 -0
- data/lib/sprinkle_dns/hosted_zone_alias.rb +91 -0
- data/lib/sprinkle_dns/hosted_zone_domain.rb +18 -0
- data/lib/sprinkle_dns/hosted_zone_entry.rb +97 -0
- data/lib/sprinkle_dns/providers/mock_client.rb +60 -0
- data/lib/sprinkle_dns/providers/route53_client.rb +155 -0
- data/lib/sprinkle_dns/version.rb +3 -0
- data/lib/sprinkle_dns.rb +5 -0
- data/logos/SDNS.png +0 -0
- data/logos/SDNS.svg +1 -0
- data/readme_files/delete_true_and_diff.png +0 -0
- data/readme_files/dry_run_and_diff.png +0 -0
- data/readme_files/force_false.png +0 -0
- data/spec/spec_helper.rb +110 -0
- data/spec/support/entry_helpers.rb +18 -0
- data/spec/unit/cli_hosted_zone_diff_spec.rb +30 -0
- data/spec/unit/hosted_zone_domain_spec.rb +12 -0
- data/spec/unit/hosted_zone_spec.rb +343 -0
- data/spec/unit/mock_client_spec.rb +59 -0
- data/spec/unit/sprinkle_dns_spec.rb +235 -0
- data/sprinkle_dns.gemspec +29 -0
- data/test_perms.rb.example +2 -0
- metadata +192 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module SprinkleDNS::CLI
|
2
|
+
class InteractiveChangeRequestPrinter
|
3
|
+
def initialize
|
4
|
+
reset!
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset!
|
8
|
+
@redraws = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def draw(change_requests, sync_word = "SYNCING", synced_word = "SYNCED")
|
12
|
+
lines = []
|
13
|
+
|
14
|
+
change_requests.each do |change_request|
|
15
|
+
dots = '.' * change_request.tries
|
16
|
+
sync = change_request.in_sync ? '✔' : '✘'
|
17
|
+
status = change_request.in_sync ? synced_word : sync_word
|
18
|
+
lines << "#{sync} #{status} #{change_request.hosted_zone.name}#{dots}"
|
19
|
+
end
|
20
|
+
|
21
|
+
if @redraws > 0
|
22
|
+
clear = "\r" + ("\e[A\e[K") * lines.size
|
23
|
+
puts clear + lines.join("\n")
|
24
|
+
else
|
25
|
+
puts lines.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
@redraws += 1
|
29
|
+
sleep(2)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SprinkleDNS::CLI
|
2
|
+
class PropagatedChangeRequestPrinter
|
3
|
+
def initialize
|
4
|
+
reset!
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset!
|
8
|
+
@completed = []
|
9
|
+
@printed = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def draw(change_requests, sync_word: "SYNCING", synced_word: "SYNCED")
|
13
|
+
change_requests.each do |change_request|
|
14
|
+
if change_request.in_sync
|
15
|
+
hosted_zone_name = change_request.hosted_zone.name
|
16
|
+
|
17
|
+
unless @completed.include?(hosted_zone_name)
|
18
|
+
@completed << hosted_zone_name + '.' * change_request.tries
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@completed.each do |complete|
|
24
|
+
unless @printed.include?(complete)
|
25
|
+
puts "#{complete}.. #{synced_word}!"
|
26
|
+
@printed << complete
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
sleep(2)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'sprinkle_dns/exceptions'
|
2
|
+
require 'sprinkle_dns/config'
|
3
|
+
require 'sprinkle_dns/hosted_zone'
|
4
|
+
require 'sprinkle_dns/hosted_zone_domain'
|
5
|
+
require 'sprinkle_dns/hosted_zone_entry'
|
6
|
+
require 'sprinkle_dns/hosted_zone_alias'
|
7
|
+
require 'sprinkle_dns/entry_policy_service'
|
8
|
+
|
9
|
+
require 'sprinkle_dns/cli/hosted_zone_diff'
|
10
|
+
require 'sprinkle_dns/cli/interactive_change_request_printer'
|
11
|
+
require 'sprinkle_dns/cli/propagated_change_request_printer'
|
12
|
+
|
13
|
+
require 'sprinkle_dns/core_ext/array_wrap'
|
14
|
+
require 'sprinkle_dns/core_ext/zonify'
|
15
|
+
|
16
|
+
module SprinkleDNS
|
17
|
+
class Client
|
18
|
+
attr_reader :wanted_hosted_zones, :config
|
19
|
+
|
20
|
+
def initialize(dns_provider, dry_run: false, diff: true, force: true, delete: false, interactive_progress: true, create_hosted_zones: false)
|
21
|
+
@config = SprinkleDNS::Config.new(
|
22
|
+
dry_run: dry_run,
|
23
|
+
diff: diff,
|
24
|
+
force: force,
|
25
|
+
delete: delete,
|
26
|
+
interactive_progress: interactive_progress,
|
27
|
+
create_hosted_zones: create_hosted_zones,
|
28
|
+
)
|
29
|
+
@dns_provider = dns_provider
|
30
|
+
@wanted_hosted_zones = []
|
31
|
+
|
32
|
+
@progress_printer = if @config.interactive_progress?
|
33
|
+
SprinkleDNS::CLI::InteractiveChangeRequestPrinter.new
|
34
|
+
else
|
35
|
+
SprinkleDNS::CLI::PropagatedChangeRequestPrinter.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def entry(type, name, value, ttl = 3600, hosted_zone = nil)
|
40
|
+
hosted_zone = find_or_init_hosted_zone(name, hosted_zone)
|
41
|
+
name = zonify!(name)
|
42
|
+
|
43
|
+
if ['CNAME', 'MX'].include?(type)
|
44
|
+
value = Array.wrap(value)
|
45
|
+
value.map!{|v| zonify!(v)}
|
46
|
+
end
|
47
|
+
hosted_zone.add_or_update_hosted_zone_entry(HostedZoneEntry.new(type, name, Array.wrap(value), ttl, hosted_zone.name))
|
48
|
+
end
|
49
|
+
|
50
|
+
def alias(type, name, hosted_zone_id, dns_name, hosted_zone = nil)
|
51
|
+
hosted_zone = find_or_init_hosted_zone(name, hosted_zone)
|
52
|
+
name = zonify!(name)
|
53
|
+
dns_name = zonify!(dns_name)
|
54
|
+
|
55
|
+
hosted_zone.add_or_update_hosted_zone_entry(HostedZoneAlias.new(type, name, hosted_zone_id, dns_name, hosted_zone.name))
|
56
|
+
end
|
57
|
+
|
58
|
+
def compare
|
59
|
+
existing_hosted_zones = @dns_provider.fetch_hosted_zones(filter: @wanted_hosted_zones.map(&:name))
|
60
|
+
|
61
|
+
# Tell our existing hosted zones about our wanted changes
|
62
|
+
existing_hosted_zones.each do |existing_hosted_zone|
|
63
|
+
wanted_hosted_zone = @wanted_hosted_zones.find{|whz| whz.name == existing_hosted_zone.name}
|
64
|
+
|
65
|
+
wanted_hosted_zone.resource_record_sets.each do |entry|
|
66
|
+
existing_hosted_zone.add_or_update_hosted_zone_entry(entry)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
[@wanted_hosted_zones, existing_hosted_zones]
|
71
|
+
end
|
72
|
+
|
73
|
+
def sprinkle!
|
74
|
+
wanted_hosted_zones, existing_hosted_zones = compare
|
75
|
+
|
76
|
+
missing_hosted_zone_names = wanted_hosted_zones.map(&:name) - existing_hosted_zones.map(&:name)
|
77
|
+
missing_hosted_zones = wanted_hosted_zones.select{|whz| missing_hosted_zone_names.include?(whz.name)}
|
78
|
+
|
79
|
+
if missing_hosted_zones.any? && !@config.create_hosted_zones?
|
80
|
+
missing_hosted_zones_error(missing_hosted_zones)
|
81
|
+
end
|
82
|
+
|
83
|
+
if @config.diff?
|
84
|
+
SprinkleDNS::CLI::HostedZoneDiff.new.diff(existing_hosted_zones, missing_hosted_zones, @config).each do |line|
|
85
|
+
puts line.join(' ')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if @config.dry_run?
|
90
|
+
return [existing_hosted_zones, nil]
|
91
|
+
end
|
92
|
+
|
93
|
+
hosted_zones = (existing_hosted_zones + missing_hosted_zones)
|
94
|
+
|
95
|
+
unless @config.force?
|
96
|
+
changes = hosted_zones.map{|h| SprinkleDNS::EntryPolicyService.new(h, @config)}.collect{|eps| eps.entries_to_change}.sum
|
97
|
+
|
98
|
+
messages = []
|
99
|
+
|
100
|
+
messages << "#{missing_hosted_zones.size} hosted-zone(s) to create" if missing_hosted_zones.any?
|
101
|
+
messages << "#{changes} change(s) to make" if changes > 0
|
102
|
+
print messages.join(' and ').concat(". Continue? (y/N)")
|
103
|
+
|
104
|
+
case gets.strip
|
105
|
+
when 'y', 'Y'
|
106
|
+
# continue
|
107
|
+
else
|
108
|
+
puts ".. exiting!"
|
109
|
+
return [hosted_zones, nil]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create missing hosted zones
|
114
|
+
change_requests_hosted_zones = @dns_provider.create_hosted_zones(missing_hosted_zones)
|
115
|
+
if change_requests_hosted_zones.any?
|
116
|
+
puts
|
117
|
+
puts "Creating hosted zones:"
|
118
|
+
@progress_printer.reset!
|
119
|
+
begin
|
120
|
+
@dns_provider.check_change_requests(change_requests_hosted_zones)
|
121
|
+
@progress_printer.draw(change_requests_hosted_zones, 'CREATING', 'CREATED')
|
122
|
+
end until change_requests_hosted_zones.all?{|cr| cr.in_sync}
|
123
|
+
end
|
124
|
+
|
125
|
+
# Update hosted zones
|
126
|
+
change_requests_entries = @dns_provider.change_hosted_zones(hosted_zones, @config)
|
127
|
+
if change_requests_entries.any?
|
128
|
+
puts
|
129
|
+
puts "Updating hosted zones:"
|
130
|
+
@progress_printer.reset!
|
131
|
+
begin
|
132
|
+
@dns_provider.check_change_requests(change_requests_entries)
|
133
|
+
@progress_printer.draw(change_requests_entries, 'UPDATING', 'UPDATED')
|
134
|
+
end until change_requests_entries.all?{|cr| cr.in_sync}
|
135
|
+
end
|
136
|
+
|
137
|
+
change_requests = change_requests_hosted_zones + change_requests_entries
|
138
|
+
|
139
|
+
[hosted_zones, change_requests]
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def find_or_init_hosted_zone(record_name, hosted_zone_name)
|
145
|
+
hosted_zone_name ||= HostedZoneDomain::parse(record_name)
|
146
|
+
hosted_zone_name = zonify!(hosted_zone_name)
|
147
|
+
|
148
|
+
wanted_hosted_zone = @wanted_hosted_zones.find{|zone| zone.name == hosted_zone_name}
|
149
|
+
if wanted_hosted_zone.nil?
|
150
|
+
wanted_hosted_zone = HostedZone.new(hosted_zone_name)
|
151
|
+
@wanted_hosted_zones << wanted_hosted_zone
|
152
|
+
end
|
153
|
+
|
154
|
+
wanted_hosted_zone
|
155
|
+
end
|
156
|
+
|
157
|
+
def missing_hosted_zones_error(missing_hosted_zones)
|
158
|
+
error_message = []
|
159
|
+
error_message << "There are #{missing_hosted_zones.size} missing hosted zones:"
|
160
|
+
|
161
|
+
missing_hosted_zones.map(&:name).sort.each do |whz|
|
162
|
+
error_message << "- #{whz}"
|
163
|
+
end
|
164
|
+
|
165
|
+
raise error_message.join("\n")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class Config
|
3
|
+
def initialize(dry_run: true, diff: true, force: false, delete: false, interactive_progress: true, create_hosted_zones: false)
|
4
|
+
@dry_run = dry_run
|
5
|
+
@diff = diff
|
6
|
+
@force = force
|
7
|
+
@delete = delete
|
8
|
+
@interactive_progress = interactive_progress
|
9
|
+
@create_hosted_zones = create_hosted_zones
|
10
|
+
|
11
|
+
raise SettingNotBoolean.new('dry_run is not a boolean') unless [true, false].include?(dry_run)
|
12
|
+
raise SettingNotBoolean.new('diff is not a boolean') unless [true, false].include?(diff)
|
13
|
+
raise SettingNotBoolean.new('force is not a boolean') unless [true, false].include?(force)
|
14
|
+
raise SettingNotBoolean.new('delete is not a boolean') unless [true, false].include?(delete)
|
15
|
+
raise SettingNotBoolean.new('interactive_progress is not a boolean') unless [true, false].include?(interactive_progress)
|
16
|
+
raise SettingNotBoolean.new('create_hosted_zones is not a boolean') unless [true, false].include?(create_hosted_zones)
|
17
|
+
end
|
18
|
+
|
19
|
+
def dry_run?
|
20
|
+
@dry_run
|
21
|
+
end
|
22
|
+
|
23
|
+
def diff?
|
24
|
+
@diff
|
25
|
+
end
|
26
|
+
|
27
|
+
def force?
|
28
|
+
@force
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete?
|
32
|
+
@delete
|
33
|
+
end
|
34
|
+
|
35
|
+
def interactive_progress?
|
36
|
+
@interactive_progress
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_hosted_zones?
|
40
|
+
@create_hosted_zones
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class EntryPolicyService
|
3
|
+
def initialize(hosted_zone, configuration)
|
4
|
+
@hosted_zone = hosted_zone
|
5
|
+
@configuration = configuration
|
6
|
+
end
|
7
|
+
|
8
|
+
def entries_to_create
|
9
|
+
@hosted_zone.entries.select{|hze| hze.referenced? && hze.new?}
|
10
|
+
end
|
11
|
+
|
12
|
+
def entries_to_update
|
13
|
+
@hosted_zone.entries.select{|hze| hze.referenced? && hze.changed? && !hze.new?}
|
14
|
+
end
|
15
|
+
|
16
|
+
def entries_not_touched
|
17
|
+
not_touched = @hosted_zone.entries.select{|hze| hze.referenced? && !hze.changed? && !hze.new?}
|
18
|
+
|
19
|
+
if @configuration.delete?
|
20
|
+
not_touched
|
21
|
+
else
|
22
|
+
(not_touched + @hosted_zone.entries.select{|hze| !hze.referenced?}).flatten
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def entries_to_delete
|
27
|
+
if @configuration.delete?
|
28
|
+
@hosted_zone.entries.select{|hze| !hze.referenced?}
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def entries_to_change
|
35
|
+
[entries_to_create, entries_to_update, entries_to_delete].sum(&:size)
|
36
|
+
end
|
37
|
+
|
38
|
+
def entries_to_change?
|
39
|
+
entries_to_change > 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def compile
|
43
|
+
generate_change_batch
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def generate_change_batch
|
49
|
+
change_batch_options = []
|
50
|
+
|
51
|
+
entries_to_delete.each do |entry|
|
52
|
+
change_batch_options << {
|
53
|
+
action: 'DELETE',
|
54
|
+
resource_record_set: entry_to_rrs(entry),
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
entries_to_update.each do |entry|
|
59
|
+
change_batch_options << {
|
60
|
+
action: 'UPSERT',
|
61
|
+
resource_record_set: entry_to_rrs(entry.new_entry),
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
entries_to_create.each do |entry|
|
66
|
+
change_batch_options << {
|
67
|
+
action: 'CREATE',
|
68
|
+
resource_record_set: entry_to_rrs(entry)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
change_batch_options
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def entry_to_rrs(entry)
|
78
|
+
case entry
|
79
|
+
when HostedZoneEntry
|
80
|
+
{
|
81
|
+
name: entry.name,
|
82
|
+
type: entry.type,
|
83
|
+
ttl: entry.ttl,
|
84
|
+
resource_records: entry.value.map{|a| {value: a}},
|
85
|
+
}
|
86
|
+
when HostedZoneAlias
|
87
|
+
{
|
88
|
+
name: entry.name,
|
89
|
+
type: entry.type,
|
90
|
+
alias_target: {
|
91
|
+
hosted_zone_id: entry.target_hosted_zone_id,
|
92
|
+
dns_name: entry.target_dns_name,
|
93
|
+
evaluate_target_health: false,
|
94
|
+
},
|
95
|
+
}
|
96
|
+
else raise "Unknown entry"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class RecordNotAString < StandardError; end
|
3
|
+
class RecordNotValid < StandardError; end
|
4
|
+
class TtlNotInteger < StandardError; end
|
5
|
+
class SettingNotBoolean < StandardError; end
|
6
|
+
|
7
|
+
class MissingHostedZones < StandardError; end
|
8
|
+
class DuplicatedHostedZones < StandardError; end
|
9
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class HostedZone
|
3
|
+
attr_reader :name
|
4
|
+
attr_accessor :resource_record_sets
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@resource_record_sets = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_or_update_hosted_zone_entry(wanted_entry)
|
12
|
+
raise if wanted_entry.hosted_zone != self.name
|
13
|
+
|
14
|
+
existing_entry = @resource_record_sets.find{|hze| hze.type == wanted_entry.type && hze.name == wanted_entry.name}
|
15
|
+
|
16
|
+
if existing_entry
|
17
|
+
if existing_entry.persisted?
|
18
|
+
existing_entry.mark_referenced!
|
19
|
+
existing_entry.new_value(wanted_entry)
|
20
|
+
else
|
21
|
+
wanted_entry.mark_referenced!
|
22
|
+
@resource_record_sets[@resource_record_sets.index(existing_entry)] = wanted_entry
|
23
|
+
end
|
24
|
+
else
|
25
|
+
wanted_entry.mark_new!
|
26
|
+
wanted_entry.mark_referenced!
|
27
|
+
@resource_record_sets << wanted_entry
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def entries
|
32
|
+
@resource_record_sets
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class HostedZoneAlias
|
3
|
+
attr_accessor :type, :name, :hosted_zone, :target_hosted_zone_id, :target_dns_name
|
4
|
+
attr_accessor :changed_type, :changed_name, :changed_target_hosted_zone_id, :changed_target_dns_name
|
5
|
+
attr_accessor :referenced, :persisted
|
6
|
+
attr_accessor :new_entry
|
7
|
+
|
8
|
+
def initialize(type, name, target_hosted_zone_id, target_dns_name, hosted_zone)
|
9
|
+
@type = type
|
10
|
+
@name = zonify!(name)
|
11
|
+
@target_hosted_zone_id = target_hosted_zone_id
|
12
|
+
@target_dns_name = target_dns_name
|
13
|
+
@hosted_zone = hosted_zone
|
14
|
+
|
15
|
+
@new_entry = nil
|
16
|
+
|
17
|
+
raise if [@type, @name, @target_hosted_zone_id, @target_dns_name, @hosted_zone].any?(&:nil?)
|
18
|
+
|
19
|
+
@changed_type = false
|
20
|
+
@changed_name = false
|
21
|
+
@changed_target_hosted_zone_id = false
|
22
|
+
@changed_target_dns_name = false
|
23
|
+
@referenced = false
|
24
|
+
@persisted = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def mark_new!
|
28
|
+
@changed_type, @changed_name, @changed_target_hosted_zone_id, @changed_target_dns_name = [true, true, true, true]
|
29
|
+
end
|
30
|
+
|
31
|
+
def new?
|
32
|
+
[@changed_type, @changed_name, @changed_target_hosted_zone_id, @changed_target_dns_name].all?
|
33
|
+
end
|
34
|
+
|
35
|
+
def persisted!
|
36
|
+
@persisted = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def persisted?
|
40
|
+
@persisted
|
41
|
+
end
|
42
|
+
|
43
|
+
def changed?
|
44
|
+
[@changed_type, @changed_name, @changed_target_hosted_zone_id, @changed_target_dns_name].any?
|
45
|
+
end
|
46
|
+
|
47
|
+
def mark_referenced!
|
48
|
+
@referenced = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def referenced?
|
52
|
+
@referenced
|
53
|
+
end
|
54
|
+
|
55
|
+
def new_value(new_entry)
|
56
|
+
if new_entry.class == SprinkleDNS::HostedZoneAlias
|
57
|
+
@changed_target_hosted_zone_id = true if @target_hosted_zone_id != new_entry.target_hosted_zone_id
|
58
|
+
@changed_target_dns_name = true if @target_dns_name != new_entry.target_dns_name
|
59
|
+
else
|
60
|
+
@changed_target_hosted_zone_id = true
|
61
|
+
@changed_target_dns_name = true
|
62
|
+
end
|
63
|
+
|
64
|
+
# TODO test this
|
65
|
+
if @changed_target_hosted_zone_id || @changed_target_dns_name
|
66
|
+
@new_entry = new_entry
|
67
|
+
end
|
68
|
+
|
69
|
+
self.changed?
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
[
|
74
|
+
"Alias",
|
75
|
+
sprintf("%4s", type),
|
76
|
+
sprintf("%30s", name),
|
77
|
+
sprintf("%10s", target_hosted_zone_id),
|
78
|
+
sprintf("%30s", target_dns_name),
|
79
|
+
sprintf("%6s", hosted_zone),
|
80
|
+
].join(" ")
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def valid_record_types
|
86
|
+
['SOA','A','TXT','NS','CNAME','MX','NAPTR','PTR','SRV','SPF','AAAA']
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class HostedZoneDomain
|
3
|
+
def self.parse(domain)
|
4
|
+
splitted = domain.split('.')
|
5
|
+
|
6
|
+
if two_dotted_domain?(domain)
|
7
|
+
[ splitted[-3], splitted[-2], splitted[-1] ].join('.') + '.'
|
8
|
+
else
|
9
|
+
[ splitted[-2], splitted[-1] ].join('.') + '.'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def self.two_dotted_domain?(domain)
|
15
|
+
true if domain.include?('.co.uk')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module SprinkleDNS
|
2
|
+
class HostedZoneEntry
|
3
|
+
attr_accessor :type, :name, :value, :ttl, :hosted_zone
|
4
|
+
attr_accessor :changed_type, :changed_name, :changed_value, :changed_ttl
|
5
|
+
attr_accessor :referenced, :persisted
|
6
|
+
attr_accessor :new_entry
|
7
|
+
|
8
|
+
def initialize(type, name, value, ttl, hosted_zone)
|
9
|
+
@type = type
|
10
|
+
@name = zonify!(name)
|
11
|
+
@value = Array.wrap(value)
|
12
|
+
@ttl = ttl
|
13
|
+
@hosted_zone = hosted_zone
|
14
|
+
|
15
|
+
@new_entry = nil
|
16
|
+
|
17
|
+
raise if [@type, @name, @value, @ttl, @hosted_zone].any?(&:nil?)
|
18
|
+
raise SprinkleDNS::RecordNotAString.new('Record-type should be a string') unless @type.is_a?(String)
|
19
|
+
raise SprinkleDNS::RecordNotValid.new("Record #{@type} is not supported") if !valid_record_types.include?(@type)
|
20
|
+
raise SprinkleDNS::TtlNotInteger.new('TTL should be an integer') unless @ttl.is_a?(Integer)
|
21
|
+
|
22
|
+
@changed_type = false
|
23
|
+
@changed_name = false
|
24
|
+
@changed_value = false
|
25
|
+
@changed_ttl = false
|
26
|
+
@referenced = false
|
27
|
+
@persisted = false
|
28
|
+
|
29
|
+
if ['CNAME', 'MX'].include?(type)
|
30
|
+
@value = @value.map!{|v| zonify!(v)}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def mark_new!
|
35
|
+
@changed_type, @changed_name, @changed_value, @changed_ttl = [true, true, true, true]
|
36
|
+
end
|
37
|
+
|
38
|
+
def new?
|
39
|
+
[@changed_type, @changed_name, @changed_value, @changed_ttl].all?
|
40
|
+
end
|
41
|
+
|
42
|
+
def persisted!
|
43
|
+
@persisted = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def persisted?
|
47
|
+
@persisted
|
48
|
+
end
|
49
|
+
|
50
|
+
def changed?
|
51
|
+
[@changed_type, @changed_name, @changed_value, @changed_ttl].any?
|
52
|
+
end
|
53
|
+
|
54
|
+
def mark_referenced!
|
55
|
+
@referenced = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def referenced?
|
59
|
+
@referenced
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_value(new_entry)
|
63
|
+
if new_entry.class == SprinkleDNS::HostedZoneEntry
|
64
|
+
@changed_value = true if @value != new_entry.value
|
65
|
+
@changed_ttl = true if @ttl != new_entry.ttl
|
66
|
+
else
|
67
|
+
@changed_value = true
|
68
|
+
@changed_ttl = true
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO test this
|
72
|
+
if @changed_value || @changed_ttl
|
73
|
+
@new_entry = new_entry
|
74
|
+
end
|
75
|
+
|
76
|
+
self.changed?
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
[
|
81
|
+
"Entry",
|
82
|
+
sprintf("%4s", type),
|
83
|
+
sprintf("%30s", name),
|
84
|
+
sprintf("%50s", value.join(", ")),
|
85
|
+
sprintf("%6s", ttl),
|
86
|
+
sprintf("%6s", hosted_zone),
|
87
|
+
].join(" ")
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def valid_record_types
|
93
|
+
['SOA','A','TXT','NS','CNAME','MX','NAPTR','PTR','SRV','SPF','AAAA']
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|