engineyard-serverside 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (220) hide show
  1. data/LICENSE +19 -0
  2. data/bin/engineyard-serverside +10 -0
  3. data/lib/engineyard-serverside.rb +49 -0
  4. data/lib/engineyard-serverside/bundle_installer.rb +4 -0
  5. data/lib/engineyard-serverside/cli.rb +146 -0
  6. data/lib/engineyard-serverside/configuration.rb +130 -0
  7. data/lib/engineyard-serverside/default_maintenance_page.html +29 -0
  8. data/lib/engineyard-serverside/deploy.rb +321 -0
  9. data/lib/engineyard-serverside/deploy_hook.rb +80 -0
  10. data/lib/engineyard-serverside/lockfile_parser.rb +55 -0
  11. data/lib/engineyard-serverside/logged_output.rb +78 -0
  12. data/lib/engineyard-serverside/server.rb +70 -0
  13. data/lib/engineyard-serverside/strategies/git.rb +136 -0
  14. data/lib/engineyard-serverside/task.rb +62 -0
  15. data/lib/engineyard-serverside/version.rb +3 -0
  16. data/lib/vendor/dataflow/HISTORY +52 -0
  17. data/lib/vendor/dataflow/LICENSE +19 -0
  18. data/lib/vendor/dataflow/README.textile +290 -0
  19. data/lib/vendor/dataflow/Rakefile +36 -0
  20. data/lib/vendor/dataflow/dataflow.rb +120 -0
  21. data/lib/vendor/dataflow/dataflow/actor.rb +22 -0
  22. data/lib/vendor/dataflow/dataflow/equality.rb +28 -0
  23. data/lib/vendor/dataflow/dataflow/future_queue.rb +24 -0
  24. data/lib/vendor/dataflow/dataflow/port.rb +54 -0
  25. data/lib/vendor/dataflow/examples/barrier.rb +9 -0
  26. data/lib/vendor/dataflow/examples/data_driven.rb +17 -0
  27. data/lib/vendor/dataflow/examples/dataflow_http_gets.rb +13 -0
  28. data/lib/vendor/dataflow/examples/flow.rb +20 -0
  29. data/lib/vendor/dataflow/examples/future_http_gets.rb +12 -0
  30. data/lib/vendor/dataflow/examples/future_queue.rb +11 -0
  31. data/lib/vendor/dataflow/examples/instance_variables.rb +15 -0
  32. data/lib/vendor/dataflow/examples/laziness.rb +9 -0
  33. data/lib/vendor/dataflow/examples/local_variables.rb +11 -0
  34. data/lib/vendor/dataflow/examples/messages.rb +26 -0
  35. data/lib/vendor/dataflow/examples/port_http_gets.rb +13 -0
  36. data/lib/vendor/dataflow/examples/port_send.rb +10 -0
  37. data/lib/vendor/dataflow/examples/ring.rb +21 -0
  38. data/lib/vendor/dataflow/spec/actor_spec.rb +28 -0
  39. data/lib/vendor/dataflow/spec/anonymous_variables_spec.rb +21 -0
  40. data/lib/vendor/dataflow/spec/barrier_spec.rb +25 -0
  41. data/lib/vendor/dataflow/spec/by_need_spec.rb +55 -0
  42. data/lib/vendor/dataflow/spec/dataflow_spec.rb +151 -0
  43. data/lib/vendor/dataflow/spec/equality_spec.rb +40 -0
  44. data/lib/vendor/dataflow/spec/flow_spec.rb +25 -0
  45. data/lib/vendor/dataflow/spec/forker_spec.rb +28 -0
  46. data/lib/vendor/dataflow/spec/future_queue_spec.rb +31 -0
  47. data/lib/vendor/dataflow/spec/inspect_spec.rb +19 -0
  48. data/lib/vendor/dataflow/spec/need_later_spec.rb +12 -0
  49. data/lib/vendor/dataflow/spec/port_spec.rb +26 -0
  50. data/lib/vendor/dataflow/spec/spec.opts +1 -0
  51. data/lib/vendor/dataflow/spec/spec_helper.rb +10 -0
  52. data/lib/vendor/escape/Readme +21 -0
  53. data/lib/vendor/escape/doc_include/template/qualitysmith.rb +631 -0
  54. data/lib/vendor/escape/lib/escape.rb +247 -0
  55. data/lib/vendor/json_pure/CHANGES +166 -0
  56. data/lib/vendor/json_pure/COPYING +58 -0
  57. data/lib/vendor/json_pure/GPL +340 -0
  58. data/lib/vendor/json_pure/README +358 -0
  59. data/lib/vendor/json_pure/Rakefile +292 -0
  60. data/lib/vendor/json_pure/TODO +1 -0
  61. data/lib/vendor/json_pure/VERSION +1 -0
  62. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  63. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  64. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  65. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  66. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  67. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  68. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  69. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  70. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  71. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  72. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  73. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  74. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  75. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  76. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  77. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  78. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  79. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  80. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  81. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  82. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  83. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  84. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  85. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  86. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  87. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  88. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  89. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  90. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  91. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  92. data/lib/vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  93. data/lib/vendor/json_pure/benchmarks/generator2_benchmark.rb +222 -0
  94. data/lib/vendor/json_pure/benchmarks/generator_benchmark.rb +224 -0
  95. data/lib/vendor/json_pure/benchmarks/ohai.json +1216 -0
  96. data/lib/vendor/json_pure/benchmarks/ohai.ruby +1 -0
  97. data/lib/vendor/json_pure/benchmarks/parser2_benchmark.rb +251 -0
  98. data/lib/vendor/json_pure/benchmarks/parser_benchmark.rb +259 -0
  99. data/lib/vendor/json_pure/bin/edit_json.rb +9 -0
  100. data/lib/vendor/json_pure/bin/prettify_json.rb +75 -0
  101. data/lib/vendor/json_pure/data/example.json +1 -0
  102. data/lib/vendor/json_pure/data/index.html +38 -0
  103. data/lib/vendor/json_pure/data/prototype.js +4184 -0
  104. data/lib/vendor/json_pure/ext/json/ext/generator/extconf.rb +16 -0
  105. data/lib/vendor/json_pure/ext/json/ext/generator/generator.c +1323 -0
  106. data/lib/vendor/json_pure/ext/json/ext/generator/generator.h +170 -0
  107. data/lib/vendor/json_pure/ext/json/ext/parser/extconf.rb +15 -0
  108. data/lib/vendor/json_pure/ext/json/ext/parser/parser.c +1935 -0
  109. data/lib/vendor/json_pure/ext/json/ext/parser/parser.h +71 -0
  110. data/lib/vendor/json_pure/ext/json/ext/parser/parser.rl +792 -0
  111. data/lib/vendor/json_pure/install.rb +26 -0
  112. data/lib/vendor/json_pure/lib/json.rb +10 -0
  113. data/lib/vendor/json_pure/lib/json/Array.xpm +21 -0
  114. data/lib/vendor/json_pure/lib/json/FalseClass.xpm +21 -0
  115. data/lib/vendor/json_pure/lib/json/Hash.xpm +21 -0
  116. data/lib/vendor/json_pure/lib/json/Key.xpm +73 -0
  117. data/lib/vendor/json_pure/lib/json/NilClass.xpm +21 -0
  118. data/lib/vendor/json_pure/lib/json/Numeric.xpm +28 -0
  119. data/lib/vendor/json_pure/lib/json/String.xpm +96 -0
  120. data/lib/vendor/json_pure/lib/json/TrueClass.xpm +21 -0
  121. data/lib/vendor/json_pure/lib/json/add/core.rb +148 -0
  122. data/lib/vendor/json_pure/lib/json/add/rails.rb +58 -0
  123. data/lib/vendor/json_pure/lib/json/common.rb +397 -0
  124. data/lib/vendor/json_pure/lib/json/editor.rb +1371 -0
  125. data/lib/vendor/json_pure/lib/json/ext.rb +15 -0
  126. data/lib/vendor/json_pure/lib/json/json.xpm +1499 -0
  127. data/lib/vendor/json_pure/lib/json/pure.rb +77 -0
  128. data/lib/vendor/json_pure/lib/json/pure/generator.rb +452 -0
  129. data/lib/vendor/json_pure/lib/json/pure/parser.rb +307 -0
  130. data/lib/vendor/json_pure/lib/json/version.rb +8 -0
  131. data/lib/vendor/json_pure/tests/fixtures/fail1.json +1 -0
  132. data/lib/vendor/json_pure/tests/fixtures/fail10.json +1 -0
  133. data/lib/vendor/json_pure/tests/fixtures/fail11.json +1 -0
  134. data/lib/vendor/json_pure/tests/fixtures/fail12.json +1 -0
  135. data/lib/vendor/json_pure/tests/fixtures/fail13.json +1 -0
  136. data/lib/vendor/json_pure/tests/fixtures/fail14.json +1 -0
  137. data/lib/vendor/json_pure/tests/fixtures/fail18.json +1 -0
  138. data/lib/vendor/json_pure/tests/fixtures/fail19.json +1 -0
  139. data/lib/vendor/json_pure/tests/fixtures/fail2.json +1 -0
  140. data/lib/vendor/json_pure/tests/fixtures/fail20.json +1 -0
  141. data/lib/vendor/json_pure/tests/fixtures/fail21.json +1 -0
  142. data/lib/vendor/json_pure/tests/fixtures/fail22.json +1 -0
  143. data/lib/vendor/json_pure/tests/fixtures/fail23.json +1 -0
  144. data/lib/vendor/json_pure/tests/fixtures/fail24.json +1 -0
  145. data/lib/vendor/json_pure/tests/fixtures/fail25.json +1 -0
  146. data/lib/vendor/json_pure/tests/fixtures/fail27.json +2 -0
  147. data/lib/vendor/json_pure/tests/fixtures/fail28.json +2 -0
  148. data/lib/vendor/json_pure/tests/fixtures/fail3.json +1 -0
  149. data/lib/vendor/json_pure/tests/fixtures/fail4.json +1 -0
  150. data/lib/vendor/json_pure/tests/fixtures/fail5.json +1 -0
  151. data/lib/vendor/json_pure/tests/fixtures/fail6.json +1 -0
  152. data/lib/vendor/json_pure/tests/fixtures/fail7.json +1 -0
  153. data/lib/vendor/json_pure/tests/fixtures/fail8.json +1 -0
  154. data/lib/vendor/json_pure/tests/fixtures/fail9.json +1 -0
  155. data/lib/vendor/json_pure/tests/fixtures/pass1.json +56 -0
  156. data/lib/vendor/json_pure/tests/fixtures/pass15.json +1 -0
  157. data/lib/vendor/json_pure/tests/fixtures/pass16.json +1 -0
  158. data/lib/vendor/json_pure/tests/fixtures/pass17.json +1 -0
  159. data/lib/vendor/json_pure/tests/fixtures/pass2.json +1 -0
  160. data/lib/vendor/json_pure/tests/fixtures/pass26.json +1 -0
  161. data/lib/vendor/json_pure/tests/fixtures/pass3.json +6 -0
  162. data/lib/vendor/json_pure/tests/test_json.rb +361 -0
  163. data/lib/vendor/json_pure/tests/test_json_addition.rb +162 -0
  164. data/lib/vendor/json_pure/tests/test_json_encoding.rb +68 -0
  165. data/lib/vendor/json_pure/tests/test_json_fixtures.rb +34 -0
  166. data/lib/vendor/json_pure/tests/test_json_generate.rb +122 -0
  167. data/lib/vendor/json_pure/tests/test_json_rails.rb +144 -0
  168. data/lib/vendor/json_pure/tests/test_json_unicode.rb +76 -0
  169. data/lib/vendor/json_pure/tools/fuzz.rb +139 -0
  170. data/lib/vendor/json_pure/tools/server.rb +61 -0
  171. data/lib/vendor/open4/lib/open4.rb +403 -0
  172. data/lib/vendor/thor/CHANGELOG.rdoc +89 -0
  173. data/lib/vendor/thor/LICENSE +20 -0
  174. data/lib/vendor/thor/README.rdoc +297 -0
  175. data/lib/vendor/thor/Thorfile +69 -0
  176. data/lib/vendor/thor/bin/rake2thor +86 -0
  177. data/lib/vendor/thor/bin/thor +6 -0
  178. data/lib/vendor/thor/lib/thor.rb +244 -0
  179. data/lib/vendor/thor/lib/thor/actions.rb +275 -0
  180. data/lib/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  181. data/lib/vendor/thor/lib/thor/actions/directory.rb +91 -0
  182. data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
  183. data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
  184. data/lib/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
  185. data/lib/vendor/thor/lib/thor/base.rb +540 -0
  186. data/lib/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
  187. data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  188. data/lib/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  189. data/lib/vendor/thor/lib/thor/error.rb +30 -0
  190. data/lib/vendor/thor/lib/thor/group.rb +271 -0
  191. data/lib/vendor/thor/lib/thor/invocation.rb +180 -0
  192. data/lib/vendor/thor/lib/thor/parser.rb +4 -0
  193. data/lib/vendor/thor/lib/thor/parser/argument.rb +67 -0
  194. data/lib/vendor/thor/lib/thor/parser/arguments.rb +150 -0
  195. data/lib/vendor/thor/lib/thor/parser/option.rb +128 -0
  196. data/lib/vendor/thor/lib/thor/parser/options.rb +169 -0
  197. data/lib/vendor/thor/lib/thor/rake_compat.rb +66 -0
  198. data/lib/vendor/thor/lib/thor/runner.rb +314 -0
  199. data/lib/vendor/thor/lib/thor/shell.rb +83 -0
  200. data/lib/vendor/thor/lib/thor/shell/basic.rb +239 -0
  201. data/lib/vendor/thor/lib/thor/shell/color.rb +108 -0
  202. data/lib/vendor/thor/lib/thor/task.rb +102 -0
  203. data/lib/vendor/thor/lib/thor/util.rb +230 -0
  204. data/lib/vendor/thor/lib/thor/version.rb +3 -0
  205. data/lib/vendor/thor/thor.gemspec +120 -0
  206. data/spec/custom_deploy_spec.rb +95 -0
  207. data/spec/deploy_hook_spec.rb +211 -0
  208. data/spec/fixtures/gitrepo.tar.gz +0 -0
  209. data/spec/fixtures/gitrepo/foo +0 -0
  210. data/spec/fixtures/invalid_hook.rb +1 -0
  211. data/spec/fixtures/valid_hook.rb +1 -0
  212. data/spec/git_strategy_spec.rb +22 -0
  213. data/spec/lockfile_parser_spec.rb +30 -0
  214. data/spec/spec_helper.rb +37 -0
  215. data/spec/support/lockfiles/0.9-no-bundler +111 -0
  216. data/spec/support/lockfiles/0.9-with-bundler +117 -0
  217. data/spec/support/lockfiles/1.0-no-bundler +54 -0
  218. data/spec/support/lockfiles/1.0.0.rc.1-with-bundler +162 -0
  219. data/spec/support/lockfiles/not-a-lockfile +10 -0
  220. metadata +279 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Engine Yard, Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require 'rubygems'
