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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +76 -0
  7. data/LICENSE +21 -0
  8. data/README.md +181 -0
  9. data/Rakefile +1 -0
  10. data/examples/example01.rb +47 -0
  11. data/examples/example02.rb +61 -0
  12. data/examples/example03.rb +27 -0
  13. data/examples/example04.rb +22 -0
  14. data/lib/sprinkle_dns/cli/hosted_zone_diff.rb +206 -0
  15. data/lib/sprinkle_dns/cli/interactive_change_request_printer.rb +32 -0
  16. data/lib/sprinkle_dns/cli/propagated_change_request_printer.rb +33 -0
  17. data/lib/sprinkle_dns/client.rb +169 -0
  18. data/lib/sprinkle_dns/config.rb +43 -0
  19. data/lib/sprinkle_dns/core_ext/array_wrap.rb +11 -0
  20. data/lib/sprinkle_dns/core_ext/zonify.rb +4 -0
  21. data/lib/sprinkle_dns/entry_policy_service.rb +100 -0
  22. data/lib/sprinkle_dns/exceptions.rb +9 -0
  23. data/lib/sprinkle_dns/hosted_zone.rb +36 -0
  24. data/lib/sprinkle_dns/hosted_zone_alias.rb +91 -0
  25. data/lib/sprinkle_dns/hosted_zone_domain.rb +18 -0
  26. data/lib/sprinkle_dns/hosted_zone_entry.rb +97 -0
  27. data/lib/sprinkle_dns/providers/mock_client.rb +60 -0
  28. data/lib/sprinkle_dns/providers/route53_client.rb +155 -0
  29. data/lib/sprinkle_dns/version.rb +3 -0
  30. data/lib/sprinkle_dns.rb +5 -0
  31. data/logos/SDNS.png +0 -0
  32. data/logos/SDNS.svg +1 -0
  33. data/readme_files/delete_true_and_diff.png +0 -0
  34. data/readme_files/dry_run_and_diff.png +0 -0
  35. data/readme_files/force_false.png +0 -0
  36. data/spec/spec_helper.rb +110 -0
  37. data/spec/support/entry_helpers.rb +18 -0
  38. data/spec/unit/cli_hosted_zone_diff_spec.rb +30 -0
  39. data/spec/unit/hosted_zone_domain_spec.rb +12 -0
  40. data/spec/unit/hosted_zone_spec.rb +343 -0
  41. data/spec/unit/mock_client_spec.rb +59 -0
  42. data/spec/unit/sprinkle_dns_spec.rb +235 -0
  43. data/sprinkle_dns.gemspec +29 -0
  44. data/test_perms.rb.example +2 -0
  45. metadata +192 -0
