axtro-rubber 1.0.2.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/CHANGELOG +124 -0
  2. data/VERSION +1 -1
  3. data/generators/vulcanize/templates/apache/config/rubber/role/web_tools/tools-apache-vhost.conf +4 -0
  4. data/generators/vulcanize/templates/apache/config/rubber/role/web_tools/tools-index.html +6 -2
  5. data/generators/vulcanize/templates/apache/config/rubber/rubber-apache.yml +0 -1
  6. data/generators/vulcanize/templates/base/config/rubber/common/crontab +2 -1
  7. data/generators/vulcanize/templates/base/config/rubber/common/monit-postfix.conf +8 -0
  8. data/generators/vulcanize/templates/base/config/rubber/common/rubber.profile +14 -0
  9. data/generators/vulcanize/templates/base/config/rubber/deploy-setup.rb +21 -41
  10. data/generators/vulcanize/templates/base/config/rubber/rubber-dns.yml +79 -0
  11. data/generators/vulcanize/templates/base/config/rubber/rubber.yml +29 -30
  12. data/generators/vulcanize/templates/complete_passenger_mysql/config/rubber/role/haproxy/haproxy-passenger.conf +2 -0
  13. data/generators/vulcanize/templates/jetty/config/rubber/deploy-jetty.rb +59 -0
  14. data/generators/vulcanize/templates/jetty/config/rubber/role/jetty/jetty.sh +589 -0
  15. data/generators/vulcanize/templates/jetty/config/rubber/role/jetty/jetty.xml +199 -0
  16. data/generators/vulcanize/templates/jetty/config/rubber/role/jetty/monit-jetty.conf +9 -0
  17. data/generators/vulcanize/templates/jetty/config/rubber/rubber-jetty.yml +10 -0
  18. data/generators/vulcanize/templates/jetty/templates.yml +1 -0
  19. data/generators/vulcanize/templates/mongrel/config/rubber/role/mongrel/monit-mongrel.conf +2 -2
  20. data/generators/vulcanize/templates/munin/config/rubber/common/munin-plugins.conf +9 -0
  21. data/generators/vulcanize/templates/munin/config/rubber/rubber-munin.yml +1 -1
  22. data/generators/vulcanize/templates/munin/script/munin/example_mysql_query.rb +3 -3
  23. data/generators/vulcanize/templates/mysql/config/rubber/rubber-mysql.yml +1 -0
  24. data/generators/vulcanize/templates/passenger/config/rubber/deploy-passenger.rb +1 -1
  25. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/munin-passenger-memory.conf +34 -0
  26. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/munin-passenger-sudoers.conf +2 -1
  27. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/munin-passenger.conf +1 -1
  28. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/passenger.conf +3 -3
  29. data/generators/vulcanize/templates/passenger/config/rubber/rubber-passenger.yml +5 -2
  30. data/generators/vulcanize/templates/passenger/templates.yml +3 -1
  31. data/generators/vulcanize/templates/redis/config/rubber/deploy-redis.rb +36 -0
  32. data/generators/vulcanize/templates/redis/config/rubber/role/redis/crontab +8 -0
  33. data/generators/vulcanize/templates/redis/config/rubber/role/redis/monit-redis.conf +9 -0
  34. data/generators/vulcanize/templates/redis/config/rubber/role/redis/redis.conf +141 -0
  35. data/generators/vulcanize/templates/redis/config/rubber/rubber-redis.yml +4 -0
  36. data/generators/vulcanize/templates/redis/templates.yml +1 -0
  37. data/generators/vulcanize/templates/resque/config/rubber/deploy-resque-worker-default.rb +38 -0
  38. data/generators/vulcanize/templates/resque/config/rubber/deploy-resque.rb +39 -0
  39. data/generators/vulcanize/templates/resque/config/rubber/role/resque_worker_default/monit-resque_worker_default.conf +19 -0
  40. data/generators/vulcanize/templates/resque/config/rubber/rubber-resque.yml +10 -0
  41. data/generators/vulcanize/templates/resque/templates.yml +3 -0
  42. data/generators/vulcanize/templates/sphinx/config/rubber/role/sphinx/crontab +2 -2
  43. data/generators/vulcanize/templates/sphinx/config/rubber/role/sphinx/monit-sphinx.conf +2 -2
  44. data/lib/rubber/cloud/aws.rb +32 -2
  45. data/lib/rubber/dns/base.rb +41 -26
  46. data/lib/rubber/dns/dyndns.rb +25 -10
  47. data/lib/rubber/dns/nettica.rb +75 -31
  48. data/lib/rubber/dns/zerigo.rb +96 -53
  49. data/lib/rubber/environment.rb +11 -3
  50. data/lib/rubber/instance.rb +11 -0
  51. data/lib/rubber/recipes/rubber/instances.rb +59 -6
  52. data/lib/rubber/recipes/rubber/security_groups.rb +1 -1
  53. data/lib/rubber/recipes/rubber/setup.rb +155 -52
  54. data/lib/rubber/recipes/rubber/spot_requests.rb +23 -0
  55. data/lib/rubber/recipes/rubber/static_ips.rb +83 -0
  56. data/lib/rubber/recipes/rubber/utils.rb +2 -2
  57. data/lib/rubber/recipes/rubber.rb +10 -3
  58. data/lib/rubber/tasks/rubber.rb +64 -6
  59. data/lib/rubber.rb +1 -1
  60. data/rails/init.rb +9 -0
  61. data/test/environment_test.rb +18 -0
  62. data/test/generator_test.rb +3 -0
  63. data/test/instance_test.rb +3 -0
  64. data/test/test_helper.rb +4 -0
  65. data/test/util_test.rb +3 -0
  66. metadata +28 -4
  67. data/generators/vulcanize/templates/base/config/rubber/common/profile.rc +0 -9
