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 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