sprinkle_dns 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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