@@ -9,6 +9,11 @@ module Rubber
9
9
  include HTTParty
10
10
  format :xml
11
11
 
12
+ @@zones = {}
13
+ def self.get_zone(domain, provider_env)
14
+ @@zones[domain] ||= Zone.new(provider_env.customer_id, provider_env.email, provider_env.token, domain)
15
+ end
16
+
12
17
  def initialize(customer_id, email, token, domain)
13
18
  self.class.basic_auth email, token
14
19
  self.class.base_uri "https://ns.zerigo.com/accounts/#{customer_id}"
@@ -26,31 +31,49 @@ module Rubber
26
31
  return response
27
32
  end
28
33
 
29
- def hosts
30
- hosts = check_status self.class.get("/zones/#{@zone['id']}/hosts.xml")
31
- return hosts['hosts']
34
+ def create_host(opts)
35
+ host = opts_to_host(opts, new_host())
36
+ check_status self.class.post("/zones/#{@zone['id']}/hosts.xml", :body => {:host => host})
32
37
  end
33
38
 
34
- def host(hostname)
35
- hosts = check_status self.class.get("/zones/#{@zone['id']}/hosts.xml?fqdn=#{hostname}.#{@domain}")
36
- return (hosts['hosts'] || []).first
37
- end
39
+ def find_host_records(opts={})
40
+ result = []
41
+ hn = opts[:host]
42
+ ht = opts[:type]
43
+ hd = opts[:data]
44
+ has_host = hn && hn != '*'
38
45
 
39
- def new_host
40
- check_status(self.class.get("/zones/#{@zone['id']}/hosts/new.xml"))['host']
41
- end
46
+ url = "/zones/#{@zone['id']}/hosts.xml"
47
+ if has_host
48
+ url << "?fqdn="
49
+ url << "#{hn}." if hn.strip.size > 0
50
+ url << "#{@domain}"
51
+ end
52
+ hosts = self.class.get(url)
42
53
 
43
- def create_host(host)
44
- check_status self.class.post("/zones/#{@zone['id']}/hosts.xml", :body => {:host => host})
54
+ # returns 404 on not found, so don't check status
55
+ hosts = check_status hosts unless has_host
56
+
57
+ hosts['hosts'].each do |h|
58
+ keep = true
59
+ if ht && h['host_type'] != ht && ht != '*'
60
+ keep = false
61
+ end
62
+ if hd && h['data'] != hd
63
+ keep = false
64
+ end
65
+ result << host_to_opts(h) if keep
66
+ end if hosts['hosts']
67
+
68
+ return result
45
69
  end
46
70
 
47
- def update_host(host)
48
- host_id = host['id']
71
+ def update_host(host_id, opts)
72
+ host = opts_to_host(opts, new_host())
49
73
  check_status self.class.put("/zones/#{@zone['id']}/hosts/#{host_id}.xml", :body => {:host => host})
