roadworker 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
 
@@ -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 = 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('-e', '--export') {|v| mode = :export }
39
- opt.on('-o', '--output FILE') {|v| output_file = v }
40
- opt.on('', '--split') {|v| split = true }
41
- opt.on('', '--with-soa-ns') {|v| options[:with_soa_ns] = true }
42
- opt.on('-t', '--test') {|v| mode = :test }
43
- opt.on('' , '--no-color') { options[:color] = false }
44
- opt.on('' , '--debug') { options[:debug] = true }
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 |hosted_zones, converter|
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
- open(route_file, 'wb') {|f| f.puts converter.call([zone]) }
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
 
@@ -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(hosted_zones)
7
- hosted_zones.map {|i| output_zone(i) }.join("\n")
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
- private
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
- if value.kind_of?(Array)
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
@@ -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
- result << zone_h
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 dns_name
243
- (@resource_record_set.alias_target || {})[:dns_name]
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
@@ -1,5 +1,5 @@
1
1
  module Roadworker
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
4
4
 
5
5
  Version = Roadworker::VERSION
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.2.5
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-07-31 00:00:00.000000000 Z
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