rubber 1.0.2

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.
Files changed (163) hide show
  1. data/CHANGELOG +39 -0
  2. data/COPYING +339 -0
  3. data/README +6 -0
  4. data/TODO +11 -0
  5. data/VERSION +1 -0
  6. data/bin/vulcanize +41 -0
  7. data/generators/vulcanize/USAGE +6 -0
  8. data/generators/vulcanize/templates/apache/config/rubber/deploy-apache.rb +51 -0
  9. data/generators/vulcanize/templates/apache/config/rubber/role/apache/deflate.conf +10 -0
  10. data/generators/vulcanize/templates/apache/config/rubber/role/apache/expires.conf +9 -0
  11. data/generators/vulcanize/templates/apache/config/rubber/role/apache/headers.conf +6 -0
  12. data/generators/vulcanize/templates/apache/config/rubber/role/apache/monit-apache.conf +8 -0
  13. data/generators/vulcanize/templates/apache/config/rubber/role/apache/ports.conf +5 -0
  14. data/generators/vulcanize/templates/apache/config/rubber/role/apache/setenvif.conf +52 -0
  15. data/generators/vulcanize/templates/apache/config/rubber/role/web_tools/tools-apache-vhost.conf +62 -0
  16. data/generators/vulcanize/templates/apache/config/rubber/role/web_tools/tools-apache.auth +7 -0
  17. data/generators/vulcanize/templates/apache/config/rubber/role/web_tools/tools-index.html +30 -0
  18. data/generators/vulcanize/templates/apache/config/rubber/rubber-apache.yml +7 -0
  19. data/generators/vulcanize/templates/apache/templates.yml +1 -0
  20. data/generators/vulcanize/templates/base/Capfile +14 -0
  21. data/generators/vulcanize/templates/base/config/deploy.rb +55 -0
  22. data/generators/vulcanize/templates/base/config/rubber/common/crontab +16 -0
  23. data/generators/vulcanize/templates/base/config/rubber/common/profile.rc +9 -0
  24. data/generators/vulcanize/templates/base/config/rubber/deploy-setup.rb +104 -0
  25. data/generators/vulcanize/templates/base/config/rubber/rubber.yml +241 -0
  26. data/generators/vulcanize/templates/base/lib/tasks/rubber.rake +15 -0
  27. data/generators/vulcanize/templates/base/script/cron-rake +18 -0
  28. data/generators/vulcanize/templates/base/script/cron-runner +18 -0
  29. data/generators/vulcanize/templates/base/script/cron-sh +67 -0
  30. data/generators/vulcanize/templates/base/templates.yml +1 -0
  31. data/generators/vulcanize/templates/complete_mongrel_mysql/config/rubber/role/haproxy/haproxy-mongrel.conf +23 -0
  32. data/generators/vulcanize/templates/complete_mongrel_mysql/config/rubber/role/nginx/nginx-mongrel.conf +113 -0
  33. data/generators/vulcanize/templates/complete_mongrel_mysql/config/rubber/rubber-complete.yml +41 -0
  34. data/generators/vulcanize/templates/complete_mongrel_mysql/templates.yml +6 -0
  35. data/generators/vulcanize/templates/complete_passenger_mysql/config/rubber/role/haproxy/haproxy-passenger.conf +19 -0
  36. data/generators/vulcanize/templates/complete_passenger_mysql/config/rubber/rubber-complete.yml +40 -0
  37. data/generators/vulcanize/templates/complete_passenger_mysql/templates.yml +10 -0
  38. data/generators/vulcanize/templates/cruise/config/rubber/deploy-cruise.rb +72 -0
  39. data/generators/vulcanize/templates/cruise/config/rubber/role/cruise/cruise +40 -0
  40. data/generators/vulcanize/templates/cruise/config/rubber/role/cruise/my.cnf +165 -0
  41. data/generators/vulcanize/templates/cruise/config/rubber/role/cruise/production.rb +8 -0
  42. data/generators/vulcanize/templates/cruise/config/rubber/role/cruise/site_config.rb +76 -0
  43. data/generators/vulcanize/templates/cruise/config/rubber/role/web_tools/cruise-nginx.conf +11 -0
  44. data/generators/vulcanize/templates/cruise/config/rubber/rubber-cruise.yml +18 -0
  45. data/generators/vulcanize/templates/cruise/templates.yml +1 -0
  46. data/generators/vulcanize/templates/haproxy/config/rubber/deploy-haproxy.rb +45 -0
  47. data/generators/vulcanize/templates/haproxy/config/rubber/role/haproxy/haproxy-base.conf +26 -0
  48. data/generators/vulcanize/templates/haproxy/config/rubber/role/haproxy/haproxy-default.conf +8 -0
  49. data/generators/vulcanize/templates/haproxy/config/rubber/role/haproxy/monit-haproxy.conf +9 -0
  50. data/generators/vulcanize/templates/haproxy/config/rubber/role/haproxy/syslog-haproxy.conf +6 -0
  51. data/generators/vulcanize/templates/haproxy/config/rubber/role/haproxy/syslogd-default.conf +17 -0
  52. data/generators/vulcanize/templates/haproxy/config/rubber/role/web_tools/haproxy-nginx.conf +10 -0
  53. data/generators/vulcanize/templates/haproxy/config/rubber/rubber-haproxy.yml +7 -0
  54. data/generators/vulcanize/templates/haproxy/templates.yml +1 -0
  55. data/generators/vulcanize/templates/memcached/config/memcached.yml +28 -0
  56. data/generators/vulcanize/templates/memcached/config/rubber/common/memcached.yml +14 -0
  57. data/generators/vulcanize/templates/memcached/config/rubber/role/memcached/memcached.conf +52 -0
  58. data/generators/vulcanize/templates/memcached/config/rubber/role/memcached/memcached_munin_plugin +249 -0
  59. data/generators/vulcanize/templates/memcached/config/rubber/rubber-memcached.yml +7 -0
  60. data/generators/vulcanize/templates/memcached/templates.yml +1 -0
  61. data/generators/vulcanize/templates/minimal_mysql/templates.yml +7 -0
  62. data/generators/vulcanize/templates/minimal_nodb/templates.yml +6 -0
  63. data/generators/vulcanize/templates/mongrel/config/rubber/deploy-mongrel.rb +75 -0
  64. data/generators/vulcanize/templates/mongrel/config/rubber/role/mongrel/mongrel_cluster.yml +12 -0
  65. data/generators/vulcanize/templates/mongrel/config/rubber/role/mongrel/monit-mongrel.conf +20 -0
  66. data/generators/vulcanize/templates/mongrel/config/rubber/rubber-mongrel.yml +9 -0
  67. data/generators/vulcanize/templates/mongrel/templates.yml +1 -0
  68. data/generators/vulcanize/templates/monit/config/rubber/common/monit-default.conf +15 -0
  69. data/generators/vulcanize/templates/monit/config/rubber/common/monit.conf +251 -0
  70. data/generators/vulcanize/templates/monit/config/rubber/deploy-monit.rb +32 -0
  71. data/generators/vulcanize/templates/monit/config/rubber/role/web_tools/monit-admin-nginx.conf +10 -0
  72. data/generators/vulcanize/templates/monit/config/rubber/rubber-monit.yml +6 -0
  73. data/generators/vulcanize/templates/monit/templates.yml +1 -0
  74. data/generators/vulcanize/templates/munin/config/rubber/common/monit-munin.conf +8 -0
  75. data/generators/vulcanize/templates/munin/config/rubber/common/munin-node.conf +48 -0
  76. data/generators/vulcanize/templates/munin/config/rubber/deploy-munin.rb +46 -0
  77. data/generators/vulcanize/templates/munin/config/rubber/role/web_tools/munin-nginx.conf +8 -0
  78. data/generators/vulcanize/templates/munin/config/rubber/role/web_tools/munin-plugins.conf +31 -0
  79. data/generators/vulcanize/templates/munin/config/rubber/role/web_tools/munin.conf +80 -0
  80. data/generators/vulcanize/templates/munin/config/rubber/rubber-munin.yml +8 -0
  81. data/generators/vulcanize/templates/munin/script/munin/example_mysql_query.rb +57 -0
  82. data/generators/vulcanize/templates/munin/script/munin/example_simple.rb +24 -0
  83. data/generators/vulcanize/templates/munin/templates.yml +1 -0
  84. data/generators/vulcanize/templates/mysql/config/rubber/common/database.yml +11 -0
  85. data/generators/vulcanize/templates/mysql/config/rubber/deploy-mysql.rb +156 -0
  86. data/generators/vulcanize/templates/mysql/config/rubber/role/db/crontab +14 -0
  87. data/generators/vulcanize/templates/mysql/config/rubber/role/db/monit-mysql.cnf +10 -0
  88. data/generators/vulcanize/templates/mysql/config/rubber/role/db/my.cnf +167 -0
  89. data/generators/vulcanize/templates/mysql/config/rubber/role/mysql_slave/mysql_slave_munin_plugin +51 -0
  90. data/generators/vulcanize/templates/mysql/config/rubber/rubber-mysql.yml +46 -0
  91. data/generators/vulcanize/templates/mysql/templates.yml +1 -0
  92. data/generators/vulcanize/templates/mysql_cluster/config/rubber/common/mysql_cluster_migrations.rb +13 -0
  93. data/generators/vulcanize/templates/mysql_cluster/config/rubber/deploy-mysql_cluster.rb +173 -0
  94. data/generators/vulcanize/templates/mysql_cluster/config/rubber/role/mysql_data/my.cnf +15 -0
  95. data/generators/vulcanize/templates/mysql_cluster/config/rubber/role/mysql_mgm/ndb_mgmd.cnf +39 -0
  96. data/generators/vulcanize/templates/mysql_cluster/config/rubber/role/mysql_sql/monit-mysql_cluster_sql.cnf +10 -0
  97. data/generators/vulcanize/templates/mysql_cluster/config/rubber/role/mysql_sql/my.cnf +23 -0
  98. data/generators/vulcanize/templates/mysql_cluster/config/rubber/rubber-mysql_cluster.yml +32 -0
  99. data/generators/vulcanize/templates/mysql_cluster/templates.yml +1 -0
  100. data/generators/vulcanize/templates/mysql_proxy/config/rubber/common/database.yml +16 -0
  101. data/generators/vulcanize/templates/mysql_proxy/config/rubber/common/monit-mysql_proxy.cnf +10 -0
  102. data/generators/vulcanize/templates/mysql_proxy/config/rubber/common/mysql-proxy +153 -0
  103. data/generators/vulcanize/templates/mysql_proxy/config/rubber/common/mysql-proxy.conf +10 -0
  104. data/generators/vulcanize/templates/mysql_proxy/config/rubber/common/mysql-proxy.lua +5 -0
  105. data/generators/vulcanize/templates/mysql_proxy/config/rubber/deploy-mysql_proxy.rb +52 -0
  106. data/generators/vulcanize/templates/mysql_proxy/config/rubber/rubber-mysql_proxy.yml +11 -0
  107. data/generators/vulcanize/templates/mysql_proxy/templates.yml +1 -0
  108. data/generators/vulcanize/templates/nginx/config/rubber/deploy-nginx.rb +45 -0
  109. data/generators/vulcanize/templates/nginx/config/rubber/role/nginx/crontab +9 -0
  110. data/generators/vulcanize/templates/nginx/config/rubber/role/nginx/monit-nginx.conf +8 -0
  111. data/generators/vulcanize/templates/nginx/config/rubber/role/nginx/nginx.conf +42 -0
  112. data/generators/vulcanize/templates/nginx/config/rubber/role/web_tools/nginx-tools.conf +55 -0
  113. data/generators/vulcanize/templates/nginx/config/rubber/role/web_tools/tools-index.html +30 -0
  114. data/generators/vulcanize/templates/nginx/config/rubber/role/web_tools/tools-nginx.auth +7 -0
  115. data/generators/vulcanize/templates/nginx/config/rubber/rubber-nginx.yml +10 -0
  116. data/generators/vulcanize/templates/nginx/templates.yml +1 -0
  117. data/generators/vulcanize/templates/passenger/config/rubber/deploy-passenger.rb +37 -0
  118. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/munin-passenger-sudoers.conf +6 -0
  119. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/munin-passenger.conf +47 -0
  120. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/passenger-apache-vhost.conf +46 -0
  121. data/generators/vulcanize/templates/passenger/config/rubber/role/passenger/passenger.conf +10 -0
  122. data/generators/vulcanize/templates/passenger/config/rubber/rubber-passenger.yml +12 -0
  123. data/generators/vulcanize/templates/passenger/templates.yml +1 -0
  124. data/generators/vulcanize/templates/sphinx/config/rubber/common/sphinx.yml +46 -0
  125. data/generators/vulcanize/templates/sphinx/config/rubber/deploy-sphinx.rb +112 -0
  126. data/generators/vulcanize/templates/sphinx/config/rubber/role/sphinx/crontab +11 -0
  127. data/generators/vulcanize/templates/sphinx/config/rubber/role/sphinx/monit-sphinx.conf +10 -0
  128. data/generators/vulcanize/templates/sphinx/config/rubber/rubber-sphinx.yml +6 -0
  129. data/generators/vulcanize/templates/sphinx/templates.yml +1 -0
  130. data/generators/vulcanize/vulcanize_generator.rb +67 -0
  131. data/lib/capistrano/hostcmd.rb +12 -0
  132. data/lib/rubber.rb +38 -0
  133. data/lib/rubber/capistrano.rb +1 -0
  134. data/lib/rubber/cloud.rb +13 -0
  135. data/lib/rubber/cloud/aws.rb +305 -0
  136. data/lib/rubber/cloud/base.rb +16 -0
  137. data/lib/rubber/configuration.rb +47 -0
  138. data/lib/rubber/dns.rb +13 -0
  139. data/lib/rubber/dns/base.rb +69 -0
  140. data/lib/rubber/dns/dyndns.rb +63 -0
  141. data/lib/rubber/dns/nettica.rb +73 -0
  142. data/lib/rubber/dns/zerigo.rb +131 -0
  143. data/lib/rubber/environment.rb +161 -0
  144. data/lib/rubber/generator.rb +197 -0
  145. data/lib/rubber/instance.rb +165 -0
  146. data/lib/rubber/recipes/rubber.rb +89 -0
  147. data/lib/rubber/recipes/rubber/bundles.rb +28 -0
  148. data/lib/rubber/recipes/rubber/deploy.rb +90 -0
  149. data/lib/rubber/recipes/rubber/instances.rb +348 -0
  150. data/lib/rubber/recipes/rubber/load_balancers.rb +44 -0
  151. data/lib/rubber/recipes/rubber/security_groups.rb +189 -0
  152. data/lib/rubber/recipes/rubber/setup.rb +357 -0
  153. data/lib/rubber/recipes/rubber/static_ips.rb +107 -0
  154. data/lib/rubber/recipes/rubber/utils.rb +203 -0
  155. data/lib/rubber/recipes/rubber/volumes.rb +264 -0
  156. data/lib/rubber/tasks/rubber.rb +221 -0
  157. data/lib/rubber/util.rb +37 -0
  158. data/test/environment_test.rb +118 -0
  159. data/test/generator_test.rb +323 -0
  160. data/test/instance_test.rb +93 -0
  161. data/test/test_helper.rb +4 -0
  162. data/test/util_test.rb +16 -0
  163. metadata +274 -0