50
74
  end
51
75
 
52
- def delete_host(hostname)
53
- host_id = host(hostname)['id']
76
+ def delete_host(host_id)
54
77
  check_status self.class.delete("/zones/#{@zone['id']}/hosts/#{host_id}.xml")
55
78
  end
56
79
 
@@ -62,67 +85,87 @@ module Rubber
62
85
  zones = check_status self.class.get('/zones.xml')
63
86
  @zone = zones["zones"].find {|z| z["domain"] == @domain }
64
87
  end
88
+ if ! @zone
89
+ zone = new_zone()
90
+ zone['domain'] = @domain
91
+ zones = check_status self.class.post('/zones.xml', :body => {:zone => zone})
92
+ @zone = zones['zone']
93
+ end
65
94
  end
66
95
 
67
- def data
96
+ def zone_record
68
97
  return @zone
69
98
  end
70
99
 
71
- protected
100
+ private
72
101
 
73
- def zones()
74
- check_status self.class.get('/zones.xml')
102
+ def new_host
103
+ check_status(self.class.get("/zones/#{@zone['id']}/hosts/new.xml"))['host']
104
+ end
105
+
106
+ def new_zone
107
+ check_status(self.class.get("/zones/new.xml"))['zone']
108
+ end
109
+
110
+ def opts_to_host(opts, host={})
111
+ host['hostname'] = opts[:host]
112
+ host['host_type'] = opts[:type]
113
+ host['data'] = opts[:data] if opts[:data]
114
+ host['ttl'] = opts[:ttl] if opts[:ttl]
115
+ host['priority'] = opts[:priority] if opts[:priority]
116
+ return host
75
117
  end
76
118
 
77
- def zone(domain_name)
78
- zone = zones
79
- return zone
119
+ def host_to_opts(host)
120
+ opts = {}
121
+ opts[:id] = host['id']
122
+ opts[:domain] = @domain
123
+ opts[:host] = host['hostname'] || ''
124
+ opts[:type] = host['host_type']
125
+ opts[:data] = host['data'] if host['data']
126
+ opts[:ttl] = host['ttl'] if host['ttl']
127
+ opts[:priority] = host['priority'] if host['priority']
128
+ return opts
80
129
  end
81
-
82
130
  end
83
131
 
84
132
  class Zerigo < Base
85
133
 
86
134
  def initialize(env)
87
- super(env)
88
- @zerigo_env = env.dns_providers.zerigo
89
- @ttl = (@zerigo_env.ttl || 300).to_i
90
- @record_type = @zerigo_env.record_type || "A"
91
- @zone = Zone.new(@zerigo_env.customer_id, @zerigo_env.email, @zerigo_env.token, env.domain)
135
+ super(env, "zerigo")
92
136
  end
93
137
 
94
- def nameserver
95
- "a.ns.zerigo.net"
96
- end
138
+ def find_host_records(opts = {})
139
+ opts = setup_opts(opts, [:host, :domain])
140
+ zone = Zone.get_zone(opts[:domain], provider_env)
97
141
 
98
- def host_exists?(host)
99
- @zone.host(host)
142
+ zone.find_host_records(opts)
100
143
  end
101
144
 
102
- def create_host_record(hostname, ip)
103
- host = @zone.new_host()
104
- host['host-type'] = @record_type
105
- host['ttl'] = @ttl
106
- host['hostname'] = hostname
107
- host['data'] = ip
108
- @zone.create_host(host)
109
- end
145
+ def create_host_record(opts = {})
146
+ opts = setup_opts(opts, [:host, :data, :domain, :type, :ttl])
147
+ zone = Zone.get_zone(opts[:domain], provider_env)
110
148
 
111
- def destroy_host_record(host)
112
- @zone.delete_host(host)
149
+ zone.create_host(opts)
113
150
  end
114
151
 
115
- def update_host_record(host, ip)
116
- old = @zone.host(host)
117
- old['data'] = ip
118
- @zone.update_host(old)
152
+ def destroy_host_record(opts = {})
153
+ opts = setup_opts(opts, [:host, :domain])
154
+ zone = Zone.get_zone(opts[:domain], provider_env)
155
+
156
+ find_host_records(opts).each do |h|
157
+ zone.delete_host(h[:id])
158
+ end
119
159
  end
120
160
 
