ey-deploy 0.7.0 → 0.7.1

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 (51) hide show
  1. data/lib/ey-deploy.rb +4 -0
  2. data/lib/ey-deploy/cli.rb +27 -16
  3. data/lib/ey-deploy/deploy.rb +13 -13
  4. data/lib/ey-deploy/deploy_hook.rb +13 -6
  5. data/lib/ey-deploy/logged_output.rb +58 -0
  6. data/lib/ey-deploy/server.rb +5 -5
  7. data/lib/ey-deploy/strategies/git.rb +5 -5
  8. data/lib/ey-deploy/task.rb +6 -12
  9. data/lib/ey-deploy/version.rb +1 -1
  10. data/lib/vendor/dataflow/HISTORY +52 -0
  11. data/lib/vendor/dataflow/LICENSE +19 -0
  12. data/lib/vendor/dataflow/README.textile +290 -0
  13. data/lib/vendor/dataflow/Rakefile +36 -0
  14. data/lib/vendor/dataflow/dataflow.rb +120 -0
  15. data/lib/vendor/dataflow/dataflow/actor.rb +22 -0
  16. data/lib/vendor/dataflow/dataflow/equality.rb +28 -0
  17. data/lib/vendor/dataflow/dataflow/future_queue.rb +24 -0
  18. data/lib/vendor/dataflow/dataflow/port.rb +54 -0
  19. data/lib/vendor/dataflow/examples/barrier.rb +9 -0
  20. data/lib/vendor/dataflow/examples/data_driven.rb +17 -0
  21. data/lib/vendor/dataflow/examples/dataflow_http_gets.rb +13 -0
  22. data/lib/vendor/dataflow/examples/flow.rb +20 -0
  23. data/lib/vendor/dataflow/examples/future_http_gets.rb +12 -0
  24. data/lib/vendor/dataflow/examples/future_queue.rb +11 -0
  25. data/lib/vendor/dataflow/examples/instance_variables.rb +15 -0
  26. data/lib/vendor/dataflow/examples/laziness.rb +9 -0
  27. data/lib/vendor/dataflow/examples/local_variables.rb +11 -0
  28. data/lib/vendor/dataflow/examples/messages.rb +26 -0
  29. data/lib/vendor/dataflow/examples/port_http_gets.rb +13 -0
  30. data/lib/vendor/dataflow/examples/port_send.rb +10 -0
  31. data/lib/vendor/dataflow/examples/ring.rb +21 -0
  32. data/lib/vendor/dataflow/spec/actor_spec.rb +28 -0
  33. data/lib/vendor/dataflow/spec/anonymous_variables_spec.rb +21 -0
  34. data/lib/vendor/dataflow/spec/barrier_spec.rb +25 -0
  35. data/lib/vendor/dataflow/spec/by_need_spec.rb +55 -0
  36. data/lib/vendor/dataflow/spec/dataflow_spec.rb +151 -0
  37. data/lib/vendor/dataflow/spec/equality_spec.rb +40 -0
  38. data/lib/vendor/dataflow/spec/flow_spec.rb +25 -0
  39. data/lib/vendor/dataflow/spec/forker_spec.rb +28 -0
  40. data/lib/vendor/dataflow/spec/future_queue_spec.rb +31 -0
  41. data/lib/vendor/dataflow/spec/inspect_spec.rb +19 -0
  42. data/lib/vendor/dataflow/spec/need_later_spec.rb +12 -0
  43. data/lib/vendor/dataflow/spec/port_spec.rb +26 -0
  44. data/lib/vendor/dataflow/spec/spec.opts +1 -0
  45. data/lib/vendor/dataflow/spec/spec_helper.rb +10 -0
  46. data/lib/vendor/open4/lib/open4.rb +403 -0
  47. data/spec/deploy_hook_spec.rb +16 -2
  48. data/spec/fixtures/invalid_hook.rb +1 -0
  49. data/spec/fixtures/valid_hook.rb +1 -0
  50. metadata +43 -4
  51. data/lib/ey-deploy/verbose_system.rb +0 -12
@@ -1,7 +1,11 @@
1
1
  require 'escape'
2
2
 