@@ -0,0 +1,107 @@
1
+ namespace :rubber do
2
+
3
+
4
+ desc <<-DESC
5
+ Sets up static IPs for the instances configured to have them
6
+ DESC
7
+ required_task :setup_static_ips do
8
+ rubber_instances.each do |ic|
9
+ env = rubber_cfg.environment.bind(ic.role_names, ic.name)
10
+ if env.use_static_ip
11
+ artifacts = rubber_instances.artifacts
12
+ ip = artifacts['static_ips'][ic.name] rescue nil
13
+
14
+ # first allocate the static ip if we don't have a global record (artifacts) for it
15
+ if ! ip
16
+ logger.info "Allocating static IP for #{ic.full_name}"
17
+ ip = allocate_static_ip()
18
+ artifacts['static_ips'][ic.name] = ip
19
+ rubber_instances.save
20
+ end
21
+
22
+ # then, associate it if we don't have a record (on instance) of association
23
+ if ! ic.static_ip
24
+ logger.info "Associating static ip #{ip} with #{ic.full_name}"
25
+ associate_static_ip(ip, ic.instance_id)
26
+
27
+ instance = cloud.describe_instances(ic.instance_id).first
28
+ ic.external_host = instance[:external_host]
29
+ ic.internal_host = instance[:internal_host]
30
+ ic.external_ip = ip
31
+ ic.static_ip = ip
32
+ rubber_instances.save()
33
+
34
+ logger.info "Waiting for static ip to associate"
35
+ while true do
36
+ task :_wait_for_static_ip, :hosts => ip do
37
+ run "echo"
38
+ end
39
+ begin
40
+ _wait_for_static_ip
41
+ rescue ConnectionError
42
+ sleep 2
43
+ logger.info "Failed to connect to static ip #{ip}, retrying"
44
+ retry
45
+ end
46
+ break
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ desc <<-DESC
55
+ Shows the configured static IPs
56
+ DESC
57
+ required_task :describe_static_ips do
58
+ results = []
59
+ format = "%-10s %-15s %-30s"
60
+ results << format % %w[InstanceID IP Alias]
61
+
62
+ ips = cloud.describe_static_ips()
63
+ ips.each do |ip_data|
64
+ instance_id = ip_data[:instance_id]
65
+ ip = ip_data[:ip]
66
+
67
+ local_alias = find_alias(ip, instance_id, false)
68
+
69
+ results << format % [instance_id || "Unassigned", ip, local_alias || "Unknown"]
70
+ end
71
+
72
+ results.each {|r| logger.info r}
73
+ end
74
+
75
+ desc <<-DESC
76
+ Deallocates the given static ip
77
+ DESC
78
+ required_task :destroy_static_ip do
79
+ ip = get_env('IP', "Static IP (run rubber:describe_static_ips for a list)", true)
80
+ destroy_static_ip(ip)
81
+ end
82
+
83
+ def allocate_static_ip()
84
+ ip = cloud.create_static_ip()
85
+ fatal "Failed to allocate static ip" if ip.nil?
86
+ return ip
87
+ end
88
+
89
+ def associate_static_ip(ip, instance_id)
90
+ success = cloud.attach_static_ip(ip, instance_id)
91
+ fatal "Failed to associate static ip" unless success
92
+ end
93
+
94
+ def destroy_static_ip(ip)
95
+ logger.info "Releasing static ip: #{ip}"
96
+ cloud.destroy_static_ip(ip) rescue logger.info("IP was not attached")
97
+
98
+ logger.info "Removing ip #{ip} from rubber instances file"
99
+ artifacts = rubber_instances.artifacts
100
+ artifacts['static_ips'].delete_if {|k,v| v == ip}
101
+ rubber_instances.each do |ic|
102
+ ic.static_ip = nil if ic.static_ip == ip
103
+ end
104
+ rubber_instances.save
105
+ end
106
+
107
+ end
@@ -0,0 +1,203 @@
1
+ namespace :rubber do
2
+
3
+ desc <<-DESC
4
+ Convenience task for creating a staging instance for the given RUBBER_ENV/RAILS_ENV.
5
+ By default this task assigns all known roles when creating the instance,
6
+ but you can specify a different default in rubber.yml:staging_roles
7
+ At the end, the instance will be up and running
8
+ e.g. RUBBER_ENV=matt cap create_staging
9
+ DESC
10
+ required_task :create_staging do
11
+ if rubber_instances.size > 0
12
+ value = Capistrano::CLI.ui.ask("The #{RUBBER_ENV} environment already has instances, Are you SURE you want to create a staging instance that may interact with them [y/N]?: ")
13
+ fatal("Exiting", 0) if value !~ /^y/
14
+ end
15
+ instance_alias = ENV['ALIAS'] = rubber.get_env("ALIAS", "Hostname to use for staging instance", true, RUBBER_ENV)
16
+ default_roles = rubber_env.staging_roles || "*"
17
+ roles = ENV['ROLES'] = rubber.get_env("ROLES", "Roles to use for staging instance", true, default_roles)
18
+
19
+ # some bootstraps update code (bootstrap_db) but if you don't have that role, need to do it here
20
+ # Since release directory variable gets reused by cap, we have to just do the symlink here - doing
21
+ # a update again will fail
22
+ set :rubber_code_was_updated, false
23
+ after "deploy:update_code" do
24
+ set :rubber_code_was_updated, true
25
+ end
26
+
27
+ if rubber_instances[instance_alias]
28
+ logger.info "Instance already exists, skipping to bootstrap"
29
+ else
30
+ rubber.create
31
+ end
32
+ rubber.bootstrap
33
+ # stop everything in case we have a bundled instance with monit, etc starting at boot
34
+ deploy.stop rescue nil
35
+ if ! rubber_code_was_updated
36
+ deploy.update_code
37
+ end
38
+ deploy.symlink
39
+ deploy.migrate
40
+ deploy.start
41
+ end
42
+
43
+ desc <<-DESC
44
+ Destroy the staging instance for the given RUBBER_ENV.
45
+ DESC
46
+ task :destroy_staging do
47
+ ENV['ALIAS'] = rubber.get_env("ALIAS", "Hostname of staging instance to be destroyed", true, RUBBER_ENV)
48
+ rubber.destroy
49
+ end
50
+
51
+ desc <<-DESC
52
+ Live tail of rails log files for all machines
53
+ By default tails the rails logs for the current RUBBER_ENV, but one can
54
+ set FILE=/path/file.*.glob to tails a different set
55
+ DESC
56
+ task :tail_logs, :roles => :app do
57
+ log_file_glob = rubber.get_env("FILE", "Log files to tail", true, "#{current_path}/log/#{RUBBER_ENV}*.log")
58
+ run "tail -qf #{log_file_glob}" do |channel, stream, data|
59
+ puts # for an extra line break before the host name
60
+ puts data
61
+ break if stream == :err
62
+ end
63
+ end
64
+
65
+ # Use instead of task to define a capistrano task that runs serially instead of in parallel
66
+ # The :groups option specifies how many groups to partition the servers into so that we can
67
+ # do the task for N (= total/groups) servers at a time. When multiple roles are supplied,
68
+ # this tries to be intelligent and slice up each role independently, but runs the slices together
69
+ # so that things don't take too long, e.g. adding an :api role to some :app servers, when restarting
70
+ # you don't want to do the api first, then the others as this would take a long time, so instead
71
+ # it does some :api and some :app, then some more of each
72
+ #
73
+ def serial_task(ns, name, options = {}, &block)
74
+ # first figure out server names for the passed in roles - when no roles
75
+ # are passed in, use all servers
76
+ serial_roles = Array(options[:roles])
77
+ servers = {}
78
+ if serial_roles.empty?
79
+ all_servers = []
80
+ self.roles.each do |rolename, serverdefs|
81
+ all_servers += serverdefs.collect {|server| server.host}
82
+ end
83
+ servers[:_serial_all] = all_servers.uniq.sort
84
+ else
85
+ # get servers for each role
86
+ self.roles.each do |rolename, serverdefs|
87
+ if serial_roles.include?(rolename)
88
+ servers[rolename] ||= []
89
+ servers[rolename] += serverdefs.collect {|server| server.host}
90
+ end
91
+ end
92
+
93
+ # Remove duplication of servers - roles which come first in list
94
+ # have precedence, so the servers show up in that group
95
+ serial_roles.each_with_index do |rolename, i|
96
+ servers[rolename] ||= []
97
+ serial_roles[i+1..-1].each do |r|
98
+ servers[r] -= servers[rolename]
99
+ end
100
+ servers[rolename] = servers[rolename].uniq.sort
101
+ end
102
+ end
103
+
104
+ # group each role's servers into slices, but combine slices across roles
105
+ slices = []
106
+ servers.each do |rolename, svrs|
107
+ next if svrs.size == 0
108
+ # figure out size of each slice by deviding server count by # of groups
109
+ slice_size = svrs.size / (options.delete(:groups) || 2)
110
+ slice_size = 1 if slice_size == 0
111
+ slice_idx = 0
112
+ svrs.each_slice(slice_size) do |srv_slice|
113
+ slices[slice_idx] ||= []
114
+ slices[slice_idx] += srv_slice
115
+ slice_idx += 1
116
+ end
117
+ end
118
+ # for each slice, define a new task specific to the hosts in that slice
119
+ task_syms = []
120
+ slices.each do |server_group|
121
+ servers = server_group.map{|s| s.gsub(/\..*/, '')}.join("_")
122
+ task_sym = "_serial_task_#{name.to_s}_#{servers}".to_sym
123
+ task_syms << task_sym
124
+ ns.task task_sym, options.merge(:hosts => server_group), &block
125
+ end
126
+
127
+ # create the top level task that calls all the serial ones
128
+ ns.task name, options do
129
+ task_syms.each do |t|
130
+ ns.send t
131
+ end
132
+ end
133
+ end
134
+
135
+ def find_alias(ip, instance_id, do_connect=true)
136
+ if instance_id
137
+ instance = rubber_instances.find {|i| i.instance_id == instance_id }
138
+ local_alias = instance.full_name if instance
139
+ end
140
+ local_alias ||= File.read("/etc/hosts").grep(/#{ip}/).first.split[1] rescue nil
141
+ if ! local_alias && do_connect
142
+ task :_get_ip, :hosts => ip do
143
+ local_alias = "* " + capture("hostname").strip
144
+ end
145
+ _get_ip rescue ConnectionError
146
+ end
147
+ return local_alias
148
+ end
149
+
150
+ def prepare_script(name, contents)
151
+ script = "/tmp/#{name}"
152
+ # this lets us abort a script if a command in the middle of it errors out
153
+ contents = "#{rubber_env.stop_on_error_cmd}\n#{contents}" if rubber_env.stop_on_error_cmd
154
+ put(contents, script)
155
+ return script
156
+ end
157
+
158
+ def run_script(name, contents)
159
+ script = prepare_script(name, contents)
160
+ run "sh #{script}"
161
+ end
162
+
163
+ def sudo_script(name, contents)
164
+ script = prepare_script(name, contents)
165
+ sudo "sh #{script}"
166
+ end
167
+
168
+ def get_env(name, desc, required=false, default=nil)
169
+ value = ENV.delete(name)
170
+ msg = "#{desc}"
171
+ msg << " [#{default}]" if default
172
+ msg << ": "
173
+ value = Capistrano::CLI.ui.ask(msg) unless value
174
+ value = value.size == 0 ? default : value
175
+ fatal "#{name} is required, pass using environment or enter at prompt" if required && ! value
176
+ return value
177
+ end
178
+
179
+ def fatal(msg, code=1)
180
+ logger.info msg
181
+ exit code
182
+ end
183
+
184
+ # Returns a map of "hostvar_<hostname>" => value for the given config value for each instance host
185
+ # This is used to run capistrano tasks scoped to the correct role/host so that a config value
186
+ # specific to a role/host will only be used for that role/host, e.g. the list of packages to
187
+ # be installed.
188
+ def get_host_options(cfg_name, &block)
189
+ opts = {}
190
+ rubber_instances.each do | ic|
191
+ env = rubber_cfg.environment.bind(ic.role_names, ic.name)
192
+ cfg_value = env[cfg_name]
193
+ if cfg_value
194
+ if block
195
+ cfg_value = block.call(cfg_value)
196
+ end
197
+ opts["hostvar_#{ic.full_name}"] = cfg_value if cfg_value && cfg_value.strip.size > 0
198
+ end
199
+ end
200
+ return opts
201
+ end
202
+
203
+ end
@@ -0,0 +1,264 @@
1
+ namespace :rubber do
2
+
3
+ desc <<-DESC
4
+ Sets up persistent volumes in the cloud
5
+ All volumes defined in rubber.yml will be created if neccessary, and attached/mounted on their associated instances
6
+ DESC
7
+ required_task :setup_volumes do
8
+ rubber_instances.filtered.each do |ic|
9
+ env = rubber_cfg.environment.bind(ic.role_names, ic.name)
10
+ created_vols = []
11
+ vol_specs = env.volumes || []
12
+ vol_specs.each do |vol_spec|
13
+ created_vols << setup_volume(ic, vol_spec)
14
+ end
15
+ created_vols.compact!
16
+
17
+ created_parts = []
18
+ partition_specs = env.local_volumes || []
19
+ partition_specs.each do |partition_spec|
20
+ created_parts << setup_partition(ic, partition_spec)
21
+ end
22
+ created_parts.compact!
23
+ zero_partitions(ic, created_parts)
24
+ created_vols += created_parts
25
+
26
+ created_vols = created_vols.compact.uniq
27
+ raid_specs = env.raid_volumes || []
28
+ raid_specs.each do |raid_spec|
29
+ # we want to format if we created the ec2 volumes, or if we don't have any
30
+ # ec2 volumes and are just creating raid array from ephemeral stores
31
+ format = raid_spec['source_devices'].all? {|dev| created_vols.include?(dev)}
32
+ setup_raid_volume(ic, raid_spec, format)
33
+ end
34
+ end
35
+ end
36
+
37
+ desc <<-DESC
38
+ Shows the configured persistent volumes
39
+ DESC
40
+ required_task :describe_volumes do
41
+ results = []
42
+ format = "%-20s %-15s %-15s %-20s"
43
+ results << format % %w[Id Status Attached Instance]
44
+
45
+ volumes = cloud.describe_volumes()
46
+ volumes.each do |volume|
47
+ results << format % [volume[:id], volume[:status], volume[:attachment_status], volume[:attachment_instance_id]]
48
+ end
49
+
50
+ results.each {|r| logger.info r}
51
+ end
52
+
53
+ desc <<-DESC
54
+ Shows the configured persistent volumes
55
+ DESC
56
+ required_task :destroy_volume do
57
+ volume_id = get_env('VOLUME_ID', "Volume ID", true)
58
+ destroy_volume(volume_id)
59
+ end
60
+
61
+ def create_volume(size, zone)
62
+ volumeId = cloud.create_volume(size.to_s, zone)
63
+ fatal "Failed to create volume" if volumeId.nil?
64
+ return volumeId
65
+ end
66
+
67
+ def attach_volume(vol_id, instance_id, device)
68
+ cloud.attach_volume(vol_id, instance_id, device)
69
+ end
70
+
71
+ def setup_volume(ic, vol_spec)
72
+ created = nil
73
+ key = "#{ic.name}_#{vol_spec['device']}"
74
+ artifacts = rubber_instances.artifacts
75
+ vol_id = artifacts['volumes'][key]
76
+
77
+ # first create the volume if we don't have a global record (artifacts) for it
78
+ if ! vol_id
79
+ logger.info "Creating volume for #{ic.full_name}:#{vol_spec['device']}"
80
+ vol_id = create_volume(vol_spec['size'], vol_spec['zone'])
81
+ artifacts['volumes'][key] = vol_id
82
+ rubber_instances.save
83
+ created = vol_spec['device']
84
+ end
85
+
86
+ # then, attach it if we don't have a record (on instance) of attachment
87
+ ic.volumes ||= []
88
+ if ! ic.volumes.include?(vol_id)
89
+ logger.info "Attaching volume #{vol_id} to #{ic.full_name}:#{vol_spec['device']}"
90
+ attach_volume(vol_id, ic.instance_id, vol_spec['device'])
91
+ ic.volumes << vol_id
92
+ rubber_instances.save
93
+
94
+ print "Waiting for volume to attach"
95
+ while true do
96
+ print "."
97
+ sleep 2
98
+ volume = cloud.describe_volumes(vol_id).first
99
+ break if volume[:status] == "in-use"
100
+ end
101
+ print "\n"
102
+
103
+ # we don't mount/format at this time if we are doing a RAID array
104
+ if vol_spec['mount'] && vol_spec['filesystem']
105
+ # then format/mount/etc if we don't have an entry in hosts file
106
+ task :_setup_volume, :hosts => ic.external_ip do
107
+ rubber.run_script 'setup_volume', <<-ENDSCRIPT
108
+ if ! grep -q '#{vol_spec['mount']}' /etc/fstab; then
109
+ if mount | grep -q '#{vol_spec['mount']}'; then
110
+ umount '#{vol_spec['mount']}'
111
+ fi
112
+ mv /etc/fstab /etc/fstab.bak
113
+ cat /etc/fstab.bak | grep -v '#{vol_spec['mount']}' > /etc/fstab
114
+ echo '#{vol_spec['device']} #{vol_spec['mount']} #{vol_spec['filesystem']} noatime 0 0 # rubber volume #{vol_id}' >> /etc/fstab
115
+
116
+ #{('yes | mkfs -t ' + vol_spec['filesystem'] + ' ' + vol_spec['device']) if created}
117
+ mkdir -p '#{vol_spec['mount']}'
118
+ mount '#{vol_spec['mount']}'
119
+ fi
120
+ ENDSCRIPT
121
+ end
122
+ _setup_volume
123
+ end
124
+
125
+ end
126
+ return created
127
+ end
128
+
129
+ def setup_partition(ic, partition_spec)
130
+ created = nil
131
+ part_id = partition_spec['partition_device']
132
+
133
+ # Only create the partition if we haven't already done so
134
+ ic.partitions ||= []
135
+ if ! ic.partitions.include?(part_id)
136
+ # then format/mount/etc if we don't have an entry in hosts file
137
+ task :_setup_partition, :hosts => ic.external_ip do
138
+ rubber.run_script 'setup_partition', <<-ENDSCRIPT
139
+ if ! fdisk -l 2>&1 | grep -q '#{partition_spec['partition_device']}'; then
140
+ if grep -q '#{partition_spec['disk_device']}\\b' /etc/fstab; then
141
+ umount #{partition_spec['disk_device']}
142
+ mv /etc/fstab /etc/fstab.bak
143
+ cat /etc/fstab.bak | grep -v '#{partition_spec['disk_device']}\\b' > /etc/fstab
144
+ fi
145
+
146
+ # partition format is: Start (blank is first available),Size(MB due to -uM),Id(83=linux,82=swap,etc),Bootable
147
+ echo "#{partition_spec['start']},#{partition_spec['size']},#{partition_spec['type']},#{partition_spec['bootable']}" | sfdisk -L -uM #{partition_spec['disk_device']}
148
+ fi
149
+ ENDSCRIPT
150
+ end
151
+ _setup_partition
152
+
153
+ ic.partitions << part_id
154
+ rubber_instances.save
155
+ created = part_id
156
+
157
+ end
158
+
159
+ return created
160
+ end
161
+
162
+ def zero_partitions(ic, partitions)
163
+ env = rubber_cfg.environment.bind(ic.role_names, ic.name)
164
+
165
+ # don't zero out the ones that we weren't told to
166
+ partitions.delete_if do |part|
167
+ spec = env.local_volumes.find {|s| s['partition_device'] == part}
168
+ ! spec['zero']
169
+ end
170
+
171
+ if partitions.size > 0
172
+ zero_script = ""
173
+ partitions.each do |partition|
174
+ zero_script << "nohup dd if=/dev/zero bs=1M of=#{partition} &> /dev/null &\n"
175
+ end
176
+ # then format/mount/etc if we don't have an entry in hosts file
177
+ task :_zero_partitions, :hosts => ic.external_ip do
178
+ rubber.run_script 'zero_partitions', <<-ENDSCRIPT
179
+ # zero out parition for performance (see amazon DevGuide)
180
+ echo "Zeroing out raid partitions to improve performance, this way take a while"
181
+ #{zero_script}
182
+
183
+ echo "Waiting for partitions to zero out"
184
+ while true; do
185
+ if ! ps ax | grep -q "[d]d.*/dev/zero"; then exit; fi
186
+ echo -n .
187
+ sleep 1
188
+ done
189
+ ENDSCRIPT
190
+ end
191
+ _zero_partitions
192
+ end
193
+ end
194
+
195
+ def setup_raid_volume(ic, raid_spec, create=false)
196
+ if create
197
+ mdadm_init = "mdadm --create #{raid_spec['device']} --level #{raid_spec['raid_level']} --raid-devices #{raid_spec['source_devices'].size} #{raid_spec['source_devices'].sort.join(' ')}"
198
+ else
199
+ mdadm_init = "mdadm --assemble #{raid_spec['device']} #{raid_spec['source_devices'].sort.join(' ')}"
200
+ end
201
+
202
+ task :_setup_raid_volume, :hosts => ic.external_ip do
203
+ rubber.run_script 'setup_raid_volume', <<-ENDSCRIPT
204
+ if ! grep -q '#{raid_spec['device']}' /etc/fstab; then
205
+ if mount | grep -q '#{raid_spec['mount']}'; then
206
+ umount '#{raid_spec['mount']}'
207
+ fi
208
+ mv /etc/fstab /etc/fstab.bak
209
+ cat /etc/fstab.bak | grep -v '#{raid_spec['mount']}' > /etc/fstab
210
+ echo '#{raid_spec['device']} #{raid_spec['mount']} #{raid_spec['filesystem']} noatime 0 0 # rubber raid volume' >> /etc/fstab
211
+
212
+ # seems to help devices initialize, otherwise mdadm fails because
213
+ # device not ready even though ec2 says the volume is attached
214
+ fdisk -l &> /dev/null
215
+
216
+ #{mdadm_init}
217
+
218
+ # set reconstruction speed
219
+ echo $((30*1024)) > /proc/sys/dev/raid/speed_limit_min
220
+
221
+ echo 'DEVICE /dev/hd*[0-9] /dev/sd*[0-9]' > /etc/mdadm/mdadm.conf
222
+ mdadm --detail --scan >> /etc/mdadm/mdadm.conf
223
+
224
+ mv /etc/rc.local /etc/rc.local.bak
225
+ echo "mdadm --assemble --scan" > /etc/rc.local
226
+ chmod +x /etc/rc.local
227
+
228
+ #{('yes | mkfs -t ' + raid_spec['filesystem'] + ' ' + raid_spec['device']) if create}
229
+ mkdir -p '#{raid_spec['mount']}'
230
+ mount '#{raid_spec['mount']}'
231
+ fi
232
+ ENDSCRIPT
233
+ end
234
+ _setup_raid_volume
235
+ end
236
+
237
+ def destroy_volume(volume_id)
238
+
239
+ logger.info "Detaching volume #{volume_id}"
240
+ cloud.detach_volume(volume_id) rescue logger.info("Volume was not attached")
241
+
242
+ print "Waiting for volume to detach"
243
+ while true do
244
+ print "."
245
+ sleep 2
246
+ volume = cloud.describe_volumes(volume_id).first
247
+ status = volume && volume[:attachment_status]
248
+ break if !status || status == "detached"
249
+ end
250
+ print "\n"
251
+
252
+ logger.info "Deleting volume #{volume_id}"
253
+ cloud.destroy_volume(volume_id)
254
+
255
+ logger.info "Removing volume #{volume_id} from rubber instances file"
256
+ artifacts = rubber_instances.artifacts
257
+ artifacts['volumes'].delete_if {|k,v| v == volume_id}
258
+ rubber_instances.each do |ic|
259
+ ic.volumes.delete(volume_id) if ic.volumes
260
+ end
261
+ rubber_instances.save
262
+ end
263
+
264
+ end