121
- # update the top level domain record which has an empty hostName
122
- def update_domain_record(ip)
123
- old = @zone.hosts.find {|h| h['hostname'].nil? }
124
- old['data'] = ip
125
- @zone.update_host(old)
161
+ def update_host_record(old_opts={}, new_opts={})
162
+ old_opts = setup_opts(old_opts, [:host, :domain])
163
+ new_opts = setup_opts(new_opts.merge(:no_defaults =>true), [])
164
+ zone = Zone.get_zone(old_opts[:domain], provider_env)
165
+
166
+ find_host_records(old_opts).each do |h|
167
+ zone.update_host(h[:id], h.merge(new_opts))
168
+ end
126
169
  end
127
170
 
128
171
  end
@@ -32,6 +32,8 @@ module Rubber
32
32
  roles_dir = File.join(@config_root, "role")
33
33
  roles = Dir.entries(roles_dir)
34
34
  roles.delete_if {|d| d =~ /(^\..*)/}
35
+ roles += @items['roles'].keys
36
+ return roles.compact.uniq
35
37
  end
36
38
 
37
39
  def current_host
@@ -41,7 +43,7 @@ module Rubber
41
43
  def current_full_host
42
44
  Socket::gethostname
43
45
  end
44
-
46
+
45
47
  def bind(roles = nil, host = nil)
46
48
  BoundEnv.new(@items, roles, host)
47
49
  end
@@ -77,6 +79,10 @@ module Rubber
77
79
  replace(receiver)
78
80
  end
79
81
 
82
+ def rubber_instances
83
+ @rubber_instances ||= Rubber::Configuration::rubber_instances
84
+ end
85
+
80
86
  def [](name)
81
87
  value = super(name)
82
88
  value = global[name] if global && !value
@@ -122,14 +128,16 @@ module Rubber
122
128
  class BoundEnv < HashValueProxy
123
129
  attr_reader :roles
124
130
  attr_reader :host
125
- attr_reader :full_host
126
131
 
127
132
  def initialize(global, roles, host)
128
133
  @roles = roles
129
134
  @host = host
130
135
  bound_global = bind_config(global)
131
136
  super(nil, bound_global)
132
- @full_host = "#{host}.#{domain}" rescue nil
137
+ end
138
+
139
+ def full_host
140
+ @full_host ||= "#{host}.#{domain}" rescue nil
133
141
  end
134
142
 
135
143
  # Forces role/host overrides into config
@@ -81,6 +81,8 @@ module Rubber
81
81
  attr_accessor :external_host, :external_ip
82
82
  attr_accessor :internal_host, :internal_ip
83
83
  attr_accessor :static_ip, :volumes, :partitions
84
+ attr_accessor :spot_instance_request_id
85
+ attr_accessor :platform
84
86
 
85
87
  def initialize(name, domain, roles, instance_id, security_group_list=[])
86
88
  @name = name
@@ -97,6 +99,15 @@ module Rubber
97
99
  def role_names()
98
100
  roles.collect {|r| r.name}
99
101
  end
102
+
103
+ def platform
104
+ # Deal with old instance configurations that don't have a platform value persisted.
105
+ @platform || 'linux'
106
+ end
107
+
108
+ def windows?
109
+ platform == 'windows'
110
+ end
100
111
  end
101
112
 
102
113
  # The configuration for a single role contained in the list
@@ -5,7 +5,13 @@ namespace :rubber do
5
5
  DESC
6
6
  required_task :create do
7
7
  instance_alias = get_env('ALIAS', "Instance alias (e.g. web01)", true)
8
- r = get_env('ROLES', "Instance roles (e.g. web,app,db:primary=true)", true)
8
+
9
+ env = rubber_cfg.environment.bind(nil, instance_alias)
10
+ default_roles = env.instance_roles
11
+ r = get_env("ROLES", "Instance roles (e.g. web,app,db:primary=true)", true, default_roles)
12
+
13
+ create_spot_instance = ENV.delete("SPOT_INSTANCE") || env.cloud_providers[env.cloud_provider].spot_instance
14
+
9
15
  if r == '*'
10
16
  instance_roles = rubber_cfg.environment.known_roles
11
17
  instance_roles = instance_roles.collect {|role| role == "db" ? "db:primary=true" : role }
@@ -29,7 +35,7 @@ namespace :rubber do
29
35
  # Add in roles that the given set of roles depends on
