sprinkle_dns 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|