engineyard-serverside 1.4.3.nodestack → 1.4.7.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/lib/core-ext/README.md +3 -0
  2. data/lib/core-ext/string.rb +9 -0
  3. data/lib/engineyard-serverside.rb +3 -1
  4. data/lib/engineyard-serverside/cli.rb +3 -2
  5. data/lib/engineyard-serverside/configuration.rb +1 -1
  6. data/lib/engineyard-serverside/deploy.rb +117 -23
  7. data/lib/engineyard-serverside/lockfile_parser.rb +3 -3
  8. data/lib/engineyard-serverside/logged_output.rb +0 -5
  9. data/lib/engineyard-serverside/task.rb +1 -0
  10. data/lib/engineyard-serverside/version.rb +1 -1
  11. data/lib/vendor/ruby_1.8.6_openssl.patch +7 -0
  12. data/spec/custom_deploy_spec.rb +14 -7
  13. data/spec/fixtures/gemfiles/activerecord_jdbcmysql/Gemfile +5 -0
  14. data/spec/fixtures/gemfiles/activerecord_jdbcmysql/Gemfile.lock +29 -0
  15. data/spec/fixtures/gemfiles/activerecord_jdbcpostgresql/Gemfile +5 -0
  16. data/spec/fixtures/gemfiles/activerecord_jdbcpostgresql/Gemfile.lock +29 -0
  17. data/spec/fixtures/gemfiles/activerecord_mysql/Gemfile +5 -0
  18. data/spec/fixtures/gemfiles/activerecord_mysql/Gemfile.lock +25 -0
  19. data/spec/fixtures/gemfiles/activerecord_mysql2/Gemfile +5 -0
  20. data/spec/fixtures/gemfiles/activerecord_mysql2/Gemfile.lock +25 -0
  21. data/spec/fixtures/gemfiles/activerecord_pg/Gemfile +5 -0
  22. data/spec/fixtures/gemfiles/activerecord_pg/Gemfile.lock +25 -0
  23. data/spec/fixtures/gemfiles/activerecord_sqlite3/Gemfile +5 -0
  24. data/spec/fixtures/gemfiles/activerecord_sqlite3/Gemfile.lock +25 -0
  25. data/spec/fixtures/gemfiles/diy_database_yml/Gemfile +5 -0
  26. data/spec/fixtures/gemfiles/diy_database_yml/Gemfile.lock +25 -0
  27. data/spec/fixtures/gemfiles/diy_database_yml/config/database.yml +7 -0
  28. data/spec/generate_configs_spec.rb +228 -0
  29. data/spec/lib/full_test_deploy.rb +86 -0
  30. data/spec/real_deploy_spec.rb +42 -121
  31. data/spec/spec_helper.rb +62 -1
  32. metadata +75 -9
  33. data/spec/fixtures/gitrepo/bar +0 -0
@@ -0,0 +1,3 @@
1
+ # methods for core Ruby classes that aren't available in ruby 1.8.6 (used by ey_resin)
2
+ # versions here are just workarounds
3
+ # FIXME remove this folder when ey_resin (and .rvmrc) updated to ruby 1.8.7 or 1.9.2
@@ -0,0 +1,9 @@
1
+ # methods for String that aren't available in ruby 1.8.6 (used by ey_resin)
2
+ # versions here are just workarounds
3
+ # FIXME remove this module when ey_resin (and .rvmrc) updated to ruby 1.8.7 or 1.9.2
4
+ module ModernString
5
+ def start_with?(prefix)
6
+ self.index(prefix) == 0
7
+ end
8
+ end
9
+ String.send(:include, ModernString)
@@ -4,6 +4,8 @@ $LOAD_PATH.unshift File.expand_path('vendor/escape/lib', File.dirname(__FILE__))
4
4
  $LOAD_PATH.unshift File.expand_path('vendor/json_pure/lib', File.dirname(__FILE__))
5
5
  $LOAD_PATH.unshift File.expand_path('vendor/dataflow', File.dirname(__FILE__))
6
6
 
7
+ require 'core-ext/string' if RUBY_VERSION == '1.8.6'
8
+
7
9
  require 'escape'
8
10
  require 'json'
9
11
  require 'dataflow'
@@ -34,7 +36,7 @@ module EY
34
36
  {}.to_json
35
37
  end
36
38
  end
37
-
39
+
38
40
  RemoteFailure = Class.new StandardError
39
41
 
40
42
  private
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require 'pathname'
3
+ require 'tmpdir'
3
4
 