30
36
  ir = Rubber::Configuration::RoleItem.expand_role_dependencies(ir, get_role_dependencies)
31
37
 
32
- create_instance(instance_alias, ir)
38
+ create_instance(instance_alias, ir, create_spot_instance)
33
39
  end
34
40
 
35
41
  desc <<-DESC
@@ -83,6 +89,10 @@ namespace :rubber do
83
89
 
84
90
  instance.roles = (instance.roles + ir).uniq
85
91
  rubber_instances.save()
92
+ logger.info "Roles for #{instance_alias} are now:"
93
+ logger.info instance.role_names.sort.join("\n")
94
+ logger.info ''
95
+ logger.info "Run 'cap rubber:bootstrap' if done adding roles"
86
96
  end
87
97
 
88
98
  desc <<-DESC
@@ -105,6 +115,8 @@ namespace :rubber do
105
115
 
106
116
  instance.roles = (instance.roles - ir).uniq
107
117
  rubber_instances.save()
118
+ logger.info "Roles for #{instance_alias} are now:"
119
+ logger.info instance.role_names.sort.join("\n")
108
120
  end
109
121
 
110
122
  desc <<-DESC
@@ -144,7 +156,7 @@ namespace :rubber do
144
156
 
145
157
  # Creates a new ec2 instance with the given alias and roles
146
158
  # Configures aliases (/etc/hosts) on local and remote machines
147
- def create_instance(instance_alias, instance_roles)
159
+ def create_instance(instance_alias, instance_roles, create_spot_instance=false)
148
160
  fatal "Instance already exists: #{instance_alias}" if rubber_instances[instance_alias]
149
161
 
150
162
  role_names = instance_roles.collect{|x| x.name}
@@ -157,12 +169,45 @@ namespace :rubber do
157
169
  ami = env.cloud_providers[env.cloud_provider].image_id
158
170
  ami_type = env.cloud_providers[env.cloud_provider].image_type
159
171
  availability_zone = env.availability_zone
160
- logger.info "Creating instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}"
161
- instance_id = cloud.create_instance(ami, ami_type, security_groups, availability_zone)
172
+
173
+ if create_spot_instance
174
+ spot_price = env.cloud_providers[env.cloud_provider].spot_price.to_s
175
+
176
+ logger.info "Creating spot instance request for instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}"
177
+ request_id = cloud.create_spot_instance_request(spot_price, ami, ami_type, security_groups, availability_zone)
178
+
179
+ print "Waiting for spot instance request to be fulfilled"
180
+ max_wait_time = env.cloud_providers[env.cloud_provider].spot_instance_request_timeout || (1.0 / 0) # Use the specified timeout value or default to infinite.
181
+ instance_id = nil
182
+ while instance_id.nil? do
183
+ print "."
184
+ sleep 2
185
+ max_wait_time -= 2
186
+
187
+ request = cloud.describe_spot_instance_requests(request_id).first
188
+ instance_id = request[:instance_id]
189
+
190
+ if max_wait_time < 0 && instance_id.nil?
191
+ cloud.destroy_spot_instance_request(request[:id])
192
+
193
+ print "\n"
194
+ print "Failed to fulfill spot instance in the time specified. Falling back to on-demand instance creation."
195
+ break
196
+ end
197
+ end
198
+
199
+ print "\n"
200
+ end
201
+
202
+ if !create_spot_instance || (create_spot_instance && max_wait_time < 0)
203
+ logger.info "Creating instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}"
204
+ instance_id = cloud.create_instance(ami, ami_type, security_groups, availability_zone)
205
+ end
162
206
 
163
207
  logger.info "Instance #{instance_id} created"
164
208
 
165
209
  instance_item = Rubber::Configuration::InstanceItem.new(instance_alias, env.domain, instance_roles, instance_id, security_groups)
210
+ instance_item.spot_instance_request_id = request_id if create_spot_instance
166
211
  rubber_instances.add(instance_item)
167
212
  rubber_instances.save()
168
213
 
@@ -180,6 +225,7 @@ namespace :rubber do
180
225
  instance_item.external_ip = instance[:external_ip]
181
226
  instance_item.internal_host = instance[:internal_host]
182
227
  instance_item.zone = instance[:zone]
228
+ instance_item.platform = instance[:platform]
183
229
  rubber_instances.save()
184
230
 
185
231
  # setup amazon elastic ips if configured to do so