3
3
  $LOAD_PATH.push(File.expand_path("ey-deploy", File.dirname(__FILE__)))
4
+ $LOAD_PATH.unshift File.expand_path('vendor/thor/lib', File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift File.expand_path('vendor/open4/lib', File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift File.expand_path('vendor/dataflow', File.dirname(__FILE__))
4
7
 
8
+ require 'dataflow'
5
9
  require 'version'
6
10
  require 'strategies/git'
7
11
  require 'task'
@@ -1,9 +1,9 @@
1
- $:.unshift File.expand_path('../vendor/thor/lib', File.dirname(__FILE__))
2
-
3
1
  require 'thor'
4
2
 
5
3
  module EY
6
4
  class CLI < Thor
5
+ include Dataflow
6
+
7
7
  def self.start(*)
8
8
  super
9
9
  rescue RemoteFailure
@@ -36,9 +36,17 @@ module EY
36
36
  method_option :instances, :type => :array,
37
37
  :desc => "Instances in cluster"
38
38
 
39
+ method_option :verbose, :type => :boolean,
40
+ :default => false,
41
+ :desc => "Verbose output",
42
+ :aliases => ["-v"]
43
+
39
44
  desc "deploy", "Deploy code from /data/<app>"
40
45
  def deploy(default_task=:deploy)
41
46
  EY::Server.all = parse_instances(options[:instances])
47
+ EY::LoggedOutput.verbose = options[:verbose]
48
+ EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
49
+
42
50
  invoke :propagate
43
51
  EY::Deploy.run(options.merge("default_task" => default_task))
44
52
  end
@@ -88,25 +96,28 @@ module EY
88
96
 
89
97
  EY::Server.config = config
90
98
 
91
- EY::Server.all.find_all do |server|
99
+ barrier(*(EY::Server.all.find_all do |server|
92
100
  !server.local? # of course this machine has it
93
- end.find_all do |server|
94
- egrep_escaped_version = VERSION.gsub(/\./, '\.')
95
- # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
96
- # 0.5.11, and mistakenly thinking 0.5.1 is there
97
- has_gem_cmd = "#{gem_binary} list ey-deploy | grep \"ey-deploy \" | egrep -q '#{egrep_escaped_version}[,)]'"
98
- !server.run(has_gem_cmd) # doesn't have this exact version
99
- end.each do |server|
100
- puts "~> Installing ey-deploy on #{server.hostname}"
101
-
102
- system(Escape.shell_command([
101
+ end.map do |server|
102
+ need_later do
103
+ egrep_escaped_version = VERSION.gsub(/\./, '\.')
104
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
105
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
106
+ has_gem_cmd = "#{gem_binary} list ey-deploy | grep \"ey-deploy \" | egrep -q '#{egrep_escaped_version}[,)]'"
107
+
108
+ if !server.run(has_gem_cmd) # doesn't have this exact version
109
+ puts "~> Installing ey-deploy on #{server.hostname}"
110
+
111
+ system(Escape.shell_command([
103
112
  'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
104
113
  "-o", "StrictHostKeyChecking=no",
105
114
  local_gem_file,
106
- "#{config.user}@#{server.hostname}:#{remote_gem_file}",
115
+ "#{config.user}@#{server.hostname}:#{remote_gem_file}",
107
116
  ]))
108
- server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
109
- end
117
+ server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
118
+ end
119
+ end
120
+ end))
110
121
  end
111
122
 
112
123
  private
@@ -31,7 +31,7 @@ module EY
31
31
 
32
32
  cleanup_old_releases
33
33
 
34
- puts "~> finalizing deploy"
34
+ puts "~> Finalizing deploy"
35
35
  rescue Exception
36
36
  puts_deploy_failure
37
37
  raise
@@ -87,20 +87,21 @@ module EY
87
87
  # task
88
88
  def push_code
89
89
  puts "~> Pushing code to all servers"
90
- EY::Server.all.each do |server|
91
- server.push_code
92
- end
90
+ barrier *(EY::Server.all.map do |server|
91
+ need_later { server.push_code }
92
+ end)
93
93
  end
94
94
 
95
95
  # task
96
96
  def restart
97
97
  @restart_failed = true
98
98
  puts "~> Restarting app servers"
99
- puts "~> restarting app: #{c.latest_release}"
100
99
  roles :app_master, :app, :solo do
101
100
  restart_command = case c.stack
102
101
  when "nginx_unicorn"
103
- sudo(%Q{if [ ! -d /proc/`cat /var/run/engineyard/unicorn_#{c.app}.pid` ]; then rm -f /var/run/engineyard/unicorn_#{c.app}.pid; fi})
102
+ pidfile = "/var/run/engineyard/unicorn_#{c.app}.pid"
103
+ condition = "[ -e #{pidfile} ] && [ ! -d /proc/`cat #{pidfile}` ]"
104
+ sudo("if #{condition}; then rm -f #{pidfile}; fi")
104
105
  sudo("/etc/init.d/unicorn_#{c.app} deploy")
105
106
  when "nginx_mongrel"
106
107
  sudo("monit restart all -g #{c.app}")
@@ -135,7 +136,7 @@ module EY
135
136
  # task
136
137
  def cleanup_old_releases
137
138
  @cleanup_failed = true
138
- puts "~> cleaning up old releases"
139
+ puts "~> Cleaning up old releases"
139
140
  sudo "ls #{c.release_dir} | head -n -3 | xargs -I{} rm -rf #{c.release_dir}/{}"
140
141
  @cleanup_failed = false
141
142
  end
@@ -143,12 +144,12 @@ module EY
143
144
  # task
144
145
  def rollback
145
146
  if c.all_releases.size > 1
146
- puts "~> rolling back to previous release"
147
+ puts "~> Rolling back to previous release"
147
148
  c.release_path = c.previous_release
148
149
  run_with_callbacks(:symlink, c.previous_release)
149
150
  cleanup_latest_release
150
151
  bundle
151
- puts "~> restarting with previous release"
152
+ puts "~> Restarting with previous release"
152
153
  with_maintenance_page { run_with_callbacks(:restart) }
153
154
  else
154
155
  puts "~> Already at oldest release, nothing to roll back to"
@@ -161,7 +162,6 @@ module EY
161
162
  return unless c.migrate?
162
163
  @migrations_reached = true
163
164
  roles :app_master, :solo do
164
- puts "~> migrating"
165
165
  cmd = "cd #{c.latest_release} && #{c.framework_envs} #{c.migration_command}"
166
166
  puts "~> Migrating: #{cmd}"
167
167
  run(cmd)
@@ -170,10 +170,10 @@ module EY
170
170
 
171
171
  # task
172
172
  def copy_repository_cache
173
- puts "~> copying to #{c.release_path}"
173
+ puts "~> Copying to #{c.release_path}"
174
174
  sudo("mkdir -p #{c.release_path} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
175
175
 
176
- puts "~> ensuring proper ownership"
176
+ puts "~> Ensuring proper ownership"
177
177
  sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
178
178
  end
179
179
 
@@ -199,7 +199,7 @@ module EY
199
199
 
200
200
  # task
201
201
  def symlink(release_to_link=c.latest_release)
202
- puts "~> symlinking code"
202
+ puts "~> Symlinking code"
203
203
  sudo "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
204
204
  @symlink_changed = true
205
205
  rescue Exception
@@ -1,9 +1,5 @@
1
- require 'ey-deploy/verbose_system'
2
-
3
1
  module EY
4
2
  class DeployHook < Task
5
- include VerboseSystem
6
-
7
3
  def initialize(options)
8
4
  super(EY::Deploy::Configuration.new(options))
9
5
  end
@@ -13,14 +9,25 @@ module EY
13
9
  end
14
10
 
15
11
  def run(hook)
16
- if File.exist?("#{c.latest_release}/deploy/#{hook}.rb")
12
+ hook_path = "#{c.latest_release}/deploy/#{hook}.rb"
13
+ if File.exist?(hook_path)
17
14
  Dir.chdir(c.latest_release) do
18
15
  puts "~> running deploy hook: deploy/#{hook}.rb"
19
- callback_context.instance_eval(IO.read("#{c.latest_release}/deploy/#{hook}.rb"))
16
+ if desc = syntax_error(hook_path)
17
+ hook_name = File.basename(hook_path)
18
+ abort "*** [Error] Invalid Ruby syntax in hook: #{hook_name} ***\n*** #{desc.chomp} ***"
19
+ else
20
+ callback_context.instance_eval(IO.read(hook_path))
21
+ end
20
22
  end
21
23
  end
22
24
  end
23
25
 
26
+ def syntax_error(file)
27
+ valid = Kernel.system("ruby -c #{file} 2>/tmp/ey_invalid_deploy_hook | grep 'Syntax OK' --quiet")
28
+ File.new("/tmp/ey_invalid_deploy_hook").gets unless valid
29
+ end
30
+
24
31
  class CallbackContext
25
32
  def initialize(config)
26
33
  @configuration = config
@@ -0,0 +1,58 @@
1
+ require 'open4'
2
+
3
+ module EY
4
+ module LoggedOutput
5
+
6
+ class Tee
7
+ def initialize(*streams)
8
+ @streams = streams.flatten
9
+ end
10
+
11
+ def <<(output)
12
+ @streams.each { |s| s << output }
13
+ self
14
+ end
15
+ end # Tee
16
+
17
+ @@logfile = File.join(ENV['HOME'], 'ey.log')
18
+ def self.logfile=(filename)
19
+ File.unlink filename if File.exist?(filename) # start fresh
20
+ @@logfile = filename
21
+ end
22
+
23
+ def self.logfile
24
+ @@logfile
25
+ end
26
+
27
+ @@verbose = false
28
+ def self.verbose=(v)
29
+ @@verbose = !!v
30
+ end
31
+
32
+ def self.verbose?
33
+ @@verbose
34
+ end
35
+
36
+ def verbose?
37
+ EY::LoggedOutput.verbose?
38
+ end
39
+
40
+ def logfile
41
+ EY::LoggedOutput.logfile
42
+ end
43
+
44
+ def logged_system(cmd)
45
+ File.open(logfile, 'a') do |log|
46
+ out = verbose? ? Tee.new($stdout, log) : log
47
+ err = Tee.new($stderr, log) # we always want to see errors
48
+
49
+ out << ":: running #{cmd}\n"
50
+
51
+ # :quiet means don't raise an error on nonzero exit status
52
+ status = Open4.spawn cmd, 0 => '', 1 => out, 2 => err, :quiet => true
53
+ status.exitstatus == 0
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -1,9 +1,9 @@
1
1
  require 'open-uri'
2
- require 'ey-deploy/verbose_system'
2
+ require 'ey-deploy/logged_output'
3
3
 
4
4
  module EY
5
5
  class Server < Struct.new(:hostname, :role, :name)
6
- include VerboseSystem
6
+ include LoggedOutput
7
7
 
8
8
  def initialize(*fields)
9
9
  super
@@ -52,14 +52,14 @@ module EY
52
52
  def push_code
53
53
  return if local?
54
54
  run "mkdir -p #{config.repository_cache}"
55
- system(%|rsync --delete -aq -e "#{ssh_command}" #{config.repository_cache}/ #{config.user}@#{hostname}:#{config.repository_cache}|)
55
+ logged_system(%|rsync --delete -aq -e "#{ssh_command}" #{config.repository_cache}/ #{config.user}@#{hostname}:#{config.repository_cache}|)
56
56
  end
57
57
 
58
58
  def run(command)
59
59
  if local?
60
- system(command)
60
+ logged_system(command)
61
61
  else
62
- system(ssh_command + " " + Escape.shell_command(["#{config.user}@#{hostname}", command]))
62
+ logged_system(ssh_command + " " + Escape.shell_command(["#{config.user}@#{hostname}", command]))
63
63
  end
64
64
  end
65
65
 
@@ -1,4 +1,4 @@
1
- require 'ey-deploy/verbose_system'
1
+ require 'ey-deploy/logged_output'
2
2
 
3
3
  module EY
4
4
  module Strategies
@@ -24,7 +24,7 @@ module EY
24
24
  end
25
25
  end
26
26
 
27
- include VerboseSystem
27
+ include LoggedOutput
28
28
 
29
29
  attr_reader :opts
30
30
 
@@ -39,10 +39,10 @@ module EY
39
39
 
40
40
  def fetch
41
41
  if usable_repository?
42
- system("#{git} fetch origin")
42
+ logged_system("#{git} fetch -q origin")
43
43
  else
44
44
  FileUtils.rm_rf(opts[:repository_cache])
45
- system("git clone #{opts[:repo]} #{opts[:repository_cache]}")
45
+ logged_system("git clone -q #{opts[:repo]} #{opts[:repository_cache]}")
46
46
  end
47
47
  end
48
48
 
@@ -53,7 +53,7 @@ module EY
53
53
  opts[:ref]
54
54
  end
55
55
 
56
- system("#{git} checkout '#{to_checkout}'") || system("#{git} reset --hard '#{to_checkout}'")
56
+ logged_system("#{git} checkout -q '#{to_checkout}'") || logged_system("#{git} reset -q --hard '#{to_checkout}'")
57
57
  end
58
58
 
59
59
  def create_revision_file(dir)
@@ -1,5 +1,6 @@
1
1
  module EY
2
2
  class Task
3
+ include Dataflow
3
4
 
4
5
  attr_reader :config
5
6
  alias :c :config
@@ -44,22 +45,15 @@ module EY
44
45
  run_on_roles(cmd, %w[sudo sh -l -c], &blk)
45
46
  end
46
47
 
47
- def prepare_run(command)
48
- Escape.shell_command ["sh", "-l", "-c", command]
49
- end
50
-
51
- def prepare_sudo(command)
52
- Escape.shell_command ["sudo", "sh", "-l", "-c", command]
53
- end
54
-
55
48
  private
56
49
 
57
50
  def run_on_roles(cmd, wrapper=%w[sh -l -c])
58
- EY::Server.from_roles(@roles).inject(false) do |acc, server|
51
+ results = EY::Server.from_roles(@roles).map do |server|
59
52
  to_run = block_given? ? yield(server, cmd.dup) : cmd
60
- failure = !server.run(Escape.shell_command(wrapper + [to_run]))
61
- acc || failure
62
- end && raise(EY::RemoteFailure)
53
+ need_later { server.run(Escape.shell_command(wrapper + [to_run])) }
54
+ end
55
+ barrier *results
56
+ results.all? || raise(EY::RemoteFailure)
63
57
  end
64
58
  end
65
59
  end
@@ -1,3 +1,3 @@
1
1
  module EY
2
- VERSION = '0.7.0'
2
+ VERSION = '0.7.1'
3
3
  end
@@ -0,0 +1,52 @@
1
+ == 0.3.0 / 2009-08-29
2
+
3
+ * Major enhancements
4
+
5
+ * "flow" abstraction for threading (use it to create threads and optionally
6
+ pass it a variable to be bound output, or override it to modify need_later
7
+ for other threading strategies such as thread pools)
8
+
9
+ * Dataflow::FutureQueue
10
+
11
+ * Nested binding through variables possible
12
+
13
+ * Minor enhancements
14
+
15
+ * Better #inspect and UnificationError debugging output
16
+
17
+ * "barrier" abstraction for manually preventing execution until variable
18
+ arguments have been bound
19
+
20
+ * Use mixin methods as class/module methods optionally
21
+ e.g. Dataflow.local {|v| v }
22
+
23
+ == 0.2.1 / 2009-07-29
24
+
25
+ * Minor enhancements
26
+
27
+ * Leave __send__ and __id__ alone when acting as a proxy
28
+
29
+ == 0.2.0 / 2009-07-09
30
+
31
+ * Major enhancements
32
+
33
+ * Made equality between objects and dataflow variables more transparent
34
+ (load equality changes with: require 'dataflow/equality')
35
+
36
+ * Minor enhancements
37
+
38
+ * Made #inspect work with unbound variables to not crash deubggers/repls/etc
39
+
40
+ * Made relationship between lazy and dataflow behavior more strict
41
+
42
+ == 0.1.1 / 2009-06-13
43
+
44
+ * Minor enhancements
45
+
46
+ * Got the "require_path" set correctly so rubygems can be used =)
47
+
48
+ == 0.1.0 / 2009-06-13
49
+
50
+ * 1 major enhancement
51
+
52
+ * Birthday!
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Larry Diehl
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.