roadworker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ # Roadworker
2
+
3
+ Roadworker is a tool to manage Route53.
4
+
5
+ It defines the state of Route53 using DSL, and updates Route53 according to DSL.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'roadworker'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install roadworker
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ shell> export AWS_ACCESS_KEY_ID='...'
25
+ shell> export AWS_SECRET_ACCESS_KEY='...'
26
+ shell> roadwork -e -o Routefile
27
+ shell> vi Routefile
28
+ shell> roadwork --dry-run
29
+ shell> roudwork
30
+ ```
31
+
32
+ ## Routefile esample
33
+
34
+ ```ruby
35
+ require 'other/routefile'
36
+
37
+ hosted_zone "winebarrel.jp." do
38
+ rrset "winebarrel.jp.", "MX" do
39
+ ttl 300
40
+ resource_records(
41
+ "10 mx.winebarrel.jp",
42
+ "10 mx2.winebarrel.jp"
43
+ )
44
+ end
45
+
46
+ rrset "winebarrel.jp.", "NS" do
47
+ ttl 86400
48
+ resource_records(
49
+ "ns-XXX.awsdns-XX.com.",
50
+ "ns-XXX.awsdns-XX.org.",
51
+ "ns-XXX.awsdns-XX.net.",
52
+ "ns-XXX.awsdns-XX.co.uk."
53
+ )
54
+ end
55
+
56
+ rrset "winebarrel.jp.", "SOA" do
57
+ ttl 86400
58
+ resource_records(
59
+ "ns-XXX.awsdns-XX.com. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
60
+ )
61
+ end
62
+ end
63
+
64
+ hosted_zone "info.winebarrel.jp." do
65
+ rrset "xxx.info.winebarrel.jp.", "A" do
66
+ dns_name "elb-dns-name.elb.amazonaws.com"
67
+ end
68
+ end
69
+ ```
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'roadworker'
5
+ require 'optparse'
6
+ require 'logger'
7
+
8
+ file = 'Routefile'
9
+ output_file = '-'
10
+ export_mode = false
11
+ logger = Logger.new($stdout)
12
+
13
+ logger.formatter = proc {|severity, datetime, progname, msg|
14
+ "#{msg}\n"
15
+ }
16
+ options = {
17
+ :logger => logger,
18
+ :dry_run => false,
19
+ :debug => false,
20
+ :force => false,
21
+ :color => true,
22
+ }
23
+
24
+ ARGV.options do |opt|
25
+ access_key = nil
26
+ secret_key = nil
27
+
28
+ opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
29
+ opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
30
+ opt.on('-f', '--file FILE') {|v| file = v }
31
+ opt.on('-e', '--export') {|v| export_mode = true }
32
+ opt.on('-o', '--output FILE') {|v| output_file = v }
33
+ opt.on('', '--dry-run') {|v| options[:dry_run] = true }
34
+ opt.on('' , '--force') { options[:force] = true }
35
+ opt.on('' , '--no-color') { options[:color] = false }
36
+ opt.on('' , '--debug') { options[:debug] = true }
37
+ opt.parse!
38
+
39
+ if access_key and secret_key
40
+ AWS.config({
41
+ :access_key_id => access_key,
42
+ :secret_access_key => secret_key,
43
+ })
44
+ elsif (access_key and !secret_key) or (!access_key and secret_key)
45
+ puts opt.help
46
+ exit 1
47
+ end
48
+ end
49
+
50
+ if options[:debug]
51
+ AWS.config(
52
+ :http_wire_trace => true,
53
+ :logger => options[:logger]
54
+ )
55
+ end
56
+
57
+ begin
58
+ client = Roadworker::Client.new(options)
59
+
60
+ if export_mode
61
+ exported = client.export
62
+
63
+ if output_file == '-'
64
+ logger.info('Export Route53')
65
+ puts client.export
66
+ else
67
+ logger.info("Export Route53 to `#{output_file}`")
68
+ open(output_file, 'wb') {|f| f.puts client.export }
69
+ end
70
+ else
71
+ unless File.exist?(file)
72
+ raise "No Routefile found (looking for: #{file})"
73
+ end
74
+
75
+ msg = "Apply `#{file}` to Route53"
76
+ msg << ' (dry-run)' if options[:dry_run]
77
+ logger.info(msg)
78
+
79
+ updated = client.apply(file)
80
+
81
+ logger.info('There is no update') unless updated
82
+ end
83
+ rescue => e
84
+ if options[:debug]
85
+ raise e
86
+ else
87
+ $stderr.puts e
88
+ end
89
+ end
@@ -0,0 +1,2 @@
1
+ require 'roadworker/version'
2
+ require 'roadworker/client'
@@ -0,0 +1,109 @@
1
+ require 'roadworker/string-ext'
2
+ require 'roadworker/dsl'
3
+ require 'roadworker/log'
4
+ require 'roadworker/route53-wrapper'
5
+
6
+ require 'logger'
7
+ require 'ostruct'
8
+
9
+ module Roadworker
10
+ class Client
11
+ include Roadworker::Log
12
+
13
+ def initialize(options)
14
+ @options = OpenStruct.new(options)
15
+ @options.logger ||= Logger.new($stdout)
16
+ String.colorize = @options.color
17
+ @options.route53 = AWS::Route53.new
18
+ @route53 = Route53Wrapper.new(@options)
19
+ end
20
+
21
+ def apply(file)
22
+ dsl = nil
23
+
24
+ if file.kind_of?(String)
25
+ open(file) do |f|
26
+ dsl = DSL.define(f.read, file).result
27
+ end
28
+ else
29
+ dsl = DSL.define(file.read, file.path).result
30
+ end
31
+
32
+ updated = false
33
+
34
+ if dsl.hosted_zones.empty? and not @options.force
35
+ log(:warn, "Nothing is defined (pass `--force` if you want to remove)", :yellow)
36
+ else
37
+ AWS.memoize {
38
+ walk_hosted_zones(dsl)
39
+ updated = @options.updated
40
+ }
41
+ end
42
+
43
+ return updated
44
+ end
45
+
46
+ def export
47
+ exported = AWS.memoize { @route53.export }
48
+ DSL.convert(exported)
49
+ end
50
+
51
+ private
52
+
53
+ def walk_hosted_zones(dsl)
54
+ expected = collection_to_hash(dsl.hosted_zones, :name)
55
+ actual = collection_to_hash(@route53.hosted_zones, :name)
56
+
57
+ expected.each do |keys, expected_zone|
58
+ name = keys[0]
59
+ actual_zone = actual.delete(keys) || @route53.hosted_zones.create(name)
60
+ walk_rrsets(expected_zone, actual_zone)
61
+ end
62
+
63
+ actual.each do |keys, zone|
64
+ zone.rrsets.each do |record|
65
+ record.delete(:cascaded => true)
66
+ end
67
+
68
+ zone.delete
69
+ end
70
+ end
71
+
72
+ def walk_rrsets(expected_zone, actual_zone)
73
+ expected = collection_to_hash(expected_zone.rrsets, :name, :type, :set_identifier)
74
+ actual = collection_to_hash(actual_zone.rrsets, :name, :type, :set_identifier)
75
+
76
+ expected.each do |keys, expected_record|
77
+ name = keys[0]
78
+ type = keys[1]
79
+ set_identifier = keys[2]
80
+
81
+ actual_record = actual.delete(keys)
82
+
83
+ if actual_record
84
+ unless actual_record.eql?(expected_record)
85
+ actual_record.update(expected_record)
86
+ end
87
+ else
88
+ actual_record = actual_zone.rrsets.create(name, type, expected_record)
89
+ end
90
+ end
91
+
92
+ actual.each do |keys, record|
93
+ record.delete
94
+ end
95
+ end
96
+
97
+ def collection_to_hash(collection, *keys)
98
+ hash = {}
99
+
100
+ collection.each do |item|
101
+ key_list = keys.map {|k| item.send(k) }
102
+ hash[key_list] = item
103
+ end
104
+
105
+ return hash
106
+ end
107
+
108
+ end # Client
109
+ end # Roadworker
@@ -0,0 +1,21 @@
1
+ module Roadworker
2
+ class Collection
3
+
4
+ class << self
5
+ def batch(collection)
6
+ if collection.respond_to?(:each_batch)
7
+ collection.each_batch do |batch|
8
+ batch.each do |item|
9
+ yield(item)
10
+ end
11
+ end
12
+ else
13
+ collection.each do |item|
14
+ yield(item)
15
+ end
16
+ end
17
+ end
18
+ end # of class method
19
+
20
+ end # Collection
21
+ end # Roadworker
@@ -0,0 +1,50 @@
1
+ module Roadworker
2
+ class DSL
3
+ class Converter
4
+
5
+ class << self
6
+ def convert(hosted_zones)
7
+ hosted_zones.map {|i| output_zone(i) }.join("\n")
8
+ end
9
+
10
+ private
11
+
12
+ def output_rrset(recrod)
13
+ name = recrod.delete(:name).inspect
14
+ type = recrod.delete(:type).inspect
15
+
16
+ attrs = recrod.map {|key, value|
17
+ if value.kind_of?(Array)
18
+ if value.empty?
19
+ nil
20
+ else
21
+ value = value.map {|i| i.inspect }.join(",\n ")
22
+ "#{key}(\n #{value}\n )"
23
+ end
24
+ else
25
+ "#{key} #{value.inspect}"
26
+ end
27
+ }.select {|i| i }.join("\n ")
28
+
29
+ return(<<-EOS)
30
+ rrset #{name}, #{type} do
31
+ #{attrs}
32
+ end
33
+ EOS
34
+ end
35
+
36
+ def output_zone(zone)
37
+ name = zone[:name].inspect
38
+ rrsets = zone[:rrsets]
39
+
40
+ return(<<-EOS)
41
+ hosted_zone #{name} do
42
+ #{rrsets.map {|i| output_rrset(i) }.join("\n").chomp}
43
+ end
44
+ EOS
45
+ end
46
+ end # of class method
47
+
48
+ end # Converter
49
+ end # DSL
50
+ end # Roadworker
@@ -0,0 +1,119 @@
1
+ require 'roadworker/dsl-converter'
2
+
3
+ require 'ostruct'
4
+
5
+ module Roadworker
6
+ class DSL
7
+
8
+ class << self
9
+ def define(source, path)
10
+ self.new(path) do
11
+ eval(source, binding)
12
+ end
13
+ end
14
+
15
+ def convert(hosted_zones)
16
+ Converter.convert(hosted_zones)
17
+ end
18
+ end # of class method
19
+
20
+ attr_reader :result
21
+
22
+ def initialize(path, &block)
23
+ @path = path
24
+ @result = OpenStruct.new({:hosted_zones => []})
25
+ instance_eval(&block)
26
+ end
27
+
28
+ private
29
+
30
+ def require(file)
31
+ routefile = File.expand_path(File.join(File.dirname(@path), file))
32
+
33
+ if File.exist?(routefile)
34
+ instance_eval(File.read(routefile))
35
+ else
36
+ Kernel.require(file)
37
+ end
38
+ end
39
+
40
+ def hosted_zone(name, &block)
41
+ @result.hosted_zones << HostedZone.new(name, &block).result
42
+ end
43
+
44
+ class HostedZone
45
+ attr_reader :result
46
+
47
+ def initialize(name, &block)
48
+ @name = name
49
+ rrsets = []
50
+
51
+ @result = OpenStruct.new({
52
+ :name => name,
53
+ :resource_record_sets => rrsets,
54
+ :rrsets => rrsets,
55
+ })
56
+
57
+ instance_eval(&block)
58
+ end
59
+
60
+ private
61
+
62
+ def resource_record_set(rrset_name, type, &block)
63
+ if rrset_name.gsub(/\.\Z/, '') !~ /#{Regexp.escape(@name.gsub(/\.\Z/, ''))}\Z/i
64
+ raise "Invalid ResourceRecordSet Name: #{rrset_name}"
65
+ end
66
+
67
+ @result.resource_record_sets << ResourceRecordSet.new(rrset_name, type, &block).result
68
+ end
69
+ alias rrset resource_record_set
70
+
71
+ class ResourceRecordSet
72
+ attr_reader :result
73
+
74
+ def initialize(name, type, &block)
75
+ @result = OpenStruct.new({
76
+ :name => name,
77
+ :type => type,
78
+ })
79
+
80
+ instance_eval(&block)
81
+ end
82
+
83
+ private
84
+
85
+ def set_identifier(value = nil)
86
+ @result.set_identifier = value
87
+ end
88
+ alias identifier set_identifier
89
+
90
+ def weight(value)
91
+ @result.weight = value
92
+ end
93
+
94
+ def ttl(value)
95
+ @result.ttl = value
96
+ end
97
+
98
+ def region(value)
99
+ @result.region = value
100
+ end
101
+
102
+ def dns_name(value)
103
+ @result.dns_name = value
104
+ end
105
+
106
+ def resource_records(*values)
107
+ if values.uniq.length != values.length
108
+ raise "Duplicate ResourceRecords: #{values.join(', ')}"
109
+ end
110
+
111
+ @result.resource_records = [values].flatten.map {|i| {:value => i} }
112
+ end
113
+
114
+ end # ResourceRecordSet
115
+
116
+ end # HostedZone
117
+
118
+ end # DSL
119
+ end # RoadWorker
@@ -0,0 +1,12 @@
1
+ module Roadworker
2
+ module Log
3
+
4
+ def log(level, message, color, log_id = nil)
5
+ log_id = yield if block_given?
6
+ message = "#{message}: #{log_id}"
7
+ message << ' (dry-run)' if @options.dry_run
8
+ @options.logger.send(level, message.send(color))
9
+ end
10
+
11
+ end # Log
12
+ end # Roadworker
@@ -0,0 +1,69 @@
1
+ require 'roadworker/collection'
2
+
3
+ require 'ostruct'
4
+
5
+ module Roadworker
6
+ class Exporter
7
+
8
+ class << self
9
+ def export(route53)
10
+ self.new(route53).export
11
+ end
12
+ end # of class method
13
+
14
+ def initialize(route53)
15
+ @route53 = route53
16
+ end
17
+
18
+ def export
19
+ result = []
20
+
21
+ Collection.batch(@route53.hosted_zones) do |zone|
22
+ zone_h = item_to_hash(zone, :name)
23
+ result << zone_h
24
+
25
+ rrsets = []
26
+ zone_h[:rrsets] = rrsets
27
+
28
+ Collection.batch(zone.rrsets) do |record|
29
+ attrs = [
30
+ :name,
31
+ :type,
32
+ :set_identifier,
33
+ :weight,
34
+ :ttl,
35
+ :resource_records,
36
+ :alias_target,
37
+ :region,
38
+ ]
39
+
40
+ record_h = item_to_hash(record, *attrs)
41
+ rrsets << record_h
42
+
43
+ rrs = record_h.delete(:resource_records)
44
+ record_h[:resource_records] = rrs.map {|i| i[:value] }
45
+
46
+ if (alias_target = record_h.delete(:alias_target))
47
+ record_h[:dns_name] = alias_target[:dns_name]
48
+ end
49
+ end
50
+ end
51
+
52
+ return result
53
+ end
54
+
55
+ private
56
+
57
+ def item_to_hash(item, *attrs)
58
+ h = {}
59
+
60
+ attrs.each do |attr|
61
+ value = item.send(attr)
62
+ h[attr] = value if value
63
+ end
64
+
65
+ return h
66
+ end
67
+
68
+ end # Exporter
69
+ end # Roadworker
@@ -0,0 +1,32 @@
1
+ require 'aws-sdk'
2
+
3
+ module AWS
4
+ class Route53
5
+
6
+ def dns_name_to_alias_target(name)
7
+ name = name.gsub(/\.\Z/, '')
8
+
9
+ unless name =~ /([^.]+)\.elb\.amazonaws.com\Z/i
10
+ raise "Invalid DNS Name: #{name}"
11
+ end
12
+
13
+ region = $1.downcase
14
+ elb = AWS::ELB.new(:region => region)
15
+
16
+ load_balancer = elb.load_balancers.find do |lb|
17
+ lb.dns_name == name
18
+ end
19
+
20
+ unless load_balancer
21
+ raise "Cannot find ELB: #{name}"
22
+ end
23
+
24
+ {
25
+ :hosted_zone_id => load_balancer.canonical_hosted_zone_name_id,
26
+ :dns_name => load_balancer.dns_name,
27
+ :evaluate_target_health => false, # XXX:
28
+ }
29
+ end
30
+
31
+ end # Route53
32
+ end # Roadworker
@@ -0,0 +1,253 @@
1
+ require 'roadworker/collection'
2
+ require 'roadworker/log'
3
+ require 'roadworker/route53-exporter'
4
+ require 'roadworker/route53-ext'
5
+
6
+ require 'ostruct'
7
+
8
+ module Roadworker
9
+ class Route53Wrapper
10
+
11
+ RRSET_ATTRS = [
12
+ :set_identifier,
13
+ :weight,
14
+ :ttl,
15
+ :resource_records,
16
+ :dns_name,
17
+ :region
18
+ ]
19
+
20
+ def initialize(options)
21
+ @options = options
22
+ end
23
+
24
+ def export
25
+ Exporter.export(@options.route53)
26
+ end
27
+
28
+ def hosted_zones
29
+ HostedZoneCollectionWrapper.new(@options.route53.hosted_zones, @options)
30
+ end
31
+
32
+ class HostedZoneCollectionWrapper
33
+ include Roadworker::Log
34
+
35
+ def initialize(hosted_zones, options)
36
+ @hosted_zones = hosted_zones
37
+ @options = options
38
+ end
39
+
40
+ def each
41
+ Collection.batch(@hosted_zones) do |zone|
42
+ yield(HostedZoneWrapper.new(zone, @options))
43
+ end
44
+ end
45
+
46
+ def create(name, opts = {})
47
+ log(:info, 'Create HostedZone', :cyan, name)
48
+
49
+ if @options.dry_run
50
+ zone = OpenStruct.new({:name => name, :rrsets => []}.merge(opts))
51
+ else
52
+ zone = @hosted_zones.create(name, opts)
53
+ @options.updated = true
54
+ end
55
+
56
+ HostedZoneWrapper.new(zone, @options)
57
+ end
58
+ end # HostedZoneCollection
59
+
60
+ class HostedZoneWrapper
61
+ include Roadworker::Log
62
+
63
+ def initialize(hosted_zone, options)
64
+ @hosted_zone = hosted_zone
65
+ @options = options
66
+ end
67
+
68
+ def resource_record_sets
69
+ ResourceRecordSetCollectionWrapper.new(@hosted_zone.rrsets, @options)
70
+ end
71
+ alias rrsets resource_record_sets
72
+
73
+ def delete
74
+ if @options.force
75
+ log(:info, 'Delete HostedZone', :red, @hosted_zone.name)
76
+
77
+ unless @options.dry_run
78
+ @hosted_zone.delete
79
+ @options.updated = true
80
+ end
81
+ else
82
+ log(:info, 'Undefined HostedZone (pass `--force` if you want to remove)', :yellow, @hosted_zone.name)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def method_missing(method_name, *args)
89
+ @hosted_zone.send(method_name, *args)
90
+ end
91
+ end # HostedZoneWrapper
92
+
93
+ class ResourceRecordSetCollectionWrapper
94
+ include Roadworker::Log
95
+
96
+ def initialize(resource_record_sets, options)
97
+ @resource_record_sets = resource_record_sets
98
+ @options = options
99
+ end
100
+
101
+ def each
102
+ Collection.batch(@resource_record_sets) do |record|
103
+ yield(ResourceRecordSetWrapper.new(record, @options))
104
+ end
105
+ end
106
+
107
+ def create(name, type, expected_record)
108
+ log(:info, 'Create ResourceRecordSet', :cyan) do
109
+ log_id = [name, type].join(' ')
110
+ rrset_setid = expected_record.set_identifier
111
+ rrset_setid ? (log_id + " (#{rrset_setid})") : log_id
112
+ end
113
+
114
+ if @options.dry_run
115
+ record = expected_record
116
+ else
117
+ opts = {}
118
+
119
+ Route53Wrapper::RRSET_ATTRS.each do |attr|
120
+ value = expected_record.send(attr)
121
+ next unless value
122
+
123
+ case attr
124
+ when :dns_name
125
+ attr = :alias_target
126
+ value = @options.route53.dns_name_to_alias_target(value)
127
+ end
128
+
129
+ opts[attr] = value
130
+ end
131
+
132
+ record = @resource_record_sets.create(name, type, opts)
133
+ @options.updated = true
134
+ end
135
+
136
+ ResourceRecordSetWrapper.new(record, @options)
137
+ end
138
+ end # ResourceRecordSetCollectionWrapper
139
+
140
+ class ResourceRecordSetWrapper
141
+ include Roadworker::Log
142
+
143
+ def initialize(resource_record_set, options)
144
+ @resource_record_set = resource_record_set
145
+ @options = options
146
+ end
147
+
148
+ def eql?(expected_record)
149
+ Route53Wrapper::RRSET_ATTRS.all? do |attr|
150
+ expected = expected_record.send(attr)
151
+ expected = nil if expected.kind_of?(Array) && expected.empty?
152
+ actual = self.send(attr)
153
+ actual = nil if actual.kind_of?(Array) && actual.empty?
154
+
155
+ if !expected and !actual
156
+ true
157
+ elsif expected and actual
158
+ case attr
159
+ when :resource_records
160
+ expected = expected.sort_by {|i| i[:value] }
161
+ actual = actual.sort_by {|i| i[:value] }
162
+ end
163
+
164
+ (expected == actual)
165
+ else
166
+ false
167
+ end
168
+ end
169
+ end
170
+
171
+ def update(expected_record)
172
+ log(:info, 'Update ResourceRecordSet', :green) do
173
+ log_id = [@resource_record_set.name, @resource_record_set.type].join(' ')
174
+ rrset_setid = @resource_record_set.set_identifier
175
+ rrset_setid ? (log_id + " (#{rrset_setid})") : log_id
176
+ end
177
+
178
+ Route53Wrapper::RRSET_ATTRS.each do |attr|
179
+ expected = expected_record.send(attr)
180
+ expected = nil if expected.kind_of?(Array) && expected.empty?
181
+ actual = self.send(attr)
182
+ actual = nil if actual.kind_of?(Array) && actual.empty?
183
+
184
+ if (expected and !actual) or (!expected and actual)
185
+ log(:info, " set #{attr}=#{expected.inspect}" , :green) do
186
+ log_id = [@resource_record_set.name, @resource_record_set.type].join(' ')
187
+ rrset_setid = @resource_record_set.set_identifier
188
+ rrset_setid ? (log_id + " (#{rrset_setid})") : log_id
189
+ end
190
+
191
+ self.send(:"#{attr}=", expected) unless @options.dry_run
192
+ elsif expected and actual
193
+ case attr
194
+ when :resource_records
195
+ expected = expected.sort_by {|i| i[:value] }
196
+ actual = actual.sort_by {|i| i[:value] }
197
+ end
198
+
199
+ if expected != actual
200
+ log(:info, " set #{attr}=#{expected.inspect}" , :green) do
201
+ log_id = [@resource_record_set.name, @resource_record_set.type].join(' ')
202
+ rrset_setid = @resource_record_set.set_identifier
203
+ rrset_setid ? (log_id + " (#{rrset_setid})") : log_id
204
+ end
205
+
206
+ self.send(:"#{attr}=", expected) unless @options.dry_run
207
+ end
208
+ end
209
+ end
210
+
211
+ unless @options.dry_run
212
+ @resource_record_set.update
213
+ @options.updated = true
214
+ end
215
+ end
216
+
217
+ def delete(opts = {})
218
+ return if type =~ /\A(SOA|NS)\Z/i
219
+ if not opts[:cascaded] or @options.force
220
+ log(:info, 'Delete ResourceRecordSet', :red) do
221
+ log_id = [@resource_record_set.name, @resource_record_set.type].join(' ')
222
+ rrset_setid = @resource_record_set.set_identifier
223
+ rrset_setid ? (log_id + " (#{rrset_setid})") : log_id
224
+ end
225
+ end
226
+
227
+ unless @options.dry_run
228
+ @resource_record_set.delete
229
+ @options.updated = true
230
+ end
231
+ end
232
+
233
+ def dns_name=(name)
234
+ if name
235
+ @resource_record_set.alias_target = @options.route53.dns_name_to_alias_target(name)
236
+ else
237
+ @resource_record_set.alias_target = nil
238
+ end
239
+ end
240
+
241
+ def dns_name
242
+ (@resource_record_set.alias_target || {})[:dns_name]
243
+ end
244
+
245
+ private
246
+
247
+ def method_missing(method_name, *args)
248
+ @resource_record_set.send(method_name, *args)
249
+ end
250
+ end # ResourceRecordSetWrapper
251
+
252
+ end # Route53Wrapper
253
+ end # Roadworker
@@ -0,0 +1,28 @@
1
+ require 'term/ansicolor'
2
+
3
+ class String
4
+ @@colorize = false
5
+
6
+ class << self
7
+ def colorize=(value)
8
+ @@colorize = value
9
+ end
10
+
11
+ def colorize
12
+ @@colorize
13
+ end
14
+ end # of class method
15
+
16
+ Term::ANSIColor::Attribute.named_attributes.map do |attr|
17
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
18
+ def #{attr.name}
19
+ if @@colorize
20
+ Term::ANSIColor.send(#{attr.name.inspect}, self)
21
+ else
22
+ self
23
+ end
24
+ end
25
+ EOS
26
+ end
27
+
28
+ end
@@ -0,0 +1,5 @@
1
+ module Roadworker
2
+ VERSION = "0.0.1"
3
+ end
4
+
5
+ Version = Roadworker::VERSION
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roadworker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - winebarrel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: term-ansicolor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Roadworker is a tool to manage Route53. It defines the state of Route53
79
+ using DSL, and updates Route53 according to DSL.
80
+ email: sgwr_dts@yahoo.co.jp
81
+ executables:
82
+ - roadwork
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - README.md
87
+ - bin/roadwork
88
+ - lib/roadworker/client.rb
89
+ - lib/roadworker/collection.rb
90
+ - lib/roadworker/dsl-converter.rb
91
+ - lib/roadworker/dsl.rb
92
+ - lib/roadworker/log.rb
93
+ - lib/roadworker/route53-exporter.rb
94
+ - lib/roadworker/route53-ext.rb
95
+ - lib/roadworker/route53-wrapper.rb
96
+ - lib/roadworker/string-ext.rb
97
+ - lib/roadworker/version.rb
98
+ - lib/roadworker.rb
99
+ homepage: https://bitbucket.org/winebarrel/roadworker
100
+ licenses:
101
+ - MIT
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.23
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Roadworker is a tool to manage Route53.
124
+ test_files: []