@@ -196,7 +242,14 @@ namespace :rubber do
196
242
  # so that we can update all aliases
197
243
 
198
244
  task :_get_ip, :hosts => instance_item.external_ip do
199
- instance_item.internal_ip = capture(print_ip_command).strip
245
+ # There's no good way to get the internal IP for a Windows host, so just set it to the external
246
+ # and let the router handle mapping to the internal network.
247
+ if instance_item.windows?
248
+ instance_item.internal_ip = instance_item.external_ip
249
+ else
250
+ instance_item.internal_ip = capture(print_ip_command).strip
251
+ end
252
+
200
253
  rubber_instances.save()
201
254
  end
202
255
 
@@ -36,7 +36,7 @@ namespace :rubber do
36
36
  security_groups << host
37
37
  security_groups += roles
38
38
  end
39
- security_groups = security_groups.uniq.compact
39
+ security_groups = security_groups.uniq.compact.reject {|x| x.empty? }
40
40
  security_groups = security_groups.collect {|x| isolate_group_name(x) } if env.isolate_security_groups
41
41
  return security_groups
42
42
  end
@@ -1,5 +1,8 @@
1
1
  namespace :rubber do
2
2
 
3
+ # Disable connecting to any Windows instance.
4
+ set :default_run_options, :except => { :platform => 'windows' }
5
+
3
6
  desc <<-DESC
4
7
  Bootstraps instances by setting timezone, installing packages and gems
5
8
  DESC
@@ -61,6 +64,83 @@ namespace :rubber do
61
64
  end
62
65
  end
63
66
 
67
+ desc <<-DESC
68
+ Sets up the additional dns records supplied in the dns_records config in rubber.yml
69
+ DESC
70
+ required_task :setup_dns_records do
71
+ records = rubber_env.dns_records
72
+ if records && rubber_env.dns_provider
73
+ provider = Rubber::Dns::get_provider(rubber_env.dns_provider, rubber_env)
74
+
75
+ # collect the round robin records (those with the same host/domain/type)
76
+ rr_records = []
77
+ records.each_with_index do |record, i|
78
+ m = records.find_all {|r| record['host'] == r['host'] && record['domain'] == r['domain'] && record['type'] == r['type']}
79
+ m = m.sort {|a,b| a.object_id <=> b.object_id}
80
+ rr_records << m if m.size > 1 && ! rr_records.include?(m)
81
+ end
82
+
83
+ # simple records are those that aren't round robin ones
84
+ simple_records = records - rr_records.flatten
85
+
86
+ # for each simple record, create or update as necessary
87
+ simple_records.each do |record|
88
+ matching = provider.find_host_records(:host => record['host'], :domain =>record['domain'], :type => record['type'])
89
+ if matching.size > 1
90
+ msg = "Multiple records in dns provider, but not in rubber.yml\n"
91
+ msg << "Round robin records need to be in both, or neither.\n"
92
+ msg << "Please fix manually:\n"
93
+ msg << matching.pretty_inspect
94
+ fatal(msg)
95
+ end
96
+
97
+ record = provider.setup_opts(record)
98
+ if matching.size == 1
99
+ match = matching.first
100
+ if provider.host_records_equal?(record, match)
101
+ logger.info "Simple dns record already up to date: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
102
+ else
103
+ logger.info "Updating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
104
+ provider.update_host_record(match, record)
105
+ end
106
+ else
107
+ logger.info "Creating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}"
108
+ provider.create_host_record(record)
109
+ end
110
+ end
111
+
112
+ # group round robin records
113
+ rr_records.each do |rr_group|
114
+ host = rr_group.first['host']
115
+ domain = rr_group.first['domain']
116
+ type = rr_group.first['type']
117
+ matching = provider.find_host_records(:host => host, :domain => domain, :type => type)
118
+
119
+ # remove from consideration the local records that are the same as remote ones
120
+ matching.clone.each do |r|
121
+ rr_group.delete_if {|rg| provider.host_records_equal?(r, rg) }
122
+ matching.delete_if {|rg| provider.host_records_equal?(r, rg) }
123
+ end
124
+ if rr_group.size == 0 && matching.size == 0
125
+ logger.info "Round robin dns records already up to date: #{host}.#{domain}:#{type}"
126
+ end
127
+
128
+ # create the local records that don't exist remotely
129
+ rr_group.each do |r|
130
+ r = provider.setup_opts(r)
131
+ logger.info "Creating round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}"
132
+ provider.create_host_record(r)
133
+ end
134
+
135
+ # remove the remote records that don't exist locally
136
+ matching.each do |r|
137
+ logger.info "Removing round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}"
138
+ provider.destroy_host_record(r)
139
+ end
140
+ end
141
+ end
142
+ end
143
+
64
144
  desc <<-DESC