4
5
  module EY
5
6
  module Serverside
@@ -214,9 +215,9 @@ module EY
214
215
  def propagate
215
216
  config = EY::Serverside::Deploy::Configuration.new
216
217
  gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
217
- local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
218
+ local_gem_file = File.join(::Gem.dir, 'cache', gem_filename)
218
219
  remote_gem_file = File.join(Dir.tmpdir, gem_filename)
219
- gem_binary = File.join(Gem.default_bindir, 'gem')
220
+ gem_binary = File.join(::Gem.default_bindir, 'gem')
220
221
 
221
222
  barrier(*(EY::Serverside::Server.all.find_all do |server|
222
223
  !server.local? # of course this machine has it
@@ -119,7 +119,7 @@ module EY
119
119
  end
120
120
 
121
121
  def framework_envs
122
- "RAILS_ENV=#{environment} RACK_ENV=#{environment} APP_ENV=#{environment} MERB_ENV=#{environment}"
122
+ "RAILS_ENV=#{environment} RACK_ENV=#{environment} MERB_ENV=#{environment}"
123
123
  end
124
124
 
125
125
  def current_path
@@ -27,6 +27,7 @@ module EY
27
27
  create_revision_file
28
28
  run_with_callbacks(:bundle)
29
29
  symlink_configs
30
+ generate_configs
30
31
  conditionally_enable_maintenance_page
31
32
  run_with_callbacks(:migrate)
32
33
  callback(:before_symlink)
@@ -136,8 +137,9 @@ module EY
136
137
  bundler_installer = if File.exist?(lockfile)
137
138
  get_bundler_installer(lockfile)
138
139
  else
139
- warn_about_missing_lockfile
140
- bundler_09_installer(default_09_bundler)
140
+ missing_lock_version = EY::Serverside::LockfileParser::Parse10::DEFAULT
141
+ warn_about_missing_lockfile missing_lock_version
142
+ bundler_10_installer missing_lock_version
141
143
  end
142
144
 
143
145
  sudo "#{$0} _#{EY::Serverside::VERSION}_ install_bundler #{bundler_installer.version}"
@@ -164,15 +166,6 @@ module EY
164
166
 
165
167
  run "mkdir -p #{bundled_gems_path} && ruby -v > #{ruby_version_file} && uname -m > #{system_version_file}"
166
168
  end
167
- if File.exist?("#{c.release_path}/package.json")
168
- unless `which npm` =~ /npm/
169
- error "~> package.json detected, BUT npm not installed"
170
- else
171
- info "~> package.json detected, installing npm packages"
172
-
173
- run "cd #{c.release_path} && npm install"
174
- end
175
- end
176
169
  end
177
170
 
178
171
  # task
@@ -227,6 +220,7 @@ module EY
227
220
  run create_revision_file_command
228
221
  end
229
222
 
223
+ # symlink to shared path; may be overridden by #generate_configs
230
224
  def symlink_configs(release_to_link=c.release_path)
231
225
  info "~> Symlinking configs"
232
226
  [ "chmod -R g+w #{release_to_link}",
@@ -237,8 +231,8 @@ module EY
237
231
  "mkdir -p #{release_to_link}/config",
238
232
  "ln -nfs #{c.shared_path}/system #{release_to_link}/public/system",
239
233
  "ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids",
240
- "find #{c.shared_path}/config -type f -exec ln -s {} #{release_to_link}/config \\;",
241
- "ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml",
234
+ "find #{c.shared_path}/config ! -name 'database.yml*' -type f -exec ln -s {} #{release_to_link}/config \\;",
235
+ # database.yml generated or symlink created in #generate_database_yml
242
236
  "ln -nfs #{c.shared_path}/config/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml",
243
237
  ].each do |cmd|
244
238
  run cmd
@@ -248,6 +242,84 @@ module EY
248
242
  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"
249
243
  end
250
244
 
245
+ def generate_configs(release_to_link=c.release_path)
246
+ generate_database_yml(release_to_link)
247
+ end
248
+
249
+ # Do nothing if there is no Gemfile.lock to determine what ORM gems are being used
250
+ # (falls back to using the symlinked shared/database.yml file)
251
+ def generate_database_yml(release_to_link)
252
+ return if keep_database_yml?(release_to_link)
253
+ if config["db_adapter"] || File.exist?("#{c.release_path}/Gemfile.lock")
254
+ info "~> Generating database.yml from Gemfile.lock"
255
+ database_yml = "#{release_to_link}/config/database.yml"
256
+ node = EY::Serverside.node
257
+ node_app = node["engineyard"]["environment"]["apps"].find { |app| app['name'] == c['app'] }
258
+ abort("Invalid application name for target environment: #{c['app']}") unless node_app
259
+
260
+ db_stack_name = node[:engineyard][:environment][:db_stack_name]
261
+ instances = node[:engineyard][:environment][:instances]
262
+ db_master = {"public_hostname" => "localhost"} # you know, just in case
263
+ db_slaves = []
264
+ instances.each do |i|
265
+ case i['role']
266
+ when 'db_master', 'solo'
267
+ db_master = i
268
+ when 'db_slave'
269
+ db_slaves << i
270
+ end
271
+ end
272
+ db_host = db_master["public_hostname"]
273
+ db_slaves_hosts = db_slaves.map {|slave| slave["public_hostname"]}
274
+
275
+ if config["db_adapter"]
276
+ adapter = config["db_adapter"]
277
+ elsif bundler_gems_include?("mysql2")
278
+ adapter = "mysql2"
279
+ elsif bundler_gems_include?("mysql")
280
+ adapter = "mysql"
281
+ elsif bundler_gems_include?("pg")
282
+ adapter = "postgresql"
283
+ elsif bundler_gems_include?("jdbc-mysql")
284
+ adapter = "mysql"
285
+ elsif bundler_gems_include?("jdbc-postgres")
286
+ adapter = "postgresql"
287
+ elsif db_stack_name && db_stack_name =~ /postgres/
288
+ adapter = "postgresql"
289
+ else
290
+ adapter = "mysql"
291
+ end
292
+
293
+ File.open(database_yml, "w") do |file|
294
+ contents = <<-EOS.gsub(/^\s{12}/, '')
295
+ #{node[:engineyard][:environment][:framework_env]}:
296
+ adapter: #{adapter}
297
+ database: #{node_app[:database_name]}
298
+ username: #{node[:engineyard][:environment][:ssh_username]}
299
+ password: #{node[:engineyard][:environment][:ssh_password]}
300
+ host: #{db_host}
301
+ reconnect: true
302
+ EOS
303
+ db_slaves_hosts.each_with_index do |host, n|
304
+ slave_name = n.zero? ? "slave" : "slave_#{n}"
305
+ contents << <<-EOS.gsub(/^\s{14}/, '')
306
+ #{slave_name}:
307
+ adapter: #{adapter}
308
+ database: #{node_app[:database_name]}
309
+ username: #{node[:engineyard][:environment][:ssh_username]}
310
+ password: #{node[:engineyard][:environment][:ssh_password]}
311
+ host: #{host}
312
+ reconnect: true
313
+ EOS
314
+ end
315
+ file << contents
316
+ end
317
+ else
318
+ info "~> Symlinking database.yml config"
319
+ run "ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml"
320
+ end
321
+ end
322
+
251
323
  # task
252
324
  def symlink(release_to_link=c.release_path)
253
325
  info "~> Symlinking code"
@@ -319,7 +391,7 @@ module EY
319
391
  raise
320
392
  end
321
393
 
322
- def warn_about_missing_lockfile
394
+ def warn_about_missing_lockfile(missing_lock_version)
323
395
  info "!>"
324
396
  info "!> WARNING: Gemfile.lock is missing!"
325
397
  info "!> You can get different gems in production than what you tested with."
@@ -329,30 +401,52 @@ module EY
329
401
  info "!> Fix this by running \"git add Gemfile.lock; git commit\" and deploying again."
330
402
  info "!> If you don't have a Gemfile.lock, run \"bundle lock\" to create one."
331
403
  info "!>"
332
- info "!> This deployment will use bundler #{default_09_bundler} to run 'bundle install'."
404
+ info "!> This deployment will use bundler #{missing_lock_version} to run 'bundle install'."
333
405
  info "!>"
334
406
  end
335
407
 
336
- def get_bundler_installer(lockfile)
408
+ def keep_database_yml?(release_to_link=c.release_path)
409
+ File.exists?(File.join(release_to_link, "config", "keep.database.yml"))
410
+ end
411
+
412
+ # returns true if "bundle list" includes all gems requested
413
+ def bundler_gems_include?(*gems)
414
+ lockfile = File.join(c.release_path, "Gemfile.lock")
415
+ @lockfile_contents ||= File.read(lockfile)
416
+
417
+ # Parsing Gemfile.lock which looks like
418
+ # GEM
419
+ # remote: http://rubygems.org/
420
+ # specs:
421
+ # activemodel (3.0.10)
422
+ # activesupport (= 3.0.10)
423
+ # builder (~> 2.1.2)
424
+ # i18n (~> 0.5.0)
425
+ #
426
+ gems.inject(true) {|found_all, gem| found_all && (@lockfile_contents =~ / #{gem} \(/)}
427
+ end
428
+
429
+ def get_bundler_installer(lockfile, options = '')
337
430
  parser = LockfileParser.new(File.read(lockfile))
338
431
  case parser.lockfile_version
339
432
  when :bundler09
340
- bundler_09_installer(parser.bundler_version)
433
+ bundler_09_installer(parser.bundler_version, options)
341
434
  when :bundler10
342
- bundler_10_installer(parser.bundler_version)
435
+ bundler_10_installer(parser.bundler_version, options)
343
436
  else
344
437
  raise "Unknown lockfile version #{parser.lockfile_version}"
345
438
  end
346
439
  end
347
440
  public :get_bundler_installer
348
441
 
349
- def bundler_09_installer(version)
350
- BundleInstaller.new(version, '--without=development --without=test')
442
+ def bundler_09_installer(version, options = '')
443
+ default_options = '--without=development --without=test'
444
+ BundleInstaller.new(version, default_options + options)
351
445
  end
352
446
 
353
- def bundler_10_installer(version)
354
- BundleInstaller.new(version,
355
- "--deployment --path #{c.shared_path}/bundled_gems --binstubs #{c.binstubs_path} --without development test")
447
+ def bundler_10_installer(version, options = '')
448
+ default_options = "--deployment --path #{c.shared_path}/bundled_gems --binstubs #{c.binstubs_path} --without development test"
449
+ BundleInstaller.new(version, default_options + options)
356
450
  end
357
451
  end # DeployBase
358
452
 
@@ -79,11 +79,11 @@ module EY
79
79
  when '='
80
80
  bundler_version
81
81
  when '>='
82
- Gem::Version.new(bundler_version) > Gem::Version.new(DEFAULT) ? bundler_version : DEFAULT
82
+ ::Gem::Version.new(bundler_version) > ::Gem::Version.new(DEFAULT) ? bundler_version : DEFAULT
83
83
  when '~>'
84
- bundler_gem_version = Gem::Version.new(bundler_version)
84
+ bundler_gem_version = ::Gem::Version.new(bundler_version)
85
85
  recommendation = bundler_gem_version.spermy_recommendation.gsub(/~>\s*(.+)$/, '\1.')
86
- DEFAULT.start_with?(recommendation) && Gem::Version.new(DEFAULT) > bundler_gem_version ? DEFAULT : bundler_version
86
+ DEFAULT.start_with?(recommendation) && ::Gem::Version.new(DEFAULT) > bundler_gem_version ? DEFAULT : bundler_version
87
87
  end
88
88
  end
89
89
 
@@ -41,11 +41,6 @@ module EY
41
41
  EY::Serverside::LoggedOutput.verbose?
42
42
  end
43
43
 
44
- # TODO color output
45
- def error(msg)
46
- info(msg)
47
- end
48
-
49
44
  def info(msg)
50
45
  with_logfile do |log|
51
46
  Tee.new($stdout, log) << (msg + "\n")
@@ -54,6 +54,7 @@ module EY
54
54
  need_later { server.run(Escape.shell_command(wrapper + [to_run])) }
55
55
  end
56
56
  barrier *results
57
+
57
58
  # MRI's truthiness check is an internal C thing that does not call
58
59
  # any methods... so Dataflow cannot proxy it & we must "x == true"
59
60
  # Rubinius, wherefore art thou!?
@@ -1,5 +1,5 @@
1
1
  module EY
2
2
  module Serverside
3
- VERSION = '1.4.3.nodestack'
3
+ VERSION = '1.4.7.pre'
4
4
  end
5
5
  end
@@ -0,0 +1,7 @@
1
+ diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h
2
+ index 2ebf61a..8a0202a 100644
3
+ --- a/ext/openssl/openssl_missing.h
4
+ +++ b/ext/openssl/openssl_missing.h
5
+ @@ -123,2 +122,0 @@ int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_
6
+ -int BN_rand_range(BIGNUM *r, BIGNUM *range);
7
+ -int BN_pseudo_rand_range(BIGNUM *r, BIGNUM *range);
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'fileutils'
2
3
 
3
4
  describe "the EY::Serverside::Deploy API" do
4
5
  it "calls tasks in the right order" do
@@ -29,9 +30,12 @@ describe "the EY::Serverside::Deploy API" do
29
30
  def cleanup_old_releases() @call_order << 'cleanup_old_releases' end
30
31
  def conditionally_enable_maintenance_page() @call_order << 'conditionally_enable_maintenance_page' end
31
32
  def disable_maintenance_page() @call_order << 'disable_maintenance_page' end
33
+ def generate_database_yml(path) @call_order << 'generate_database_yml' end
32
34
  end
33
35
 
34
- td = TestDeploy.new(EY::Serverside::Deploy::Configuration.new)
36
+ setup_dna_json
37
+
38
+ td = TestDeploy.new(EY::Serverside::Deploy::Configuration.new('app' => 'myfirstapp'))
35
39
  td.deploy
36
40
  td.call_order.should == %w(
37
41
  push_code
@@ -39,6 +43,7 @@ describe "the EY::Serverside::Deploy API" do
39
43
  create_revision_file
40
44
  bundle
41
45
  symlink_configs
46
+ generate_database_yml
42
47
  conditionally_enable_maintenance_page
43
48
  migrate
44
49
  symlink
@@ -53,15 +58,17 @@ describe "the EY::Serverside::Deploy API" do
53
58
  end
54
59
 
55
60
  before(:each) do
56
- @tempdir = `mktemp -d -t custom_deploy_spec.XXXXX`.strip
57
- @config = EY::Serverside::Deploy::Configuration.new('repository_cache' => @tempdir)
58
- @deploy = TestQuietDeploy.new(@config)
61
+ @tempdir = File.join(Dir.tmpdir, "serverside-deploy-#{Time.now.to_i}-#{$$}")
62
+ @config = EY::Serverside::Deploy::Configuration.new('repository_cache' => @tempdir)
63
+ @deploy = TestQuietDeploy.new(@config)
64
+ end
65
+
66
+ after do
67
+ FileUtils.rm_rf(@tempdir)
59
68
  end
60
69
 
61
70
  def write_eydeploy(relative_path, contents = "def got_new_methods() 'from the file on disk' end")
62
- FileUtils.mkdir_p(File.join(
63
- @tempdir,
64
- File.dirname(relative_path)))
71
+ FileUtils.mkdir_p(File.join(@tempdir, File.dirname(relative_path)))
65
72
 
66
73
  File.open(File.join(@tempdir, relative_path), 'w') do |f|
67
74
  f.write contents
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "activerecord"
5
+ gem "activerecord-jdbcmysql-adapter"
@@ -0,0 +1,29 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.10)
5
+ activesupport (= 3.0.10)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activerecord (3.0.10)
9
+ activemodel (= 3.0.10)
10
+ activesupport (= 3.0.10)
11
+ arel (~> 2.0.10)
12
+ tzinfo (~> 0.3.23)
13
+ activerecord-jdbc-adapter (1.1.3)
14
+ activerecord-jdbcmysql-adapter (1.1.3)
15
+ activerecord-jdbc-adapter (= 1.1.3)
16
+ jdbc-mysql (~> 5.1.0)
17
+ activesupport (3.0.10)
18
+ arel (2.0.10)
19
+ builder (2.1.2)
20
+ i18n (0.5.0)
21
+ jdbc-mysql (5.1.13)
22
+ tzinfo (0.3.29)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ activerecord
29
+ activerecord-jdbcmysql-adapter
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "activerecord"
5
+ gem "activerecord-jdbcpostgresql-adapter"
@@ -0,0 +1,29 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.10)
5
+ activesupport (= 3.0.10)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activerecord (3.0.10)
9
+ activemodel (= 3.0.10)
10
+ activesupport (= 3.0.10)
11
+ arel (~> 2.0.10)
12
+ tzinfo (~> 0.3.23)
13
+ activerecord-jdbc-adapter (1.1.3)
14
+ activerecord-jdbcpostgresql-adapter (1.1.3)
15
+ activerecord-jdbc-adapter (= 1.1.3)
16
+ jdbc-postgres (~> 9.0.0)
17
+ activesupport (3.0.10)
18
+ arel (2.0.10)
19
+ builder (2.1.2)
20
+ i18n (0.5.0)
21
+ jdbc-postgres (9.0.801)
22
+ tzinfo (0.3.29)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ activerecord
29
+ activerecord-jdbcpostgresql-adapter