roadworker 0.2.5 → 0.3.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.
- data/README.md +22 -1
- data/bin/roadwork +23 -16
- data/lib/roadworker/client.rb +6 -0
- data/lib/roadworker/dsl-converter.rb +20 -5
- data/lib/roadworker/dsl.rb +12 -0
- data/lib/roadworker/route53-exporter.rb +16 -6
- data/lib/roadworker/route53-health-check.rb +120 -0
- data/lib/roadworker/route53-wrapper.rb +17 -3
- data/lib/roadworker/version.rb +1 -1
- metadata +19 -2
data/README.md
CHANGED
@@ -6,7 +6,6 @@ It defines the state of Route53 using DSL, and updates Route53 according to DSL.
|
|
6
6
|
|
7
7
|
**Notice**
|
8
8
|
|
9
|
-
* HealthCheck is not supported.
|
10
9
|
* Cannot update TTL of two or more same records (with different SetIdentifier) after creation.
|
11
10
|
|
12
11
|
## Installation
|
@@ -61,6 +60,28 @@ hosted_zone "info.winebarrel.jp." do
|
|
61
60
|
rrset "xxx.info.winebarrel.jp.", "A" do
|
62
61
|
dns_name "elb-dns-name.elb.amazonaws.com"
|
63
62
|
end
|
63
|
+
|
64
|
+
rrset "zzz.info.winebarrel.jp", "A" do
|
65
|
+
set_identifier "Primary"
|
66
|
+
failover "PRIMARY"
|
67
|
+
health_check "http://192.0.43.10:80/path", "example.com"
|
68
|
+
ttl 456
|
69
|
+
resource_records(
|
70
|
+
"127.0.0.1",
|
71
|
+
"127.0.0.2"
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
rrset "zzz.info.winebarrel.jp", "A" do
|
76
|
+
set_identifier "Secondary"
|
77
|
+
failover "SECONDARY"
|
78
|
+
health_check "tcp://192.0.43.10:3306"
|
79
|
+
ttl 456
|
80
|
+
resource_records(
|
81
|
+
"127.0.0.3",
|
82
|
+
"127.0.0.4"
|
83
|
+
)
|
84
|
+
end
|
64
85
|
end
|
65
86
|
```
|
66
87
|
|
data/bin/roadwork
CHANGED
@@ -29,19 +29,20 @@ ARGV.options do |opt|
|
|
29
29
|
access_key = nil
|
30
30
|
secret_key = nil
|
31
31
|
|
32
|
-
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key
|
33
|
-
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key
|
34
|
-
opt.on('-a', '--apply') {|v| mode
|
35
|
-
opt.on('-f', '--file FILE') {|v| file
|
36
|
-
opt.on('', '--dry-run') {|v| options[:dry_run]
|
37
|
-
opt.on('' , '--force') { options[:force]
|
38
|
-
opt.on('
|
39
|
-
opt.on('-
|
40
|
-
opt.on('',
|
41
|
-
opt.on('', '--
|
42
|
-
opt.on('
|
43
|
-
opt.on(''
|
44
|
-
opt.on('' , '--
|
32
|
+
opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
|
33
|
+
opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
|
34
|
+
opt.on('-a', '--apply') {|v| mode = :apply }
|
35
|
+
opt.on('-f', '--file FILE') {|v| file = v }
|
36
|
+
opt.on('', '--dry-run') {|v| options[:dry_run] = true }
|
37
|
+
opt.on('' , '--force') { options[:force] = true }
|
38
|
+
opt.on('', '--no-health-check-gc') {|v| options[:no_health_check_gc] = true }
|
39
|
+
opt.on('-e', '--export') {|v| mode = :export }
|
40
|
+
opt.on('-o', '--output FILE') {|v| output_file = v }
|
41
|
+
opt.on('', '--split') {|v| split = true }
|
42
|
+
opt.on('', '--with-soa-ns') {|v| options[:with_soa_ns] = true }
|
43
|
+
opt.on('-t', '--test') {|v| mode = :test }
|
44
|
+
opt.on('' , '--no-color') { options[:color] = false }
|
45
|
+
opt.on('' , '--debug') { options[:debug] = true }
|
45
46
|
opt.parse!
|
46
47
|
|
47
48
|
if access_key and secret_key
|
@@ -76,13 +77,19 @@ begin
|
|
76
77
|
output_file = 'Routefile' if output_file == '-'
|
77
78
|
requires = []
|
78
79
|
|
79
|
-
client.export do |
|
80
|
-
hosted_zones.each do |zone|
|
80
|
+
client.export do |exported, converter|
|
81
|
+
exported[:hosted_zones].each do |zone|
|
81
82
|
route_file = File.join(File.dirname(output_file), "#{zone[:name].sub(/\.\Z/, '')}.route")
|
82
83
|
requires << route_file
|
83
84
|
|
84
85
|
logger.info(" write `#{route_file}`")
|
85
|
-
|
86
|
+
|
87
|
+
open(route_file, 'wb') do |f|
|
88
|
+
f.puts converter.call({
|
89
|
+
:hosted_zones => [zone],
|
90
|
+
:health_checks => exported[:health_checks],
|
91
|
+
})
|
92
|
+
end
|
86
93
|
end
|
87
94
|
end
|
88
95
|
|
data/lib/roadworker/client.rb
CHANGED
@@ -15,6 +15,8 @@ module Roadworker
|
|
15
15
|
@options.logger ||= Logger.new($stdout)
|
16
16
|
String.colorize = @options.color
|
17
17
|
@options.route53 = AWS::Route53.new
|
18
|
+
@health_checks = HealthCheck.health_checks(@options.route53, :extended => true)
|
19
|
+
@options.health_checks = @health_checks
|
18
20
|
@route53 = Route53Wrapper.new(@options)
|
19
21
|
end
|
20
22
|
|
@@ -31,6 +33,10 @@ module Roadworker
|
|
31
33
|
}
|
32
34
|
end
|
33
35
|
|
36
|
+
if updated and not @options.no_health_check_gc
|
37
|
+
HealthCheck.gc(@options.route53, :logger => @options.logger)
|
38
|
+
end
|
39
|
+
|
34
40
|
return updated
|
35
41
|
end
|
36
42
|
|
@@ -3,24 +3,40 @@ module Roadworker
|
|
3
3
|
class Converter
|
4
4
|
|
5
5
|
class << self
|
6
|
-
def convert(
|
7
|
-
|
6
|
+
def convert(exported)
|
7
|
+
self.new(exported).convert
|
8
8
|
end
|
9
|
+
end # of class method
|
10
|
+
|
11
|
+
def initialize(exported)
|
12
|
+
@health_checks = exported[:health_checks]
|
13
|
+
@hosted_zones = exported[:hosted_zones]
|
14
|
+
end
|
9
15
|
|
10
|
-
|
16
|
+
def convert
|
17
|
+
@hosted_zones.map {|i| output_zone(i) }.join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
11
21
|
|
12
22
|
def output_rrset(recrod)
|
13
23
|
name = recrod.delete(:name).inspect
|
14
24
|
type = recrod.delete(:type).inspect
|
15
25
|
|
16
26
|
attrs = recrod.map {|key, value|
|
17
|
-
|
27
|
+
case key
|
28
|
+
when :resource_records
|
18
29
|
if value.empty?
|
19
30
|
nil
|
20
31
|
else
|
21
32
|
value = value.map {|i| i.inspect }.join(",\n ")
|
22
33
|
"#{key}(\n #{value}\n )"
|
23
34
|
end
|
35
|
+
when :health_check_id
|
36
|
+
config = HealthCheck.config_to_hash(@health_checks[value])
|
37
|
+
hc_args = config[:url].inspect
|
38
|
+
hc_args << ", #{config[:host_name].inspect}" if config[:host_name]
|
39
|
+
"health_check #{hc_args}"
|
24
40
|
else
|
25
41
|
"#{key} #{value.inspect}"
|
26
42
|
end
|
@@ -43,7 +59,6 @@ hosted_zone #{name} do
|
|
43
59
|
end
|
44
60
|
EOS
|
45
61
|
end
|
46
|
-
end # of class method
|
47
62
|
|
48
63
|
end # Converter
|
49
64
|
end # DSL
|
data/lib/roadworker/dsl.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'roadworker/dsl-converter'
|
2
2
|
require 'roadworker/dsl-tester'
|
3
|
+
require 'roadworker/route53-health-check'
|
3
4
|
|
4
5
|
require 'ostruct'
|
6
|
+
require 'uri'
|
5
7
|
|
6
8
|
module Roadworker
|
7
9
|
class DSL
|
@@ -111,6 +113,16 @@ module Roadworker
|
|
111
113
|
@result.dns_name = value
|
112
114
|
end
|
113
115
|
|
116
|
+
def failover(value)
|
117
|
+
@result.failover = value
|
118
|
+
end
|
119
|
+
|
120
|
+
def health_check(url, host_name = nil)
|
121
|
+
config = HealthCheck.parse_url(url)
|
122
|
+
config[:fully_qualified_domain_name] = host_name if host_name
|
123
|
+
@result.health_check = config
|
124
|
+
end
|
125
|
+
|
114
126
|
def resource_records(*values)
|
115
127
|
if values.uniq.length != values.length
|
116
128
|
raise "Duplicate ResourceRecords: #{values.join(', ')}"
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'roadworker/collection'
|
2
|
+
require 'roadworker/route53-health-check'
|
2
3
|
|
3
4
|
require 'ostruct'
|
4
5
|
|
@@ -16,11 +17,22 @@ module Roadworker
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def export
|
19
|
-
result =
|
20
|
+
result = {
|
21
|
+
:health_checks => HealthCheck.health_checks(@options.route53),
|
22
|
+
}
|
20
23
|
|
24
|
+
hosted_zones = result[:hosted_zones] = []
|
25
|
+
export_hosted_zones(hosted_zones)
|
26
|
+
|
27
|
+
return result
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def export_hosted_zones(hosted_zones)
|
21
33
|
Collection.batch(@options.route53.hosted_zones) do |zone|
|
22
34
|
zone_h = item_to_hash(zone, :name)
|
23
|
-
|
35
|
+
hosted_zones << zone_h
|
24
36
|
|
25
37
|
rrsets = []
|
26
38
|
zone_h[:rrsets] = rrsets
|
@@ -39,6 +51,8 @@ module Roadworker
|
|
39
51
|
:resource_records,
|
40
52
|
:alias_target,
|
41
53
|
:region,
|
54
|
+
:failover,
|
55
|
+
:health_check_id,
|
42
56
|
]
|
43
57
|
|
44
58
|
record_h = item_to_hash(record, *attrs)
|
@@ -53,12 +67,8 @@ module Roadworker
|
|
53
67
|
end
|
54
68
|
end
|
55
69
|
end
|
56
|
-
|
57
|
-
return result
|
58
70
|
end
|
59
71
|
|
60
|
-
private
|
61
|
-
|
62
72
|
def item_to_hash(item, *attrs)
|
63
73
|
h = {}
|
64
74
|
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'uuid'
|
3
|
+
|
4
|
+
module Roadworker
|
5
|
+
class HealthCheck
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def health_checks(route53, options = {})
|
9
|
+
self.new(route53).health_checks(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def gc(route53, options = {})
|
13
|
+
self.new(route53).gc(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def config_to_hash(config)
|
17
|
+
ipaddr = config[:ip_address]
|
18
|
+
port = config[:port]
|
19
|
+
type = config[:type].downcase
|
20
|
+
path = config[:resource_path]
|
21
|
+
fqdn = config[:fully_qualified_domain_name].downcase
|
22
|
+
|
23
|
+
url = "#{type}://#{ipaddr}:#{port}"
|
24
|
+
url << path if path && path != '/'
|
25
|
+
|
26
|
+
{:url => url, :host_name => fqdn}
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_url(url)
|
30
|
+
url = URI.parse(url)
|
31
|
+
path = url.path
|
32
|
+
|
33
|
+
if path.nil? or path.empty? or path == '/'
|
34
|
+
path = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
config = {}
|
38
|
+
|
39
|
+
{
|
40
|
+
:ip_address => url.host,
|
41
|
+
:port => url.port,
|
42
|
+
:type => url.scheme.upcase,
|
43
|
+
:resource_path => path,
|
44
|
+
}.each {|key, value|
|
45
|
+
config[key] = value if value
|
46
|
+
}
|
47
|
+
|
48
|
+
return config
|
49
|
+
end
|
50
|
+
end # of class method
|
51
|
+
|
52
|
+
def initialize(route53)
|
53
|
+
@route53 = route53
|
54
|
+
end
|
55
|
+
|
56
|
+
def health_checks(options = {})
|
57
|
+
check_list = {}
|
58
|
+
|
59
|
+
is_truncated = true
|
60
|
+
next_marker = nil
|
61
|
+
|
62
|
+
while is_truncated
|
63
|
+
opts = next_marker ? {:marker => next_marker} : {}
|
64
|
+
response = @route53.client.list_health_checks(opts)
|
65
|
+
|
66
|
+
response[:health_checks].each do |check|
|
67
|
+
check_list[check[:id]] = check[:health_check_config]
|
68
|
+
end
|
69
|
+
|
70
|
+
is_truncated = response[:is_truncated]
|
71
|
+
next_marker = response[:next_marker]
|
72
|
+
end
|
73
|
+
|
74
|
+
if options[:extended]
|
75
|
+
check_list.instance_variable_set(:@route53, @route53)
|
76
|
+
|
77
|
+
def check_list.find_or_create(attrs)
|
78
|
+
health_check_id, config = self.find {|hcid, elems| elems == attrs }
|
79
|
+
|
80
|
+
unless health_check_id
|
81
|
+
response = @route53.client.create_health_check({
|
82
|
+
:caller_reference => UUID.new.generate,
|
83
|
+
:health_check_config => attrs,
|
84
|
+
})
|
85
|
+
|
86
|
+
health_check_id = response[:health_check][:id]
|
87
|
+
config = response[:health_check][:health_check_config]
|
88
|
+
self[health_check_id] = config
|
89
|
+
end
|
90
|
+
|
91
|
+
return health_check_id
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
return check_list
|
96
|
+
end
|
97
|
+
|
98
|
+
def gc(options = {})
|
99
|
+
AWS.memoize {
|
100
|
+
check_list = health_checks
|
101
|
+
return if check_list.empty?
|
102
|
+
|
103
|
+
if (logger = options[:logger])
|
104
|
+
logger.info('Clean HealthChecks (pass `--no-health-check-gc` if you do not want to clean)')
|
105
|
+
end
|
106
|
+
|
107
|
+
@route53.hosted_zones.each do |zone|
|
108
|
+
zone.rrsets.each do |record|
|
109
|
+
check_list.delete(record.health_check_id)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
check_list.each do |health_check_id, config|
|
114
|
+
@route53.client.delete_health_check(:health_check_id => health_check_id)
|
115
|
+
end
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
end # HealthCheck
|
120
|
+
end # Roadworker
|
@@ -14,7 +14,9 @@ module Roadworker
|
|
14
14
|
:ttl,
|
15
15
|
:resource_records,
|
16
16
|
:dns_name,
|
17
|
-
:region
|
17
|
+
:region,
|
18
|
+
:failover,
|
19
|
+
:health_check,
|
18
20
|
]
|
19
21
|
|
20
22
|
def initialize(options)
|
@@ -130,6 +132,9 @@ module Roadworker
|
|
130
132
|
when :dns_name
|
131
133
|
attr = :alias_target
|
132
134
|
value = @options.route53.dns_name_to_alias_target(value)
|
135
|
+
when :health_check
|
136
|
+
attr = :health_check_id
|
137
|
+
value = @options.health_checks.find_or_create(value)
|
133
138
|
end
|
134
139
|
|
135
140
|
opts[attr] = value
|
@@ -231,6 +236,10 @@ module Roadworker
|
|
231
236
|
value ? value.gsub("\\052", '*') : value
|
232
237
|
end
|
233
238
|
|
239
|
+
def dns_name
|
240
|
+
(@resource_record_set.alias_target || {})[:dns_name]
|
241
|
+
end
|
242
|
+
|
234
243
|
def dns_name=(name)
|
235
244
|
if name
|
236
245
|
@resource_record_set.alias_target = @options.route53.dns_name_to_alias_target(name)
|
@@ -239,8 +248,13 @@ module Roadworker
|
|
239
248
|
end
|
240
249
|
end
|
241
250
|
|
242
|
-
def
|
243
|
-
|
251
|
+
def health_check
|
252
|
+
@options.health_checks[@resource_record_set.health_check_id]
|
253
|
+
end
|
254
|
+
|
255
|
+
def health_check=(check)
|
256
|
+
health_check_id = check ? @options.health_checks.find_or_create(check) : nil
|
257
|
+
@resource_record_set.health_check_id = health_check_id
|
244
258
|
end
|
245
259
|
|
246
260
|
private
|
data/lib/roadworker/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roadworker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.8.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: uuid
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.3.7
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.3.7
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: bundler
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +141,7 @@ files:
|
|
125
141
|
- lib/roadworker/log.rb
|
126
142
|
- lib/roadworker/route53-exporter.rb
|
127
143
|
- lib/roadworker/route53-ext.rb
|
144
|
+
- lib/roadworker/route53-health-check.rb
|
128
145
|
- lib/roadworker/route53-wrapper.rb
|
129
146
|
- lib/roadworker/string-ext.rb
|
130
147
|
- lib/roadworker/version.rb
|