65
145
  Sets up aliases for instance hostnames based on contents of instance.yml.
66
146
  Generates /etc/hosts for remote machines and sets hostname on remote instances
@@ -144,11 +224,33 @@ namespace :rubber do
144
224
  desc <<-DESC
145
225
  Install ruby gems defined in the rails environment.rb
146
226
  DESC
147
- after "deploy:symlink", "rubber:install_rails_gems" if Rubber::Util.is_rails?
227
+ after "rubber:config", "rubber:install_rails_gems" if Rubber::Util.is_rails?
148
228
  task :install_rails_gems do
149
229
  sudo "sh -c 'cd #{current_path} && RAILS_ENV=#{RUBBER_ENV} rake gems:install'"
150
230
  end
151
231
 
232
+ desc <<-DESC
233
+ Convenience task for installing your defined set of ruby gems locally.
234
+ DESC
235
+ required_task :install_local_gems do
236
+ fatal("install_local_gems can only be run in development") if RUBBER_ENV != 'development'
237
+ env = rubber_cfg.environment.bind(rubber_cfg.environment.known_roles)
238
+ gems = env['gems']
239
+ expanded_gem_list = []
240
+ gems.each do |gem_spec|
241
+ if gem_spec.is_a?(Array)
242
+ expanded_gem_list << "#{gem_spec[0]}:#{gem_spec[1]}"
243
+ else
244
+ expanded_gem_list << gem_spec
245
+ end
246
+ end
247
+ expanded_gem_list = expanded_gem_list.join(' ')
248
+
249
+ logger.info "Installing gems:#{expanded_gem_list}"
250
+ open("/tmp/gem_helper", "w") {|f| f.write(gem_helper_script)}
251
+ system "sh /tmp/gem_helper install #{expanded_gem_list}"
252
+ end
253
+
152
254
  desc <<-DESC
153
255
  Setup ruby gems sources. Set 'gemsources' in rubber.yml to \
154
256
  be an array of URI strings.
@@ -280,11 +382,61 @@ namespace :rubber do
280
382
  end
281
383
  end
282
384
 
385
+ # Rubygems always installs even if the gem is already installed
386
+ # When providing versions, rubygems fails unless versions are provided for all gems
387
+ # This helper script works around these issues by installing gems only if they
388
+ # aren't already installed, and separates versioned/unversioned into two separate
389
+ # calls to rubygems
390
+ #
391
+ set :gem_helper_script, <<-'ENDSCRIPT'
392
+ ruby - $@ <<-'EOF'
393
+
394
+ gem_cmd = ARGV[0]
395
+ gems = ARGV[1..-1]
396
+ cmd = "gem #{gem_cmd} --no-rdoc --no-ri"
397
+
398
+ to_install = {}
399
+ to_install_ver = {}
400
+ # gem list passed in, possibly with versions, as "gem1 gem2:1.2 gem3"
401
+ gems.each do |gem_spec|
402
+ parts = gem_spec.split(':')
403
+ if parts[1]
404
+ to_install_ver[parts[0]] = parts[1]
405
+ else
406
+ to_install[parts[0]] = true
407
+ end
408
+ end
409
+
410
+ installed = {}
411
+ `gem list --local`.each do |line|
412
+ parts = line.scan(/(.*) \((.*)\)/).first
413
+ next unless parts && parts.size == 2
414
+ installed[parts[0]] = parts[1].split(",")
415
+ end
416
+
417
+ to_install.delete_if {|g, v| installed.has_key?(g) } if gem_cmd == 'install'
418
+ to_install_ver.delete_if {|g, v| installed.has_key?(g) && installed[g].include?(v) }
419
+
420
+ # rubygems can only do asingle versioned gem at a time so we need
421
+ # to do the two groups separately
422
+ # install versioned ones first so unversioned don't pull in a newer version
423
+ to_install_ver.each do |g, v|
424
+ system "#{cmd} #{g} -v #{v}"
425
+ fail "Unable to install versioned gem #{g}:#{v}" if $?.exitstatus > 0
426
+ end
427
+ if to_install.size > 0
428
+ gem_list = to_install.keys.join(' ')
429
+ system "#{cmd} #{gem_list}"
430
+ fail "Unable to install gems" if $?.exitstatus > 0
431
+ end
432
+
433
+ 'EOF'
434
+ ENDSCRIPT
435
+
283
436
  # Helper for installing gems,allows one to respond to prompts