@@ -0,0 +1,343 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SprinkleDNS::HostedZone do
4
+ context "update data" do
5
+ it 'should update changed data accordingly' do
6
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
7
+ hz_entry01 = sprinkle_entry('A', 'updatevalue.test.billetto.com.', '80.80.22.22', 60, hz.name)
8
+ hz_entry02 = sprinkle_entry('A', 'updatettl.test.billetto.com.', '80.80.22.22', 60, hz.name)
9
+ hz_entry01.persisted!
10
+ hz_entry02.persisted!
11
+ hz.resource_record_sets = [hz_entry01, hz_entry02]
12
+
13
+ hz_entry03 = sprinkle_entry('A', 'updatevalue.test.billetto.com.', '90.90.22.22', 60, hz.name)
14
+ hz_entry04 = sprinkle_entry('A', 'updatettl.test.billetto.com.', '80.80.22.22', 120, hz.name)
15
+ [hz_entry03, hz_entry04].each do |hz_entry|
16
+ hz.add_or_update_hosted_zone_entry(hz_entry)
17
+ end
18
+
19
+ updatedvalue = hz.resource_record_sets.select{|rrs| rrs.name == 'updatevalue.test.billetto.com.'}.first
20
+ expect(updatedvalue.changed_value).to eq true
21
+ expect(updatedvalue.changed_ttl).to eq false
22
+
23
+ updatedttl = hz.resource_record_sets.select{|rrs| rrs.name == 'updatettl.test.billetto.com.'}.first
24
+ expect(updatedttl.changed_value).to eq false
25
+ expect(updatedttl.changed_ttl).to eq true
26
+ end
27
+ end
28
+
29
+ context "compile_change_batch" do
30
+ it 'should correctly calculate a change_batch' do
31
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
32
+
33
+ hze01 = sprinkle_entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
34
+ hze02 = sprinkle_entry('A', 'foo.test.billetto.com.', '80.80.23.23', 70, 'test.billetto.com.')
35
+ hze03 = sprinkle_entry('A', 'bar.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
36
+
37
+ [hze01, hze02, hze03].each do |hze|
38
+ hz.add_or_update_hosted_zone_entry(hze)
39
+ end
40
+
41
+ policy = SprinkleDNS::EntryPolicyService.new(hz, SprinkleDNS::Config.new)
42
+
43
+ expect(policy.compile).to eq([
44
+ {:action=>"CREATE", :resource_record_set=>{:name=>"www.test.billetto.com.", :type=>"A", :ttl=>60, :resource_records=>[{:value=>"80.80.22.22"}]}},
45
+ {:action=>"CREATE", :resource_record_set=>{:name=>"foo.test.billetto.com.", :type=>"A", :ttl=>70, :resource_records=>[{:value=>"80.80.23.23"}]}},
46
+ {:action=>"CREATE", :resource_record_set=>{:name=>"bar.test.billetto.com.", :type=>"A", :ttl=>80, :resource_records=>[{:value=>"80.80.24.24"}]}},
47
+ ])
48
+ end
49
+
50
+ context 'delete unreferenced' do
51
+ before(:all) do
52
+ hz = SprinkleDNS::HostedZone.new('unreferenced.com.')
53
+
54
+ # Entries
55
+ pe01 = sprinkle_entry('A', 'bar.unreferenced.com', '80.80.24.24', 80, 'unreferenced.com.')
56
+ pe02 = sprinkle_entry('A', 'noref.unreferenced.com', '127.0.0.1', 80, 'unreferenced.com.')
57
+
58
+ # We are emulating that these records are already live, mark them as persisted
59
+ [pe01, pe02].each do |persisted|
60
+ persisted.persisted!
61
+ hz.resource_record_sets << persisted
62
+ end
63
+
64
+ client = SprinkleDNS::MockClient.new([hz])
65
+ sdns = SprinkleDNS::Client.new(client, delete: true)
66
+
67
+ sdns.entry('A', 'bar.unreferenced.com', '80.80.24.24', 80)
68
+
69
+ _, existing_hzs = sdns.compare
70
+ @existing_hz = existing_hzs.first
71
+ end
72
+
73
+ it 'should include unreferenced entries in deleted list if delete=true' do
74
+ policy = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: true))
75
+
76
+ expect(policy.entries_to_delete.size).to eq 1
77
+ delete_entry = policy.entries_to_delete.first
78
+ expect(delete_entry.type).to eq 'A'
79
+ expect(delete_entry.name).to eq 'noref.unreferenced.com.'
80
+
81
+ expect(policy.compile).to eq [
82
+ {:action=>"DELETE", :resource_record_set=>{:name=>"noref.unreferenced.com.", :type=>"A", :ttl=>80, :resource_records=>[{:value=>"127.0.0.1"}]}}
83
+ ]
84
+ end
85
+
86
+ it 'should not include unreferenced entries in deleted list if delete=false' do
87
+ policy = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: false))
88
+
89
+ expect(policy.entries_to_delete.size).to eq 0
90
+ expect(policy.compile).to eq []
91
+ end
92
+ end
93
+
94
+ context 'advanced compile_change_batch' do
95
+ before(:all) do
96
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
97
+
98
+ # Static
99
+ ps01 = sprinkle_entry('A', 'staticentry.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
100
+ ps02 = sprinkle_alias('A', 'staticalias.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
101
+
102
+ # Entries
103
+ pe01 = sprinkle_entry('A', 'bar.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
104
+ pe02 = sprinkle_entry('A', 'noref.test.billetto.com.', '127.0.0.1', 80, 'test.billetto.com.')
105
+
106
+ # Aliases
107
+ pa01 = sprinkle_alias('A', 'war.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
108
+ pa02 = sprinkle_alias('A', 'noraf.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
109
+
110
+ # Mixed to overwrite
111
+ pi01 = sprinkle_entry('A', 'entry-to-alias.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
112
+ pi02 = sprinkle_alias('A', 'alias-to-entry.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
113
+
114
+ # We are emulating that these records are already live, mark them as persisted
115
+ [ps01, ps02, pe01, pe02, pa01, pa02, pi01, pi02].each do |persisted|
116
+ persisted.persisted!
117
+ hz.resource_record_sets << persisted
118
+ end
119
+
120
+ client = SprinkleDNS::MockClient.new([hz])
121
+ sdns = SprinkleDNS::Client.new(client)
122
+
123
+ # Static entries
124
+ sdns.entry('A', 'staticentry.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
125
+ sdns.alias('A', 'staticalias.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
126
+
127
+ # PURE ENTRIES
128
+ # Adds new
129
+ sdns.entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
130
+
131
+ # Adds new and overwrites
132
+ sdns.entry('A', 'foo.test.billetto.com.', '80.80.23.23', 70, 'test.billetto.com.')
133
+ sdns.entry('A', 'foo.test.billetto.com.', '81.81.24.24', 80, 'test.billetto.com.')
134
+
135
+ # Modifies existing
136
+ sdns.entry('A', 'bar.test.billetto.com.', '82.82.26.26', 90, 'test.billetto.com.')
137
+
138
+ # PURE ALIASES
139
+ # Adds new
140
+ sdns.alias('A', 'wap.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
141
+
142
+ # Adds new and overwrites
143
+ sdns.alias('A', 'woo.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
144
+ sdns.alias('A', 'woo.test.billetto.com.', 'Z215JYRZR1TBD6', 'dualstack.mothership-test-elb-444444444.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
145
+
146
+ # Modifies existing
147
+ sdns.alias('A', 'war.test.billetto.com.', 'Z215JYRZR1TBD6', 'dualstack.mothership-test-elb-444444444.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
148
+
149
+ # MIXED ENTRIES/ALIASES OVERWRITE
150
+ sdns.alias('A', 'entry-to-alias.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
151
+ sdns.entry('A', 'alias-to-entry.test.billetto.com.', '80.80.24.24', 80, 'test.billetto.com.')
152
+
153
+ _, existing_hzs = sdns.compare
154
+ @existing_hz = existing_hzs.first
155
+ end
156
+
157
+ it "should have a correct number of changes when delete=true" do
158
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: true))
159
+
160
+ expect(policy_service.entries_to_create.size).to eq 4
161
+ expect(policy_service.entries_to_update.size).to eq 4
162
+ expect(policy_service.entries_to_delete.size).to eq 2
163
+ expect(policy_service.entries_not_touched.size).to eq 2
164
+ expect(@existing_hz.entries.size).to eq 4+4+2+2 # 12
165
+
166
+ expect(policy_service.entries_to_delete.map(&:name)).to include('noref.test.billetto.com.')
167
+ expect(policy_service.entries_to_delete.map(&:name)).to include('noraf.test.billetto.com.')
168
+ end
169
+
170
+ it "should have a correct number of changes when delete=false" do
171
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: false))
172
+
173
+ expect(policy_service.entries_to_create.size).to eq 4
174
+ expect(policy_service.entries_to_update.size).to eq 4
175
+ expect(policy_service.entries_to_delete.size).to eq 0
176
+ expect(policy_service.entries_not_touched.size).to eq 4
177
+ expect(@existing_hz.entries.size).to eq 4+4+0+4 # 12
178
+
179
+ expect(policy_service.entries_not_touched.map(&:name)).to include('noref.test.billetto.com.')
180
+ expect(policy_service.entries_not_touched.map(&:name)).to include('noraf.test.billetto.com.')
181
+ end
182
+
183
+ context "references should be correct for" do
184
+ it "entries" do
185
+ ['www.test.billetto.com.', 'foo.test.billetto.com.', 'bar.test.billetto.com.'].each do |referenced|
186
+ expect(@existing_hz.resource_record_sets
187
+ .select{|r| r.name == referenced}
188
+ .select{|r| r.class == SprinkleDNS::HostedZoneEntry}
189
+ .first.referenced?).to eq true
190
+ end
191
+ ['noref.test.billetto.com.'].each do |unreferenced|
192
+ expect(@existing_hz.resource_record_sets
193
+ .select{|r| r.name == unreferenced}
194
+ .select{|r| r.class == SprinkleDNS::HostedZoneEntry}
195
+ .first.referenced?).to eq false
196
+ end
197
+ end
198
+
199
+ it "aliases" do
200
+ ['wap.test.billetto.com.', 'woo.test.billetto.com.', 'war.test.billetto.com.'].each do |referenced|
201
+ expect(@existing_hz.resource_record_sets
202
+ .select{|r| r.name == referenced}
203
+ .select{|r| r.class == SprinkleDNS::HostedZoneAlias}
204
+ .first.referenced?).to eq true
205
+ end
206
+ ['noraf.test.billetto.com.'].each do |unreferenced|
207
+ expect(@existing_hz.resource_record_sets
208
+ .select{|r| r.name == unreferenced}
209
+ .select{|r| r.class == SprinkleDNS::HostedZoneAlias}
210
+ .first.referenced?).to eq false
211
+ end
212
+ end
213
+
214
+ it "mixed" do
215
+ entry_to_alias = @existing_hz.resource_record_sets.select{|r| r.name == 'entry-to-alias.test.billetto.com.'}.first
216
+ expect(entry_to_alias.referenced?).to eq true
217
+ expect(entry_to_alias.class).to eq SprinkleDNS::HostedZoneEntry
218
+ expect(entry_to_alias.new_entry.class).to eq SprinkleDNS::HostedZoneAlias
219
+
220
+ alias_to_entry = @existing_hz.resource_record_sets.select{|r| r.name == 'alias-to-entry.test.billetto.com.'}.first
221
+ expect(alias_to_entry.referenced?).to eq true
222
+ expect(alias_to_entry.class).to eq SprinkleDNS::HostedZoneAlias
223
+ expect(alias_to_entry.new_entry.class).to eq SprinkleDNS::HostedZoneEntry
224
+ end
225
+ end
226
+
227
+ context "entries should be correctly set for" do
228
+ it 'entries' do
229
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: true))
230
+
231
+ expect(policy_service.entries_to_create.map(&:name)).to include('www.test.billetto.com.', 'foo.test.billetto.com.')
232
+ expect(policy_service.entries_to_update.map(&:name)).to include('bar.test.billetto.com.')
233
+ expect(policy_service.entries_to_delete.map(&:name)).to include('noref.test.billetto.com.')
234
+ end
235
+
236
+ it 'aliases' do
237
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: true))
238
+
239
+ expect(policy_service.entries_to_create.map(&:name)).to include('wap.test.billetto.com.', 'woo.test.billetto.com.')
240
+ expect(policy_service.entries_to_update.map(&:name)).to include('war.test.billetto.com.')
241
+ expect(policy_service.entries_to_delete.map(&:name)).to include('noraf.test.billetto.com.')
242
+ end
243
+
244
+ it "mixed" do
245
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: true))
246
+
247
+ expect(policy_service.entries_to_update.map(&:name)).to include('entry-to-alias.test.billetto.com.', 'alias-to-entry.test.billetto.com.')
248
+ end
249
+ end
250
+
251
+ it "should calculate complicated compile_change_batch" do
252
+ policy_service = SprinkleDNS::EntryPolicyService.new(@existing_hz, SprinkleDNS::Config.new(delete: false))
253
+
254
+ expect(policy_service.compile).to eq [
255
+ {:action=>"UPSERT", :resource_record_set=>{:name=>"bar.test.billetto.com.", :type=>"A", :ttl=>90, :resource_records=>[{:value=>"82.82.26.26"}]}},
256
+ {:action=>"UPSERT", :resource_record_set=>{:name=>"war.test.billetto.com.", :type=>"A", :alias_target=>{:hosted_zone_id=>"Z215JYRZR1TBD6", :dns_name=>"dualstack.mothership-test-elb-444444444.eu-central-1.elb.amazonaws.com.", :evaluate_target_health=>false}}},
257
+ {:action=>"UPSERT", :resource_record_set=>{:name=>"entry-to-alias.test.billetto.com.", :type=>"A", :alias_target=>{:hosted_zone_id=>"Z215JYRZR1TBD5", :dns_name=> "dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com.", :evaluate_target_health=>false}}},
258
+ {:action=>"UPSERT", :resource_record_set=>{:name=>"alias-to-entry.test.billetto.com.", :type=>"A", :ttl=>80, :resource_records=>[{:value=>"80.80.24.24"}]}},
259
+ {:action=>"CREATE", :resource_record_set=>{:name=>"www.test.billetto.com.", :type=>"A", :ttl=>60, :resource_records=>[{:value=>"80.80.22.22"}]}},
260
+ {:action=>"CREATE", :resource_record_set=>{:name=>"foo.test.billetto.com.", :type=>"A", :ttl=>80, :resource_records=>[{:value=>"81.81.24.24"}]}},
261
+ {:action=>"CREATE", :resource_record_set=>{:name=>"wap.test.billetto.com.", :type=>"A", :alias_target=>{:hosted_zone_id=>"Z215JYRZR1TBD5", :dns_name=>"dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com.", :evaluate_target_health=>false}}},
262
+ {:action=>"CREATE", :resource_record_set=>{:name=>"woo.test.billetto.com.", :type=>"A", :alias_target=>{:hosted_zone_id=>"Z215JYRZR1TBD6", :dns_name=>"dualstack.mothership-test-elb-444444444.eu-central-1.elb.amazonaws.com.", :evaluate_target_health=>false}}},
263
+ ]
264
+ end
265
+ end
266
+ end
267
+
268
+ context "overwrite an entry" do
269
+ it 'should correctly replace an entry if not persisted' do
270
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
271
+
272
+ hz_entry = sprinkle_entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
273
+ expect(hz_entry.persisted?).to eq false
274
+ hz_alias = sprinkle_alias('A', 'www.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
275
+ expect(hz_alias.persisted?).to eq false
276
+
277
+ hz.add_or_update_hosted_zone_entry(hz_entry)
278
+ expect(hz.resource_record_sets).to include(hz_entry)
279
+ expect(hz.resource_record_sets.size).to eq 1
280
+
281
+ hz.add_or_update_hosted_zone_entry(hz_alias)
282
+ expect(hz.resource_record_sets).to include(hz_alias)
283
+ expect(hz.resource_record_sets).not_to include(hz_entry)
284
+ expect(hz.resource_record_sets.size).to eq 1
285
+ end
286
+
287
+ it 'should use #new_value if persisted' do
288
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
289
+
290
+ hz_entry = sprinkle_entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
291
+ hz_alias = sprinkle_alias('A', 'www.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
292
+
293
+ hz_entry.persisted!
294
+
295
+ hz.add_or_update_hosted_zone_entry(hz_entry)
296
+ expect(hz.resource_record_sets).to include(hz_entry)
297
+ expect(hz.resource_record_sets.size).to eq 1
298
+
299
+ hz.add_or_update_hosted_zone_entry(hz_alias)
300
+ expect(hz.resource_record_sets).not_to include(hz_alias)
301
+ expect(hz.resource_record_sets.size).to eq 1
302
+ expect(hz.resource_record_sets.first.new_entry).to eq hz_alias
303
+ end
304
+ end
305
+
306
+ context "overwrite an alias" do
307
+ it 'should correctly replace an alias if not persisted' do
308
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
309
+
310
+ hz_alias = sprinkle_alias('A', 'www.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
311
+ expect(hz_alias.persisted?).to eq false
312
+ hz_entry = sprinkle_entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
313
+ expect(hz_entry.persisted?).to eq false
314
+
315
+ hz.add_or_update_hosted_zone_entry(hz_alias)
316
+ expect(hz.resource_record_sets).to include(hz_alias)
317
+ expect(hz.resource_record_sets.size).to eq 1
318
+
319
+ hz.add_or_update_hosted_zone_entry(hz_entry)
320
+ expect(hz.resource_record_sets).to include(hz_entry)
321
+ expect(hz.resource_record_sets).not_to include(hz_alias)
322
+ expect(hz.resource_record_sets.size).to eq 1
323
+ end
324
+
325
+ it 'should use #new_value if persisted' do
326
+ hz = SprinkleDNS::HostedZone.new('test.billetto.com.')
327
+
328
+ hz_entry = sprinkle_entry('A', 'www.test.billetto.com.', '80.80.22.22', 60, 'test.billetto.com.')
329
+ hz_alias = sprinkle_alias('A', 'www.test.billetto.com.', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com.')
330
+
331
+ hz_entry.persisted!
332
+
333
+ hz.add_or_update_hosted_zone_entry(hz_entry)
334
+ expect(hz.resource_record_sets).to include(hz_entry)
335
+ expect(hz.resource_record_sets.size).to eq 1
336
+
337
+ hz.add_or_update_hosted_zone_entry(hz_alias)
338
+ expect(hz.resource_record_sets).not_to include(hz_alias)
339
+ expect(hz.resource_record_sets.size).to eq 1
340
+ expect(hz.resource_record_sets.first.new_entry).to eq hz_alias
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SprinkleDNS::MockClient do
4
+ context "#fetch" do
5
+ it "should return empty list when given empty array" do
6
+ hz = SprinkleDNS::HostedZone.new('billetto.se.')
7
+ en = sprinkle_entry("A", "beta.billetto.se", '88.80.188.143', 60, hz.name)
8
+ hz.add_or_update_hosted_zone_entry(en)
9
+ c42 = SprinkleDNS::MockClient.new([en])
10
+
11
+ expect(c42.fetch_hosted_zones).to eq []
12
+ expect(c42.fetch_hosted_zones(filter: [])).to eq []
13
+ end
14
+
15
+ it "should initialize correctly" do
16
+ hz = SprinkleDNS::HostedZone.new('billetto.se.')
17
+ en = sprinkle_entry("A", "beta.billetto.se", '88.80.188.143', 60, hz.name)
18
+ hz.add_or_update_hosted_zone_entry(en)
19
+ c42 = SprinkleDNS::MockClient.new([hz])
20
+
21
+ expect(en.persisted?).to be false
22
+ c42.fetch_hosted_zones(filter: [hz.name])
23
+ expect(en.persisted?).to be true
24
+ end
25
+
26
+ it "should filter correctly" do
27
+ hosted_zones = []
28
+
29
+ hzdk = SprinkleDNS::HostedZone.new('billetto.dk.')
30
+
31
+ e1 = sprinkle_entry("A", "beta.billetto.dk.", '88.80.188.143', 60, hzdk.name)
32
+ e2 = sprinkle_entry("A", "alph.billetto.dk.", '88.80.188.144', 60, hzdk.name)
33
+ e3 = sprinkle_entry("A", "lolp.billetto.dk.", '88.80.188.145', 60, hzdk.name)
34
+
35
+ [e1, e2, e3].each do |entry|
36
+ hzdk.add_or_update_hosted_zone_entry(entry)
37
+ end
38
+
39
+ hosted_zones << hzdk
40
+
41
+ hzse = SprinkleDNS::HostedZone.new('billetto.se.')
42
+
43
+ e4 = sprinkle_entry("A", "beta.billetto.se", '88.80.188.143', 60, hzse.name)
44
+ e5 = sprinkle_entry("A", "alph.billetto.se", '88.80.188.144', 60, hzse.name)
45
+ e6 = sprinkle_entry("A", "lolp.billetto.se", '88.80.188.145', 60, hzse.name)
46
+
47
+ [e4, e5, e6].each do |entry|
48
+ hzse.add_or_update_hosted_zone_entry(entry)
49
+ end
50
+
51
+ hosted_zones << hzse
52
+
53
+ c42 = SprinkleDNS::MockClient.new(hosted_zones)
54
+ expect(c42.fetch_hosted_zones(filter: ['billetto.dk.', 'billetto.se.'])).to include(hzdk, hzse)
55
+ expect(c42.fetch_hosted_zones(filter: ['billetto.dk.'])).to include(hzdk)
56
+ expect(c42.fetch_hosted_zones(filter: ['billetto.se.'])).to include(hzse)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SprinkleDNS::Client do
4
+ it 'should parse and setup' do
5
+ r53c = SprinkleDNS::Route53Client.new('1','2')
6
+ sdns = SprinkleDNS::Client.new(r53c)
7
+
8
+ sdns.entry('A', 'kaspergrubbe.com', '88.80.80.80', 60)
9
+ sdns.entry('A', 'assets.kaspergrubbe.com', '88.80.80.80', 60)
10
+ sdns.entry('MX', 'mail.kaspergrubbe.com', ['10 mailserver.example.com', '20 mailserver2.example.com'], 300)
11
+ sdns.entry('MX', 'main.kaspergrubbe.com', ['10 mailserver.example.com'], 300)
12
+ sdns.entry('A', 'streamy.kaspergrubbe.com.', '198.211.96.200', 60)
13
+ sdns.entry('A', 'blog.kaspergrubbe.com', '198.211.96.200', 60)
14
+
15
+ sdns.entry('CNAME', 'www.es.kaspergrubbe.com', "#{Time.now.to_i}.example.com.", 42, 'es.kaspergrubbe.com')
16
+ sdns.entry('CNAME', 'staging.es.kaspergrubbe.com.', "#{Time.now.to_i}.example.com.", 42, 'es.kaspergrubbe.com.')
17
+
18
+ expect(sdns.wanted_hosted_zones.count).to eq 2
19
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'kaspergrubbe.com.'}.first).to be_truthy
20
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'es.kaspergrubbe.com.'}.first).to be_truthy
21
+
22
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'kaspergrubbe.com.'}.first.resource_record_sets.count).to eq 6
23
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'es.kaspergrubbe.com.'}.first.resource_record_sets.count).to eq 2
24
+ end
25
+
26
+ it 'should support alias records' do
27
+ r53c = SprinkleDNS::Route53Client.new('1','2')
28
+ sdns = SprinkleDNS::Client.new(r53c)
29
+
30
+ sdns.entry('A', 'billetto.com', '88.80.80.80', 60)
31
+ sdns.alias('A', 'www.billetto.com', 'Z215JYRZR1TBD5', 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com')
32
+
33
+ expect(sdns.wanted_hosted_zones.count).to eq 1
34
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'billetto.com.'}.first).to be_truthy
35
+ expect(sdns.wanted_hosted_zones.select{|whz| whz.name == 'billetto.com.'}.first.resource_record_sets.count).to eq 2
36
+ end
37
+
38
+ context "overwrites" do
39
+ it 'should allow overwrites for a-records' do
40
+ r53c = SprinkleDNS::Route53Client.new('1','2')
41
+ sdns = SprinkleDNS::Client.new(r53c)
42
+
43
+ ['billetto.at', 'billetto.io', 'billetto.my'].each do |domain|
44
+ sdns.entry('A', domain, '88.80.80.80', 60)
45
+ sdns.entry('A', "www.#{domain}", '88.80.80.80', 60)
46
+ end
47
+ # Overwrite and null-route to localhost
48
+ sdns.entry('A', 'billetto.at', '127.0.0.1', 70)
49
+
50
+ hz = sdns.wanted_hosted_zones.select{|hz| hz.name == 'billetto.at.'}.first
51
+ rrs = hz.resource_record_sets.select{|rrs| rrs.type == 'A' && rrs.name == 'billetto.at.'}.first
52
+
53
+ expect(rrs.ttl).to eq 70
54
+ expect(rrs.value).to eq ["127.0.0.1"]
55
+ end
56
+
57
+ it 'should allow overwrites for aliases' do
58
+ r53c = SprinkleDNS::Route53Client.new('1','2')
59
+ sdns = SprinkleDNS::Client.new(r53c)
60
+
61
+ ['billetto.at', 'billetto.io', 'billetto.my'].each do |domain|
62
+ sdns.alias('A', domain, 'Z215JYRZR1TBD5', 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com')
63
+ end
64
+ sdns.alias('A', 'billetto.at', 'X317JYRZR1TBD5', 'triplestack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com')
65
+
66
+ hz = sdns.wanted_hosted_zones.select{|hz| hz.name == 'billetto.at.'}.first
67
+ rrs = hz.resource_record_sets.select{|rrs| rrs.type == 'A' && rrs.name == 'billetto.at.'}.first
68
+
69
+ expect(rrs.target_hosted_zone_id).to eq 'X317JYRZR1TBD5'
70
+ expect(rrs.target_dns_name).to eq 'triplestack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com.'
71
+ end
72
+
73
+ it 'should allow overwrites of an a-record with an alias' do
74
+ r53c = SprinkleDNS::Route53Client.new('1','2')
75
+ sdns = SprinkleDNS::Client.new(r53c)
76
+
77
+ sdns.entry('A', "billetto.pl", '88.80.80.80', 60)
78
+ sdns.alias('A', 'billetto.pl', 'Z215JYRZR1TBD5', 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com')
79
+
80
+ hz = sdns.wanted_hosted_zones.select{|hz| hz.name == 'billetto.pl.'}.first
81
+ rrs = hz.resource_record_sets.select{|rrs| rrs.type == 'A' && rrs.name == 'billetto.pl.'}.first
82
+
83
+ expect(rrs.target_hosted_zone_id).to eq 'Z215JYRZR1TBD5'
84
+ expect(rrs.target_dns_name).to eq 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com.'
85
+ end
86
+
87
+ it 'should allow overwrites of an alias with an a-record' do
88
+ r53c = SprinkleDNS::Route53Client.new('1','2')
89
+ sdns = SprinkleDNS::Client.new(r53c)
90
+
91
+ sdns.alias('A', 'billetto.pl', 'Z215JYRZR1TBD5', 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com')
92
+ sdns.entry('A', "billetto.pl", '88.80.80.80', 60)
93
+
94
+ hz = sdns.wanted_hosted_zones.select{|hz| hz.name == 'billetto.pl.'}.first
95
+ rrs = hz.resource_record_sets.select{|rrs| rrs.type == 'A' && rrs.name == 'billetto.pl.'}.first
96
+
97
+ expect(rrs.ttl).to eq 60
98
+ expect(rrs.value).to eq ['88.80.80.80']
99
+ end
100
+ end
101
+
102
+ context 'syncing' do
103
+ before(:all) do
104
+ hz = SprinkleDNS::HostedZone.new('billetto.se.')
105
+ en = sprinkle_entry("A", "beta.billetto.se", '88.80.188.143', 60, hz.name)
106
+ hz.add_or_update_hosted_zone_entry(en)
107
+
108
+ r42c = SprinkleDNS::MockClient.new([hz])
109
+ @sdns = SprinkleDNS::Client.new(r42c)
110
+
111
+ @sdns.entry('A', 'billetto.se', '88.80.80.80', 60)
112
+ @sdns.entry('A', 'beta.billetto.se', '88.80.80.80', 70)
113
+ end
114
+
115
+ it 'sprinkle! should return change_requests' do
116
+ _, change_requests = @sdns.sprinkle!
117
+
118
+ change_requests.each do |cr|
119
+ expect(cr.in_sync).to eq true
120
+ expect(cr.tries).to eq cr.tries_needed
121
+ end
122
+ end
123
+ end
124
+
125
+ context 'delete config option' do
126
+ before(:all) do
127
+ @hz01 = SprinkleDNS::HostedZone.new('colourful.co.uk.')
128
+
129
+ e1 = SprinkleDNS::HostedZoneEntry.new('A', 'updateme.colourful.co.uk.', Array.wrap('80.80.80.80'), 3600, @hz01.name)
130
+ e2 = SprinkleDNS::HostedZoneEntry.new('TXT', 'txt.colourful.co.uk.', %Q{"#{Time.now.to_i}"}, 60, @hz01.name)
131
+ e3 = SprinkleDNS::HostedZoneEntry.new('A', 'nochange.colourful.co.uk.', Array.wrap('80.80.80.80'), 60, @hz01.name)
132
+ e4 = SprinkleDNS::HostedZoneEntry.new('A', 'noref.colourful.co.uk.', Array.wrap('80.80.80.80'), 3600, @hz01.name)
133
+ e5 = SprinkleDNS::HostedZoneEntry.new('A', 'www1.colourful.co.uk.', Array.wrap('80.80.80.80'), 60, @hz01.name)
134
+ e6 = SprinkleDNS::HostedZoneEntry.new('A', 'www2.colourful.co.uk.', Array.wrap('80.80.80.80'), 60, @hz01.name)
135
+ e7 = SprinkleDNS::HostedZoneEntry.new('A', 'www3.colourful.co.uk.', Array.wrap('80.80.80.80'), 60, @hz01.name)
136
+
137
+ # We are emulating that these records are already live, mark them as persisted
138
+ [e1, e2, e3, e4, e5, e6, e7].each do |persisted|
139
+ persisted.persisted!
140
+ @hz01.resource_record_sets << persisted
141
+ end
142
+ end
143
+
144
+ it 'it should not list deletes if delete=false' do
145
+ client = SprinkleDNS::MockClient.new([@hz01])
146
+ sdns = SprinkleDNS::Client.new(client, dry_run: true, delete: false)
147
+
148
+ sdns.entry('A', 'updateme.colourful.co.uk', '90.90.90.90', 3601)
149
+ sdns.entry('A', 'addnew.colourful.co.uk', '90.90.90.90', 3601)
150
+ sdns.entry('TXT', 'txt.colourful.co.uk', %Q{"#{Time.now.to_i+1}"}, 60)
151
+ sdns.entry('A', 'nochange.colourful.co.uk.', Array.wrap('80.80.80.80'), 60)
152
+
153
+ existing_hosted_zones, _ = sdns.sprinkle!
154
+
155
+ expect(existing_hosted_zones.size).to eq 1
156
+
157
+ policy_service = SprinkleDNS::EntryPolicyService.new(@hz01, sdns.config)
158
+ expect(policy_service.entries_to_delete.size).to eq 0
159
+ expect(policy_service.compile.select{|rrset| rrset[:action] == 'DELETE'}.count).to eq 0
160
+ end
161
+
162
+ it 'it should list deletes if delete=true' do
163
+ client = SprinkleDNS::MockClient.new([@hz01])
164
+ sdns = SprinkleDNS::Client.new(client, dry_run: true, delete: true)
165
+
166
+ sdns.entry('A', 'updateme.colourful.co.uk', '90.90.90.90', 3601)
167
+ sdns.entry('A', 'addnew.colourful.co.uk', '90.90.90.90', 3601)
168
+ sdns.entry('TXT', 'txt.colourful.co.uk', %Q{"#{Time.now.to_i+1}"}, 60)
169
+ sdns.entry('A', 'nochange.colourful.co.uk.', Array.wrap('80.80.80.80'), 60)
170
+
171
+ existing_hosted_zones, _ = sdns.sprinkle!
172
+
173
+ expect(existing_hosted_zones.size).to eq 1
174
+
175
+ policy_service = SprinkleDNS::EntryPolicyService.new(@hz01, sdns.config)
176
+ expect(policy_service.entries_to_delete.size).to eq 4
177
+ expect(policy_service.compile.select{|rrset| rrset[:action] == 'DELETE'}.count).to eq 4
178
+ end
179
+ end
180
+
181
+ context 'record validation' do
182
+ it 'should only allow valid string records' do
183
+ valid_records = ['SOA','A','TXT','NS','CNAME','MX','NAPTR','PTR','SRV','SPF','AAAA']
184
+
185
+ valid_records.each do |record_type|
186
+ r53c = SprinkleDNS::Route53Client.new('1','2')
187
+ sdns = SprinkleDNS::Client.new(r53c)
188
+
189
+ sdns.entry(record_type, 'kaspergrubbe.com', '88.80.80.80')
190
+ end
191
+ end
192
+
193
+ it 'should not allow symbols for records' do
194
+ invalid_records = [:SOA, :A, :TXT, :NS, :CNAME, :MX, :NAPTR, :PTR, :SRV, :SPF, :AAAA]
195
+
196
+ invalid_records.each do |record_type|
197
+ r53c = SprinkleDNS::Route53Client.new('1','2')
198
+ sdns = SprinkleDNS::Client.new(r53c)
199
+
200
+ expect{sdns.entry(record_type, 'kaspergrubbe.com', '88.80.80.80')}.to raise_error(SprinkleDNS::RecordNotAString)
201
+ end
202
+ end
203
+
204
+ it 'should not allow other types of records' do
205
+ invalid_records = ['a', 'r', 'p']
206
+
207
+ invalid_records.each do |record_type|
208
+ r53c = SprinkleDNS::Route53Client.new('1','2')
209
+ sdns = SprinkleDNS::Client.new(r53c)
210
+
211
+ expect{sdns.entry(record_type, 'kaspergrubbe.com', '88.80.80.80')}.to raise_error(SprinkleDNS::RecordNotValid)
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'ttl validation' do
217
+ it 'should allow integers' do
218
+ (1..(3600*60)).minmax.each do |ttl|
219
+ r53c = SprinkleDNS::Route53Client.new('1','2')
220
+ sdns = SprinkleDNS::Client.new(r53c)
221
+
222
+ sdns.entry('A', 'kaspergrubbe.com', '88.80.80.80', ttl)
223
+ end
224
+ end
225
+
226
+ it 'should not allow strings' do
227
+ (1..(3600*60)).minmax.each do |ttl|
228
+ r53c = SprinkleDNS::Route53Client.new('1','2')
229
+ sdns = SprinkleDNS::Client.new(r53c)
230
+
231
+ expect{sdns.entry('A', 'kaspergrubbe.com', '88.80.80.80', ttl.to_s)}.to raise_error(SprinkleDNS::TtlNotInteger)
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,29 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'sprinkle_dns/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "sprinkle_dns"
7
+ gem.version = SprinkleDNS::VERSION
8
+ gem.authors = ["Kasper Grubbe"]
9
+ gem.email = ["kaspergrubbe@gmail.com"]
10
+ gem.homepage = "http://github.com/gfish/sprinkle_dns"
11
+ gem.summary = %q{Make handling DNS easier}
12
+ gem.description = %q{Make handling DNS easier by using simple Ruby constructs}
13
+
14
+ gem.licenses = ['MIT', 'GPL-2.0']
15
+
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.required_ruby_version = ">= 2.4.0"
21
+ gem.add_runtime_dependency 'aws-sdk-route53', '~> 1.21'
22
+
23
+ gem.add_development_dependency "rspec", '~> 3.8'
24
+ gem.add_development_dependency "simplecov", '~> 0.16'
25
+ gem.add_development_dependency "pry", '~> 0.12'
26
+ gem.add_development_dependency "rake", '~> 12.3'
27
+ gem.add_development_dependency "vcr", '~> 3.0'
28
+ gem.add_development_dependency "webmock", '~> 2.3'
29
+ end