4
+ rescue LoadError
5
+ end
6
+
7
+ $LOAD_PATH.push(File.expand_path("../lib", File.dirname(__FILE__)))
8
+ require 'engineyard-serverside'
9
+
10
+ EY::CLI.start
@@ -0,0 +1,49 @@
1
+ $LOAD_PATH.push(File.expand_path("engineyard-serverside", File.dirname(__FILE__)))
2
+ $LOAD_PATH.unshift File.expand_path('vendor/thor/lib', File.dirname(__FILE__))
3
+ $LOAD_PATH.unshift File.expand_path('vendor/open4/lib', File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift File.expand_path('vendor/escape/lib', File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift File.expand_path('vendor/json_pure/lib', File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift File.expand_path('vendor/dataflow', File.dirname(__FILE__))
7
+
8
+ require 'escape'
9
+ require 'json'
10
+ require 'dataflow'
11
+
12
+ require 'version'
13
+ require 'strategies/git'
14
+ require 'task'
15
+ require 'server'
16
+ require 'deploy'
17
+ require 'deploy_hook'
18
+ require 'lockfile_parser'
19
+ require 'bundle_installer'
20
+ require 'cli'
21
+ require 'configuration'
22
+
23
+ module EY
24
+ def self.node
25
+ @node ||= deep_indifferentize(JSON.parse(dna_json))
26
+ end
27
+
28
+ def self.dna_json
29
+ @dna_json ||= `sudo cat /etc/chef/dna.json`
30
+ end
31
+
32
+ RemoteFailure = Class.new StandardError
33
+
34
+ private
35
+ def self.deep_indifferentize(thing)
36
+ if thing.kind_of?(Hash)
37
+ indifferent_hash = Thor::CoreExt::HashWithIndifferentAccess.new
38
+ thing.each do |k, v|
39
+ indifferent_hash[k] = deep_indifferentize(v)
40
+ end
41
+ indifferent_hash
42
+ elsif thing.kind_of?(Array)
43
+ thing.map {|x| deep_indifferentize(x)}
44
+ else
45
+ thing
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,4 @@
1
+ module EY
2
+ class BundleInstaller < Struct.new(:version, :options)
3
+ end
4
+ end
@@ -0,0 +1,146 @@
1
+ require 'thor'
2
+
3
+ module EY
4
+ class CLI < Thor
5
+ include Dataflow
6
+
7
+ def self.start(*)
8
+ super
9
+ rescue RemoteFailure
10
+ exit(1)
11
+ end
12
+
13
+ method_option :migrate, :type => :string,
14
+ :desc => "Run migrations with this deploy",
15
+ :aliases => ["-m"]
16
+
17
+ method_option :branch, :type => :string,
18
+ :desc => "Git ref to deploy, defaults to master. May be a branch, a tag, or a SHA",
19
+ :aliases => %w[-b --ref --tag]
20
+
21
+ method_option :repo, :type => :string,
22
+ :desc => "Remote repo to deploy",
23
+ :aliases => ["-r"]
24
+
25
+ method_option :app, :type => :string,
26
+ :required => true,
27
+ :desc => "Application to deploy",
28
+ :aliases => ["-a"]
29
+
30
+ method_option :framework_env, :type => :string,
31
+ :required => true,
32
+ :desc => "Ruby web framework environment",
33
+ :aliases => ["-e"]
34
+
35
+ method_option :config, :type => :string,
36
+ :desc => "Additional configuration"
37
+
38
+ method_option :stack, :type => :string,
39
+ :desc => "Web stack (so we can restart it correctly)"
40
+
41
+ method_option :instances, :type => :array,
42
+ :desc => "Instances in cluster"
43
+
44
+ method_option :verbose, :type => :boolean,
45
+ :default => false,
46
+ :desc => "Verbose output",
47
+ :aliases => ["-v"]
48
+
49
+ desc "deploy", "Deploy code from /data/<app>"
50
+ def deploy(default_task=:deploy)
51
+ EY::Server.all = parse_instances(options[:instances])
52
+ EY::LoggedOutput.verbose = options[:verbose]
53
+ EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
54
+
55
+ invoke :propagate
56
+ EY::Deploy.run(options.merge("default_task" => default_task))
57
+ end
58
+
59
+ method_option :app, :type => :string,
60
+ :required => true,
61
+ :desc => "Which application's hooks to run",
62
+ :aliases => ["-a"]
63
+
64
+ method_option :release_path, :type => :string,
65
+ :desc => "Value for #release_path in hooks (mostly for internal coordination)",
66
+ :aliases => ["-r"]
67
+
68
+ method_option :current_role, :type => :string,
69
+ :desc => "Value for #current_role in hooks"
70
+
71
+ method_option :framework_env, :type => :string,
72
+ :required => true,
73
+ :desc => "Ruby web framework environment",
74
+ :aliases => ["-e"]
75
+
76
+ method_option :config, :type => :string,
77
+ :desc => "Additional configuration"
78
+
79
+ method_option :current_name, :type => :string,
80
+ :desc => "Value for #current_name in hooks"
81
+
82
+ desc "hook [NAME]", "Run a particular deploy hook"
83
+ def hook(hook_name)
84
+ EY::DeployHook.new(options).run(hook_name)
85
+ end
86
+
87
+ desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
88
+ def install_bundler(version)
89
+ egrep_escaped_version = version.gsub(/\./, '\.')
90
+ # the grep "bundler " is so that gems like bundler08 don't get
91
+ # their versions considered too
92
+ #
93
+ # the [,$] is to stop us from looking for e.g. 0.9.2, seeing
94
+ # 0.9.22, and mistakenly thinking 0.9.2 is there
95
+ has_bundler_cmd = "gem list bundler | grep \"bundler \" | egrep -q '#{egrep_escaped_version}[,)]'"
96
+
97
+ unless system(has_bundler_cmd)
98
+ system("gem install bundler -q --no-rdoc --no-ri -v '#{version}'")
99
+ end
100
+ end
101
+
102
+ desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{VERSION} and remove other versions if found."
103
+ def propagate
104
+ config = EY::Deploy::Configuration.new
105
+ gem_filename = "engineyard-serverside-#{VERSION}.gem"
106
+ local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
107
+ remote_gem_file = File.join(Dir.tmpdir, gem_filename)
108
+ gem_binary = File.join(Gem.default_bindir, 'gem')
109
+
110
+ EY::Server.config = config
111
+
112
+ barrier(*(EY::Server.all.find_all do |server|
113
+ !server.local? # of course this machine has it
114
+ end.map do |server|
115
+ need_later do
116
+ egrep_escaped_version = VERSION.gsub(/\./, '\.')
117
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
118
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
119
+ has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
120
+
121
+ if !server.run(has_gem_cmd) # doesn't have this exact version
122
+ puts "~> Installing engineyard-serverside on #{server.hostname}"
123
+
124
+ system(Escape.shell_command([
125
+ 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
126
+ "-o", "StrictHostKeyChecking=no",
127
+ local_gem_file,
128
+ "#{config.user}@#{server.hostname}:#{remote_gem_file}",
129
+ ]))
130
+ server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
131
+ end
132
+ end
133
+ end))
134
+ end
135
+
136
+ private
137
+
138
+ def parse_instances(instance_strings)
139
+ instance_strings.map do |s|
140
+ tuple = s.split(/,/)
141
+ {:hostname => tuple[0], :role => tuple[1], :name => tuple[2]}
142
+ end
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,130 @@
1
+ require 'json'
2
+ require 'thor'
3
+
4
+ module EY
5
+ class Deploy::Configuration
6
+ DEFAULT_CONFIG = Thor::CoreExt::HashWithIndifferentAccess.new({
7
+ "branch" => "master",
8
+ "strategy" => "Git",
9
+ })
10
+
11
+ attr_reader :configuration
12
+ alias :c :configuration
13
+
14
+ attr_writer :release_path
15
+
16
+ def initialize(opts={})
17
+ @release_path = opts[:release_path]
18
+ config = JSON.parse(opts["config"] || "{}")
19
+ @configuration = DEFAULT_CONFIG.merge(config).merge(opts)
20
+ end
21
+
22
+ # Delegate to the configuration objects
23
+ def method_missing(meth, *args, &blk)
24
+ c.key?(meth.to_s) ? c[meth.to_s] : super
25
+ end
26
+
27
+ def respond_to?(meth, include_private=false)
28
+ c.key?(meth.to_s) ? true : super
29
+ end
30
+
31
+ def [](key)
32
+ if respond_to?(key.to_sym)
33
+ send(key.to_sym)
34
+ else
35
+ c[key]
36
+ end
37
+ end
38
+
39
+ def has_key?(key)
40
+ if respond_to?(key.to_sym)
41
+ true
42
+ else
43
+ c.has_key?(key)
44
+ end
45
+ end
46
+
47
+ def node
48
+ EY.node
49
+ end
50
+
51
+ def revision
52
+ IO.read(File.join(latest_release, 'REVISION'))
53
+ end
54
+
55
+ def repository_cache
56
+ configuration['repository_cache'] || File.join(deploy_to, "/shared/cached-copy")
57
+ end
58
+
59
+ def deploy_to
60
+ configuration['deploy_to'] || "/data/#{app}"
61
+ end
62
+
63
+ def migrate?
64
+ !!configuration['migrate']
65
+ end
66
+
67
+ def migration_command
68
+ configuration['migrate'] == "migrate" ? DEFAULT_CONFIG["migrate"] : configuration['migrate']
69
+ end
70
+
71
+ def user
72
+ ENV['USER']
73
+ end
74
+ alias :group :user
75
+
76
+ def role
77
+ node['instance_role']
78
+ end
79
+
80
+ def copy_exclude
81
+ @copy_exclude ||= Array(configuration.fetch("copy_exclude", []))
82
+ end
83
+
84
+ def environment
85
+ configuration['framework_env']
86
+ end
87
+
88
+ def latest_release
89
+ all_releases.last
90
+ end
91
+
92
+ def previous_release(current=latest_release)
93
+ index = all_releases.index(current)
94
+ all_releases[index-1]
95
+ end
96
+
97
+ def oldest_release
98
+ all_releases.first
99
+ end
100
+
101
+ def all_releases
102
+ Dir.glob("#{release_dir}/*").sort
103
+ end
104
+
105
+ def framework_envs
106
+ "RAILS_ENV=#{environment} RACK_ENV=#{environment} MERB_ENV=#{environment}"
107
+ end
108
+
109
+ def current_path
110
+ File.join(deploy_to, "current")
111
+ end
112
+
113
+ def shared_path
114
+ File.join(deploy_to, "shared")
115
+ end
116
+
117
+ def release_dir
118
+ File.join(deploy_to, "releases")
119
+ end
120
+
121
+ def release_path
122
+ @release_path ||= File.join(release_dir, Time.now.utc.strftime("%Y%m%d%H%M%S"))
123
+ end
124
+
125
+ def exclusions
126
+ copy_exclude.map { |e| %|--exclude="#{e}"| }.join(' ')
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html >
2
+ <html>
3
+ <head>
4
+ <title>Undergoing Maintenance</title>
5
+
6
+ <style type="text/css">
7
+ body{font-size: 75%;font-family: Helvetica,Arial,sans-serif;color:#333;background:#FDFDFD;}
8
+ .container{font-size:1.1em;width:700px;text-align:center;margin:80px auto;}
9
+ .message{background:#FFF;border:1px solid #EEE;padding:30px;}
10
+ h1{margin:0 0 0.5em;font-weight:normal;}
11
+ p{margin:0;line-height:1.75em;}
12
+ .foot{color:#999;margin-top:30px}
13
+ .foot a{color:#666;text-decoration:none}
14
+ </style>
15
+ </head>
16
+
17
+ <body>
18
+ <div class="container">
19
+ <div class="message">
20
+ <h1>This site is currently undergoing some maintenance.</h1>
21
+ <p>Customize this maintenance page by updating the file located at: /public/maintenance.html</p>
22
+ </div>
23
+ <div class="foot">
24
+ Hosted by <a href="http://www.engineyard.com">Engine Yard</a>
25
+ </div>
26
+ </div>
27
+ </body>
28
+
29
+ </html>
@@ -0,0 +1,321 @@
1
+ # stolen wholesale from capistrano, thanks Jamis!
2
+ require 'base64'
3
+ require 'fileutils'
4
+ require 'json'
5
+
6
+ module EY
7
+ class DeployBase < Task
8
+ include LoggedOutput
9
+
10
+ # default task
11
+ def deploy
12
+ debug "Starting deploy at #{Time.now.asctime}"
13
+ update_repository_cache
14
+ require_custom_tasks
15
+ push_code
16
+
17
+ info "~> Starting full deploy"
18
+ copy_repository_cache
19
+
20
+ with_failed_release_cleanup do
21
+ create_revision_file
22
+ bundle
23
+ symlink_configs
24
+ conditionally_enable_maintenance_page
25
+ run_with_callbacks(:migrate)
26
+ callback(:before_symlink)
27
+ symlink
28
+ end
29
+
30
+ callback(:after_symlink)
31
+ run_with_callbacks(:restart)
32
+ disable_maintenance_page
33
+
34
+ cleanup_old_releases
35
+ debug "Finished deploy at #{Time.now.asctime}"
36
+ rescue Exception
37
+ debug "Finished failing to deploy at #{Time.now.asctime}"
38
+ puts_deploy_failure
39
+ raise
40
+ end
41
+
42
+ def enable_maintenance_page
43
+ maintenance_page_candidates = [
44
+ "public/maintenance.html.custom",
45
+ "public/maintenance.html.tmp",
46
+ "public/maintenance.html",
47
+ "public/system/maintenance.html.default",
48
+ ].map do |file|
49
+ File.join(c.latest_release, file)
50
+ end
51
+
52
+ # this one is guaranteed to exist
53
+ maintenance_page_candidates << File.expand_path(
54
+ "default_maintenance_page.html",
55
+ File.dirname(__FILE__)
56
+ )
57
+
58
+ # put in the maintenance page
59
+ maintenance_file = maintenance_page_candidates.detect do |file|
60
+ File.exists?(file)
61
+ end
62
+
63
+ @maintenance_up = true
64
+ roles :app_master, :app, :solo do
65
+ visible_maint_page = File.join(c.shared_path, "system", "maintenance.html")
66
+ run "cp '#{maintenance_file}' '#{visible_maint_page}'"
67
+ end
68
+ end
69
+
70
+ def conditionally_enable_maintenance_page
71
+ if c.migrate? || c.stack == "nginx_mongrel"
72
+ enable_maintenance_page
73
+ end
74
+ end
75
+
76
+ def disable_maintenance_page
77
+ @maintenance_up = false
78
+ roles :app_master, :app, :solo do
79
+ run "rm -f #{File.join(c.shared_path, "system", "maintenance.html")}"
80
+ end
81
+ end
82
+
83
+ def run_with_callbacks(task, *args)
84
+ callback(:"before_#{task}")
85
+ send(task, *args)
86
+ callback(:"after_#{task}")
87
+ end
88
+
89
+ # task
90
+ def push_code
91
+ info "~> Pushing code to all servers"
92
+ barrier *(EY::Server.all.map do |server|
93
+ need_later { server.push_code }
94
+ end)
95
+ end
96
+
97
+ # task
98
+ def restart
99
+ @restart_failed = true
100
+ info "~> Restarting app servers"
101
+ roles :app_master, :app, :solo do
102
+ restart_command = case c.stack
103
+ when "nginx_unicorn"
104
+ pidfile = "/var/run/engineyard/unicorn_#{c.app}.pid"
105
+ condition = "[ -e #{pidfile} ] && [ ! -d /proc/`cat #{pidfile}` ]"
106
+ run("if #{condition}; then rm -f #{pidfile}; fi")
107
+ run("/engineyard/bin/app_#{c.app} deploy")
108
+ when "nginx_mongrel"
109
+ sudo("monit restart all -g #{c.app}")
110
+ when "nginx_passenger"
111
+ run("touch #{c.current_path}/tmp/restart.txt")
112
+ else
113
+ raise "Unknown stack #{c.stack}; restart failed!"
114
+ end
115
+ end
116
+ @restart_failed = false
117
+ end
118
+
119
+ # task
120
+ def bundle
121
+ if File.exist?("#{c.release_path}/Gemfile")
122
+ info "~> Gemfile detected, bundling gems"
123
+ lockfile = File.join(c.release_path, "Gemfile.lock")
124
+
125
+ bundler_installer = if File.exist?(lockfile)
126
+ get_bundler_installer(lockfile)
127
+ else
128
+ warn_about_missing_lockfile
129
+ BundleInstaller.new(DEFAULT_09_BUNDLER, "--without=development --without=test")
130
+ end
131
+
132
+ sudo "#{$0} _#{VERSION}_ install_bundler #{bundler_installer.version}"
133
+
134
+ run "cd #{c.release_path} && bundle _#{bundler_installer.version}_ install #{bundler_installer.options}"
135
+ end
136
+ end
137
+
138
+ # task
139
+ def cleanup_old_releases
140
+ @cleanup_failed = true
141
+ info "~> Cleaning up old releases"
142
+ sudo "ls #{c.release_dir} | head -n -3 | xargs -I{} rm -rf #{c.release_dir}/{}"
143
+ @cleanup_failed = false
144
+ end
145
+
146
+ # task
147
+ def rollback
148
+ if c.all_releases.size > 1
149
+ c.release_path = c.previous_release
150
+
151
+ revision = File.read(File.join(c.release_path, 'REVISION')).strip
152
+ info "~> Rolling back to previous release: #{short_log_message(revision)}"
153
+
154
+ run_with_callbacks(:symlink, c.previous_release)
155
+ cleanup_current_release
156
+ bundle
157
+ info "~> Restarting with previous release"
158
+ with_maintenance_page { run_with_callbacks(:restart) }
159
+ else
160
+ info "~> Already at oldest release, nothing to roll back to"
161
+ exit(1)
162
+ end
163
+ end
164
+
165
+ # task
166
+ def migrate
167
+ return unless c.migrate?
168
+ @migrations_reached = true
169
+ roles :app_master, :solo do
170
+ cmd = "cd #{c.release_path} && #{c.framework_envs} #{c.migration_command}"
171
+ info "~> Migrating: #{cmd}"
172
+ run(cmd)
173
+ end
174
+ end
175
+
176
+ # task
177
+ def copy_repository_cache
178
+ info "~> Copying to #{c.release_path}"
179
+ run("mkdir -p #{c.release_path} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
180
+
181
+ info "~> Ensuring proper ownership"
182
+ sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
183
+ end
184
+
185
+ def create_revision_file
186
+ run create_revision_file_command
187
+ end
188
+
189
+ def symlink_configs(release_to_link=c.release_path)
190
+ info "~> Symlinking configs"
191
+ [ "chmod -R g+w #{release_to_link}",
192
+ "rm -rf #{release_to_link}/log #{release_to_link}/public/system #{release_to_link}/tmp/pids",
193
+ "mkdir -p #{release_to_link}/tmp",
194
+ "ln -nfs #{c.shared_path}/log #{release_to_link}/log",
195
+ "mkdir -p #{release_to_link}/public",
196
+ "mkdir -p #{release_to_link}/config",
197
+ "ln -nfs #{c.shared_path}/system #{release_to_link}/public/system",
198
+ "ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids",
199
+ "ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml",
200
+ "ln -nfs #{c.shared_path}/config/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml",
201
+ ].each do |cmd|
202
+ run cmd
203
+ end
204
+
205
+ sudo "chown -R #{c.user}:#{c.group} #{release_to_link}"
206
+ run "if [ -f \"#{c.shared_path}/config/newrelic.yml\" ]; then ln -nfs #{c.shared_path}/config/newrelic.yml #{release_to_link}/config/newrelic.yml; fi"
207
+ end
208
+
209
+ # task
210
+ def symlink(release_to_link=c.release_path)
211
+ info "~> Symlinking code"
212
+ run "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
213
+ @symlink_changed = true
214
+ rescue Exception
215
+ sudo "rm -f #{c.current_path} && ln -nfs #{c.previous_release(release_to_link)} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
216
+ @symlink_changed = false
217
+ raise
218
+ end
219
+
220
+ def callback(what)
221
+ @callbacks_reached ||= true
222
+ if File.exist?("#{c.release_path}/deploy/#{what}.rb")
223
+ eydeploy_path = $0 # invoke others just like we were invoked
224
+ run "#{eydeploy_path} _#{VERSION}_ hook '#{what}' --app '#{config.app}' --release-path #{config.release_path}" do |server, cmd|
225
+ cmd << " --framework-env '#{c.environment}'"
226
+ cmd << " --current-role '#{server.role}'"
227
+ cmd << " --current-name '#{server.name}'" if server.name
228
+ cmd << " --config '#{c[:config]}'" if c.has_key?(:config)
229
+ cmd
230
+ end
231
+ end
232
+ end
233
+
234
+ protected
235
+
236
+ def puts_deploy_failure
237
+ if @cleanup_failed
238
+ info "~> [Relax] Your site is running new code, but cleaning up old deploys failed"
239
+ elsif @maintenance_up
240
+ info "~> [Attention] Maintenance page still up, consider the following before removing:"
241
+ info " * any deploy hooks ran, be careful if they were destructive" if @callbacks_reached
242
+ info " * any migrations ran, be careful if they were destructive" if @migrations_reached
243
+ if @symlink_changed
244
+ info " * your new code is symlinked as current"
245
+ else
246
+ info " * your old code is still symlinked as current"
247
+ end
248
+ info " * application servers failed to restart" if @restart_failed
249
+ else
250
+ info "~> [Relax] Your site is still running old code and nothing destructive could have occurred"
251
+ end
252
+ end
253
+
254
+ def with_maintenance_page
255
+ conditionally_enable_maintenance_page
256
+ yield if block_given?
257
+ disable_maintenance_page
258
+ end
259
+
260
+ def with_failed_release_cleanup
261
+ yield
262
+ rescue Exception
263
+ cleanup_current_release
264
+ raise
265
+ end
266
+
267
+ def cleanup_current_release
268
+ sudo "rm -rf #{c.release_path}"
269
+ end
270
+
271
+ DEFAULT_09_BUNDLER = '0.9.26'
272
+ DEFAULT_10_BUNDLER = '1.0.0.rc.3'
273
+
274
+ def warn_about_missing_lockfile
275
+ info "!>"
276
+ info "!> WARNING: Gemfile.lock is missing!"
277
+ info "!> You can get different gems in production than what you tested with."
278
+ info "!> You can get different gems on every deployment even if your Gemfile hasn't changed."
279
+ info "!> Deploying may take a long time."
280
+ info "!>"
281
+ info "!> Fix this by running \"git add Gemfile.lock; git commit\" and deploying again."
282
+ info "!> If you don't have a Gemfile.lock, run \"bundle lock\" to create one."
283
+ info "!>"
284
+ info "!> This deployment will use bundler #{DEFAULT_09_BUNDLER} to run 'bundle install'."
285
+ info "!>"
286
+ end
287
+
288
+ def get_bundler_installer(lockfile)
289
+ parser = LockfileParser.new(File.read(lockfile))
290
+ case parser.lockfile_version
291
+ when :bundler09
292
+ BundleInstaller.new(
293
+ parser.bundler_version || DEFAULT_09_BUNDLER,
294
+ "--without=development --without=test")
295
+ when :bundler10
296
+ BundleInstaller.new(
297
+ parser.bundler_version || DEFAULT_10_BUNDLER,
298
+ "--deployment --path #{c.shared_path}/bundled_gems --without development test"
299
+ )
300
+ else
301
+ raise "Unknown lockfile version #{parser.lockfile_version}"
302
+ end
303
+ end
304
+ public :get_bundler_installer
305
+
306
+ end # DeployBase
307
+
308
+ class Deploy < DeployBase
309
+ def self.new(opts={})
310
+ # include the correct fetch strategy
311
+ include EY::Strategies.const_get(opts.strategy)::Helpers
312
+ super
313
+ end
314
+
315
+ def self.run(opts={})
316
+ conf = EY::Deploy::Configuration.new(opts)
317
+ EY::Server.config = conf
318
+ new(conf).send(opts["default_task"])
319
+ end
320
+ end
321
+ end