284
437
  def gem_helper(update=false)
285
438
  cmd = update ? "update" : "install"
286
439
 
287
-
288
440
  opts = get_host_options('gems') do |gem_list|
289
441
  expanded_gem_list = []
290
442
  gem_list.each do |gem_spec|
@@ -298,56 +450,7 @@ namespace :rubber do
298
450
  end
299
451
 
300
452
  if opts.size > 0
301
- # Rubygems always installs even if the gem is already installed
302
- # When providing versions, rubygems fails unless versions are provided for all gems
303
- # This helper script works around these issues by installing gems only if they
304
- # aren't already installed, and separates versioned/unversioned into two separate
305
- # calls to rubygems
306
- script = prepare_script 'gem_helper', <<-'ENDSCRIPT'
307
- ruby - $@ <<-'EOF'
308
-
309
- gem_cmd = ARGV[0]
310
- gems = ARGV[1..-1]
311
- cmd = "gem #{gem_cmd} --no-rdoc --no-ri"
312
-
313
- to_install = {}
314
- to_install_ver = {}
315
- # gem list passed in, possibly with versions, as "gem1 gem2:1.2 gem3"
316
- gems.each do |gem_spec|
317
- parts = gem_spec.split(':')
318
- if parts[1]
319
- to_install_ver[parts[0]] = parts[1]
320
- else
321
- to_install[parts[0]] = true
322
- end
323
- end
324
-
325
- installed = {}
326
- `gem list --local`.each do |line|
327
- parts = line.scan(/(.*) \((.*)\)/).first
328
- next unless parts && parts.size == 2
329
- installed[parts[0]] = parts[1].split(",")
330
- end
331
-
332
- to_install.delete_if {|g, v| installed.has_key?(g) } if gem_cmd == 'install'
333
- to_install_ver.delete_if {|g, v| installed.has_key?(g) && installed[g].include?(v) }
334
-
335
- # rubygems can only do asingle versioned gem at a time so we need
336
- # to do the two groups separately
337
- # install versioned ones first so unversioned don't pull in a newer version
338
- to_install_ver.each do |g, v|
339
- system "#{cmd} #{g} -v #{v}"
340
- fail "Unable to install versioned gem #{g}:#{v}" if $?.exitstatus > 0
341
- end
342
- if to_install.size > 0
343
- gem_list = to_install.keys.join(' ')
344
- system "#{cmd} #{gem_list}"
345
- fail "Unable to install gems" if $?.exitstatus > 0
346
- end
347
-
348
- 'EOF'
349
- ENDSCRIPT
350
-
453
+ script = prepare_script('gem_helper', gem_helper_script)
351
454
  sudo "sh #{script} #{cmd} $CAPISTRANO:VAR$", opts do |ch, str, data|
352
455
  handle_gem_prompt(ch, data, str)
353
456
  end
@@ -0,0 +1,23 @@
1
+ namespace :rubber do
2
+
3
+ desc "Describes all your spot instance requests"
4
+ required_task :describe_spot_instance_requests do
5
+ requests = cloud.describe_spot_instance_requests()
6
+ requests.each do |request|
7
+ logger.info "======================"
8
+ logger.info "ID: #{request[:id]}"
9
+ logger.info "Created at: #{request[:created_at]}"
10
+ logger.info "Max. price: $#{request[:spot_price]}"
11
+ logger.info "State: #{request[:state]}"
12
+ logger.info "Instance type: #{request[:type]}"
13
+ logger.info "AMI: #{request[:image_id]}"
14
+ end
15
+ end
16
+
17
+ desc "Cancel the spot instances request for the given id"
18
+ required_task :cancel_spot_instances_request do
19
+ request_id = get_env('SPOT_INSTANCE_REQUEST_ID', 'The id of the spot instances request to cancel', true)
20
+ cloud.destroy_spot_instance_request(request_id)
21
+ end
22
+
23
+ end