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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1fab0389257851cf18cca41f85ba7902d2d6991599f6f41467612fad0861f754
4
+ data.tar.gz: daa92a1e2c7a5ad893c606bfadc181e355c76622ee55bc905f8a1afcda734f2a
5
+ SHA512:
6
+ metadata.gz: c2e87abb06df0034e18b215e688fd6808452fbb95fa105d95a4ab6f1532b4bdc7d35db72ae0ef8afda82f6d445dcfc21e200cb7bb07ca70cb83d1711555aaa22
7
+ data.tar.gz: c26276c21b51b54b22a589fcfe622bee85261cf20f9907b36ea7792a300ea81e2e31a830ae8967b0e35c89ada0636f0b8c347261c3fe6b1c2710d483f6b066fb
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/
2
+ test_perms.rb
3
+ coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,76 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sprinkle_dns (1.0.0)
5
+ aws-sdk-route53 (~> 1.21)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.6.0)
11
+ public_suffix (>= 2.0.2, < 4.0)
12
+ aws-eventstream (1.0.3)
13
+ aws-partitions (1.160.0)
14
+ aws-sdk-core (3.50.0)
15
+ aws-eventstream (~> 1.0, >= 1.0.2)
16
+ aws-partitions (~> 1.0)
17
+ aws-sigv4 (~> 1.1)
18
+ jmespath (~> 1.0)
19
+ aws-sdk-route53 (1.22.0)
20
+ aws-sdk-core (~> 3, >= 3.48.2)
21
+ aws-sigv4 (~> 1.1)
22
+ aws-sigv4 (1.1.0)
23
+ aws-eventstream (~> 1.0, >= 1.0.2)
24
+ coderay (1.1.2)
25
+ crack (0.4.3)
26
+ safe_yaml (~> 1.0.0)
27
+ diff-lcs (1.3)
28
+ docile (1.3.1)
29
+ hashdiff (0.3.9)
30
+ jmespath (1.4.0)
31
+ json (2.2.0)
32
+ method_source (0.9.2)
33
+ pry (0.12.2)
34
+ coderay (~> 1.1.0)
35
+ method_source (~> 0.9.0)
36
+ public_suffix (3.0.3)
37
+ rake (12.3.2)
38
+ rspec (3.8.0)
39
+ rspec-core (~> 3.8.0)
40
+ rspec-expectations (~> 3.8.0)
41
+ rspec-mocks (~> 3.8.0)
42
+ rspec-core (3.8.0)
43
+ rspec-support (~> 3.8.0)
44
+ rspec-expectations (3.8.3)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.8.0)
47
+ rspec-mocks (3.8.0)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.8.0)
50
+ rspec-support (3.8.0)
51
+ safe_yaml (1.0.5)
52
+ simplecov (0.16.1)
53
+ docile (~> 1.1)
54
+ json (>= 1.8, < 3)
55
+ simplecov-html (~> 0.10.0)
56
+ simplecov-html (0.10.2)
57
+ vcr (3.0.3)
58
+ webmock (2.3.2)
59
+ addressable (>= 2.3.6)
60
+ crack (>= 0.3.2)
61
+ hashdiff
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ pry (~> 0.12)
68
+ rake (~> 12.3)
69
+ rspec (~> 3.8)
70
+ simplecov (~> 0.16)
71
+ sprinkle_dns!
72
+ vcr (~> 3.0)
73
+ webmock (~> 2.3)
74
+
75
+ BUNDLED WITH
76
+ 1.17.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2015 Billetto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ ![SprinkleDNS logo](logos/SDNS.png)
2
+
3
+ # SprinkleDNS
4
+
5
+ A diff-based way of managing DNS for people with lots of domains for AWS Route53.
6
+
7
+ ## How
8
+
9
+ Use plain old Ruby to define your DNS configuration:
10
+
11
+ ```ruby
12
+ require 'sprinkle_dns'
13
+
14
+ client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
15
+ sdns = SprinkleDNS::Client.new(client)
16
+
17
+ sdns.entry('A', 'www.billetto.com', '88.80.188.142', 360)
18
+ sdns.entry('A', 'staging.billetto.com', '88.80.188.143', 360)
19
+
20
+ sdns.sprinkle!
21
+ ```
22
+
23
+ Or a more advanced example using loops and interpolation:
24
+
25
+ ```ruby
26
+ require 'sprinkle_dns'
27
+
28
+ client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
29
+ sdns = SprinkleDNS::Client.new(client)
30
+
31
+ domains = ['billetto.dk', 'billetto.co.uk', 'billetto.com', 'billetto.se']
32
+
33
+ domains.each do |domain|
34
+ sdns.entry('A', domain, '88.80.188.142', 360)
35
+ sdns.entry('A', "www.#{domain}", '88.80.188.142', 360)
36
+
37
+ s.entry("CNAME", "docs.#{domain}", 'ghs.googlehosted.com', 43200)
38
+ s.entry("CNAME", "mail.#{domain}", 'ghs.googlehosted.com', 43200)
39
+ s.entry("CNAME", "drive.#{domain}", 'ghs.googlehosted.com', 43200)
40
+
41
+ s.entry("MX", domain, ['1 aspmx.l.google.com',
42
+ '5 alt1.aspmx.l.google.com',
43
+ '5 alt2.aspmx.l.google.com',
44
+ '10 aspmx2.googlemail.com',
45
+ '10 aspmx3.googlemail.com'], 60)
46
+ end
47
+
48
+ # Overwrite one of the domains, to test our new loadbalancer:
49
+ sdns.entry('A', 'billetto.com', '89.81.189.143', 360)
50
+
51
+ sdns.sprinkle!
52
+ ```
53
+
54
+ ## Configuration
55
+
56
+ You can configure the `SprinkleDNS::Client` like so:
57
+
58
+ ```ruby
59
+ client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
60
+ sdns = SprinkleDNS::Client.new(client,
61
+ dry_run: false,
62
+ diff: true,
63
+ force: true,
64
+ delete: false,
65
+ interactive_progress: true,
66
+ create_hosted_zones: false,
67
+ )
68
+ ```
69
+
70
+ Here is a table that shows the different configuration options:
71
+
72
+ | Name | Description | Default value |
73
+ |------------------------|-----------------------------------------------------------------------------------------------------------|---------------|
74
+ | `dry_run` | Do not make any changes, just compare and exit, useful with `diff: true`. | `true` |
75
+ | `diff` | Prints a diff to list the changes that are going to be made. | `true` |
76
+ | `force` | Do not ask before changes are made, just apply. | `false` |
77
+ | `delete` | Specifies whether unreferenced entries should be deleted. | `false` |
78
+ | `interactive_progress` | Shows interactive progress whilst changes are being applied, nice for your terminal, not for your CI-job. | `true` |
79
+ | `create_hosted_zones` | Specifies whether or not hosted zones should be created. | `false` |
80
+
81
+ ### `dry_run` and `diff`
82
+
83
+ `dry_run` is useful combined with `diff` because it will let you see the changes in a safe manner without any changes being applied:
84
+
85
+ ![dry_run and diff](readme_files/dry_run_and_diff.png)
86
+
87
+ ### `force: false`
88
+
89
+ With `force` being set to `false` you will be asked whether or not you want to apply the changes:
90
+
91
+ ![force set to false](readme_files/force_false.png)
92
+
93
+ ### `delete: true`
94
+
95
+ With `delete` being set to `true` SprinkleDNS will delete **any** entries not being referenced, these will also show up in the diff (if it is enabled):
96
+
97
+ ![delete true shows up in diffs](readme_files/delete_true_and_diff.png)
98
+
99
+ ### `create_hosted_zones: true`
100
+
101
+ With `create_hosted_zones` set to `true`, SprinkleDNS will create a hosted zone if not existing, it requires the `route53:CreateHostedZone` permission.
102
+
103
+ ## Support for ALIAS-records
104
+
105
+ Route53 supports ALIAS-records to achieve CNAME-flattening, SprinkleDNS also supports that, here we point our root domain to an ELB:
106
+
107
+ ```ruby
108
+ require 'sprinkle_dns'
109
+
110
+ client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
111
+ sdns = SprinkleDNS::Client.new(client)
112
+
113
+ sdns.alias('A', 'billetto.com', 'Z215JYRZR1TBD5', 'dualstack.mothership-test-elb-546580691.eu-central-1.elb.amazonaws.com')
114
+
115
+ sdns.sprinkle!
116
+ ```
117
+
118
+ ## Amazon policy
119
+
120
+ This gem uses the following permissions to manage hosted zones:
121
+
122
+ - `route53:ListHostedZones`, for getting the list of hosted zones.
123
+ - `route53:ListResourceRecordSets`, to read the records for a hosted zone.
124
+ - `route53:ChangeResourceRecordSets`, to change records for a hosted zone.
125
+ - `route53:GetChange`, for reading when a change have been applied.
126
+
127
+ Additionally, you can consider adding the following permissions:
128
+
129
+ - `route53:CreateHostedZone`, for allowing the gem to create hosted zones.
130
+
131
+ You can allow it for all of your hosted zones:
132
+
133
+ ```json
134
+ {
135
+ "Version": "2012-10-17",
136
+ "Statement": [
137
+ {
138
+ "Effect": "Allow",
139
+ "Action": [
140
+ "route53:ListResourceRecordSets",
141
+ "route53:ChangeResourceRecordSets",
142
+ "route53:GetChange",
143
+ "route53:ListHostedZones"
144
+ ],
145
+ "Resource": [
146
+ "*"
147
+ ]
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ For a more "locked down" policy you can use this (remember to update the `resource` array):
154
+
155
+ ```json
156
+ {
157
+ "Version": "2012-10-17",
158
+ "Statement": [
159
+ {
160
+ "Effect": "Allow",
161
+ "Action": [
162
+ "route53:ListResourceRecordSets",
163
+ "route53:ChangeResourceRecordSets"
164
+ ],
165
+ "Resource": [
166
+ "arn:aws:route53:::hostedzone/Z3EATJAGJWXQE8"
167
+ ]
168
+ },
169
+ {
170
+ "Effect": "Allow",
171
+ "Action": [
172
+ "route53:GetChange",
173
+ "route53:ListHostedZones"
174
+ ],
175
+ "Resource": [
176
+ "*"
177
+ ]
178
+ }
179
+ ]
180
+ }
181
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,47 @@
1
+ require 'sprinkle_dns'
2
+
3
+ # require_relative '../test_perms'
4
+ # client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
5
+
6
+ require 'sprinkle_dns/providers/mock_client'
7
+ hz = SprinkleDNS::HostedZone.new('test.colourful.com.')
8
+ pe01 = SprinkleDNS::HostedZoneEntry.new('A', 'noref.test.colourful.com.', Array.wrap('80.80.80.80'), 3600, hz.name)
9
+ pe02 = SprinkleDNS::HostedZoneEntry.new('A', 'updateme.test.colourful.com.', Array.wrap('80.80.80.80'), 3600, hz.name)
10
+ pe03 = SprinkleDNS::HostedZoneEntry.new('TXT', 'txt.test.colourful.com.', %Q{"#{Time.now.to_i}"}, 60, hz.name)
11
+ pe04 = SprinkleDNS::HostedZoneEntry.new('A', 'nochange.test.colourful.com.', Array.wrap('80.80.80.80'), 60, hz.name)
12
+ sleep(1)
13
+ # We are emulating that these records are already live, mark them as persisted
14
+ [pe01, pe02, pe03, pe04].each do |persisted|
15
+ persisted.persisted!
16
+ hz.resource_record_sets << persisted
17
+ end
18
+
19
+ client = SprinkleDNS::MockClient.new([hz])
20
+ sdns = SprinkleDNS::Client.new(client, delete: true, force: true)
21
+
22
+ sdns.entry('A', 'noref.test.billetto.com.', '127.0.0.1', 7200, 'test.billetto.com')
23
+ sdns.alias('A', 'www.test.billetto.com', 'Z215JYRZR1TBD5', 'dualstack.mothership-prod-elb-546580691.eu-central-1.elb.amazonaws.com', 'test.billetto.com')
24
+ sdns.entry('A', 'updateme.test.billetto.com.', '90.90.90.90', 7200, 'test.billetto.com')
25
+ sdns.entry('TXT', 'txt.test.billetto.com', %Q{"#{Time.now.to_i}"}, 60, 'test.billetto.com')
26
+ sdns.entry('A', 'nochange.test.billetto.com.', '80.80.80.80', 60, 'test.billetto.com')
27
+
28
+ existing_hosted_zones, _ = sdns.sprinkle!
29
+
30
+ # ------------------------------------------------------------------------------------------
31
+ # ##########################################################################################
32
+ # ------------------------------------------------------------------------------------------
33
+
34
+ sdns = SprinkleDNS::Client.new(client, delete: true, force: true)
35
+
36
+ sdns.entry('A', 'www.test.billetto.com', '90.90.90.90', 7200, 'test.billetto.com')
37
+ sdns.entry('A', 'updateme.test.billetto.com.', '90.90.90.90', 7200, 'test.billetto.com')
38
+ #sdns.entry('TXT', 'txt.test.billetto.com', %Q{"#{Time.now.to_i}"}, 60, 'test.billetto.com')
39
+ sdns.entry('A', 'nochange.test.billetto.com.', '80.80.80.80', 60, 'test.billetto.com')
40
+
41
+ sdns.entry("MX", 'test.billetto.com', ['1 aspmx.l.google.com',
42
+ '5 alt1.aspmx.l.google.com',
43
+ '5 alt2.aspmx.l.google.com',
44
+ '10 aspmx2.googlemail.com',
45
+ '10 aspmx3.googlemail.com'], 60, 'test.billetto.com')
46
+
47
+ existing_hosted_zones, _ = sdns.sprinkle!
@@ -0,0 +1,61 @@
1
+ require 'sprinkle_dns'
2
+ require 'sprinkle_dns/providers/mock_client'
3
+
4
+ hz01 = SprinkleDNS::HostedZone.new('colourful.co.uk.')
5
+ pe01 = SprinkleDNS::HostedZoneEntry.new('A', 'noref.colourful.co.uk.', Array.wrap('80.80.80.80'), 3600, hz01.name)
6
+ pe02 = SprinkleDNS::HostedZoneEntry.new('A', 'updateme.colourful.co.uk.', Array.wrap('80.80.80.80'), 3600, hz01.name)
7
+ pe03 = SprinkleDNS::HostedZoneEntry.new('TXT', 'txt.colourful.co.uk.', %Q{"#{Time.now.to_i}"}, 60, hz01.name)
8
+ pe04 = SprinkleDNS::HostedZoneEntry.new('A', 'unchanged.colourful.co.uk.', Array.wrap('80.80.80.80'), 60, hz01.name)
9
+
10
+ # We are emulating that these records are already live, mark them as persisted
11
+ [pe01, pe02, pe03, pe04].each do |persisted|
12
+ persisted.persisted!
13
+ hz01.resource_record_sets << persisted
14
+ end
15
+
16
+ hz02 = SprinkleDNS::HostedZone.new('colorful.com.')
17
+ pe05 = SprinkleDNS::HostedZoneEntry.new('A', 'noref.colorful.com.', Array.wrap('80.80.80.80'), 3600, hz02.name)
18
+ pe06 = SprinkleDNS::HostedZoneEntry.new('A', 'updateme.colorful.com.', Array.wrap('80.80.80.80'), 3600, hz02.name)
19
+ pe07 = SprinkleDNS::HostedZoneEntry.new('TXT', 'txt.colorful.com.', %Q{"#{Time.now.to_i}"}, 60, hz02.name)
20
+ pe08 = SprinkleDNS::HostedZoneEntry.new('A', 'nochange.colorful.com.', Array.wrap('80.80.80.80'), 60, hz02.name)
21
+
22
+ # We are emulating that these records are already live, mark them as persisted
23
+ [pe05, pe06, pe07, pe08].each do |persisted|
24
+ persisted.persisted!
25
+ hz02.resource_record_sets << persisted
26
+ end
27
+
28
+ hz03 = SprinkleDNS::HostedZone.new('kolorowy.pl.')
29
+ pe09 = SprinkleDNS::HostedZoneEntry.new('A', 'noref.kolorowy.pl.', Array.wrap('80.80.80.80'), 3600, hz03.name)
30
+ pe10 = SprinkleDNS::HostedZoneEntry.new('A', 'updateme.kolorowy.pl.', Array.wrap('80.80.80.80'), 3600, hz03.name)
31
+ pe11 = SprinkleDNS::HostedZoneEntry.new('TXT', 'txt.kolorowy.pl.', %Q{"#{Time.now.to_i}"}, 60, hz03.name)
32
+ pe12 = SprinkleDNS::HostedZoneEntry.new('A', 'nochange.kolorowy.pl.', Array.wrap('80.80.80.80'), 60, hz03.name)
33
+
34
+ # We are emulating that these records are already live, mark them as persisted
35
+ [pe09, pe10, pe11, pe12].each do |persisted|
36
+ persisted.persisted!
37
+ hz03.resource_record_sets << persisted
38
+ end
39
+
40
+ client = SprinkleDNS::MockClient.new([hz01, hz02, hz03])
41
+ sdns = SprinkleDNS::Client.new(client, force: true, diff: true, delete: true, interactive_progress: true)
42
+
43
+ sdns.entry('A', 'colourful.co.uk', '90.90.90.90', 3600)
44
+ sdns.entry('A', 'updateme.colourful.co.uk', '90.90.90.90', 3600)
45
+ sdns.entry('A', 'unchanged.colourful.co.uk', '80.80.80.80', 60)
46
+ sdns.entry('TXT', 'txt.colourful.co.uk', %Q{"#{Time.now.to_i+1}"}, 60)
47
+ sdns.entry('A', 'colorful.com.', '80.80.80.80', 3601)
48
+ sdns.entry('A', 'kolorowy.pl.', '80.80.80.80', 3601)
49
+
50
+ existing_hosted_zones, _ = sdns.sprinkle!
51
+
52
+ puts "--------------------------------------------------------------------------------------------"
53
+
54
+ client = SprinkleDNS::MockClient.new([hz02, hz01, hz03])
55
+ sdns = SprinkleDNS::Client.new(client, delete: false, interactive_progress: false, force: false)
56
+
57
+ sdns.entry('A', 'colourful.co.uk', '90.90.90.90', 3601)
58
+ sdns.entry('A', 'colorful.com.', '80.80.80.80', 3601)
59
+ sdns.entry('A', 'kolorowy.pl.', '80.80.80.80', 3601)
60
+
61
+ existing_hosted_zones, _ = sdns.sprinkle!
@@ -0,0 +1,27 @@
1
+ require 'sprinkle_dns'
2
+
3
+ require_relative 'test_perms'
4
+ client = SprinkleDNS::Route53Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
5
+
6
+ 25.times do |retry_count|
7
+ sdns = SprinkleDNS::Client.new(client, delete: true, force: true)
8
+
9
+ sdns.entry('A', 'www.mxtest.billetto.com', '90.90.90.90', 7200, 'mxtest.billetto.com')
10
+ sdns.entry('A', 'updateme.mxtest.billetto.com.', '90.90.90.90', 7200, 'mxtest.billetto.com')
11
+ sdns.entry('TXT', 'txt.mxtest.billetto.com', %Q{"#{Time.now.to_i}"}, 60, 'mxtest.billetto.com')
12
+ sdns.entry('A', 'nochange.mxtest.billetto.com.', '80.80.80.80', 60, 'mxtest.billetto.com')
13
+
14
+ sdns.entry("MX", 'mxtest.billetto.com', ['1 aspmx.l.google.com',
15
+ '5 alt1.aspmx.l.google.com',
16
+ '5 alt2.aspmx.l.google.com',
17
+ '10 aspmx2.googlemail.com',
18
+ '10 aspmx3.googlemail.com'], 60, 'mxtest.billetto.com')
19
+
20
+ existing_hosted_zones, _ = sdns.sprinkle!
21
+
22
+ sleep_time = (retry_count ** 4) + 15 + (rand(30) * (retry_count + 1))
23
+ sleep_time_time = Time.now + sleep_time
24
+ puts "Sleeping #{sleep_time} seconds until #{sleep_time_time}"
25
+ puts "------------------------------------------------------------------------------------"
26
+ sleep sleep_time
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'sprinkle_dns'
2
+ require 'sprinkle_dns/providers/mock_client'
3
+
4
+ hz01 = SprinkleDNS::HostedZone.new('colorful.com.')
5
+ pe01 = SprinkleDNS::HostedZoneEntry.new('A', 'colorful.com.', Array.wrap('80.80.80.80'), 3601, hz01.name)
6
+ # We are emulating that these records are already live, mark them as persisted
7
+ [pe01].each do |persisted|
8
+ persisted.persisted!
9
+ hz01.resource_record_sets << persisted
10
+ end
11
+
12
+ client = SprinkleDNS::MockClient.new([hz01])
13
+ sdns = SprinkleDNS::Client.new(client, force: false, diff: true, delete: true, interactive_progress: true, create_hosted_zones: true)
14
+
15
+ sdns.entry('A', 'colourful.co.uk', '90.90.90.90', 3600)
16
+ sdns.entry('A', 'updateme.colourful.co.uk', '90.90.90.90', 3600)
17
+ sdns.entry('A', 'unchanged.colourful.co.uk', '80.80.80.80', 60)
18
+ sdns.entry('TXT', 'txt.colourful.co.uk', %Q{"#{Time.now.to_i+1}"}, 60)
19
+ sdns.entry('A', 'colorful.com.', '80.80.80.80', 3601)
20
+ sdns.entry('A', 'kolorowy.pl.', '80.80.80.80', 3601)
21
+
22
+ existing_hosted_zones, _ = sdns.sprinkle!
@@ -0,0 +1,206 @@
1
+ module SprinkleDNS::CLI
2
+ class HostedZoneDiff
3
+ HostedZone = Struct.new(:action, :name)
4
+ Entry = Struct.new(:action, :type, :type_highlight, :name, :name_highlight, :value1, :value1_highlight, :value2, :value2_highlight, :hosted_zone)
5
+
6
+ def diff(existing_hosted_zones, missing_hosted_zones, configuration)
7
+ entries = []
8
+
9
+ hosted_zones = if configuration.create_hosted_zones?
10
+ (existing_hosted_zones + missing_hosted_zones)
11
+ else
12
+ existing_hosted_zones
13
+ end
14
+
15
+ hosted_zones.each do |hosted_zone|
16
+ policy_service = SprinkleDNS::EntryPolicyService.new(hosted_zone, configuration)
17
+
18
+ to_create = policy_service.entries_to_create
19
+ to_update = policy_service.entries_to_update
20
+ to_delete = policy_service.entries_to_delete
21
+
22
+ if missing_hosted_zones.include?(hosted_zone)
23
+ entries << hosted_zone_to_struct('+', hosted_zone)
24
+ end
25
+
26
+ hosted_zone.entries.each do |entry|
27
+ if to_create.include?(entry)
28
+ entries << entry_to_struct('+', entry, hosted_zone)
29
+ elsif to_update.include?(entry)
30
+ old_entry = entry
31
+ new_entry = entry.new_entry
32
+
33
+ entries << entry_to_struct('u-', old_entry, hosted_zone)
34
+ entries << entry_to_struct('u+', new_entry, hosted_zone, old_entry)
35
+ elsif to_delete.include?(entry)
36
+ entries << entry_to_struct('-', entry, hosted_zone)
37
+ else
38
+ entries << entry_to_struct(nil, entry, hosted_zone)
39
+ end
40
+ end
41
+ end
42
+
43
+ coloured_entries = []
44
+
45
+ entries.each do |e|
46
+ colour_mod = case e.action
47
+ when '+'
48
+ ->(text) { "#{fg(*green)}#{text}#{color_reset}" }
49
+ when '-'
50
+ ->(text) { "#{fg(*red)}#{text}#{color_reset}" }
51
+ when nil
52
+ ->(text) { "#{text}" }
53
+ end
54
+
55
+ colour_mod_highlight = case e.action
56
+ when '+'
57
+ ->(text) { "#{fg(*black)}#{bg(*green)}#{text}#{color_reset}" }
58
+ when '-'
59
+ ->(text) { "#{fg(*black)}#{bg(*red)}#{text}#{color_reset}" }
60
+ when nil
61
+ ->(text) { "#{text}" }
62
+ end
63
+
64
+ case e
65
+ when HostedZone
66
+ information = [colour_mod.call(e.action), colour_mod_highlight.call(bold(e.name))]
67
+ when Entry
68
+ information = [colour_mod.call(e.action)]
69
+
70
+ information << if e.type_highlight
71
+ colour_mod_highlight.call(e.type)
72
+ else
73
+ colour_mod.call(e.type)
74
+ end
75
+
76
+ information << if e.name_highlight
77
+ colour_mod_highlight.call(e.name)
78
+ else
79
+ colour_mod.call(e.name)
80
+ end
81
+
82
+ information << if e.value1_highlight
83
+ colour_mod_highlight.call(e.value1)
84
+ else
85
+ colour_mod.call(e.value1)
86
+ end
87
+
88
+ information << if e.value2_highlight
89
+ colour_mod_highlight.call(e.value2)
90
+ else
91
+ colour_mod.call(e.value2)
92
+ end
93
+ end
94
+
95
+ coloured_entries << information.compact.delete_if(&:empty?)
96
+ end
97
+
98
+ coloured_entries
99
+ end
100
+
101
+ private
102
+
103
+ def hex_to_rgb(hex)
104
+ hex_split = hex.match(/#(..)(..)(..)/)
105
+ [hex_split[1], hex_split[2], hex_split[3]].map(&:hex)
106
+ end
107
+
108
+ def red
109
+ hex_to_rgb('#ff6e67')
110
+ end
111
+
112
+ def green
113
+ hex_to_rgb('#5bf68c')
114
+ end
115
+
116
+ def black
117
+ hex_to_rgb('#000000')
118
+ end
119
+
120
+ def fg(r, g, b)
121
+ "\x1b[38;2;#{r};#{g};#{b}m"
122
+ end
123
+
124
+ def bg(r, g, b)
125
+ "\x1b[48;2;#{r};#{g};#{b}m"
126
+ end
127
+
128
+ def color_reset
129
+ "\x1b[0m"
130
+ end
131
+
132
+ def bold(text)
133
+ "\033[1m#{text}\033[0m"
134
+ end
135
+
136
+ def hosted_zone_to_struct(action, hosted_zone)
137
+ HostedZone.new(action, hosted_zone.name)
138
+ end
139
+
140
+ def entry_to_struct(action, entry, hosted_zone, parent_entry = nil)
141
+ type_highlight, name_highlight, value1_highlight, value2_highlight = if !parent_entry && ['+', '-'].include?(action)
142
+ [true, true, true, true]
143
+ else
144
+ [false, false, nil, nil]
145
+ end
146
+
147
+ action = case action
148
+ when 'u-'
149
+ '-'
150
+ when 'u+'
151
+ '+'
152
+ else
153
+ action
154
+ end
155
+
156
+ value1_highlight ||= if parent_entry
157
+ case parent_entry
158
+ when SprinkleDNS::HostedZoneEntry
159
+ parent_entry.changed_value
160
+ when SprinkleDNS::HostedZoneAlias
161
+ parent_entry.changed_target_hosted_zone_id
162
+ end
163
+ else
164
+ case entry
165
+ when SprinkleDNS::HostedZoneEntry
166
+ entry.changed_value
167
+ when SprinkleDNS::HostedZoneAlias
168
+ entry.changed_target_hosted_zone_id
169
+ end
170
+ end
171
+
172
+ value2_highlight ||= if parent_entry
173
+ case parent_entry
174
+ when SprinkleDNS::HostedZoneEntry
175
+ parent_entry.changed_ttl
176
+ when SprinkleDNS::HostedZoneAlias
177
+ parent_entry.changed_target_dns_name
178
+ end
179
+ else
180
+ case entry
181
+ when SprinkleDNS::HostedZoneEntry
182
+ entry.changed_ttl
183
+ when SprinkleDNS::HostedZoneAlias
184
+ entry.changed_target_dns_name
185
+ end
186
+ end
187
+
188
+ case entry
189
+ when SprinkleDNS::HostedZoneEntry
190
+ Entry.new(action,
191
+ entry.type, type_highlight,
192
+ entry.name, name_highlight,
193
+ entry.value, value1_highlight,
194
+ entry.ttl, value2_highlight,
195
+ hosted_zone.name)
196
+ when SprinkleDNS::HostedZoneAlias
197
+ Entry.new(action,
198
+ entry.type, type_highlight,
199
+ entry.name, name_highlight,
200
+ entry.target_hosted_zone_id, value1_highlight,
201
+ entry.target_dns_name, value2_highlight,
202
+ hosted_zone.name)
203
+ end
204
+ end
205
+ end
206
+ end