engineyard-serverside 1.5.28.pre.timestamps4 → 1.5.28

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.
@@ -26,8 +26,6 @@ require 'engineyard-serverside/cli'
26
26
  require 'engineyard-serverside/configuration'
27
27
  require 'engineyard-serverside/deprecation'
28
28
  require 'engineyard-serverside/future'
29
- require 'engineyard-serverside/shell'
30
-
31
29
 
32
30
  module EY
33
31
  module Serverside
@@ -51,12 +51,14 @@ module EY
51
51
  desc "deploy", "Deploy code from /data/<app>"
52
52
  def deploy(default_task=:deploy)
53
53
  config = EY::Serverside::Deploy::Configuration.new(options)
54
- shell = init_shell(config.verbose, "#{config.app}-deploy")
55
- load_servers(config)
54
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
55
+
56
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
57
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
56
58
 
57
- propagate(shell)
59
+ invoke :propagate
58
60
 
59
- EY::Serverside::Deploy.new(config, shell).send(default_task)
61
+ EY::Serverside::Deploy.new(config).send(default_task)
60
62
  end
61
63
 
62
64
  method_option :app, :type => :string,
@@ -118,6 +120,9 @@ module EY
118
120
  :aliases => ["-v"]
119
121
  desc "integrate", "Integrate other instances into this cluster"
120
122
  def integrate
123
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
124
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-integrate.log")
125
+
121
126
  app_dir = Pathname.new "/data/#{options[:app]}"
122
127
  current_app_dir = app_dir + "current"
123
128
 
@@ -129,23 +134,22 @@ module EY
129
134
  integrate_options[:branch] = (current_app_dir + 'REVISION').read.strip
130
135
 
131
136
  config = EY::Serverside::Deploy::Configuration.new(integrate_options)
132
- shell = init_shell(config.verbose, "#{config.app}-integrate")
133
137
 
134
- load_servers(config)
138
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
135
139
 
136
- propagate(shell)
140
+ invoke :propagate
137
141
 
138
142
  EY::Serverside::Server.all.each do |server|
139
- server.sync_directory(app_dir) { |cmd| shell.logged_system(cmd) }
143
+ server.sync_directory app_dir
140
144
  # we're just about to recreate this, so it has to be gone
141
145
  # first. otherwise, non-idempotent deploy hooks could screw
142
146
  # things up, and since we don't control deploy hooks, we must
143
147
  # assume the worst.
144
- run_on_server(server,"rm -rf #{current_app_dir}")
148
+ server.run("rm -rf #{current_app_dir}")
145
149
  end
146
150
 
147
151
  # deploy local-ref to other instances into /data/$app/local-current
148
- EY::Serverside::Deploy.new(config, shell).cached_deploy
152
+ EY::Serverside::Deploy.new(config).cached_deploy
149
153
  end
150
154
 
151
155
  method_option :app, :type => :string,
@@ -173,13 +177,15 @@ module EY
173
177
  :aliases => ["-v"]
174
178
  desc "restart", "Restart app servers, conditionally enabling maintenance page"
175
179
  def restart
180
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
181
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-restart.log")
182
+
176
183
  config = EY::Serverside::Deploy::Configuration.new(options)
177
- shell = init_shell(config.verbose, "#{config.app}-restart")
178
- load_servers(config)
184
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
179
185
 
180
- propagate(shell)
186
+ invoke :propagate
181
187
 
182
- EY::Serverside::Deploy.new(config, shell).restart_with_maintenance_page
188
+ EY::Serverside::Deploy.new(config).restart_with_maintenance_page
183
189
  end
184
190
 
185
191
  desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
@@ -197,10 +203,8 @@ module EY
197
203
  end
198
204
  end
199
205
 
200
- private
201
-
202
- # Put the same engineyard-serverside on all the servers (Used to be public but is unused as an actual CLI command now)
203
- def propagate(shell)
206
+ desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{EY::Serverside::VERSION}."
207
+ def propagate
204
208
  config = EY::Serverside::Deploy::Configuration.new
205
209
  gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
206
210
  local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
@@ -215,36 +219,23 @@ module EY
215
219
  # 0.5.11, and mistakenly thinking 0.5.1 is there
216
220
  has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
217
221
 
218
- if !run_on_server(server,has_gem_cmd) # doesn't have this exact version
219
- shell.status "Installing engineyard-serverside on #{server.hostname}"
222
+ if !server.run(has_gem_cmd) # doesn't have this exact version
223
+ puts "~> Installing engineyard-serverside on #{server.hostname}"
220
224
 
221
- shell.logged_system(Escape.shell_command([
225
+ system(Escape.shell_command([
222
226
  'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
223
227
  "-o", "StrictHostKeyChecking=no",
224
228
  local_gem_file,
225
229
  "#{config.user}@#{server.hostname}:#{remote_gem_file}",
226
230
  ]))
227
- run_on_server(server,"sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
231
+ server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
228
232
  end
229
233
  end
230
234
 
231
235
  EY::Serverside::Future.success?(futures)
232
236
  end
233
237
 
234
- def init_shell(verbose, log_basename)
235
- EY::Serverside::Shell.new(
236
- :verbose => verbose,
237
- :log_path => File.join(ENV['HOME'], "#{log_basename}.log")
238
- )
239
- end
240
-
241
- def load_servers(config)
242
- EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
243
- end
244
-
245
- def run_on_server(server, command)
246
- server.run(command) { |cmd| shell.logged_system(cmd) }
247
- end
238
+ private
248
239
 
249
240
  def assemble_instance_hashes(config)
250
241
  options[:instances].collect { |hostname|
@@ -55,10 +55,6 @@ module EY
55
55
  EY::Serverside.node
56
56
  end
57
57
 
58
- def verbose
59
- configuration['verbose']
60
- end
61
-
62
58
  def app
63
59
  configuration['app'].to_s
64
60
  end
@@ -7,21 +7,22 @@ require 'engineyard-serverside/rails_asset_support'
7
7
  module EY
8
8
  module Serverside
9
9
  class DeployBase < Task
10
+ include LoggedOutput
10
11
  include ::EY::Serverside::RailsAssetSupport
11
12
 
12
13
  # default task
13
14
  def deploy
14
- shell.status "Starting deploy at #{shell.start_time.asctime}"
15
+ debug "Starting deploy at #{Time.now.asctime}"
15
16
  update_repository_cache
16
17
  cached_deploy
17
18
  end
18
19
 
19
20
  def cached_deploy
20
- shell.status "Deploying app from cached copy at #{Time.now.asctime}"
21
+ debug "Deploying app from cached copy at #{Time.now.asctime}"
21
22
  require_custom_tasks
22
23
  push_code
23
24
 
24
- shell.status "Starting full deploy"
25
+ info "~> Starting full deploy"
25
26
  copy_repository_cache
26
27
  check_repository
27
28
 
@@ -46,9 +47,9 @@ module EY
46
47
  disable_maintenance_page
47
48
 
48
49
  cleanup_old_releases
49
- shell.status "Finished deploy at #{Time.now.asctime}"
50
+ debug "Finished deploy at #{Time.now.asctime}"
50
51
  rescue Exception
51
- shell.status "Finished failing to deploy at #{Time.now.asctime}"
52
+ debug "Finished failing to deploy at #{Time.now.asctime}"
52
53
  puts_deploy_failure
53
54
  raise
54
55
  end
@@ -72,9 +73,9 @@ module EY
72
73
 
73
74
  def check_repository
74
75
  if gemfile?
75
- shell.status "Gemfile found."
76
+ info "~> Gemfile found."
76
77
  if lockfile
77
- shell.status "Gemfile.lock found."
78
+ info "~> Gemfile.lock found."
78
79
  unless lockfile.any_database_adapter?
79
80
  warning <<-WARN
80
81
  Gemfile.lock does not contain a recognized database adapter.
@@ -96,7 +97,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
96
97
  WARN
97
98
  end
98
99
  else
99
- shell.status "No Gemfile. Deploying without bundler support."
100
+ info "~> No Gemfile. Deploying without bundler support."
100
101
  end
101
102
  end
102
103
 
@@ -162,9 +163,9 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
162
163
 
163
164
  # task
164
165
  def push_code
165
- shell.status "Pushing code to all servers"
166
+ info "~> Pushing code to all servers"
166
167
  futures = EY::Serverside::Future.call(EY::Serverside::Server.all) do |server|
167
- server.sync_directory(config.repository_cache) { |cmd| shell.logged_system(cmd) }
168
+ server.sync_directory(config.repository_cache)
168
169
  end
169
170
  EY::Serverside::Future.success?(futures)
170
171
  end
@@ -172,7 +173,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
172
173
  # task
173
174
  def restart
174
175
  @restart_failed = true
175
- shell.status "Restarting app servers"
176
+ info "~> Restarting app servers"
176
177
  roles :app_master, :app, :solo do
177
178
  run(restart_command)
178
179
  end
@@ -242,7 +243,7 @@ WRAP
242
243
  def clean_release_directory(dir, count = 3)
243
244
  @cleanup_failed = true
244
245
  ordinal = count.succ.to_s
245
- shell.status "Cleaning release directory: #{dir}"
246
+ info "~> Cleaning release directory: #{dir}"
246
247
  sudo "ls -r #{dir} | tail -n +#{ordinal} | xargs -I@ rm -rf #{dir}/@"
247
248
  @cleanup_failed = false
248
249
  end
@@ -254,15 +255,15 @@ WRAP
254
255
  c.release_path = c.previous_release(rolled_back_release)
255
256
 
256
257
  revision = File.read(File.join(c.release_path, 'REVISION')).strip
257
- shell.status "Rolling back to previous release: #{short_log_message(revision)}"
258
+ info "~> Rolling back to previous release: #{short_log_message(revision)}"
258
259
 
259
260
  run_with_callbacks(:symlink)
260
261
  sudo "rm -rf #{rolled_back_release}"
261
262
  bundle
262
- shell.status "Restarting with previous release."
263
+ info "~> Restarting with previous release."
263
264
  with_maintenance_page { run_with_callbacks(:restart) }
264
265
  else
265
- shell.status "Already at oldest release, nothing to roll back to."
266
+ info "~> Already at oldest release, nothing to roll back to."
266
267
  exit(1)
267
268
  end
268
269
  end
@@ -273,17 +274,17 @@ WRAP
273
274
  @migrations_reached = true
274
275
  roles :app_master, :solo do
275
276
  cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} #{c.migration_command}"
276
- shell.status "Migrating: #{cmd}"
277
+ info "~> Migrating: #{cmd}"
277
278
  run(cmd)
278
279
  end
279
280
  end
280
281
 
281
282
  # task
282
283
  def copy_repository_cache
283
- shell.status "Copying to #{c.release_path}"
284
+ info "~> Copying to #{c.release_path}"
284
285
  run("mkdir -p #{c.release_path} #{c.failed_release_dir} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
285
286
 
286
- shell.status "Ensuring proper ownership."
287
+ info "~> Ensuring proper ownership."
287
288
  sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
288
289
  end
289
290
 
@@ -304,7 +305,7 @@ WRAP
304
305
  end
305
306
 
306
307
  def setup_services
307
- shell.status "Setting up external services."
308
+ info "~> Setting up external services."
308
309
  previously_configured_services = parse_configured_services
309
310
  begin
310
311
  sudo(services_command_check)
@@ -340,24 +341,24 @@ YML
340
341
  WRAP
341
342
  ["Symlink database.yml", "ln -nfs #{c.shared_path}/config/database.sqlite3.yml #{c.release_path}/config/database.yml"],
342
343
  ].each do |what, cmd|
343
- shell.status "#{what}"
344
+ info "~> #{what}"
344
345
  run(cmd)
345
346
  end
346
347
 
347
348
  owner = [c.user, c.group].join(':')
348
- shell.status "Setting ownership to #{owner}"
349
+ info "~> Setting ownership to #{owner}"
349
350
  sudo "chown -R #{owner} #{c.release_path}"
350
351
  end
351
352
  end
352
353
 
353
354
  def symlink_configs(release_to_link=c.release_path)
354
- shell.status "Preparing shared resources for release."
355
+ info "~> Preparing shared resources for release."
355
356
  symlink_tasks(release_to_link).each do |what, cmd|
356
- shell.status "#{what}"
357
+ info "~> #{what}"
357
358
  run(cmd)
358
359
  end
359
360
  owner = [c.user, c.group].join(':')
360
- shell.status "Setting ownership to #{owner}"
361
+ info "~> Setting ownership to #{owner}"
361
362
  sudo "chown -R #{owner} #{release_to_link}"
362
363
  end
363
364
 
@@ -380,7 +381,7 @@ WRAP
380
381
 
381
382
  # task
382
383
  def symlink(release_to_link=c.release_path)
383
- shell.status "Symlinking code."
384
+ info "~> Symlinking code."
384
385
  run "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
385
386
  @symlink_changed = true
386
387
  rescue Exception
@@ -428,9 +429,9 @@ WRAP
428
429
 
429
430
  def puts_deploy_failure
430
431
  if @cleanup_failed
431
- shell.status "[Relax] Your site is running new code, but clean up of old deploys failed."
432
+ info "~> [Relax] Your site is running new code, but clean up of old deploys failed."
432
433
  elsif @maintenance_up
433
- shell.status "[Attention] Maintenance page still up, consider the following before removing:"
434
+ info "~> [Attention] Maintenance page still up, consider the following before removing:"
434
435
  info " * Deploy hooks ran. This might cause problems for reverting to old code." if @callbacks_reached
435
436
  info " * Migrations ran. This might cause problems for reverting to old code." if @migrations_reached
436
437
  if @symlink_changed
@@ -440,9 +441,9 @@ WRAP
440
441
  end
441
442
  info " * Application servers failed to restart." if @restart_failed
442
443
  info ""
443
- shell.status "Need help? File a ticket for support."
444
+ info "~> Need help? File a ticket for support."
444
445
  else
445
- shell.status "[Relax] Your site is still running old code and nothing destructive has occurred."
446
+ info "~> [Relax] Your site is still running old code and nothing destructive has occurred."
446
447
  end
447
448
  end
448
449
 
@@ -455,7 +456,7 @@ WRAP
455
456
  def with_failed_release_cleanup
456
457
  yield
457
458
  rescue Exception
458
- shell.status "Release #{c.release_path} failed, saving release to #{c.failed_release_dir}."
459
+ info "~> Release #{c.release_path} failed, saving release to #{c.failed_release_dir}."
459
460
  sudo "mv #{c.release_path} #{c.failed_release_dir}"
460
461
  raise
461
462
  end
@@ -488,7 +489,7 @@ WRAP
488
489
 
489
490
  def check_ruby_bundler
490
491
  if gemfile?
491
- shell.status "Bundling gems..."
492
+ info "~> Bundling gems..."
492
493
 
493
494
  clean_bundle_on_system_version_change
494
495
 
@@ -522,7 +523,7 @@ WRAP
522
523
  unless run(node_package_manager_command_check)
523
524
  abort "*** [Error] package.json detected, but npm was not installed"
524
525
  else
525
- shell.status "package.json detected, installing npm packages"
526
+ info "~> package.json detected, installing npm packages"
526
527
  run "cd #{c.release_path} && npm install"
527
528
  end
528
529
  end
@@ -530,7 +531,7 @@ WRAP
530
531
  end # DeployBase
531
532
 
532
533
  class Deploy < DeployBase
533
- def self.new(config, shell=nil)
534
+ def self.new(config)
534
535
  # include the correct fetch strategy
535
536
  include EY::Serverside::Strategies.const_get(config.strategy)::Helpers
536
537
  super
@@ -1,8 +1,8 @@
1
1
  module EY
2
2
  module Serverside
3
3
  class DeployHook < Task
4
- def initialize(options, shell = nil)
5
- super(EY::Serverside::Deploy::Configuration.new(options), shell)
4
+ def initialize(options)
5
+ super(EY::Serverside::Deploy::Configuration.new(options))
6
6
  end
7
7
 
8
8
  def callback_context
@@ -3,14 +3,24 @@ module EY
3
3
  def self.deprecation_warning(msg)
4
4
  $stderr.puts "DEPRECATION WARNING: #{msg}"
5
5
  end
6
+ end
6
7
 
7
- def self.const_missing(const)
8
- if const == :LoggedOutput
9
- EY::Serverside.deprecation_warning("EY::Serverside::LoggedOutput has been deprecated. Use EY::Serverside::Shell::Helpers instead.")
10
- EY::Serverside::Shell::Helpers
11
- else
12
- super
13
- end
8
+ def self.const_missing(const)
9
+ if EY::Serverside.const_defined?(const)
10
+ EY::Serverside.deprecation_warning("EY::#{const} has been deprecated. use EY::Serverside::#{const} instead")
11
+ EY::Serverside.class_eval(const.to_s)
12
+ else
13
+ super
14
14
  end
15
15
  end
16
+
17
+ def self.node
18
+ EY::Serverside.deprecation_warning("EY.node has been deprecated. use EY::Serverside.node instead")
19
+ EY::Serverside.node
20
+ end
21
+
22
+ def self.dna_json
23
+ EY::Serverside.deprecation_warning("EY.dna_json has been deprecated. use EY::Serverside.dna_json instead")
24
+ EY::Serverside.dna_json
25
+ end
16
26
  end
@@ -0,0 +1,90 @@
1
+ require 'open4'
2
+
3
+ module EY
4
+ module Serverside
5
+ module LoggedOutput
6
+
7
+ class Tee
8
+ def initialize(*streams)
9
+ @streams = streams.flatten
10
+ end
11
+
12
+ def <<(output)
13
+ @streams.each do |s|
14
+ s << output
15
+ s.flush
16
+ end
17
+ self
18
+ end
19
+ end # Tee
20
+
21
+ @@logfile = File.join(ENV['HOME'], 'ey.log')
22
+ def self.logfile=(filename)
23
+ File.unlink filename if File.exist?(filename) # start fresh
24
+ @@logfile = filename
25
+ end
26
+
27
+ def self.logfile
28
+ @@logfile
29
+ end
30
+
31
+ @@verbose = false
32
+ def self.verbose=(v)
33
+ @@verbose = !!v
34
+ end
35
+
36
+ def self.verbose?
37
+ @@verbose
38
+ end
39
+
40
+ def verbose?
41
+ EY::Serverside::LoggedOutput.verbose?
42
+ end
43
+
44
+ def warning(msg)
45
+ info "WARNING: #{msg}\n".gsub(/^/,'!> ')
46
+ end
47
+
48
+ def info(msg)
49
+ with_logfile do |log|
50
+ Tee.new($stdout, log) << ("#{with_timestamp(msg)}\n")
51
+ end
52
+ end
53
+
54
+ def debug(msg)
55
+ with_logfile do |log|
56
+ log << "#{with_timestamp(msg)}\n"
57
+ end
58
+ end
59
+
60
+ def logged_system(cmd)
61
+ with_logfile do |log|
62
+ out = verbose? ? Tee.new($stdout, log) : log
63
+ err = Tee.new($stderr, log) # we always want to see errors
64
+
65
+ out << with_timestamp(":: running #{cmd}\n")
66
+
67
+ # :quiet means don't raise an error on nonzero exit status
68
+ status = Open4.spawn cmd, 0 => '', 1 => out, 2 => err, :quiet => true
69
+ status.exitstatus == 0
70
+ end
71
+ end
72
+
73
+ private
74
+ def with_logfile
75
+ File.open(logfile, 'a') {|f| yield f }
76
+ end
77
+
78
+ def logfile
79
+ EY::Serverside::LoggedOutput.logfile
80
+ end
81
+
82
+ def with_timestamp(msg)
83
+ return msg unless respond_to?(:starting_time)
84
+ time_passed = Time.now.to_i - starting_time.to_i
85
+ timestamp = "+%2dm %02ds " % time_passed.divmod(60)
86
+ msg.gsub(/^/, timestamp)
87
+ end
88
+ end
89
+ end
90
+ end